forked from Imagelibrary/rtems
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
This commit is contained in:
@@ -539,6 +539,29 @@ rtems_bdbuf_read (
|
||||
rtems_bdbuf_buffer** bd
|
||||
);
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
@@ -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,16 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -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 && 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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ***
|
||||
|
||||
@@ -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 (sc == RTEMS_SUCCESSFUL) {
|
||||
if (action->peek != NULL) {
|
||||
(*action->peek)(dd, action->block, 1);
|
||||
}
|
||||
|
||||
if (sc == RTEMS_SUCCESSFUL && action->release != NULL) {
|
||||
sc = (*action->release)(bd);
|
||||
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user