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.
564 lines
15 KiB
564 lines
15 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ntos\tdi\isn\fwd\packets.c
|
|
|
|
Abstract:
|
|
IPX Forwarder Driver packet allocator
|
|
|
|
|
|
Author:
|
|
|
|
Vadim Eydelman
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
ULONG RcvPktsPerSegment = DEF_RCV_PKTS_PER_SEGMENT;
|
|
ULONG MaxRcvPktsPoolSize =0;
|
|
ULONG RcvPktsPoolSize = 0;
|
|
KSPIN_LOCK AllocatorLock;
|
|
const LONGLONG SegmentTimeout = -10i64*10000000i64;
|
|
|
|
SEGMENT_LIST ListEther={1500};
|
|
SEGMENT_LIST ListTR4={4500};
|
|
SEGMENT_LIST ListTR16={17986};
|
|
|
|
PSEGMENT_LIST SegmentMap[FRAME_SIZE_VARIATIONS][FRAME_SIZE_VARIATIONS] = {
|
|
{&ListEther, &ListEther, &ListEther},
|
|
{&ListEther, &ListTR4, &ListTR4},
|
|
{&ListEther, &ListTR4, &ListTR16}
|
|
};
|
|
|
|
VOID
|
|
AllocationWorker (
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
SegmentTimeoutDpc (
|
|
PKDPC dpc,
|
|
PVOID Context,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2
|
|
);
|
|
|
|
/*++
|
|
*******************************************************************
|
|
C r e a t e S e g m e n t
|
|
|
|
Routine Description:
|
|
Allocates and initializes packet segment
|
|
Arguments:
|
|
list - segment list to which new segment will be added
|
|
Return Value:
|
|
Pointer to allocated segment, NULL if fails
|
|
|
|
*******************************************************************
|
|
--*/
|
|
PPACKET_SEGMENT
|
|
CreateSegment (
|
|
PSEGMENT_LIST list
|
|
) {
|
|
KIRQL oldIRQL;
|
|
NDIS_STATUS status;
|
|
PPACKET_SEGMENT segment;
|
|
ULONG segmentsize = list->SL_BlockCount*list->SL_BlockSize
|
|
+FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers);
|
|
if (MaxRcvPktsPoolSize!=0) {
|
|
// Check if this allocation would exceed the limit
|
|
KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
|
|
if (RcvPktsPoolSize+segmentsize<MaxRcvPktsPoolSize) {
|
|
RcvPktsPoolSize += segmentsize;
|
|
KeReleaseSpinLock (&AllocatorLock, oldIRQL);
|
|
}
|
|
else {
|
|
KeReleaseSpinLock (&AllocatorLock, oldIRQL);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Allocate chunk of memory to hold segment header and buffers
|
|
segment = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
segmentsize,
|
|
FWD_POOL_TAG);
|
|
if (segment!=NULL) {
|
|
segment->PS_SegmentList = list;
|
|
segment->PS_FreeHead = NULL;
|
|
segment->PS_BusyCount = 0;
|
|
segment->PS_PacketPool = (NDIS_HANDLE)FWD_POOL_TAG;
|
|
KeQuerySystemTime ((PLARGE_INTEGER)&segment->PS_FreeStamp);
|
|
NdisAllocatePacketPoolEx (
|
|
&status,
|
|
&segment->PS_PacketPool,
|
|
list->SL_BlockCount,
|
|
0,
|
|
IPXMacHeaderSize
|
|
+FIELD_OFFSET (PACKET_TAG, PT_MacHeader));
|
|
if (status==NDIS_STATUS_SUCCESS) {
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: CreateSegent pool: %x\n", segment->PS_PacketPool));
|
|
|
|
NdisAllocateBufferPool (
|
|
&status,
|
|
&segment->PS_BufferPool,
|
|
list->SL_BlockCount*2);
|
|
if (status==NDIS_STATUS_SUCCESS) {
|
|
PUCHAR bufferptr = segment->PS_Buffers;
|
|
PNDIS_PACKET packetDscr;
|
|
PNDIS_BUFFER bufferDscr;
|
|
PPACKET_TAG packetTag;
|
|
ULONG i;
|
|
|
|
for (i=0; i<list->SL_BlockCount; i++,
|
|
bufferptr+=list->SL_BlockSize) {
|
|
NdisAllocatePacket (
|
|
&status,
|
|
&packetDscr,
|
|
segment->PS_PacketPool);
|
|
ASSERT (status==NDIS_STATUS_SUCCESS);
|
|
|
|
packetTag = (PPACKET_TAG)packetDscr->ProtocolReserved;
|
|
packetTag->PT_Segment = segment;
|
|
packetTag->PT_Data = bufferptr;
|
|
packetTag->PT_InterfaceReference = NULL;
|
|
|
|
NdisAllocateBuffer (
|
|
&status,
|
|
&packetTag->PT_MacHdrBufDscr,
|
|
segment->PS_BufferPool,
|
|
packetTag->PT_MacHeader,
|
|
IPXMacHeaderSize);
|
|
ASSERT (status==NDIS_STATUS_SUCCESS);
|
|
|
|
NdisAllocateBuffer (
|
|
&status,
|
|
&bufferDscr,
|
|
segment->PS_BufferPool,
|
|
bufferptr,
|
|
list->SL_BlockSize);
|
|
ASSERT (status==NDIS_STATUS_SUCCESS);
|
|
if (bufferDscr)
|
|
{
|
|
NdisChainBufferAtFront (packetDscr, bufferDscr);
|
|
}
|
|
|
|
packetTag->PT_Next = segment->PS_FreeHead;
|
|
segment->PS_FreeHead = packetTag;
|
|
}
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: Allocated packet segment %08lx for list %ld.\n",
|
|
segment, list->SL_BlockSize));
|
|
return segment;
|
|
}
|
|
else {
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR,
|
|
("IpxFwd: Failed to allocate buffer pool"
|
|
" for new segment in list %ld.\n",
|
|
list->SL_BlockSize));
|
|
}
|
|
NdisFreePacketPool (segment->PS_PacketPool);
|
|
}
|
|
else {
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR,
|
|
("IpxFwd: Failed to allocate packet pool"
|
|
" for new segment in list %ld.\n",
|
|
list->SL_BlockSize));
|
|
}
|
|
ExFreePool (segment);
|
|
}
|
|
else {
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_ERROR,
|
|
("IpxFwd: Failed to allocate new segment for list %ld.\n",
|
|
list->SL_BlockSize));
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*++
|
|
*******************************************************************
|
|
D e l e t e S e g m e n t
|
|
|
|
Routine Description:
|
|
Frees packet segment
|
|
Arguments:
|
|
segment - segment to free
|
|
Return Value:
|
|
None
|
|
|
|
*******************************************************************
|
|
--*/
|
|
VOID
|
|
DeleteSegment (
|
|
PPACKET_SEGMENT segment
|
|
) {
|
|
PSEGMENT_LIST list = segment->PS_SegmentList;
|
|
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: DeleteSegment entered. %d %x\n",segment->PS_BusyCount, segment->PS_PacketPool));
|
|
|
|
ASSERT (segment->PS_BusyCount == 0);
|
|
|
|
// Free all NDIS packet and buffer descriptors first
|
|
while (segment->PS_FreeHead!=NULL) {
|
|
PNDIS_BUFFER bufferDscr;
|
|
PPACKET_TAG packetTag = segment->PS_FreeHead;
|
|
PNDIS_PACKET packetDscr = CONTAINING_RECORD (packetTag,
|
|
NDIS_PACKET, ProtocolReserved);
|
|
|
|
segment->PS_FreeHead = packetTag->PT_Next;
|
|
|
|
ASSERT (packetTag->PT_MacHdrBufDscr!=NULL);
|
|
NdisFreeBuffer (packetTag->PT_MacHdrBufDscr);
|
|
|
|
NdisUnchainBufferAtFront (packetDscr, &bufferDscr);
|
|
ASSERT (bufferDscr!=NULL);
|
|
NdisFreeBuffer (bufferDscr);
|
|
|
|
NdisFreePacket (packetDscr);
|
|
}
|
|
NdisFreeBufferPool (segment->PS_BufferPool);
|
|
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: DeleteSegment pool: %x\n", segment->PS_PacketPool));
|
|
|
|
NdisFreePacketPool (segment->PS_PacketPool);
|
|
|
|
// [pmay] Remove this -- for debugging only
|
|
segment->PS_PacketPool = NULL;
|
|
|
|
// Decrement memory used if we have a quota
|
|
if (MaxRcvPktsPoolSize!=0) {
|
|
KIRQL oldIRQL;
|
|
ULONG segmentsize = list->SL_BlockCount*list->SL_BlockSize
|
|
+FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers);
|
|
KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
|
|
RcvPktsPoolSize -= segmentsize;
|
|
KeReleaseSpinLock (&AllocatorLock, oldIRQL);
|
|
}
|
|
ExFreePool (segment);
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: Deleting segment %08lx in list %ld.\n",
|
|
segment, list->SL_BlockSize));
|
|
}
|
|
|
|
|
|
/*++
|
|
*******************************************************************
|
|
R e g i s t e r P a c k e t C o n s u m e r
|
|
|
|
Routine Description:
|
|
Registers a consumer (bound interface) of packets of the
|
|
given size
|
|
Arguments:
|
|
pktsize - maximum size of packets needed
|
|
listId - buffer to return packet list id where packets
|
|
of required size are located
|
|
Return Value:
|
|
STATUS_SUCCESS - registration succeded
|
|
STATUS_INSUFFICIENT_RESOURCES - not enogh resources to register
|
|
|
|
*******************************************************************
|
|
--*/
|
|
NTSTATUS
|
|
RegisterPacketConsumer (
|
|
IN ULONG pktsize,
|
|
OUT INT *listID
|
|
) {
|
|
NTSTATUS status=STATUS_SUCCESS;
|
|
KIRQL oldIRQL;
|
|
PSEGMENT_LIST list;
|
|
INT i;
|
|
LONG addRefCount = 1;
|
|
|
|
KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
|
|
ASSERT (pktsize<=SegmentMap[FRAME_SIZE_VARIATIONS-1]
|
|
[FRAME_SIZE_VARIATIONS-1]->SL_BlockSize);
|
|
|
|
for (i=0; i<FRAME_SIZE_VARIATIONS; i++) {
|
|
list = SegmentMap[i][i];
|
|
if (pktsize<=list->SL_BlockSize) {
|
|
list->SL_RefCount += 1;
|
|
*listID = i;
|
|
break;
|
|
}
|
|
}
|
|
KeReleaseSpinLock (&AllocatorLock, oldIRQL);
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: Registered packet consumer, pktsz: %ld, list: %ld.\n",
|
|
pktsize, list->SL_BlockSize));
|
|
return status;
|
|
}
|
|
|
|
/*++
|
|
*******************************************************************
|
|
D e r e g i s t e r P a c k e t C o n s u m e r
|
|
|
|
Routine Description:
|
|
Deregisters a consumer (bound interface) of packets of the
|
|
given size
|
|
Arguments:
|
|
listId - packet list id used by the consumer
|
|
Return Value:
|
|
None
|
|
|
|
*******************************************************************
|
|
--*/
|
|
VOID
|
|
DeregisterPacketConsumer (
|
|
IN INT listID
|
|
) {
|
|
KIRQL oldIRQL;
|
|
PSEGMENT_LIST list;
|
|
|
|
ASSERT ((listID>=0) && (listID<FRAME_SIZE_VARIATIONS));
|
|
|
|
KeAcquireSpinLock (&AllocatorLock, &oldIRQL);
|
|
list = SegmentMap[listID][listID];
|
|
|
|
ASSERT (list->SL_RefCount>0);
|
|
|
|
list->SL_RefCount -= 1;
|
|
|
|
KeReleaseSpinLock (&AllocatorLock, oldIRQL);
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
("IpxFwd: Deregistered packet consumer, list: %ld.\n",
|
|
list->SL_BlockSize));
|
|
|
|
}
|
|
|
|
/*++
|
|
*******************************************************************
|
|
I n i t i a l i z e S e g m e n t L i s t
|
|
|
|
Routine Description:
|
|
Initializes list of packet segments
|
|
Arguments:
|
|
list - list to initalize
|
|
Return Value:
|
|
None
|
|
|
|
*******************************************************************
|
|
--*/
|
|
VOID
|
|
InitializeSegmentList(
|
|
PSEGMENT_LIST list
|
|
) {
|
|
InitializeListHead (&list->SL_Head);
|
|
list->SL_FreeCount = 0;
|
|
// Make sure we don't have any leftover larger than
|
|
// the buffer size (kernel memory allocator
|
|
// allocates full pages)
|
|
list->SL_BlockCount = (ULONG)
|
|
(ROUND_TO_PAGES (
|
|
list->SL_BlockSize*RcvPktsPerSegment
|
|
+FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers))
|
|
-FIELD_OFFSET(PACKET_SEGMENT,PS_Buffers))
|
|
/list->SL_BlockSize;
|
|
list->SL_LowCount = list->SL_BlockCount/2;
|
|
list->SL_RefCount = 0;
|
|
list->SL_AllocatorPending = FALSE;
|
|
list->SL_TimerDpcPending = FALSE;
|
|
KeInitializeSpinLock (&list->SL_Lock);
|
|
KeInitializeTimer (&list->SL_Timer);
|
|
KeInitializeDpc (&list->SL_TimerDpc, SegmentTimeoutDpc, list);
|
|
ExInitializeWorkItem (&list->SL_Allocator, AllocationWorker, list);
|
|
}
|
|
|
|
/*++
|
|
*******************************************************************
|
|
D e l e t e S e g m e n t L i s t
|
|
|
|
Routine Description:
|
|
Deletes list of packet segments
|
|
Arguments:
|
|
list - list to delete
|
|
Return Value:
|
|
None
|
|
|
|
*******************************************************************
|
|
--*/
|
|
VOID
|
|
DeleteSegmentList (
|
|
PSEGMENT_LIST list
|
|
) {
|
|
KeCancelTimer (&list->SL_Timer);
|
|
while (!IsListEmpty (&list->SL_Head)) {
|
|
PPACKET_SEGMENT segment;
|
|
segment = CONTAINING_RECORD (list->SL_Head.Blink,
|
|
PACKET_SEGMENT, PS_Link);
|
|
|
|
RemoveEntryList (&segment->PS_Link);
|
|
DeleteSegment (segment);
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
*******************************************************************
|
|
S e g m e n t T i m e o u t D p c
|
|
|
|
Routine Description:
|
|
Timer DPC that launches allocator worker to get rid of unused
|
|
segments
|
|
Arguments:
|
|
Context - segment list to check for unused segments
|
|
Return Value:
|
|
None
|
|
|
|
*******************************************************************
|
|
--*/
|
|
VOID
|
|
SegmentTimeoutDpc (
|
|
PKDPC dpc,
|
|
PVOID Context,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2
|
|
) {
|
|
#define list ((PSEGMENT_LIST)Context)
|
|
KIRQL oldIRQL;
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_INFORMATION,
|
|
("IpxFwd: Segment timed out in list: %ld.\n",
|
|
list->SL_BlockSize));
|
|
KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
|
|
list->SL_TimerDpcPending = FALSE;
|
|
if (!list->SL_AllocatorPending
|
|
&& (list->SL_FreeCount>=list->SL_BlockCount)
|
|
&& EnterForwarder ()) {
|
|
list->SL_AllocatorPending = TRUE;
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
ExQueueWorkItem (&list->SL_Allocator, DelayedWorkQueue);
|
|
}
|
|
else {
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
}
|
|
LeaveForwarder ();
|
|
#undef list
|
|
}
|
|
|
|
|
|
/*++
|
|
*******************************************************************
|
|
A l l o c a t i o n W o r k e r
|
|
|
|
Routine Description:
|
|
Adds new segment or releases unused segments from the list
|
|
depending on the free packet count and time that segments
|
|
are not used
|
|
Arguments:
|
|
Context - packet list to process
|
|
Return Value:
|
|
None
|
|
|
|
*******************************************************************
|
|
--*/
|
|
VOID
|
|
AllocationWorker (
|
|
PVOID Context
|
|
) {
|
|
#define list ((PSEGMENT_LIST)Context)
|
|
KIRQL oldIRQL;
|
|
PPACKET_SEGMENT segment = NULL;
|
|
LONGLONG curTime;
|
|
|
|
IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_INFORMATION,
|
|
("IpxFwd: Allocating/scavenging segment(s) in list: %ld.\n",
|
|
list->SL_BlockSize));
|
|
KeQuerySystemTime ((PLARGE_INTEGER)&curTime);
|
|
KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
|
|
list->SL_AllocatorPending = FALSE;
|
|
if (list->SL_FreeCount<list->SL_BlockCount) {
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
// First allocate a segment
|
|
segment = CreateSegment (list);
|
|
if (segment!=NULL) {
|
|
KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
|
|
InsertTailList (&list->SL_Head, &segment->PS_Link);
|
|
list->SL_FreeCount += list->SL_BlockCount;
|
|
if (!list->SL_TimerDpcPending
|
|
&& EnterForwarder ()) {
|
|
list->SL_TimerDpcPending = TRUE;
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
KeSetTimer (&list->SL_Timer, *((PLARGE_INTEGER)&SegmentTimeout), &list->SL_TimerDpc);
|
|
}
|
|
else {
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Make sure that there is either more than segment in the list
|
|
// or there is one and no registered users
|
|
if (!IsListEmpty (&list->SL_Head)) {
|
|
|
|
// [pmay] Remove this -- for debugging purposes only.
|
|
//
|
|
//{
|
|
// LIST_ENTRY * pEntry = &list->SL_Head;
|
|
//
|
|
// IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
// ("IpxFwd: Scanning %x for possible segment deletion.\n",list));
|
|
//
|
|
// while (pEntry->Flink != list->SL_Head.Flink) {
|
|
// segment = CONTAINING_RECORD (pEntry->Flink, PACKET_SEGMENT, PS_Link);
|
|
// IpxFwdDbgPrint (DBG_PACKET_ALLOC, DBG_WARNING,
|
|
// ("IpxFwd: Segment: %x\n",segment));
|
|
// pEntry = pEntry->Flink;
|
|
// }
|
|
//}
|
|
|
|
segment = CONTAINING_RECORD (list->SL_Head.Blink,
|
|
PACKET_SEGMENT, PS_Link);
|
|
// Check for all segments with no used blocks
|
|
// except for the last one (delete event the last
|
|
// one if there are no clients)
|
|
while ((segment->PS_BusyCount==0)
|
|
&& ((list->SL_Head.Flink!=&segment->PS_Link)
|
|
|| (list->SL_RefCount<=0))) {
|
|
LONGLONG timeDiff;
|
|
// Check if it has not been used for long enough
|
|
timeDiff = SegmentTimeout - (segment->PS_FreeStamp-curTime);
|
|
if (timeDiff>=0) {
|
|
// Delete the segment
|
|
RemoveEntryList (&segment->PS_Link);
|
|
list->SL_FreeCount -= list->SL_BlockCount;
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
DeleteSegment (segment);
|
|
KeAcquireSpinLock (&list->SL_Lock, &oldIRQL);
|
|
if (!IsListEmpty (&list->SL_Head)) {
|
|
segment = CONTAINING_RECORD (list->SL_Head.Blink,
|
|
PACKET_SEGMENT, PS_Link);
|
|
continue;
|
|
}
|
|
}
|
|
else { // Reschedule the timer otherwise
|
|
if (!list->SL_TimerDpcPending
|
|
&& EnterForwarder ()) {
|
|
list->SL_TimerDpcPending = TRUE;
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
KeSetTimer (&list->SL_Timer,
|
|
*((PLARGE_INTEGER)&timeDiff),
|
|
&list->SL_TimerDpc);
|
|
goto ExitAllocator; // Spinlock is already released
|
|
}
|
|
}
|
|
break;
|
|
} // while
|
|
} // if (IsListEmpty)
|
|
KeReleaseSpinLock (&list->SL_Lock, oldIRQL);
|
|
}
|
|
ExitAllocator:
|
|
LeaveForwarder ();
|
|
#undef list
|
|
}
|
|
|
|
|