/*++
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 */