diff -urN linux.orig/include/linux/tqueue.h linux.diff/include/linux/tqueue.h
--- linux.orig/include/linux/tqueue.h	Wed Aug 15 17:21:11 2001
+++ linux.diff/include/linux/tqueue.h	Fri Jan 25 21:10:56 2002
@@ -67,6 +67,7 @@
 #define TQ_ACTIVE(q)		(!list_empty(&q))
 
 extern task_queue tq_timer, tq_immediate, tq_disk;
+extern struct tq_struct run_disk_tq;
 
 /*
  * To implement your own list of active bottom halfs, use the following
diff -urN linux.orig/include/linux/worktodo.h linux.diff/include/linux/worktodo.h
--- linux.orig/include/linux/worktodo.h	Wed Dec 31 19:00:00 1969
+++ linux.diff/include/linux/worktodo.h	Fri Jan 25 21:11:53 2002
@@ -0,0 +1,75 @@
+/*
+ *	Written by Benjamin LaHaise.
+ *
+ *	Copyright 2000-2001 Red Hat, Inc.
+ *
+ *	#include "gpl.h"
+ *
+ *	Basic design idea from Jeff Merkey.
+ *	Stack based on ideas from Ingo Molnar.
+ */
+#ifndef __LINUX__WORKTODO_H
+#define __LINUX__WORKTODO_H
+
+#ifndef _LINUX_WAIT_H
+#include <linux/wait.h>
+#endif
+#ifndef _LINUX_TQUEUE_H
+#include <linux/tqueue.h>
+#endif
+
+struct wtd_stack {
+	void	(*fn)(void *data);
+	void	*data;
+};
+
+struct worktodo {
+	wait_queue_t		wait;
+	struct tq_struct	tq;
+
+	void			*data;	/* for use by the wtd_ primatives */
+
+	int			sp;
+	struct wtd_stack	stack[3];
+};
+
+/* FIXME NOTE: factor from kernel/context.c */
+#define wtd_init(wtd, routine) do {			\
+	INIT_TQUEUE(&(wtd)->tq, (routine), (wtd));	\
+	(wtd)->data = 0;				\
+	(wtd)->sp = 0;					\
+} while (0)
+
+#define wtd_queue(wtd)	schedule_task(&(wtd)->tq)
+
+#define wtd_push(wtd, action, wtddata)			\
+do {							\
+	(wtd)->stack[(wtd)->sp].fn = (wtd)->tq.routine;	\
+	(wtd)->stack[(wtd)->sp++].data = (wtd)->tq.data;\
+	(wtd)->tq.routine = action;			\
+	(wtd)->tq.data = wtddata;			\
+} while (0)
+
+static inline void wtd_pop(struct worktodo *wtd)
+{
+	if (wtd->sp) {
+		wtd->sp--;
+		wtd->tq.routine = wtd->stack[wtd->sp].fn;
+		wtd->tq.data = wtd->stack[wtd->sp].data;
+	}
+}
+
+#define wtd_set_action(wtd, action, wtddata)	INIT_TQUEUE(&(wtd)->tq, action, wtddata)
+
+struct page;
+struct buffer_head;
+extern int wtd_lock_page(struct worktodo *wtd, struct page *page);
+extern int wtd_wait_on_buffer(struct worktodo *wtd, struct buffer_head *bh);
+
+#if 0	/* not implemented yet */
+extern void wtd_down(struct worktodo *wtd, struct semaphore *sem);
+extern void wtd_down_write(struct worktodo *wtd, struct rw_semaphore *sem);
+extern void wtd_down_read(struct worktodo *wtd, struct rw_semaphore *sem);
+#endif
+
+#endif /* __LINUX__WORKTODO_H */
diff -urN linux.orig/mm/Makefile linux.diff/mm/Makefile
--- linux.orig/mm/Makefile	Fri Jan 25 21:02:34 2002
+++ linux.diff/mm/Makefile	Fri Jan 25 21:10:56 2002
@@ -17,3 +17,5 @@
 	    shmem.o
 
+obj-y += wtd.o
+ 
 include $(TOPDIR)/Rules.make
diff -urN linux.orig/mm/wtd.c linux.diff/mm/wtd.c
--- linux.orig/mm/wtd.c	Wed Dec 31 19:00:00 1969
+++ linux.diff/mm/wtd.c	Fri Jan 25 21:11:53 2002
@@ -0,0 +1,70 @@
+#include <linux/worktodo.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+static void __wtd_lock_page_waiter(wait_queue_t *wait)
+{
+	struct worktodo *wtd = (struct worktodo *)wait;
+	struct page *page = (struct page *)wtd->data;
+
+	if (!TryLockPage(page)) {
+		__remove_wait_queue(&page->wait, &wtd->wait);
+		wtd_queue(wtd);
+	} else
+		schedule_task(&run_disk_tq);
+}
+
+int wtd_lock_page(struct worktodo *wtd, struct page *page)
+{
+	if (TryLockPage(page)) {
+		wtd->data = page;
+		init_waitqueue_func_entry(&wtd->wait, __wtd_lock_page_waiter);
+
+		/* Wakeups may race with TryLockPage, so try again within the wait 
+		 * queue spinlock.
+		 */
+		if (!add_wait_queue_cond(&page->wait, &wtd->wait, TryLockPage(page))) {
+			/* Page is still locked.  Kick the disk queue... */
+			run_task_queue(&tq_disk);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static void __wtd_bh_waiter(wait_queue_t *wait)
+{
+	struct worktodo *wtd = (struct worktodo *)wait;
+	struct buffer_head *bh = (struct buffer_head *)wtd->data;
+
+	if (!buffer_locked(bh)) {
+		__remove_wait_queue(&bh->b_wait, &wtd->wait);
+		wtd_queue(wtd);
+	} else {
+		schedule_task(&run_disk_tq);
+	}
+}
+
+int wtd_wait_on_buffer(struct worktodo *wtd, struct buffer_head *bh)
+{
+	if (!buffer_locked(bh))
+		return 1;
+	wtd->data = bh;
+	init_waitqueue_func_entry(&wtd->wait, __wtd_bh_waiter);
+	if (add_wait_queue_cond(&bh->b_wait, &wtd->wait, buffer_locked(bh)))
+		return 1;
+	run_task_queue(&tq_disk);
+	return 0;
+}
+
+void do_run_tq_disk(void *data)
+{
+	run_task_queue(&tq_disk);
+}
+
+struct tq_struct run_disk_tq = {
+	routine: do_run_tq_disk,
+	data: NULL
+};
+