See below…
-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of disney_cheng@qq.com
Sent: Sunday, December 19, 2010 12:46 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] A crash after copying data to a buffer getting from an
MDL
Hi, Joe
Thanks for your answer.
The A(), B(), C() can concrete describe as:
A(): Entry of the IRP handling routine, get the virtual address of the MDL
provided by the IRP. Now is at Dispatch Level.
B(): Sending and receiving datas using TDI. Now is at Passive Level.
C(): Completing the IRP. It has to be at Dispatch Level.
************************************************************
No, that was not the point of what I was saying, and you seem to have missed
it completely. You were asking about doing things at passive level from
dispatch level, and I was referring to a sequence that required A, B and C
be executed sequentially, starting with A at dispatch level. Instead, you
make a completely different example. If A() is, as you describe, the
top-level dispatch routine, then indeed it is at passive level, but you
cannot then say “now is at dispatch level”. There’s nothing you’ve
suggested about the code that would suddenly place it at DISPATCH_LEVEL.
You have said B() is sending and receiving data using TDI, but I’m not a
network driver expert, so I have no idea what the requirements are at that
level. Is it running at PASSIVE_LEVEL or DISPATCH_LEVEL? And you cannot
just wave your hands and say “now is at passive level”. You would indicate
that the call to B() is made from PASSIVE_LEVEL. Or, if the call is made a
DISPATCH_LEVEL, you would indicate this, and it is not clear what level you
start at, nor is it clear why you magically have ended up at DISPATCH_LEVEL.
You say C() completes the IRP has to be at DISPATHC_LEVEL. Actually, this
is not true; you can complete an IRP and anything < DIRQL level. You have
not indicated why you are currently at DISPATCH_LEVEL. Of course, it may be
that in the networking components, it is the nature of the completion
routine that it is actually running at DISPATCH_LEVEL (it may be that in the
networking world this is true, but in that case, you have not provided any
motivation as to why you need to go to PASSIVE_LEVEL). So I’m now unclear
as to what you are asking.
Bottom line: the problem statement is at best incoherent. Clarify what the
question is.
And when reading an example, try not to change it into some other example
and try to demonstrate equivalence.
***********************************************************
The solution you said below is really true of the IRQL requirement:
A(); Enqueue work item for X();
X() { KSPIN_LOCK L;
KIRQL irql;
B();
KeInitializeSpinLock(&L);
KeAcquireSpinLock(&L, &irql);
C();
KeReleaseSpinLock(&L, irdl);
}
The IRP will be completed at Dispatch Level. But we don’t know what happen
in the system during the B() part, since it’s at Passive Level. Maybe
several preemptions occur.
****
So it is preempted. Why does this matter? And what relationship is there
between the context in which the IRP is completed, and my hypothesized B()
segment of code?
****
Now I see that the point is B(). Whatever solution was used, it’s lowering
the IRQL between the process of a Dispatch Level IRP. Maybe I should try to
make B() work at Dispatch Level by using other interface functions instead
of TdiBuildInternalDeviceControlIrp() and KeWaitForSingleObject().
****
The key about using the worker thread is that you are NOT lowering the IRQL
of the thread running at DISPATCH_LEVEL. You are deferring the processing
to a PASSIVE_LEVEL thread, which executes in a completely different context.
So the requirements of DISPATCH_LEVEL are not violated by lowering *that*
thread’s IRQL. Note that if B() is supposed to be running at PASSIVE_LEVEL,
it also means that it assumes that it is entitled to block, or deschedule
itself in any way imaginable, and if you raise the IRQL level, you violate
*that thread’s* requirements.
Generally, IRQL levels are sacred. If you raise the IRQL level (e.g., using
the spinlock trick) then you must restore it, and during the time it is
raised, you may not violate any DISPATCH_LEVEL constraints. You should
assume that it is always unsafe to lower IRQL of a thread that expects to be
at DISPATCH_LEVEL, because it is making assumptions about not being
preempted (for example, in my above example, it would be really, REALLY bad
in C() to lower the IRQL if the spin lock was really locking something that
was potentially shared).
So we’re back to the problem of not understanding what question you are
asking. Also, across this same subject, you have asked two different
questions, and it is not clear what context you are interpreting this answer
in.
joe
*****
-----Original Message-----
You’re still trying to think sequentially. As long as you think
sequentially in an asynchronous event-driven system you are doomed. Your
failure is thinking the right thing to to do now is write
A(); Enqueue work item for B(); Wait for workitem to complete; C();
Which is just your old sequentiality problem all over again! You have
solved nothing.
Supposed you had to do A(); B(); C();
You discover you are at DISPATCH_LEVEL but B() must be called at
PASSIVE_LEVEL
There are several approaches. The most commone one is to study what C() is
doing and whether or not it REQUIRES that you be at DISPATCH_LEVEL. If not,
you can do
A(); Enqueue work item for X();
X() { B(); C(); }
But you really, really have to understand what the code is, and stop thiking
that you must do it in the syntactic sequence
A(); B(); C();
If C() *must* be executed at DISPATCH_LEVEL, I might code it as either of
these: (and others may tell me this is wrong, so wait for replies)
A(); Enqueue work item for X();
X() { B(); Requst a DPC for Y();}
Y() { C(); }
**** [Note this is a real cheat]
A(); Enqueue work item for X();
X() { KSPIN_LOCK L;
KIRQL irql;
B();
KeInitializeSpinLock(&L);
KeAcquireSpinLock(&L, &irql);
C();
KeReleaseSpinLock(&L, irdl);
}
Key here is making sure correct context is maintained across all these
calls. Again, a major failure is teaching that context is kept in local
variables, and this won’t work in the example above. You have to allocate
heap-based packets to hold state. But it is all based on an asynchronous
event-driven model.
Note that anything computed in A() but required in B() must be preserved so
it can be used in B(). This can be done in the device extension (if you can
only have a maximum of one transaction of this nature at a time) or in the
heap, which is probably the preferable approach. Similarly, anything
computed in A() and used in C() must be passed into the execution context of
C().
Of course, if you require that A() and C() execute in the same
DISPATCH_LEVEL thread and no context swap is permitted between them, you are
doomed. This won’t ever happen. As soon as you queue up that passive
thread request, you are done with your current context, and you will never
again be in that context.
Since we don’t have a good picture of everything you are trying to do, it is
not clear what to advise you to do. But you need to carefully think out the
contexts required.
joe
NTDEV is sponsored by OSR
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
–
This message has been scanned for viruses and dangerous content by
MailScanner, and is believed to be clean.