Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:15

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup rtems_sparse_disk
0007  *
0008  * @brief Sparse disk block device implementation.
0009  */
0010 
0011 /*
0012  * Copyright (c) 2012 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <stdlib.h>
0037 #include <errno.h>
0038 #include <string.h>
0039 
0040 #include <rtems.h>
0041 #include <rtems/blkdev.h>
0042 #include <rtems/fatal.h>
0043 
0044 #include "rtems/sparse-disk.h"
0045 
0046 /*
0047  * Allocate RAM for sparse disk
0048  */
0049 static rtems_sparse_disk *sparse_disk_allocate(
0050   const uint32_t          media_block_size,
0051   const rtems_blkdev_bnum blocks_with_buffer )
0052 {
0053   size_t const key_table_size = blocks_with_buffer
0054                                 * sizeof( rtems_sparse_disk_key );
0055   size_t const data_size      = blocks_with_buffer * media_block_size;
0056   size_t const alloc_size     = sizeof( rtems_sparse_disk )
0057                                 + key_table_size + data_size;
0058 
0059   rtems_sparse_disk *const sd = (rtems_sparse_disk *) malloc(
0060     alloc_size );
0061 
0062   return sd;
0063 }
0064 
0065 /*
0066  * Initialize sparse disk data
0067  */
0068 static rtems_status_code sparse_disk_initialize( rtems_sparse_disk *sd,
0069   const uint32_t                                                    media_block_size,
0070   const rtems_blkdev_bnum                                           blocks_with_buffer,
0071   const rtems_sparse_disk_delete_handler                            sparse_disk_delete,
0072   const uint8_t                                                     fill_pattern )
0073 {
0074   rtems_blkdev_bnum i;
0075 
0076   if ( NULL == sd )
0077     return RTEMS_INVALID_ADDRESS;
0078 
0079   uint8_t     *data           = (uint8_t *) sd;
0080   size_t const key_table_size = blocks_with_buffer
0081                                 * sizeof( rtems_sparse_disk_key );
0082   size_t const data_size      = blocks_with_buffer * media_block_size;
0083 
0084   memset( data, 0, sizeof( rtems_sparse_disk ) + key_table_size );
0085 
0086   sd->fill_pattern = fill_pattern;
0087   memset( (uint8_t *) ( data + sizeof( rtems_sparse_disk ) + key_table_size ),
0088           sd->fill_pattern,
0089           data_size );
0090 
0091   sd->delete_handler = sparse_disk_delete;
0092 
0093   rtems_mutex_init( &sd->mutex, "Sparse Disk" );
0094 
0095   data                  += sizeof( rtems_sparse_disk );
0096 
0097   sd->blocks_with_buffer = blocks_with_buffer;
0098   sd->key_table          = (rtems_sparse_disk_key *) data;
0099 
0100   data                  += key_table_size;
0101 
0102   for ( i = 0; i < blocks_with_buffer; ++i, data += media_block_size ) {
0103     sd->key_table[i].data = data;
0104   }
0105 
0106   sd->media_block_size = media_block_size;
0107   return RTEMS_SUCCESSFUL;
0108 }
0109 
0110 /*
0111  * Block comparison
0112  */
0113 static int sparse_disk_compare( const void *aa, const void *bb )
0114 {
0115   const rtems_sparse_disk_key *a = aa;
0116   const rtems_sparse_disk_key *b = bb;
0117 
0118   if ( a->block < b->block ) {
0119     return -1;
0120   } else if ( a->block == b->block ) {
0121     return 0;
0122   } else {
0123     return 1;
0124   }
0125 }
0126 
0127 static rtems_sparse_disk_key *sparse_disk_find_block(
0128   const rtems_sparse_disk *sparse_disk,
0129   rtems_blkdev_bnum        block
0130 )
0131 {
0132   rtems_sparse_disk_key key = { .block = block };
0133 
0134   return bsearch(
0135     &key,
0136     sparse_disk->key_table,
0137     sparse_disk->used_count,
0138     sizeof( rtems_sparse_disk_key ),
0139     sparse_disk_compare
0140   );
0141 }
0142 
0143 static rtems_sparse_disk_key *sparse_disk_get_new_block(
0144   rtems_sparse_disk      *sparse_disk,
0145   const rtems_blkdev_bnum block
0146 )
0147 {
0148   rtems_sparse_disk_key *key;
0149 
0150   if ( sparse_disk->used_count >= sparse_disk->blocks_with_buffer ) {
0151     return NULL;
0152   }
0153 
0154   key = &sparse_disk->key_table[ sparse_disk->used_count ];
0155   key->block = block;
0156   ++sparse_disk->used_count;
0157   qsort(
0158     sparse_disk->key_table,
0159     sparse_disk->used_count,
0160     sizeof( rtems_sparse_disk_key ),
0161     sparse_disk_compare
0162   );
0163   return sparse_disk_find_block( sparse_disk, block );
0164 }
0165 
0166 static int sparse_disk_read_block(
0167   const rtems_sparse_disk *sparse_disk,
0168   const rtems_blkdev_bnum  block,
0169   uint8_t                 *buffer,
0170   const size_t             buffer_size )
0171 {
0172   size_t                 bytes_to_copy = sparse_disk->media_block_size;
0173   rtems_sparse_disk_key *key;
0174 
0175   if ( buffer_size < bytes_to_copy )
0176     bytes_to_copy = buffer_size;
0177 
0178   key = sparse_disk_find_block( sparse_disk, block );
0179 
0180   if ( NULL != key )
0181     memcpy( buffer, key->data, bytes_to_copy );
0182   else
0183     memset( buffer, sparse_disk->fill_pattern, buffer_size );
0184 
0185   return bytes_to_copy;
0186 }
0187 
0188 static int sparse_disk_write_block(
0189   rtems_sparse_disk      *sparse_disk,
0190   const rtems_blkdev_bnum block,
0191   const uint8_t          *buffer,
0192   const size_t            buffer_size )
0193 {
0194   size_t                 bytes_to_copy = sparse_disk->media_block_size;
0195   bool                   block_needs_writing = false;
0196   rtems_sparse_disk_key *key;
0197   size_t                 i;
0198 
0199   if ( buffer_size < bytes_to_copy )
0200     bytes_to_copy = buffer_size;
0201 
0202   /* we only need to write the block if it is different from the fill pattern.
0203    * If the read method does not find a block it will deliver the fill pattern anyway.
0204    */
0205 
0206   key = sparse_disk_find_block( sparse_disk, block );
0207 
0208   if ( NULL == key ) {
0209     for ( i = 0; ( !block_needs_writing ) && ( i < bytes_to_copy ); ++i ) {
0210       if ( buffer[i] != sparse_disk->fill_pattern )
0211         block_needs_writing = true;
0212     }
0213 
0214     if ( block_needs_writing ) {
0215       key = sparse_disk_get_new_block( sparse_disk, block );
0216     }
0217   }
0218 
0219   if ( NULL != key )
0220     memcpy( key->data, buffer, bytes_to_copy );
0221   else if ( block_needs_writing )
0222     return -1;
0223 
0224   return bytes_to_copy;
0225 }
0226 
0227 /*
0228  * Read/write handling
0229  */
0230 static int sparse_disk_read_write(
0231   rtems_sparse_disk    *sparse_disk,
0232   rtems_blkdev_request *req,
0233   const bool            read )
0234 {
0235   int                     rv = 0;
0236   uint32_t                req_buffer;
0237   rtems_blkdev_sg_buffer *scatter_gather;
0238   rtems_blkdev_bnum       block;
0239   uint8_t                *buff;
0240   size_t                  buff_size;
0241   unsigned int            bytes_handled;
0242 
0243   rtems_mutex_lock( &sparse_disk->mutex );
0244 
0245   for ( req_buffer = 0;
0246         ( 0 <= rv ) && ( req_buffer < req->bufnum );
0247         ++req_buffer ) {
0248     scatter_gather = &req->bufs[req_buffer];
0249 
0250     bytes_handled  = 0;
0251     buff           = (uint8_t *) scatter_gather->buffer;
0252     block          = scatter_gather->block;
0253     buff_size      = scatter_gather->length;
0254 
0255     while ( ( 0 <= rv ) && ( 0 < buff_size ) ) {
0256       if ( read )
0257         rv = sparse_disk_read_block( sparse_disk,
0258                                      block,
0259                                      &buff[bytes_handled],
0260                                      buff_size );
0261       else
0262         rv = sparse_disk_write_block( sparse_disk,
0263                                       block,
0264                                       &buff[bytes_handled],
0265                                       buff_size );
0266 
0267       ++block;
0268       bytes_handled += rv;
0269       buff_size     -= rv;
0270     }
0271   }
0272 
0273   rtems_mutex_unlock( &sparse_disk->mutex );
0274 
0275   if ( 0 > rv )
0276     rtems_blkdev_request_done( req, RTEMS_IO_ERROR );
0277   else
0278     rtems_blkdev_request_done( req, RTEMS_SUCCESSFUL );
0279 
0280   return 0;
0281 }
0282 
0283 /*
0284  * ioctl handler to be passed to the block device handler
0285  */
0286 static int sparse_disk_ioctl( rtems_disk_device *dd, uint32_t req, void *argp )
0287 {
0288   rtems_sparse_disk *sd = rtems_disk_get_driver_data( dd );
0289 
0290   if ( RTEMS_BLKIO_REQUEST == req ) {
0291     rtems_blkdev_request *r = argp;
0292 
0293     switch ( r->req ) {
0294       case RTEMS_BLKDEV_REQ_READ:
0295       case RTEMS_BLKDEV_REQ_WRITE:
0296         return sparse_disk_read_write( sd, r, r->req == RTEMS_BLKDEV_REQ_READ );
0297       default:
0298         break;
0299     }
0300   } else if ( RTEMS_BLKIO_DELETED == req ) {
0301     rtems_mutex_destroy( &sd->mutex );
0302 
0303     if ( NULL != sd->delete_handler )
0304       ( *sd->delete_handler )( sd );
0305 
0306     return 0;
0307   } else {
0308     return rtems_blkdev_ioctl( dd, req, argp );
0309   }
0310 
0311   errno = EINVAL;
0312   return -1;
0313 }
0314 
0315 void rtems_sparse_disk_free( rtems_sparse_disk *sd )
0316 {
0317   free( sd );
0318 }
0319 
0320 rtems_status_code rtems_sparse_disk_create_and_register(
0321   const char       *device_file_name,
0322   uint32_t          media_block_size,
0323   rtems_blkdev_bnum blocks_with_buffer,
0324   rtems_blkdev_bnum media_block_count,
0325   uint8_t           fill_pattern )
0326 {
0327   rtems_status_code  sc          = RTEMS_SUCCESSFUL;
0328   rtems_sparse_disk *sparse_disk = sparse_disk_allocate(
0329     media_block_size,
0330     blocks_with_buffer
0331   );
0332 
0333   if ( sparse_disk != NULL ) {
0334     sc = rtems_sparse_disk_register(
0335       device_file_name,
0336       sparse_disk,
0337       media_block_size,
0338       blocks_with_buffer,
0339       media_block_count,
0340       fill_pattern,
0341       rtems_sparse_disk_free
0342     );
0343   } else {
0344     sc = RTEMS_NO_MEMORY;
0345   }
0346 
0347   return sc;
0348 }
0349 
0350 rtems_status_code rtems_sparse_disk_register(
0351   const char                      *device_file_name,
0352   rtems_sparse_disk               *sparse_disk,
0353   uint32_t                         media_block_size,
0354   rtems_blkdev_bnum                blocks_with_buffer,
0355   rtems_blkdev_bnum                media_block_count,
0356   uint8_t                          fill_pattern,
0357   rtems_sparse_disk_delete_handler sparse_disk_delete )
0358 {
0359   rtems_status_code sc;
0360 
0361   if ( blocks_with_buffer <= media_block_count ) {
0362     sc = sparse_disk_initialize(
0363       sparse_disk,
0364       media_block_size,
0365       blocks_with_buffer,
0366       sparse_disk_delete,
0367       fill_pattern
0368     );
0369 
0370     if ( RTEMS_SUCCESSFUL == sc ) {
0371       sc = rtems_blkdev_create(
0372         device_file_name,
0373         media_block_size,
0374         media_block_count,
0375         sparse_disk_ioctl,
0376         sparse_disk
0377       );
0378     }
0379   } else {
0380     sc = RTEMS_INVALID_NUMBER;
0381   }
0382 
0383   return sc;
0384 }