mirror of https://github.com/lianthony/NT4.0
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.
597 lines
14 KiB
597 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
packet.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code that implements the TP_PACKET object, which
|
|
describes an NDIS packet.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "st.h"
|
|
|
|
//
|
|
// This is temporary; this is the quota that we charge for a receive
|
|
// packet for now, until we fix the problem with token-ring needing
|
|
// big packets and using all the memory. The number is the actual
|
|
// value for Ethernet.
|
|
//
|
|
|
|
#if 1
|
|
#define RECEIVE_BUFFER_QUOTA(_DeviceContext) 1533
|
|
#else
|
|
#define RECEIVE_BUFFER_QUOTA(_DeviceContext) (_DeviceContext)->ReceiveBufferLength
|
|
#endif
|
|
|
|
|
|
|
|
|
|
VOID
|
|
StAllocateSendPacket(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
OUT PTP_PACKET *TransportSendPacket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates storage for a send packet. Some initialization
|
|
is done here.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
TransportSendPacket - Returns a pointer to the packet, or NULL if no
|
|
storage can be allocated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PTP_PACKET Packet;
|
|
NDIS_STATUS NdisStatus;
|
|
PNDIS_PACKET NdisPacket;
|
|
PSEND_PACKET_TAG SendTag;
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
if ((DeviceContext->MemoryLimit != 0) &&
|
|
((DeviceContext->MemoryUsage + DeviceContext->PacketLength) >
|
|
DeviceContext->MemoryLimit)) {
|
|
PANIC("ST: Could not allocate send packet: limit\n");
|
|
StWriteResourceErrorLog (DeviceContext, DeviceContext->PacketLength, 107);
|
|
*TransportSendPacket = NULL;
|
|
return;
|
|
}
|
|
|
|
Packet = (PTP_PACKET)ExAllocatePool (NonPagedPool, DeviceContext->PacketLength);
|
|
if (Packet == NULL) {
|
|
PANIC("ST: Could not allocate send packet: no pool\n");
|
|
StWriteResourceErrorLog (DeviceContext, DeviceContext->PacketLength, 207);
|
|
*TransportSendPacket = NULL;
|
|
return;
|
|
}
|
|
RtlZeroMemory (Packet, DeviceContext->PacketLength);
|
|
|
|
DeviceContext->MemoryUsage += DeviceContext->PacketLength;
|
|
|
|
NdisAllocatePacket (
|
|
&NdisStatus,
|
|
&NdisPacket,
|
|
DeviceContext->SendPacketPoolHandle);
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
ExFreePool (Packet);
|
|
StWriteResourceErrorLog (DeviceContext, 0, 307);
|
|
*TransportSendPacket = NULL;
|
|
return;
|
|
}
|
|
|
|
NdisAllocateBuffer(
|
|
&NdisStatus,
|
|
&NdisBuffer,
|
|
DeviceContext->NdisBufferPoolHandle,
|
|
Packet->Header,
|
|
DeviceContext->PacketHeaderLength);
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
NdisFreePacket (NdisPacket);
|
|
ExFreePool (Packet);
|
|
*TransportSendPacket = NULL;
|
|
return;
|
|
}
|
|
|
|
NdisChainBufferAtFront (NdisPacket, NdisBuffer);
|
|
|
|
Packet->NdisPacket = NdisPacket;
|
|
SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved;
|
|
SendTag->Type = TYPE_I_FRAME;
|
|
SendTag->Packet = Packet;
|
|
SendTag->Owner = NULL;
|
|
|
|
Packet->Type = ST_PACKET_SIGNATURE;
|
|
Packet->Size = sizeof (TP_PACKET);
|
|
Packet->Provider = DeviceContext;
|
|
|
|
++DeviceContext->PacketAllocated;
|
|
|
|
*TransportSendPacket = Packet;
|
|
|
|
} /* StAllocateSendPacket */
|
|
|
|
|
|
VOID
|
|
StDeallocateSendPacket(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
IN PTP_PACKET TransportSendPacket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees storage for a send packet.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
TransportSendPacket - A pointer to the send packet.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PNDIS_PACKET NdisPacket = TransportSendPacket->NdisPacket;
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
|
|
if (NdisBuffer != NULL) {
|
|
NdisFreeBuffer (NdisBuffer);
|
|
}
|
|
|
|
NdisFreePacket (NdisPacket);
|
|
ExFreePool (TransportSendPacket);
|
|
|
|
--DeviceContext->PacketAllocated;
|
|
DeviceContext->MemoryUsage -= DeviceContext->PacketLength;
|
|
|
|
} /* StDeallocateSendPacket */
|
|
|
|
|
|
VOID
|
|
StAllocateReceivePacket(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
OUT PNDIS_PACKET *TransportReceivePacket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates storage for a receive packet. Some initialization
|
|
is done here.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
TransportReceivePacket - Returns a pointer to the packet, or NULL if no
|
|
storage can be allocated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NDIS_STATUS NdisStatus;
|
|
PNDIS_PACKET NdisPacket;
|
|
PRECEIVE_PACKET_TAG ReceiveTag;
|
|
|
|
//
|
|
// This does not count in DeviceContext->MemoryUsage because
|
|
// the storage is allocated when we allocate the packet pool.
|
|
//
|
|
|
|
NdisAllocatePacket (
|
|
&NdisStatus,
|
|
&NdisPacket,
|
|
DeviceContext->ReceivePacketPoolHandle);
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
StWriteResourceErrorLog (DeviceContext, 0, 309);
|
|
*TransportReceivePacket = NULL;
|
|
return;
|
|
}
|
|
|
|
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
|
|
ReceiveTag->PacketType = TYPE_AT_INDICATE;
|
|
|
|
++DeviceContext->ReceivePacketAllocated;
|
|
|
|
*TransportReceivePacket = NdisPacket;
|
|
|
|
} /* StAllocateReceivePacket */
|
|
|
|
|
|
VOID
|
|
StDeallocateReceivePacket(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
IN PNDIS_PACKET TransportReceivePacket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees storage for a receive packet.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
TransportReceivePacket - A pointer to the packet.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NdisFreePacket (TransportReceivePacket);
|
|
|
|
--DeviceContext->ReceivePacketAllocated;
|
|
|
|
} /* StDeallocateReceivePacket */
|
|
|
|
|
|
VOID
|
|
StAllocateReceiveBuffer(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
OUT PBUFFER_TAG *TransportReceiveBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates storage for a receive buffer. Some initialization
|
|
is done here.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
TransportReceiveBuffer - Returns a pointer to the buffer, or NULL if no
|
|
storage can be allocated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PBUFFER_TAG BufferTag;
|
|
NDIS_STATUS NdisStatus;
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
|
|
if ((DeviceContext->MemoryLimit != 0) &&
|
|
((DeviceContext->MemoryUsage + RECEIVE_BUFFER_QUOTA(DeviceContext)) >
|
|
DeviceContext->MemoryLimit)) {
|
|
PANIC("ST: Could not allocate receive buffer: limit\n");
|
|
StWriteResourceErrorLog (DeviceContext, RECEIVE_BUFFER_QUOTA(DeviceContext), 108);
|
|
*TransportReceiveBuffer = NULL;
|
|
return;
|
|
}
|
|
|
|
BufferTag = (PBUFFER_TAG)ExAllocatePool (
|
|
NonPagedPoolCacheAligned,
|
|
DeviceContext->ReceiveBufferLength);
|
|
|
|
if (BufferTag == NULL) {
|
|
PANIC("ST: Could not allocate receive buffer: no pool\n");
|
|
StWriteResourceErrorLog (DeviceContext, DeviceContext->ReceiveBufferLength, 208);
|
|
*TransportReceiveBuffer = NULL;
|
|
return;
|
|
}
|
|
|
|
DeviceContext->MemoryUsage += RECEIVE_BUFFER_QUOTA(DeviceContext);
|
|
|
|
//
|
|
// point to the buffer for NDIS
|
|
//
|
|
|
|
NdisAllocateBuffer(
|
|
&NdisStatus,
|
|
&NdisBuffer,
|
|
DeviceContext->NdisBufferPoolHandle,
|
|
BufferTag->Buffer,
|
|
DeviceContext->MaxReceivePacketSize);
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
ExFreePool (BufferTag);
|
|
*TransportReceiveBuffer = NULL;
|
|
return;
|
|
}
|
|
|
|
BufferTag->Length = DeviceContext->MaxReceivePacketSize;
|
|
BufferTag->NdisBuffer = NdisBuffer;
|
|
|
|
++DeviceContext->ReceiveBufferAllocated;
|
|
|
|
*TransportReceiveBuffer = BufferTag;
|
|
|
|
} /* StAllocateReceiveBuffer */
|
|
|
|
|
|
VOID
|
|
StDeallocateReceiveBuffer(
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
IN PBUFFER_TAG TransportReceiveBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees storage for a receive buffer.
|
|
|
|
NOTE: This routine is called with the device context spinlock
|
|
held, or at such a time as synchronization is unnecessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
TransportReceiveBuffer - A pointer to the buffer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NdisFreeBuffer (TransportReceiveBuffer->NdisBuffer);
|
|
ExFreePool (TransportReceiveBuffer);
|
|
|
|
--DeviceContext->ReceiveBufferAllocated;
|
|
DeviceContext->MemoryUsage -= RECEIVE_BUFFER_QUOTA(DeviceContext);
|
|
|
|
} /* StDeallocateReceiveBuffer */
|
|
|
|
|
|
NTSTATUS
|
|
StCreatePacket(
|
|
PDEVICE_CONTEXT DeviceContext,
|
|
PTP_PACKET *Packet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a packet from the device context's pool,
|
|
and prepares the MAC and DLC headers for use by the connection.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to our device context to charge the packet to.
|
|
|
|
Packet - Pointer to a place where we will return a pointer to the
|
|
allocated packet.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql;
|
|
PSINGLE_LIST_ENTRY s;
|
|
PTP_PACKET ThePacket;
|
|
|
|
s = ExInterlockedPopEntryList (
|
|
&DeviceContext->PacketPool,
|
|
&DeviceContext->Interlock);
|
|
|
|
if (s == NULL) {
|
|
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
|
++DeviceContext->PacketExhausted;
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage);
|
|
|
|
ThePacket->Provider = DeviceContext; // who owns this packet
|
|
ThePacket->PacketSent = FALSE;
|
|
ThePacket->PacketNoNdisBuffer = FALSE;
|
|
|
|
*Packet = ThePacket; // return pointer to the packet.
|
|
return STATUS_SUCCESS;
|
|
} /* StCreatePacket */
|
|
|
|
|
|
VOID
|
|
StDestroyPacket(
|
|
PTP_PACKET Packet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys a packet, thereby returning it to the pool. If
|
|
it is determined that there is at least one connection waiting for a
|
|
packet to become available (and it just has), then the connection is
|
|
removed from the device context's list and AdvanceSend is called to
|
|
prep the connection further.
|
|
|
|
Arguments:
|
|
|
|
Packet - Pointer to a packet to be returned to the pool.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql1;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
PTP_CONNECTION Connection;
|
|
PLIST_ENTRY p;
|
|
PNDIS_BUFFER HeaderBuffer;
|
|
PNDIS_BUFFER NdisBuffer;
|
|
|
|
|
|
//
|
|
// Strip off and unmap the buffers describing data and header.
|
|
//
|
|
|
|
NdisUnchainBufferAtFront (Packet->NdisPacket, &HeaderBuffer);
|
|
|
|
// data buffers get thrown away
|
|
|
|
if (Packet->PacketNoNdisBuffer) {
|
|
|
|
//
|
|
// If the NDIS_BUFFER chain is not ours, then we can't
|
|
// start unchaining since that would mess up the queue;
|
|
// instead we just drop the rest of the chain.
|
|
//
|
|
|
|
NdisReinitializePacket (Packet->NdisPacket);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Return all the NDIS_BUFFERs to the system.
|
|
//
|
|
|
|
NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer);
|
|
while (NdisBuffer != NULL) {
|
|
NdisFreeBuffer (NdisBuffer);
|
|
NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer);
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT (HeaderBuffer != NULL);
|
|
NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL;
|
|
|
|
NdisChainBufferAtFront (Packet->NdisPacket, HeaderBuffer);
|
|
|
|
|
|
//
|
|
// Put the packet back for use again.
|
|
//
|
|
|
|
DeviceContext = Packet->Provider;
|
|
|
|
ExInterlockedPushEntryList (
|
|
&DeviceContext->PacketPool,
|
|
(PSINGLE_LIST_ENTRY)&Packet->Linkage,
|
|
&DeviceContext->Interlock);
|
|
|
|
//
|
|
// If there is a connection waiting to ship out more packets, then
|
|
// wake it up and start packetizing again.
|
|
//
|
|
// We do a quick check without the lock; there is a small
|
|
// window where we may not take someone off, but this
|
|
// window exists anyway and we assume that more packets
|
|
// will be freed in the future.
|
|
//
|
|
|
|
if (IsListEmpty (&DeviceContext->PacketWaitQueue)) {
|
|
return;
|
|
}
|
|
|
|
p = ExInterlockedRemoveHeadList(
|
|
&DeviceContext->PacketWaitQueue,
|
|
&DeviceContext->SpinLock);
|
|
|
|
if (p != NULL) {
|
|
|
|
//
|
|
// Remove a connection from the "packet starved" queue.
|
|
//
|
|
|
|
Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketWaitLinkage);
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1);
|
|
Connection->Flags &= ~CONNECTION_FLAGS_SUSPENDED;
|
|
|
|
//
|
|
// Place the connection on the packetize queue and start
|
|
// packetizing the next connection to be serviced. If he
|
|
// is already on the packetize queue for some reason, then
|
|
// don't do this.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
|
|
|
if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING) &&
|
|
!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) {
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
StReferenceConnection ("Packet available", Connection);
|
|
|
|
ExInterlockedInsertTailList(
|
|
&DeviceContext->PacketizeQueue,
|
|
&Connection->PacketizeLinkage,
|
|
&DeviceContext->SpinLock);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
|
PacketizeConnections (DeviceContext);
|
|
|
|
}
|
|
|
|
} /* StDestroyPacket */
|
|
|