[PATCH] ntoskrnl.exe: Implement Ke(Initialize|Insert|Remove)DeviceQueue.

Rémi Bernon rbernon at codeweavers.com
Tue Jun 22 12:33:34 CDT 2021


On 6/22/21 6:14 PM, Zebediah Figura (she/her) wrote:
> On 6/22/21 10:29 AM, Rémi Bernon wrote:
>> On 6/22/21 5:20 PM, Zebediah Figura (she/her) wrote:
>>> On 6/22/21 7:02 AM, Rémi Bernon wrote:
>>>> Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
>>>> ---
>>>>
>>>> MSDN says that the cancel spinlock has to be held whenever these device
>>>> queues are used, when setting cancel routines, but it's not clear to me
>>>> why, as IRP cancellation has apparently no effect on these functions
>>>> behavior (I initially thought they should check and not queue cancelled
>>>> IRPs, but it looks like they don't).
>>>
>>> Where do you see this? I couldn't find any mention of it from a brief
>>> search.
>>>
>>
>> For instance [1]:
>>
>> "A driver must hold the system cancel spin lock when calling this
>> routine if the driver uses the I/O manager-supplied device queue in the
>> device object. The driver runs at IRQL = DISPATCH_LEVEL after calling
>> IoAcquireCancelSpinLock until it releases the cancel spin lock with
>> IoReleaseCancelSpinLock."
>>
>> [1]
>> https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-iosetcancelroutine 
>>
>>
>> I understand the "I/O manager-supplied device queue in the device
>> object" is the "KDEVICE_QUEUE DeviceQueue" member that I wanted to use
>> with these functions instead of re-implementing a queue and a lock again.
> 
> Based on [2] and [3] I'm guessing the device queue functions never 
> acquire the lock, but rather the point is to synchronize with dequeuing 
> and cancelling. I can't figure out why it makes a difference, though...
> 
> It doesn't seem obvious to me that using KDEVICE_QUEUEs in Wine makes 
> our job any easier, fwiw. Especially the part where you can't actually 
> queue the first pending IRP. I'm not sure it's meant to handle those 
> kinds of pending IRPs, but then it's not really clear how these things 
> are supposed to be used; there are no code samples anywhere.
> 

It probably also has something to do with IoStart(Next)Packet which need 
to handle cancellation.

Even when not using these, and even if queue-ing an IRP needs a loop I 
think using the device queue directly makes the code slightly simpler as 
it doesn't need locking.

It also saves a dedicated irp_queue and irp_queue_lock members which are 
already in the device object, and currently unused.

>>>> +
>>>> +static void test_queue(void)
>>>> +{
>>>> +    KDEVICE_QUEUE_ENTRY *entry;
>>>> +    KDEVICE_QUEUE queue;
>>>> +    BOOLEAN ret;
>>>> +    KIRQL irql;
>>>> +    IRP *irp;
>>>> +
>>>> +    irp = IoAllocateIrp(1, FALSE);
>>>> +
>>>> +    memset(&queue, 0xcd, sizeof(queue));
>>>> +    KeInitializeDeviceQueue(&queue);
>>>> +    ok(!queue.Busy, "unexpected Busy state\n");
>>>> +    ok(queue.Size == sizeof(queue), "unexpected Size %x\n", 
>>>> queue.Size);
>>>> +    ok(queue.Type == IO_TYPE_DEVICE_QUEUE, "unexpected Type %x\n",
>>>> queue.Type);
>>>
>>> Could we test the actual contents of the queue list after all these 
>>> calls?
>>>
>>
>> I guess. It's hopefully going to be empty?
>>
> 
> Empty after this call, but not after an eventual KeInsertDeviceQueue 
> call; I think it's worth testing the state after all of the Ke* calls 
> below.
> 

Yeah I figured, I'll add more tests.
-- 
Rémi Bernon <rbernon at codeweavers.com>



More information about the wine-devel mailing list