Windows File System filter driver development tutorial (7)

Source: Internet
Author: User
Windows File System filter driver development tutorial 7. IRP completion function, interrupt level, how to surpass Interrupt Level Limit

First, let's discuss how the volumne device gets it. First, let's look at the following functions:

// ------------------ Content in WDF. h -------------------------
Typedef VPB wd_vpb;
_ Inline wd_vpb * wd_dev_vbp (wd_dev * Dev)
{
Return Dev-> VPB;
}

_ Inline wd_dev * wd_vbp_dev (wd_vpb * VPB)
{
Return VPB-> deviceobject;
}

VPB is a volume parameter block. It is a data structure. Its main function is to associate the object of the actually stored media device with the object of the volume device on the file system.

Wd_dev_vbp allows you to get a VPB from a storage device object, and the wd_vbp_dev function can get the volmue device corresponding to this VPB.

First, you need to get the storage device object. In fact, this object is saved in the current io_stack_location.

// ------------------ Content in WDF. h -----------------------
_ Inline wd_dev * wd_irpsp_mount_storage (wd_io_stack * irpsp)
{
Return irpsp-> parameters. mountvolume. VPB-> realdevice;
};

Then, starting from IRP, I can finally get the volumue device in the following ways:

Wd_irpsp * irpsp = wd_cur_io_stack (IRP );
Wd_dev * storage_dev = wd_irpsp_mount_storage (IRP );
Wd_vpb * VPB = wd_dev_vbp (storage_dev );
Wd_dev * volume_dev = wd_vbp_dev (VPB );

However, the actual situation is not that simple. here, the IRP is a MOUNT request. the volume device object is actually the result returned after the request is completed. therefore, before this request is completed, we try to obtain the volume device object. Of course, it is empty.

Here, you can directly copy the current io_stack_location and then send down the request, but before that, you need to assign a complete function to the IRP. once IRP is completed, your completion function will be called. in this way, you can get the volume device in the completion function and implement your binding process.

Here we will discuss the issue of interrupt level. We often encounter people asking what a function can only be called at passive level. In short, any of ourCodeDuring execution, it is always at the Current Interruption level. some system calls can only be executed at the low-level interruption level. please note that if a call can run at a height, it can run at a low level, otherwise it will not.

All we need to know is that we care about passive level and dispatch level. in addition, the dispatch level has a high interrupt level. generally, DDK indicates that if IRQ level> = dispatch is specified, you cannot call them in passive level code.

So how do you determine the interrupt level of the current Code? I usually make this decision: If your code execution is caused by applicationsProgram(Or upper layer) calls should be triggered at the passive level. If your code execution is caused by the underlying hardware, it may be at the dispatch level.

Hope you don't understand me mechanically! The above is just a rough and easy-to-remember method. the actual application should be like this: all dispatch functions calls are caused by the IRP sent from the upper layer, so they should be passive level, where you can call the vast majority of system calls. for example, the NIC's onreceive function may be at the dispatch level when the hard disk reads and writes are complete and the function is returned. it is possible, not absolutely. however, once possible, we should consider it as appropriate.

Well, now we find that we have registered the complete function, and this function may be executed at dispatch level.

The problem is that we have decided to call ioattachdevicetodevicestack in the completion function to bind volume. The DDK indicates callers of ioattachdevicetodevicestack must be running at IRQL <= dispatch_level.

In fact, I mentioned ioattachdevicetodevicestacksafe before, and this call can be performed at the dispatch level. However, this call only appears in systems above XP.

There are several methods to go beyond the interrupt level. the first is to generate a system thread to complete the process. the system thread will run in passive level. another way is to insert your job into the windows worker thread, which will make your job run sooner or later. if your task is small, you can use the second method. it is easy for the system and troublesome for programmers.

I have done the following functions to insert tasks to the worker thread.
// --------------- Content in WDF. h ------------------------
Typedef work_queue_item wd_work_item;
Typedef pworker_thread_routine wd_work_func;
// Task Initialization
_ Inline wd_void wd_work_init (wd_work_item * item,
Wd_work_func worker,
Wd_void * context)
{
Exinitializeworkitem (item, worker, context );
}

