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.

357 lines
13 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. NUM_LOCKMEDIAREMOVAL_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. NUM_MODESENSE_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. * When the caller of this function sees that the status was changed to success,
  93. * it will add the transferred length to the original irp.
  94. */
  95. Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
  96. shouldRetry = FALSE;
  97. }
  98. }
  99. else if (pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY){
  100. /*
  101. * This is a Drive Capacity SRB. Interpret its sense info specially.
  102. */
  103. shouldRetry = ClassInterpretSenseInfo(
  104. Pkt->Fdo,
  105. &Pkt->Srb,
  106. IRP_MJ_SCSI,
  107. 0,
  108. NUM_DRIVECAPACITY_RETRIES - Pkt->NumRetries,
  109. &Pkt->Irp->IoStatus.Status,
  110. &Pkt->RetryIntervalSec);
  111. if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
  112. shouldRetry = TRUE;
  113. }
  114. }
  115. else if ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
  116. (pCdb->CDB10.OperationCode == SCSIOP_WRITE)){
  117. /*
  118. * This is a Read/Write Data packet.
  119. */
  120. PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
  121. shouldRetry = ClassInterpretSenseInfo(
  122. Pkt->Fdo,
  123. &Pkt->Srb,
  124. origCurrentSp->MajorFunction,
  125. 0,
  126. NUM_IO_RETRIES - Pkt->NumRetries,
  127. &Pkt->Irp->IoStatus.Status,
  128. &Pkt->RetryIntervalSec);
  129. /*
  130. * Deal with some special cases.
  131. */
  132. if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
  133. /*
  134. * We are in extreme low-memory stress.
  135. * We will retry in smaller chunks.
  136. */
  137. shouldRetry = TRUE;
  138. }
  139. else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
  140. (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){
  141. /*
  142. * We are still verifying a (possibly) reloaded disk/cdrom.
  143. * So retry the request.
  144. */
  145. Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
  146. shouldRetry = TRUE;
  147. }
  148. }
  149. else {
  150. DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
  151. }
  152. return shouldRetry;
  153. }
  154. /*
  155. * RetryTransferPacket
  156. *
  157. * Retry sending a TRANSFER_PACKET.
  158. *
  159. * Return TRUE iff the packet is complete.
  160. * (if so the status in pkt->irp is the final status).
  161. */
  162. BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
  163. {
  164. BOOLEAN packetDone;
  165. DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb)));
  166. ASSERT(Pkt->NumRetries > 0);
  167. Pkt->NumRetries--;
  168. //
  169. // If this is the last retry, then turn off disconnect, sync transfer,
  170. // and tagged queuing. On all other retries, leave the original settings.
  171. //
  172. if ( 0 == Pkt->NumRetries ) {
  173. /*
  174. * Tone down performance on the retry.
  175. * This increases the chance for success on the retry.
  176. * We've seen instances of drives that fail consistently but then start working
  177. * once this scale-down is applied.
  178. */
  179. SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
  180. SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
  181. CLEAR_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
  182. Pkt->Srb.QueueTag = SP_UNTAGGED;
  183. }
  184. if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
  185. PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
  186. BOOLEAN isReadWrite = ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
  187. (pCdb->CDB10.OperationCode == SCSIOP_WRITE));
  188. if (Pkt->InLowMemRetry || !isReadWrite){
  189. /*
  190. * This should never happen.
  191. * The memory manager guarantees that at least four pages will
  192. * be available to allow forward progress in the port driver.
  193. * So a one-page transfer should never fail with insufficient resources.
  194. */
  195. ASSERT(isReadWrite && !Pkt->InLowMemRetry);
  196. packetDone = TRUE;
  197. }
  198. else {
  199. /*
  200. * We are in low-memory stress.
  201. * Start the low-memory retry state machine, which tries to
  202. * resend the packet in little one-page chunks.
  203. */
  204. InitLowMemRetry( Pkt,
  205. Pkt->BufPtrCopy,
  206. Pkt->BufLenCopy,
  207. Pkt->TargetLocationCopy);
  208. StepLowMemRetry(Pkt);
  209. packetDone = FALSE;
  210. }
  211. }
  212. else {
  213. /*
  214. * Retry the packet by simply resending it after a delay.
  215. * Put the packet back in the pending queue and
  216. * schedule a timer to retry the transfer.
  217. *
  218. * Do not call SetupReadWriteTransferPacket again because:
  219. * (1) The minidriver may have set some bits
  220. * in the SRB that it needs again and
  221. * (2) doing so would reset numRetries.
  222. *
  223. * BECAUSE we do not call SetupReadWriteTransferPacket again,
  224. * we have to reset a couple fields in the SRB that
  225. * some miniports overwrite when they fail an SRB.
  226. */
  227. Pkt->Srb.DataBuffer = Pkt->BufPtrCopy;
  228. Pkt->Srb.DataTransferLength = Pkt->BufLenCopy;
  229. if (Pkt->RetryIntervalSec == 0){
  230. /*
  231. * Always delay by at least a little when retrying.
  232. * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
  233. */
  234. LARGE_INTEGER timerPeriod;
  235. timerPeriod.HighPart = -1;
  236. timerPeriod.LowPart = -(LONG)((ULONG)MINIMUM_RETRY_UNITS*KeQueryTimeIncrement());
  237. KeInitializeTimer(&Pkt->RetryTimer);
  238. KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
  239. KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
  240. }
  241. else {
  242. LARGE_INTEGER timerPeriod;
  243. ASSERT(Pkt->RetryIntervalSec < 100); // sanity check
  244. timerPeriod.HighPart = -1;
  245. timerPeriod.LowPart = Pkt->RetryIntervalSec*-10000000;
  246. KeInitializeTimer(&Pkt->RetryTimer);
  247. KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
  248. KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
  249. }
  250. packetDone = FALSE;
  251. }
  252. return packetDone;
  253. }
  254. VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc,
  255. IN PVOID DeferredContext,
  256. IN PVOID SystemArgument1,
  257. IN PVOID SystemArgument2)
  258. {
  259. PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext;
  260. SubmitTransferPacket(pkt);
  261. }
  262. VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
  263. {
  264. ASSERT(Len > 0);
  265. ASSERT(!Pkt->InLowMemRetry);
  266. Pkt->InLowMemRetry = TRUE;
  267. Pkt->LowMemRetry_remainingBufPtr = BufPtr;
  268. Pkt->LowMemRetry_remainingBufLen = Len;
  269. Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
  270. }
  271. /*
  272. * StepLowMemRetry
  273. *
  274. * During extreme low-memory stress, this function retries
  275. * a packet in small one-page chunks, sent serially.
  276. *
  277. * Returns TRUE iff the packet is done.
  278. */
  279. BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
  280. {
  281. BOOLEAN packetDone;
  282. if (Pkt->LowMemRetry_remainingBufLen == 0){
  283. packetDone = TRUE;
  284. }
  285. else {
  286. ULONG thisChunkLen;
  287. ULONG bytesToNextPageBoundary;
  288. /*
  289. * Make sure the little chunk we send is <= a page length
  290. * AND that it does not cross any page boundaries.
  291. */
  292. bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
  293. thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
  294. /*
  295. * Set up the transfer packet for the new little chunk.
  296. * This will reset numRetries so that we retry each chunk as required.
  297. */
  298. SetupReadWriteTransferPacket(Pkt,
  299. Pkt->LowMemRetry_remainingBufPtr,
  300. thisChunkLen,
  301. Pkt->LowMemRetry_nextChunkTargetLocation,
  302. Pkt->OriginalIrp);
  303. Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
  304. Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
  305. Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
  306. SubmitTransferPacket(Pkt);
  307. packetDone = FALSE;
  308. }
  309. return packetDone;
  310. }