diff --git a/src/core/xferbuf.c b/src/core/xferbuf.c index c2c7eb576..7f9780b3a 100644 --- a/src/core/xferbuf.c +++ b/src/core/xferbuf.c @@ -28,6 +28,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include /** @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,25 +70,77 @@ 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 * @@ -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; - /* Calculate new buffer position */ - if ( meta->flags & XFER_FL_ABS_OFFSET ) - xferbuf->pos = 0; - xferbuf->pos += meta->offset; + /* Start profiling */ + profile_start ( &xferbuf_deliver_profiler ); - /* 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 ) + /* Calculate new buffer position */ + pos = xferbuf->pos; + if ( meta->flags & XFER_FL_ABS_OFFSET ) + pos = 0; + pos += meta->offset; + + /* 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, +}; diff --git a/src/include/ipxe/xferbuf.h b/src/include/ipxe/xferbuf.h index 22d8e8e8e..f2f336252 100644 --- a/src/include/ipxe/xferbuf.h +++ b/src/include/ipxe/xferbuf.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include /** A data transfer buffer */ @@ -21,9 +22,77 @@ struct xfer_buffer { size_t len; /** Current offset within data */ size_t pos; + /** Data transfer buffer operations */ + struct xfer_buffer_operations *op; }; -extern void xferbuf_done ( struct xfer_buffer *xferbuf ); +/** Data transfer buffer operations */ +struct xfer_buffer_operations { + /** Reallocate data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ + int ( * realloc ) ( struct xfer_buffer *xferbuf, size_t len ); + /** Write data to buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + * + * This call is simply a wrapper for the appropriate + * memcpy()-like operation: the caller is responsible for + * ensuring that the write does not exceed the buffer length. + */ + void ( * write ) ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ); + /** Read data from buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + * + * This call is simply a wrapper for the appropriate + * memcpy()-like operation: the caller is responsible for + * ensuring that the read does not exceed the buffer length. + */ + void ( * read ) ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ); +}; + +extern struct xfer_buffer_operations xferbuf_malloc_operations; +extern struct xfer_buffer_operations xferbuf_umalloc_operations; + +/** + * Initialise malloc()-based data transfer buffer + * + * @v xferbuf Data transfer buffer + */ +static inline __attribute__ (( always_inline )) void +xferbuf_malloc_init ( struct xfer_buffer *xferbuf ) { + xferbuf->op = &xferbuf_malloc_operations; +} + +/** + * Initialise umalloc()-based data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v data User pointer + */ +static inline __attribute__ (( always_inline )) void +xferbuf_umalloc_init ( struct xfer_buffer *xferbuf, userptr_t *data ) { + xferbuf->data = data; + xferbuf->op = &xferbuf_umalloc_operations; +} + +extern void xferbuf_free ( struct xfer_buffer *xferbuf ); +extern int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ); +extern int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ); extern int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ); diff --git a/src/net/validator.c b/src/net/validator.c index a01269da8..db968398a 100644 --- a/src/net/validator.c +++ b/src/net/validator.c @@ -83,7 +83,7 @@ static void validator_free ( struct refcnt *refcnt ) { DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); x509_chain_put ( validator->chain ); ocsp_put ( validator->ocsp ); - xferbuf_done ( &validator->buffer ); + xferbuf_free ( &validator->buffer ); free ( validator ); } @@ -392,7 +392,7 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { goto err_append; /* Free downloaded data */ - xferbuf_done ( &validator->buffer ); + xferbuf_free ( &validator->buffer ); /* Resume validation process */ process_add ( &validator->process ); @@ -557,6 +557,7 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) { process_init ( &validator->process, &validator_process_desc, &validator->refcnt ); validator->chain = x509_chain_get ( chain ); + xferbuf_malloc_init ( &validator->buffer ); /* Attach parent interface, mortalise self, and return */ intf_plug_plug ( &validator->job, job );