NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context) { PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context; PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(pkt->OriginalIrp); BOOLEAN packetDone = FALSE; /* * Put all the assertions and spew in here so we don't have to look at them. */ DBGCHECKRETURNEDPKT(pkt); if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){ fdoData->LoggedTURFailureSinceLastIO = FALSE; /* * The port driver should not have allocated a sense buffer * if the SRB succeeded. */ ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)); /* * Add this packet's transferred length to the original IRP's. */ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, (LONG)pkt->Srb.DataTransferLength); if (pkt->InLowMemRetry){ packetDone = StepLowMemRetry(pkt); } else { packetDone = TRUE; } } else { /* * The packet failed. We may retry it if possible. */ BOOLEAN shouldRetry; /* * Make sure IRP status matches SRB error status (since we propagate it). */ if (NT_SUCCESS(Irp->IoStatus.Status)){ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } /* * Interpret the SRB error (to a meaningful IRP status) * and determine if we should retry this packet. * This call looks at the returned SENSE info to figure out what to do. */ shouldRetry = InterpretTransferPacketError(pkt); /* * Sometimes the port driver can allocates a new 'sense' buffer * to report transfer errors, e.g. when the default sense buffer * is too small. If so, it is up to us to free it. * Now that we're done interpreting the sense info, free it if appropriate. */ if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) { DBGTRACE(ClassDebugSenseInfo, ("Freeing port-allocated sense buffer for pkt %ph.", pkt)); FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb); pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData; pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); } /* * If the SRB queue is locked-up, release it. * Do this after calling the error handler. */ if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){ ClassReleaseQueue(pkt->Fdo); } if (shouldRetry && (pkt->NumRetries > 0)){ packetDone = RetryTransferPacket(pkt); } else { packetDone = TRUE; } } /* * If the packet is completed, put it back in the free list. * If it is the last packet servicing the original request, complete the original irp. */ if (packetDone){ LONG numPacketsRemaining; PIRP deferredIrp; PDEVICE_OBJECT Fdo = pkt->Fdo; UCHAR uniqueAddr; /* * In case a remove is pending, bump the lock count so we don't get freed * right after we complete the original irp. */ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr); /* * The original IRP should get an error code * if any one of the packets failed. */ if (!NT_SUCCESS(Irp->IoStatus.Status)){ pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; /* * If the original I/O originated in user space (i.e. it is thread-queued), * and the error is user-correctable (e.g. media is missing, for removable media), * alert the user. * Since this is only one of possibly several packets completing for the original IRP, * we may do this more than once for a single request. That's ok; this allows * us to test each returned status with IoIsErrorUserInduced(). */ if (IoIsErrorUserInduced(Irp->IoStatus.Status) && pkt->CompleteOriginalIrpWhenLastPacketCompletes && pkt->OriginalIrp->Tail.Overlay.Thread){ IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, pkt->Fdo); } } /* * We use a field in the original IRP to count * down the transfer pieces as they complete. */ numPacketsRemaining = InterlockedDecrement( (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]); if (numPacketsRemaining > 0){ /* * More transfer pieces remain for the original request. * Wait for them to complete before completing the original irp. */ } else { /* * All the transfer pieces are done. * Complete the original irp if appropriate. */ ASSERT(numPacketsRemaining == 0); if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){ ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == origCurrentSp->Parameters.Read.Length); ClasspPerfIncrementSuccessfulIo(fdoExt); } ClassReleaseRemoveLock(pkt->Fdo, pkt->OriginalIrp); ClassCompleteRequest(pkt->Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT); /* * We may have been called by one of the class drivers (e.g. cdrom) * via the legacy API ClassSplitRequest. * This is the only case for which the packet engine is called for an FDO * with a StartIo routine; in that case, we have to call IoStartNextPacket * now that the original irp has been completed. */ if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) { if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){ DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)")); } else { KIRQL oldIrql; KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(pkt->Fdo, FALSE); KeLowerIrql(oldIrql); } } } } /* * If the packet was synchronous, write the final * result back to the issuer's status buffer and * signal his event. */ if (pkt->SyncEventPtr){ KeSetEvent(pkt->SyncEventPtr, 0, FALSE); pkt->SyncEventPtr = NULL; } /* * Free the completed packet. */ pkt->OriginalIrp = NULL; pkt->InLowMemRetry = FALSE; EnqueueFreeTransferPacket(pkt->Fdo, pkt); /* * Now that we have freed some resources, * try again to send one of the previously deferred irps. */ deferredIrp = DequeueDeferredClientIrp(fdoData); if (deferredIrp){ DBGWARN(("... retrying deferred irp %xh.", deferredIrp)); ServiceTransferRequest(pkt->Fdo, deferredIrp); } ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr); } return STATUS_MORE_PROCESSING_REQUIRED; }
/*++//////////////////////////////////////////////////////////////////////////// ClassReleaseRemoveLock() Routine Description: This routine is called to release the remove lock on the device object. It must be called when finished using a previously locked reference to the device object. If an Tag was specified when acquiring the lock then the same Tag must be specified when releasing the lock. When the lock count reduces to zero, this routine will signal the waiting remove Tag to delete the device object. As a result the DeviceObject pointer should not be used again once the lock has been released. Arguments: DeviceObject - the device object to lock Tag - The irp (if any) specified when acquiring the lock. This is used for lock tracking purposes Return Value: none --*/ VOID NTAPI ClassReleaseRemoveLock( IN PDEVICE_OBJECT DeviceObject, IN OPTIONAL PIRP Tag ) { PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; LONG lockValue; #if DBG PREMOVE_TRACKING_BLOCK *listEntry = (PREMOVE_TRACKING_BLOCK*)&commonExtension->RemoveTrackingList; BOOLEAN found = FALSE; LONGLONG maxCount; BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE); KIRQL oldIrql; if(isRemoved) { DBGTRAP(("ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen")); InterlockedDecrement(&(commonExtension->RemoveLock)); return; } // // Check the tick count and make sure this thing hasn't been locked // for more than MaxLockedMinutes. // maxCount = KeQueryTimeIncrement() * 10; // microseconds maxCount *= 1000; // milliseconds maxCount *= 1000; // seconds maxCount *= 60; // minutes maxCount *= MaxLockedMinutes; DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " "maxCount = %0I64x\n", maxCount)); KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql); while(*listEntry != NULL) { PREMOVE_TRACKING_BLOCK block; LARGE_INTEGER difference; block = *listEntry; KeQueryTickCount((&difference)); difference.QuadPart -= block->TimeLocked.QuadPart; DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " "Object %p (tag %p) locked for %I64d ticks\n", DeviceObject, block->Tag, difference.QuadPart)); if(difference.QuadPart >= maxCount) { DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " "Object %p (tag %p) locked for %I64d ticks - TOO LONG\n", DeviceObject, block->Tag, difference.QuadPart)); DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " "Lock acquired in file %s on line %d\n", block->File, block->Line)); ASSERT(FALSE); } if((found == FALSE) && ((*listEntry)->Tag == Tag)) { *listEntry = block->NextBlock; ExFreePool(block); found = TRUE; } else { listEntry = &((*listEntry)->NextBlock); } } if(!found) { if(commonExtension->RemoveTrackingUntrackedCount == 0) { DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " "Couldn't find Tag %p in the lock tracking list\n", Tag)); ASSERT(FALSE); } else { DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: " "Couldn't find Tag %p in the lock tracking list - " "may be one of the %d untracked requests still " "outstanding\n", Tag, commonExtension->RemoveTrackingUntrackedCount)); commonExtension->RemoveTrackingUntrackedCount--; ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0); } } KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql); #endif lockValue = InterlockedDecrement(&commonExtension->RemoveLock); DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " "Released for Object %p & irp %p - count is %d\n", DeviceObject, Tag, lockValue)); ASSERT(lockValue >= 0); ASSERTMSG("RemoveLock decreased to meet LockLowWatermark", ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark))); if(lockValue == 0) { ASSERT(commonExtension->IsRemoved); // // The device needs to be removed. Signal the remove event // that it's safe to go ahead. // DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: " "Release for object %p & irp %p caused lock to go to zero\n", DeviceObject, Tag)); KeSetEvent(&commonExtension->RemoveEvent, IO_NO_INCREMENT, FALSE); } return; }