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 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel