sortix-mirror/kernel/net/packet.cpp

105 lines
3.3 KiB
C++

/*
* Copyright (c) 2015 Meisaka Yukara.
* Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* net/packet.cpp
* Reference counted network packets.
*/
#include <assert.h>
#include <errno.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/packet.h>
#include <sortix/kernel/pci-mmio.h>
namespace Sortix {
// Limit the memory usage of network packets to this fraction of total memory.
// This constant is documented in if(4) under NOTES.
static const size_t MAX_PACKET_FRACTION = 16;
// Keep this amount of virtually mapped buffers around at all times.
// This constant is documented in if(4) under NOTES.
static const size_t PACKET_CACHE_TARGET_SIZE = 384;
// A cache of physical memory allocations for quick allocation to new packets.
static kthread_mutex_t packet_cache_lock = KTHREAD_MUTEX_INITIALIZER;
static paddrmapped_t* packet_cache = NULL;
static size_t packet_cache_used = 0;
static size_t packet_cache_allocated = 0;
static size_t packet_count = 0;
Packet::Packet(paddrmapped_t _pmap) : pmap(_pmap)
{
from = (unsigned char*) pmap.from;
length = 0;
offset = 0;
netif = NULL;
packet_count++;
}
Packet::~Packet()
{
// Refuse to do recursive destructor calls that could stack overflow.
assert(!next);
ScopedLock lock(&packet_cache_lock);
if ( packet_cache_used < packet_cache_allocated )
packet_cache[packet_cache_used++] = pmap;
else
FreeAllocatedAndMappedPage(&pmap);
packet_count--;
}
Ref<Packet> GetPacket()
{
ScopedLock lock(&packet_cache_lock);
if ( packet_cache == NULL )
{
size_t new_allocated = PACKET_CACHE_TARGET_SIZE;
packet_cache = new paddrmapped_t[new_allocated];
if ( !packet_cache )
return errno = ENOBUFS, Ref<Packet>(NULL);
packet_cache_allocated = new_allocated;
}
paddrmapped_t pmap;
// Fast reuse of an existing physical allocation if available.
if ( 0 < packet_cache_used )
pmap = packet_cache[--packet_cache_used];
// Otherwise make a new physical allocation for the packet.
else
{
size_t total_memory;
Memory::Statistics(NULL, &total_memory);
size_t total_pages = total_memory / Page::Size();
size_t max_packets = total_pages / MAX_PACKET_FRACTION;
if ( max_packets <= packet_count )
return errno = ENOBUFS, Ref<Packet>(NULL);
if ( !AllocateAndMapPage(&pmap, PAGE_USAGE_NETWORK_PACKET) )
return errno = ENOBUFS, Ref<Packet>(NULL);
}
Ref<Packet> pkt = Ref<Packet>(new Packet(pmap));
if ( !pkt )
{
FreeAllocatedAndMappedPage(&pmap);
return errno = ENOBUFS, Ref<Packet>(NULL);
}
return pkt;
}
} // namespace Sortix