diff options
author | Michael Brown | 2012-07-08 18:14:08 +0200 |
---|---|---|
committer | Michael Brown | 2012-07-08 18:54:53 +0200 |
commit | 4a8a7bd91a6911779e3972a6d00d5a25bd7c0367 (patch) | |
tree | d793f7afd35e41b7a05df35ef272ba8a6717f344 /src/core/iobuf.c | |
parent | [netdevice] Process all received packets in net_poll() (diff) | |
download | ipxe-4a8a7bd91a6911779e3972a6d00d5a25bd7c0367.tar.gz ipxe-4a8a7bd91a6911779e3972a6d00d5a25bd7c0367.tar.xz ipxe-4a8a7bd91a6911779e3972a6d00d5a25bd7c0367.zip |
[iobuf] Allocate I/O buffer descriptor separately to conserve aligned memory
I/O buffers are allocated on aligned boundaries. The I/O buffer
descriptor (the struct io_buffer) is currently attached to the end of
the I/O buffer. When the size of the buffer is close to its
alignment, this can waste large amounts of aligned memory.
For example, a network card using 2048-byte receive buffers will end
up allocating 2072 bytes on a 2048-byte boundary. This effectively
wastes 50% of the available memory.
Improve the situation by allocating the descriptor separately from the
main I/O buffer if inline allocation would cause the total allocated
size to cross the alignment boundary.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/core/iobuf.c')
-rw-r--r-- | src/core/iobuf.c | 66 |
1 files changed, 53 insertions, 13 deletions
diff --git a/src/core/iobuf.c b/src/core/iobuf.c index 3dfaf18c..0c61306e 100644 --- a/src/core/iobuf.c +++ b/src/core/iobuf.c @@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @c IOBUF_SIZE. */ struct io_buffer * alloc_iob ( size_t len ) { - struct io_buffer *iobuf = NULL; + struct io_buffer *iobuf; size_t align; void *data; @@ -57,14 +57,37 @@ struct io_buffer * alloc_iob ( size_t len ) { */ align = ( 1 << fls ( len - 1 ) ); - /* Allocate memory for buffer plus descriptor */ - data = malloc_dma ( len + sizeof ( *iobuf ), align ); - if ( ! data ) - return NULL; + /* Allocate buffer plus descriptor as a single unit, unless + * doing so will push the total size over the alignment + * boundary. + */ + if ( ( len + sizeof ( *iobuf ) ) <= align ) { + + /* Allocate memory for buffer plus descriptor */ + data = malloc_dma ( len + sizeof ( *iobuf ), align ); + if ( ! data ) + return NULL; + iobuf = ( data + len ); + + } else { + + /* Allocate memory for buffer */ + data = malloc_dma ( len, align ); + if ( ! data ) + return NULL; - iobuf = ( struct io_buffer * ) ( data + len ); + /* Allocate memory for descriptor */ + iobuf = malloc ( sizeof ( *iobuf ) ); + if ( ! iobuf ) { + free_dma ( data, len ); + return NULL; + } + } + + /* Populate descriptor */ iobuf->head = iobuf->data = iobuf->tail = data; - iobuf->end = iobuf; + iobuf->end = ( data + len ); + return iobuf; } @@ -75,12 +98,29 @@ struct io_buffer * alloc_iob ( size_t len ) { * @v iobuf I/O buffer */ void free_iob ( struct io_buffer *iobuf ) { - if ( iobuf ) { - assert ( iobuf->head <= iobuf->data ); - assert ( iobuf->data <= iobuf->tail ); - assert ( iobuf->tail <= iobuf->end ); - free_dma ( iobuf->head, - ( iobuf->end - iobuf->head ) + sizeof ( *iobuf ) ); + size_t len; + + /* Allow free_iob(NULL) to be valid */ + if ( ! iobuf ) + return; + + /* Sanity checks */ + assert ( iobuf->head <= iobuf->data ); + assert ( iobuf->data <= iobuf->tail ); + assert ( iobuf->tail <= iobuf->end ); + + /* Free buffer */ + len = ( iobuf->end - iobuf->head ); + if ( iobuf->end == iobuf ) { + + /* Descriptor is inline */ + free_dma ( iobuf->head, ( len + sizeof ( *iobuf ) ) ); + + } else { + + /* Descriptor is detached */ + free_dma ( iobuf->head, len ); + free ( iobuf ); } } |