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.

348 lines
12 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1991 - 1999
  3. Module Name:
  4. retry.c
  5. Abstract:
  6. Packet retry routines for CLASSPNP
  7. Environment:
  8. kernel mode only
  9. Notes:
  10. Revision History:
  11. --*/
  12. #include "classp.h"
  13. #include "debug.h"
  14. /*
  15. * InterpretTransferPacketError
  16. *
  17. * Interpret the SRB error into a meaningful IRP status.
  18. * ClassInterpretSenseInfo also may modify the SRB for the retry.
  19. *
  20. * Return TRUE iff packet should be retried.
  21. */
  22. BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
  23. {
  24. BOOLEAN shouldRetry = FALSE;
  25. PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
  26. /*
  27. * Interpret the error using the returned sense info first.
  28. */
  29. Pkt->RetryIntervalSec = 0;
  30. if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){
  31. /*
  32. * This is an Ejection Control SRB. Interpret its sense info specially.
  33. */
  34. shouldRetry = ClassInterpretSenseInfo(
  35. Pkt->Fdo,
  36. &Pkt->Srb,
  37. IRP_MJ_SCSI,
  38. 0,
  39. MAXIMUM_RETRIES - Pkt->NumRetries,
  40. &Pkt->Irp->IoStatus.Status,
  41. &Pkt->RetryIntervalSec);
  42. if (shouldRetry){
  43. /*
  44. * If the device is not ready, wait at least 2 seconds before retrying.
  45. */
  46. PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer;
  47. ASSERT(senseInfoBuffer);
  48. if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
  49. (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) ||
  50. (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){
  51. Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
  52. }
  53. }
  54. }
  55. else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
  56. (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){
  57. /*
  58. * This is an Mode Sense SRB. Interpret its sense info specially.
  59. */
  60. shouldRetry = ClassInterpretSenseInfo(
  61. Pkt->Fdo,
  62. &Pkt->Srb,
  63. IRP_MJ_SCSI,
  64. 0,
  65. MAXIMUM_RETRIES - Pkt->NumRetries,
  66. &Pkt->Irp->IoStatus.Status,
  67. &Pkt->RetryIntervalSec);
  68. if (shouldRetry){
  69. /*
  70. * If the device is not ready, wait at least 2 seconds before retrying.
  71. */
  72. PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer;
  73. ASSERT(senseInfoBuffer);
  74. if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
  75. (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) ||
  76. (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){
  77. Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
  78. }
  79. }
  80. /*
  81. * Some special cases for mode sense.
  82. */
  83. if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
  84. shouldRetry = TRUE;
  85. }
  86. else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN){
  87. /*
  88. * This is a HACK.
  89. * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
  90. * underrun (i.e. success, and the buffer is longer than needed).
  91. * So treat this as a success.
  92. */
  93. Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
  94. InterlockedExchangeAdd((PLONG)&Pkt->OriginalIrp->IoStatus.Information, (LONG)Pkt->Srb.DataTransferLength);
  95. shouldRetry = FALSE;
  96. }
  97. }
  98. else if (pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY){
  99. /*
  100. * This is a Drive Capacity SRB. Interpret its sense info specially.
  101. */
  102. shouldRetry = ClassInterpretSenseInfo(
  103. Pkt->Fdo,
  104. &Pkt->Srb,
  105. IRP_MJ_SCSI,
  106. 0,
  107. MAXIMUM_RETRIES - Pkt->NumRetries,
  108. &Pkt->Irp->IoStatus.Status,
  109. &Pkt->RetryIntervalSec);
  110. if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
  111. shouldRetry = TRUE;
  112. }
  113. }
  114. else if ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
  115. (pCdb->CDB10.OperationCode == SCSIOP_WRITE)){
  116. /*
  117. * This is a Read/Write Data packet.
  118. */
  119. PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
  120. shouldRetry = ClassInterpretSenseInfo(
  121. Pkt->Fdo,
  122. &Pkt->Srb,
  123. origCurrentSp->MajorFunction,
  124. 0,
  125. MAXIMUM_RETRIES - Pkt->NumRetries,
  126. &Pkt->Irp->IoStatus.Status,
  127. &Pkt->RetryIntervalSec);
  128. /*
  129. * Deal with some special cases.
  130. */
  131. if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
  132. /*
  133. * We are in extreme low-memory stress.
  134. * We will retry in smaller chunks.
  135. */
  136. shouldRetry = TRUE;
  137. }
  138. else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
  139. (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){
  140. /*
  141. * We are still verifying a (possibly) reloaded disk/cdrom.
  142. * So retry the request.
  143. */
  144. Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
  145. shouldRetry = TRUE;
  146. }
  147. }
  148. else {
  149. DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
  150. }
  151. return shouldRetry;
  152. }
  153. /*
  154. * RetryTransferPacket
  155. *
  156. * Retry sending a TRANSFER_PACKET.
  157. *
  158. * Return TRUE iff the packet is complete.
  159. * (if so the status in pkt->irp is the final status).
  160. */
  161. BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
  162. {
  163. BOOLEAN packetDone;
  164. DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb)));
  165. ASSERT(Pkt->NumRetries > 0);
  166. Pkt->NumRetries--;
  167. /*
  168. * Tone down performance on the retry.
  169. * This increases the chance for success on the retry.
  170. * We've seen instances of drives that fail consistently but then start working
  171. * once this scale-down is applied.
  172. */
  173. SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
  174. SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
  175. CLEAR_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
  176. Pkt->Srb.QueueTag = SP_UNTAGGED;
  177. if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
  178. PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
  179. BOOLEAN isReadWrite = ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
  180. (pCdb->CDB10.OperationCode == SCSIOP_WRITE));
  181. if (Pkt->InLowMemRetry || !isReadWrite){
  182. /*
  183. * This should never happen.
  184. * The memory manager guarantees that at least four pages will
  185. * be available to allow forward progress in the port driver.
  186. * So a one-page transfer should never fail with insufficient resources.
  187. */
  188. ASSERT(isReadWrite && !Pkt->InLowMemRetry);
  189. packetDone = TRUE;
  190. }
  191. else {
  192. /*
  193. * We are in low-memory stress.
  194. * Start the low-memory retry state machine, which tries to
  195. * resend the packet in little one-page chunks.
  196. */
  197. InitLowMemRetry( Pkt,
  198. Pkt->BufPtrCopy,
  199. Pkt->BufLenCopy,
  200. Pkt->TargetLocationCopy);
  201. StepLowMemRetry(Pkt);
  202. packetDone = FALSE;
  203. }
  204. }
  205. else {
  206. /*
  207. * Retry the packet by simply resending it after a delay.
  208. * Put the packet back in the pending queue and
  209. * schedule a timer to retry the transfer.
  210. *
  211. * Do not call SetupReadWriteTransferPacket again because:
  212. * (1) The minidriver may have set some bits
  213. * in the SRB that it needs again and
  214. * (2) doing so would reset numRetries.
  215. *
  216. * BECAUSE we do not call SetupReadWriteTransferPacket again,
  217. * we have to reset a couple fields in the SRB that
  218. * some miniports overwrite when they fail an SRB.
  219. */
  220. Pkt->Srb.DataBuffer = Pkt->BufPtrCopy;
  221. Pkt->Srb.DataTransferLength = Pkt->BufLenCopy;
  222. if (Pkt->RetryIntervalSec == 0){
  223. /*
  224. * Always delay by at least a little when retrying.
  225. * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
  226. */
  227. LARGE_INTEGER timerPeriod;
  228. timerPeriod.HighPart = -1;
  229. timerPeriod.LowPart = -(LONG)((ULONG)MINIMUM_RETRY_UNITS*KeQueryTimeIncrement());
  230. KeInitializeTimer(&Pkt->RetryTimer);
  231. KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
  232. KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
  233. }
  234. else {
  235. LARGE_INTEGER timerPeriod;
  236. ASSERT(Pkt->RetryIntervalSec < 100); // sanity check
  237. timerPeriod.HighPart = -1;
  238. timerPeriod.LowPart = Pkt->RetryIntervalSec*-10000000;
  239. KeInitializeTimer(&Pkt->RetryTimer);
  240. KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
  241. KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
  242. }
  243. packetDone = FALSE;
  244. }
  245. return packetDone;
  246. }
  247. VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc,
  248. IN PVOID DeferredContext,
  249. IN PVOID SystemArgument1,
  250. IN PVOID SystemArgument2)
  251. {
  252. PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext;
  253. SubmitTransferPacket(pkt);
  254. }
  255. VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
  256. {
  257. ASSERT(Len > 0);
  258. ASSERT(!Pkt->InLowMemRetry);
  259. Pkt->InLowMemRetry = TRUE;
  260. Pkt->LowMemRetry_remainingBufPtr = BufPtr;
  261. Pkt->LowMemRetry_remainingBufLen = Len;
  262. Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
  263. }
  264. /*
  265. * StepLowMemRetry
  266. *
  267. * During extreme low-memory stress, this function retries
  268. * a packet in small one-page chunks, sent serially.
  269. *
  270. * Returns TRUE iff the packet is done.
  271. */
  272. BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
  273. {
  274. BOOLEAN packetDone;
  275. if (Pkt->LowMemRetry_remainingBufLen == 0){
  276. packetDone = TRUE;
  277. }
  278. else {
  279. ULONG thisChunkLen;
  280. ULONG bytesToNextPageBoundary;
  281. /*
  282. * Make sure the little chunk we send is <= a page length
  283. * AND that it does not cross any page boundaries.
  284. */
  285. bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
  286. thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
  287. /*
  288. * Set up the transfer packet for the new little chunk.
  289. * This will reset numRetries so that we retry each chunk as required.
  290. */
  291. SetupReadWriteTransferPacket(Pkt,
  292. Pkt->LowMemRetry_remainingBufPtr,
  293. thisChunkLen,
  294. Pkt->LowMemRetry_nextChunkTargetLocation,
  295. Pkt->OriginalIrp);
  296. Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
  297. Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
  298. Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
  299. SubmitTransferPacket(Pkt);
  300. packetDone = FALSE;
  301. }
  302. return packetDone;
  303. }