Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup rtems_rfs
0007  *
0008  * @brief RTEMS File Systems Buffer Routines
0009  */
0010 
0011 /*
0012  *  COPYRIGHT (c) 2010 Chris Johns <chrisj@rtems.org>
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 #ifdef HAVE_CONFIG_H
0037 #include "config.h"
0038 #endif
0039 
0040 #include <sys/stat.h>
0041 #include <inttypes.h>
0042 #include <errno.h>
0043 #include <fcntl.h>
0044 #include <string.h>
0045 
0046 #include <rtems/rfs/rtems-rfs-buffer.h>
0047 #include <rtems/rfs/rtems-rfs-file-system.h>
0048 
0049 /**
0050  * Scan the chain for a buffer that matches the block number.
0051  *
0052  * @param chain The chain to scan.
0053  * @param count The number of items on the chain.
0054  * @param block The block number to find.
0055  * @return  rtems_rfs_buffer* The buffer if found else NULL.
0056  */
0057 static rtems_rfs_buffer*
0058 rtems_rfs_scan_chain (rtems_chain_control*   chain,
0059                       uint32_t*              count,
0060                       rtems_rfs_buffer_block block)
0061 {
0062   rtems_rfs_buffer* buffer;
0063   rtems_chain_node* node;
0064 
0065   node = rtems_chain_last (chain);
0066 
0067   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS))
0068     printf ("rtems-rfs: buffer-scan: count=%" PRIu32 ", block=%" PRIu32 ": ", *count, block);
0069 
0070   while (!rtems_chain_is_head (chain, node))
0071   {
0072     buffer = (rtems_rfs_buffer*) node;
0073 
0074     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS))
0075       printf ("%" PRIiPTR " ", ((intptr_t) buffer->user));
0076 
0077     if (((rtems_rfs_buffer_block) ((intptr_t)(buffer->user))) == block)
0078     {
0079       if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS))
0080         printf (": found block=%" PRIiPTR "\n",
0081                 ((intptr_t)(buffer->user)));
0082 
0083       (*count)--;
0084       rtems_chain_extract_unprotected (node);
0085       rtems_chain_set_off_chain (node);
0086       return buffer;
0087     }
0088     node = rtems_chain_previous (node);
0089   }
0090 
0091   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS))
0092     printf (": not found\n");
0093 
0094   return NULL;
0095 }
0096 
0097 int
0098 rtems_rfs_buffer_handle_request (rtems_rfs_file_system*   fs,
0099                                  rtems_rfs_buffer_handle* handle,
0100                                  rtems_rfs_buffer_block   block,
0101                                  bool                     read)
0102 {
0103   int rc;
0104 
0105   /*
0106    * If the handle has a buffer release it. This allows a handle to be reused
0107    * without needing to close then open it again.
0108    */
0109   if (rtems_rfs_buffer_handle_has_block (handle))
0110   {
0111     /*
0112      * Treat block 0 as special to handle the loading of the super block.
0113      */
0114     if (block && (rtems_rfs_buffer_bnum (handle) == block))
0115       return 0;
0116 
0117     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST))
0118       printf ("rtems-rfs: buffer-request: handle has buffer: %" PRIu32 "\n",
0119               rtems_rfs_buffer_bnum (handle));
0120 
0121     rc = rtems_rfs_buffer_handle_release (fs, handle);
0122     if (rc > 0)
0123       return rc;
0124     handle->dirty = false;
0125     handle->bnum = 0;
0126   }
0127 
0128   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST))
0129     printf ("rtems-rfs: buffer-request: block=%" PRIu32 "\n", block);
0130 
0131   /*
0132    * First check to see if the buffer has already been requested and is
0133    * currently attached to a handle. If it is share the access. A buffer could
0134    * be shared where different parts of the block have separate functions. An
0135    * example is an inode block and the file system needs to handle 2 inodes in
0136    * the same block at the same time.
0137    */
0138   if (fs->buffers_count)
0139   {
0140     /*
0141      * Check the active buffer list for shared buffers.
0142      */
0143     handle->buffer = rtems_rfs_scan_chain (&fs->buffers,
0144                                            &fs->buffers_count,
0145                                            block);
0146     if (rtems_rfs_buffer_handle_has_block (handle) &&
0147         rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST))
0148       printf ("rtems-rfs: buffer-request: buffer shared: refs: %d\n",
0149               rtems_rfs_buffer_refs (handle) + 1);
0150   }
0151 
0152   /*
0153    * If the buffer has not been found check the local cache of released
0154    * buffers. There are release and released modified lists to preserve the
0155    * state.
0156    */
0157   if (!rtems_rfs_fs_no_local_cache (fs) &&
0158       !rtems_rfs_buffer_handle_has_block (handle))
0159   {
0160     /*
0161      * Check the local cache of released buffers.
0162      */
0163     if (fs->release_count)
0164       handle->buffer = rtems_rfs_scan_chain (&fs->release,
0165                                              &fs->release_count,
0166                                              block);
0167 
0168     if (!rtems_rfs_buffer_handle_has_block (handle) &&
0169         fs->release_modified_count)
0170     {
0171       handle->buffer = rtems_rfs_scan_chain (&fs->release_modified,
0172                                              &fs->release_modified_count,
0173                                              block);
0174       /*
0175        * If we found a buffer retain the dirty buffer state.
0176        */
0177       if (rtems_rfs_buffer_handle_has_block (handle))
0178         rtems_rfs_buffer_mark_dirty (handle);
0179     }
0180   }
0181 
0182   /*
0183    * If not located we request the buffer from the I/O layer.
0184    */
0185   if (!rtems_rfs_buffer_handle_has_block (handle))
0186   {
0187     rc = rtems_rfs_buffer_io_request (fs, block, read, &handle->buffer);
0188 
0189     if (rc > 0)
0190     {
0191       if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST))
0192         printf ("rtems-rfs: buffer-request: block=%" PRIu32 ": bdbuf-%s: %d: %s\n",
0193                 block, read ? "read" : "get", rc, strerror (rc));
0194       return rc;
0195     }
0196 
0197     rtems_chain_set_off_chain (rtems_rfs_buffer_link(handle));
0198   }
0199 
0200   /*
0201    * Increase the reference count of the buffer.
0202    */
0203   rtems_rfs_buffer_refs_up (handle);
0204   rtems_chain_append_unprotected (&fs->buffers,
0205                                   rtems_rfs_buffer_link (handle));
0206   fs->buffers_count++;
0207 
0208   handle->buffer->user = (void*) ((intptr_t) block);
0209   handle->bnum = block;
0210 
0211   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_REQUEST))
0212     printf ("rtems-rfs: buffer-request: block=%" PRIu32 " bdbuf-%s=%" PRIu32 " refs=%d\n",
0213             block, read ? "read" : "get", handle->buffer->block,
0214             handle->buffer->references);
0215 
0216   return 0;
0217 }
0218 
0219 int
0220 rtems_rfs_buffer_handle_release (rtems_rfs_file_system*   fs,
0221                                  rtems_rfs_buffer_handle* handle)
0222 {
0223   int rc = 0;
0224 
0225   if (rtems_rfs_buffer_handle_has_block (handle))
0226   {
0227     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_RELEASE))
0228       printf ("rtems-rfs: buffer-release: block=%" PRIu32 " %s refs=%d %s\n",
0229               rtems_rfs_buffer_bnum (handle),
0230               rtems_rfs_buffer_dirty (handle) ? "(dirty)" : "",
0231               rtems_rfs_buffer_refs (handle),
0232               rtems_rfs_buffer_refs (handle) == 0 ? "BAD REF COUNT" : "");
0233 
0234     if (rtems_rfs_buffer_refs (handle) > 0)
0235       rtems_rfs_buffer_refs_down (handle);
0236 
0237     if (rtems_rfs_buffer_refs (handle) == 0)
0238     {
0239       rtems_chain_extract_unprotected (rtems_rfs_buffer_link (handle));
0240       fs->buffers_count--;
0241 
0242       if (rtems_rfs_fs_no_local_cache (fs))
0243       {
0244         handle->buffer->user = (void*) 0;
0245         rc = rtems_rfs_buffer_io_release (handle->buffer,
0246                                           rtems_rfs_buffer_dirty (handle));
0247       }
0248       else
0249       {
0250         /*
0251          * If the total number of held buffers is higher than the configured
0252          * value remove a buffer from the queue with the most buffers and
0253          * release. The buffers are held on the queues with the newest at the
0254          * head.
0255          *
0256          * This code stops a large series of transactions causing all the
0257          * buffers in the cache being held in queues of this file system.
0258          */
0259         if ((fs->release_count +
0260              fs->release_modified_count) >= fs->max_held_buffers)
0261         {
0262           rtems_rfs_buffer* buffer;
0263           bool              modified;
0264 
0265           if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_HANDLE_RELEASE))
0266             printf ("rtems-rfs: buffer-release: local cache overflow:"
0267                     " %" PRIu32 "\n", fs->release_count + fs->release_modified_count);
0268 
0269           if (fs->release_count > fs->release_modified_count)
0270           {
0271             buffer = (rtems_rfs_buffer*)
0272               rtems_chain_get_unprotected (&fs->release);
0273             fs->release_count--;
0274             modified = false;
0275           }
0276           else
0277           {
0278             buffer = (rtems_rfs_buffer*)
0279               rtems_chain_get_unprotected (&fs->release_modified);
0280             fs->release_modified_count--;
0281             modified = true;
0282           }
0283           buffer->user = (void*) 0;
0284           rc = rtems_rfs_buffer_io_release (buffer, modified);
0285         }
0286 
0287         if (rtems_rfs_buffer_dirty (handle))
0288         {
0289           rtems_chain_append_unprotected (&fs->release_modified,
0290                                           rtems_rfs_buffer_link (handle));
0291           fs->release_modified_count++;
0292         }
0293         else
0294         {
0295           rtems_chain_append_unprotected (&fs->release,
0296                                           rtems_rfs_buffer_link (handle));
0297           fs->release_count++;
0298         }
0299       }
0300     }
0301     handle->buffer = NULL;
0302   }
0303 
0304   return rc;
0305 }
0306 
0307 int
0308 rtems_rfs_buffer_open (const char* name, rtems_rfs_file_system* fs)
0309 {
0310   struct stat st;
0311 #if RTEMS_RFS_USE_LIBBLOCK
0312   int rv;
0313 #endif
0314 
0315   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC))
0316     printf ("rtems-rfs: buffer-open: opening: %s\n", name);
0317 
0318   fs->device = open (name, O_RDWR);
0319   if (fs->device < 0)
0320   {
0321     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN))
0322       printf ("rtems-rfs: buffer-open: cannot open file\n");
0323     return ENXIO;
0324   }
0325 
0326   if (fstat (fs->device, &st) < 0)
0327   {
0328     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN))
0329       printf ("rtems-rfs: buffer-open: stat '%s' failed: %s\n",
0330               name, strerror (errno));
0331     return ENXIO;
0332   }
0333 
0334 #if RTEMS_RFS_USE_LIBBLOCK
0335   /*
0336    * Is the device a block device ?
0337    */
0338   if (!S_ISBLK (st.st_mode))
0339   {
0340     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN))
0341       printf ("rtems-rfs: buffer-open: '%s' is not a block device\n", name);
0342     return ENXIO;
0343   }
0344 
0345   /*
0346    * Check that device is registred as a block device and lock it.
0347    */
0348   rv = rtems_disk_fd_get_disk_device (fs->device, &fs->disk);
0349   if (rv != 0)
0350   {
0351     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_OPEN))
0352       printf ("rtems-rfs: buffer-open: cannot obtain the disk\n");
0353     return ENXIO;
0354   }
0355 #else
0356   fs->media_size = st.st_size;
0357   strcat (fs->name, name);
0358 #endif
0359 
0360   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC))
0361     printf ("rtems-rfs: buffer-open: blks=%" PRId32 ", blk-size=%" PRId32 "\n",
0362             rtems_rfs_fs_media_blocks (fs),
0363             rtems_rfs_fs_media_block_size (fs));
0364 
0365   return 0;
0366 }
0367 
0368 int
0369 rtems_rfs_buffer_close (rtems_rfs_file_system* fs)
0370 {
0371   int rc = 0;
0372 
0373   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE))
0374     printf ("rtems-rfs: buffer-close: closing\n");
0375 
0376   /*
0377    * Change the block size to the media device size. It will release and sync
0378    * all buffers.
0379    */
0380   rc = rtems_rfs_buffer_setblksize (fs, rtems_rfs_fs_media_block_size (fs));
0381 
0382   if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE))
0383     printf ("rtems-rfs: buffer-close: set media block size failed: %d: %s\n",
0384             rc, strerror (rc));
0385 
0386   if (close (fs->device) < 0)
0387   {
0388     rc = errno;
0389     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE))
0390       printf ("rtems-rfs: buffer-close: file close failed: %d: %s\n",
0391               rc, strerror (rc));
0392   }
0393 
0394   return rc;
0395 }
0396 
0397 int
0398 rtems_rfs_buffer_sync (rtems_rfs_file_system* fs)
0399 {
0400   int result = 0;
0401 #if RTEMS_RFS_USE_LIBBLOCK
0402   rtems_status_code sc;
0403 #endif
0404 
0405   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC))
0406     printf ("rtems-rfs: buffer-sync: syncing\n");
0407 
0408   /*
0409    * @todo Split in the separate files for each type.
0410    */
0411 #if RTEMS_RFS_USE_LIBBLOCK
0412   sc = rtems_bdbuf_syncdev (rtems_rfs_fs_device (fs));
0413   if (sc != RTEMS_SUCCESSFUL)
0414   {
0415     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SYNC))
0416       printf ("rtems-rfs: buffer-sync: device sync failed: %s\n",
0417               rtems_status_text (sc));
0418     result = EIO;
0419   }
0420 #else
0421   if (fsync (fs->device) < 0)
0422   {
0423     result = errno;
0424     if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CLOSE))
0425       printf ("rtems-rfs: buffer-sync: file sync failed: %d: %s\n",
0426               result, strerror (result));
0427   }
0428 #endif
0429   return result;
0430 }
0431 
0432 int
0433 rtems_rfs_buffer_setblksize (rtems_rfs_file_system* fs, uint32_t size)
0434 {
0435   int rc;
0436 
0437   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE))
0438     printf ("rtems-rfs: buffer-setblksize: block size: %" PRIu32 "\n", size);
0439 
0440   rc = rtems_rfs_buffers_release (fs);
0441   if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE))
0442     printf ("rtems-rfs: buffer-setblksize: buffer release failed: %d: %s\n",
0443             rc, strerror (rc));
0444 
0445   rc = rtems_rfs_buffer_sync (fs);
0446   if ((rc > 0) && rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_SETBLKSIZE))
0447     printf ("rtems-rfs: buffer-setblksize: device sync failed: %d: %s\n",
0448             rc, strerror (rc));
0449 
0450 #if RTEMS_RFS_USE_LIBBLOCK
0451   rc = fs->disk->ioctl (fs->disk, RTEMS_BLKIO_SETBLKSIZE, &size);
0452   if (rc < 0)
0453     rc = errno;
0454 #endif
0455   return rc;
0456 }
0457 
0458 static int
0459 rtems_rfs_release_chain (rtems_chain_control* chain,
0460                          uint32_t*            count,
0461                          bool                 modified)
0462 {
0463   rtems_rfs_buffer* buffer;
0464   int               rrc = 0;
0465   int               rc;
0466 
0467   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_CHAINS))
0468     printf ("rtems-rfs: release-chain: count=%" PRIu32 "\n", *count);
0469 
0470   while (!rtems_chain_is_empty (chain))
0471   {
0472     buffer = (rtems_rfs_buffer*) rtems_chain_get_unprotected (chain);
0473     (*count)--;
0474 
0475     buffer->user = (void*) 0;
0476 
0477     rc = rtems_rfs_buffer_io_release (buffer, modified);
0478     if ((rc > 0) && (rrc == 0))
0479       rrc = rc;
0480   }
0481   return rrc;
0482 }
0483 
0484 int
0485 rtems_rfs_buffers_release (rtems_rfs_file_system* fs)
0486 {
0487   int rrc = 0;
0488   int rc;
0489 
0490   if (rtems_rfs_trace (RTEMS_RFS_TRACE_BUFFER_RELEASE))
0491     printf ("rtems-rfs: buffers-release: active:%" PRIu32 " "
0492             "release:%" PRIu32 " release-modified:%" PRIu32 "\n",
0493             fs->buffers_count, fs->release_count, fs->release_modified_count);
0494 
0495   rc = rtems_rfs_release_chain (&fs->release,
0496                                 &fs->release_count,
0497                                 false);
0498   if ((rc > 0) && (rrc == 0))
0499     rrc = rc;
0500   rc = rtems_rfs_release_chain (&fs->release_modified,
0501                                 &fs->release_modified_count,
0502                                 true);
0503   if ((rc > 0) && (rrc == 0))
0504     rrc = rc;
0505 
0506   return rrc;
0507 }