/*++ Copyright (c) 2013 Minoca Corp. All Rights Reserved Module Name: buf.c Abstract: This module handles common buffer-related support for the core networking library. Author: Evan Green 5-Apr-2013 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include "netcore.h" // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // // // -------------------------------------------------------------------- Globals // // // Store a pointer to the global list of network buffers. // LIST_ENTRY NetFreeBufferList; PQUEUED_LOCK NetBufferListLock; // // ------------------------------------------------------------------ Functions // NET_API KSTATUS NetAllocateBuffer ( ULONG HeaderSize, ULONG Size, ULONG FooterSize, PNET_LINK Link, ULONG Flags, PNET_PACKET_BUFFER *NewBuffer ) /*++ Routine Description: This routine allocates a network buffer. Arguments: HeaderSize - Supplies the number of header bytes needed. Size - Supplies the number of data bytes needed. FooterSize - Supplies the number of footer bytes needed. Link - Supplies a pointer to the link the buffer will be sent through. If a link is provided, then the buffer will be backed by physically contiguous pages for the link's hardware. If no link is provided, then the buffer will not be backed by physically contiguous pages. Flags - Supplies a bitmask of allocation flags. See NET_ALLOCATE_BUFFER_FLAG_* for definitions. NewBuffer - Supplies a pointer where a pointer to the new allocation will be returned on success. Return Value: STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if a zero length buffer was requested. STATUS_INSUFFICIENT_RESOURCES if the buffer or any auxiliary structures could not be allocated. --*/ { ULONG Alignment; PNET_PACKET_BUFFER Buffer; PHYSICAL_ADDRESS BufferPhysical; ULONGLONG BufferSize; PLIST_ENTRY CurrentEntry; PNET_DATA_LINK_ENTRY DataLinkEntry; ULONG DataLinkMask; ULONG DataSize; ULONG IoBufferFlags; BOOL LockHeld; PHYSICAL_ADDRESS MaximumPhysicalAddress; ULONG MinPacketSize; ULONG PacketSizeFlags; ULONG Padding; NET_PACKET_SIZE_INFORMATION SizeInformation; KSTATUS Status; ULONG TotalSize; ASSERT(KeGetRunLevel() == RunLevelLow); if (Link != NULL) { // // If requested, add the additional headers and footers. // if ((Flags & NET_ALLOCATE_BUFFER_FLAG_ADD_DEVICE_LINK_HEADERS) != 0) { HeaderSize += Link->Properties.PacketSizeInformation.HeaderSize; } if ((Flags & NET_ALLOCATE_BUFFER_FLAG_ADD_DEVICE_LINK_FOOTERS) != 0) { FooterSize += Link->Properties.PacketSizeInformation.FooterSize; } DataLinkMask = NET_ALLOCATE_BUFFER_FLAG_ADD_DATA_LINK_HEADERS | NET_ALLOCATE_BUFFER_FLAG_ADD_DATA_LINK_FOOTERS; if ((Flags & DataLinkMask) != 0) { PacketSizeFlags = 0; if ((Flags & NET_ALLOCATE_BUFFER_FLAG_UNENCRYPTED) != 0) { PacketSizeFlags |= NET_PACKET_SIZE_FLAG_UNENCRYPTED; } DataLinkEntry = Link->DataLinkEntry; DataLinkEntry->Interface.GetPacketSizeInformation( Link->DataLinkContext, &SizeInformation, PacketSizeFlags); if ((Flags & NET_ALLOCATE_BUFFER_FLAG_ADD_DATA_LINK_HEADERS) != 0) { HeaderSize += SizeInformation.HeaderSize; } if ((Flags & NET_ALLOCATE_BUFFER_FLAG_ADD_DATA_LINK_FOOTERS) != 0) { FooterSize += SizeInformation.FooterSize; } } Alignment = Link->Properties.TransmitAlignment; if (Alignment == 0) { Alignment = 1; } ASSERT(POWER_OF_2(Alignment)); MaximumPhysicalAddress = Link->Properties.MaxPhysicalAddress; MinPacketSize = Link->Properties.PacketSizeInformation.MinPacketSize; } else { Alignment = 1; MaximumPhysicalAddress = MAX_UINTN; MinPacketSize = 0; } DataSize = HeaderSize + Size + FooterSize; // // If the total packet size is less than the link's allowed minimum, record // the size of the padding so that it can be zero'd later. // Padding = 0; if (DataSize < MinPacketSize) { Padding = MinPacketSize - DataSize; } TotalSize = DataSize + Padding; TotalSize = ALIGN_RANGE_UP(TotalSize, Alignment); // // Loop through the list looking for the first buffer that fits. // KeAcquireQueuedLock(NetBufferListLock); LockHeld = TRUE; CurrentEntry = NetFreeBufferList.Next; while (CurrentEntry != &NetFreeBufferList) { Buffer = LIST_VALUE(CurrentEntry, NET_PACKET_BUFFER, ListEntry); CurrentEntry = CurrentEntry->Next; BufferSize = Buffer->IoBuffer->Fragment[0].Size; if (BufferSize < TotalSize) { continue; } BufferPhysical = Buffer->IoBuffer->Fragment[0].PhysicalAddress; if (Link == NULL) { if (BufferPhysical != INVALID_PHYSICAL_ADDRESS) { continue; } } else { if ((BufferPhysical == INVALID_PHYSICAL_ADDRESS) || ((BufferPhysical + BufferSize) > MaximumPhysicalAddress) || (ALIGN_RANGE_DOWN(BufferPhysical, Alignment) != BufferPhysical)) { continue; } } LIST_REMOVE(&(Buffer->ListEntry)); Status = STATUS_SUCCESS; goto AllocateBufferEnd; } KeReleaseQueuedLock(NetBufferListLock); LockHeld = FALSE; // // Allocate a network packet buffer, but do not bother to zero it. This // routine takes care to initialize all the necessary fields before it is // used. // Buffer = MmAllocatePagedPool(sizeof(NET_PACKET_BUFFER), NET_CORE_ALLOCATION_TAG); if (Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AllocateBufferEnd; } // // A buffer will need to be allocated. // if (Link != NULL) { IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS; Buffer->IoBuffer = MmAllocateNonPagedIoBuffer(0, MaximumPhysicalAddress, Alignment, TotalSize, IoBufferFlags); } else { Buffer->IoBuffer = MmAllocatePagedIoBuffer(TotalSize, 0); } if (Buffer->IoBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AllocateBufferEnd; } ASSERT(Buffer->IoBuffer->FragmentCount == 1); Buffer->BufferPhysicalAddress = Buffer->IoBuffer->Fragment[0].PhysicalAddress; Buffer->Buffer = Buffer->IoBuffer->Fragment[0].VirtualAddress; Status = STATUS_SUCCESS; AllocateBufferEnd: if (LockHeld != FALSE) { KeReleaseQueuedLock(NetBufferListLock); } if (!KSUCCESS(Status)) { if (Buffer != NULL) { if (Buffer->IoBuffer != NULL) { MmFreeIoBuffer(Buffer->IoBuffer); } MmFreePagedPool(Buffer); Buffer = NULL; } } else { Buffer->Flags = 0; if ((Flags & NET_ALLOCATE_BUFFER_FLAG_UNENCRYPTED) != 0) { Buffer->Flags |= NET_PACKET_FLAG_UNENCRYPTED; } Buffer->BufferSize = TotalSize; Buffer->DataSize = DataSize; Buffer->DataOffset = HeaderSize; Buffer->FooterOffset = Buffer->DataOffset + Size; // // If padding was added to the packet, then zero it. // if (Padding != 0) { RtlZeroMemory(Buffer->Buffer + DataSize, Padding); } } *NewBuffer = Buffer; return Status; } NET_API VOID NetFreeBuffer ( PNET_PACKET_BUFFER Buffer ) /*++ Routine Description: This routine frees a previously allocated network buffer. Arguments: Buffer - Supplies a pointer to the buffer returned by the allocation routine. Return Value: None. --*/ { KeAcquireQueuedLock(NetBufferListLock); INSERT_AFTER(&(Buffer->ListEntry), &NetFreeBufferList); KeReleaseQueuedLock(NetBufferListLock); return; } NET_API VOID NetDestroyBufferList ( PNET_PACKET_LIST BufferList ) /*++ Routine Description: This routine destroys a list of network packet buffers, releasing all of its associated resources, not including the buffer list structure. Arguments: BufferList - Supplies a pointer to the buffer list to be destroyed. Return Value: None. --*/ { PNET_PACKET_BUFFER Buffer; while (NET_PACKET_LIST_EMPTY(BufferList) == FALSE) { Buffer = LIST_VALUE(BufferList->Head.Next, NET_PACKET_BUFFER, ListEntry); NET_REMOVE_PACKET_FROM_LIST(Buffer, BufferList); NetFreeBuffer(Buffer); } return; } KSTATUS NetpInitializeBuffers ( VOID ) /*++ Routine Description: This routine initializes support for network buffers. Arguments: None. Return Value: Status code. --*/ { INITIALIZE_LIST_HEAD(&NetFreeBufferList); NetBufferListLock = KeCreateQueuedLock(); if (NetBufferListLock == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } return STATUS_SUCCESS; } VOID NetpDestroyBuffers ( VOID ) /*++ Routine Description: This routine destroys any allocations made during network buffer initialization. Arguments: None. Return Value: None. --*/ { if (NetBufferListLock != NULL) { KeDestroyQueuedLock(NetBufferListLock); } return; } // // --------------------------------------------------------- Internal Functions //