[PATCH rtems 1/2] libblock: Add rtems_bdbuf_peek()
Christian Mauderer
christian.mauderer at embedded-brains.de
Fri Jan 22 13:24:28 UTC 2021
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.
---
cpukit/include/rtems/bdbuf.h | 21 ++++
cpukit/include/rtems/diskdevs.h | 27 ++++-
cpukit/libblock/src/bdbuf.c | 80 +++++++++++----
cpukit/libblock/src/blkdev-print-stats.c | 28 ++---
testsuites/fstests/fsdosfswrite01/init.c | 51 +++++-----
testsuites/libtests/block14/block14.scn | 37 ++++---
testsuites/libtests/block14/init.c | 124 +++++++++++++++++------
7 files changed, 264 insertions(+), 104 deletions(-)
diff --git a/cpukit/include/rtems/bdbuf.h b/cpukit/include/rtems/bdbuf.h
index fbb4fc05e9..0cde571816 100644
--- a/cpukit/include/rtems/bdbuf.h
+++ b/cpukit/include/rtems/bdbuf.h
@@ -539,6 +539,27 @@ rtems_bdbuf_read (
rtems_bdbuf_buffer** bd
);
+/**
+ * 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
diff --git a/cpukit/include/rtems/diskdevs.h b/cpukit/include/rtems/diskdevs.h
index 85d157dcd5..d7529cbe89 100644
--- a/cpukit/include/rtems/diskdevs.h
+++ b/cpukit/include/rtems/diskdevs.h
@@ -58,6 +58,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.
*/
@@ -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,10 +124,19 @@ 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 (with and without size).
*/
uint32_t read_ahead_transfers;
+ /**
+ * @brief Read-ahead transfers with given size.
+ *
+ * Number of times a read ahead transfer has been given a size. This is the
+ * case for read ahead transfers that are caused by a peek.
+ */
+ uint32_t read_ahead_transfers_with_size;
+
/**
* @brief Count of blocks transfered from the device.
*/
diff --git a/cpukit/libblock/src/bdbuf.c b/cpukit/libblock/src/bdbuf.c
index a7d471507c..02acf11f54 100644
--- a/cpukit/libblock/src/bdbuf.c
+++ b/cpukit/libblock/src/bdbuf.c
@@ -2018,6 +2018,23 @@ rtems_bdbuf_read_ahead_reset (rtems_disk_device *dd)
dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
}
+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)
+ {
+ 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_till_end_of_disk = dd->block_count - block;
uint32_t max_transfer_count = bdbuf_config.max_read_ahead_blocks;
- if (transfer_count >= max_transfer_count)
- {
+ if (transfer_count > blocks_till_end_of_disk) {
+ transfer_count = blocks_till_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;
+
+ if (transfer_count == RTEMS_DISK_READ_AHEAD_SIZE_AUTO) {
+ transfer_count = blocks_till_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 {
+ ++dd->stats.read_ahead_transfers_with_size;
}
++dd->stats.read_ahead_transfers;
diff --git a/cpukit/libblock/src/blkdev-print-stats.c b/cpukit/libblock/src/blkdev-print-stats.c
index 8edf24fd8c..8e07e97b68 100644
--- a/cpukit/libblock/src/blkdev-print-stats.c
+++ b/cpukit/libblock/src/blkdev-print-stats.c
@@ -40,25 +40,27 @@ void rtems_blkdev_print_stats(
printer,
"-------------------------------------------------------------------------------\n"
" DEVICE STATISTICS\n"
- "----------------------+--------------------------------------------------------\n"
- " MEDIA BLOCK SIZE | %" PRIu32 "\n"
- " MEDIA BLOCK COUNT | %" PRIu32 "\n"
- " BLOCK SIZE | %" PRIu32 "\n"
- " READ HITS | %" PRIu32 "\n"
- " READ MISSES | %" PRIu32 "\n"
- " READ AHEAD TRANSFERS | %" PRIu32 "\n"
- " READ BLOCKS | %" PRIu32 "\n"
- " READ ERRORS | %" PRIu32 "\n"
- " WRITE TRANSFERS | %" PRIu32 "\n"
- " WRITE BLOCKS | %" PRIu32 "\n"
- " WRITE ERRORS | %" PRIu32 "\n"
- "----------------------+--------------------------------------------------------\n",
+ "--------------------------------+----------------------------------------------\n"
+ " MEDIA BLOCK SIZE | %" PRIu32 "\n"
+ " MEDIA BLOCK COUNT | %" PRIu32 "\n"
+ " BLOCK SIZE | %" PRIu32 "\n"
+ " READ HITS | %" PRIu32 "\n"
+ " READ MISSES | %" PRIu32 "\n"
+ " READ AHEAD TRANSFERS | %" PRIu32 "\n"
+ " READ AHEAD TRANSFERS WITH SIZE | %" PRIu32 "\n"
+ " READ BLOCKS | %" PRIu32 "\n"
+ " READ ERRORS | %" PRIu32 "\n"
+ " WRITE TRANSFERS | %" PRIu32 "\n"
+ " WRITE BLOCKS | %" PRIu32 "\n"
+ " WRITE ERRORS | %" PRIu32 "\n"
+ "--------------------------------+----------------------------------------------\n",
media_block_size,
media_block_count,
block_size,
stats->read_hits,
stats->read_misses,
stats->read_ahead_transfers,
+ stats->read_ahead_transfers_with_size,
stats->read_blocks,
stats->read_errors,
stats->write_transfers,
diff --git a/testsuites/fstests/fsdosfswrite01/init.c b/testsuites/fstests/fsdosfswrite01/init.c
index e5d8e5d6dd..e87289cf6d 100644
--- a/testsuites/fstests/fsdosfswrite01/init.c
+++ b/testsuites/fstests/fsdosfswrite01/init.c
@@ -126,34 +126,37 @@ static void test_normal_file_write(
const char *file_name )
{
static const rtems_blkdev_stats complete_existing_block_stats = {
- .read_hits = 0,
- .read_misses = 0,
- .read_ahead_transfers = 0,
- .read_blocks = 0,
- .read_errors = 0,
- .write_transfers = 1,
- .write_blocks = 1,
- .write_errors = 0
+ .read_hits = 0,
+ .read_misses = 0,
+ .read_ahead_transfers = 0,
+ .read_ahead_transfers_with_size = 0,
+ .read_blocks = 0,
+ .read_errors = 0,
+ .write_transfers = 1,
+ .write_blocks = 1,
+ .write_errors = 0
};
static const rtems_blkdev_stats complete_new_block_stats = {
- .read_hits = 3,
- .read_misses = 2,
- .read_ahead_transfers = 0,
- .read_blocks = 2,
- .read_errors = 0,
- .write_transfers = 1,
- .write_blocks = 3,
- .write_errors = 0
+ .read_hits = 3,
+ .read_misses = 2,
+ .read_ahead_transfers = 0,
+ .read_ahead_transfers_with_size = 0,
+ .read_blocks = 2,
+ .read_errors = 0,
+ .write_transfers = 1,
+ .write_blocks = 3,
+ .write_errors = 0
};
static const rtems_blkdev_stats partial_new_block_stats = {
- .read_hits = 3,
- .read_misses = 3,
- .read_ahead_transfers = 0,
- .read_blocks = 3,
- .read_errors = 0,
- .write_transfers = 1,
- .write_blocks = 3,
- .write_errors = 0
+ .read_hits = 3,
+ .read_misses = 3,
+ .read_ahead_transfers = 0,
+ .read_ahead_transfers_with_size = 0,
+ .read_blocks = 3,
+ .read_errors = 0,
+ .write_transfers = 1,
+ .write_blocks = 3,
+ .write_errors = 0
};
int rv;
diff --git a/testsuites/libtests/block14/block14.scn b/testsuites/libtests/block14/block14.scn
index 7170522579..48c22b716d 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 ERRORS | 1
- WRITE TRANSFERS | 2
- WRITE BLOCKS | 2
- WRITE ERRORS | 1
-----------------------+--------------------------------------------------------
+--------------------------------+----------------------------------------------
+ MEDIA BLOCK SIZE | 0
+ MEDIA BLOCK COUNT | 1
+ BLOCK SIZE | 2
+ READ HITS | 4
+ READ MISSES | 7
+ READ AHEAD TRANSFERS | 6
+ READ AHEAD TRANSFERS WITH SIZE | 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 b4e73aadc9..24441c4fe8 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_transfers_with_size = 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);
}
--
2.26.2
More information about the devel
mailing list