Index: cpukit/libblock/include/rtems/bdbuf.h
===================================================================
--- cpukit/libblock/include/rtems/bdbuf.h	(revision c4c8efd02782845db7658b1885a6e509ca15a4f1)
+++ cpukit/libblock/include/rtems/bdbuf.h	(revision 5c587596f09da0c52528bcb6a0c3726fcd2cfb07)
@@ -81,9 +81,10 @@
  * digraph state {
  *   size="16,8";
- *   e [label="EMPTY",style="filled",fillcolor="aquamarine"];
- *   f [label="FRESH",style="filled",fillcolor="seagreen"];
+ *   f [label="FREE",style="filled",fillcolor="aquamarine"];
+ *   e [label="EMPTY",style="filled",fillcolor="seagreen"];
  *   c [label="CACHED",style="filled",fillcolor="chartreuse"];
- *   a [label="ACCESS",style="filled",fillcolor="royalblue"];
+ *   ac [label="ACCESS CACHED",style="filled",fillcolor="royalblue"];
  *   am [label="ACCESS MODIFIED",style="filled",fillcolor="royalblue"];
+ *   ae [label="ACCESS EMPTY",style="filled",fillcolor="royalblue"];
  *   t [label="TRANSFER",style="filled",fillcolor="red"];
  *   s [label="SYNC",style="filled",fillcolor="red"];
@@ -94,20 +95,26 @@
  *   legend_access [label="Access Wake-Up",fontcolor="royalblue",shape="none"];
  *
- *   i -> e [label="Init"];
- *   e -> f [label="Buffer Recycle"];
- *   f -> a [label="Get"];
- *   f -> t [label="Read\nRead Ahead"];
- *   c -> e [label="Reallocate\nBlock Size Changed"];
- *   c -> a [label="Get\nRead"];
- *   c -> f [label="Buffer Recycle"];
- *   t -> c [label="Write Transfer Done\nRead Transfer Done\nRead Ahead Transfer Done",color="red",fontcolor="red"];
+ *   i -> f [label="Init"];
+ *   f -> e [label="Buffer Recycle"];
+ *   e -> ae [label="Get"];
+ *   e -> t [label="Read\nRead Ahead"];
+ *   c -> f [label="Reallocate\nBlock Size Changed"];
+ *   c -> ac [label="Get\nRead"];
+ *   c -> e [label="Buffer Recycle"];
+ *   t -> c [label="Transfer Done",color="red",fontcolor="red"];
+ *   t -> e [label="Transfer Error With Waiter",color="red",fontcolor="red"];
+ *   t -> f [label="Transfer Error Without Waiter",color="red",fontcolor="red"];
  *   m -> t [label="Swapout"];
  *   m -> s [label="Block Size Changed"];
  *   m -> am [label="Get\nRead"];
- *   a -> m [label="Release Modified",color="royalblue",fontcolor="royalblue"];
- *   a -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
- *   a -> c [label="Release",color="royalblue",fontcolor="royalblue"];
+ *   ac -> m [label="Release Modified",color="royalblue",fontcolor="royalblue"];
+ *   ac -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
+ *   ac -> c [label="Release",color="royalblue",fontcolor="royalblue"];
  *   am -> m [label="Release\nRelease Modified",color="royalblue",fontcolor="royalblue"];
  *   am -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
+ *   ae -> m [label="Release Modified",color="royalblue",fontcolor="royalblue"];
+ *   ae -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
+ *   ae -> e [label="Release With Waiter",color="royalblue",fontcolor="royalblue"];
+ *   ae -> f [label="Release Without Waiter",color="royalblue",fontcolor="royalblue"];
  *   s -> t [label="Swapout"];
  * }
@@ -165,51 +172,83 @@
  * The state has several implications.  Depending on the state a buffer can be
  * in the AVL tree, in a list, in use by an entity and a group user or not.
+ *
+ * <table>
+ *   <tr>
+ *     <th>State</th><th>Valid Data</th><th>AVL Tree</th>
+ *     <th>LRU List</th><th>Modified List</th><th>Synchronization List</th>
+ *     <th>Group User</th><th>External User</th>
+ *   </tr>
+ *   <tr>
+ *     <td>FREE</td><td></td><td></td>
+ *     <td>X</td><td></td><td></td><td></td><td></td>
+ *   </tr>
+ *   <tr>
+ *     <td>EMPTY</td><td></td><td>X</td>
+ *     <td>X</td><td></td><td></td><td></td><td></td>
+ *   </tr>
+ *   <tr>
+ *     <td>CACHED</td><td>X</td><td>X</td>
+ *     <td>X</td><td></td><td></td><td></td><td></td>
+ *   </tr>
+ *   <tr>
+ *     <td>ACCESS_CACHED</td><td>X</td><td>X</td>
+ *     <td></td><td></td><td></td><td>X</td><td>X</td>
+ *   </tr>
+ *   <tr>
+ *     <td>ACCESS_MODIFIED</td><td>X</td><td>X</td>
+ *     <td></td><td></td><td></td><td>X</td><td>X</td>
+ *   </tr>
+ *   <tr>
+ *     <td>ACCESS_EMPTY</td><td></td><td>X</td>
+ *     <td></td><td></td><td></td><td>X</td><td>X</td>
+ *   </tr>
+ *   <tr>
+ *     <td>MODIFIED</td><td>X</td><td>X</td>
+ *     <td></td><td>X</td><td></td><td>X</td><td></td>
+ *   </tr>
+ *   <tr>
+ *     <td>SYNC</td><td>X</td><td>X</td>
+ *     <td></td><td></td><td>X</td><td>X</td><td></td>
+ *   </tr>
+ *   <tr>
+ *     <td>TRANSFER</td><td>X</td><td>X</td>
+ *     <td></td><td></td><td></td><td>X</td><td>X</td>
+ *   </tr>
+ * </table>
  */
 typedef enum
 {
   /**
+   * @brief Free.
+   */
+  RTEMS_BDBUF_STATE_FREE = 0,
+
+  /**
    * @brief Empty.
-   *
-   * Not in the AVL tree.  Not in a list.  Not in use.  Not a user of its
-   * group.
-   */
-  RTEMS_BDBUF_STATE_EMPTY = 0,
-
-  /**
-   * @brief Fresh.
-   *
-   * In the AVL tree.  Not in a list.  In use by a get or read request.  A user
-   * of its group.
-   */
-  RTEMS_BDBUF_STATE_FRESH,
+   */
+  RTEMS_BDBUF_STATE_EMPTY,
 
   /**
    * @brief Cached.
-   *
-   * In the AVL tree.  In the LRU list.  Not in use.  Not a user of its group.
    */
   RTEMS_BDBUF_STATE_CACHED,
 
   /**
-   * @brief Accessed by upper layer.
-   *
-   * In the AVL tree.  Not in a list.  In use by an upper layer.  A user of its
-   * group.
-   */
-  RTEMS_BDBUF_STATE_ACCESS,
-
-  /**
-   * @brief Accessed and modified by upper layer.
-   *
-   * In the AVL tree.  Not in a list.  In use by an upper layer.  A user of its
-   * group.
+   * @brief Accessed by upper layer with cached data.
+   */
+  RTEMS_BDBUF_STATE_ACCESS_CACHED,
+
+  /**
+   * @brief Accessed by upper layer with modified data.
    */
   RTEMS_BDBUF_STATE_ACCESS_MODIFIED,
 
   /**
+   * @brief Accessed by upper layer with invalid data.
+   */
+  RTEMS_BDBUF_STATE_ACCESS_EMPTY,
+
+  /**
    * @brief Modified by upper layer.
-   *
-   * In the AVL tree.  In the modified list.  In use by swapout mechanic.  A
-   * user of its group.
    */
   RTEMS_BDBUF_STATE_MODIFIED,
@@ -217,7 +256,4 @@
   /**
    * @brief Scheduled for synchronization.
-   *
-   * In the AVL tree.  In the sync list.  In use by swapout mechanic.  A user
-   * of its group.
    */
   RTEMS_BDBUF_STATE_SYNC,
@@ -225,7 +261,4 @@
   /**
    * @brief In transfer by block device driver.
-   *
-   * In the AVL tree.  Not in a list.  In use by the block device driver.  A
-   * user of its group.
    */
   RTEMS_BDBUF_STATE_TRANSFER
@@ -261,6 +294,4 @@
 
   unsigned char*    buffer;     /**< Pointer to the buffer memory area */
-  int               error;      /**< If not 0 indicate an error value (errno)
-                                 * which can be used by user later */
 
   volatile rtems_bdbuf_buf_state state;  /**< State of the buffer. */
Index: cpukit/libblock/include/rtems/blkdev.h
===================================================================
--- cpukit/libblock/include/rtems/blkdev.h	(revision c4c8efd02782845db7658b1885a6e509ca15a4f1)
+++ cpukit/libblock/include/rtems/blkdev.h	(revision 5c587596f09da0c52528bcb6a0c3726fcd2cfb07)
@@ -60,14 +60,15 @@
 
 /**
- * Type for block device request done callback function.
- *
- * @param arg Argument supplied in @ref rtems_blkdev_request.
- * @param status Status code for this operation.
- * @param errno The @c errno value to be passed to the user when status is not
- * equal to @c RTEMS_SUCCESSFUL.
- */
-typedef void (* rtems_blkdev_request_cb)(void *arg,
-                                         rtems_status_code status,
-                                         int error);
+ * @brief Block device request done callback function type.
+ *
+ * The first parameter @a arg must be the argument provided by the block device
+ * request structure @ref rtems_blkdev_request.
+ *
+ * The second parameter @a status should contain the status of the operation:
+ *  - @c RTEMS_SUCCESSFUL Operation was successful.
+ *  - @c RTEMS_IO_ERROR Some sort of input or output error.
+ *  - @c RTEMS_UNSATISFIED Media no more present.
+ */
+typedef void (*rtems_blkdev_request_cb)(void *arg, rtems_status_code status);
 
 /**
@@ -127,10 +128,4 @@
    */
   rtems_status_code status;
-
-  /**
-   * If @c status is not equal to @c RTEMS_SUCCESSFUL, this field contains the
-   * error number.
-   */
-  int error;
 
   /**
Index: cpukit/libblock/src/bdbuf.c
===================================================================
--- cpukit/libblock/src/bdbuf.c	(revision c4c8efd02782845db7658b1885a6e509ca15a4f1)
+++ cpukit/libblock/src/bdbuf.c	(revision 5c587596f09da0c52528bcb6a0c3726fcd2cfb07)
@@ -124,5 +124,7 @@
   rtems_chain_control sync;              /**< Buffers to sync list */
 
-  rtems_bdbuf_waiters access_waiters;    /**< Wait for a buffer in ACCESS
+  rtems_bdbuf_waiters access_waiters;    /**< Wait for a buffer in
+                                          * ACCESS_CACHED, ACCESS_MODIFIED or
+                                          * ACCESS_EMPTY
                                           * state. */
   rtems_bdbuf_waiters transfer_waiters;  /**< Wait for a buffer in TRANSFER
@@ -143,5 +145,4 @@
   (((uint32_t)'B' << 24) | ((uint32_t)(n) & (uint32_t)0x00FFFFFF))
 
-#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_3       RTEMS_BLKDEV_FATAL_ERROR(1)
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_4       RTEMS_BLKDEV_FATAL_ERROR(2)
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_5       RTEMS_BLKDEV_FATAL_ERROR(3)
@@ -151,5 +152,5 @@
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_9       RTEMS_BLKDEV_FATAL_ERROR(7)
 #define RTEMS_BLKDEV_FATAL_BDBUF_STATE_10      RTEMS_BLKDEV_FATAL_ERROR(8)
-#define RTEMS_BLKDEV_FATAL_BDBUF_CACHE_RM      RTEMS_BLKDEV_FATAL_ERROR(9)
+#define RTEMS_BLKDEV_FATAL_BDBUF_TREE_RM       RTEMS_BLKDEV_FATAL_ERROR(9)
 #define RTEMS_BLKDEV_FATAL_BDBUF_SWAPOUT       RTEMS_BLKDEV_FATAL_ERROR(10)
 #define RTEMS_BLKDEV_FATAL_BDBUF_SYNC_LOCK     RTEMS_BLKDEV_FATAL_ERROR(11)
@@ -1055,13 +1056,13 @@
    * buffer never getting to 0 and never being forced onto disk. This raises a
    * difficult question. Is a snapshot of a block that is changing better than
-   * nothing being written ? We have tended to think we should hold changes for
+   * nothing being written? We have tended to think we should hold changes for
    * only a specific period of time even if still changing and get onto disk
    * and letting the file system try and recover this position if it can.
    */
-  if (bd->state == RTEMS_BDBUF_STATE_ACCESS)
+  if (bd->state == RTEMS_BDBUF_STATE_ACCESS_CACHED
+        || bd->state == RTEMS_BDBUF_STATE_ACCESS_EMPTY)
     bd->hold_timer = bdbuf_config.swap_block_hold;
 
   rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_MODIFIED);
-
   rtems_chain_append (&bdbuf_cache.modified, &bd->link);
 
@@ -1076,7 +1077,5 @@
 {
   rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_CACHED);
-
   rtems_chain_append (&bdbuf_cache.lru, &bd->link);
-
   rtems_bdbuf_group_release (bd);
 
@@ -1123,13 +1122,19 @@
 
 static void
-rtems_bdbuf_remove_from_cache_and_lru_list (rtems_bdbuf_buffer *bd)
+rtems_bdbuf_remove_from_tree (rtems_bdbuf_buffer *bd)
+{
+  if (rtems_bdbuf_avl_remove (&bdbuf_cache.tree, bd) != 0)
+    rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_TREE_RM);
+}
+
+static void
+rtems_bdbuf_remove_from_tree_and_lru_list (rtems_bdbuf_buffer *bd)
 {
   switch (bd->state)
   {
-    case RTEMS_BDBUF_STATE_EMPTY:
+    case RTEMS_BDBUF_STATE_FREE:
       break;
     case RTEMS_BDBUF_STATE_CACHED:
-      if (rtems_bdbuf_avl_remove (&bdbuf_cache.tree, bd) != 0)
-        rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_3);
+      rtems_bdbuf_remove_from_tree (bd);
       break;
     default:
@@ -1141,8 +1146,34 @@
 
 static void
+rtems_bdbuf_make_free_and_add_to_lru_list (rtems_bdbuf_buffer *bd)
+{
+  rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_FREE);
+  rtems_chain_prepend (&bdbuf_cache.lru, &bd->link);
+}
+
+static void
 rtems_bdbuf_make_empty_and_add_to_lru_list (rtems_bdbuf_buffer *bd)
 {
   rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_EMPTY);
-  rtems_chain_prepend (&bdbuf_cache.lru, &bd->link);
+  rtems_chain_append (&bdbuf_cache.lru, &bd->link);
+}
+
+static void
+rtems_bdbuf_release_empty_buffer (rtems_bdbuf_buffer *bd)
+{
+  rtems_bdbuf_group_release (bd);
+
+  if (bd->waiters)
+  {
+    rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_EMPTY);
+    rtems_chain_append (&bdbuf_cache.lru, &bd->link);
+    rtems_bdbuf_wake (&bdbuf_cache.access_waiters);
+  }
+  else
+  {
+    rtems_bdbuf_remove_from_tree (bd);
+    rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+    rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+  }
 }
 
@@ -1173,5 +1204,5 @@
        b < group->bds_per_group;
        b++, bd += bufs_per_bd)
-    rtems_bdbuf_remove_from_cache_and_lru_list (bd);
+    rtems_bdbuf_remove_from_tree_and_lru_list (bd);
 
   group->bds_per_group = new_bds_per_group;
@@ -1181,5 +1212,5 @@
        b < group->bds_per_group;
        b++, bd += bufs_per_bd)
-    rtems_bdbuf_make_empty_and_add_to_lru_list (bd);
+    rtems_bdbuf_make_free_and_add_to_lru_list (bd);
 
   if (b > 1)
@@ -1194,15 +1225,14 @@
                             rtems_blkdev_bnum   block)
 {
-  rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_FRESH);
-
   bd->dev       = dev;
   bd->block     = block;
   bd->avl.left  = NULL;
   bd->avl.right = NULL;
-  bd->error     = 0;
   bd->waiters   = 0;
 
   if (rtems_bdbuf_avl_insert (&bdbuf_cache.tree, bd) != 0)
     rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_RECYCLE);
+
+  rtems_bdbuf_make_empty_and_add_to_lru_list (bd);
 }
 
@@ -1232,5 +1262,5 @@
       if (bd->group->bds_per_group == bds_per_group)
       {
-        rtems_bdbuf_remove_from_cache_and_lru_list (bd);
+        rtems_bdbuf_remove_from_tree_and_lru_list (bd);
 
         recycle_bd = bd;
@@ -1490,14 +1520,14 @@
     switch (bd->state)
     {
-      case RTEMS_BDBUF_STATE_FRESH:
-        return;
       case RTEMS_BDBUF_STATE_MODIFIED:
         rtems_bdbuf_group_release (bd);
         /* Fall through */
       case RTEMS_BDBUF_STATE_CACHED:
+      case RTEMS_BDBUF_STATE_EMPTY:
         rtems_chain_extract (&bd->link);
         return;
+      case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+      case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
       case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
-      case RTEMS_BDBUF_STATE_ACCESS:
         rtems_bdbuf_wait (bd, &bdbuf_cache.access_waiters);
         break;
@@ -1534,5 +1564,5 @@
     switch (bd->state)
     {
-      case RTEMS_BDBUF_STATE_EMPTY:
+      case RTEMS_BDBUF_STATE_FREE:
         return true;
       case RTEMS_BDBUF_STATE_MODIFIED:
@@ -1540,4 +1570,5 @@
         break;
       case RTEMS_BDBUF_STATE_CACHED:
+      case RTEMS_BDBUF_STATE_EMPTY:
         if (bd->waiters == 0)
           return true;
@@ -1553,6 +1584,7 @@
           return false;
         }
+      case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+      case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
       case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
-      case RTEMS_BDBUF_STATE_ACCESS:
         rtems_bdbuf_wait (bd, &bdbuf_cache.access_waiters);
         break;
@@ -1565,6 +1597,4 @@
     }
   }
-
-  return true;
 }
 
@@ -1577,6 +1607,8 @@
     {
       case RTEMS_BDBUF_STATE_CACHED:
+      case RTEMS_BDBUF_STATE_EMPTY:
       case RTEMS_BDBUF_STATE_MODIFIED:
-      case RTEMS_BDBUF_STATE_ACCESS:
+      case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+      case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
       case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
         return;
@@ -1643,6 +1675,6 @@
         if (rtems_bdbuf_wait_for_recycle (bd))
         {
-          rtems_bdbuf_remove_from_cache_and_lru_list (bd);
-          rtems_bdbuf_make_empty_and_add_to_lru_list (bd);
+          rtems_bdbuf_remove_from_tree_and_lru_list (bd);
+          rtems_bdbuf_make_free_and_add_to_lru_list (bd);
           rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
         }
@@ -1761,6 +1793,8 @@
   {
     case RTEMS_BDBUF_STATE_CACHED:
-    case RTEMS_BDBUF_STATE_FRESH:
-      rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS);
+      rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_CACHED);
+      break;
+    case RTEMS_BDBUF_STATE_EMPTY:
+      rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_EMPTY);
       break;
     case RTEMS_BDBUF_STATE_MODIFIED:
@@ -1802,12 +1836,10 @@
  *            block device request structure).
  * @param status I/O completion status
