Is this a production thing, or a custom deployment? Read the rest of this
with the assumption that you are deploying this in controlled environments.
If you are committed to not returning control/status data (your 32 bit
number plus values to be named later?) into the user buffer, you need to
select a way to return an arbitrary buffer on both the input and output
IOCTLs. If you could guarantee that your status data would never be larger
than 32 bits, you could constrain it to be user-defined NTSTATUS values
that always succeed the NTSUCCESS() macro, and return it as the completion
status. Since you say that the status may grow beyond a single 32 bit
value, that won’t work.
IMHO, your best bet is to define your IOCTL as METHOD_BUFFERED, pass the
same block as input and output, and embed a pointer to your input/output
buffer in that block. Alternatively, you can define separate input and
output blocks, but you still have to embed the user buffer pointer in the
input block that contains the control data. In either case, you have to
probe and lock the user buffer referenced by the embedded pointer in order
to use it, and you have to unlock it when you are done. Doing all that
isn’t *that* bad, despite all the moaning on this list over what a pain it
is to get right.
What the METHOD_BUFFERED approach buys you is that the app can’t yank your
control buffer out from under you as you are attempting to use it, which
can’t be said for METHOD_NEITHER. If you are willing to Probe&Lock your
input and output blocks before you access them, you could use the same
embedded pointer technique with METHOD_NEITHER. I’m not sure whether the
cost of the control block P&L is greater or less than the cost of
allocating NP memory and copying the control block for a “small” control
block, but for a “large” control block, it’s probably going to be faster to
P&L. But “large” is probably a lot of KB before P&L is a win. And
“small” is somewhat subjective, too. So unless your control block is huge,
METHOD_BUFFERED is probably the better way to deliver your control block.
As noted above, in either case, you will have to P&L your data buffer.
However, I do want to caution that although we do the METHOD_BUFFERED with
embedded pointers in a lab harness, and are pretty confident that we’ve got
it right, I would need a lot more malicious testing on it before I released
it as a product.
As I’ve said on several occasions, if MS had provided for a means of
returning a status block in METHOD_OUT_DIRECT, I wouldn’t have any need for
any of this, but they didn’t and that horse has long left the barn, so
unless they give us another method that does allow a status block on an
OUT_DIRECT, embedded pointers appears to be the only other way to get an
arbitrary small buffer back from the driver when you send a really large
data buffer to the hardware, without incurring either a nasty large copy
penalty or having a funky header in your output data buffer after you are
done.
Phil
Philip D. Barila
Seagate Technology LLC
(720) 684-1842
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of “Michael Kohne”
Sent: Tuesday, November 14, 2006 11:12 AM
To: “Windows System Software Devs Interest List”
Subject: [ntdev] Secondary return path for IOCTL - what’s the usual way?
I’m defining IOCTLs for my driver and I have multiple pieces of information
that I need to return from the same IOCTL. The first is a large (several
megabyte) buffer full of data, the second is a simple 32 bit number (this
might grow later on, but will never get very big). This is the ‘input’
IOCTL.
Another IOCTL takes a several meg buffer as an input, and still needs to
return the same 32 bit number. This is the ‘output’ IOCTL.
I would like to NOT put the ancillary data (the 32 bit number) in the same
buffer as the primary data (the several megabyte buffer). This would be
very inconvenient for the app, especially in the output case, where the app
guys would like to be able to use the same (pre-defined) buffer in multiple
IOCTL calls simultaneously.
I would like to use the same method for both the IOCTLs.
METHOD_BUFFERED is right out because it’s one buffer in, one buffer out,
AND I’d take a buffer copy on every IOCTL. No good.
METHOD_IN_DIRECT and METHOD_OUT_DIRECT get me the ability to DMA straight
from/to the user’s buffer, but the only way that I can return ancillary
data would be through the ‘Information’ field in the IRP (the nBytes param
to the DeviceIoControl call). I assume this will work, but it does prevent
me from returning the actual number of bytes involved, which may be a
problem for the ‘input’ IOCTL.
METHOD_NEITHER would allow me to directly access either buffer (since I’m a
function driver, my IOCTLs are called in the context of the calling app).
Thus, I could write to the user’s input buffer, which could then be a
structure of some sort. However, this adds extra complexity with all the
locking, etc to make sure that I don’t screw up and leave holes or crash
points.
The only other way I can think of to deal with this is to call a second
IOCTL to get the ancillary data. However, that seems like a good way for
the two to become out of sync on the slightest app or driver error, leading
to hard to debug issues.
Is there a usual way to solve this problem? I tend to think I’m not the
first one to want to do this, and I also tend to think that I’m missing
something obvious.
Thanks for your assistance.
Michael Kohne
xxxxx@kohne.org
“You must be smarter than the equipment you are trying to operate.”
—
Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256
To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer