Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

910 lines
33 KiB

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