[rtems commit] libblock: Add rtems_bdbuf_peek()

Christian Mauderer christianm at rtems.org
Fri Mar 26 13:28:18 UTC 2021


Module:    rtems
Branch:    master
Commit:    6ae79e6df6fbc89ffbd6badb3f84d6a2b513fe83
Changeset: http://git.rtems.org/rtems/commit/?id=6ae79e6df6fbc89ffbd6badb3f84d6a2b513fe83

Author:    Christian Mauderer <christian.mauderer at embedded-brains.de>
Date:      Tue Jan 19 15:33:35 2021 +0100

libblock: Add rtems_bdbuf_peek()

Adds a peek function that allows (for example) a file system to suggest
the next blocks that should be used for read ahead. This can increase
the read speed of fragmented files.

Update #3689

---

 cpukit/include/rtems/bdbuf.h             |  23 ++++++
 cpukit/include/rtems/diskdevs.h          |  24 +++++-
 cpukit/libblock/src/bdbuf.c              |  84 +++++++++++++++------
 cpukit/libblock/src/blkdev-print-stats.c |   2 +
 testsuites/fstests/fsdosfswrite01/init.c |   3 +
 testsuites/libtests/block14/block14.scn  |  19 ++++-
 testsuites/libtests/block14/init.c       | 124 +++++++++++++++++++++++--------
 7 files changed, 219 insertions(+), 60 deletions(-)

