[PATCH] libblock: Add read-ahead task

sebastian.huber at embedded-brains.de sebastian.huber at embedded-brains.de
Thu May 31 09:23:54 UTC 2012


From: Sebastian Huber <sebastian.huber at embedded-brains.de>

Read-ahead requests were previously executed in the context of the
reading task.  This blocks the reading task until the complete read
with read-ahead transfer is finished.  A read-ahead task is introduced
to off-load the read-ahead transfer.  This allows the reading task to
work with the requested block more quickly.  The read-ahead is triggered
after two misses of ascending consecutive blocks or a read hit of a
block read by the most-resent read-ahead transfer.
---
 cpukit/libblock/include/rtems/bdbuf.h    |   26 ++-
 cpukit/libblock/include/rtems/diskdevs.h |   37 ++++
 cpukit/libblock/src/bdbuf.c              |  347 +++++++++++++++++++-----------
 cpukit/libblock/src/diskdevs-init.c      |    2 +
 cpukit/sapi/include/confdefs.h           |   11 +-
 testsuites/libtests/Makefile.am          |    1 +
 testsuites/libtests/block13/Makefile.am  |   19 ++
 testsuites/libtests/block13/block13.doc  |   11 +
 testsuites/libtests/block13/block13.scn  |   11 +
 testsuites/libtests/block13/init.c       |  232 ++++++++++++++++++++
 testsuites/libtests/configure.ac         |    1 +
 11 files changed, 568 insertions(+), 130 deletions(-)
 create mode 100644 testsuites/libtests/block13/Makefile.am
 create mode 100644 testsuites/libtests/block13/block13.doc
 create mode 100644 testsuites/libtests/block13/block13.scn
 create mode 100644 testsuites/libtests/block13/init.c

diff --git a/cpukit/libblock/include/rtems/bdbuf.h b/cpukit/libblock/include/rtems/bdbuf.h
index 43a95df..5275c13 100644
--- a/cpukit/libblock/include/rtems/bdbuf.h
+++ b/cpukit/libblock/include/rtems/bdbuf.h
@@ -46,7 +46,7 @@ extern "C" {
  * @ingroup rtems_libblock
  *
  * The Block Device Buffer Management implements a cache between the disk
- * devices and file systems.  The code provides read ahead and write queuing to
+ * devices and file systems.  The code provides read-ahead and write queuing to
  * the drivers and fast cache look-up using an AVL tree.
  *
  * The block size used by a file system can be set at runtime and must be a
@@ -152,10 +152,13 @@ extern "C" {
  * written.  This would be a performance problem.
  *
  * The code performs multiple block reads and writes.  Multiple block reads or
- * read ahead increases performance with hardware that supports it.  It also
+ * read-ahead increases performance with hardware that supports it.  It also
  * helps with a large cache as the disk head movement is reduced.  It however
  * is a speculative operation so excessive use can remove valuable and needed
- * blocks from the cache.
+ * blocks from the cache.  The read-ahead is triggered after two misses of
+ * ascending consecutive blocks or a read hit of a block read by the
+ * most-resent read-ahead transfer.  The read-ahead works per disk, but all
+ * transfers are issued by the read-ahead task.
  *
  * The cache has the following lists of buffers:
  *  - LRU: Accessed or transfered buffers released in least recently used
@@ -379,6 +382,8 @@ typedef struct rtems_bdbuf_config {
   uint32_t            buffer_max;              /**< Maximum buffer size
                                                 * supported. It is also the
                                                 * allocation size. */
+  rtems_task_priority read_ahead_priority;     /**< Priority of the read-ahead
+                                                * task. */
 } rtems_bdbuf_config;
 
 /**
@@ -389,8 +394,8 @@ typedef struct rtems_bdbuf_config {
 extern const rtems_bdbuf_config rtems_bdbuf_configuration;
 
 /**
- * The max_read_ahead_blocks value is altered if there are fewer buffers
- * than this defined max. This stops thrashing in the cache.
+ * The default value for the maximum read-ahead blocks disables the read-ahead
+ * feature.
  */
 #define RTEMS_BDBUF_MAX_READ_AHEAD_BLOCKS_DEFAULT    0
 
@@ -426,6 +431,12 @@ extern const rtems_bdbuf_config rtems_bdbuf_configuration;
                              RTEMS_BDBUF_SWAPOUT_TASK_PRIORITY_DEFAULT
 
 /**
+ * Default read-ahead task priority.  The same as the swap-out task.
+ */
+#define RTEMS_BDBUF_READ_AHEAD_TASK_PRIORITY_DEFAULT \
+  RTEMS_BDBUF_SWAPOUT_TASK_PRIORITY_DEFAULT
+
+/**
  * Default task stack size for swap-out and worker tasks.
  */
 #define RTEMS_BDBUF_TASK_STACK_SIZE_DEFAULT RTEMS_MINIMUM_STACK_SIZE
@@ -621,7 +632,7 @@ rtems_bdbuf_syncdev (rtems_disk_device *dd);
 /**
  * @brief Purges all buffers corresponding to the disk device @a dd.
  *
- * This may result in loss of data.
+ * This may result in loss of data.  The read-ahead state of this device is reset.
  *
  * Before you can use this function, the rtems_bdbuf_init() routine must be
  * called at least once to initialize the cache, otherwise a fatal error will
@@ -635,7 +646,8 @@ rtems_bdbuf_purge_dev (rtems_disk_device *dd);
 /**
  * @brief Sets the block size of a disk device.
  *
- * This will set the block size derived fields of the disk device.
+ * This will set the block size derived fields of the disk device.  The
+ * read-ahead state of this device is reset.
  *
  * Before you can use this function, the rtems_bdbuf_init() routine must be
  * called at least once to initialize the cache, otherwise a fatal error will
diff --git a/cpukit/libblock/include/rtems/diskdevs.h b/cpukit/libblock/include/rtems/diskdevs.h
index fe931f8..0b19409 100644
--- a/cpukit/libblock/include/rtems/diskdevs.h
+++ b/cpukit/libblock/include/rtems/diskdevs.h
@@ -16,6 +16,7 @@
 
 #include <rtems.h>
 #include <rtems/libio.h>
+#include <rtems/chain.h>
 #include <stdlib.h>
 
 #ifdef __cplusplus
@@ -55,6 +56,37 @@ typedef int (*rtems_block_device_ioctl)(
 );
 
 /**
+ * @brief Trigger value to disable further read-ahead requests.
+ */
+#define RTEMS_DISK_READ_AHEAD_NO_TRIGGER ((rtems_blkdev_bnum) -1)
+
+/**
+ * @brief Read-ahead control.
+ */
+typedef struct {
+  /**
+   * @brief Chain node for the read-ahead request queue of the read-ahead task.
+   */
+  rtems_chain_node node;
+
+  /**
+   * @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.
+   */
+  rtems_blkdev_bnum trigger;
+
+  /**
+   * @brief Start block for the next read-ahead request.
+   *
+   * In case the trigger value is out of range of valid blocks, this value my
+   * be arbitrary.
+   */
+  rtems_blkdev_bnum next;
+} rtems_disk_read_ahread;
+
+/**
  * @brief Description of a disk device (logical and physical disks).
  *
  * An array of pointer tables to rtems_disk_device structures is maintained.
@@ -168,6 +200,11 @@ struct rtems_disk_device {
    * releases this disk.
    */
   bool deleted;
+
+  /**
+   * @brief Read-ahead control for this disk.
+   */
+  rtems_disk_read_ahread read_ahead;
 };
 
 /**
diff --git a/cpukit/libblock/src/bdbuf.c b/cpukit/libblock/src/bdbuf.c
index 82c442f..9f6165f 100644
--- a/cpukit/libblock/src/bdbuf.c
+++ b/cpukit/libblock/src/bdbuf.c
@@ -134,6 +134,9 @@ typedef struct rtems_bdbuf_cache
 
   size_t              group_count;       /**< The number of groups. */
   rtems_bdbuf_group*  groups;            /**< The groups. */
+  rtems_id            read_ahead_task;   /**< Read-ahead task */
+  rtems_chain_control read_ahead_chain;  /**< Read-ahead request chain */
+  bool                read_ahead_enabled; /**< Read-ahead enabled */
 
   bool                initialised;       /**< Initialised state. */
 } rtems_bdbuf_cache;
@@ -180,6 +183,7 @@ typedef struct rtems_bdbuf_cache
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_0       RTEMS_BLKDEV_FATAL_ERROR(28)
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_1       RTEMS_BLKDEV_FATAL_ERROR(29)
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_2       RTEMS_BLKDEV_FATAL_ERROR(30)
+#define RTEMS_BLKDEV_FATAL_BDBUF_RA_WAKE_UP    RTEMS_BLKDEV_FATAL_ERROR(31)
 
 /**
  * The events used in this code. These should be system events rather than
@@ -187,6 +191,7 @@ typedef struct rtems_bdbuf_cache
  */
 #define RTEMS_BDBUF_TRANSFER_SYNC  RTEMS_EVENT_1
 #define RTEMS_BDBUF_SWAPOUT_SYNC   RTEMS_EVENT_2
+#define RTEMS_BDBUF_READ_AHEAD_WAKE_UP RTEMS_EVENT_1
 
 /**
  * Lock semaphore attributes. This is used for locking type mutexes.
@@ -219,11 +224,10 @@ typedef struct rtems_bdbuf_cache
   (TOD_MICROSECONDS_TO_TICKS (20000000))
 #endif
 
-/*
- * The swap out task.
- */
 static rtems_task rtems_bdbuf_swapout_task(rtems_task_argument arg);
 
+static rtems_task rtems_bdbuf_read_ahead_task(rtems_task_argument arg);
+
 /**
  * The Buffer Descriptor cache.
  */
@@ -1365,6 +1369,7 @@ rtems_bdbuf_init (void)
   rtems_chain_initialize_empty (&bdbuf_cache.lru);
   rtems_chain_initialize_empty (&bdbuf_cache.modified);
   rtems_chain_initialize_empty (&bdbuf_cache.sync);
+  rtems_chain_initialize_empty (&bdbuf_cache.read_ahead_chain);
 
   /*
    * Create the locks for the cache.
@@ -1486,12 +1491,28 @@ rtems_bdbuf_init (void)
   if (sc != RTEMS_SUCCESSFUL)
     goto error;
 
+  if (bdbuf_config.max_read_ahead_blocks > 0)
+  {
+    bdbuf_cache.read_ahead_enabled = true;
+    sc = rtems_bdbuf_create_task (rtems_build_name('B', 'R', 'D', 'A'),
+                                  bdbuf_config.read_ahead_priority,
+                                  RTEMS_BDBUF_READ_AHEAD_TASK_PRIORITY_DEFAULT,
+                                  rtems_bdbuf_read_ahead_task,
+                                  0,
+                                  &bdbuf_cache.read_ahead_task);
+    if (sc != RTEMS_SUCCESSFUL)
+      goto error;
+  }
+
   rtems_bdbuf_unlock_cache ();
 
   return RTEMS_SUCCESSFUL;
 
 error:
 
+  if (bdbuf_cache.read_ahead_task != 0)
+    rtems_task_delete (bdbuf_cache.read_ahead_task);
+
   if (bdbuf_cache.swapout != 0)
     rtems_task_delete (bdbuf_cache.swapout);
 
@@ -1852,81 +1873,6 @@ rtems_bdbuf_transfer_done (void* arg, rtems_status_code status)
   rtems_event_send (req->io_task, RTEMS_BDBUF_TRANSFER_SYNC);
 }
 
-static void
-rtems_bdbuf_create_read_request (const rtems_disk_device *dd,
-                                 rtems_blkdev_bnum        media_block,
-                                 rtems_blkdev_request    *req,
-                                 rtems_bdbuf_buffer     **bd_ptr)
-{
-  rtems_bdbuf_buffer *bd = NULL;
-  rtems_blkdev_bnum   media_block_end = dd->start + dd->size;
-  rtems_blkdev_bnum   media_block_count = dd->block_to_media_block_shift >= 0 ?
-    1U << dd->block_to_media_block_shift
-      : dd->block_size / dd->media_block_size;
-  uint32_t            block_size = dd->block_size;
-  uint32_t            transfer_index = 1;
-  uint32_t            transfer_count = bdbuf_config.max_read_ahead_blocks + 1;
-
-  if (media_block_end - media_block < transfer_count)
-    transfer_count = media_block_end - media_block;
-
-  req->req = RTEMS_BLKDEV_REQ_READ;
-  req->req_done = rtems_bdbuf_transfer_done;
-  req->done_arg = req;
-  req->io_task = rtems_task_self ();
-  req->status = RTEMS_RESOURCE_IN_USE;
-  req->bufnum = 0;
-
-  bd = rtems_bdbuf_get_buffer_for_access (dd, media_block);
-
-  *bd_ptr = bd;
-
-  req->bufs [0].user   = bd;
-  req->bufs [0].block  = media_block;
-  req->bufs [0].length = block_size;
-  req->bufs [0].buffer = bd->buffer;
-
-  if (rtems_bdbuf_tracer)
-    rtems_bdbuf_show_users ("read", bd);
-
-  switch (bd->state)
-  {
-    case RTEMS_BDBUF_STATE_CACHED:
-    case RTEMS_BDBUF_STATE_MODIFIED:
-      return;
-    case RTEMS_BDBUF_STATE_EMPTY:
-      rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
-      break;
-    default:
-      rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_1);
-      break;
-  }
-
-  while (transfer_index < transfer_count)
-  {
-    media_block += media_block_count;
-
-    bd = rtems_bdbuf_get_buffer_for_read_ahead (dd, media_block);
-
-    if (bd == NULL)
-      break;
-
-    rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
-
-    req->bufs [transfer_index].user   = bd;
-    req->bufs [transfer_index].block  = media_block;
-    req->bufs [transfer_index].length = block_size;
-    req->bufs [transfer_index].buffer = bd->buffer;
-
-    if (rtems_bdbuf_tracer)
-      rtems_bdbuf_show_users ("read-ahead", bd);
-
-    ++transfer_index;
-  }
-
-  req->bufnum = transfer_index;
-}
-
 static rtems_status_code
 rtems_bdbuf_execute_transfer_request (const rtems_disk_device *dd,
                                       rtems_blkdev_request    *req,
@@ -1989,15 +1935,16 @@ rtems_bdbuf_execute_transfer_request (const rtems_disk_device *dd,
     return RTEMS_IO_ERROR;
 }
 
-rtems_status_code
-rtems_bdbuf_read (rtems_disk_device   *dd,
-                  rtems_blkdev_bnum    block,
-                  rtems_bdbuf_buffer **bd_ptr)
+static rtems_status_code
+rtems_bdbuf_execute_read_request (const rtems_disk_device *dd,
+                                  rtems_bdbuf_buffer      *bd,
+                                  uint32_t                 transfer_count)
 {
-  rtems_status_code     sc = RTEMS_SUCCESSFUL;
   rtems_blkdev_request *req = NULL;
-  rtems_bdbuf_buffer   *bd = NULL;
-  rtems_blkdev_bnum     media_block;
+  rtems_blkdev_bnum media_block = bd->block;
+  uint32_t media_blocks_per_block = dd->media_blocks_per_block;
+  uint32_t block_size = dd->block_size;
+  uint32_t transfer_index = 1;
 
   /*
    * TODO: This type of request structure is wrong and should be removed.
@@ -2005,8 +1952,113 @@ rtems_bdbuf_read (rtems_disk_device   *dd,
 #define bdbuf_alloc(size) __builtin_alloca (size)
 
   req = bdbuf_alloc (sizeof (rtems_blkdev_request) +
-                     sizeof (rtems_blkdev_sg_buffer) *
-                      (bdbuf_config.max_read_ahead_blocks + 1));
+                     sizeof (rtems_blkdev_sg_buffer) * transfer_count);
+
+  req->req = RTEMS_BLKDEV_REQ_READ;
+  req->req_done = rtems_bdbuf_transfer_done;
+  req->done_arg = req;
+  req->io_task = rtems_task_self ();
+  req->status = RTEMS_RESOURCE_IN_USE;
+  req->bufnum = 0;
+
+  rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
+
+  req->bufs [0].user   = bd;
+  req->bufs [0].block  = media_block;
+  req->bufs [0].length = block_size;
+  req->bufs [0].buffer = bd->buffer;
+
+  if (rtems_bdbuf_tracer)
+    rtems_bdbuf_show_users ("read", bd);
+
+  while (transfer_index < transfer_count)
+  {
+    media_block += media_blocks_per_block;
+
+    bd = rtems_bdbuf_get_buffer_for_read_ahead (dd, media_block);
+
+    if (bd == NULL)
+      break;
+
+    rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
+
+    req->bufs [transfer_index].user   = bd;
+    req->bufs [transfer_index].block  = media_block;
+    req->bufs [transfer_index].length = block_size;
+    req->bufs [transfer_index].buffer = bd->buffer;
+
+    if (rtems_bdbuf_tracer)
+      rtems_bdbuf_show_users ("read", bd);
+
+    ++transfer_index;
+  }
+
+  req->bufnum = transfer_index;
+
+  return rtems_bdbuf_execute_transfer_request (dd, req, true);
+}
+
+static bool
+rtems_bdbuf_is_read_ahead_active (const rtems_disk_device *dd)
+{
+  return !rtems_chain_is_node_off_chain (&dd->read_ahead.node);
+}
+
+static void
+rtems_bdbuf_read_ahead_cancel (rtems_disk_device *dd)
+{
+  if (rtems_bdbuf_is_read_ahead_active (dd))
+  {
+    rtems_chain_extract_unprotected (&dd->read_ahead.node);
+    rtems_chain_set_off_chain (&dd->read_ahead.node);
+  }
+}
+
+static void
+rtems_bdbuf_read_ahead_reset (rtems_disk_device *dd)
+{
+  rtems_bdbuf_read_ahead_cancel (dd);
+  dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
+}
+
+static void
+rtems_bdbuf_check_read_ahead_trigger (rtems_disk_device *dd,
+                                      rtems_blkdev_bnum  block)
+{
+  if (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;
+
+    rtems_chain_append_unprotected (chain, &dd->read_ahead.node);
+    sc = rtems_event_send (bdbuf_cache.read_ahead_task,
+                           RTEMS_BDBUF_READ_AHEAD_WAKE_UP);
+    if (sc != RTEMS_SUCCESSFUL)
+      rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_RA_WAKE_UP);
+  }
+}
+
+static void
+rtems_bdbuf_set_read_ahead_trigger (rtems_disk_device *dd,
+                                    rtems_blkdev_bnum  block)
+{
+  if (dd->read_ahead.trigger != block)
+  {
+    rtems_bdbuf_read_ahead_cancel (dd);
+    dd->read_ahead.trigger = block + 1;
+    dd->read_ahead.next = block + 2;
+  }
+}
+
+rtems_status_code
+rtems_bdbuf_read (rtems_disk_device   *dd,
+                  rtems_blkdev_bnum    block,
+                  rtems_bdbuf_buffer **bd_ptr)
+{
+  rtems_status_code     sc = RTEMS_SUCCESSFUL;
+  rtems_bdbuf_buffer   *bd = NULL;
+  rtems_blkdev_bnum     media_block;
 
   rtems_bdbuf_lock_cache ();
 
@@ -2017,42 +2069,33 @@ rtems_bdbuf_read (rtems_disk_device   *dd,
       printf ("bdbuf:read: %" PRIu32 " (%" PRIu32 ") (dev = %08x)\n",
               media_block + dd->start, block, (unsigned) dd->dev);
 
-    rtems_bdbuf_create_read_request (dd, media_block, req, &bd);
-
-    if (req->bufnum > 0)
-    {
-      sc = rtems_bdbuf_execute_transfer_request (dd, req, true);
-      if (sc == RTEMS_SUCCESSFUL)
-      {
-        rtems_chain_extract_unprotected (&bd->link);
-        rtems_bdbuf_group_obtain (bd);
-      }
-    }
-
-    if (sc == RTEMS_SUCCESSFUL)
+    rtems_bdbuf_check_read_ahead_trigger (dd, block);
+    bd = rtems_bdbuf_get_buffer_for_access (dd, media_block);
+    switch (bd->state)
     {
-      switch (bd->state)
-      {
-        case RTEMS_BDBUF_STATE_CACHED:
+      case RTEMS_BDBUF_STATE_CACHED:
+        rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_CACHED);
+        break;
+      case RTEMS_BDBUF_STATE_MODIFIED:
+        rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_MODIFIED);
+        break;
+      case RTEMS_BDBUF_STATE_EMPTY:
+        rtems_bdbuf_set_read_ahead_trigger (dd, block);
+        sc = rtems_bdbuf_execute_read_request (dd, bd, 1);
+        if (sc == RTEMS_SUCCESSFUL)
+        {
           rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_CACHED);
-          break;
-        case RTEMS_BDBUF_STATE_MODIFIED:
-          rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_MODIFIED);
-          break;
-        default:
-          rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_4);
-          break;
-      }
-
-      if (rtems_bdbuf_tracer)
-      {
-        rtems_bdbuf_show_users ("read", bd);
-        rtems_bdbuf_show_usage ();
-      }
-    }
-    else
-    {
-      bd = NULL;
+          rtems_chain_extract_unprotected (&bd->link);
+          rtems_bdbuf_group_obtain (bd);
+        }
+        else
+        {
+          bd = NULL;
+        }
+        break;
+      default:
+        rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_4);
+        break;
     }
   }
 
@@ -2884,6 +2927,7 @@ rtems_bdbuf_purge_dev (rtems_disk_device *dd)
 
   rtems_chain_initialize_empty (&purge_list);
   rtems_bdbuf_lock_cache ();
+  rtems_bdbuf_read_ahead_reset (dd);
   rtems_bdbuf_gather_for_purge (&purge_list, dd);
   rtems_bdbuf_purge_list (&purge_list);
   rtems_bdbuf_unlock_cache ();
@@ -2919,6 +2963,8 @@ rtems_bdbuf_set_block_size (rtems_disk_device *dd, uint32_t block_size)
       dd->media_blocks_per_block = media_blocks_per_block;
       dd->block_to_media_block_shift = block_to_media_block_shift;
       dd->bds_per_group = bds_per_group;
+
+      rtems_bdbuf_read_ahead_reset (dd);
     }
     else
     {
@@ -2934,3 +2980,62 @@ rtems_bdbuf_set_block_size (rtems_disk_device *dd, uint32_t block_size)
 
   return sc;
 }
+
+static rtems_task
+rtems_bdbuf_read_ahead_task (rtems_task_argument arg)
+{
+  rtems_chain_control *chain = &bdbuf_cache.read_ahead_chain;
+
+  while (bdbuf_cache.read_ahead_enabled)
+  {
+    rtems_chain_node *node;
+
+    rtems_bdbuf_wait_for_event (RTEMS_BDBUF_READ_AHEAD_WAKE_UP);
+    rtems_bdbuf_lock_cache ();
+
+    while ((node = rtems_chain_get_unprotected (chain)) != NULL)
+    {
+      rtems_disk_device *dd = (rtems_disk_device *)
+        ((char *) node - offsetof (rtems_disk_device, read_ahead.node));
+      rtems_blkdev_bnum block = dd->read_ahead.next;
+      rtems_blkdev_bnum media_block = 0;
+      rtems_status_code sc =
+        rtems_bdbuf_get_media_block (dd, block, &media_block);
+
+      rtems_chain_set_off_chain (&dd->read_ahead.node);
+
+      if (sc == RTEMS_SUCCESSFUL)
+      {
+        rtems_bdbuf_buffer *bd =
+          rtems_bdbuf_get_buffer_for_read_ahead (dd, media_block);
+
+        if (bd != NULL)
+        {
+          uint32_t transfer_count = 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 += max_transfer_count / 2 + 1;
+            dd->read_ahead.next += max_transfer_count;
+          }
+          else
+          {
+            dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
+          }
+
+          rtems_bdbuf_execute_read_request (dd, bd, transfer_count);
+        }
+      }
+      else
+      {
+        dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
+      }
+    }
+
+    rtems_bdbuf_unlock_cache ();
+  }
+
+  rtems_task_delete (RTEMS_SELF);
+}
diff --git a/cpukit/libblock/src/diskdevs-init.c b/cpukit/libblock/src/diskdevs-init.c
index ccf954a..5ec96b2 100644
--- a/cpukit/libblock/src/diskdevs-init.c
+++ b/cpukit/libblock/src/diskdevs-init.c
@@ -36,6 +36,7 @@ rtems_status_code rtems_disk_init_phys(
   dd->media_block_size = block_size;
   dd->ioctl = handler;
   dd->driver_data = driver_data;
+  dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
 
   if (block_count > 0) {
     if ((*handler)(dd, RTEMS_BLKIO_CAPABILITIES, &dd->capabilities) != 0) {
@@ -67,6 +68,7 @@ rtems_status_code rtems_disk_init_log(
   dd->media_block_size = phys_dd->media_block_size;
   dd->ioctl = phys_dd->ioctl;
   dd->driver_data = phys_dd->driver_data;
+  dd->read_ahead.trigger = RTEMS_DISK_READ_AHEAD_NO_TRIGGER;
 
   if (phys_dd->phys_dev == phys_dd) {
     rtems_blkdev_bnum phys_block_count = phys_dd->size;
diff --git a/cpukit/sapi/include/confdefs.h b/cpukit/sapi/include/confdefs.h
index 4e43a27..0f804ee 100644
--- a/cpukit/sapi/include/confdefs.h
+++ b/cpukit/sapi/include/confdefs.h
@@ -1241,6 +1241,10 @@ rtems_fs_init_functions_t    rtems_fs_init_helper =
     #define CONFIGURE_BDBUF_BUFFER_MAX_SIZE \
                               RTEMS_BDBUF_BUFFER_MAX_SIZE_DEFAULT
   #endif
+  #ifndef CONFIGURE_BDBUF_READ_AHEAD_TASK_PRIORITY
+    #define CONFIGURE_BDBUF_READ_AHEAD_TASK_PRIORITY \
+                              RTEMS_BDBUF_READ_AHEAD_TASK_PRIORITY_DEFAULT
+  #endif
   #ifdef CONFIGURE_INIT
     const rtems_bdbuf_config rtems_bdbuf_configuration = {
       CONFIGURE_BDBUF_MAX_READ_AHEAD_BLOCKS,
@@ -1253,11 +1257,14 @@ rtems_fs_init_functions_t    rtems_fs_init_helper =
       CONFIGURE_BDBUF_TASK_STACK_SIZE,
       CONFIGURE_BDBUF_CACHE_MEMORY_SIZE,
       CONFIGURE_BDBUF_BUFFER_MIN_SIZE,
-      CONFIGURE_BDBUF_BUFFER_MAX_SIZE
+      CONFIGURE_BDBUF_BUFFER_MAX_SIZE,
+      CONFIGURE_BDBUF_READ_AHEAD_TASK_PRIORITY
     };
   #endif
 
-  #define CONFIGURE_LIBBLOCK_TASKS (1 + CONFIGURE_SWAPOUT_WORKER_TASKS)
+  #define CONFIGURE_LIBBLOCK_TASKS \
+    (1 + CONFIGURE_SWAPOUT_WORKER_TASKS + \
+    (CONFIGURE_BDBUF_MAX_READ_AHEAD_BLOCKS != 0))
 
   #define CONFIGURE_LIBBLOCK_TASK_EXTRA_STACKS \
     (CONFIGURE_LIBBLOCK_TASKS * \
diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am
index 1274f26..9747ad1 100644
--- a/testsuites/libtests/Makefile.am
+++ b/testsuites/libtests/Makefile.am
@@ -1,6 +1,7 @@
 ACLOCAL_AMFLAGS = -I ../aclocal
 
 SUBDIRS = POSIX
+SUBDIRS += block13
 SUBDIRS += rbheap01
 SUBDIRS += flashdisk01
 
diff --git a/testsuites/libtests/block13/Makefile.am b/testsuites/libtests/block13/Makefile.am
new file mode 100644
index 0000000..9b4a76d
--- /dev/null
+++ b/testsuites/libtests/block13/Makefile.am
@@ -0,0 +1,19 @@
+rtems_tests_PROGRAMS = block13
+block13_SOURCES = init.c
+
+dist_rtems_tests_DATA = block13.scn block13.doc
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP at .cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+
+LINK_OBJS = $(block13_OBJECTS)
+LINK_LIBS = $(block13_LDLIBS)
+
+block13$(EXEEXT): $(block13_OBJECTS) $(block13_DEPENDENCIES)
+	@rm -f block13$(EXEEXT)
+	$(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/libtests/block13/block13.doc b/testsuites/libtests/block13/block13.doc
new file mode 100644
index 0000000..04fc066
--- /dev/null
+++ b/testsuites/libtests/block13/block13.doc
@@ -0,0 +1,11 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: block13
+
+directives:
+
+  TBD
+
+concepts:
+
+  Tests the read-ahead feature of bdbuf.
diff --git a/testsuites/libtests/block13/block13.scn b/testsuites/libtests/block13/block13.scn
new file mode 100644
index 0000000..8cce718
--- /dev/null
+++ b/testsuites/libtests/block13/block13.scn
@@ -0,0 +1,11 @@
+*** TEST BLOCK 13 ***
+0 2 3 4 5 6 7 8 
+reset
+8 
+reset
+7 
+reset
+6 7 
+reset
+5 6 8 
+*** END OF TEST BLOCK 13 ***
diff --git a/testsuites/libtests/block13/init.c b/testsuites/libtests/block13/init.c
new file mode 100644
index 0000000..43ce388
--- /dev/null
+++ b/testsuites/libtests/block13/init.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2012 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Obere Lagerstr. 30
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include "tmacros.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <rtems/blkdev.h>
+#include <rtems/bdbuf.h>
+
+#define BLOCK_COUNT 9
+#define READ_COUNT 19
+
+static int block_access_counts [BLOCK_COUNT];
+
+#define RESET_CACHE (-1)
+
+static const int action_sequence [READ_COUNT] = {
+  0, 2, 3, 4, 5, 6, 7, 8,
+  RESET_CACHE,
+  8,
+  RESET_CACHE,
+  7,
+  RESET_CACHE,
+  6, 7,
+  RESET_CACHE,
+  5, 6, 8
+};
+
+#define UNUSED_LINE { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+static int expected_block_access_counts [READ_COUNT] [BLOCK_COUNT] = {
+   { 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+   { 1, 0, 1, 0, 0, 0, 0, 0, 0 },
+   { 1, 0, 1, 1, 1, 1, 0, 0, 0 },
+   { 1, 0, 1, 1, 1, 1, 0, 0, 0 },
+   { 1, 0, 1, 1, 1, 1, 1, 1, 0 },
+   { 1, 0, 1, 1, 1, 1, 1, 1, 0 },
+   { 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+   { 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+   UNUSED_LINE,
+   { 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+   UNUSED_LINE,
+   { 0, 0, 0, 0, 0, 0, 0, 1, 0 },
+   UNUSED_LINE,
+   { 0, 0, 0, 0, 0, 0, 1, 0, 0 },
+   { 0, 0, 0, 0, 0, 0, 1, 1, 1 },
+   UNUSED_LINE,
+   { 0, 0, 0, 0, 0, 1, 0, 0, 0 },
+   { 0, 0, 0, 0, 0, 1, 1, 1, 1 },
+   { 0, 0, 0, 0, 0, 1, 1, 1, 1 }
+};
+
+#define NO_TRIGGER RTEMS_DISK_READ_AHEAD_NO_TRIGGER
+
+#define TRIGGER_AFTER_RESET RTEMS_DISK_READ_AHEAD_NO_TRIGGER
+
+static const rtems_blkdev_bnum trigger [READ_COUNT] = {
+  1, 3, 5, 5, 7, 7, NO_TRIGGER, NO_TRIGGER,
+  TRIGGER_AFTER_RESET,
+  9,
+  TRIGGER_AFTER_RESET,
+  8,
+  TRIGGER_AFTER_RESET,
+  7, NO_TRIGGER,
+  TRIGGER_AFTER_RESET,
+  6, 8, NO_TRIGGER
+};
+
+#define NOT_CHANGED_BY_RESET(i) (i)
+
+static const rtems_blkdev_bnum next [READ_COUNT] = {
+  2, 4, 6, 6, 8, 8, 8, 8,
+  NOT_CHANGED_BY_RESET(8),
+  10,
+  NOT_CHANGED_BY_RESET(10),
+  9,
+  NOT_CHANGED_BY_RESET(9),
+  8, 8,
+  NOT_CHANGED_BY_RESET(8),
+  7, 9, 9
+};
+
+static int test_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg)
+{
+  int rv = 0;
+
+  if (req == RTEMS_BLKIO_REQUEST) {
+    rtems_blkdev_request *breq = arg;
+    rtems_blkdev_sg_buffer *sg = breq->bufs;
+    uint32_t i;
+
+    rtems_test_assert(breq->req == RTEMS_BLKDEV_REQ_READ);
+
+    for (i = 0; i < breq->bufnum; ++i) {
+      rtems_blkdev_bnum block = sg [i].block;
+
+      rtems_test_assert(block < BLOCK_COUNT);
+
+      ++block_access_counts [block];
+    }
+
+    (*breq->req_done)(breq->done_arg, RTEMS_SUCCESSFUL);
+  } else {
+    errno = EINVAL;
+    rv = -1;
+  }
+
+  return rv;
+}
+
+static void test_read_ahead(rtems_disk_device *dd)
+{
+  int i;
+
+  for (i = 0; i < READ_COUNT; ++i) {
+    int action = action_sequence [i];
+
+    if (action != RESET_CACHE) {
+      rtems_blkdev_bnum block = (rtems_blkdev_bnum) action;
+      rtems_status_code sc;
+      rtems_bdbuf_buffer *bd;
+
+      printf("%i ", action);
+
+      sc = rtems_bdbuf_read(dd, block, &bd);
+      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+      sc = rtems_bdbuf_release(bd);
+      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+      rtems_test_assert(
+        memcmp(
+          block_access_counts,
+          expected_block_access_counts [i],
+          sizeof(block_access_counts)
+        ) == 0
+      );
+    } else {
+      printf("\nreset\n");
+
+      rtems_bdbuf_purge_dev(dd);
+      memset(&block_access_counts, 0, sizeof(block_access_counts));
+    }
+
+    rtems_test_assert(trigger [i] == dd->read_ahead.trigger);
+    rtems_test_assert(next [i] == dd->read_ahead.next);
+  }
+
+  printf("\n");
+}
+
+static void test(void)
+{
+  rtems_status_code sc;
+  dev_t dev = 0;
+  rtems_disk_device *dd;
+
+  sc = rtems_disk_io_initialize();
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_disk_create_phys(
+    dev,
+    1,
+    BLOCK_COUNT,
+    test_disk_ioctl,
+    NULL,
+    NULL
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  dd = rtems_disk_obtain(dev);
+  rtems_test_assert(dd != NULL);
+
+  test_read_ahead(dd);
+
+  sc = rtems_disk_release(dd);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_disk_delete(dev);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void Init(rtems_task_argument arg)
+{
+  puts("\n\n*** TEST BLOCK 13 ***");
+
+  test();
+
+  puts("*** END OF TEST BLOCK 13 ***");
+
+  rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
+
+#define CONFIGURE_BDBUF_BUFFER_MIN_SIZE 1
+#define CONFIGURE_BDBUF_BUFFER_MAX_SIZE 1
+#define CONFIGURE_BDBUF_CACHE_MEMORY_SIZE BLOCK_COUNT
+#define CONFIGURE_BDBUF_MAX_READ_AHEAD_BLOCKS 2
+#define CONFIGURE_BDBUF_READ_AHEAD_TASK_PRIORITY 1
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+
+#define CONFIGURE_MAXIMUM_TASKS 1
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES
+#define CONFIGURE_INIT_TASK_PRIORITY 2
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/libtests/configure.ac b/testsuites/libtests/configure.ac
index f6af3b1..7df0520 100644
--- a/testsuites/libtests/configure.ac
+++ b/testsuites/libtests/configure.ac
@@ -41,6 +41,7 @@ AM_CONDITIONAL(NETTESTS,test "$rtems_cv_RTEMS_NETWORKING" = "yes")
 
 # Explicitly list all Makefiles here
 AC_CONFIG_FILES([Makefile
+block13/Makefile
 rbheap01/Makefile
 syscall01/Makefile
 flashdisk01/Makefile
-- 
1.6.4.2




More information about the devel mailing list