diff -urN linux-2.6.10-rc3-mm1/arch/i386/kernel/entry.S linux-2.6.10-rc3-no1/arch/i386/kernel/entry.S
--- linux-2.6.10-rc3-mm1/arch/i386/kernel/entry.S	2004-12-16 21:11:15.617125264 -0500
+++ linux-2.6.10-rc3-no1/arch/i386/kernel/entry.S	2004-12-16 23:25:08.222583172 -0500
@@ -941,5 +941,7 @@
 	.long sys_vperfctr_unlink
 	.long sys_vperfctr_iresume
 	.long sys_vperfctr_read
+	.long sys_ioprio_set		/* 295 */
+	.long sys_ioprio_get
 
 syscall_table_size=(.-sys_call_table)
diff -urN linux-2.6.10-rc3-mm1/arch/ia64/kernel/entry.S linux-2.6.10-rc3-no1/arch/ia64/kernel/entry.S
--- linux-2.6.10-rc3-mm1/arch/ia64/kernel/entry.S	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/arch/ia64/kernel/entry.S	2004-12-16 23:25:08.240580618 -0500
@@ -1531,8 +1531,8 @@
 	data8 sys_add_key
 	data8 sys_request_key
 	data8 sys_keyctl
-	data8 sys_ni_syscall
-	data8 sys_ni_syscall			// 1275
+	data8 sys_ioprio_set
+	data8 sys_ioprio_get			// 1275
 	data8 sys_ni_syscall
 	data8 sys_ni_syscall
 	data8 sys_ni_syscall
diff -urN linux-2.6.10-rc3-mm1/arch/ppc/kernel/misc.S linux-2.6.10-rc3-no1/arch/ppc/kernel/misc.S
--- linux-2.6.10-rc3-mm1/arch/ppc/kernel/misc.S	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/arch/ppc/kernel/misc.S	2004-12-16 23:25:08.256578348 -0500
@@ -1455,3 +1455,5 @@
 	.long sys_vperfctr_unlink
 	.long sys_vperfctr_iresume	/* 275 */
 	.long sys_vperfctr_read
+	.long sys_ioprio_set
+	.long sys_ioprio_get
diff -urN linux-2.6.10-rc3-mm1/drivers/block/cfq-iosched.c linux-2.6.10-rc3-no1/drivers/block/cfq-iosched.c
--- linux-2.6.10-rc3-mm1/drivers/block/cfq-iosched.c	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/drivers/block/cfq-iosched.c	2004-12-16 23:25:08.286574091 -0500
@@ -22,21 +22,29 @@
 #include <linux/rbtree.h>
 #include <linux/mempool.h>
 
-static unsigned long max_elapsed_crq;
-static unsigned long max_elapsed_dispatch;
-
 /*
  * tunables
  */
 static int cfq_quantum = 4;		/* max queue in one round of service */
 static int cfq_queued = 8;		/* minimum rq allocate limit per-queue*/
-static int cfq_service = HZ;		/* period over which service is avg */
 static int cfq_fifo_expire_r = HZ / 2;	/* fifo timeout for sync requests */
 static int cfq_fifo_expire_w = 5 * HZ;	/* fifo timeout for async requests */
 static int cfq_fifo_rate = HZ / 8;	/* fifo expiry rate */
 static int cfq_back_max = 16 * 1024;	/* maximum backwards seek, in KiB */
 static int cfq_back_penalty = 2;	/* penalty of a backwards seek */
 
+static int cfq_slice_sync = HZ / 40;
+static int cfq_slice_async = HZ / 175;
+static int cfq_slice_async_rq = 16;
+static int cfq_slice_idle = HZ / 100;
+
+#define CFQ_MAX_SEEK_MEAN	(100000)
+
+/*
+ * disable queueing at the driver/hardware level
+ */
+static int cfq_max_depth = 1;
+
 /*
  * for the hash of cfqq inside the cfqd
  */
@@ -55,6 +63,7 @@
 #define list_entry_hash(ptr)	hlist_entry((ptr), struct cfq_rq, hash)
 
 #define list_entry_cfqq(ptr)	list_entry((ptr), struct cfq_queue, cfq_list)
+#define list_entry_fifo(ptr)	list_entry((ptr), struct request, queuelist)
 
 #define RQ_DATA(rq)		(rq)->elevator_private
 
@@ -76,34 +85,30 @@
 #define rq_rb_key(rq)		(rq)->sector
 
 /*
- * threshold for switching off non-tag accounting
- */
-#define CFQ_MAX_TAG		(4)
-
-/*
  * sort key types and names
  */
 enum {
 	CFQ_KEY_PGID,
 	CFQ_KEY_TGID,
+	CFQ_KEY_PID,
 	CFQ_KEY_UID,
 	CFQ_KEY_GID,
 	CFQ_KEY_LAST,
 };
 
-static char *cfq_key_types[] = { "pgid", "tgid", "uid", "gid", NULL };
-
-/*
- * spare queue
- */
-#define CFQ_KEY_SPARE		(~0UL)
+static char *cfq_key_types[] = { "pgid", "tgid", "pid", "uid", "gid", NULL };
 
 static kmem_cache_t *crq_pool;
 static kmem_cache_t *cfq_pool;
 static kmem_cache_t *cfq_ioc_pool;
 
+#define CFQ_PRIO_NR		IOPRIO_NR
+
 struct cfq_data {
-	struct list_head rr_list;
+	atomic_t ref;
+
+	struct list_head rr_list[CFQ_PRIO_NR];
+	struct list_head cur_rr;
 	struct list_head empty_list;
 
 	struct hlist_head *cfq_hash;
@@ -114,8 +119,6 @@
 
 	unsigned int max_queued;
 
-	atomic_t ref;
-
 	int key_type;
 
 	mempool_t *crq_pool;
@@ -127,6 +130,19 @@
 	int rq_in_driver;
 
 	/*
+	 * schedule slice state info
+	 */
+	struct timer_list timer;
+	struct work_struct unplug_work;
+	struct cfq_queue *active_queue;
+	struct cfq_io_context *active_cic;
+	int cur_prio;
+	unsigned int dispatch_slice;
+	unsigned long idle_start;
+
+	sector_t new_seek_mean;
+
+	/*
 	 * tunables, see top of file
 	 */
 	unsigned int cfq_quantum;
@@ -136,9 +152,10 @@
 	unsigned int cfq_fifo_batch_expire;
 	unsigned int cfq_back_penalty;
 	unsigned int cfq_back_max;
-	unsigned int find_best_crq;
-
-	unsigned int cfq_tagged;
+	unsigned int cfq_slice[2];
+	unsigned int cfq_slice_async_rq;
+	unsigned int cfq_slice_idle;
+	unsigned int cfq_max_depth;
 };
 
 struct cfq_queue {
@@ -150,8 +167,6 @@
 	struct hlist_node cfq_hash;
 	/* hash key */
 	unsigned long key;
-	/* whether queue is on rr (or empty) list */
-	int on_rr;
 	/* on either rr or empty list of cfqd */
 	struct list_head cfq_list;
 	/* sorted list of pending requests */
@@ -169,15 +184,30 @@
 
 	int key_type;
 
-	unsigned long service_start;
-	unsigned long service_used;
-
-	unsigned int max_rate;
+	unsigned long slice_start;
+	unsigned long slice_end;
+	unsigned long slice_left;
+	unsigned long service_last;
 
 	/* number of requests that have been handed to the driver */
 	int in_flight;
-	/* number of currently allocated requests */
-	int alloc_limit[2];
+
+	/* io prio of this group */
+	int ioprio;
+
+	unsigned int idle_hit, idle_miss;
+
+	/* whether queue is on rr (or empty) list */
+	unsigned int on_rr : 1;
+	/* idle slice, waiting for new request submission */
+	unsigned int wait_request : 1;
+	/* set when wait_request gets set, reset on first rq alloc */
+	unsigned int must_alloc : 1;
+	/* idle slice, request added, now waiting to dispatch it */
+	unsigned int must_dispatch : 1;
+
+	unsigned int idle_window : 1;
+	unsigned int doing_sync : 1;
 };
 
 struct cfq_rq {
@@ -192,16 +222,19 @@
 	unsigned long service_start;
 	unsigned long queue_start;
 
+	sector_t end_pos;
+
 	unsigned int in_flight : 1;
 	unsigned int accounted : 1;
 	unsigned int is_sync   : 1;
-	unsigned int is_write  : 1;
+	unsigned int requeued  : 1;
 };
 
 static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long);
 static void cfq_dispatch_sort(request_queue_t *, struct cfq_rq *);
 static void cfq_update_next_crq(struct cfq_rq *);
 static void cfq_put_cfqd(struct cfq_data *cfqd);
+static struct cfq_io_context *cfq_get_io_context(struct cfq_queue **, int);
 
 /*
  * what the fairness is based on (ie how processes are grouped and
@@ -219,6 +252,8 @@
 		default:
 		case CFQ_KEY_TGID:
 			return tsk->tgid;
+		case CFQ_KEY_PID:
+			return tsk->pid;
 		case CFQ_KEY_UID:
 			return tsk->uid;
 		case CFQ_KEY_GID:
@@ -292,6 +327,10 @@
 		return crq2;
 	if (crq2 == NULL)
 		return crq1;
+	if (crq1->requeued)
+		return crq1;
+	if (crq2->requeued)
+		return crq2;
 
 	s1 = crq1->request->sector;
 	s2 = crq2->request->sector;
@@ -406,67 +445,23 @@
 		cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq);
 }
 
-static int cfq_check_sort_rr_list(struct cfq_queue *cfqq)
-{
-	struct list_head *head = &cfqq->cfqd->rr_list;
-	struct list_head *next, *prev;
-
-	/*
-	 * list might still be ordered
-	 */
-	next = cfqq->cfq_list.next;
-	if (next != head) {
-		struct cfq_queue *cnext = list_entry_cfqq(next);
-
-		if (cfqq->service_used > cnext->service_used)
-			return 1;
-	}
-
-	prev = cfqq->cfq_list.prev;
-	if (prev != head) {
-		struct cfq_queue *cprev = list_entry_cfqq(prev);
-
-		if (cfqq->service_used < cprev->service_used)
-			return 1;
-	}
-
-	return 0;
-}
-
-static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue)
+static void cfq_resort_rr_list(struct cfq_queue *cfqq)
 {
-	struct list_head *entry = &cfqq->cfqd->rr_list;
-
-	if (!cfqq->on_rr)
-		return;
-	if (!new_queue && !cfq_check_sort_rr_list(cfqq))
-		return;
+	struct list_head *list = &cfqq->cfqd->rr_list[cfqq->ioprio];
+	struct list_head *entry = list;
 
 	list_del(&cfqq->cfq_list);
 
 	/*
-	 * sort by our mean service_used, sub-sort by in-flight requests
+	 * sort by when queue was last serviced
 	 */
-	while ((entry = entry->prev) != &cfqq->cfqd->rr_list) {
+	while ((entry = entry->prev) != list) {
 		struct cfq_queue *__cfqq = list_entry_cfqq(entry);
 
-		if (cfqq->service_used > __cfqq->service_used)
+		if (!__cfqq->service_last)
+			break;
+		if (time_before(__cfqq->service_last, cfqq->service_last))
 			break;
-		else if (cfqq->service_used == __cfqq->service_used) {
-			struct list_head *prv;
-
-			while ((prv = entry->prev) != &cfqq->cfqd->rr_list) {
-				__cfqq = list_entry_cfqq(prv);
-
-				WARN_ON(__cfqq->service_used > cfqq->service_used);
-				if (cfqq->service_used != __cfqq->service_used)
-					break;
-				if (cfqq->in_flight > __cfqq->in_flight)
-					break;
-
-				entry = prv;
-			}
-		}
 	}
 
 	list_add(&cfqq->cfq_list, entry);
@@ -477,18 +472,19 @@
  * the pending list according to requests serviced
  */
 static inline void
-cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq, int requeue)
 {
-	/*
-	 * it's currently on the empty list
-	 */
-	cfqq->on_rr = 1;
-	cfqd->busy_queues++;
+	BUG_ON(cfqq->on_rr);
 
-	if (time_after(jiffies, cfqq->service_start + cfq_service))
-		cfqq->service_used >>= 3;
+	cfqd->busy_queues++;
+	cfqq->on_rr = 1;
 
-	cfq_sort_rr_list(cfqq, 1);
+	if (!requeue)
+		cfq_resort_rr_list(cfqq);
+	else {
+		list_del(&cfqq->cfq_list);
+		list_add_tail(&cfqq->cfq_list, &cfqd->cur_rr);
+	}
 }
 
 static inline void
@@ -512,10 +508,10 @@
 		struct cfq_data *cfqd = cfqq->cfqd;
 
 		BUG_ON(!cfqq->queued[crq->is_sync]);
+		cfqq->queued[crq->is_sync]--;
 
 		cfq_update_next_crq(crq);
 
-		cfqq->queued[crq->is_sync]--;
 		rb_erase(&crq->rb_node, &cfqq->sort_list);
 		RB_CLEAR_COLOR(&crq->rb_node);
 
@@ -567,7 +563,7 @@
 	rb_insert_color(&crq->rb_node, &cfqq->sort_list);
 
 	if (!cfqq->on_rr)
-		cfq_add_cfqq_rr(cfqd, cfqq);
+		cfq_add_cfqq_rr(cfqd, cfqq, crq->requeued);
 
 	/*
 	 * check if this request is a better next-serve candidate
@@ -615,22 +611,33 @@
 /*
  * make sure the service time gets corrected on reissue of this request
  */
+static void cfq_enqueue(struct cfq_data *cfqd, struct request *rq);
 static void cfq_requeue_request(request_queue_t *q, struct request *rq)
 {
+	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct cfq_rq *crq = RQ_DATA(rq);
 
 	if (crq) {
 		struct cfq_queue *cfqq = crq->cfq_queue;
 
-		if (cfqq->cfqd->cfq_tagged) {
-			cfqq->service_used--;
-			cfq_sort_rr_list(cfqq, 0);
+		if (crq->accounted) {
+			crq->accounted = 0;
+			WARN_ON(!cfqq->cfqd->rq_in_driver);
+			cfqq->cfqd->rq_in_driver--;
+		}
+		if (crq->in_flight) {
+			crq->in_flight = 0;
+			WARN_ON(!cfqq->in_flight);
+			cfqq->in_flight--;
 		}
-
-		crq->accounted = 0;
-		cfqq->cfqd->rq_in_driver--;
 	}
-	list_add(&rq->queuelist, &q->queue_head);
+
+	if (blk_fs_request(rq)) {
+		crq->cfq_queue->next_crq = crq;
+		crq->requeued = 1;
+		cfq_enqueue(cfqd, rq);
+	} else
+		list_add(&rq->queuelist, &q->queue_head);
 }
 
 static void cfq_remove_request(request_queue_t *q, struct request *rq)
@@ -640,9 +647,8 @@
 	if (crq) {
 		cfq_remove_merge_hints(q, crq);
 		list_del_init(&rq->queuelist);
+		cfq_del_crq_rb(crq);
 
-		if (crq->cfq_queue)
-			cfq_del_crq_rb(crq);
 	}
 }
 
@@ -723,6 +729,165 @@
 	cfq_remove_request(q, next);
 }
 
+static inline void
+__cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+{
+	if (cfqq) {
+		cfqq->slice_start = jiffies;
+		cfqq->slice_end = 0;
+		cfqq->slice_left = 0;
+		cfqd->cur_prio = cfqq->ioprio;
+	}
+
+	cfqd->active_queue = cfqq;
+}
+
+static void cfq_set_active_queue(struct cfq_data *cfqd)
+{
+	struct cfq_queue *cfqq = NULL;
+	int prio = cfqd->cur_prio;
+
+	do {
+		/*
+		 * to prevent starvation, we splice the prio list when we
+		 * first start serving that prio level.
+		 */
+		if (!list_empty(&cfqd->cur_rr)) {
+			cfqq = list_entry_cfqq(cfqd->cur_rr.next);
+			break;
+		} else if (!list_empty(&cfqd->rr_list[prio])) {
+			list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr);
+			cfqq = list_entry_cfqq(cfqd->cur_rr.next);
+			break;
+		}
+
+		if (++prio == CFQ_PRIO_NR)
+			prio = 0;
+
+	} while (prio != cfqd->cur_prio);
+
+	__cfq_set_active_queue(cfqd, cfqq);
+}
+
+/*
+ * current cfqq expired its slice (or was too idle), select new one
+ */
+static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted)
+{
+	struct cfq_queue *cfqq = cfqd->active_queue;
+
+	if (cfqq) {
+		unsigned long now = jiffies;
+
+		if (cfqq->wait_request)
+			del_timer(&cfqd->timer);
+
+		if (!preempted)
+			cfqq->service_last = now;
+
+		cfqq->must_dispatch = 0;
+		cfqq->wait_request = 0;
+
+		/*
+		 * store what was left of this slice, if the queue idled out
+		 * or was preempted
+		 */
+		if (time_before(now, cfqq->slice_end))
+			cfqq->slice_left = now - cfqq->slice_end;
+		else
+			cfqq->slice_left = 0;
+
+		if (cfqq->on_rr) {
+			/*
+			 * try to be fair to the preempted queue - it gets
+			 * first service next time this prio level is served
+			 */
+			if (preempted)
+				list_move(&cfqq->cfq_list, &cfqd->rr_list[cfqq->ioprio]);
+			else
+				cfq_resort_rr_list(cfqq);
+		}
+
+		/*
+		 * check whether we should move the current prio service level.
+		 * we do so once we have served all queues at this level once
+		 */
+		if (list_empty(&cfqd->cur_rr)) {
+			if (++cfqd->cur_prio == CFQ_PRIO_NR)
+				cfqd->cur_prio = 0;
+		}
+
+		cfqd->active_queue = NULL;
+
+		if (cfqd->active_cic) {
+			put_io_context(cfqd->active_cic->ioc);
+			cfqd->active_cic = NULL;
+		}
+	}
+
+	cfqd->dispatch_slice = 0;
+}
+
+static int
+cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+			
+{
+	WARN_ON(!RB_EMPTY(&cfqq->sort_list));
+
+	if (!cfqd->cfq_slice_idle)
+		return 0;
+	if (!cfqq->idle_window)
+		return 0;
+
+	if (!timer_pending(&cfqd->timer)) {
+		unsigned long slice_left = cfqq->slice_end - 1;
+
+		cfqd->idle_start = jiffies;
+		cfqd->timer.expires = min(jiffies + cfqd->cfq_slice_idle, slice_left);
+		add_timer(&cfqd->timer);
+	}
+
+	cfqq->wait_request = 1;
+	cfqq->must_alloc = 1;
+	return 1;
+}
+
+/*
+ * get next queue for service
+ */
+static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
+{
+	unsigned long now = jiffies;
+	struct cfq_queue *cfqq;
+
+	cfqq = cfqd->active_queue;
+	if (!cfqq)
+		goto new_queue;
+
+	/*
+	 * slice has expired
+	 */
+	if (!cfqq->must_dispatch && time_after(jiffies, cfqq->slice_end))
+		goto new_queue;
+
+	/*
+	 * if queue has requests, dispatch one. if not, check if
+	 * enough slice is left to wait for one
+	 */
+	if (!RB_EMPTY(&cfqq->sort_list))
+		goto keep_queue;
+	else if (time_before(now, cfqq->slice_end)) {
+		if (cfq_arm_slice_timer(cfqd, cfqq))
+			return NULL;
+	}
+
+new_queue:
+	cfq_slice_expired(cfqd, 0);
+	cfq_set_active_queue(cfqd);
+keep_queue:
+	return cfqd->active_queue;
+}
+
 /*
  * we dispatch cfqd->cfq_quantum requests in total from the rr_list queues,
  * this function sector sorts the selected request to minimize seeks. we start
@@ -736,31 +901,34 @@
 	struct request *__rq;
 	sector_t last;
 
-	cfq_del_crq_rb(crq);
-	cfq_remove_merge_hints(q, crq);
 	list_del(&crq->request->queuelist);
 
 	last = cfqd->last_sector;
-	while ((entry = entry->prev) != head) {
-		__rq = list_entry_rq(entry);
-
-		if (blk_barrier_rq(crq->request))
+	list_for_each_entry_reverse(__rq, head, queuelist) {
+		if (blk_barrier_rq(__rq))
 			break;
-		if (!blk_fs_request(crq->request))
+		if (!blk_fs_request(__rq))
 			break;
 
-		if (crq->request->sector > __rq->sector)
+		if (__rq->sector <= crq->request->sector)
 			break;
 		if (__rq->sector > last && crq->request->sector < last) {
-			last = crq->request->sector;
+			last = crq->request->sector + crq->request->nr_sectors;
 			break;
 		}
+		entry = &__rq->queuelist;
 	}
 
 	cfqd->last_sector = last;
+
+	cfq_del_crq_rb(crq);
+	cfq_remove_merge_hints(q, crq);
+
+	crq->end_pos = crq->request->sector + crq->request->nr_sectors;
 	crq->in_flight = 1;
+	crq->requeued = 0;
 	cfqq->in_flight++;
-	list_add(&crq->request->queuelist, entry);
+	list_add_tail(&crq->request->queuelist, entry);
 }
 
 /*
@@ -777,97 +945,113 @@
 	if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire))
 		return NULL;
 
-	crq = RQ_DATA(list_entry(cfqq->fifo[0].next, struct request, queuelist));
-	if (reads && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_r)) {
-		cfqq->last_fifo_expire = now;
-		return crq;
+	if (reads) {
+		crq = RQ_DATA(list_entry_fifo(cfqq->fifo[READ].next));
+		if (time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_r)) {
+			cfqq->last_fifo_expire = now;
+			return crq;
+		}
 	}
 
-	crq = RQ_DATA(list_entry(cfqq->fifo[1].next, struct request, queuelist));
-	if (writes && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_w)) {
-		cfqq->last_fifo_expire = now;
-		return crq;
+	if (writes) {
+		crq = RQ_DATA(list_entry_fifo(cfqq->fifo[WRITE].next));
+		if (time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_w)) {
+			cfqq->last_fifo_expire = now;
+			return crq;
+		}
 	}
 
 	return NULL;
 }
 
 /*
- * dispatch a single request from given queue
+ * Scale schedule slice based on io priority
  */
-static inline void
-cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd,
-		     struct cfq_queue *cfqq)
+static inline int cfq_prio_to_slice(struct cfq_data *cfqd, int prio, int sync)
 {
-	struct cfq_rq *crq;
+	int base_slice = cfqd->cfq_slice[sync];
 
-	/*
-	 * follow expired path, else get first next available
-	 */
-	if ((crq = cfq_check_fifo(cfqq)) == NULL) {
-		if (cfqd->find_best_crq)
+	return base_slice + base_slice * (CFQ_PRIO_NR - 1 - prio);
+}
+
+static int
+__cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq,
+			int max_dispatch)
+{
+	int dispatched = 0, sync = 0;
+
+	BUG_ON(RB_EMPTY(&cfqq->sort_list));
+
+	do {
+		struct cfq_rq *crq;
+
+		/*
+		 * follow expired path, else get first next available
+		 */
+		if ((crq = cfq_check_fifo(cfqq)) == NULL)
 			crq = cfqq->next_crq;
-		else
-			crq = rb_entry_crq(rb_first(&cfqq->sort_list));
-	}
 
-	cfqd->last_sector = crq->request->sector + crq->request->nr_sectors;
+		/*
+		 * finally, insert request into driver list
+		 */
+		cfq_dispatch_sort(cfqd->queue, crq);
+
+		cfqd->dispatch_slice++;
+		dispatched++;
+		sync += crq->is_sync;
+
+		if (!cfqd->active_cic) {
+			atomic_inc(&crq->io_context->ioc->refcount);
+			cfqd->active_cic = crq->io_context;
+		}
+
+		if (RB_EMPTY(&cfqq->sort_list))
+			break;
+
+	} while (dispatched < max_dispatch);
+
+	/*
+	 * if slice end isn't set yet, set it. if at least one request was
+	 * sync, use the sync time slice value
+	 */
+	if (!cfqq->slice_end)
+		cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq->ioprio, !!sync) + jiffies;
 
 	/*
-	 * finally, insert request into driver list
+	 * expire an async queue immediately if it has used up its slice
 	 */
-	cfq_dispatch_sort(q, crq);
+	if (!sync && cfqd->dispatch_slice >= cfqd->cfq_slice_async_rq)
+		cfq_slice_expired(cfqd, 0);
+
+	cfqq->doing_sync = !!sync;
+	return dispatched;
 }
 
 static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct cfq_queue *cfqq;
-	struct list_head *entry, *tmp;
-	int queued, busy_queues, first_round;
 
-	if (list_empty(&cfqd->rr_list))
+	if (!cfqd->busy_queues)
 		return 0;
 
-	queued = 0;
-	first_round = 1;
-restart:
-	busy_queues = 0;
-	list_for_each_safe(entry, tmp, &cfqd->rr_list) {
-		cfqq = list_entry_cfqq(entry);
-
-		BUG_ON(RB_EMPTY(&cfqq->sort_list));
-
-		/*
-		 * first round of queueing, only select from queues that
-		 * don't already have io in-flight
-		 */
-		if (first_round && cfqq->in_flight)
-			continue;
-
-		cfq_dispatch_request(q, cfqd, cfqq);
-
-		if (!RB_EMPTY(&cfqq->sort_list))
-			busy_queues++;
-
-		queued++;
-	}
-
-	if ((queued < max_dispatch) && (busy_queues || first_round)) {
-		first_round = 0;
-		goto restart;
+	cfqq = cfq_select_queue(cfqd);
+	if (cfqq) {
+		cfqq->wait_request = 0;
+		cfqq->must_dispatch = 0;
+		del_timer(&cfqd->timer);
+		return __cfq_dispatch_requests(cfqd, cfqq, max_dispatch);
 	}
 
-	return queued;
+	return 0;
 }
 
 static inline void cfq_account_dispatch(struct cfq_rq *crq)
 {
 	struct cfq_queue *cfqq = crq->cfq_queue;
 	struct cfq_data *cfqd = cfqq->cfqd;
-	unsigned long now, elapsed;
 
-	if (!blk_fs_request(crq->request))
+	if (unlikely(!blk_fs_request(crq->request)))
 		return;
 
 	/*
@@ -877,37 +1061,9 @@
 	if (crq->accounted)
 		return;
 
-	now = jiffies;
-	if (cfqq->service_start == ~0UL)
-		cfqq->service_start = now;
-
-	/*
-	 * on drives with tagged command queueing, command turn-around time
-	 * doesn't necessarily reflect the time spent processing this very
-	 * command inside the drive. so do the accounting differently there,
-	 * by just sorting on the number of requests
-	 */
-	if (cfqd->cfq_tagged) {
-		if (time_after(now, cfqq->service_start + cfq_service)) {
-			cfqq->service_start = now;
-			cfqq->service_used /= 10;
-		}
-
-		cfqq->service_used++;
-		cfq_sort_rr_list(cfqq, 0);
-	}
-
-	elapsed = now - crq->queue_start;
-	if (elapsed > max_elapsed_dispatch)
-		max_elapsed_dispatch = elapsed;
-
 	crq->accounted = 1;
-	crq->service_start = now;
-
-	if (++cfqd->rq_in_driver >= CFQ_MAX_TAG && !cfqd->cfq_tagged) {
-		cfqq->cfqd->cfq_tagged = 1;
-		printk("cfq: depth %d reached, tagging now on\n", CFQ_MAX_TAG);
-	}
+	crq->service_start = jiffies;
+	cfqd->rq_in_driver++;
 }
 
 static inline void
@@ -921,20 +1077,9 @@
 	WARN_ON(!cfqd->rq_in_driver);
 	cfqd->rq_in_driver--;
 
-	if (!cfqd->cfq_tagged) {
-		unsigned long now = jiffies;
-		unsigned long duration = now - crq->service_start;
-
-		if (time_after(now, cfqq->service_start + cfq_service)) {
-			cfqq->service_start = now;
-			cfqq->service_used >>= 3;
-		}
-
-		cfqq->service_used += duration;
-		cfq_sort_rr_list(cfqq, 0);
-
-		if (duration > max_elapsed_crq)
-			max_elapsed_crq = duration;
+	if (crq->is_sync) {
+		crq->io_context->last_end_request = jiffies;
+		crq->io_context->last_request_pos = crq->end_pos;
 	}
 }
 
@@ -948,7 +1093,15 @@
 dispatch:
 		rq = list_entry_rq(q->queue_head.next);
 
-		if ((crq = RQ_DATA(rq)) != NULL) {
+		crq = RQ_DATA(rq);
+		if (crq) {
+			/*
+			 * if idle window is disable, allow queue buildup
+			 */
+			if (!crq->in_flight && !crq->cfq_queue->idle_window &&
+			    cfqd->rq_in_driver >= cfqd->cfq_max_depth)
+				return NULL;
+
 			cfq_remove_merge_hints(q, crq);
 			cfq_account_dispatch(crq);
 		}
@@ -970,6 +1123,8 @@
  */
 static void cfq_put_queue(struct cfq_queue *cfqq)
 {
+	struct cfq_data *cfqd = cfqq->cfqd;
+
 	BUG_ON(!atomic_read(&cfqq->ref));
 
 	if (!atomic_dec_and_test(&cfqq->ref))
@@ -978,6 +1133,9 @@
 	BUG_ON(rb_first(&cfqq->sort_list));
 	BUG_ON(cfqq->on_rr);
 
+	if (unlikely(cfqd->active_queue == cfqq))
+		cfq_slice_expired(cfqd, 0);
+
 	cfq_put_cfqd(cfqq->cfqd);
 
 	/*
@@ -1044,50 +1202,64 @@
 	kmem_cache_free(cfq_ioc_pool, cic);
 }
 
+static void cfq_exit_single_io_context(struct cfq_io_context *cic)
+{
+	struct cfq_data *cfqd = cic->cfqq->cfqd;
+	request_queue_t *q = cfqd->queue;
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	if (cic->cfqq == cfqd->active_queue) {
+		cfq_slice_expired(cfqd, 0);
+
+		if (cfqd->busy_queues)
+			kblockd_schedule_work(&cfqd->unplug_work);
+	}
+
+	cfq_put_queue(cic->cfqq);
+	cic->cfqq = NULL;
+	spin_unlock_irqrestore(q->queue_lock, flags);
+	put_io_context(cic->ioc);
+}
+
 /*
- * locking hierarchy is: io_context lock -> queue locks
+ * Only the task itself ever browses or updates the cic list, so no locking
+ * is needed
  */
 static void cfq_exit_io_context(struct cfq_io_context *cic)
 {
-	struct cfq_queue *cfqq = cic->cfqq;
-	struct list_head *entry = &cic->list;
-	request_queue_t *q;
-	unsigned long flags;
+	struct cfq_io_context *__cic, *nxt;
 
 	/*
 	 * put the reference this task is holding to the various queues
 	 */
-	spin_lock_irqsave(&cic->ioc->lock, flags);
-	while ((entry = cic->list.next) != &cic->list) {
-		struct cfq_io_context *__cic;
-
-		__cic = list_entry(entry, struct cfq_io_context, list);
-		list_del(entry);
-
-		q = __cic->cfqq->cfqd->queue;
-		spin_lock(q->queue_lock);
-		cfq_put_queue(__cic->cfqq);
-		spin_unlock(q->queue_lock);
+	list_for_each_entry_safe(__cic, nxt, &cic->list, list) {
+		list_del(&__cic->list);
+		cfq_exit_single_io_context(__cic);
 	}
 
-	q = cfqq->cfqd->queue;
-	spin_lock(q->queue_lock);
-	cfq_put_queue(cfqq);
-	spin_unlock(q->queue_lock);
-
-	cic->cfqq = NULL;
-	spin_unlock_irqrestore(&cic->ioc->lock, flags);
+	cfq_exit_single_io_context(cic);
 }
 
-static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags)
+static struct cfq_io_context *
+cfq_alloc_io_context(struct cfq_data *cfqd, int gfp_flags)
 {
 	struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags);
 
 	if (cic) {
-		cic->dtor = cfq_free_io_context;
-		cic->exit = cfq_exit_io_context;
 		INIT_LIST_HEAD(&cic->list);
 		cic->cfqq = NULL;
+		cic->last_end_request = jiffies;
+		cic->ttime_total = 0;
+		cic->ttime_samples = 0;
+		cic->ttime_mean = 0;
+		cic->seek_samples = 0;
+		cic->last_request_pos = cfqd->last_sector;
+		cic->seek_total = 0;
+		cic->seek_mean = 0;
+		cic->dtor = cfq_free_io_context;
+		cic->exit = cfq_exit_io_context;
 	}
 
 	return cic;
@@ -1114,18 +1286,24 @@
 		return NULL;
 
 	if ((cic = ioc->cic) == NULL) {
-		cic = cfq_alloc_io_context(gfp_flags);
+		cic = cfq_alloc_io_context(cfqd, gfp_flags);
 
 		if (cic == NULL)
 			goto err;
 
+		/*
+		 * manually increment generic io_context usage count, it
+		 * cannot go away since we are already holding one ref to it
+		 */
 		ioc->cic = cic;
 		cic->ioc = ioc;
+		atomic_inc(&ioc->refcount);
+
 		cic->cfqq = __cfqq;
 		atomic_inc(&__cfqq->ref);
+		atomic_inc(&cfqd->ref);
 	} else {
 		struct cfq_io_context *__cic;
-		unsigned long flags;
 
 		/*
 		 * since the first cic on the list is actually the head
@@ -1140,35 +1318,37 @@
 		 * should be ok here, the list will usually not be more than
 		 * 1 or a few entries long
 		 */
-		spin_lock_irqsave(&ioc->lock, flags);
 		list_for_each_entry(__cic, &cic->list, list) {
 			/*
 			 * this process is already holding a reference to
 			 * this queue, so no need to get one more
 			 */
 			if (__cic->cfqq == __cfqq) {
+				/*
+				 * move hot entries to the front
+				 */
+				list_del(&__cic->list);
+				list_add(&__cic->list, &cic->list);
 				cic = __cic;
-				spin_unlock_irqrestore(&ioc->lock, flags);
 				goto out;
 			}
 		}
-		spin_unlock_irqrestore(&ioc->lock, flags);
 
 		/*
 		 * nope, process doesn't have a cic assoicated with this
 		 * cfqq yet. get a new one and add to list
 		 */
-		__cic = cfq_alloc_io_context(gfp_flags);
+		__cic = cfq_alloc_io_context(cfqd, gfp_flags);
 		if (__cic == NULL)
 			goto err;
 
 		__cic->ioc = ioc;
+		atomic_inc(&ioc->refcount);
+
 		__cic->cfqq = __cfqq;
 		atomic_inc(&__cfqq->ref);
-		spin_lock_irqsave(&ioc->lock, flags);
+		atomic_inc(&cfqd->ref);
 		list_add(&__cic->list, &cic->list);
-		spin_unlock_irqrestore(&ioc->lock, flags);
-
 		cic = __cic;
 		*cfqq = __cfqq;
 	}
@@ -1188,7 +1368,7 @@
 }
 
 static struct cfq_queue *
-__cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask)
+cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask)
 {
 	const int hashval = hash_long(key, CFQ_QHASH_SHIFT);
 	struct cfq_queue *cfqq, *new_cfqq = NULL;
@@ -1205,8 +1385,11 @@
 			new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask);
 			spin_lock_irq(cfqd->queue->queue_lock);
 			goto retry;
-		} else
-			goto out;
+		} else {
+			cfqq = kmem_cache_alloc(cfq_pool, gfp_mask);
+			if (!cfqq)
+				goto out;
+		}
 
 		memset(cfqq, 0, sizeof(*cfqq));
 
@@ -1222,7 +1405,13 @@
 		cfqq->cfqd = cfqd;
 		atomic_inc(&cfqd->ref);
 		cfqq->key_type = cfqd->key_type;
-		cfqq->service_start = ~0UL;
+		cfqq->service_last = 0;
+		/*
+		 * set ->slice_left to allow preemption for a new process
+		 */
+		cfqq->slice_left = 2 * cfqd->cfq_slice_idle;
+		cfqq->ioprio = task_ioprio(current);
+		cfqq->idle_window = 1;
 	}
 
 	if (new_cfqq)
@@ -1234,36 +1423,223 @@
 	return cfqq;
 }
 
-static struct cfq_queue *
-cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask)
+static void
+cfq_update_io_thinktime(struct cfq_data *cfqd, struct cfq_io_context *cic)
+{
+	unsigned long elapsed, ttime;
+
+	/*
+	 * if this context already has stuff queued, thinktime is from
+	 * last queue not last end
+	 */
+#if 0
+	if (time_after(cic->last_end_request, cic->last_queue))
+		elapsed = jiffies - cic->last_end_request;
+	else
+		elapsed = jiffies - cic->last_queue;
+#else
+		elapsed = jiffies - cic->last_end_request;
+#endif
+
+	ttime = min(elapsed, 2UL * cfqd->cfq_slice_idle);
+
+	cic->ttime_samples = (7*cic->ttime_samples + 256) / 8;
+	cic->ttime_total = (7*cic->ttime_total + 256*ttime) / 8;
+	cic->ttime_mean = (cic->ttime_total + 128) / cic->ttime_samples;
+}
+
+static void
+cfq_update_io_seektime(struct cfq_data *cfqd, struct cfq_rq *crq,
+		       struct cfq_io_context *cic)
+{
+	sector_t seek;
+	u64 total;
+
+	if (!cic->last_request_pos)
+		seek = 0;
+	else if (cic->last_request_pos < crq->request->sector)
+		seek = crq->request->sector - cic->last_request_pos;
+	else
+		seek = cic->last_request_pos - crq->request->sector;
+
+	/*
+	 * Don't allow the seek distance to get too large from the
+	 * odd fragment, pagein, etc
+	 */
+	if (cic->seek_samples <= 60) /* second&third seek */
+		seek = min(seek, (cic->seek_mean * 4) + 2*1024*1024);
+	else
+		seek = min(seek, (cic->seek_mean * 4)	+ 2*1024*64);
+
+	cic->seek_samples = (7*cic->seek_samples + 256) / 8;
+	cic->seek_total = (7*cic->seek_total + (u64)256*seek) / 8;
+	total = cic->seek_total + (cic->seek_samples/2);
+	do_div(total, cic->seek_samples);
+	cic->seek_mean = total;
+}
+
+#define sample_valid(samples)	((samples) > 80)
+
+/*
+ * Disable idle window if the process thinks too long or seeks so much that
+ * it doesn't matter
+ */
+static void
+cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq,
+		       struct cfq_io_context *cic)
+{
+	int enable_idle = 0;
+
+	if (!cic->ioc->task)
+		goto disable;
+
+	if (sample_valid(cic->seek_samples) &&
+	    cic->seek_mean > CFQ_MAX_SEEK_MEAN)
+		goto disable;
+
+	if (sample_valid(cic->ttime_samples) && 
+	    cic->ttime_mean > cfqd->cfq_slice_idle)
+		goto disable;
+
+	if (!cfqd->cfq_slice_idle)
+		goto disable;
+
+	enable_idle = 1;
+disable:
+	cfqq->idle_window = enable_idle;
+}
+
+
+/*
+ * Check if new_cfqq should preempt the currently active queue. Return 0 for
+ * no or if we aren't sure, a 1 will cause a preempt.
+ */
+static int
+cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq,
+		   struct cfq_rq *crq)
+{
+	struct cfq_queue *cfqq = cfqd->active_queue;
+
+	if (!new_cfqq->wait_request)
+		return 0;
+	if (!cfqq)
+		return 0;
+	if (new_cfqq->ioprio > cfqq->ioprio)
+		return 0;
+	if (crq->is_sync && !cfqq->doing_sync)
+		return 1;
+	if (new_cfqq->ioprio == cfqq->ioprio)
+		return 0;
+
+	/*
+	 * if it doesn't have slice left, forget it
+	 */
+	if (new_cfqq->slice_left >= 2 * cfqd->cfq_slice_idle)
+		return 1;
+
+	return 0;
+}
+
+static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+{
+	struct cfq_queue *__cfqq, *next;
+
+	list_for_each_entry_safe(__cfqq, next, &cfqd->cur_rr, cfq_list)
+		cfq_resort_rr_list(__cfqq);
+
+	if (!cfqq->slice_left)
+		cfqq->slice_left = cfq_prio_to_slice(cfqd, cfqq->ioprio, 1)/2;
+	cfqq->slice_end = cfqq->slice_left + jiffies;
+	cfq_slice_expired(cfqd, 1);
+	__cfq_set_active_queue(cfqd, cfqq);
+}
+
+/*
+ * should really be a ll_rw_blk.c helper
+ */
+static void cfq_start_queueing(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
 	request_queue_t *q = cfqd->queue;
-	struct cfq_queue *cfqq;
 
-	spin_lock_irq(q->queue_lock);
-	cfqq = __cfq_get_queue(cfqd, key, gfp_mask);
-	spin_unlock_irq(q->queue_lock);
+	if (!blk_queue_plugged(q))
+		q->request_fn(q);
+	else
+		__generic_unplug_device(q);
+}
 
-	return cfqq;
+/*
+ * Called when a new fs request (crq) is added (to cfqq). Check if there's
+ * something we should do about it
+ */
+static void
+cfq_crq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq,
+		 struct cfq_rq *crq)
+{
+
+	if (crq->is_sync) {
+		struct cfq_io_context *cic = crq->io_context;
+
+		cfq_update_io_thinktime(cfqd, cic);
+		cfq_update_io_seektime(cfqd, crq, cic);
+		cfq_update_idle_window(cfqd, cfqq, cic);
+
+		cic->last_queue = jiffies;
+	}
+
+	/*
+	 * queue must be plugged, let unplugging kick it
+	 */
+	if (!cfqd->active_queue)
+		return;
+
+	if (cfqq == cfqd->active_queue) {
+		/*
+		 * if we are waiting for a request for this queue, let it rip
+		 * immediately and flag that we must not expire this queue
+		 * just now
+		 */
+		if (cfqq->wait_request) {
+			cfqq->idle_hit++;
+			cfqq->must_dispatch = 1;
+			del_timer(&cfqd->timer);
+			cfq_start_queueing(cfqd, cfqq);
+		}
+	} else if (cfq_should_preempt(cfqd, cfqq, crq)) {
+		/*
+		 * not the active queue - expire current slice if it is
+		 * idle and has expired it's mean thinktime or this new queue
+		 * has some old slice time left and is of higher priority
+		 */
+		cfq_preempt_queue(cfqd, cfqq);
+		cfqq->must_dispatch = 1;
+		cfq_start_queueing(cfqd, cfqq);
+	}
 }
 
-static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq)
+static void cfq_enqueue(struct cfq_data *cfqd, struct request *rq)
 {
-	crq->is_sync = 0;
-	if (rq_data_dir(crq->request) == READ || current->flags & PF_SYNCWRITE)
-		crq->is_sync = 1;
+	struct cfq_rq *crq = RQ_DATA(rq);
+	struct cfq_queue *cfqq = crq->cfq_queue;
 
 	cfq_add_crq_rb(crq);
 	crq->queue_start = jiffies;
 
-	list_add_tail(&crq->request->queuelist, &crq->cfq_queue->fifo[crq->is_sync]);
+	list_add_tail(&rq->queuelist, &cfqq->fifo[crq->is_sync]);
+
+	if (rq_mergeable(rq)) {
+		cfq_add_crq_hash(cfqd, crq);
+
+		if (!cfqd->queue->last_merge)
+			cfqd->queue->last_merge = rq;
+	}
+
+	cfq_crq_enqueued(cfqd, cfqq, crq);
 }
 
 static void
 cfq_insert_request(request_queue_t *q, struct request *rq, int where)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
-	struct cfq_rq *crq = RQ_DATA(rq);
 
 	switch (where) {
 		case ELEVATOR_INSERT_BACK:
@@ -1276,26 +1652,19 @@
 			break;
 		case ELEVATOR_INSERT_SORT:
 			BUG_ON(!blk_fs_request(rq));
-			cfq_enqueue(cfqd, crq);
+			cfq_enqueue(cfqd, rq);
 			break;
 		default:
 			printk("%s: bad insert point %d\n", __FUNCTION__,where);
 			return;
 	}
-
-	if (rq_mergeable(rq)) {
-		cfq_add_crq_hash(cfqd, crq);
-
-		if (!q->last_merge)
-			q->last_merge = rq;
-	}
 }
 
 static int cfq_queue_empty(request_queue_t *q)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 
-	return list_empty(&q->queue_head) && list_empty(&cfqd->rr_list);
+	return list_empty(&q->queue_head) && !cfqd->busy_queues;
 }
 
 static void cfq_completed_request(request_queue_t *q, struct request *rq)
@@ -1344,46 +1713,30 @@
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct cfq_queue *cfqq;
-	int ret = ELV_MQUEUE_MAY;
-
-	if (current->flags & PF_MEMALLOC)
-		return ELV_MQUEUE_MAY;
 
 	cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current));
 	if (cfqq) {
-		int limit = cfqd->max_queued;
-
+		if (cfqq->must_alloc)
+			return ELV_MQUEUE_MUST;
 		if (cfqq->allocated[rw] < cfqd->cfq_queued)
 			return ELV_MQUEUE_MUST;
-
-		if (cfqd->busy_queues)
-			limit = q->nr_requests / cfqd->busy_queues;
-
-		if (limit < cfqd->cfq_queued)
-			limit = cfqd->cfq_queued;
-		else if (limit > cfqd->max_queued)
-			limit = cfqd->max_queued;
-
-		if (cfqq->allocated[rw] >= limit) {
-			if (limit > cfqq->alloc_limit[rw])
-				cfqq->alloc_limit[rw] = limit;
-
-			ret = ELV_MQUEUE_NO;
-		}
+		if (cfqq->allocated[rw] > cfqd->max_queued)
+			return ELV_MQUEUE_NO;
 	}
 
-	return ret;
+	return ELV_MQUEUE_MAY;
 }
 
 static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq)
 {
 	struct request_list *rl = &q->rq;
-	const int write = waitqueue_active(&rl->wait[WRITE]);
-	const int read = waitqueue_active(&rl->wait[READ]);
+	const int writes = waitqueue_active(&rl->wait[WRITE]);
+	const int reads = waitqueue_active(&rl->wait[READ]);
+	struct cfq_data *cfqd = q->elevator->elevator_data;
 
-	if (read && cfqq->allocated[READ] < cfqq->alloc_limit[READ])
+	if (reads && cfqq->allocated[READ] == cfqd->max_queued)
 		wake_up(&rl->wait[READ]);
-	if (write && cfqq->allocated[WRITE] < cfqq->alloc_limit[WRITE])
+	if (writes && cfqq->allocated[WRITE] == cfqd->max_queued)
 		wake_up(&rl->wait[WRITE]);
 }
 
@@ -1397,15 +1750,15 @@
 
 	if (crq) {
 		struct cfq_queue *cfqq = crq->cfq_queue;
+		const int rw = rq_data_dir(rq);
 
 		BUG_ON(q->last_merge == rq);
 		BUG_ON(!hlist_unhashed(&crq->hash));
 
-		if (crq->io_context)
-			put_io_context(crq->io_context->ioc);
+		BUG_ON(!cfqq->allocated[rw]);
+		cfqq->allocated[rw]--;
 
-		BUG_ON(!cfqq->allocated[crq->is_write]);
-		cfqq->allocated[crq->is_write]--;
+		put_io_context(crq->io_context->ioc);
 
 		mempool_free(crq, cfqd->crq_pool);
 		rq->elevator_private = NULL;
@@ -1422,31 +1775,25 @@
 static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
 {
 	struct cfq_data *cfqd = q->elevator->elevator_data;
-	struct cfq_io_context *cic;
 	const int rw = rq_data_dir(rq);
 	struct cfq_queue *cfqq, *saved_cfqq;
+	struct cfq_io_context *cic;
 	struct cfq_rq *crq;
 	unsigned long flags;
+	int must_alloc;
 
 	might_sleep_if(gfp_mask & __GFP_WAIT);
 
 	spin_lock_irqsave(q->queue_lock, flags);
 
-	cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask);
-	if (!cfqq) {
-#if 0
-		cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, gfp_mask);
-		printk("%s: got spare queue\n", current->comm);
-#else
+	cfqq = cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask);
+	if (!cfqq)
 		goto out_lock;
-#endif
-	}
 
 repeat:
-	if (cfqq->allocated[rw] >= cfqd->max_queued)
-		goto out_lock;
-
 	cfqq->allocated[rw]++;
+	must_alloc = cfqq->must_alloc;
+	cfqq->must_alloc = 0;
 	spin_unlock_irqrestore(q->queue_lock, flags);
 
 	/*
@@ -1461,6 +1808,7 @@
 	 * repeat allocation checks on queue change
 	 */
 	if (unlikely(saved_cfqq != cfqq)) {
+		put_io_context(cic->ioc);
 		spin_lock_irqsave(q->queue_lock, flags);
 		saved_cfqq->allocated[rw]--;
 		goto repeat;
@@ -1475,10 +1823,10 @@
 		crq->cfq_queue = cfqq;
 		crq->io_context = cic;
 		crq->service_start = crq->queue_start = 0;
-		crq->in_flight = crq->accounted = crq->is_sync = 0;
-		crq->is_write = rw;
+		crq->in_flight = crq->accounted = 0;
+		crq->is_sync = (rw == READ || current->flags & PF_SYNCWRITE);
+		crq->requeued = 0;
 		rq->elevator_private = crq;
-		cfqq->alloc_limit[rw] = 0;
 		return 0;
 	}
 
@@ -1486,28 +1834,78 @@
 err:
 	spin_lock_irqsave(q->queue_lock, flags);
 	cfqq->allocated[rw]--;
+	cfqq->must_alloc = must_alloc;
 	cfq_put_queue(cfqq);
 out_lock:
 	spin_unlock_irqrestore(q->queue_lock, flags);
 	return 1;
 }
 
+static void cfq_kick_queue(void *data)
+{
+	request_queue_t *q = data;
+
+	blk_run_queue(q);
+}
+
+static void cfq_schedule_timer(unsigned long data)
+{
+	struct cfq_data *cfqd = (struct cfq_data *) data;
+	struct cfq_queue *cfqq;
+	unsigned long flags;
+
+	spin_lock_irqsave(cfqd->queue->queue_lock, flags);
+
+	if ((cfqq = cfqd->active_queue) != NULL) {
+		/*
+		 * expired
+		 */
+		if (time_after(jiffies, cfqq->slice_end))
+			goto miss;
+
+		/*
+		 * only expire and reinvoke request handler, if there are
+		 * other queues with pending requests
+		 */
+		if (!cfqd->busy_queues) {
+			unsigned long slice_left = cfqq->slice_end - 1;
+	
+			cfqd->timer.expires = min(jiffies + cfqd->cfq_slice_idle, slice_left);
+			add_timer(&cfqd->timer);
+			goto out_cont;
+		}
+
+		/*
+		 * not expired and it has a request pending, let it dispatch
+		 */
+		if (!RB_EMPTY(&cfqq->sort_list)) {
+			cfqq->must_dispatch = 1;
+			goto out_cont;
+		}
+miss:
+		cfqq->idle_miss++;
+	}
+
+	cfq_slice_expired(cfqd, 0);
+out_cont:
+	if (cfqd->busy_queues)
+		kblockd_schedule_work(&cfqd->unplug_work);
+	spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
+}
+
 static void cfq_put_cfqd(struct cfq_data *cfqd)
 {
 	request_queue_t *q = cfqd->queue;
-	struct cfq_queue *cfqq;
 
 	if (!atomic_dec_and_test(&cfqd->ref))
 		return;
 
+	blk_sync_queue(q);
+
 	/*
-	 * kill spare queue, getting it means we have two refences to it.
-	 * drop both
+	 * restore ->nr_requests
 	 */
-	spin_lock_irq(q->queue_lock);
-	cfqq = __cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_ATOMIC);
-	cfq_put_queue(cfqq);
-	cfq_put_queue(cfqq);
+	q->nr_requests = BLKDEV_MAX_RQ;
 	spin_unlock_irq(q->queue_lock);
 
 	blk_put_queue(q);
@@ -1526,7 +1924,6 @@
 static int cfq_init_queue(request_queue_t *q, elevator_t *e)
 {
 	struct cfq_data *cfqd;
-	struct cfq_queue *cfqq;
 	int i;
 
 	cfqd = kmalloc(sizeof(*cfqd), GFP_KERNEL);
@@ -1534,7 +1931,11 @@
 		return -ENOMEM;
 
 	memset(cfqd, 0, sizeof(*cfqd));
-	INIT_LIST_HEAD(&cfqd->rr_list);
+
+	for (i = 0; i < CFQ_PRIO_NR; i++)
+		INIT_LIST_HEAD(&cfqd->rr_list[i]);
+
+	INIT_LIST_HEAD(&cfqd->cur_rr);
 	INIT_LIST_HEAD(&cfqd->empty_list);
 
 	cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL);
@@ -1559,22 +1960,16 @@
 	cfqd->queue = q;
 	atomic_inc(&q->refcnt);
 
-	/*
-	 * setup spare failure queue
-	 */
-	cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_KERNEL);
-	if (!cfqq)
-		goto out_spare;
-
-	/*
-	 * just set it to some high value, we want anyone to be able to queue
-	 * some requests. fairness is handled differently
-	 */
-	q->nr_requests = 1024;
-	cfqd->max_queued = q->nr_requests / 16;
+	cfqd->max_queued = q->nr_requests / 4;
 	q->nr_batching = cfq_queued;
-	cfqd->key_type = CFQ_KEY_TGID;
-	cfqd->find_best_crq = 1;
+	cfqd->key_type = CFQ_KEY_PID;
+
+	init_timer(&cfqd->timer);
+	cfqd->timer.function = cfq_schedule_timer;
+	cfqd->timer.data = (unsigned long) cfqd;
+
+	INIT_WORK(&cfqd->unplug_work, cfq_kick_queue, q);
+
 	atomic_set(&cfqd->ref, 1);
 
 	cfqd->cfq_queued = cfq_queued;
@@ -1584,10 +1979,13 @@
 	cfqd->cfq_fifo_batch_expire = cfq_fifo_rate;
 	cfqd->cfq_back_max = cfq_back_max;
 	cfqd->cfq_back_penalty = cfq_back_penalty;
+	cfqd->cfq_slice[0] = cfq_slice_async;
+	cfqd->cfq_slice[1] = cfq_slice_sync;
+	cfqd->cfq_slice_async_rq = cfq_slice_async_rq;
+	cfqd->cfq_slice_idle = cfq_slice_idle;
+	cfqd->cfq_max_depth = cfq_max_depth;
 
 	return 0;
-out_spare:
-	mempool_destroy(cfqd->crq_pool);
 out_crqpool:
 	kfree(cfqd->cfq_hash);
 out_cfqhash:
@@ -1630,7 +2028,6 @@
 	return -ENOMEM;
 }
 
-
 /*
  * sysfs parts below -->
  */
@@ -1656,13 +2053,6 @@
 }
 
 static ssize_t
-cfq_clear_elapsed(struct cfq_data *cfqd, const char *page, size_t count)
-{
-	max_elapsed_dispatch = max_elapsed_crq = 0;
-	return count;
-}
-
-static ssize_t
 cfq_set_key_type(struct cfq_data *cfqd, const char *page, size_t count)
 {
 	spin_lock_irq(cfqd->queue->queue_lock);
@@ -1670,6 +2060,8 @@
 		cfqd->key_type = CFQ_KEY_PGID;
 	else if (!strncmp(page, "tgid", 4))
 		cfqd->key_type = CFQ_KEY_TGID;
+	else if (!strncmp(page, "pid", 3))
+		cfqd->key_type = CFQ_KEY_PID;
 	else if (!strncmp(page, "uid", 3))
 		cfqd->key_type = CFQ_KEY_UID;
 	else if (!strncmp(page, "gid", 3))
@@ -1707,9 +2099,13 @@
 SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r, 1);
 SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w, 1);
 SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire, 1);
-SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq, 0);
 SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max, 0);
 SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty, 0);
+SHOW_FUNCTION(cfq_slice_idle_show, cfqd->cfq_slice_idle, 1);
+SHOW_FUNCTION(cfq_slice_sync_show, cfqd->cfq_slice[1], 1);
+SHOW_FUNCTION(cfq_slice_async_show, cfqd->cfq_slice[0], 1);
+SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0);
+SHOW_FUNCTION(cfq_max_depth_show, cfqd->cfq_max_depth, 0);
 #undef SHOW_FUNCTION
 
 #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV)			\
@@ -1732,9 +2128,13 @@
 STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX, 1);
 STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX, 1);
 STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX, 1);
-STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1, 0);
 STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX, 0);
 STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX, 0);
+STORE_FUNCTION(cfq_slice_idle_store, &cfqd->cfq_slice_idle, 0, UINT_MAX, 1);
+STORE_FUNCTION(cfq_slice_sync_store, &cfqd->cfq_slice[1], 1, UINT_MAX, 1);
+STORE_FUNCTION(cfq_slice_async_store, &cfqd->cfq_slice[0], 1, UINT_MAX, 1);
+STORE_FUNCTION(cfq_slice_async_rq_store, &cfqd->cfq_slice_async_rq, 1, UINT_MAX, 0);
+STORE_FUNCTION(cfq_max_depth_store, &cfqd->cfq_max_depth, 1, UINT_MAX, 0);
 #undef STORE_FUNCTION
 
 static struct cfq_fs_entry cfq_quantum_entry = {
@@ -1762,11 +2162,6 @@
 	.show = cfq_fifo_batch_expire_show,
 	.store = cfq_fifo_batch_expire_store,
 };
-static struct cfq_fs_entry cfq_find_best_entry = {
-	.attr = {.name = "find_best_crq", .mode = S_IRUGO | S_IWUSR },
-	.show = cfq_find_best_show,
-	.store = cfq_find_best_store,
-};
 static struct cfq_fs_entry cfq_back_max_entry = {
 	.attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR },
 	.show = cfq_back_max_show,
@@ -1777,15 +2172,36 @@
 	.show = cfq_back_penalty_show,
 	.store = cfq_back_penalty_store,
 };
-static struct cfq_fs_entry cfq_clear_elapsed_entry = {
-	.attr = {.name = "clear_elapsed", .mode = S_IWUSR },
-	.store = cfq_clear_elapsed,
+static struct cfq_fs_entry cfq_slice_sync_entry = {
+	.attr = {.name = "slice_sync", .mode = S_IRUGO | S_IWUSR },
+	.show = cfq_slice_sync_show,
+	.store = cfq_slice_sync_store,
+};
+static struct cfq_fs_entry cfq_slice_async_entry = {
+	.attr = {.name = "slice_async", .mode = S_IRUGO | S_IWUSR },
+	.show = cfq_slice_async_show,
+	.store = cfq_slice_async_store,
+};
+static struct cfq_fs_entry cfq_slice_async_rq_entry = {
+	.attr = {.name = "slice_async_rq", .mode = S_IRUGO | S_IWUSR },
+	.show = cfq_slice_async_rq_show,
+	.store = cfq_slice_async_rq_store,
+};
+static struct cfq_fs_entry cfq_slice_idle_entry = {
+	.attr = {.name = "slice_idle", .mode = S_IRUGO | S_IWUSR },
+	.show = cfq_slice_idle_show,
+	.store = cfq_slice_idle_store,
 };
 static struct cfq_fs_entry cfq_key_type_entry = {
 	.attr = {.name = "key_type", .mode = S_IRUGO | S_IWUSR },
 	.show = cfq_read_key_type,
 	.store = cfq_set_key_type,
 };
+static struct cfq_fs_entry cfq_max_depth_entry = {
+	.attr = {.name = "max_depth", .mode = S_IRUGO | S_IWUSR },
+	.show = cfq_max_depth_show,
+	.store = cfq_max_depth_store,
+};
 
 static struct attribute *default_attrs[] = {
 	&cfq_quantum_entry.attr,
@@ -1794,10 +2210,13 @@
 	&cfq_fifo_expire_w_entry.attr,
 	&cfq_fifo_batch_expire_entry.attr,
 	&cfq_key_type_entry.attr,
-	&cfq_find_best_entry.attr,
 	&cfq_back_max_entry.attr,
 	&cfq_back_penalty_entry.attr,
-	&cfq_clear_elapsed_entry.attr,
+	&cfq_slice_sync_entry.attr,
+	&cfq_slice_async_entry.attr,
+	&cfq_slice_async_rq_entry.attr,
+	&cfq_slice_idle_entry.attr,
+	&cfq_max_depth_entry.attr,
 	NULL,
 };
 
@@ -1862,25 +2281,50 @@
 	.elevator_owner =	THIS_MODULE,
 };
 
-int cfq_init(void)
+static int __init cfq_init(void)
 {
 	int ret;
 
+	/*
+	 * could be 0 on HZ < 1000 setups
+	 */
+	if (!cfq_slice_async)
+		cfq_slice_async = 1;
+	if (!cfq_slice_idle)
+		cfq_slice_idle = 1;
+
 	if (cfq_slab_setup())
 		return -ENOMEM;
 
 	ret = elv_register(&iosched_cfq);
-	if (!ret) {
-		__module_get(THIS_MODULE);
-		return 0;
-	}
+	if (ret)
+		cfq_slab_kill();
 
-	cfq_slab_kill();
 	return ret;
 }
 
 static void __exit cfq_exit(void)
 {
+	struct task_struct *g, *p;
+	unsigned long flags;
+
+	read_lock_irqsave(&tasklist_lock, flags);
+
+	/*
+	 * iterate each process in the system, removing our io_context
+	 */
+	do_each_thread(g, p) {
+		struct io_context *ioc = p->io_context;
+
+		if (ioc && ioc->cic) {
+			ioc->cic->exit(ioc->cic);
+			cfq_free_io_context(ioc->cic);
+			ioc->cic = NULL;
+		}
+	} while_each_thread(g, p);
+
+	read_unlock_irqrestore(&tasklist_lock, flags);
+
 	cfq_slab_kill();
 	elv_unregister(&iosched_cfq);
 }
diff -urN linux-2.6.10-rc3-mm1/drivers/block/ll_rw_blk.c linux-2.6.10-rc3-no1/drivers/block/ll_rw_blk.c
--- linux-2.6.10-rc3-mm1/drivers/block/ll_rw_blk.c	2004-12-16 21:11:17.100922197 -0500
+++ linux-2.6.10-rc3-no1/drivers/block/ll_rw_blk.c	2004-12-16 23:25:08.321569125 -0500
@@ -1261,11 +1261,7 @@
 	if (!blk_remove_plug(q))
 		return;
 
-	/*
-	 * was plugged, fire request_fn if queue has stuff to do
-	 */
-	if (elv_next_request(q))
-		q->request_fn(q);
+	q->request_fn(q);
 }
 EXPORT_SYMBOL(__generic_unplug_device);
 
@@ -2157,7 +2153,6 @@
 		return;
 
 	req->rq_status = RQ_INACTIVE;
-	req->q = NULL;
 	req->rl = NULL;
 
 	/*
@@ -2507,6 +2502,7 @@
 {
 	struct request_list *rl = &q->rq;
 	struct request *rq;
+	int requeued = 0;
 
 	spin_lock_irq(q->queue_lock);
 	clear_bit(QUEUE_FLAG_DRAIN, &q->queue_flags);
@@ -2515,9 +2511,13 @@
 		rq = list_entry_rq(q->drain_list.next);
 
 		list_del_init(&rq->queuelist);
-		__elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
+		elv_requeue_request(q, rq);
+		requeued++;
 	}
 
+	if (requeued)
+		q->request_fn(q);
+
 	spin_unlock_irq(q->queue_lock);
 
 	wake_up(&rl->wait[0]);
@@ -3074,6 +3074,7 @@
 	local_irq_save(flags);
 	ioc = current->io_context;
 	current->io_context = NULL;
+	ioc->task = NULL;
 	local_irq_restore(flags);
 
 	if (ioc->aic && ioc->aic->exit)
@@ -3108,12 +3109,11 @@
 	ret = kmem_cache_alloc(iocontext_cachep, gfp_flags);
 	if (ret) {
 		atomic_set(&ret->refcount, 1);
-		ret->pid = tsk->pid;
+		ret->task = current;
 		ret->last_waited = jiffies; /* doesn't matter... */
 		ret->nr_batch_requests = 0; /* because this is 0 */
 		ret->aic = NULL;
 		ret->cic = NULL;
-		spin_lock_init(&ret->lock);
 
 		local_irq_save(flags);
 
@@ -3410,3 +3410,17 @@
 		kobject_put(&disk->kobj);
 	}
 }
+
+asmlinkage int sys_ioprio_set(int ioprio)
+{
+	if (ioprio < 0 || ioprio >= IOPRIO_NR)
+		return -EINVAL;
+
+	current->ioprio = ioprio;
+	return 0;
+}
+
+asmlinkage int sys_ioprio_get(void)
+{
+	return task_ioprio(current);
+}
diff -urN linux-2.6.10-rc3-mm1/drivers/char/Kconfig.orig linux-2.6.10-rc3-no1/drivers/char/Kconfig.orig
--- linux-2.6.10-rc3-mm1/drivers/char/Kconfig.orig	2004-12-16 21:11:16.313030025 -0500
+++ linux-2.6.10-rc3-no1/drivers/char/Kconfig.orig	1969-12-31 19:00:00.000000000 -0500
@@ -1,1027 +0,0 @@
-#
-# Character device configuration
-#
-
-menu "Character devices"
-
-config VT
-	bool "Virtual terminal" if EMBEDDED
-	select INPUT
-	default y if !VIOCONS
-	---help---
-	  If you say Y here, you will get support for terminal devices with
-	  display and keyboard devices. These are called "virtual" because you
-	  can run several virtual terminals (also called virtual consoles) on
-	  one physical terminal. This is rather useful, for example one
-	  virtual terminal can collect system messages and warnings, another
-	  one can be used for a text-mode user session, and a third could run
-	  an X session, all in parallel. Switching between virtual terminals
-	  is done with certain key combinations, usually Alt-<function key>.
-
-	  The setterm command ("man setterm") can be used to change the
-	  properties (such as colors or beeping) of a virtual terminal. The
-	  man page console_codes(4) ("man console_codes") contains the special
-	  character sequences that can be used to change those properties
-	  directly. The fonts used on virtual terminals can be changed with
-	  the setfont ("man setfont") command and the key bindings are defined
-	  with the loadkeys ("man loadkeys") command.
-
-	  You need at least one virtual terminal device in order to make use
-	  of your keyboard and monitor. Therefore, only people configuring an
-	  embedded system would want to say N here in order to save some
-	  memory; the only way to log into such a system is then via a serial
-	  or network connection.
-
-	  If unsure, say Y, or else you won't be able to do much with your new
-	  shiny Linux system :-)
-
-config VT_CONSOLE
-	bool "Support for console on virtual terminal" if EMBEDDED
-	depends on VT
-	default y
-	---help---
-	  The system console is the device which receives all kernel messages
-	  and warnings and which allows logins in single user mode. If you
-	  answer Y here, a virtual terminal (the device used to interact with
-	  a physical terminal) can be used as system console. This is the most
-	  common mode of operations, so you should say Y here unless you want
-	  the kernel messages be output only to a serial port (in which case
-	  you should say Y to "Console on serial port", below).
-
-	  If you do say Y here, by default the currently visible virtual
-	  terminal (/dev/tty0) will be used as system console. You can change
-	  that with a kernel command line option such as "console=tty3" which
-	  would use the third virtual terminal as system console. (Try "man
-	  bootparam" or see the documentation of your boot loader (lilo or
-	  loadlin) about how to pass options to the kernel at boot time.)
-
-	  If unsure, say Y.
-
-config HW_CONSOLE
-	bool
-	depends on VT && !S390 && !USERMODE
-	default y
-
-config SERIAL_NONSTANDARD
-	bool "Non-standard serial port support"
-	---help---
-	  Say Y here if you have any non-standard serial boards -- boards
-	  which aren't supported using the standard "dumb" serial driver.
-	  This includes intelligent serial boards such as Cyclades,
-	  Digiboards, etc. These are usually used for systems that need many
-	  serial ports because they serve many terminals or dial-in
-	  connections.
-
-	  Note that the answer to this question won't directly affect the
-	  kernel: saying N will just cause the configurator to skip all
-	  the questions about non-standard serial boards.
-
-	  Most people can say N here.
-
-config COMPUTONE
-	tristate "Computone IntelliPort Plus serial support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	---help---
-	  This driver supports the entire family of Intelliport II/Plus
-	  controllers with the exception of the MicroChannel controllers and
-	  products previous to the Intelliport II. These are multiport cards,
-	  which give you many serial ports. You would need something like this
-	  to connect more than two modems to your Linux box, for instance in
-	  order to become a dial-in server. If you have a card like that, say
-	  Y here and read <file:Documentation/computone.txt>.
-
-	  To compile this driver as modules, choose M here: the
-	  modules will be called ip2 and ip2main.
-
-config ROCKETPORT
-	tristate "Comtrol RocketPort support"
-	depends on SERIAL_NONSTANDARD
-	help
-	  This driver supports Comtrol RocketPort and RocketModem PCI boards.   
-          These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
-          modems.  For information about the RocketPort/RocketModem  boards
-          and this driver read <file:Documentation/rocket.txt>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called rocket.
-
-	  If you want to compile this driver into the kernel, say Y here.  If
-          you don't have a Comtrol RocketPort/RocketModem card installed, say N.
-
-config CYCLADES
-	tristate "Cyclades async mux support"
-	depends on SERIAL_NONSTANDARD
-	---help---
-	  This driver supports Cyclades Z and Y multiserial boards.
-	  You would need something like this to connect more than two modems to
-	  your Linux box, for instance in order to become a dial-in server.
-
-	  For information about the Cyclades-Z card, read
-	  <file:drivers/char/README.cycladesZ>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called cyclades.
-
-	  If you haven't heard about it, it's safe to say N.
-
-config CYZ_INTR
-	bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && CYCLADES
-	help
-	  The Cyclades-Z family of multiport cards allows 2 (two) driver op
-	  modes: polling and interrupt. In polling mode, the driver will check
-	  the status of the Cyclades-Z ports every certain amount of time
-	  (which is called polling cycle and is configurable). In interrupt
-	  mode, it will use an interrupt line (IRQ) in order to check the
-	  status of the Cyclades-Z ports. The default op mode is polling. If
-	  unsure, say N.
-
-config DIGIEPCA
-	tristate "Digiboard Intelligent Async Support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	---help---
-	  This is a driver for Digi International's Xx, Xeve, and Xem series
-	  of cards which provide multiple serial ports. You would need
-	  something like this to connect more than two modems to your Linux
-	  box, for instance in order to become a dial-in server. This driver
-	  supports the original PC (ISA) boards as well as PCI, and EISA. If
-	  you have a card like this, say Y here and read the file
-	  <file:Documentation/digiepca.txt>.
-
-	  NOTE: There is another, separate driver for the Digiboard PC boards:
-	  "Digiboard PC/Xx Support" below. You should (and can) only select
-	  one of the two drivers.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called epca.
-
-config DIGI
-	tristate "Digiboard PC/Xx Support"
-	depends on SERIAL_NONSTANDARD && DIGIEPCA=n && BROKEN_ON_SMP
-	help
-	  This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards
-	  that give you many serial ports. You would need something like this
-	  to connect more than two modems to your Linux box, for instance in
-	  order to become a dial-in server. If you have a card like that, say
-	  Y here and read the file <file:Documentation/digiboard.txt>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called pcxx.
-
-config ESPSERIAL
-	tristate "Hayes ESP serial port support"
-	depends on SERIAL_NONSTANDARD && ISA && BROKEN_ON_SMP
-	help
-	  This is a driver which supports Hayes ESP serial ports.  Both single
-	  port cards and multiport cards are supported.  Make sure to read
-	  <file:Documentation/hayes-esp.txt>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called esp.
-
-	  If unsure, say N.
-
-config MOXA_INTELLIO
-	tristate "Moxa Intellio support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	help
-	  Say Y here if you have a Moxa Intellio multiport serial card.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called moxa.
-
-config MOXA_SMARTIO
-	tristate "Moxa SmartIO support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	help
-	  Say Y here if you have a Moxa SmartIO multiport serial card.
-
-	  This driver can also be built as a module ( = code which can be
-	  inserted in and removed from the running kernel whenever you want).
-	  The module will be called mxser. If you want to do that, say M
-	  here.
-
-config ISI
-	tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
-	depends on SERIAL_NONSTANDARD && PCI && EXPERIMENTAL && BROKEN_ON_SMP && m
-	help
-	  This is a driver for the Multi-Tech cards which provide several
-	  serial ports.  The driver is experimental and can currently only be
-	  built as a module. The module will be called isicom.
-	  If you want to do that, choose M here.
-
-config SYNCLINK
-	tristate "Microgate SyncLink card support"
-	depends on SERIAL_NONSTANDARD && PCI
-	help
-	  Provides support for the SyncLink ISA and PCI multiprotocol serial
-	  adapters. These adapters support asynchronous and HDLC bit
-	  synchronous communication up to 10Mbps (PCI adapter).
-
-	  This driver can only be built as a module ( = code which can be
-	  inserted in and removed from the running kernel whenever you want).
-	  The module will be called synclink.  If you want to do that, say M
-	  here.
-
-config SYNCLINKMP
-	tristate "SyncLink Multiport support"
-	depends on SERIAL_NONSTANDARD
-	help
-	  Enable support for the SyncLink Multiport (2 or 4 ports)
-	  serial adapter, running asynchronous and HDLC communications up
-	  to 2.048Mbps. Each ports is independently selectable for
-	  RS-232, V.35, RS-449, RS-530, and X.21
-
-	  This driver may be built as a module ( = code which can be
-	  inserted in and removed from the running kernel whenever you want).
-	  The module will be called synclinkmp.  If you want to do that, say M
-	  here.
-
-config N_HDLC
-	tristate "HDLC line discipline support"
-	depends on SERIAL_NONSTANDARD
-	help
-	  Allows synchronous HDLC communications with tty device drivers that
-	  support synchronous HDLC such as the Microgate SyncLink adapter.
-
-	  This driver can only be built as a module ( = code which can be
-	  inserted in and removed from the running kernel whenever you want).
-	  The module will be called n_hdlc. If you want to do that, say M
-	  here.
-
-config RISCOM8
-	tristate "SDL RISCom/8 card support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	help
-	  This is a driver for the SDL Communications RISCom/8 multiport card,
-	  which gives you many serial ports. You would need something like
-	  this to connect more than two modems to your Linux box, for instance
-	  in order to become a dial-in server. If you have a card like that,
-	  say Y here and read the file <file:Documentation/riscom8.txt>.
-
-	  Also it's possible to say M here and compile this driver as kernel
-	  loadable module; the module will be called riscom8.
-
-config SPECIALIX
-	tristate "Specialix IO8+ card support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	help
-	  This is a driver for the Specialix IO8+ multiport card (both the
-	  ISA and the PCI version) which gives you many serial ports. You
-	  would need something like this to connect more than two modems to
-	  your Linux box, for instance in order to become a dial-in server.
-
-	  If you have a card like that, say Y here and read the file
-	  <file:Documentation/specialix.txt>. Also it's possible to say M here
-	  and compile this driver as kernel loadable module which will be
-	  called specialix.
-
-config SPECIALIX_RTSCTS
-	bool "Specialix DTR/RTS pin is RTS"
-	depends on SPECIALIX
-	help
-	  The Specialix IO8+ card can only support either RTS or DTR. If you
-	  say N here, the driver will use the pin as "DTR" when the tty is in
-	  software handshake mode.  If you say Y here or hardware handshake is
-	  on, it will always be RTS.  Read the file
-	  <file:Documentation/specialix.txt> for more information.
-
-config SX
-	tristate "Specialix SX (and SI) card support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	help
-	  This is a driver for the SX and SI multiport serial cards.
-	  Please read the file <file:Documentation/sx.txt> for details.
-
-	  This driver can only be built as a module ( = code which can be
-	  inserted in and removed from the running kernel whenever you want).
-	  The module will be called sx. If you want to do that, say M here.
-
-config RIO
-	tristate "Specialix RIO system support"
-	depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP
-	help
-	  This is a driver for the Specialix RIO, a smart serial card which
-	  drives an outboard box that can support up to 128 ports.  Product
-	  information is at <http://www.perle.com/support/documentation.html#multiport>.
-	  There are both ISA and PCI versions.
-
-config RIO_OLDPCI
-	bool "Support really old RIO/PCI cards"
-	depends on RIO
-	help
-	  Older RIO PCI cards need some initialization-time configuration to
-	  determine the IRQ and some control addresses.  If you have a RIO and
-	  this doesn't seem to work, try setting this to Y.
-
-config STALDRV
-	bool "Stallion multiport serial support"
-	depends on SERIAL_NONSTANDARD
-	help
-	  Stallion cards give you many serial ports.  You would need something
-	  like this to connect more than two modems to your Linux box, for
-	  instance in order to become a dial-in server.  If you say Y here,
-	  you will be asked for your specific card model in the next
-	  questions.  Make sure to read <file:Documentation/stallion.txt> in
-	  this case.  If you have never heard about all this, it's safe to
-	  say N.
-
-config STALLION
-	tristate "Stallion EasyIO or EC8/32 support"
-	depends on STALDRV && BROKEN_ON_SMP
-	help
-	  If you have an EasyIO or EasyConnection 8/32 multiport Stallion
-	  card, then this is for you; say Y.  Make sure to read
-	  <file:Documentation/stallion.txt>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called stallion.
-
-config ISTALLION
-	tristate "Stallion EC8/64, ONboard, Brumby support"
-	depends on STALDRV && BROKEN_ON_SMP
-	help
-	  If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
-	  serial multiport card, say Y here. Make sure to read
-	  <file:Documentation/stallion.txt>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called istallion.
-
-config SERIAL_TX3912
-	bool "TX3912/PR31700 serial port support"
-	depends on SERIAL_NONSTANDARD && MIPS && BROKEN_ON_SMP
-	help
-	  The TX3912 is a Toshiba RISC processor based o the MIPS 3900 core;
-	  see <http://www.toshiba.com/taec/components/Generic/risc/tx3912.htm>.
-	  Say Y here to enable kernel support for the on-board serial port.
-
-config SERIAL_TX3912_CONSOLE
-	bool "Console on TX3912/PR31700 serial port"
-	depends on SERIAL_TX3912
-	help
-	  The TX3912 is a Toshiba RISC processor based o the MIPS 3900 core;
-	  see <http://www.toshiba.com/taec/components/Generic/risc/tx3912.htm>.
-	  Say Y here to direct console I/O to the on-board serial port.
-
-config AU1000_UART
-	bool "Enable Au1000 UART Support"
-	depends on SERIAL_NONSTANDARD && MIPS
-	help
-	  If you have an Alchemy AU1000 processor (MIPS based) and you want
-	  to use serial ports, say Y.  Otherwise, say N.
-
-config AU1000_SERIAL_CONSOLE
-	bool "Enable Au1000 serial console"
-	depends on AU1000_UART
-	help
-	  If you have an Alchemy AU1000 processor (MIPS based) and you want
-	  to use a console on a serial port, say Y.  Otherwise, say N.
-
-config QTRONIX_KEYBOARD
-	bool "Enable Qtronix 990P Keyboard Support"
-	depends on IT8712
-	help
-	  Images of Qtronix keyboards are at
-	  <http://www.qtronix.com/keyboard.html>.
-
-config IT8172_CIR
-	bool
-	depends on QTRONIX_KEYBOARD
-	default y
-
-config IT8172_SCR0
-	bool "Enable Smart Card Reader 0 Support "
-	depends on IT8712
-	help
-	  Say Y here to support smart-card reader 0 (SCR0) on the Integrated
-	  Technology Express, Inc. ITE8172 SBC.  Vendor page at
-	  <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the
-	  board at <http://www.mvista.com/partners/semiconductor/ite.html>.
-
-config IT8172_SCR1
-	bool "Enable Smart Card Reader 1 Support "
-	depends on IT8712
-	help
-	  Say Y here to support smart-card reader 1 (SCR1) on the Integrated
-	  Technology Express, Inc. ITE8172 SBC.  Vendor page at
-	  <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the
-	  board at <http://www.mvista.com/partners/semiconductor/ite.html>.
-
-config A2232
-	tristate "Commodore A2232 serial support (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP
-	---help---
-	  This option supports the 2232 7-port serial card shipped with the
-	  Amiga 2000 and other Zorro-bus machines, dating from 1989.  At
-	  a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip
-	  each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The
-	  ports were connected with 8 pin DIN connectors on the card bracket,
-	  for which 8 pin to DB25 adapters were supplied. The card also had
-	  jumpers internally to toggle various pinning configurations.
-
-	  This driver can be built as a module; but then "generic_serial"
-	  will also be built as a module. This has to be loaded before
-	  "ser_a2232". If you want to do this, answer M here.
-
-config SGI_SNSC
-	bool "SGI Altix system controller communication support"
-	depends on (IA64_SGI_SN2 || IA64_GENERIC)
-	help
-	  If you have an SGI Altix and you want to enable system
-	  controller communication from user space (you want this!),
-	  say Y.  Otherwise, say N.
-
-source "drivers/serial/Kconfig"
-
-config UNIX98_PTYS
-	bool "Unix98 PTY support" if EMBEDDED
-	default y
-	---help---
-	  A pseudo terminal (PTY) is a software device consisting of two
-	  halves: a master and a slave. The slave device behaves identical to
-	  a physical terminal; the master device is used by a process to
-	  read data from and write data to the slave, thereby emulating a
-	  terminal. Typical programs for the master side are telnet servers
-	  and xterms.
-
-	  Linux has traditionally used the BSD-like names /dev/ptyxx for
-	  masters and /dev/ttyxx for slaves of pseudo terminals. This scheme
-	  has a number of problems. The GNU C library glibc 2.1 and later,
-	  however, supports the Unix98 naming standard: in order to acquire a
-	  pseudo terminal, a process opens /dev/ptmx; the number of the pseudo
-	  terminal is then made available to the process and the pseudo
-	  terminal slave can be accessed as /dev/pts/<number>. What was
-	  traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
-
-	  All modern Linux systems use the Unix98 ptys.  Say Y unless
-	  you're on an embedded system and want to conserve memory.
-
-config LEGACY_PTYS
-	bool "Legacy (BSD) PTY support"
-	default y
-	---help---
-	  A pseudo terminal (PTY) is a software device consisting of two
-	  halves: a master and a slave. The slave device behaves identical to
-	  a physical terminal; the master device is used by a process to
-	  read data from and write data to the slave, thereby emulating a
-	  terminal. Typical programs for the master side are telnet servers
-	  and xterms.
-
-	  Linux has traditionally used the BSD-like names /dev/ptyxx
-	  for masters and /dev/ttyxx for slaves of pseudo
-	  terminals. This scheme has a number of problems, including
-	  security.  This option enables these legacy devices; on most
-	  systems, it is safe to say N.
-
-
-config LEGACY_PTY_COUNT
-	int "Maximum number of legacy PTY in use"
-	depends on LEGACY_PTYS
-	range 1 256
-	default "256"
-	---help---
-	  The maximum number of legacy PTYs that can be used at any one time.
-	  The default is 256, and should be more than enough.  Embedded
-	  systems may want to reduce this to save memory.
-
-	  When not in use, each legacy PTY occupies 12 bytes on 32-bit
-	  architectures and 24 bytes on 64-bit architectures.
-
-config PRINTER
-	tristate "Parallel printer support"
-	depends on PARPORT
-	---help---
-	  If you intend to attach a printer to the parallel port of your Linux
-	  box (as opposed to using a serial printer; if the connector at the
-	  printer has 9 or 25 holes ["female"], then it's serial), say Y.
-	  Also read the Printing-HOWTO, available from
-	  <http://www.tldp.org/docs.html#howto>.
-
-	  It is possible to share one parallel port among several devices
-	  (e.g. printer and ZIP drive) and it is safe to compile the
-	  corresponding drivers into the kernel.
-
-	  To compile this driver as a module, choose M here and read
-	  <file:Documentation/parport.txt>.  The module will be called lp.
-
-	  If you have several parallel ports, you can specify which ports to
-	  use with the "lp" kernel command line option.  (Try "man bootparam"
-	  or see the documentation of your boot loader (lilo or loadlin) about
-	  how to pass options to the kernel at boot time.)  The syntax of the
-	  "lp" command line option can be found in <file:drivers/char/lp.c>.
-
-	  If you have more than 8 printers, you need to increase the LP_NO
-	  macro in lp.c and the PARPORT_MAX macro in parport.h.
-
-config LP_CONSOLE
-	bool "Support for console on line printer"
-	depends on PRINTER
-	---help---
-	  If you want kernel messages to be printed out as they occur, you
-	  can have a console on the printer. This option adds support for
-	  doing that; to actually get it to happen you need to pass the
-	  option "console=lp0" to the kernel at boot time.
-
-	  If the printer is out of paper (or off, or unplugged, or too
-	  busy..) the kernel will stall until the printer is ready again.
-	  By defining CONSOLE_LP_STRICT to 0 (at your own risk) you
-	  can make the kernel continue when this happens,
-	  but it'll lose the kernel messages.
-
-	  If unsure, say N.
-
-config PPDEV
-	tristate "Support for user-space parallel port device drivers"
-	depends on PARPORT
-	---help---
-	  Saying Y to this adds support for /dev/parport device nodes.  This
-	  is needed for programs that want portable access to the parallel
-	  port, for instance deviceid (which displays Plug-and-Play device
-	  IDs).
-
-	  This is the parallel port equivalent of SCSI generic support (sg).
-	  It is safe to say N to this -- it is not needed for normal printing
-	  or parallel port CD-ROM/disk support.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called ppdev.
-
-	  If unsure, say N.
-
-config TIPAR
-	tristate "Texas Instruments parallel link cable support"
-	depends on PARPORT
-	---help---
-	  If you own a Texas Instruments graphing calculator and use a
-	  parallel link cable, then you might be interested in this driver.
-
-	  If you enable this driver, you will be able to communicate with
-	  your calculator through a set of device nodes under /dev. The
-	  main advantage of this driver is that you don't have to be root
-	  to use this precise link cable (depending on the permissions on
-	  the device nodes, though).
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called tipar.
-
-	  If you don't know what a parallel link cable is or what a Texas
-	  Instruments graphing calculator is, then you probably don't need this
-	  driver.
-
-	  If unsure, say N.
-
-config HVC_CONSOLE
-	bool "pSeries Hypervisor Virtual Console support"
-	depends on PPC_PSERIES
-	help
-	  pSeries machines when partitioned support a hypervisor virtual
-	  console. This driver allows each pSeries partition to have a console
-	  which is accessed via the HMC.
-
-config HVCS
-	tristate "IBM Hypervisor Virtual Console Server support"
-	depends on PPC_PSERIES
-	help
-	  Partitionable IBM Power5 ppc64 machines allow hosting of
-	  firmware virtual consoles from one Linux partition by
-	  another Linux partition.  This driver allows console data
-	  from Linux partitions to be accessed through TTY device
-	  interfaces in the device tree of a Linux partition running
-	  this driver.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called hvcs.ko.  Additionally, this module
-	  will depend on arch specific APIs exported from hvcserver.ko
-	  which will also be compiled when this driver is built as a
-	  module.
-
-source "drivers/char/ipmi/Kconfig"
-
-source "drivers/char/watchdog/Kconfig"
-
-config DS1620
-	tristate "NetWinder thermometer support"
-	depends on ARCH_NETWINDER
-	help
-	  Say Y here to include support for the thermal management hardware
-	  found in the NetWinder. This driver allows the user to control the
-	  temperature set points and to read the current temperature.
-
-	  It is also possible to say M here to build it as a module (ds1620)
-	  It is recommended to be used on a NetWinder, but it is not a
-	  necessity.
-
-config NWBUTTON
-	tristate "NetWinder Button"
-	depends on ARCH_NETWINDER
-	---help---
-	  If you say Y here and create a character device node /dev/nwbutton
-	  with major and minor numbers 10 and 158 ("man mknod"), then every
-	  time the orange button is pressed a number of times, the number of
-	  times the button was pressed will be written to that device.
-
-	  This is most useful for applications, as yet unwritten, which
-	  perform actions based on how many times the button is pressed in a
-	  row.
-
-	  Do not hold the button down for too long, as the driver does not
-	  alter the behaviour of the hardware reset circuitry attached to the
-	  button; it will still execute a hard reset if the button is held
-	  down for longer than approximately five seconds.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called nwbutton.
-
-	  Most people will answer Y to this question and "Reboot Using Button"
-	  below to be able to initiate a system shutdown from the button.
-
-config NWBUTTON_REBOOT
-	bool "Reboot Using Button"
-	depends on NWBUTTON
-	help
-	  If you say Y here, then you will be able to initiate a system
-	  shutdown and reboot by pressing the orange button a number of times.
-	  The number of presses to initiate the shutdown is two by default,
-	  but this can be altered by modifying the value of NUM_PRESSES_REBOOT
-	  in nwbutton.h and recompiling the driver or, if you compile the
-	  driver as a module, you can specify the number of presses at load
-	  time with "insmod button reboot_count=<something>".
-
-config NWFLASH
-	tristate "NetWinder flash support"
-	depends on ARCH_NETWINDER
-	---help---
-	  If you say Y here and create a character device /dev/flash with
-	  major 10 and minor 160 you can manipulate the flash ROM containing
-	  the NetWinder firmware. Be careful as accidentally overwriting the
-	  flash contents can render your computer unbootable. On no account
-	  allow random users access to this device. :-)
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called nwflash.
-
-	  If you're not sure, say N.
-
-config HW_RANDOM
-	tristate "Intel/AMD/VIA HW Random Number Generator support"
-	depends on (X86 || IA64) && PCI
-	---help---
-	  This driver provides kernel-side support for the Random Number
-	  Generator hardware found on Intel i8xx-based motherboards,
-	  AMD 76x-based motherboards, and Via Nehemiah CPUs.
-
-	  Provides a character driver, used to read() entropy data.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called hw_random.
-
-	  If unsure, say N.
-
-config NVRAM
-	tristate "/dev/nvram support"
-	depends on ATARI || X86 || X86_64 || ARM || GENERIC_NVRAM
-	---help---
-	  If you say Y here and create a character special file /dev/nvram
-	  with major number 10 and minor number 144 using mknod ("man mknod"),
-	  you get read and write access to the extra bytes of non-volatile
-	  memory in the real time clock (RTC), which is contained in every PC
-	  and most Ataris.  The actual number of bytes varies, depending on the
-	  nvram in the system, but is usually 114 (128-14 for the RTC).
-
-	  This memory is conventionally called "CMOS RAM" on PCs and "NVRAM"
-	  on Ataris. /dev/nvram may be used to view settings there, or to
-	  change them (with some utility). It could also be used to frequently
-	  save a few bits of very important data that may not be lost over
-	  power-off and for which writing to disk is too insecure. Note
-	  however that most NVRAM space in a PC belongs to the BIOS and you
-	  should NEVER idly tamper with it. See Ralf Brown's interrupt list
-	  for a guide to the use of CMOS bytes by your BIOS.
-
-	  On Atari machines, /dev/nvram is always configured and does not need
-	  to be selected.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called nvram.
-
-config RTC
-	tristate "Enhanced Real Time Clock Support"
-	depends on !PPC32 && !PARISC && !IA64 && !M68K
-	---help---
-	  If you say Y here and create a character special file /dev/rtc with
-	  major number 10 and minor number 135 using mknod ("man mknod"), you
-	  will get access to the real time clock (or hardware clock) built
-	  into your computer.
-
-	  Every PC has such a clock built in. It can be used to generate
-	  signals from as low as 1Hz up to 8192Hz, and can also be used
-	  as a 24 hour alarm. It reports status information via the file
-	  /proc/driver/rtc and its behaviour is set by various ioctls on
-	  /dev/rtc.
-
-	  If you run Linux on a multiprocessor machine and said Y to
-	  "Symmetric Multi Processing" above, you should say Y here to read
-	  and set the RTC in an SMP compatible fashion.
-
-	  If you think you have a use for such a device (such as periodic data
-	  sampling), then say Y here, and read <file:Documentation/rtc.txt>
-	  for details.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called rtc.
-
-config RTC_HISTOGRAM
-	tristate "Real Time Clock Histogram Support"
-	default y
-	depends on RTC
-	---help---
-	  If you say Y here then the kernel will track the delivery and
-	  wakeup latency of /dev/rtc using tasks and will report a
-	  histogram to the kernel log when the application closes /dev/rtc.
-
-config BLOCKER
-	tristate "Priority Inheritance Debugging (Blocker) Device Support"
-	default y
-	---help---
-	  If you say Y here then a device will be created that the userspace
-	  pi_test suite uses to test and measure kernel locking primitives.
-
-config SGI_DS1286
-	tristate "SGI DS1286 RTC support"
-	depends on SGI_IP22
-	help
-	  If you say Y here and create a character special file /dev/rtc with
-	  major number 10 and minor number 135 using mknod ("man mknod"), you
-	  will get access to the real time clock built into your computer.
-	  Every SGI has such a clock built in. It reports status information
-	  via the file /proc/rtc and its behaviour is set by various ioctls on
-	  /dev/rtc.
-
-config SGI_IP27_RTC
-	bool "SGI M48T35 RTC support"
-	depends on SGI_IP27
-	help
-	  If you say Y here and create a character special file /dev/rtc with
-	  major number 10 and minor number 135 using mknod ("man mknod"), you
-	  will get access to the real time clock built into your computer.
-	  Every SGI has such a clock built in. It reports status information
-	  via the file /proc/rtc and its behaviour is set by various ioctls on
-	  /dev/rtc.
-
-config GEN_RTC
-	tristate "Generic /dev/rtc emulation"
-	depends on RTC!=y && !IA64 && !ARM
-	---help---
-	  If you say Y here and create a character special file /dev/rtc with
-	  major number 10 and minor number 135 using mknod ("man mknod"), you
-	  will get access to the real time clock (or hardware clock) built
-	  into your computer.
-
-	  It reports status information via the file /proc/driver/rtc and its
-	  behaviour is set by various ioctls on /dev/rtc. If you enable the
-	  "extended RTC operation" below it will also provide an emulation
-	  for RTC_UIE which is required by some programs and may improve
-	  precision in some cases.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called genrtc.
-
-config GEN_RTC_X
-	bool "Extended RTC operation"
-	depends on GEN_RTC
-	help
-	  Provides an emulation for RTC_UIE which is required by some programs
-	  and may improve precision of the generic RTC support in some cases.
-
-config EFI_RTC
-	bool "EFI Real Time Clock Services"
-	depends on IA64
-
-config DS1302
-	tristate "DS1302 RTC support"
-	depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT)
-	help
-	  If you say Y here and create a character special file /dev/rtc with
-	  major number 121 and minor number 0 using mknod ("man mknod"), you
-	  will get access to the real time clock (or hardware clock) built
-	  into your computer.
-
-config S3C2410_RTC
-	bool "S3C2410 RTC Driver"
-	depends on ARCH_S3C2410
-	help
-	  RTC (Realtime Clock) driver for the clock inbuilt into the
-	  Samsung S3C2410. This can provide periodic interrupt rates
-	  from 1Hz to 64Hz for user programs, and wakeup from Alarm.
-
-config COBALT_LCD
-	bool "Support for Cobalt LCD"
-	depends on MIPS_COBALT
-	help
-	  This option enables support for the LCD display and buttons found
-	  on Cobalt systems through a misc device.
-
-config DTLK
-	tristate "Double Talk PC internal speech card support"
-	help
-	  This driver is for the DoubleTalk PC, a speech synthesizer
-	  manufactured by RC Systems (<http://www.rcsys.com/>).  It is also
-	  called the `internal DoubleTalk'.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called dtlk.
-
-config R3964
-	tristate "Siemens R3964 line discipline"
-	---help---
-	  This driver allows synchronous communication with devices using the
-	  Siemens R3964 packet protocol. Unless you are dealing with special
-	  hardware like PLCs, you are unlikely to need this.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called n_r3964.
-
-	  If unsure, say N.
-
-config APPLICOM
-	tristate "Applicom intelligent fieldbus card support"
-	depends on PCI
-	---help---
-	  This driver provides the kernel-side support for the intelligent
-	  fieldbus cards made by Applicom International. More information
-	  about these cards can be found on the WWW at the address
-	  <http://www.applicom-int.com/>, or by email from David Woodhouse
-	  <dwmw2@infradead.org>.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called applicom.
-
-	  If unsure, say N.
-
-config SONYPI
-	tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)"
-	depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT
-	---help---
-	  This driver enables access to the Sony Programmable I/O Control
-	  Device which can be found in many (all ?) Sony Vaio laptops.
-
-	  If you have one of those laptops, read
-	  <file:Documentation/sonypi.txt>, and say Y or M here.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called sonypi.
-
-
-menu "Ftape, the floppy tape device driver"
-
-config FTAPE
-	tristate "Ftape (QIC-80/Travan) support"
-	depends on BROKEN_ON_SMP && (ALPHA || X86)
-	---help---
-	  If you have a tape drive that is connected to your floppy
-	  controller, say Y here.
-
-	  Some tape drives (like the Seagate "Tape Store 3200" or the Iomega
-	  "Ditto 3200" or the Exabyte "Eagle TR-3") come with a "high speed"
-	  controller of their own. These drives (and their companion
-	  controllers) are also supported if you say Y here.
-
-	  If you have a special controller (such as the CMS FC-10, FC-20,
-	  Mountain Mach-II, or any controller that is based on the Intel 82078
-	  FDC like the high speed controllers by Seagate and Exabyte and
-	  Iomega's "Ditto Dash") you must configure it by selecting the
-	  appropriate entries from the "Floppy tape controllers" sub-menu
-	  below and possibly modify the default values for the IRQ and DMA
-	  channel and the IO base in ftape's configuration menu.
-
-	  If you want to use your floppy tape drive on a PCI-bus based system,
-	  please read the file <file:drivers/char/ftape/README.PCI>.
-
-	  The ftape kernel driver is also available as a runtime loadable
-	  module. To compile this driver as a module, choose M here: the
-	  module will be called ftape.
-
-	  Note that the Ftape-HOWTO is out of date (sorry) and documents the
-	  older version 2.08 of this software but still contains useful
-	  information.  There is a web page with more recent documentation at
-	  <http://www.instmath.rwth-aachen.de/~heine/ftape/>.  This page
-	  always contains the latest release of the ftape driver and useful
-	  information (backup software, ftape related patches and
-	  documentation, FAQ).  Note that the file system interface has
-	  changed quite a bit compared to previous versions of ftape.  Please
-	  read <file:Documentation/ftape.txt>.
-
-source "drivers/char/ftape/Kconfig"
-
-endmenu
-
-source "drivers/char/agp/Kconfig"
-
-source "drivers/char/drm/Kconfig"
-
-source "drivers/char/pcmcia/Kconfig"
-
-config MWAVE
-	tristate "ACP Modem (Mwave) support"
-	depends on X86
-	select SERIAL_8250
-	---help---
-	  The ACP modem (Mwave) for Linux is a WinModem. It is composed of a
-	  kernel driver and a user level application. Together these components
-	  support direct attachment to public switched telephone networks (PSTNs)
-	  and support selected world wide countries.
-
-	  This version of the ACP Modem driver supports the IBM Thinkpad 600E,
-	  600, and 770 that include on board ACP modem hardware.
-
-	  The modem also supports the standard communications port interface
-	  (ttySx) and is compatible with the Hayes AT Command Set.
-
-	  The user level application needed to use this driver can be found at
-	  the IBM Linux Technology Center (LTC) web site:
-	  <http://www.ibm.com/linux/ltc/>.
-
-	  If you own one of the above IBM Thinkpads which has the Mwave chipset
-	  in it, say Y.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called mwave.
-
-config SCx200_GPIO
-	tristate "NatSemi SCx200 GPIO Support"
-	depends on SCx200
-	help
-	  Give userspace access to the GPIO pins on the National
-	  Semiconductor SCx200 processors.
-
-	  If compiled as a module, it will be called scx200_gpio.
-
-config RAW_DRIVER
-	tristate "RAW driver (/dev/raw/rawN) (OBSOLETE)"
-	help
-	  The raw driver permits block devices to be bound to /dev/raw/rawN. 
-	  Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. 
-	  See the raw(8) manpage for more details.
-
-          The raw driver is deprecated and may be removed from 2.7
-          kernels.  Applications should simply open the device (eg /dev/hda1)
-          with the O_DIRECT flag.
-
-config HPET
-	bool "HPET - High Precision Event Timer" if (X86 || IA64)
-	default n
-	depends on ACPI
-	help
-	  If you say Y here, you will have a miscdevice named "/dev/hpet/".  Each
-	  open selects one of the timers supported by the HPET.  The timers are
-	  non-periodioc and/or periodic.
-
-config HPET_RTC_IRQ
-	bool "HPET Control RTC IRQ" if !HPET_EMULATE_RTC
-	default n
-	depends on HPET
-	help
-	  If you say Y here, you will disable RTC_IRQ in drivers/char/rtc.c. It
-	  is assumed the platform called hpet_alloc with the RTC IRQ values for
-	  the HPET timers.
-
-config HPET_MMAP
-	bool "Allow mmap of HPET"
-	default y
-	depends on HPET
-	help
-	  If you say Y here, user applications will be able to mmap
-	  the HPET registers.
-
-	  In some hardware implementations, the page containing HPET
-	  registers may also contain other things that shouldn't be
-	  exposed to the user.  If this applies to your hardware,
-	  say N here.
-
-config MAX_RAW_DEVS
-	int "Maximum number of RAW devices to support (1-8192)"
-	depends on RAW_DRIVER
-	default "256"
-	help
-	  The maximum number of RAW devices that are supported.
-	  Default is 256. Increase this number in case you need lots of
-	  raw devices.
-
-config HANGCHECK_TIMER
-	tristate "Hangcheck timer"
-	depends on X86_64 || X86
-	help
-	  The hangcheck-timer module detects when the system has gone
-	  out to lunch past a certain margin.  It can reboot the system
-	  or merely print a warning.
-
-config MMTIMER
-	tristate "MMTIMER Memory mapped RTC for SGI Altix"
-	depends on IA64_GENERIC || IA64_SGI_SN2
-	default y
-	help
-	  The mmtimer device allows direct userspace access to the
-	  Altix system timer.
-
-endmenu
-
diff -urN linux-2.6.10-rc3-mm1/include/asm-i386/unistd.h linux-2.6.10-rc3-no1/include/asm-i386/unistd.h
--- linux-2.6.10-rc3-mm1/include/asm-i386/unistd.h	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/include/asm-i386/unistd.h	2004-12-16 23:25:08.323568842 -0500
@@ -299,8 +299,10 @@
 #define __NR_vperfctr_unlink	(__NR_vperfctr_open+2)
 #define __NR_vperfctr_iresume	(__NR_vperfctr_open+3)
 #define __NR_vperfctr_read	(__NR_vperfctr_open+4)
+#define __NR_ioprio_set		294
+#define __NR_ioprio_get		295
 
-#define NR_syscalls 294
+#define NR_syscalls 296
 
 /*
  * user-visible error numbers are in the range -1 - -128: see
diff -urN linux-2.6.10-rc3-mm1/include/asm-ia64/unistd.h linux-2.6.10-rc3-no1/include/asm-ia64/unistd.h
--- linux-2.6.10-rc3-mm1/include/asm-ia64/unistd.h	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/include/asm-ia64/unistd.h	2004-12-16 23:25:08.325568558 -0500
@@ -263,6 +263,8 @@
 #define __NR_add_key			1271
 #define __NR_request_key		1272
 #define __NR_keyctl			1273
+#define __NR_ioprio_set			1274
+#define __NR_ioprio_get			1275
 
 #ifdef __KERNEL__
 
diff -urN linux-2.6.10-rc3-mm1/include/asm-ppc/unistd.h linux-2.6.10-rc3-no1/include/asm-ppc/unistd.h
--- linux-2.6.10-rc3-mm1/include/asm-ppc/unistd.h	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/include/asm-ppc/unistd.h	2004-12-16 23:25:08.326568416 -0500
@@ -281,8 +281,10 @@
 #define __NR_vperfctr_unlink	(__NR_vperfctr_open+2)
 #define __NR_vperfctr_iresume	(__NR_vperfctr_open+3)
 #define __NR_vperfctr_read	(__NR_vperfctr_open+4)
+#define __NR_ioprio_set		277
+#define __NR_ioprio_get		278
 
-#define __NR_syscalls		277
+#define __NR_syscalls		279
 
 #define __NR(n)	#n
 
diff -urN linux-2.6.10-rc3-mm1/include/asm-x86_64/unistd.h linux-2.6.10-rc3-no1/include/asm-x86_64/unistd.h
--- linux-2.6.10-rc3-mm1/include/asm-x86_64/unistd.h	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/include/asm-x86_64/unistd.h	2004-12-16 23:25:08.386559903 -0500
@@ -566,8 +566,12 @@
 __SYSCALL(__NR_vperfctr_iresume, sys_vperfctr_iresume)
 #define __NR_vperfctr_read	(__NR_vperfctr_open+4)
 __SYSCALL(__NR_vperfctr_read, sys_vperfctr_read)
+#define __NR_ioprio_set		254
+__SYSCALL(__NR_ioprio_set, sys_ioprio_set)
+#define __NR_ioprio_get		255
+__SYSCALL(__NR_ioprio_get, sys_ioprio_get)
 
-#define __NR_syscall_max __NR_vperfctr_read
+#define __NR_syscall_max __NR_ioprio_get
 #ifndef __NO_STUBS
 
 /* user-visible error numbers are in the range -1 - -4095 */
diff -urN linux-2.6.10-rc3-mm1/include/linux/blkdev.h linux-2.6.10-rc3-no1/include/linux/blkdev.h
--- linux-2.6.10-rc3-mm1/include/linux/blkdev.h	2004-12-14 20:33:25.000000000 -0500
+++ linux-2.6.10-rc3-no1/include/linux/blkdev.h	2004-12-16 23:25:08.388559619 -0500
@@ -54,16 +54,27 @@
 
 struct cfq_queue;
 struct cfq_io_context {
-	void (*dtor)(struct cfq_io_context *);
-	void (*exit)(struct cfq_io_context *);
-
-	struct io_context *ioc;
-
 	/*
 	 * circular list of cfq_io_contexts belonging to a process io context
 	 */
 	struct list_head list;
 	struct cfq_queue *cfqq;
+
+	struct io_context *ioc;
+
+	unsigned long last_end_request;
+	unsigned long last_queue;
+	unsigned long ttime_total;
+	unsigned long ttime_samples;
+	unsigned long ttime_mean;
+
+	unsigned int seek_samples;
+	sector_t last_request_pos;
+	u64 seek_total;
+	sector_t seek_mean;
+
+	void (*dtor)(struct cfq_io_context *);
+	void (*exit)(struct cfq_io_context *);
 };
 
 /*
@@ -73,7 +84,7 @@
  */
 struct io_context {
 	atomic_t refcount;
-	pid_t pid;
+	struct task_struct *task;
 
 	/*
 	 * For request batching
@@ -81,8 +92,6 @@
 	unsigned long last_waited; /* Time last woken after wait for request */
 	int nr_batch_requests;     /* Number of requests left in the batch */
 
-	spinlock_t lock;
-
 	struct as_io_context *aic;
 	struct cfq_io_context *cic;
 };
@@ -723,5 +732,24 @@
 #define MODULE_ALIAS_BLOCKDEV_MAJOR(major) \
 	MODULE_ALIAS("block-major-" __stringify(major) "-*")
 
+/*
+ * io prio stuff
+ */
+#define IOPRIO_NR	(8)
+
+asmlinkage int sys_ioprio_set(int);
+asmlinkage int sys_ioprio_get(void);
+
+/*
+ * if process has set io priority explicitly, use that. if not, convert
+ * the cpu scheduler nice value to an io priority
+ */
+static inline int task_ioprio(struct task_struct *task)
+{
+	if (task->ioprio != -1)
+		return task->ioprio;
+	else
+		return (task_nice(task) + 20) / 5;
+}
 
 #endif
diff -urN linux-2.6.10-rc3-mm1/include/linux/init_task.h linux-2.6.10-rc3-no1/include/linux/init_task.h
--- linux-2.6.10-rc3-mm1/include/linux/init_task.h	2004-12-16 21:11:18.058791106 -0500
+++ linux-2.6.10-rc3-no1/include/linux/init_task.h	2004-12-16 23:25:08.393558909 -0500
@@ -116,6 +116,7 @@
 	.pi_waiters	= LIST_HEAD_INIT(tsk.pi_waiters),		\
 	.private_pages	= LIST_HEAD_INIT(tsk.private_pages),		\
 	.private_pages_count = 0,					\
+	.ioprio		= -1,						\
 }
 
 #endif
diff -urN linux-2.6.10-rc3-mm1/include/linux/sched.h linux-2.6.10-rc3-no1/include/linux/sched.h
--- linux-2.6.10-rc3-mm1/include/linux/sched.h	2004-12-16 21:11:18.417741981 -0500
+++ linux-2.6.10-rc3-no1/include/linux/sched.h	2004-12-16 23:29:48.803805303 -0500
@@ -666,6 +666,8 @@
 	struct list_head run_list;
 	prio_array_t *array;
 
+	int ioprio;
+
 	unsigned long sleep_avg;
 	unsigned long long timestamp, last_ran;
 	int activated;
@@ -919,6 +921,7 @@
 extern int task_nice(const task_t *p);
 extern int task_curr(const task_t *p);
 extern int idle_cpu(int cpu);
+extern unsigned long task_will_schedule_at(const task_t *p);
 extern void mutex_setprio(task_t *p, int prio);
 extern int mutex_getprio(task_t *p);
 
diff -urN linux-2.6.10-rc3-mm1/kernel/fork.c linux-2.6.10-rc3-no1/kernel/fork.c
--- linux-2.6.10-rc3-mm1/kernel/fork.c	2004-12-16 21:11:14.846230765 -0500
+++ linux-2.6.10-rc3-no1/kernel/fork.c	2004-12-16 23:25:08.410556497 -0500
@@ -1012,6 +1012,11 @@
 	INIT_LIST_HEAD(&p->ptrace_children);
 	INIT_LIST_HEAD(&p->ptrace_list);
 
+	/*
+	 * inherit ioprio
+	 */
+	p->ioprio = current->ioprio;
+
 	/* Need tasklist lock for parent etc handling! */
 	write_lock_irq(&tasklist_lock);
 
diff -urN linux-2.6.10-rc3-mm1/kernel/sched.c linux-2.6.10-rc3-no1/kernel/sched.c
--- linux-2.6.10-rc3-mm1/kernel/sched.c	2004-12-16 21:11:14.831232818 -0500
+++ linux-2.6.10-rc3-no1/kernel/sched.c	2004-12-16 23:25:08.435552950 -0500
@@ -886,6 +886,41 @@
 	return cpu_curr(task_cpu(p)) == p;
 }
 
+/**
+ * task_will_schedule_at - in how many ticks will the task run, most likely
+ * @p: the task in question
+ */
+unsigned long task_will_schedule_at(const task_t *p)
+{
+	/*
+	 * Task is executing right now
+	 */
+	if (task_curr(p))
+		return 0;
+
+	/*
+	 * Task is not executing but on a runqueue - try to guess
+	 * how much time it will take for it to run again, but using
+	 * the current task's remaining ticks. This is not accurate,
+	 * but a good guess. (We use the min() to avoid the small race
+	 * that is due to us dereferencing the current task without
+	 * locking)
+	 */
+	if (p->array)
+		return min(cpu_curr(task_cpu(p))->time_slice,
+					(unsigned int)MAX_SLEEP_AVG);
+
+	/*
+	 * for blocked tasks, return half of the average sleep time.
+	 * (because this is the average sleep-time we'll see if we
+	 * sample the period randomly.)
+	 */
+	return NS_TO_JIFFIES(p->sleep_avg) / 2;
+}
+
+EXPORT_SYMBOL_GPL(task_will_schedule_at);
+
+
 #ifdef CONFIG_SMP
 enum request_type {
 	REQ_MOVE_TASK,
