Using manual dispatching of I/o requests in WDF serial driver sample

Hi,

I am developing. A serial driver for com ports which are hosted on a PCI-e controller. I am taking WDF serial driver sample driver as reference for this.

In WDF serial driver I see that they are using manual dispatching for the read write queues and they are using DPC routines to complete a request and to start a new request.

In my driver however I am using sequential dispatching only. Do I still need to complete the requests in DPC routine only ?

Also what is the advantage of using manual dispatching over sequential dispatching? As per my knowledge manual dispatching is used mainly to push the requests from some other queue into a queue and hold them. But I can’t understand the reasons for using manual dispatching for I/o requests.

> Hi,

I am developing. A serial driver for com ports which are hosted on a PCI-e
controller. I am taking WDF serial driver sample driver as reference for
this.

In WDF serial driver I see that they are using manual dispatching for the
read write queues and they are using DPC routines to complete a request
and to start a new request.

In my driver however I am using sequential dispatching only. Do I still
need to complete the requests in DPC routine only ?

OK, this introduces all kinds of questions about how the driver works. If
you have multiple COM ports, you MUST dispatch in parallel; otherwise, you
are saying that an IRP_MJ_READ for port 3 cannot be dispatched until the
IRP_MJ_READ for port 2 has completed, which makes no sense.

Furthermore, serial ports are by design full-duplex devices, with reads
and writes being asynchronous. Consider that one thread has fired off an
IRP_MJ_READ on port 3 and another thread wants to do an IRP_MJ_WRITE on
port 3. Furthermore, that IRP_MJ_WRITE is a request to the remote device
to send an answer back. The Read can’t complete until after the Write
happens, but the Write can’t happen as long as the Read is pending.
Deadlock.

Could you explain why sequential scheduling could make sense on serial ports?

Every IRP /must/ be completed. This is not negotiable. Where you
complete them depends on the nature of the driver. If you have an
interrupt-driven device, then you get routed to the ISR when an interrupt
comes in. At some point, you determine that the request has finished.
For example, for a Read operation, this could be when a certain character
(e.g., LF) is seen, or when the buffer is filled, or when the line has
been idle for some defined period of time, or some other criterion,
whichever happens first. So, assume that you discover in the ISR that you
have met the required criterion for completion. What are you going to do?
You want to complete the IRP, but it is not possible to complete an IRP
from an ISR. So you have to request a lower-IRQL thread to do this. How
do you do this? Well, you could queue up a request in a queue for a
passive-level thread. Oh, wait. You can’t use a spinlock at ISR level.
OK, that’s out. Or, you could queue up a request in a queue for a
DISPATCH_LEVEL thread. Well, that’s easy. It’s called scheduling a DPC.
So if you don’t complete the IRP in the DPC, how do you propose to
complete it? What do you think your options might be? I would say: you
complete it in the DPC, because that is the only option that is available.

Also what is the advantage of using manual dispatching over sequential
dispatching? As per my knowledge manual dispatching is used mainly to push
the requests from some other queue into a queue and hold them. But I can’t
understand the reasons for using manual dispatching for I/o requests.

“Manual dispatching” and “Sequential dispatching” are not opposites.
Sequential dispatching means that one IRP cannot be dispatched until the
previous IRP has completed (see earlier discussion). If you open a COM
port without FILE_FLAG_OVERLAPPED, the I/O Manager already does this for
you. The reason a COM port dispatches its IRPs internally is that it has
to be able to handle Write and Read requests independently, because it is
a full-duplex device. A Write cannot be delayed waiting for a Read to
complete, and a Read cannot be delayed waiting for a Write. So Read
operations go into one queue, and Write operations go to another. They are
dispatched, sequentially, to the read side or write side of the COM port.
Note that you cannot be running two Read operations, or two Write
operations, on the same port at the same time. But you /must/ be able to
handle Read and Write operations in parallel on the same port. Otherwise,
you are not a COM port, but something else that vaguely resembles one.
Since it is not an exact implementation of the specifications of a COM
port, it cannot work correctly for people who expect COM port semantics.
joe


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Joseph M. Newcomer wrote:

OK, this introduces all kinds of questions about how the
driver works. If you have multiple COM ports, you MUST
dispatch in parallel; otherwise, you are saying that an
IRP_MJ_READ for port 3 cannot be dispatched until the
IRP_MJ_READ for port 2 has completed, which makes
no sense.

This “advice” is horribly broken for at least two reasons:

  1. If you have two COM ports, they’re going to be on two different devnodes, which means two different WDFDEVICEs and two sets of queues that know nothing about each other.

  2. Even if this weren’t the case, pending a request from a sequentially dispatched queue by placing it into a manual queue allows for further requests to be dispatched.

In a serial driver, I/O requests that can’t be completed (i.e. a read for more bytes than are available, or an IOCTL resulting from WaitCommEvent(), etc.) are going to sit in a manual queue to support cancellation.

So, there’s no reason why you couldn’t necessarily use sequential dispatch in a serial driver.

Note that you cannot be running two Read operations on
the same port at the same time.

This is not true at all either. The reads will be queued and completed in the order they were received.

> Joseph M. Newcomer wrote:

> OK, this introduces all kinds of questions about how the
> driver works. If you have multiple COM ports, you MUST
> dispatch in parallel; otherwise, you are saying that an
> IRP_MJ_READ for port 3 cannot be dispatched until the
> IRP_MJ_READ for port 2 has completed, which makes
> no sense.

This “advice” is horribly broken for at least two reasons:

  1. If you have two COM ports, they’re going to be on two different
    devnodes, which means two different WDFDEVICEs and two sets of queues that
    know nothing about each other.

It depends on what level you are writing the driver. For example, the COM
ports could be represented as higher-level FDOs, but the driver in
question is managing a plurality of devices on a single card. So there
are many ways to represent the driver structure. So under this scenario,
my advice is not “broken”. Since there is absolutely no description of
the actual driver structure, just a vague handwave, I can make any
assumptions I want about what the driver might look like.

  1. Even if this weren’t the case, pending a request from a sequentially
    dispatched queue by placing it into a manual queue allows for further
    requests to be dispatched.

It depends. If you are talking about sequential dispatching from the I/O
Manager, no, additional requests will be held until the current IRP is
completed. Otherwise, it depends on the nature of how sequential
dispatching is done. There are many models of this, but the absence of
any accurate description of the nature of the driver makes it hard to
guess what is going on. So again, I can make some guesses, and comment on
those. My guesses are based on inadequate information, and if they are
wrong, better information will point this out.

In a serial driver, I/O requests that can’t be completed (i.e. a read for
more bytes than are available, or an IOCTL resulting from WaitCommEvent(),
etc.) are going to sit in a manual queue to support cancellation.

If the read is for more bytes than available, the timeouts can be set so
that the IRP will complete if the line appears to have gone idle.
SetCommTimeout, for example, is one of the many APIs that sets some of the
timing parameters. So the decision about whether or not an I/O request
can be completed depends upon factors you have ignored. Cancellation, of
course, is one of the many actions that can cause an IRP to be completed.

So, there’s no reason why you couldn’t necessarily use sequential dispatch
in a serial driver.

> Note that you cannot be running two Read operations on
> the same port at the same time.

This is not true at all either. The reads will be queued and completed in
the order they were received.

Oh, then I am right: you can’t run two Read operations on the same port at
the same time. You have to serialize them. So how can it be “not true”
when you simply restate what I said and say that it is right? You are
running one at a time, and yes, you need a queue to handle the ones you
cannot deal with. Why does this appear to be news?
joe


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer