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