2

I have 2 structures declared:

struct irp_list {
   IRP *irp;
   LIST_ENTRY lh;
};

and

struct dev_info {
...
   LIST_ENTRY lh;
...
};

Inside DriverWrite function (IRP_MJ_WRITE) I do:

struct irp_list *il;
struct dev_info *di = (struct dev_info*)device->DeviceExtension;

if (!(il = ExAllocatePool(NonPagedPool, sizeof(*il)))) {
    ret = STATUS_NO_MEMORY;
    DbgPrint("[uart] UartWrite can't handle irp...\n");
    goto error;
}

il->irp = irp;  // store DriverWrite irp

InsertTailList(&di->lh, &il->lh);   // this insert is not failing...
irp->IoStatus.Information = 0;
IoMarkIrpPending(irp);

return STATUS_PENDING;

Inside a DPC function I try access the nonpaged memory of il with:

struct dev_info* di;
di = (struct dev_info*)device->DeviceExtension;

if(!IsListEmpty(&di->lh))
{
// code never reached
}

I know that a DPC can only read nonpaged memory, but why is !IsListEmpty always returning FALSE as if the insert failed?

johnye2e
  • 111
  • 9
  • Since I don't have the source-code for your driver, I don't know what happens between the `DriverWrite` and your DPC function. Is it possible that something has actually made the list empty in the steps between? – Mats Petersson Aug 25 '13 at 17:29
  • @PP. `IsListEmpty` returns true -> list empty. `! IsListEmpty` returns true -> list not empty. `! IsListEmpty` returns false -> list empty. – johnye2e Aug 25 '13 at 17:32
  • @MatsPetersson No, it's not. That list is "touched" only in DriverWrite and the DPC. I know for sure that "lh" keyword is present in my source code only at the lines I showed in the description (and in DriverEntry: `InitializeListHead(&di->lh);` I'm thinking that the reason for thie problem is that I can't access the nonpaged memory from the DPC... – johnye2e Aug 25 '13 at 17:39
  • 1
    Is the list properly locked? `InsertTailList` is not atomic. – avakar Aug 25 '13 at 17:55
  • 1
    No, if you access memory that isn't available, it would crash - and NP memory is fine to access ANYWHERE, that's the entire point of NP memory, it can't be paged out, so there is no problem with it being accessed where paging in memory could cause problems. – Mats Petersson Aug 25 '13 at 17:56
  • Also, you should probably not touch the irp after adding it into the list as you'll be racing between `IoMarkIrpPending` and whatever you do in the DPC. Mark it pending before you add it to the list. – avakar Aug 25 '13 at 17:59
  • @avakar Silly me :) the lock code lines were commented out... I'm using a DPC spinlock that's released from the DPC, and acquired in DriverWrite. And the sequence of execution just proves that `! IsListEmpty` was called before the actual insert. Thanks! @MatsPetersson Thanks for the details. – johnye2e Aug 25 '13 at 18:04
  • @avakar I'm just writing bytes to a port in the DPC, and then completing the IRP, so I don't think there are any racing issues. – johnye2e Aug 25 '13 at 19:14
  • 1
    Once the irp is in the list, another processor can pick it up (from within the DPC) and call `IoCompleteRequest`, at the same time the original thread is calling `IoMarkIrpPending`. That's where the race is. – avakar Aug 25 '13 at 19:33

1 Answers1

1

This may not be a proper answer, but it's a bit too complicated for a comment, so I'm writing it as an answer, for proper formatting, etc:

Reading the docs for InsertTailList:

VOID InsertTailList(
  _Inout_  PLIST_ENTRY ListHead,
  _Inout_  PLIST_ENTRY Entry
);

InsertTailList updates ListHead->Blink to point to Entry. It updates Entry->Blink to point to the old last entry in the list, and sets Entry->Flink to ListHead. The Flink of the previous last entry is updated to point to Entry as well.

Where IsListEmpty says:

IsListEmpty returns TRUE if there are currently no entries in the list and FALSE otherwise.

Remarks

IsListEmpty returns TRUE if ListHead->Flink refers back to ListHead.

Now, I'm not sure if I understand all of this, but to me, it seems like ListHead->Flink is not updated by InsertListTail (which seems rather strange). Although the sentence

The Flink of the previous last entry is updated to point to Entry as well.

may indicate that it does indeed update the head if it's the only thing in the list.

(Gah, just spotted the comment saying you've solved it).

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • I can't mark this as an answer because my problem was with the syncing between the DPC and the dispatch routine, but thanks for the explicit citing of the documentation. `InsertListTail` updates `Flink`, although. – johnye2e Aug 25 '13 at 19:12
  • @johnye2eÖ Yes, I came to that conclusion as well. – Mats Petersson Aug 25 '13 at 19:13