diff --git a/cpukit/include/rtems/bdbuf.h b/cpukit/include/rtems/bdbuf.h
index fbb4fc0..4d11a47 100644
--- a/cpukit/include/rtems/bdbuf.h
+++ b/cpukit/include/rtems/bdbuf.h
@@ -540,6 +540,29 @@ rtems_bdbuf_read (
 );
 
 /**
+ * @brief Give a hint which blocks should be cached next.
+ *
+ * Provide a hint to the read ahead mechanism which blocks should be cached
+ * next. This overwrites the default linear pattern. You should use it in (for
+ * example) a file system to tell bdbuf where the next part of a fragmented file
+ * is. If you know the length of the file, you can provide that too.
+ *
+ * Before you can use this function, the rtems_bdbuf_init() routine must be
+ * called at least once to initialize everything. Otherwise you might get
+ * unexpected results.
+ *
+ * @param dd [in] The disk device.
+ * @param block [in] Linear media block number.
+ * @param nr_blocks [in] Number of consecutive blocks that can be pre-fetched.
+ */
+void
+rtems_bdbuf_peek (
+  rtems_disk_device *dd,
+  rtems_blkdev_bnum block,
+  uint32_t nr_blocks
+);
+
+/**
  * Release the buffer obtained by a read call back to the cache. If the buffer
  * was obtained by a get call and was not already in the cache the release
  * modified call should be used. A buffer released with this call obtained by a
diff --git a/cpukit/include/rtems/diskdevs.h b/cpukit/include/rtems/diskdevs.h
index 85d157d..527a8cc 100644
--- a/cpukit/include/rtems/diskdevs.h
+++ b/cpukit/include/rtems/diskdevs.h
@@ -59,6 +59,11 @@ typedef int (*rtems_block_device_ioctl)(
 #define RTEMS_DISK_READ_AHEAD_NO_TRIGGER ((rtems_blkdev_bnum) -1)
 
 /**
+ * @brief Size value to set number of blocks based on config and disk size.
+ */
+#define RTEMS_DISK_READ_AHEAD_SIZE_AUTO (0)
+
+/**
  * @brief Block device read-ahead control.
  */
 typedef struct {
@@ -71,7 +76,8 @@ typedef struct {
    * @brief Block value to trigger the read-ahead request.
    *
    * A value of @ref RTEMS_DISK_READ_AHEAD_NO_TRIGGER will disable further
-   * read-ahead requests since no valid block can have this value.
+   * read-ahead requests (except the ones triggered by @a rtems_bdbuf_peek)
+   * since no valid block can have this value.
    */
   rtems_blkdev_bnum trigger;
 
@@ -82,6 +88,14 @@ typedef struct {
    * be arbitrary.
    */
   rtems_blkdev_bnum next;
+
+  /**
+   * @brief Size of the next read-ahead request in blocks.
+   *
+   * A value of @ref RTEMS_DISK_READ_AHEAD_SIZE_AUTO will try to read the rest
+   * of the disk but at most the configured max_read_ahead_blocks.
+   */
+  uint32_t nr_blocks;
 } rtems_blkdev_read_ahead;
 
 /**
@@ -110,11 +124,17 @@ typedef struct {
   /**
    * @brief Read-ahead transfer count.
    *
-   * Each read-ahead transfer may read multiple blocks.
+   * Each read-ahead transfer may read multiple blocks. This counts all
+   * transfers (including peeks).
    */
   uint32_t read_ahead_transfers;
 
   /**
+   * @brief Read-ahead transfers caused by a peek.
+   */
+  uint32_t read_ahead_peeks;
+
+  /**
    * @brief Count of blocks transfered from the device.
    */
   uint32_t read_blocks;
diff --git a/cpukit/libblock/src/bdbuf.c b/cpukit/libblock/src/bdbuf.c
index a7d4715..8aadf9c 100644
--- a/cpukit/libblock/src/bdbuf.c
+++ b/cpukit/libblock/src/bdbuf.c
@@ -2019,6 +2019,23 @@ rtems_bdbuf_read_ahead_reset (rtems_disk_device *dd)
 }
 
 static void
+rtems_bdbuf_read_ahead_add_to_chain (rtems_disk_device *dd)
+{
+  rtems_status_code sc;
+  rtems_chain_control *chain = &bdbuf_cache.read_ahead_chain;
+
+  if (rtems_chain_is_empty (chain))
+  {
+    sc = rtems_event_send (bdbuf_cache.read_ahead_task,
+                           RTEMS_BDBUF_READ_AHEAD_WAKE_UP);
+    if (sc != RTEMS_SUCCESSFUL)
+      rtems_bdbuf_fatal (RTEMS_BDBUF_FATAL_RA_WAKE_UP);
+  }
+
+  rtems_chain_append_unprotected (chain, &dd->read_ahead.node);
+}
+
+static void
 rtems_bdbuf_check_read_ahead_trigger (rtems_disk_device *dd,
                                       rtems_blkdev_bnum  block)
 {
@@ -2026,18 +2043,8 @@ rtems_bdbuf_check_read_ahead_trigger (rtems_disk_device *dd,
       && dd->read_ahead.trigger == block
       && !rtems_bdbuf_is_read_ahead_active (dd))
   {
-    rtems_status_code sc;
-    rtems_chain_control *chain = &bdbuf_cache.read_ahead_chain;
-
-    if (rtems_chain_is_empty (chain))
-    {
-      sc = rtems_event_send (bdbuf_cache.read_ahead_task,
-                             RTEMS_BDBUF_READ_AHEAD_WAKE_UP);
-      if (sc != RTEMS_SUCCESSFUL)
-        rtems_bdbuf_fatal (RTEMS_BDBUF_FATAL_RA_WAKE_UP);
-    }
-
-    rtems_chain_append_unprotected (chain, &dd->read_ahead.node);
+    dd->read_ahead.nr_blocks = RTEMS_DISK_READ_AHEAD_SIZE_AUTO;
+    rtems_bdbuf_read_ahead_add_to_chain(dd);
   }
 }
 
@@ -2112,6 +2119,24 @@ rtems_bdbuf_read (rtems_disk_device   *dd,
   return sc;
 }
 
+void
+rtems_bdbuf_peek (rtems_disk_device *dd,
+                  rtems_blkdev_bnum block,
+                  uint32_t nr_blocks)
+{
+  rtems_bdbuf_lock_cache ();
+
+  if (bdbuf_cache.read_ahead_enabled && nr_blocks > 0)
+  {
+    rtems_bdbuf_read_ahead_reset(dd);
+    dd->read_ahead.next = block;
+    dd->read_ahead.nr_blocks = nr_blocks;
+    rtems_bdbuf_read_ahead_add_to_chain(dd);
+  }
+
+  rtems_bdbuf_unlock_cache ();
+}
+
 static rtems_status_code
 rtems_bdbuf_check_bd_and_lock_cache (rtems_bdbuf_buffer *bd, const char *kind)
 {
@@ -2952,18 +2977,33 @@ rtems_bdbuf_read_ahead_task (rtems_task_argument arg)
 
         if (bd != NULL)
         {
-          uint32_t transfer_count = dd->block_count - block;
+          uint32_t transfer_count = dd->read_ahead.nr_blocks;
+          uint32_t blocks_until_end_of_disk = dd->block_count - block;
           uint32_t max_transfer_count = bdbuf_config.max_read_ahead_blocks;
 
-          if (transfer_count >= max_transfer_count)
-          {
-            transfer_count = max_transfer_count;
-            dd->read_ahead.trigger = block + transfer_count / 2;
-            dd->read_ahead.next = block + transfer_count;
-          }
-          else
-          {
-            dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
+          if (transfer_count == RTEMS_DISK_READ_AHEAD_SIZE_AUTO) {
+            transfer_count = blocks_until_end_of_disk;
+
+            if (transfer_count >= max_transfer_count)
+            {
+              transfer_count = max_transfer_count;
+              dd->read_ahead.trigger = block + transfer_count / 2;
+              dd->read_ahead.next = block + transfer_count;
+            }
+            else
+            {
+              dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
+            }
+          } else {
+            if (transfer_count > blocks_until_end_of_disk) {
+              transfer_count = blocks_until_end_of_disk;
+            }
+
+            if (transfer_count > max_transfer_count) {
+              transfer_count = max_transfer_count;
+            }
+
+            ++dd->stats.read_ahead_peeks;
           }
 
           ++dd->stats.read_ahead_transfers;
diff --git a/cpukit/libblock/src/blkdev-print-stats.c b/cpukit/libblock/src/blkdev-print-stats.c
index 8edf24f..539ff08 100644
--- a/cpukit/libblock/src/blkdev-print-stats.c
+++ b/cpukit/libblock/src/blkdev-print-stats.c
@@ -47,6 +47,7 @@ void rtems_blkdev_print_stats(
      " READ HITS            | %" PRIu32 "\n"
      " READ MISSES          | %" PRIu32 "\n"
      " READ AHEAD TRANSFERS | %" PRIu32 "\n"
+     " READ AHEAD PEEKS     | %" PRIu32 "\n"
      " READ BLOCKS          | %" PRIu32 "\n"
      " READ ERRORS          | %" PRIu32 "\n"
      " WRITE TRANSFERS      | %" PRIu32 "\n"
@@ -59,6 +60,7 @@ void rtems_blkdev_print_stats(
      stats->read_hits,
      stats->read_misses,
      stats->read_ahead_transfers,
+     stats->read_ahead_peeks,
      stats->read_blocks,
      stats->read_errors,
      stats->write_transfers,
diff --git a/testsuites/fstests/fsdosfswrite01/init.c b/testsuites/fstests/fsdosfswrite01/init.c
index e5d8e5d..64b43b7 100644
--- a/testsuites/fstests/fsdosfswrite01/init.c
+++ b/testsuites/fstests/fsdosfswrite01/init.c
@@ -129,6 +129,7 @@ static void test_normal_file_write(
     .read_hits            = 0,
     .read_misses          = 0,
     .read_ahead_transfers = 0,
+    .read_ahead_peeks     = 0,
     .read_blocks          = 0,
     .read_errors          = 0,
     .write_transfers      = 1,
@@ -139,6 +140,7 @@ static void test_normal_file_write(
     .read_hits            = 3,
     .read_misses          = 2,
     .read_ahead_transfers = 0,
+    .read_ahead_peeks     = 0,
     .read_blocks          = 2,
     .read_errors          = 0,
     .write_transfers      = 1,
@@ -149,6 +151,7 @@ static void test_normal_file_write(
     .read_hits            = 3,
     .read_misses          = 3,
     .read_ahead_transfers = 0,
+    .read_ahead_peeks     = 0,
     .read_blocks          = 3,
     .read_errors          = 0,
     .write_transfers      = 1,
diff --git a/testsuites/libtests/block14/block14.scn b/testsuites/libtests/block14/block14.scn
index 7170522..cf5518a 100644
--- a/testsuites/libtests/block14/block14.scn
+++ b/testsuites/libtests/block14/block14.scn
@@ -6,19 +6,30 @@ action 3
 action 4
 action 5
 action 6
+action 7
+action 8
+action 9
+action 10
+action 11
+action 12
+action 13
+action 14
+action 15
 -------------------------------------------------------------------------------
                                DEVICE STATISTICS
 ----------------------+--------------------------------------------------------
  MEDIA BLOCK SIZE     | 0
  MEDIA BLOCK COUNT    | 1
  BLOCK SIZE           | 2
- READ HITS            | 2
- READ MISSES          | 3
- READ AHEAD TRANSFERS | 2
- READ BLOCKS          | 5
+ READ HITS            | 4
+ READ MISSES          | 7
+ READ AHEAD TRANSFERS | 6
+ READ AHEAD PEEKS     | 3
+ READ BLOCKS          | 13
  READ ERRORS          | 1
  WRITE TRANSFERS      | 2
  WRITE BLOCKS         | 2
  WRITE ERRORS         | 1
 ----------------------+--------------------------------------------------------
+
 *** END OF TEST BLOCK 14 ***
diff --git a/testsuites/libtests/block14/init.c b/testsuites/libtests/block14/init.c
index b4e73aa..13b7ab0 100644
--- a/testsuites/libtests/block14/init.c
+++ b/testsuites/libtests/block14/init.c
@@ -29,9 +29,9 @@
 
 const char rtems_test_name[] = "BLOCK 14";
 
-#define ACTION_COUNT 7
+#define ACTION_COUNT 16
 
-#define BLOCK_COUNT 6
+#define BLOCK_COUNT 14
 
 #define DISK_PATH "/disk"
 
@@ -42,50 +42,104 @@ typedef struct {
     rtems_blkdev_bnum block,
     rtems_bdbuf_buffer **bd_ptr
   );
+  void (*peek)(
+    rtems_disk_device *dd,
+    rtems_blkdev_bnum block,
+    uint32_t nr_blocks
+  );
   rtems_status_code expected_get_status;
   rtems_status_code (*release)(rtems_bdbuf_buffer *bd);
 } test_action;
 
 static const test_action actions [ACTION_COUNT] = {
-  { 0, rtems_bdbuf_read, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
-  { 1, rtems_bdbuf_read, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
-  { 2, rtems_bdbuf_read, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
-  { 0, rtems_bdbuf_read, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
-  { 4, rtems_bdbuf_get, RTEMS_SUCCESSFUL, rtems_bdbuf_sync },
-  { 5, rtems_bdbuf_read, RTEMS_IO_ERROR, rtems_bdbuf_release },
-  { 5, rtems_bdbuf_get, RTEMS_SUCCESSFUL, rtems_bdbuf_sync }
+  /* normal read ahead */
+  { 0, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+  { 1, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+  { 2, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+
+  /* re-read a cached block */
+  { 0, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+
+  /* cause some writes */
+  { 4, rtems_bdbuf_get, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_sync },
+  { 5, rtems_bdbuf_read, NULL, RTEMS_IO_ERROR, rtems_bdbuf_release },
+  { 5, rtems_bdbuf_get, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_sync },
+
+  /* interrupt normal read ahead with a peek */
+  { 9, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+  { 13, NULL, rtems_bdbuf_peek, 0, NULL },
+  { 10, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+  { 11, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+  { 12, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+
+  /* peek with hit */
+  { 6, NULL, rtems_bdbuf_peek, 0, NULL },
+  { 6, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
+
+  /* (wrong) peek with reading different block */
+  { 8, NULL, rtems_bdbuf_peek, 0, NULL },
+  { 7, rtems_bdbuf_read, NULL, RTEMS_SUCCESSFUL, rtems_bdbuf_release },
 };
 
-#define STATS(a, b, c, d, e, f, g, h) \
+#define STATS(a, b, c, d, e, f, g, h, i) \
   { \
     .read_hits = a, \
     .read_misses = b, \
     .read_ahead_transfers = c, \
-    .read_blocks = d, \
-    .read_errors = e, \
-    .write_transfers = f, \
-    .write_blocks = g, \
-    .write_errors = h \
+    .read_ahead_peeks = d, \
+    .read_blocks = e, \
+    .read_errors = f, \
+    .write_transfers = g, \
+    .write_blocks = h, \
+    .write_errors = i \
   }
 
 static const rtems_blkdev_stats expected_stats [ACTION_COUNT] = {
-  STATS(0, 1, 0, 1, 0, 0, 0, 0),
-  STATS(0, 2, 1, 3, 0, 0, 0, 0),
-  STATS(1, 2, 2, 4, 0, 0, 0, 0),
-  STATS(2, 2, 2, 4, 0, 0, 0, 0),
-  STATS(2, 2, 2, 4, 0, 1, 1, 0),
-  STATS(2, 3, 2, 5, 1, 1, 1, 0),
-  STATS(2, 3, 2, 5, 1, 2, 2, 1)
+  STATS(0, 1, 0, 0, 1, 0, 0, 0, 0),
+  STATS(0, 2, 1, 0, 3, 0, 0, 0, 0),
+  STATS(1, 2, 2, 0, 4, 0, 0, 0, 0),
+
+  STATS(2, 2, 2, 0, 4, 0, 0, 0, 0),
+
+  STATS(2, 2, 2, 0, 4, 0, 1, 1, 0),
+  STATS(2, 3, 2, 0, 5, 1, 1, 1, 0),
+  STATS(2, 3, 2, 0, 5, 1, 2, 2, 1),
+
+  STATS(2, 4, 2, 0, 6, 1, 2, 2, 1),
+  STATS(2, 4, 3, 1, 7, 1, 2, 2, 1),
+  STATS(2, 5, 3, 1, 8, 1, 2, 2, 1),
+  STATS(2, 6, 4, 1, 10, 1, 2, 2, 1),
+  STATS(3, 6, 4, 1, 10, 1, 2, 2, 1),
+
+  STATS(3, 6, 5, 2, 11, 1, 2, 2, 1),
+  STATS(4, 6, 5, 2, 11, 1, 2, 2, 1),
+
+  STATS(4, 6, 6, 3, 12, 1, 2, 2, 1),
+  STATS(4, 7, 6, 3, 13, 1, 2, 2, 1),
 };
 
 static const int expected_block_access_counts [ACTION_COUNT] [BLOCK_COUNT] = {
-   { 1, 0, 0, 0, 0, 0 },
-   { 1, 1, 1, 0, 0, 0 },
-   { 1, 1, 1, 1, 0, 0 },
-   { 1, 1, 1, 1, 0, 0 },
-   { 1, 1, 1, 1, 1, 0 },
-   { 1, 1, 1, 1, 1, 1 },
-   { 1, 1, 1, 1, 1, 2 }
+   { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+   { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+   { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+   { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+   { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+   { 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+   { 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+   { 1, 1, 1, 1, 1, 2, 0, 0, 0, 1, 0, 0, 0, 0 },
+   { 1, 1, 1, 1, 1, 2, 0, 0, 0, 1, 0, 0, 0, 1 },
+   { 1, 1, 1, 1, 1, 2, 0, 0, 0, 1, 1, 0, 0, 1 },
+   { 1, 1, 1, 1, 1, 2, 0, 0, 0, 1, 1, 1, 1, 1 },
+   { 1, 1, 1, 1, 1, 2, 0, 0, 0, 1, 1, 1, 1, 1 },
+
+   { 1, 1, 1, 1, 1, 2, 1, 0, 0, 1, 1, 1, 1, 1 },
+   { 1, 1, 1, 1, 1, 2, 1, 0, 0, 1, 1, 1, 1, 1 },
+
+   { 1, 1, 1, 1, 1, 2, 1, 0, 1, 1, 1, 1, 1, 1 },
+   { 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1 },
 };
 
 static int block_access_counts [BLOCK_COUNT];
@@ -132,10 +186,16 @@ static void test_actions(rtems_disk_device *dd)
 
     printf("action %i\n", i);
 
-    sc = (*action->get)(dd, action->block, &bd);
-    rtems_test_assert(sc == action->expected_get_status);
+    if (action->get != NULL) {
+      sc = (*action->get)(dd, action->block, &bd);
+      rtems_test_assert(sc == action->expected_get_status);
+    }
+
+    if (action->peek != NULL) {
+      (*action->peek)(dd, action->block, 1);
+    }
 
-    if (sc == RTEMS_SUCCESSFUL) {
+    if (sc == RTEMS_SUCCESSFUL && action->release != NULL) {
       sc = (*action->release)(bd);
       rtems_test_assert(sc == RTEMS_SUCCESSFUL);
     }



More information about the vc mailing list