Leaked source code of windows server 2003
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.

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