?? xferpkt.c
字號(hào):
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
xferpkt.c
Abstract:
Packet routines for CLASSPNP
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, InitializeTransferPackets)
#pragma alloc_text(PAGE, DestroyAllTransferPackets)
#pragma alloc_text(PAGE, SetupEjectionTransferPacket)
#pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
#endif
ULONG MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
ULONG MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
/*
* InitializeTransferPackets
*
* Allocate/initialize TRANSFER_PACKETs and related resources.
*/
NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo)
{
PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
ULONG hwMaxPages;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
/*
* Precompute the maximum transfer length
*/
ASSERT(adapterDesc->MaximumTransferLength);
hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0;
fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT);
fdoData->HwMaxXferLen = MAX(fdoData->HwMaxXferLen, PAGE_SIZE);
fdoData->NumTotalTransferPackets = 0;
fdoData->NumFreeTransferPackets = 0;
InitializeSListHead(&fdoData->FreeTransferPacketsList);
InitializeListHead(&fdoData->AllTransferPacketsList);
InitializeListHead(&fdoData->DeferredClientIrpList);
/*
* Set the packet threshold numbers based on the Windows SKU.
*/
if (ExVerifySuite(Personal)){
// this is Windows Personal
MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
}
else if (ExVerifySuite(Enterprise) || ExVerifySuite(DataCenter)){
// this is Advanced Server or Datacenter
MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise;
MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise;
}
else if (ExVerifySuite(TerminalServer)){
// this is standard Server or Pro with terminal server
MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Server;
MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Server;
}
else {
// this is Professional without terminal server
MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
}
while (fdoData->NumFreeTransferPackets < MIN_INITIAL_TRANSFER_PACKETS){
PTRANSFER_PACKET pkt = NewTransferPacket(Fdo);
if (pkt){
InterlockedIncrement(&fdoData->NumTotalTransferPackets);
EnqueueFreeTransferPacket(Fdo, pkt);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
}
fdoData->DbgPeakNumTransferPackets = fdoData->NumTotalTransferPackets;
/*
* Pre-initialize our SCSI_REQUEST_BLOCK template with all
* the constant fields. This will save a little time for each xfer.
* NOTE: a CdbLength field of 10 may not always be appropriate
*/
RtlZeroMemory(&fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK));
fdoData->SrbTemplate.Length = sizeof(SCSI_REQUEST_BLOCK);
fdoData->SrbTemplate.Function = SRB_FUNCTION_EXECUTE_SCSI;
fdoData->SrbTemplate.QueueAction = SRB_SIMPLE_TAG_REQUEST;
fdoData->SrbTemplate.SenseInfoBufferLength = sizeof(SENSE_DATA);
fdoData->SrbTemplate.CdbLength = 10;
return status;
}
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);
}
PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
PTRANSFER_PACKET newPkt;
newPkt = ExAllocatePoolWithTag(NonPagedPool, sizeof(TRANSFER_PACKET), 'pnPC');
if (newPkt){
RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET)); // just to be sure
/*
* Allocate resources for the packet.
*/
newPkt->Irp = IoAllocateIrp(Fdo->StackSize, FALSE);
if (newPkt->Irp){
KIRQL oldIrql;
//
// Allocate a MDL. Add one page to the length to insure an extra page
// entry is allocated if the buffer does not start on page boundaries.
//
newPkt->PartialMdl = IoAllocateMdl(NULL,
fdoData->HwMaxXferLen + PAGE_SIZE,
FALSE,
FALSE,
NULL);
if (newPkt->PartialMdl) {
ASSERT(newPkt->PartialMdl->Size >= (CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(fdoData->HwMaxXferLen) * sizeof(PFN_NUMBER)));
newPkt->Fdo = Fdo;
#if DBG
newPkt->DbgPktId = InterlockedIncrement(&fdoData->DbgMaxPktId);
#endif
/*
* Enqueue the packet in our static AllTransferPacketsList
* (just so we can find it during debugging if its stuck somewhere).
*/
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
InsertTailList(&fdoData->AllTransferPacketsList, &newPkt->AllPktsListEntry);
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
} else {
IoFreeIrp(newPkt->Irp);
ExFreePool(newPkt);
newPkt = NULL;
}
}
else {
ExFreePool(newPkt);
newPkt = NULL;
}
}
return newPkt;
}
/*
* DestroyTransferPacket
*
*/
VOID DestroyTransferPacket(PTRANSFER_PACKET Pkt)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
KIRQL oldIrql;
ASSERT(!Pkt->SlistEntry.Next);
ASSERT(!Pkt->OriginalIrp);
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
/*
* Delete the packet from our all-packets queue.
*/
ASSERT(!IsListEmpty(&Pkt->AllPktsListEntry));
ASSERT(!IsListEmpty(&fdoData->AllTransferPacketsList));
RemoveEntryList(&Pkt->AllPktsListEntry);
InitializeListHead(&Pkt->AllPktsListEntry);
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
IoFreeMdl(Pkt->PartialMdl);
IoFreeIrp(Pkt->Irp);
ExFreePool(Pkt);
}
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,
(PSINGLE_LIST_ENTRY)&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);
}
}
}
}
PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
PTRANSFER_PACKET pkt;
PSLIST_ENTRY slistEntry;
slistEntry = InterlockedPopEntrySList(&fdoData->FreeTransferPacketsList);
if (slistEntry){
slistEntry->Next = NULL;
pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
InterlockedDecrement(&fdoData->NumFreeTransferPackets);
}
else {
if (AllocIfNeeded){
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -