diff --git a/src/core/iobuf.c b/src/core/iobuf.c new file mode 100644 index 00000000..d04ede51 --- /dev/null +++ b/src/core/iobuf.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +/** @file + * + * I/O buffers + * + */ + +/** + * Allocate I/O buffer + * + * @v len Required length of buffer + * @ret iobuf I/O buffer, or NULL if none available + * + * The I/O buffer will be physically aligned to a multiple of + * @c IOBUF_SIZE. + */ +struct io_buffer * alloc_iob ( size_t len ) { + struct io_buffer *iobuf = NULL; + void *data; + + /* Pad to minimum length */ + if ( len < IOB_ZLEN ) + len = IOB_ZLEN; + + /* Align buffer length */ + len = ( len + __alignof__( *iobuf ) - 1 ) & + ~( __alignof__( *iobuf ) - 1 ); + + /* Allocate memory for buffer plus descriptor */ + data = malloc_dma ( len + sizeof ( *iobuf ), IOB_ALIGN ); + if ( ! data ) + return NULL; + + iobuf = ( struct io_buffer * ) ( data + len ); + iobuf->head = iobuf->data = iobuf->tail = data; + iobuf->end = iobuf; + return iobuf; +} + +/** + * Free I/O buffer + * + * @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 ) ); + } +} diff --git a/src/include/gpxe/iobuf.h b/src/include/gpxe/iobuf.h index cf99050d..b5de6a51 100644 --- a/src/include/gpxe/iobuf.h +++ b/src/include/gpxe/iobuf.h @@ -11,6 +11,26 @@ #include #include +/** + * I/O buffer alignment + * + * I/O buffers allocated via alloc_iob() are guaranteed to be + * physically aligned to this boundary. Some cards cannot DMA across + * a 4kB boundary. With a standard Ethernet MTU, aligning to a 2kB + * boundary is sufficient to guarantee no 4kB boundary crossings. For + * a jumbo Ethernet MTU, a packet may be larger than 4kB anyway. + */ +#define IOB_ALIGN 2048 + +/** + * Minimum I/O buffer length + * + * alloc_iob() will round up the allocated length to this size if + * necessary. This is used on behalf of hardware that is not capable + * of auto-padding. + */ +#define IOB_ZLEN 64 + /** * A persistent I/O buffer * @@ -40,109 +60,109 @@ struct io_buffer { /** * Reserve space at start of I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @v len Length to reserve * @ret data Pointer to new start of buffer */ -static inline void * iob_reserve ( struct io_buffer *iob, size_t len ) { - iob->data += len; - iob->tail += len; - assert ( iob->tail <= iob->end ); - return iob->data; +static inline void * iob_reserve ( struct io_buffer *iobuf, size_t len ) { + iobuf->data += len; + iobuf->tail += len; + assert ( iobuf->tail <= iobuf->end ); + return iobuf->data; } /** * Add data to start of I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @v len Length to add * @ret data Pointer to new start of buffer */ -static inline void * iob_push ( struct io_buffer *iob, size_t len ) { - iob->data -= len; - assert ( iob->data >= iob->head ); - return iob->data; +static inline void * iob_push ( struct io_buffer *iobuf, size_t len ) { + iobuf->data -= len; + assert ( iobuf->data >= iobuf->head ); + return iobuf->data; } /** * Remove data from start of I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @v len Length to remove * @ret data Pointer to new start of buffer */ -static inline void * iob_pull ( struct io_buffer *iob, size_t len ) { - iob->data += len; - assert ( iob->data <= iob->tail ); - return iob->data; +static inline void * iob_pull ( struct io_buffer *iobuf, size_t len ) { + iobuf->data += len; + assert ( iobuf->data <= iobuf->tail ); + return iobuf->data; } /** * Add data to end of I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @v len Length to add * @ret data Pointer to newly added space */ -static inline void * iob_put ( struct io_buffer *iob, size_t len ) { - void *old_tail = iob->tail; - iob->tail += len; - assert ( iob->tail <= iob->end ); +static inline void * iob_put ( struct io_buffer *iobuf, size_t len ) { + void *old_tail = iobuf->tail; + iobuf->tail += len; + assert ( iobuf->tail <= iobuf->end ); return old_tail; } /** * Remove data from end of I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @v len Length to remove */ -static inline void iob_unput ( struct io_buffer *iob, size_t len ) { - iob->tail -= len; - assert ( iob->tail >= iob->data ); +static inline void iob_unput ( struct io_buffer *iobuf, size_t len ) { + iobuf->tail -= len; + assert ( iobuf->tail >= iobuf->data ); } /** * Empty an I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer */ -static inline void iob_empty ( struct io_buffer *iob ) { - iob->tail = iob->data; +static inline void iob_empty ( struct io_buffer *iobuf ) { + iobuf->tail = iobuf->data; } /** * Calculate length of data in an I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @ret len Length of data in buffer */ -static inline size_t iob_len ( struct io_buffer *iob ) { - return ( iob->tail - iob->data ); +static inline size_t iob_len ( struct io_buffer *iobuf ) { + return ( iobuf->tail - iobuf->data ); } /** * Calculate available space at start of an I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @ret len Length of data available at start of buffer */ -static inline size_t iob_headroom ( struct io_buffer *iob ) { - return ( iob->data - iob->head ); +static inline size_t iob_headroom ( struct io_buffer *iobuf ) { + return ( iobuf->data - iobuf->head ); } /** * Calculate available space at end of an I/O buffer * - * @v iob I/O buffer + * @v iobuf I/O buffer * @ret len Length of data available at end of buffer */ -static inline size_t iob_tailroom ( struct io_buffer *iob ) { - return ( iob->end - iob->tail ); +static inline size_t iob_tailroom ( struct io_buffer *iobuf ) { + return ( iobuf->end - iobuf->tail ); } extern struct io_buffer * alloc_iob ( size_t len ); -extern void free_iob ( struct io_buffer *iob ); -extern void iob_pad ( struct io_buffer *iob, size_t min_len ); +extern void free_iob ( struct io_buffer *iobuf ); +extern void iob_pad ( struct io_buffer *iobuf, size_t min_len ); #endif /* _GPXE_IOBUF_H */