Async buffer wait


 fs/buffer.c                 |   33 ++++++++++++++++++++++++++++-----
 include/linux/buffer_head.h |   17 +++++++++++++++++
 2 files changed, 45 insertions(+), 5 deletions(-)

diff -puN fs/buffer.c~aio-04-buffer_wq fs/buffer.c
--- 25/fs/buffer.c~aio-04-buffer_wq	2003-08-30 15:42:25.000000000 -0700
+++ 25-akpm/fs/buffer.c	2003-08-30 15:42:25.000000000 -0700
@@ -116,27 +116,50 @@ void unlock_buffer(struct buffer_head *b
 }
 
 /*
- * Block until a buffer comes unlocked.  This doesn't stop it
+ * Wait until a buffer comes unlocked.  This doesn't stop it
  * from becoming locked again - you have to lock it yourself
  * if you want to preserve its state.
+ * If the wait queue parameter specifies an async i/o callback,
+ * then instead of blocking, we just queue up the callback
+ * on the wait queue for async notification when the buffer gets
+ * unlocked.
+ * A NULL wait queue parameter defaults to synchronous behaviour
  */
-void __wait_on_buffer(struct buffer_head * bh)
+int __wait_on_buffer_wq(struct buffer_head * bh, wait_queue_t *wait)
 {
 	wait_queue_head_t *wqh = bh_waitq_head(bh);
-	DEFINE_WAIT(wait);
+	DEFINE_WAIT(local_wait);
+
+	if (!wait)
+		wait = &local_wait;
 
 	if (atomic_read(&bh->b_count) == 0 &&
 			(!bh->b_page || !PageLocked(bh->b_page)))
 		buffer_error();
 
 	do {
-		prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
+		prepare_to_wait(wqh, wait, TASK_UNINTERRUPTIBLE);
 		if (buffer_locked(bh)) {
 			blk_run_queues();
+			if (!is_sync_wait(wait)) {
+				/*
+				 * if we've queued an async wait queue
+				 * callback do not block; just tell the
+				 * caller to return and retry later when
+				 * the callback is notified
+				 */
+				return -EIOCBRETRY;
+			}
 			io_schedule();
 		}
 	} while (buffer_locked(bh));
-	finish_wait(wqh, &wait);
+	finish_wait(wqh, wait);
+	return 0;
+}
+
+void __wait_on_buffer(struct buffer_head * bh)
+{
+	__wait_on_buffer_wq(bh, NULL);
 }
 
 static void
diff -puN include/linux/buffer_head.h~aio-04-buffer_wq include/linux/buffer_head.h
--- 25/include/linux/buffer_head.h~aio-04-buffer_wq	2003-08-30 15:42:25.000000000 -0700
+++ 25-akpm/include/linux/buffer_head.h	2003-08-30 15:42:25.000000000 -0700
@@ -162,6 +162,7 @@ void mark_buffer_async_write(struct buff
 void invalidate_bdev(struct block_device *, int);
 int sync_blockdev(struct block_device *bdev);
 void __wait_on_buffer(struct buffer_head *);
+int __wait_on_buffer_wq(struct buffer_head *, wait_queue_t *wait);
 wait_queue_head_t *bh_waitq_head(struct buffer_head *bh);
 void wake_up_buffer(struct buffer_head *bh);
 int fsync_bdev(struct block_device *);
@@ -283,10 +284,26 @@ static inline void wait_on_buffer(struct
 		__wait_on_buffer(bh);
 }
 
+static inline int wait_on_buffer_wq(struct buffer_head *bh, wait_queue_t *wait)
+{
+	if (buffer_locked(bh))
+		return __wait_on_buffer_wq(bh, wait);
+
+	return 0;
+}
+
 static inline void lock_buffer(struct buffer_head *bh)
 {
 	while (test_set_buffer_locked(bh))
 		__wait_on_buffer(bh);
 }
 
+static inline int lock_buffer_wq(struct buffer_head *bh, wait_queue_t *wait)
+{
+	if (test_set_buffer_locked(bh))
+		return __wait_on_buffer_wq(bh, wait);
+
+	return 0;
+}
+
 #endif /* _LINUX_BUFFER_HEAD_H */

_