diff options
author | Michael Brown | 2015-07-15 14:43:58 +0200 |
---|---|---|
committer | Michael Brown | 2015-07-22 22:17:47 +0200 |
commit | cbbd6b761ed315ab6df82f1434657ef76bb3e676 (patch) | |
tree | a241c73bb7bf7a94555c62e0c939da8ddac798da /src/core | |
parent | [xfer] Add xfer_check_order() utility function (diff) | |
download | ipxe-cbbd6b761ed315ab6df82f1434657ef76bb3e676.tar.gz ipxe-cbbd6b761ed315ab6df82f1434657ef76bb3e676.tar.xz ipxe-cbbd6b761ed315ab6df82f1434657ef76bb3e676.zip |
[xferbuf] Generalise to handle umalloc()-based buffers
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/xferbuf.c | 217 |
1 files changed, 195 insertions, 22 deletions
diff --git a/src/core/xferbuf.c b/src/core/xferbuf.c index c2c7eb57..7f9780b3 100644 --- a/src/core/xferbuf.c +++ b/src/core/xferbuf.c @@ -28,6 +28,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/xfer.h> #include <ipxe/iobuf.h> +#include <ipxe/umalloc.h> +#include <ipxe/profile.h> #include <ipxe/xferbuf.h> /** @file @@ -36,14 +38,26 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** Data delivery profiler */ +static struct profiler xferbuf_deliver_profiler __profiler = + { .name = "xferbuf.deliver" }; + +/** Data write profiler */ +static struct profiler xferbuf_write_profiler __profiler = + { .name = "xferbuf.write" }; + +/** Data read profiler */ +static struct profiler xferbuf_read_profiler __profiler = + { .name = "xferbuf.read" }; + /** - * Finish using data transfer buffer + * Free data transfer buffer * * @v xferbuf Data transfer buffer */ -void xferbuf_done ( struct xfer_buffer *xferbuf ) { - free ( xferbuf->data ); - xferbuf->data = NULL; +void xferbuf_free ( struct xfer_buffer *xferbuf ) { + + xferbuf->op->realloc ( xferbuf, 0 ); xferbuf->len = 0; xferbuf->pos = 0; } @@ -56,26 +70,78 @@ void xferbuf_done ( struct xfer_buffer *xferbuf ) { * @ret rc Return status code */ static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { - void *new_data; + int rc; /* If buffer is already large enough, do nothing */ if ( len <= xferbuf->len ) return 0; /* Extend buffer */ - new_data = realloc ( xferbuf->data, len ); - if ( ! new_data ) { + if ( ( rc = xferbuf->op->realloc ( xferbuf, len ) ) != 0 ) { DBGC ( xferbuf, "XFERBUF %p could not extend buffer to " - "%zd bytes\n", xferbuf, len ); - return -ENOSPC; + "%zd bytes: %s\n", xferbuf, len, strerror ( rc ) ); + return rc; } - xferbuf->data = new_data; xferbuf->len = len; return 0; } /** + * Write to data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + */ +int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + size_t max_len; + int rc; + + /* Check for overflow */ + max_len = ( offset + len ); + if ( max_len < offset ) + return -EOVERFLOW; + + /* Ensure buffer is large enough to contain this write */ + if ( ( rc = xferbuf_ensure_size ( xferbuf, max_len ) ) != 0 ) + return rc; + + /* Copy data to buffer */ + profile_start ( &xferbuf_write_profiler ); + xferbuf->op->write ( xferbuf, offset, data, len ); + profile_stop ( &xferbuf_write_profiler ); + + return 0; +} + +/** + * Read from data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + */ +int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + + /* Check that read is within buffer range */ + if ( ( offset > xferbuf->len ) || + ( len > ( xferbuf->len - offset ) ) ) + return -ENOENT; + + /* Copy data from buffer */ + profile_start ( &xferbuf_read_profiler ); + xferbuf->op->read ( xferbuf, offset, data, len ); + profile_stop ( &xferbuf_read_profiler ); + + return 0; +} + +/** * Add received data to data transfer buffer * * @v xferbuf Data transfer buffer @@ -85,28 +151,135 @@ static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { */ int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - size_t len; - size_t max; + size_t len = iob_len ( iobuf ); + size_t pos; int rc; + /* Start profiling */ + profile_start ( &xferbuf_deliver_profiler ); + /* Calculate new buffer position */ + pos = xferbuf->pos; if ( meta->flags & XFER_FL_ABS_OFFSET ) - xferbuf->pos = 0; - xferbuf->pos += meta->offset; + pos = 0; + pos += meta->offset; - /* Ensure that we have enough buffer space for this data */ - len = iob_len ( iobuf ); - max = ( xferbuf->pos + len ); - if ( ( rc = xferbuf_ensure_size ( xferbuf, max ) ) != 0 ) + /* Write data to buffer */ + if ( ( rc = xferbuf_write ( xferbuf, pos, iobuf->data, len ) ) != 0 ) goto done; - /* Copy data to buffer */ - memcpy ( ( xferbuf->data + xferbuf->pos ), iobuf->data, len ); - /* Update current buffer position */ - xferbuf->pos += len; + xferbuf->pos = ( pos + len ); done: free_iob ( iobuf ); + profile_stop ( &xferbuf_deliver_profiler ); return rc; } + +/** + * Reallocate malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int xferbuf_malloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) { + void *new_data; + + new_data = realloc ( xferbuf->data, len ); + if ( ! new_data ) + return -ENOSPC; + xferbuf->data = new_data; + return 0; +} + +/** + * Write data to malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void xferbuf_malloc_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + + memcpy ( ( xferbuf->data + offset ), data, len ); +} + +/** + * Read data from malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void xferbuf_malloc_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + + memcpy ( data, ( xferbuf->data + offset ), len ); +} + +/** malloc()-based data buffer operations */ +struct xfer_buffer_operations xferbuf_malloc_operations = { + .realloc = xferbuf_malloc_realloc, + .write = xferbuf_malloc_write, + .read = xferbuf_malloc_read, +}; + +/** + * Reallocate umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int xferbuf_umalloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) { + userptr_t *udata = xferbuf->data; + userptr_t new_udata; + + new_udata = urealloc ( *udata, len ); + if ( ! new_udata ) + return -ENOSPC; + *udata = new_udata; + return 0; +} + +/** + * Write data to umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void xferbuf_umalloc_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + userptr_t *udata = xferbuf->data; + + copy_to_user ( *udata, offset, data, len ); +} + +/** + * Read data from umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void xferbuf_umalloc_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + userptr_t *udata = xferbuf->data; + + copy_from_user ( data, *udata, offset, len ); +} + +/** umalloc()-based data buffer operations */ +struct xfer_buffer_operations xferbuf_umalloc_operations = { + .realloc = xferbuf_umalloc_realloc, + .write = xferbuf_umalloc_write, + .read = xferbuf_umalloc_read, +}; |