iPXE
iobuf.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <strings.h>
00028 #include <errno.h>
00029 #include <ipxe/malloc.h>
00030 #include <ipxe/iobuf.h>
00031 
00032 /** @file
00033  *
00034  * I/O buffers
00035  *
00036  */
00037 
00038 /**
00039  * Allocate I/O buffer with specified alignment and offset
00040  *
00041  * @v len       Required length of buffer
00042  * @v align     Physical alignment
00043  * @v offset    Offset from physical alignment
00044  * @ret iobuf   I/O buffer, or NULL if none available
00045  *
00046  * @c align will be rounded up to the nearest power of two.
00047  */
00048 struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
00049         struct io_buffer *iobuf;
00050         size_t padding;
00051         size_t threshold;
00052         unsigned int align_log2;
00053         void *data;
00054 
00055         /* Calculate padding required below alignment boundary to
00056          * ensure that a correctly aligned inline struct io_buffer
00057          * could fit (regardless of the requested offset).
00058          */
00059         padding = ( sizeof ( *iobuf ) + __alignof__ ( *iobuf ) - 1 );
00060 
00061         /* Round up requested alignment to at least the size of the
00062          * padding, to simplify subsequent calculations.
00063          */
00064         if ( align < padding )
00065                 align = padding;
00066 
00067         /* Round up alignment to the nearest power of two, avoiding
00068          * a potentially undefined shift operation.
00069          */
00070         align_log2 = fls ( align - 1 );
00071         if ( align_log2 >= ( 8 * sizeof ( align ) ) )
00072                 return NULL;
00073         align = ( 1UL << align_log2 );
00074 
00075         /* Calculate length threshold */
00076         assert ( align >= padding );
00077         threshold = ( align - padding );
00078 
00079         /* Allocate buffer plus an inline descriptor as a single unit,
00080          * unless doing so would push the total size over the
00081          * alignment boundary.
00082          */
00083         if ( len <= threshold ) {
00084 
00085                 /* Round up buffer length to ensure that struct
00086                  * io_buffer is aligned.
00087                  */
00088                 len += ( ( - len - offset ) & ( __alignof__ ( *iobuf ) - 1 ) );
00089 
00090                 /* Allocate memory for buffer plus descriptor */
00091                 data = malloc_dma_offset ( len + sizeof ( *iobuf ), align,
00092                                            offset );
00093                 if ( ! data )
00094                         return NULL;
00095                 iobuf = ( data + len );
00096 
00097         } else {
00098 
00099                 /* Allocate memory for buffer */
00100                 data = malloc_dma_offset ( len, align, offset );
00101                 if ( ! data )
00102                         return NULL;
00103 
00104                 /* Allocate memory for descriptor */
00105                 iobuf = malloc ( sizeof ( *iobuf ) );
00106                 if ( ! iobuf ) {
00107                         free_dma ( data, len );
00108                         return NULL;
00109                 }
00110         }
00111 
00112         /* Populate descriptor */
00113         iobuf->head = iobuf->data = iobuf->tail = data;
00114         iobuf->end = ( data + len );
00115 
00116         return iobuf;
00117 }
00118 
00119 /**
00120  * Allocate I/O buffer
00121  *
00122  * @v len       Required length of buffer
00123  * @ret iobuf   I/O buffer, or NULL if none available
00124  *
00125  * The I/O buffer will be physically aligned on its own size (rounded
00126  * up to the nearest power of two).
00127  */
00128 struct io_buffer * alloc_iob ( size_t len ) {
00129 
00130         /* Pad to minimum length */
00131         if ( len < IOB_ZLEN )
00132                 len = IOB_ZLEN;
00133 
00134         /* Align buffer on its own size to avoid potential problems
00135          * with boundary-crossing DMA.
00136          */
00137         return alloc_iob_raw ( len, len, 0 );
00138 }
00139 
00140 /**
00141  * Free I/O buffer
00142  *
00143  * @v iobuf     I/O buffer
00144  */
00145 void free_iob ( struct io_buffer *iobuf ) {
00146         size_t len;
00147 
00148         /* Allow free_iob(NULL) to be valid */
00149         if ( ! iobuf )
00150                 return;
00151 
00152         /* Sanity checks */
00153         assert ( iobuf->head <= iobuf->data );
00154         assert ( iobuf->data <= iobuf->tail );
00155         assert ( iobuf->tail <= iobuf->end );
00156 
00157         /* Free buffer */
00158         len = ( iobuf->end - iobuf->head );
00159         if ( iobuf->end == iobuf ) {
00160 
00161                 /* Descriptor is inline */
00162                 free_dma ( iobuf->head, ( len + sizeof ( *iobuf ) ) );
00163 
00164         } else {
00165 
00166                 /* Descriptor is detached */
00167                 free_dma ( iobuf->head, len );
00168                 free ( iobuf );
00169         }
00170 }
00171 
00172 /**
00173  * Ensure I/O buffer has sufficient headroom
00174  *
00175  * @v iobuf     I/O buffer
00176  * @v len       Required headroom
00177  *
00178  * This function currently only checks for the required headroom; it
00179  * does not reallocate the I/O buffer if required.  If we ever have a
00180  * code path that requires this functionality, it's a fairly trivial
00181  * change to make.
00182  */
00183 int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ) {
00184 
00185         if ( iob_headroom ( iobuf ) >= len )
00186                 return 0;
00187         return -ENOBUFS;
00188 }
00189 
00190 /**
00191  * Concatenate I/O buffers into a single buffer
00192  *
00193  * @v list      List of I/O buffers
00194  * @ret iobuf   Concatenated I/O buffer, or NULL on allocation failure
00195  *
00196  * After a successful concatenation, the list will be empty.
00197  */
00198 struct io_buffer * iob_concatenate ( struct list_head *list ) {
00199         struct io_buffer *iobuf;
00200         struct io_buffer *tmp;
00201         struct io_buffer *concatenated;
00202         size_t len = 0;
00203 
00204         /* If the list contains only a single entry, avoid an
00205          * unnecessary additional allocation.
00206          */
00207         if ( list_is_singular ( list ) ) {
00208                 iobuf = list_first_entry ( list, struct io_buffer, list );
00209                 INIT_LIST_HEAD ( list );
00210                 return iobuf;
00211         }
00212 
00213         /* Calculate total length */
00214         list_for_each_entry ( iobuf, list, list )
00215                 len += iob_len ( iobuf );
00216 
00217         /* Allocate new I/O buffer */
00218         concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 );
00219         if ( ! concatenated )
00220                 return NULL;
00221 
00222         /* Move data to new I/O buffer */
00223         list_for_each_entry_safe ( iobuf, tmp, list, list ) {
00224                 list_del ( &iobuf->list );
00225                 memcpy ( iob_put ( concatenated, iob_len ( iobuf ) ),
00226                          iobuf->data, iob_len ( iobuf ) );
00227                 free_iob ( iobuf );
00228         }
00229 
00230         return concatenated;
00231 }
00232 
00233 /**
00234  * Split I/O buffer
00235  *
00236  * @v iobuf             I/O buffer
00237  * @v len               Length to split into a new I/O buffer
00238  * @ret split           New I/O buffer, or NULL on allocation failure
00239  *
00240  * Split the first @c len bytes of the existing I/O buffer into a
00241  * separate I/O buffer.  The resulting buffers are likely to have no
00242  * headroom or tailroom.
00243  *
00244  * If this call fails, then the original buffer will be unmodified.
00245  */
00246 struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ) {
00247         struct io_buffer *split;
00248 
00249         /* Sanity checks */
00250         assert ( len <= iob_len ( iobuf ) );
00251 
00252         /* Allocate new I/O buffer */
00253         split = alloc_iob ( len );
00254         if ( ! split )
00255                 return NULL;
00256 
00257         /* Copy in data */
00258         memcpy ( iob_put ( split, len ), iobuf->data, len );
00259         iob_pull ( iobuf, len );
00260         return split;
00261 }