VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo) { PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; TRANSFER_PACKET *pkt; PAGED_CODE(); ASSERT(IsListEmpty(&fdoData->DeferredClientIrpList)); while (pkt = DequeueFreeTransferPacket(Fdo, FALSE)){ DestroyTransferPacket(pkt); InterlockedDecrement(&fdoData->NumTotalTransferPackets); } ASSERT(fdoData->NumTotalTransferPackets == 0); }
VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, PTRANSFER_PACKET Pkt) { PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; KIRQL oldIrql; ULONG newNumPkts; ASSERT(!Pkt->SlistEntry.Next); InterlockedPushEntrySList(&fdoData->FreeTransferPacketsList, &Pkt->SlistEntry); newNumPkts = InterlockedIncrement(&fdoData->NumFreeTransferPackets); ASSERT(newNumPkts <= fdoData->NumTotalTransferPackets); /* * If the total number of packets is larger than MinWorkingSetTransferPackets, * that means that we've been in stress. If all those packets are now * free, then we are now out of stress and can free the extra packets. * Free down to MaxWorkingSetTransferPackets immediately, and * down to MinWorkingSetTransferPackets lazily (one at a time). */ if (fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets){ /* * 1. Immediately snap down to our UPPER threshold. */ if (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets){ SINGLE_LIST_ENTRY pktList; PSINGLE_LIST_ENTRY slistEntry; PTRANSFER_PACKET pktToDelete; DBGTRACE(ClassDebugTrace, ("Exiting stress, block freeing (%d-%d) packets.", fdoData->NumTotalTransferPackets, MaxWorkingSetTransferPackets)); /* * Check the counter again with lock held. This eliminates a race condition * while still allowing us to not grab the spinlock in the common codepath. * * Note that the spinlock does not synchronize with threads dequeuing free * packets to send (DequeueFreeTransferPacket does that with a lightweight * interlocked exchange); the spinlock prevents multiple threads in this function * from deciding to free too many extra packets at once. */ SimpleInitSlistHdr(&pktList); KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); while ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) && (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets)){ pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE); if (pktToDelete){ SimplePushSlist(&pktList, &pktToDelete->SlistEntry); InterlockedDecrement(&fdoData->NumTotalTransferPackets); } else { DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1).", MaxWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets)); break; } } KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); while (slistEntry = SimplePopSlist(&pktList)){ pktToDelete = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry); DestroyTransferPacket(pktToDelete); } } /* * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time). */ if (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets){ /* * Check the counter again with lock held. This eliminates a race condition * while still allowing us to not grab the spinlock in the common codepath. * * Note that the spinlock does not synchronize with threads dequeuing free * packets to send (DequeueFreeTransferPacket does that with a lightweight * interlocked exchange); the spinlock prevents multiple threads in this function * from deciding to free too many extra packets at once. */ PTRANSFER_PACKET pktToDelete = NULL; DBGTRACE(ClassDebugTrace, ("Exiting stress, lazily freeing one of %d/%d packets.", fdoData->NumTotalTransferPackets, MinWorkingSetTransferPackets)); KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); if ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) && (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets)){ pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE); if (pktToDelete){ InterlockedDecrement(&fdoData->NumTotalTransferPackets); } else { DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2).", MinWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets)); } } KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); if (pktToDelete){ DestroyTransferPacket(pktToDelete); } } } }
/* * BUGBUG RESTORE * This is a new implementation of the function that doesn't thrash memory * or depend on the srbLookasideList. * HOWEVER, it seems to cause pagefile initialization to fail during boot * for some reason. Need to resolve this before switching to this function. */ NTSTATUS NTAPI ClasspEjectionControl( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN MEDIA_LOCK_TYPE LockType, IN BOOLEAN Lock ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension; PFILE_OBJECT_EXTENSION fsContext; BOOLEAN fileHandleOk = TRUE; BOOLEAN countChanged = FALSE; NTSTATUS status; PAGED_CODE(); status = KeWaitForSingleObject( &fdoExt->EjectSynchronizationEvent, UserRequest, UserMode, FALSE, NULL); ASSERT(status == STATUS_SUCCESS); /* * If this is a "secured" request, we have to make sure * that the file handle is valid. */ if (LockType == SecureMediaLock){ PIO_STACK_LOCATION thisSp = IoGetCurrentIrpStackLocation(Irp); /* * Make sure that the file object we are supplied has a * proper FsContext before we try doing a secured lock. */ if (thisSp->FileObject){ PCOMMON_DEVICE_EXTENSION commonExt = (PCOMMON_DEVICE_EXTENSION)fdoExt; fsContext = ClasspGetFsContext(commonExt, thisSp->FileObject); } else { fsContext = NULL; } if (!fsContext){ ASSERT(fsContext); fileHandleOk = FALSE; } } if (fileHandleOk){ /* * Adjust the lock counts and make sure they make sense. */ status = STATUS_SUCCESS; if (Lock){ switch(LockType) { case SimpleMediaLock: fdoExt->LockCount++; countChanged = TRUE; break; case SecureMediaLock: fsContext->LockCount++; fdoExt->ProtectedLockCount++; countChanged = TRUE; break; case InternalMediaLock: fdoExt->InternalLockCount++; countChanged = TRUE; break; } } else { /* * This is an unlock command. If it's a secured one then make sure * the caller has a lock outstanding or return an error. */ switch (LockType){ case SimpleMediaLock: if (fdoExt->LockCount > 0){ fdoExt->LockCount--; countChanged = TRUE; } else { ASSERT(fdoExt->LockCount > 0); status = STATUS_INTERNAL_ERROR; } break; case SecureMediaLock: if (fsContext->LockCount > 0){ ASSERT(fdoExt->ProtectedLockCount > 0); fsContext->LockCount--; fdoExt->ProtectedLockCount--; countChanged = TRUE; } else { ASSERT(fsContext->LockCount > 0); status = STATUS_INVALID_DEVICE_STATE; } break; case InternalMediaLock: ASSERT(fdoExt->InternalLockCount > 0); fdoExt->InternalLockCount--; countChanged = TRUE; break; } } if (NT_SUCCESS(status)){ /* * We only send an unlock command to the drive if * all the lock counts have dropped to zero. */ if (!Lock && (fdoExt->ProtectedLockCount || fdoExt->InternalLockCount || fdoExt->LockCount)){ /* * The lock count is still positive, so don't unlock yet. */ status = STATUS_SUCCESS; } else if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { /* * The device isn't removable media. don't send a cmd. */ status = STATUS_SUCCESS; } else { TRANSFER_PACKET *pkt; pkt = DequeueFreeTransferPacket(Fdo, TRUE); if (pkt){ KEVENT event; /* * Store the number of packets servicing the irp (one) * inside the original IRP. It will be used to counted down * to zero when the packet completes. * Initialize the original IRP's status to success. * If the packet fails, we will set it to the error status. */ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1); Irp->IoStatus.Status = STATUS_SUCCESS; /* * Set this up as a SYNCHRONOUS transfer, submit it, * and wait for the packet to complete. The result * status will be written to the original irp. */ KeInitializeEvent(&event, SynchronizationEvent, FALSE); SetupEjectionTransferPacket(pkt, Lock, &event, Irp); SubmitTransferPacket(pkt); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } } } else { status = STATUS_INVALID_PARAMETER; } if (!NT_SUCCESS(status) && countChanged) { // // have to revert to previous counts if the // lock/unlock operation actually failed. // if(Lock) { switch(LockType) { case SimpleMediaLock: { FdoExtension->LockCount--; break; } case SecureMediaLock: { fsContext->LockCount--; FdoExtension->ProtectedLockCount--; break; } case InternalMediaLock: { FdoExtension->InternalLockCount--; break; } } } else { switch(LockType) { case SimpleMediaLock: { FdoExtension->LockCount++; break; } case SecureMediaLock: { fsContext->LockCount++; FdoExtension->ProtectedLockCount++; break; } case InternalMediaLock: { FdoExtension->InternalLockCount++; break; } } } } KeSetEvent(&fdoExt->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE); return status; }