- * @param error errno error code if status != RTEMS_SUCCESSFUL
- */
-static void
-rtems_bdbuf_read_done (void* arg, rtems_status_code status, int error)
+ */
+static void
+rtems_bdbuf_transfer_done (void* arg, rtems_status_code status)
 {
   rtems_blkdev_request* req = (rtems_blkdev_request*) arg;
 
-  req->error = error;
   req->status = status;
 
@@ -1816,8 +1848,9 @@
 
 static void
-rtems_bdbuf_create_read_request (rtems_blkdev_request *req,
-                                 rtems_disk_device    *dd,
-                                 rtems_blkdev_bnum     media_block,
-                                 size_t                bds_per_group)
+rtems_bdbuf_create_read_request (const rtems_disk_device *dd,
+                                 rtems_blkdev_bnum        media_block,
+                                 size_t                   bds_per_group,
+                                 rtems_blkdev_request    *req,
+                                 rtems_bdbuf_buffer     **bd_ptr)
 {
   rtems_bdbuf_buffer *bd = NULL;
@@ -1832,7 +1865,14 @@
     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 (dev, media_block, bds_per_group);
+
+  *bd_ptr = bd;
 
   req->bufs [0].user   = bd;
@@ -1849,5 +1889,5 @@
     case RTEMS_BDBUF_STATE_MODIFIED:
       return;
-    case RTEMS_BDBUF_STATE_FRESH:
+    case RTEMS_BDBUF_STATE_EMPTY:
       rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
       break;
@@ -1883,94 +1923,74 @@
 }
 
-static rtems_bdbuf_buffer *
-rtems_bdbuf_execute_read_request (rtems_blkdev_request *req,
-                                  rtems_disk_device    *dd)
-{
-  if (req->bufnum)
-  {
-    /*
-     * Unlock the cache. We have the buffer for the block and it will be in the
-     * access or transfer state. We may also have a number of read ahead blocks
-     * if we need to transfer data. At this point any other threads can gain
-     * access to the cache and if they are after any of the buffers we have
-     * they will block and be woken when the buffer is returned to the cache.
-     *
-     * If a transfer is needed the I/O operation will occur with pre-emption
-     * enabled and the cache unlocked. This is a change to the previous version
-     * of the bdbuf code.
-     */
-    int      result = 0;
-    int      error = 0;
-    uint32_t transfer_index = 0;
-    bool     wake_transfer = false;
-    bool     wake_buffer = false;
-
+static rtems_status_code
+rtems_bdbuf_execute_transfer_request (const rtems_disk_device *dd,
+                                      rtems_blkdev_request    *req,
+                                      bool                     cache_locked)
+{
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+  int result = 0;
+  uint32_t transfer_index = 0;
+  bool wake_transfer = false;
+  bool wake_buffer = false;
+
+  if (cache_locked)
     rtems_bdbuf_unlock_cache ();
 
-    req->req = RTEMS_BLKDEV_REQ_READ;
-    req->req_done = rtems_bdbuf_read_done;
-    req->done_arg = req;
-    req->io_task = rtems_task_self ();
-    req->status = RTEMS_RESOURCE_IN_USE;
-    req->error = 0;
-
-    result = dd->ioctl (dd->phys_dev, RTEMS_BLKIO_REQUEST, req);
-
-    if (result == 0)
-    {
-      rtems_bdbuf_wait_for_event (RTEMS_BDBUF_TRANSFER_SYNC);
-      error = req->error;
-    }
+  result = dd->ioctl (dd->phys_dev, RTEMS_BLKIO_REQUEST, req);
+
+  if (result == 0)
+  {
+    rtems_bdbuf_wait_for_event (RTEMS_BDBUF_TRANSFER_SYNC);
+    sc = req->status;
+  }
+  else
+    sc = RTEMS_IO_ERROR;
+
+  rtems_bdbuf_lock_cache ();
+
+  for (transfer_index = 0; transfer_index < req->bufnum; ++transfer_index)
+  {
+    rtems_bdbuf_buffer *bd = req->bufs [transfer_index].user;
+    bool waiters = bd->waiters > 0;
+
+    if (waiters)
+      wake_transfer = true;
     else
-      error = errno;
-
-    rtems_bdbuf_lock_cache ();
-
-    for (transfer_index = 0; transfer_index < req->bufnum; ++transfer_index)
-    {
-      rtems_bdbuf_buffer *bd = req->bufs [transfer_index].user;
-      bool waiters = bd->waiters;
-
+      wake_buffer = true;
+
+    rtems_bdbuf_group_release (bd);
+
+    if (sc == RTEMS_SUCCESSFUL)
+    {
       rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_CACHED);
-
-      if (waiters)
-        wake_transfer = true;
-
-      bd->error = error;
-
-      if (rtems_bdbuf_tracer)
-        rtems_bdbuf_show_users ("read-ahead", bd);
-
-      if (transfer_index > 0)
-      {
-        /*
-         * This is a read ahead buffer.
-         */
-
-        rtems_bdbuf_group_release (bd);
-
-        if (!waiters)
-          wake_buffer = true;
-
-        if (error == 0 || waiters)
-          rtems_chain_append (&bdbuf_cache.lru, &bd->link);
-        else
-        {
-          rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_EMPTY);
-          rtems_chain_prepend (&bdbuf_cache.lru, &bd->link);
-          if (rtems_bdbuf_avl_remove (&bdbuf_cache.tree, bd) != 0)
-            rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_CACHE_RM);
-        }
-      }
-    }
-
-    if (wake_transfer)
-      rtems_bdbuf_wake (&bdbuf_cache.transfer_waiters);
-
-    if (wake_buffer)
-      rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
-  }
-
-  return req->bufs [0].user;
+      rtems_chain_append (&bdbuf_cache.lru, &bd->link);
+    }
+    else if (waiters)
+    {
+      rtems_bdbuf_make_empty_and_add_to_lru_list (bd);
+    }
+    else
+    {
+      rtems_bdbuf_remove_from_tree (bd);
+      rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+    }
+
+    if (rtems_bdbuf_tracer)
+      rtems_bdbuf_show_users ("transfer", bd);
+  }
+
+  if (wake_transfer)
+    rtems_bdbuf_wake (&bdbuf_cache.transfer_waiters);
+
+  if (wake_buffer)
+    rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+
+  if (!cache_locked)
+    rtems_bdbuf_unlock_cache ();
+
+  if (sc == RTEMS_SUCCESSFUL || sc == RTEMS_UNSATISFIED)
+    return sc;
+  else
+    return RTEMS_IO_ERROR;
 }
 
@@ -1982,6 +2002,6 @@
   rtems_status_code     sc = RTEMS_SUCCESSFUL;
   rtems_disk_device    *dd = NULL;
+  rtems_blkdev_request *req = NULL;
   rtems_bdbuf_buffer   *bd = NULL;
-  rtems_blkdev_request *req = NULL;
   rtems_blkdev_bnum     media_block = 0;
   size_t                bds_per_group = 0;
@@ -2005,33 +2025,46 @@
 
   rtems_bdbuf_lock_cache ();
-  rtems_bdbuf_create_read_request (req, dd, media_block, bds_per_group);
-
-  bd = rtems_bdbuf_execute_read_request (req, dd);
-
-  switch (bd->state)
-  {
-    case RTEMS_BDBUF_STATE_CACHED:
-      rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS);
-      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 ();
-  }
+  rtems_bdbuf_create_read_request (dd, media_block, bds_per_group, req, &bd);
+
+  if (req->bufnum > 0)
+  {
+    sc = rtems_bdbuf_execute_transfer_request (dd, req, true);
+    if (sc == RTEMS_SUCCESSFUL)
+    {
+      rtems_chain_extract (&bd->link);
+      rtems_bdbuf_group_obtain (bd);
+    }
+  }
+
+  if (sc == RTEMS_SUCCESSFUL)
+  {
+    switch (bd->state)
+    {
+      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;
+      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 ();
+    }
+
+    *bd_ptr = bd;
+  }
+  else
+    *bd_ptr = NULL;
 
   rtems_bdbuf_unlock_cache ();
   rtems_bdbuf_release_disk (dd);
 
-  *bd_ptr = bd;
-
-  return RTEMS_SUCCESSFUL;
+  return sc;
 }
 
@@ -2064,6 +2097,9 @@
   switch (bd->state)
   {
-    case RTEMS_BDBUF_STATE_ACCESS:
+    case RTEMS_BDBUF_STATE_ACCESS_CACHED:
       rtems_bdbuf_add_to_lru_list_after_access (bd);
+      break;
+    case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+      rtems_bdbuf_release_empty_buffer (bd);
       break;
     case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
@@ -2094,5 +2130,6 @@
   switch (bd->state)
   {
-    case RTEMS_BDBUF_STATE_ACCESS:
+    case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+    case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
     case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
       rtems_bdbuf_add_to_modified_list_after_access (bd);
@@ -2122,5 +2159,6 @@
   switch (bd->state)
   {
-    case RTEMS_BDBUF_STATE_ACCESS:
+    case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+    case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
     case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
       rtems_bdbuf_add_to_sync_list_after_access (bd);
@@ -2141,6 +2179,15 @@
    * recycled.
    */
-  if (bd->state == RTEMS_BDBUF_STATE_CACHED && bd->waiters == 0)
+  if (bd->waiters == 0
+        && (bd->state == RTEMS_BDBUF_STATE_CACHED
+          || bd->state == RTEMS_BDBUF_STATE_EMPTY))
+  {
+    if (bd->state == RTEMS_BDBUF_STATE_EMPTY)
+    {
+      rtems_bdbuf_remove_from_tree (bd);
+      rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+    }
     rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+  }
 
   rtems_bdbuf_unlock_cache ();
@@ -2191,23 +2238,8 @@
 }
 
-/**
- * Call back handler called by the low level driver when the transfer has
- * completed. This function may be invoked from interrupt handlers.
- *
- * @param arg Arbitrary argument specified in block device request
- *            structure (in this case - pointer to the appropriate
- *            block device request structure).
- * @param status I/O completion status
- * @param error errno error code if status != RTEMS_SUCCESSFUL
- */
-static void
-rtems_bdbuf_write_done(void *arg, rtems_status_code status, int error)
-{
-  rtems_blkdev_request* req = (rtems_blkdev_request*) arg;
-
-  req->error = error;
-  req->status = status;
-
-  rtems_event_send (req->io_task, RTEMS_BDBUF_TRANSFER_SYNC);
+static int
+rtems_bdbuf_null_disk_ioctl (rtems_disk_device *dd, uint32_t req, void *arg)
+{
+  return -1;
 }
 
@@ -2222,5 +2254,8 @@
 rtems_bdbuf_swapout_write (rtems_bdbuf_swapout_transfer* transfer)
 {
-  rtems_disk_device* dd;
+  static rtems_disk_device null_disk = {
+    .capabilities = 0,
+    .ioctl = rtems_bdbuf_null_disk_ioctl
+  };
 
   if (rtems_bdbuf_tracer)
@@ -2232,165 +2267,98 @@
   if (!rtems_chain_is_empty (&transfer->bds))
   {
+    /*
+     * The last block number used when the driver only supports
+     * continuous blocks in a single request.
+     */
+    uint32_t last_block = 0;
+
+    /*
+     * Number of buffers per bd. This is used to detect the next
+     * block.
+     */
+    uint32_t bufs_per_bd = 0;
+
     /*
      * Obtain the disk device. The cache's mutex has been released to avoid a
      * dead lock.
      */
-    dd = rtems_disk_obtain (transfer->dev);
-    if (dd)
-    {
+    rtems_disk_device *dd = rtems_disk_obtain (transfer->dev);
+
+    if (dd == NULL)
+      dd = &null_disk;
+
+    bufs_per_bd = dd->block_size / bdbuf_config.buffer_min;
+
+    /*
+     * Take as many buffers as configured and pass to the driver. Note, the
+     * API to the drivers has an array of buffers and if a chain was passed
+     * we could have just passed the list. If the driver API is updated it
+     * should be possible to make this change with little effect in this
+     * code. The array that is passed is broken in design and should be
+     * removed. Merging members of a struct into the first member is
+     * trouble waiting to happen.
+     */
+    transfer->write_req->status = RTEMS_RESOURCE_IN_USE;
+    transfer->write_req->bufnum = 0;
+
+    while (!rtems_chain_is_empty (&transfer->bds))
+    {
+      rtems_bdbuf_buffer* bd =
+        (rtems_bdbuf_buffer*) rtems_chain_get (&transfer->bds);
+
+      bool write = false;
+
       /*
-       * The last block number used when the driver only supports
-       * continuous blocks in a single request.
+       * If the device only accepts sequential buffers and this is not the
+       * first buffer (the first is always sequential, and the buffer is not
+       * sequential then put the buffer back on the transfer chain and write
+       * the committed buffers.
        */
-      uint32_t last_block = 0;
+
+      if (rtems_bdbuf_tracer)
+        printf ("bdbuf:swapout write: bd:%" PRIu32 ", bufnum:%" PRIu32 " mode:%s\n",
+                bd->block, transfer->write_req->bufnum,
+                dd->phys_dev->capabilities &
+                RTEMS_BLKDEV_CAP_MULTISECTOR_CONT ? "MULIT" : "SCAT");
+
+      if ((dd->phys_dev->capabilities & RTEMS_BLKDEV_CAP_MULTISECTOR_CONT) &&
+          transfer->write_req->bufnum &&
+          (bd->block != (last_block + bufs_per_bd)))
+      {
+        rtems_chain_prepend (&transfer->bds, &bd->link);
+        write = true;
+      }
+      else
+      {
+        rtems_blkdev_sg_buffer* buf;
+        buf = &transfer->write_req->bufs[transfer->write_req->bufnum];
+        transfer->write_req->bufnum++;
+        buf->user   = bd;
+        buf->block  = bd->block;
+        buf->length = dd->block_size;
+        buf->buffer = bd->buffer;
+        last_block  = bd->block;
+      }
 
       /*
-       * Number of buffers per bd. This is used to detect the next
-       * block.
+       * Perform the transfer if there are no more buffers, or the transfer
+       * size has reached the configured max. value.
        */
-      uint32_t bufs_per_bd = dd->block_size / bdbuf_config.buffer_min;
-
-      /*
-       * Take as many buffers as configured and pass to the driver. Note, the
-       * API to the drivers has an array of buffers and if a chain was passed
-       * we could have just passed the list. If the driver API is updated it
-       * should be possible to make this change with little effect in this
-       * code. The array that is passed is broken in design and should be
-       * removed. Merging members of a struct into the first member is
-       * trouble waiting to happen.
-       */
-      transfer->write_req->status = RTEMS_RESOURCE_IN_USE;
-      transfer->write_req->error = 0;
-      transfer->write_req->bufnum = 0;
-
-      while (!rtems_chain_is_empty (&transfer->bds))
+
+      if (rtems_chain_is_empty (&transfer->bds) ||
+          (transfer->write_req->bufnum >= bdbuf_config.max_write_blocks))
+        write = true;
+
+      if (write)
       {
-        rtems_bdbuf_buffer* bd =
-          (rtems_bdbuf_buffer*) rtems_chain_get (&transfer->bds);
-
-        bool write = false;
-
-        /*
-         * If the device only accepts sequential buffers and this is not the
-         * first buffer (the first is always sequential, and the buffer is not
-         * sequential then put the buffer back on the transfer chain and write
-         * the committed buffers.
-         */
-
-        if (rtems_bdbuf_tracer)
-          printf ("bdbuf:swapout write: bd:%" PRIu32 ", bufnum:%" PRIu32 " mode:%s\n",
-                  bd->block, transfer->write_req->bufnum,
-                  dd->phys_dev->capabilities &
-                  RTEMS_BLKDEV_CAP_MULTISECTOR_CONT ? "MULIT" : "SCAT");
-
-        if ((dd->phys_dev->capabilities & RTEMS_BLKDEV_CAP_MULTISECTOR_CONT) &&
-            transfer->write_req->bufnum &&
-            (bd->block != (last_block + bufs_per_bd)))
-        {
-          rtems_chain_prepend (&transfer->bds, &bd->link);
-          write = true;
-        }
-        else
-        {
-          rtems_blkdev_sg_buffer* buf;
-          buf = &transfer->write_req->bufs[transfer->write_req->bufnum];
-          transfer->write_req->bufnum++;
-          buf->user   = bd;
-          buf->block  = bd->block;
-          buf->length = dd->block_size;
-          buf->buffer = bd->buffer;
-          last_block  = bd->block;
-        }
-
-        /*
-         * Perform the transfer if there are no more buffers, or the transfer
-         * size has reached the configured max. value.
-         */
-
-        if (rtems_chain_is_empty (&transfer->bds) ||
-            (transfer->write_req->bufnum >= bdbuf_config.max_write_blocks))
-          write = true;
-
-        if (write)
-        {
-          int result;
-          uint32_t b;
-
-          if (rtems_bdbuf_tracer)
-            printf ("bdbuf:swapout write: writing bufnum:%" PRIu32 "\n",
-                    transfer->write_req->bufnum);
-
-          /*
-           * Perform the transfer. No cache locks, no preemption, only the disk
-           * device is being held.
-           */
-          result = dd->ioctl (dd->phys_dev, RTEMS_BLKIO_REQUEST,
-                              transfer->write_req);
-          if (result < 0)
-          {
-            rtems_bdbuf_lock_cache ();
-
-            for (b = 0; b < transfer->write_req->bufnum; b++)
-            {
-              bd = transfer->write_req->bufs[b].user;
-              rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_MODIFIED);
-              bd->error = errno;
-
-              /*
-               * Place back on the cache's modified queue and try again.
-               *
-               * @warning Not sure this is the best option but I do not know
-               *          what else can be done.
-               */
-              rtems_chain_append (&bdbuf_cache.modified, &bd->link);
-            }
-          }
-          else
-          {
-            rtems_bdbuf_wait_for_event (RTEMS_BDBUF_TRANSFER_SYNC);
-
-            rtems_bdbuf_lock_cache ();
-
-            for (b = 0; b < transfer->write_req->bufnum; b++)
-            {
-              bd = transfer->write_req->bufs[b].user;
-              rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_CACHED);
-              bd->error = 0;
-
-              rtems_bdbuf_group_release (bd);
-
-              if (rtems_bdbuf_tracer)
-                rtems_bdbuf_show_users ("write", bd);
-
-              rtems_chain_append (&bdbuf_cache.lru, &bd->link);
-
-              if (bd->waiters)
-                rtems_bdbuf_wake (&bdbuf_cache.transfer_waiters);
-              else
-                rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
-            }
-          }
-
-          if (rtems_bdbuf_tracer)
-            rtems_bdbuf_show_usage ();
-
-          rtems_bdbuf_unlock_cache ();
-
-          transfer->write_req->status = RTEMS_RESOURCE_IN_USE;
-          transfer->write_req->error = 0;
-          transfer->write_req->bufnum = 0;
-        }
+        rtems_bdbuf_execute_transfer_request (dd, transfer->write_req, false);
+
+        transfer->write_req->status = RTEMS_RESOURCE_IN_USE;
+        transfer->write_req->bufnum = 0;
       }
-
+    }
+
+    if (dd != &null_disk)
       rtems_disk_release (dd);
-    }
-    else
-    {
-      /*
-       * We have buffers but no device. Put the BDs back onto the
-       * ready queue and exit.
-       */
-      /* @todo fixme */
-    }
   }
 }
@@ -2645,5 +2613,5 @@
 
   write_req->req = RTEMS_BLKDEV_REQ_WRITE;
-  write_req->req_done = rtems_bdbuf_write_done;
+  write_req->req_done = rtems_bdbuf_transfer_done;
   write_req->done_arg = write_req;
   write_req->io_task = rtems_task_self ();
Index: cpukit/libblock/src/flashdisk.c
===================================================================
--- cpukit/libblock/src/flashdisk.c	(revision c4c8efd02782845db7658b1885a6e509ca15a4f1)
+++ cpukit/libblock/src/flashdisk.c	(revision 5c587596f09da0c52528bcb6a0c3726fcd2cfb07)
@@ -2072,6 +2072,5 @@
   }
 
-  req->req_done (req->done_arg,
-                 ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR, ret);
+  req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
 
   return ret;
@@ -2108,6 +2107,5 @@
   }
 
-  req->req_done (req->done_arg,
-                 ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR, ret);
+  req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
 
   return 0;
Index: cpukit/libblock/src/nvdisk.c
===================================================================
--- cpukit/libblock/src/nvdisk.c	(revision c4c8efd02782845db7658b1885a6e509ca15a4f1)
+++ cpukit/libblock/src/nvdisk.c	(revision 5c587596f09da0c52528bcb6a0c3726fcd2cfb07)
@@ -596,6 +596,5 @@
   }
 
-  req->req_done (req->done_arg,
-                 ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR, ret);
+  req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
 
   return ret;
@@ -636,6 +635,5 @@
   }
 
-  req->req_done (req->done_arg,
-                 ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR, ret);
+  req->req_done (req->done_arg, ret ? RTEMS_SUCCESSFUL : RTEMS_IO_ERROR);
 
   return 0;
Index: cpukit/libblock/src/ramdisk-driver.c
===================================================================
--- cpukit/libblock/src/ramdisk-driver.c	(revision c4c8efd02782845db7658b1885a6e509ca15a4f1)
+++ cpukit/libblock/src/ramdisk-driver.c	(revision 5c587596f09da0c52528bcb6a0c3726fcd2cfb07)
@@ -66,5 +66,5 @@
         memcpy(sg->buffer, from + (sg->block * rd->block_size), sg->length);
     }
-    req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0);
+    req->req_done(req->done_arg, RTEMS_SUCCESSFUL);
     return 0;
 }
@@ -90,5 +90,5 @@
         memcpy(to + (sg->block * rd->block_size), sg->buffer, sg->length);
     }
-    req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0);
+    req->req_done(req->done_arg, RTEMS_SUCCESSFUL);
     return 0;
 }