// Three Task queues
Typedef Enum _ wd_work_quque_type {
Wd_work_crit = criticalworkqueue,
Wd_work_delay = delayedworkqueue,
Wd_work_hyper = hypercriticalworkqueue
} Wd_work_queue_type;

_ Inline wd_void wd_work_queue (in wd_work_item * item,
In wd_work_queue_type)
{
Exqueueworkitem (item, (work_queue_type) type );
}

_ Inline wd_void wd_work_run (in wd_work_item * item)
{
(Item-> workerroutine) (item-> parameter );
}

A task is a data structure that has been redefined as wd_work_item. wd_work_init can initialize it. during initialization, you only need to enter a function for your task. at the same time, a context is used to record upstream and downstream parameters. (This is a null pointer. You can only think of any parameter type you want ).

Generally, this task is automatically executed, but sometimes we want to ignore the queue and execute it ourselves. Then we can call wd_work_run.

Then wd_work_queque is called to insert the worker queue, which will be executed. The insertion type is selected here wd_work_delay.

I hope you are not confused by this string. now I will write a function for "setting completion function. after execution, your completion function is automatically executed at the passive level. hope it won't make you dizzy :).

// Complete the context of the routine. Several fsctl registration completion routines are required. While the work in the routine may
// It can only be run in passive level, so you have to add a work_item to plug the task
// The input working thread is waiting for completion.
Typedef struct _ my_fsctl_comp_con
{
Wd_work_item work;
Wd_dev * dev;
Wd_irp * IRP;
Wd_dev * new_dev; // This element is only used for mounting. Because I
// You need to generate a new device to bind to VDO.
} My_fsctl_comp_con;

Wd_bool my_fsctl_set_comp (wd_dev * Dev,
Wd_irp * IRP,
Wd_dev * new_dev,
Wd_irp_comp_func complete,
Wd_work_func work_complete)
{
My_fsctl_comp_con * context;
Context = (wdff_fsctl_comp_con *) wd_malloc (wd_false,
Sizeof (wdff_fsctl_comp_con ));
If (context = NULL)
{
Wd_printf0 ("fsctl set COMP: failed to malloc context. \ r \ n ");
Return wd_false;
}

// Initialization details
Wd_work_init (& context-> Work,
Work_complete,
Context );

Context-> Dev = dev;
Context-> IRP = IRP;
Context-> new_dev = new_dev;

// Set the IRP completion routine
Wd_irp_comp (IRP, complete, context );

Return wd_true;
}

// The following functions are used as the parameters of the above complete
Wd_stat my_fsctl_comp (in wd_dev * Dev,
In wd_irp * IRP,
In wd_void * context)
{
Wd_printf0 ("fsctl_comp: Come in !!! \ R \ n ");
Unreferenced_parameter (Dev );
Unreferenced_parameter (IRP );
// Judge the Current Interruption level
If (wd_get_cur_irql ()> wd_irql_passive)
{
Wd_printf0 ("fsctl_comp: into quque !!! \ R \ n ");
// If it is at a lower interrupt level than passive, it must be inserted to the delayed queue for operation.
Wd_work_queue (wd_work_item *) Context, wd_work_delay );
}
Else
{
// Otherwise, you can directly execute
Wd_printf0 ("fsctl_comp: run directly !!! \ R \ n ");
Wd_work_run (wd_work_item *) context );
}
Return wd_stat_more_processing;
}

I think the above process is understandable! After registering the basic completion process complete function (that is, after the final function my_fsctl_comp I wrote), IRP executes the execution and calls back my_fsctl_comp. I have already completed the completed task (wd_work_item) in advance) written in context pointer. once this function is called back, I will insert the queue into wd_work_queque. the work_complete function recorded in wd_work_item is obviously executed in the passive level. our system will also remain stable.

The work_complete function obtains enough parameters from the context pointer to bind volume.

I hope you are not confused. :) we will break it down next time.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.