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