* Sync to trunk HEAD (r53473).
[reactos.git] / drivers / storage / classpnp / xferpkt.c
1 /*++
2
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4
5 Module Name:
6
7 xferpkt.c
8
9 Abstract:
10
11 Packet routines for CLASSPNP
12
13 Environment:
14
15 kernel mode only
16
17 Notes:
18
19
20 Revision History:
21
22 --*/
23
24 #include "classp.h"
25 #include "debug.h"
26
27 #ifdef ALLOC_PRAGMA
28 #pragma alloc_text(PAGE, InitializeTransferPackets)
29 #pragma alloc_text(PAGE, DestroyAllTransferPackets)
30 #pragma alloc_text(PAGE, SetupEjectionTransferPacket)
31 #pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
32 #endif
33
34
35 ULONG MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
36 ULONG MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
37
38
39 /*
40 * InitializeTransferPackets
41 *
42 * Allocate/initialize TRANSFER_PACKETs and related resources.
43 */
44 NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo)
45 {
46 PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
47 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
48 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
49 PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
50 ULONG hwMaxPages;
51 NTSTATUS status = STATUS_SUCCESS;
52
53 PAGED_CODE();
54
55 /*
56 * Precompute the maximum transfer length
57 */
58 ASSERT(adapterDesc->MaximumTransferLength);
59 ASSERT(adapterDesc->MaximumPhysicalPages);
60 hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0;
61
62 #if defined(_AMD64_SIMULATOR_)
63
64 //
65 // The simulator appears to have a problem with large transfers.
66 //
67
68 if (hwMaxPages > 4) {
69 hwMaxPages = 4;
70 }
71
72 #endif
73
74 fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT);
75 fdoData->HwMaxXferLen = MAX(fdoData->HwMaxXferLen, PAGE_SIZE);
76
77 fdoData->NumTotalTransferPackets = 0;
78 fdoData->NumFreeTransferPackets = 0;
79 InitializeSListHead(&fdoData->FreeTransferPacketsList);
80 InitializeListHead(&fdoData->AllTransferPacketsList);
81 InitializeListHead(&fdoData->DeferredClientIrpList);
82
83 /*
84 * Set the packet threshold numbers based on the Windows SKU.
85 */
86 if (ExVerifySuite(Personal)){
87 // this is Windows Personal
88 MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
89 MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
90 }
91 else if (ExVerifySuite(Enterprise) || ExVerifySuite(DataCenter)){
92 // this is Advanced Server or Datacenter
93 MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise;
94 MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise;
95 }
96 else if (ExVerifySuite(TerminalServer)){
97 // this is standard Server or Pro with terminal server
98 MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Server;
99 MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Server;
100 }
101 else {
102 // this is Professional without terminal server
103 MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
104 MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
105 }
106
107 while (fdoData->NumFreeTransferPackets < MIN_INITIAL_TRANSFER_PACKETS){
108 PTRANSFER_PACKET pkt = NewTransferPacket(Fdo);
109 if (pkt){
110 InterlockedIncrement(&fdoData->NumTotalTransferPackets);
111 EnqueueFreeTransferPacket(Fdo, pkt);
112 }
113 else {
114 status = STATUS_INSUFFICIENT_RESOURCES;
115 break;
116 }
117 }
118 fdoData->DbgPeakNumTransferPackets = fdoData->NumTotalTransferPackets;
119
120 /*
121 * Pre-initialize our SCSI_REQUEST_BLOCK template with all
122 * the constant fields. This will save a little time for each xfer.
123 * NOTE: a CdbLength field of 10 may not always be appropriate
124 */
125 RtlZeroMemory(&fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK));
126 fdoData->SrbTemplate.Length = sizeof(SCSI_REQUEST_BLOCK);
127 fdoData->SrbTemplate.Function = SRB_FUNCTION_EXECUTE_SCSI;
128 fdoData->SrbTemplate.QueueAction = SRB_SIMPLE_TAG_REQUEST;
129 fdoData->SrbTemplate.SenseInfoBufferLength = sizeof(SENSE_DATA);
130 fdoData->SrbTemplate.CdbLength = 10;
131
132 return status;
133 }
134
135
136 VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo)
137 {
138 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
139 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
140 TRANSFER_PACKET *pkt;
141
142 PAGED_CODE();
143
144 ASSERT(IsListEmpty(&fdoData->DeferredClientIrpList));
145
146 while (pkt = DequeueFreeTransferPacket(Fdo, FALSE)){
147 DestroyTransferPacket(pkt);
148 InterlockedDecrement(&fdoData->NumTotalTransferPackets);
149 }
150
151 ASSERT(fdoData->NumTotalTransferPackets == 0);
152 }
153
154
155 PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo)
156 {
157 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
158 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
159 PTRANSFER_PACKET newPkt;
160
161 newPkt = ExAllocatePoolWithTag(NonPagedPool, sizeof(TRANSFER_PACKET), 'pnPC');
162 if (newPkt){
163 RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET)); // just to be sure
164
165 /*
166 * Allocate resources for the packet.
167 */
168 newPkt->Irp = IoAllocateIrp(Fdo->StackSize, FALSE);
169 if (newPkt->Irp){
170 KIRQL oldIrql;
171
172 newPkt->Fdo = Fdo;
173
174 /*
175 * Enqueue the packet in our static AllTransferPacketsList
176 * (just so we can find it during debugging if its stuck somewhere).
177 */
178 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
179 InsertTailList(&fdoData->AllTransferPacketsList, &newPkt->AllPktsListEntry);
180 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
181 }
182 else {
183 ExFreePool(newPkt);
184 newPkt = NULL;
185 }
186 }
187
188 return newPkt;
189 }
190
191
192 /*
193 * DestroyTransferPacket
194 *
195 */
196 VOID DestroyTransferPacket(PTRANSFER_PACKET Pkt)
197 {
198 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
199 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
200 KIRQL oldIrql;
201
202 ASSERT(!Pkt->SlistEntry.Next);
203 ASSERT(!Pkt->OriginalIrp);
204
205 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
206
207 /*
208 * Delete the packet from our all-packets queue.
209 */
210 ASSERT(!IsListEmpty(&Pkt->AllPktsListEntry));
211 ASSERT(!IsListEmpty(&fdoData->AllTransferPacketsList));
212 RemoveEntryList(&Pkt->AllPktsListEntry);
213 InitializeListHead(&Pkt->AllPktsListEntry);
214
215 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
216
217 IoFreeIrp(Pkt->Irp);
218 ExFreePool(Pkt);
219 }
220
221
222 VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, PTRANSFER_PACKET Pkt)
223 {
224 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
225 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
226 KIRQL oldIrql;
227 ULONG newNumPkts;
228
229 ASSERT(!Pkt->SlistEntry.Next);
230
231 InterlockedPushEntrySList(&fdoData->FreeTransferPacketsList, &Pkt->SlistEntry);
232 newNumPkts = InterlockedIncrement(&fdoData->NumFreeTransferPackets);
233 ASSERT(newNumPkts <= fdoData->NumTotalTransferPackets);
234
235 /*
236 * If the total number of packets is larger than MinWorkingSetTransferPackets,
237 * that means that we've been in stress. If all those packets are now
238 * free, then we are now out of stress and can free the extra packets.
239 * Free down to MaxWorkingSetTransferPackets immediately, and
240 * down to MinWorkingSetTransferPackets lazily (one at a time).
241 */
242 if (fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets){
243
244 /*
245 * 1. Immediately snap down to our UPPER threshold.
246 */
247 if (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets){
248 SINGLE_LIST_ENTRY pktList;
249 PSINGLE_LIST_ENTRY slistEntry;
250 PTRANSFER_PACKET pktToDelete;
251
252 DBGTRACE(ClassDebugTrace, ("Exiting stress, block freeing (%d-%d) packets.", fdoData->NumTotalTransferPackets, MaxWorkingSetTransferPackets));
253
254 /*
255 * Check the counter again with lock held. This eliminates a race condition
256 * while still allowing us to not grab the spinlock in the common codepath.
257 *
258 * Note that the spinlock does not synchronize with threads dequeuing free
259 * packets to send (DequeueFreeTransferPacket does that with a lightweight
260 * interlocked exchange); the spinlock prevents multiple threads in this function
261 * from deciding to free too many extra packets at once.
262 */
263 SimpleInitSlistHdr(&pktList);
264 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
265 while ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) &&
266 (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets)){
267
268 pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE);
269 if (pktToDelete){
270 SimplePushSlist(&pktList, &pktToDelete->SlistEntry);
271 InterlockedDecrement(&fdoData->NumTotalTransferPackets);
272 }
273 else {
274 DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1).", MaxWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets));
275 break;
276 }
277 }
278 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
279
280 while (slistEntry = SimplePopSlist(&pktList)){
281 pktToDelete = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
282 DestroyTransferPacket(pktToDelete);
283 }
284
285 }
286
287 /*
288 * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time).
289 */
290 if (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets){
291 /*
292 * Check the counter again with lock held. This eliminates a race condition
293 * while still allowing us to not grab the spinlock in the common codepath.
294 *
295 * Note that the spinlock does not synchronize with threads dequeuing free
296 * packets to send (DequeueFreeTransferPacket does that with a lightweight
297 * interlocked exchange); the spinlock prevents multiple threads in this function
298 * from deciding to free too many extra packets at once.
299 */
300 PTRANSFER_PACKET pktToDelete = NULL;
301
302 DBGTRACE(ClassDebugTrace, ("Exiting stress, lazily freeing one of %d/%d packets.", fdoData->NumTotalTransferPackets, MinWorkingSetTransferPackets));
303
304 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
305 if ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) &&
306 (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets)){
307
308 pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE);
309 if (pktToDelete){
310 InterlockedDecrement(&fdoData->NumTotalTransferPackets);
311 }
312 else {
313 DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2).", MinWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets));
314 }
315 }
316 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
317
318 if (pktToDelete){
319 DestroyTransferPacket(pktToDelete);
320 }
321 }
322
323 }
324
325 }
326
327
328 PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded)
329 {
330 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
331 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
332 PTRANSFER_PACKET pkt;
333 PSINGLE_LIST_ENTRY slistEntry;
334 KIRQL oldIrql;
335
336 slistEntry = InterlockedPopEntrySList(&fdoData->FreeTransferPacketsList);
337 if (slistEntry){
338 slistEntry->Next = NULL;
339 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
340 ASSERT(fdoData->NumFreeTransferPackets > 0);
341 InterlockedDecrement(&fdoData->NumFreeTransferPackets);
342 }
343 else {
344 if (AllocIfNeeded){
345 /*
346 * We are in stress and have run out of lookaside packets.
347 * In order to service the current transfer,
348 * allocate an extra packet.
349 * We will free it lazily when we are out of stress.
350 */
351 pkt = NewTransferPacket(Fdo);
352 if (pkt){
353 InterlockedIncrement(&fdoData->NumTotalTransferPackets);
354 fdoData->DbgPeakNumTransferPackets = max(fdoData->DbgPeakNumTransferPackets, fdoData->NumTotalTransferPackets);
355 }
356 else {
357 DBGWARN(("DequeueFreeTransferPacket: packet allocation failed"));
358 }
359 }
360 else {
361 pkt = NULL;
362 }
363 }
364
365 return pkt;
366 }
367
368
369
370 /*
371 * SetupReadWriteTransferPacket
372 *
373 * This function is called once to set up the first attempt to send a packet.
374 * It is not called before a retry, as SRB fields may be modified for the retry.
375 *
376 * Set up the Srb of the TRANSFER_PACKET for the transfer.
377 * The Irp is set up in SubmitTransferPacket because it must be reset
378 * for each packet submission.
379 */
380 VOID SetupReadWriteTransferPacket( PTRANSFER_PACKET Pkt,
381 PVOID Buf,
382 ULONG Len,
383 LARGE_INTEGER DiskLocation,
384 PIRP OriginalIrp)
385 {
386 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
387 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
388 PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(OriginalIrp);
389 UCHAR majorFunc = origCurSp->MajorFunction;
390 ULONG logicalBlockAddr;
391 ULONG numTransferBlocks;
392 PCDB pCdb;
393
394 logicalBlockAddr = (ULONG)Int64ShrlMod32(DiskLocation.QuadPart, fdoExt->SectorShift);
395 numTransferBlocks = Len >> fdoExt->SectorShift;
396
397 /*
398 * Slap the constant SRB fields in from our pre-initialized template.
399 * We'll then only have to fill in the unique fields for this transfer.
400 * Tell lower drivers to sort the SRBs by the logical block address
401 * so that disk seeks are minimized.
402 */
403 Pkt->Srb = fdoData->SrbTemplate; // copies _contents_ of SRB blocks
404 Pkt->Srb.DataBuffer = Buf;
405 Pkt->Srb.DataTransferLength = Len;
406 Pkt->Srb.QueueSortKey = logicalBlockAddr;
407 Pkt->Srb.OriginalRequest = Pkt->Irp;
408 Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
409 Pkt->Srb.TimeOutValue = (Len/0x10000) + ((Len%0x10000) ? 1 : 0);
410 Pkt->Srb.TimeOutValue *= fdoExt->TimeOutValue;
411
412 /*
413 * Arrange values in CDB in big-endian format.
414 */
415 pCdb = (PCDB)Pkt->Srb.Cdb;
416 pCdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte3;
417 pCdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte2;
418 pCdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte1;
419 pCdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte0;
420 pCdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
421 pCdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
422 pCdb->CDB10.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ : SCSIOP_WRITE;
423
424 /*
425 * Set SRB and IRP flags
426 */
427 Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
428 if (TEST_FLAG(OriginalIrp->Flags, IRP_PAGING_IO) ||
429 TEST_FLAG(OriginalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO)){
430 SET_FLAG(Pkt->Srb.SrbFlags, SRB_CLASS_FLAGS_PAGING);
431 }
432 SET_FLAG(Pkt->Srb.SrbFlags, (majorFunc==IRP_MJ_READ) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
433
434 /*
435 * Allow caching only if this is not a write-through request.
436 * If write-through and caching is enabled on the device, force
437 * media access.
438 */
439 if (TEST_FLAG(origCurSp->Flags, SL_WRITE_THROUGH)){
440 if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE)){
441 pCdb->CDB10.ForceUnitAccess = TRUE;
442 }
443 }
444 else {
445 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
446 }
447
448 /*
449 * Remember the buf and len in the SRB because miniports
450 * can overwrite SRB.DataTransferLength and we may need it again
451 * for the retry.
452 */
453 Pkt->BufPtrCopy = Buf;
454 Pkt->BufLenCopy = Len;
455 Pkt->TargetLocationCopy = DiskLocation;
456
457 Pkt->OriginalIrp = OriginalIrp;
458 Pkt->NumRetries = MAXIMUM_RETRIES;
459 Pkt->SyncEventPtr = NULL;
460 Pkt->CompleteOriginalIrpWhenLastPacketCompletes = TRUE;
461 }
462
463
464 /*
465 * SubmitTransferPacket
466 *
467 * Set up the IRP for the TRANSFER_PACKET submission and send it down.
468 */
469 VOID SubmitTransferPacket(PTRANSFER_PACKET Pkt)
470 {
471 PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
472 PDEVICE_OBJECT nextDevObj = commonExtension->LowerDeviceObject;
473 PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Pkt->Irp);
474
475 ASSERT(Pkt->Irp->CurrentLocation == Pkt->Irp->StackCount+1);
476
477 /*
478 * Attach the SRB to the IRP.
479 * The reused IRP's stack location has to be rewritten for each retry
480 * call because IoCompleteRequest clears the stack locations.
481 */
482 IoReuseIrp(Pkt->Irp, STATUS_NOT_SUPPORTED);
483 nextSp->MajorFunction = IRP_MJ_SCSI;
484 nextSp->Parameters.Scsi.Srb = &Pkt->Srb;
485 Pkt->Srb.ScsiStatus = Pkt->Srb.SrbStatus = 0;
486 if (Pkt->CompleteOriginalIrpWhenLastPacketCompletes){
487 /*
488 * Only dereference the "original IRP"'s stack location
489 * if its a real client irp (as opposed to a static irp
490 * we're using just for result status for one of the non-IO scsi commands).
491 *
492 * For read/write, propagate the storage-specific IRP stack location flags
493 * (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH).
494 */
495 PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
496 nextSp->Flags = origCurSp->Flags;
497 }
498
499 /*
500 * Write MDL address to new IRP. In the port driver the SRB DataBuffer
501 * field is used as the actual buffer pointer within the MDL,
502 * so the same MDL can be used for each partial transfer.
503 * This saves having to build a new MDL for each partial transfer.
504 */
505 Pkt->Irp->MdlAddress = Pkt->OriginalIrp->MdlAddress;
506
507 IoSetCompletionRoutine(Pkt->Irp, TransferPktComplete, Pkt, TRUE, TRUE, TRUE);
508 IoCallDriver(nextDevObj, Pkt->Irp);
509 }
510
511
512 NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context)
513 {
514 PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context;
515 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension;
516 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
517 PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(pkt->OriginalIrp);
518 BOOLEAN packetDone = FALSE;
519
520 /*
521 * Put all the assertions and spew in here so we don't have to look at them.
522 */
523 DBGCHECKRETURNEDPKT(pkt);
524
525 if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){
526
527 fdoData->LoggedTURFailureSinceLastIO = FALSE;
528
529 /*
530 * The port driver should not have allocated a sense buffer
531 * if the SRB succeeded.
532 */
533 ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb));
534
535 /*
536 * Add this packet's transferred length to the original IRP's.
537 */
538 InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information,
539 (LONG)pkt->Srb.DataTransferLength);
540
541 if (pkt->InLowMemRetry){
542 packetDone = StepLowMemRetry(pkt);
543 }
544 else {
545 packetDone = TRUE;
546 }
547
548 }
549 else {
550 /*
551 * The packet failed. We may retry it if possible.
552 */
553 BOOLEAN shouldRetry;
554
555 /*
556 * Make sure IRP status matches SRB error status (since we propagate it).
557 */
558 if (NT_SUCCESS(Irp->IoStatus.Status)){
559 Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
560 }
561
562 /*
563 * Interpret the SRB error (to a meaningful IRP status)
564 * and determine if we should retry this packet.
565 * This call looks at the returned SENSE info to figure out what to do.
566 */
567 shouldRetry = InterpretTransferPacketError(pkt);
568
569 /*
570 * Sometimes the port driver can allocates a new 'sense' buffer
571 * to report transfer errors, e.g. when the default sense buffer
572 * is too small. If so, it is up to us to free it.
573 * Now that we're done interpreting the sense info, free it if appropriate.
574 */
575 if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) {
576 DBGTRACE(ClassDebugSenseInfo, ("Freeing port-allocated sense buffer for pkt %ph.", pkt));
577 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb);
578 pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData;
579 pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
580 }
581
582 /*
583 * If the SRB queue is locked-up, release it.
584 * Do this after calling the error handler.
585 */
586 if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){
587 ClassReleaseQueue(pkt->Fdo);
588 }
589
590 if (shouldRetry && (pkt->NumRetries > 0)){
591 packetDone = RetryTransferPacket(pkt);
592 }
593 else {
594 packetDone = TRUE;
595 }
596
597 }
598
599 /*
600 * If the packet is completed, put it back in the free list.
601 * If it is the last packet servicing the original request, complete the original irp.
602 */
603 if (packetDone){
604 LONG numPacketsRemaining;
605 PIRP deferredIrp;
606 PDEVICE_OBJECT Fdo = pkt->Fdo;
607 UCHAR uniqueAddr;
608
609 /*
610 * In case a remove is pending, bump the lock count so we don't get freed
611 * right after we complete the original irp.
612 */
613 ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr);
614
615 /*
616 * The original IRP should get an error code
617 * if any one of the packets failed.
618 */
619 if (!NT_SUCCESS(Irp->IoStatus.Status)){
620 pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status;
621
622 /*
623 * If the original I/O originated in user space (i.e. it is thread-queued),
624 * and the error is user-correctable (e.g. media is missing, for removable media),
625 * alert the user.
626 * Since this is only one of possibly several packets completing for the original IRP,
627 * we may do this more than once for a single request. That's ok; this allows
628 * us to test each returned status with IoIsErrorUserInduced().
629 */
630 if (IoIsErrorUserInduced(Irp->IoStatus.Status) &&
631 pkt->CompleteOriginalIrpWhenLastPacketCompletes &&
632 pkt->OriginalIrp->Tail.Overlay.Thread){
633
634 IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, pkt->Fdo);
635 }
636 }
637
638 /*
639 * We use a field in the original IRP to count
640 * down the transfer pieces as they complete.
641 */
642 numPacketsRemaining = InterlockedDecrement(
643 (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]);
644
645 if (numPacketsRemaining > 0){
646 /*
647 * More transfer pieces remain for the original request.
648 * Wait for them to complete before completing the original irp.
649 */
650 }
651 else {
652
653 /*
654 * All the transfer pieces are done.
655 * Complete the original irp if appropriate.
656 */
657 ASSERT(numPacketsRemaining == 0);
658 if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){
659 if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){
660 ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == origCurrentSp->Parameters.Read.Length);
661 ClasspPerfIncrementSuccessfulIo(fdoExt);
662 }
663 ClassReleaseRemoveLock(pkt->Fdo, pkt->OriginalIrp);
664
665 ClassCompleteRequest(pkt->Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT);
666
667 /*
668 * We may have been called by one of the class drivers (e.g. cdrom)
669 * via the legacy API ClassSplitRequest.
670 * This is the only case for which the packet engine is called for an FDO
671 * with a StartIo routine; in that case, we have to call IoStartNextPacket
672 * now that the original irp has been completed.
673 */
674 if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) {
675 if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){
676 DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)"));
677 }
678 else {
679 KIRQL oldIrql;
680 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
681 IoStartNextPacket(pkt->Fdo, FALSE);
682 KeLowerIrql(oldIrql);
683 }
684 }
685 }
686 }
687
688 /*
689 * If the packet was synchronous, write the final
690 * result back to the issuer's status buffer and
691 * signal his event.
692 */
693 if (pkt->SyncEventPtr){
694 KeSetEvent(pkt->SyncEventPtr, 0, FALSE);
695 pkt->SyncEventPtr = NULL;
696 }
697
698 /*
699 * Free the completed packet.
700 */
701 pkt->OriginalIrp = NULL;
702 pkt->InLowMemRetry = FALSE;
703 EnqueueFreeTransferPacket(pkt->Fdo, pkt);
704
705 /*
706 * Now that we have freed some resources,
707 * try again to send one of the previously deferred irps.
708 */
709 deferredIrp = DequeueDeferredClientIrp(fdoData);
710 if (deferredIrp){
711 DBGWARN(("... retrying deferred irp %xh.", deferredIrp));
712 ServiceTransferRequest(pkt->Fdo, deferredIrp);
713 }
714
715 ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr);
716 }
717
718 return STATUS_MORE_PROCESSING_REQUIRED;
719 }
720
721
722 /*
723 * SetupEjectionTransferPacket
724 *
725 * Set up a transferPacket for a synchronous Ejection Control transfer.
726 */
727 VOID SetupEjectionTransferPacket( TRANSFER_PACKET *Pkt,
728 BOOLEAN PreventMediaRemoval,
729 PKEVENT SyncEventPtr,
730 PIRP OriginalIrp)
731 {
732 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
733 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
734 PCDB pCdb;
735
736 PAGED_CODE();
737
738 RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
739
740 Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
741 Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
742 Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
743 Pkt->Srb.CdbLength = 6;
744 Pkt->Srb.OriginalRequest = Pkt->Irp;
745 Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
746 Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
747 Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue;
748
749 Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
750 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
751 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
752
753 pCdb = (PCDB)Pkt->Srb.Cdb;
754 pCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
755 pCdb->MEDIA_REMOVAL.Prevent = PreventMediaRemoval;
756
757 Pkt->BufPtrCopy = NULL;
758 Pkt->BufLenCopy = 0;
759
760 Pkt->OriginalIrp = OriginalIrp;
761 Pkt->NumRetries = NUM_LOCKMEDIAREMOVAL_RETRIES;
762 Pkt->SyncEventPtr = SyncEventPtr;
763 Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
764 }
765
766
767 /*
768 * SetupModeSenseTransferPacket
769 *
770 * Set up a transferPacket for a synchronous Mode Sense transfer.
771 */
772 VOID SetupModeSenseTransferPacket( TRANSFER_PACKET *Pkt,
773 PKEVENT SyncEventPtr,
774 PVOID ModeSenseBuffer,
775 UCHAR ModeSenseBufferLen,
776 UCHAR PageMode,
777 PIRP OriginalIrp)
778 {
779 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
780 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
781 PCDB pCdb;
782
783 PAGED_CODE();
784
785 RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
786
787 Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
788 Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
789 Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
790 Pkt->Srb.CdbLength = 6;
791 Pkt->Srb.OriginalRequest = Pkt->Irp;
792 Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
793 Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
794 Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue;
795 Pkt->Srb.DataBuffer = ModeSenseBuffer;
796 Pkt->Srb.DataTransferLength = ModeSenseBufferLen;
797
798 Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
799 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DATA_IN);
800 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
801 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
802
803 pCdb = (PCDB)Pkt->Srb.Cdb;
804 pCdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
805 pCdb->MODE_SENSE.PageCode = PageMode;
806 pCdb->MODE_SENSE.AllocationLength = (UCHAR)ModeSenseBufferLen;
807
808 Pkt->BufPtrCopy = ModeSenseBuffer;
809 Pkt->BufLenCopy = ModeSenseBufferLen;
810
811 Pkt->OriginalIrp = OriginalIrp;
812 Pkt->NumRetries = NUM_MODESENSE_RETRIES;
813 Pkt->SyncEventPtr = SyncEventPtr;
814 Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
815 }
816
817
818 /*
819 * SetupDriveCapacityTransferPacket
820 *
821 * Set up a transferPacket for a synchronous Drive Capacity transfer.
822 */
823 VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt,
824 PVOID ReadCapacityBuffer,
825 ULONG ReadCapacityBufferLen,
826 PKEVENT SyncEventPtr,
827 PIRP OriginalIrp)
828 {
829 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
830 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
831 PCDB pCdb;
832
833 RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
834
835 Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
836 Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
837 Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
838 Pkt->Srb.CdbLength = 10;
839 Pkt->Srb.OriginalRequest = Pkt->Irp;
840 Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
841 Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
842 Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue;
843 Pkt->Srb.DataBuffer = ReadCapacityBuffer;
844 Pkt->Srb.DataTransferLength = ReadCapacityBufferLen;
845
846 Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
847 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DATA_IN);
848 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
849 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
850
851 pCdb = (PCDB)Pkt->Srb.Cdb;
852 pCdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
853
854 Pkt->BufPtrCopy = ReadCapacityBuffer;
855 Pkt->BufLenCopy = ReadCapacityBufferLen;
856
857 Pkt->OriginalIrp = OriginalIrp;
858 Pkt->NumRetries = NUM_DRIVECAPACITY_RETRIES;
859 Pkt->SyncEventPtr = SyncEventPtr;
860 Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
861 }
862
863
864 #if 0
865 /*
866 * SetupSendStartUnitTransferPacket
867 *
868 * Set up a transferPacket for a synchronous Send Start Unit transfer.
869 */
870 VOID SetupSendStartUnitTransferPacket( TRANSFER_PACKET *Pkt,
871 PIRP OriginalIrp)
872 {
873 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
874 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
875 PCDB pCdb;
876
877 PAGED_CODE();
878
879 RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
880
881 /*
882 * Initialize the SRB.
883 * Use a very long timeout value to give the drive time to spin up.
884 */
885 Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
886 Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
887 Pkt->Srb.TimeOutValue = START_UNIT_TIMEOUT;
888 Pkt->Srb.CdbLength = 6;
889 Pkt->Srb.OriginalRequest = Pkt->Irp;
890 Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
891 Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
892 Pkt->Srb.Lun = 0;
893
894 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
895 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
896 SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
897
898 pCdb = (PCDB)Pkt->Srb.Cdb;
899 pCdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
900 pCdb->START_STOP.Start = 1;
901 pCdb->START_STOP.Immediate = 0;
902 pCdb->START_STOP.LogicalUnitNumber = 0;
903
904 Pkt->OriginalIrp = OriginalIrp;
905 Pkt->NumRetries = 0;
906 Pkt->SyncEventPtr = NULL;
907 Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
908 }
909 #endif
910
911