When Progress Is Required…
Today’s post is going to be a quick overview of one of the great new features in KMDF 1.9- built-in support for “guaranteed forward progress”.
What is it, and why would I want to have it?
The essential case occurs in storage device drivers. If one of the system’s paging files resides on that device, then the driver is going to be asked to read and write memory pages to and from the paging file. Failure of this I/O isn’t an option- the system itself could be dead if it were to fail.
Since it is common for drivers to subdivide requests into smaller pieces (for instance), this can mean that the driver may need IRPs or MDLs or other items to complete this work. But if it asks for them from the system, those calls may fail. So, to guarantee forward progress under low-memory conditions (and of course paging is critical in such cases), it is a common practice to pre-allocate IRPs, MDLs and other items that may be needed when memory is tight, and to use them only when this situation occurs. Basically a rainy day account used to keep things moving under stress, but not touched in the normal course of events.
So what’s this new stuff about?
Well, KMDF has to create WDFREQUEST objects for incoming I/O, and this is a memory allocation. So, beginning with KMDF 1.9, you can now set a “Forward Progress Policy” on a WDFQUEUE object while setting up your device.
This policy consists of these things:
- A number of WDFREQUEST objects that the framework is to pre-allocate, use only as needed, and not let go of.
- An optional event callback that allows you to create additional items for each of these reserved requests ass they are created (so you can do all of your pre-allocation when the policy is established).
- A similar callback for normal requests (so all requests can be assumed to have similar contexts in your IO callbacks).
- A choice of policies regarding when this feature is utilized (more on that in the next list).
The choices I alluded to (things the framework will do when it fails to get a functional WDFREQUEST for an incoming IRP destined for your queue) are these (they are mutually exclusive):
- Always use a reserved request in the event of a failure to wrap an incoming request in a WDFREQUEST (if you have the callback which adds items to a normal request, failure of that callback also triggers this condition- a factoid that came in handy when testing this feature, of course).
- Provide an optional “examination” callback which will look at the incoming IRP and decide whether you want to just fail it or use a reserved request.
- Examine the IRP to see if it is paging I/O (the OS marks these)- if it is, then use a reserved request, otherwise, fail it.
A new DDI was added to set this policy on a queue, and another was added to allow you to see if a given WDFREQUEST is a reserved request or not.
If requests continue to fail, and all the reserved requests are in use, then the incoming requests get pended and remain in a list at the queue- as each reserved request is completed, it will get recycled as needed until there are no more failing requests to guarantee progress on. So a queue with this configured becomes a counted queue of sorts under low-memory conditions.
For the most part you can treat these requests in the normal fashion- you can forward them to other queues, for instance. When completed, they return to their owner- the bookkeeping behind all of that makes this a challenging feature from a test perspective.
Details on this are in the Win7 Beta WDK- see WdfIoQueueAssignForwardProgressPolicy and WdfRequestIsreserved as a starting point for further reading- this post is just meant for the mile-high overview [so I feel like I did something besides practice my guitar during today’s long builds].
Now playing: Yuki Kajiura- Madlax OST Volume 2- Lost Command