aboutsummaryrefslogtreecommitdiffstats
path: root/packages/linux
diff options
context:
space:
mode:
authorFelix Domke <tmbinc@elitedvb.ne>2008-03-12 12:55:21 +0000
committerFelix Domke <tmbinc@elitedvb.ne>2008-03-12 12:55:21 +0000
commit5cc251c9fec58868a897f2dd3b2fe651f7ce2d40 (patch)
treea7efa3fb801f1a58d175adedda189aad75fbf746 /packages/linux
parente42d44ef47be658e6ea1f4c05a5b2e0668a83d57 (diff)
downloadopenembedded-5cc251c9fec58868a897f2dd3b2fe651f7ce2d40.tar.gz
linux-dm800: add missing patches
Diffstat (limited to 'packages/linux')
-rw-r--r--packages/linux/linux-dm800/linux-2.6.12-add-ioprio.patch4148
-rw-r--r--packages/linux/linux-dm800/linux-2.6.12-dream-misc.patch87
-rw-r--r--packages/linux/linux-dm800/linux-2.6.12-fix-serial.patch32
-rw-r--r--packages/linux/linux-dm800/linuxmips-2.6.12-fix-fadvise.patch29
-rw-r--r--packages/linux/linux-dm800/linuxmips-2.6.12-fix-futex.patch368
-rw-r--r--packages/linux/linux-dm800/linuxmips-2.6.12-gcc4-compile-fix.patch91
-rw-r--r--packages/linux/linux-dm800/linuxmips-2.6.12-gdb-fix.patch22
7 files changed, 4777 insertions, 0 deletions
diff --git a/packages/linux/linux-dm800/linux-2.6.12-add-ioprio.patch b/packages/linux/linux-dm800/linux-2.6.12-add-ioprio.patch
new file mode 100644
index 0000000000..ef35dd3fe9
--- /dev/null
+++ b/packages/linux/linux-dm800/linux-2.6.12-add-ioprio.patch
@@ -0,0 +1,4148 @@
+diff -Naur 2.6.12-5.0-org/Documentation/block/ioprio.txt 2.6.12-5.0-patched/Documentation/block/ioprio.txt
+--- 2.6.12-5.0-org/Documentation/block/ioprio.txt 1970-01-01 01:00:00.000000000 +0100
++++ 2.6.12-5.0-patched/Documentation/block/ioprio.txt 2007-12-11 12:34:52.000000000 +0100
+@@ -0,0 +1,179 @@
++Block io priorities
++===================
++
++
++Intro
++-----
++
++With the introduction of cfq v3 (aka cfq-ts or time sliced cfq), basic io
++priorities is supported for reads on files. This enables users to io nice
++processes or process groups, similar to what has been possible to cpu
++scheduling for ages. This document mainly details the current possibilites
++with cfq, other io schedulers do not support io priorities so far.
++
++Scheduling classes
++------------------
++
++CFQ implements three generic scheduling classes that determine how io is
++served for a process.
++
++IOPRIO_CLASS_RT: This is the realtime io class. This scheduling class is given
++higher priority than any other in the system, processes from this class are
++given first access to the disk every time. Thus it needs to be used with some
++care, one io RT process can starve the entire system. Within the RT class,
++there are 8 levels of class data that determine exactly how much time this
++process needs the disk for on each service. In the future this might change
++to be more directly mappable to performance, by passing in a wanted data
++rate instead.
++
++IOPRIO_CLASS_BE: This is the best-effort scheduling class, which is the default
++for any process that hasn't set a specific io priority. The class data
++determines how much io bandwidth the process will get, it's directly mappable
++to the cpu nice levels just more coarsely implemented. 0 is the highest
++BE prio level, 7 is the lowest. The mapping between cpu nice level and io
++nice level is determined as: io_nice = (cpu_nice + 20) / 5.
++
++IOPRIO_CLASS_IDLE: This is the idle scheduling class, processes running at this
++level only get io time when no one else needs the disk. The idle class has no
++class data, since it doesn't really apply here.
++
++Tools
++-----
++
++See below for a sample ionice tool. Usage:
++
++# ionice -c<class> -n<level> -p<pid>
++
++If pid isn't given, the current process is assumed. IO priority settings
++are inherited on fork, so you can use ionice to start the process at a given
++level:
++
++# ionice -c2 -n0 /bin/ls
++
++will run ls at the best-effort scheduling class at the highest priority.
++For a running process, you can give the pid instead:
++
++# ionice -c1 -n2 -p100
++
++will change pid 100 to run at the realtime scheduling class, at priority 2.
++
++---> snip ionice.c tool <---
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <getopt.h>
++#include <unistd.h>
++#include <sys/ptrace.h>
++#include <asm/unistd.h>
++
++extern int sys_ioprio_set(int, int, int);
++extern int sys_ioprio_get(int, int);
++
++#if defined(__i386__)
++#define __NR_ioprio_set 289
++#define __NR_ioprio_get 290
++#elif defined(__ppc__)
++#define __NR_ioprio_set 273
++#define __NR_ioprio_get 274
++#elif defined(__x86_64__)
++#define __NR_ioprio_set 251
++#define __NR_ioprio_get 252
++#elif defined(__ia64__)
++#define __NR_ioprio_set 1274
++#define __NR_ioprio_get 1275
++#elif defined(__mips__)
++#define __NR_ioprio_set 4284
++#define __NR_ioprio_get 4285
++#else
++#error "Unsupported arch"
++#endif
++
++_syscall3(int, ioprio_set, int, which, int, who, int, ioprio);
++_syscall2(int, ioprio_get, int, which, int, who);
++
++enum {
++ IOPRIO_CLASS_NONE,
++ IOPRIO_CLASS_RT,
++ IOPRIO_CLASS_BE,
++ IOPRIO_CLASS_IDLE,
++};
++
++enum {
++ IOPRIO_WHO_PROCESS = 1,
++ IOPRIO_WHO_PGRP,
++ IOPRIO_WHO_USER,
++};
++
++#define IOPRIO_CLASS_SHIFT 13
++
++const char *to_prio[] = { "none", "realtime", "best-effort", "idle", };
++
++int main(int argc, char *argv[])
++{
++ int ioprio = 4, set = 0, ioprio_class = IOPRIO_CLASS_BE;
++ int c, pid = 0;
++
++ while ((c = getopt(argc, argv, "+n:c:p:")) != EOF) {
++ switch (c) {
++ case 'n':
++ ioprio = strtol(optarg, NULL, 10);
++ set = 1;
++ break;
++ case 'c':
++ ioprio_class = strtol(optarg, NULL, 10);
++ set = 1;
++ break;
++ case 'p':
++ pid = strtol(optarg, NULL, 10);
++ break;
++ }
++ }
++
++ switch (ioprio_class) {
++ case IOPRIO_CLASS_NONE:
++ ioprio_class = IOPRIO_CLASS_BE;
++ break;
++ case IOPRIO_CLASS_RT:
++ case IOPRIO_CLASS_BE:
++ break;
++ case IOPRIO_CLASS_IDLE:
++ ioprio = 7;
++ break;
++ default:
++ printf("bad prio class %d\n", ioprio_class);
++ return 1;
++ }
++
++ if (!set) {
++ if (!pid && argv[optind])
++ pid = strtol(argv[optind], NULL, 10);
++
++ ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
++
++ printf("pid=%d, %d\n", pid, ioprio);
++
++ if (ioprio == -1)
++ perror("ioprio_get");
++ else {
++ ioprio_class = ioprio >> IOPRIO_CLASS_SHIFT;
++ ioprio = ioprio & 0xff;
++ printf("%s: prio %d\n", to_prio[ioprio_class], ioprio);
++ }
++ } else {
++ if (ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio | ioprio_class << IOPRIO_CLASS_SHIFT) == -1) {
++ perror("ioprio_set");
++ return 1;
++ }
++
++ if (argv[optind])
++ execvp(argv[optind], &argv[optind]);
++ }
++
++ return 0;
++}
++
++---> snip ionice.c tool <---
++
++
++March 11 2005, Jens Axboe <axboe@suse.de>
+diff -Naur 2.6.12-5.0-org/drivers/block/as-iosched.c 2.6.12-5.0-patched/drivers/block/as-iosched.c
+--- 2.6.12-5.0-org/drivers/block/as-iosched.c 2007-07-26 00:53:20.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/block/as-iosched.c 2007-12-11 12:34:52.000000000 +0100
+@@ -1806,7 +1806,8 @@
+ rq->elevator_private = NULL;
+ }
+
+-static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
++static int as_set_request(request_queue_t *q, struct request *rq,
++ struct bio *bio, int gfp_mask)
+ {
+ struct as_data *ad = q->elevator->elevator_data;
+ struct as_rq *arq = mempool_alloc(ad->arq_pool, gfp_mask);
+@@ -1827,7 +1828,7 @@
+ return 1;
+ }
+
+-static int as_may_queue(request_queue_t *q, int rw)
++static int as_may_queue(request_queue_t *q, int rw, struct bio *bio)
+ {
+ int ret = ELV_MQUEUE_MAY;
+ struct as_data *ad = q->elevator->elevator_data;
+diff -Naur 2.6.12-5.0-org/drivers/block/cfq-iosched.c 2.6.12-5.0-patched/drivers/block/cfq-iosched.c
+--- 2.6.12-5.0-org/drivers/block/cfq-iosched.c 2007-07-26 00:53:20.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/block/cfq-iosched.c 2007-12-11 12:34:52.000000000 +0100
+@@ -21,22 +21,34 @@
+ #include <linux/hash.h>
+ #include <linux/rbtree.h>
+ #include <linux/mempool.h>
+-
+-static unsigned long max_elapsed_crq;
+-static unsigned long max_elapsed_dispatch;
++#include <linux/ioprio.h>
++#include <linux/writeback.h>
+
+ /*
+ * 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_fifo_expire[2] = { HZ / 4, HZ / 8 };
+ 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 / 10;
++static int cfq_slice_async = HZ / 25;
++static int cfq_slice_async_rq = 2;
++static int cfq_slice_idle = HZ / 100;
++
++#define CFQ_IDLE_GRACE (HZ / 10)
++#define CFQ_SLICE_SCALE (5)
++
++#define CFQ_KEY_ASYNC (0)
++#define CFQ_KEY_ANY (0xffff)
++
++/*
++ * disable queueing at the driver/hardware level
++ */
++static int cfq_max_depth = 2;
++
+ /*
+ * for the hash of cfqq inside the cfqd
+ */
+@@ -55,6 +67,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
+
+@@ -75,78 +88,110 @@
+ #define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node)
+ #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_UID,
+- CFQ_KEY_GID,
+- CFQ_KEY_LAST,
+-};
+-
+-static char *cfq_key_types[] = { "pgid", "tgid", "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_LISTS IOPRIO_BE_NR
++#define cfq_class_idle(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_IDLE)
++#define cfq_class_be(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_BE)
++#define cfq_class_rt(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_RT)
++
++#define ASYNC (0)
++#define SYNC (1)
++
++#define cfq_cfqq_dispatched(cfqq) \
++ ((cfqq)->on_dispatch[ASYNC] + (cfqq)->on_dispatch[SYNC])
++
++#define cfq_cfqq_class_sync(cfqq) ((cfqq)->key != CFQ_KEY_ASYNC)
++
++#define cfq_cfqq_sync(cfqq) \
++ (cfq_cfqq_class_sync(cfqq) || (cfqq)->on_dispatch[SYNC])
++
++/*
++ * Per block device queue structure
++ */
+ struct cfq_data {
+- struct list_head rr_list;
++ atomic_t ref;
++ request_queue_t *queue;
++
++ /*
++ * rr list of queues with requests and the count of them
++ */
++ struct list_head rr_list[CFQ_PRIO_LISTS];
++ struct list_head busy_rr;
++ struct list_head cur_rr;
++ struct list_head idle_rr;
++ unsigned int busy_queues;
++
++ /*
++ * non-ordered list of empty cfqq's
++ */
+ struct list_head empty_list;
+
++ /*
++ * cfqq lookup hash
++ */
+ struct hlist_head *cfq_hash;
+- struct hlist_head *crq_hash;
+
+- /* queues on rr_list (ie they have pending requests */
+- unsigned int busy_queues;
++ /*
++ * global crq hash for all queues
++ */
++ struct hlist_head *crq_hash;
+
+ unsigned int max_queued;
+
+- atomic_t ref;
++ mempool_t *crq_pool;
+
+- int key_type;
++ int rq_in_driver;
+
+- mempool_t *crq_pool;
++ /*
++ * schedule slice state info
++ */
++ /*
++ * idle window management
++ */
++ struct timer_list idle_slice_timer;
++ struct work_struct unplug_work;
+
+- request_queue_t *queue;
++ struct cfq_queue *active_queue;
++ struct cfq_io_context *active_cic;
++ int cur_prio, cur_end_prio;
++ unsigned int dispatch_slice;
++
++ struct timer_list idle_class_timer;
+
+ sector_t last_sector;
++ unsigned long last_end_request;
+
+- int rq_in_driver;
++ unsigned int rq_starved;
+
+ /*
+ * tunables, see top of file
+ */
+ unsigned int cfq_quantum;
+ unsigned int cfq_queued;
+- unsigned int cfq_fifo_expire_r;
+- unsigned int cfq_fifo_expire_w;
+- unsigned int cfq_fifo_batch_expire;
++ unsigned int cfq_fifo_expire[2];
+ 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;
+ };
+
++/*
++ * Per process-grouping structure
++ */
+ struct cfq_queue {
+ /* reference count */
+ atomic_t ref;
+ /* parent cfq_data */
+ struct cfq_data *cfqd;
+- /* hash of mergeable requests */
++ /* cfqq lookup hash */
+ struct hlist_node cfq_hash;
+ /* hash key */
+- unsigned long key;
+- /* whether queue is on rr (or empty) list */
+- int on_rr;
++ unsigned int key;
+ /* on either rr or empty list of cfqd */
+ struct list_head cfq_list;
+ /* sorted list of pending requests */
+@@ -158,21 +203,22 @@
+ /* currently allocated requests */
+ int allocated[2];
+ /* fifo list of requests in sort_list */
+- struct list_head fifo[2];
+- /* last time fifo expired */
+- unsigned long last_fifo_expire;
++ struct list_head fifo;
+
+- int key_type;
++ unsigned long slice_start;
++ unsigned long slice_end;
++ unsigned long slice_left;
++ unsigned long service_last;
++
++ /* number of requests that are on the dispatch list */
++ int on_dispatch[2];
++
++ /* io prio of this group */
++ unsigned short ioprio, org_ioprio;
++ unsigned short ioprio_class, org_ioprio_class;
+
+- unsigned long service_start;
+- unsigned long service_used;
+-
+- unsigned int max_rate;
+-
+- /* number of requests that have been handed to the driver */
+- int in_flight;
+- /* number of currently allocated requests */
+- int alloc_limit[2];
++ /* various state flags, see below */
++ unsigned int flags;
+ };
+
+ struct cfq_rq {
+@@ -184,42 +230,78 @@
+ struct cfq_queue *cfq_queue;
+ struct cfq_io_context *io_context;
+
+- unsigned long service_start;
+- unsigned long queue_start;
++ unsigned int crq_flags;
++};
++
++enum cfqq_state_flags {
++ CFQ_CFQQ_FLAG_on_rr = 0,
++ CFQ_CFQQ_FLAG_wait_request,
++ CFQ_CFQQ_FLAG_must_alloc,
++ CFQ_CFQQ_FLAG_must_alloc_slice,
++ CFQ_CFQQ_FLAG_must_dispatch,
++ CFQ_CFQQ_FLAG_fifo_expire,
++ CFQ_CFQQ_FLAG_idle_window,
++ CFQ_CFQQ_FLAG_prio_changed,
++ CFQ_CFQQ_FLAG_expired,
++};
++
++#define CFQ_CFQQ_FNS(name) \
++static inline void cfq_mark_cfqq_##name(struct cfq_queue *cfqq) \
++{ \
++ cfqq->flags |= (1 << CFQ_CFQQ_FLAG_##name); \
++} \
++static inline void cfq_clear_cfqq_##name(struct cfq_queue *cfqq) \
++{ \
++ cfqq->flags &= ~(1 << CFQ_CFQQ_FLAG_##name); \
++} \
++static inline int cfq_cfqq_##name(const struct cfq_queue *cfqq) \
++{ \
++ return (cfqq->flags & (1 << CFQ_CFQQ_FLAG_##name)) != 0; \
++}
+
+- unsigned int in_flight : 1;
+- unsigned int accounted : 1;
+- unsigned int is_sync : 1;
+- unsigned int is_write : 1;
++CFQ_CFQQ_FNS(on_rr);
++CFQ_CFQQ_FNS(wait_request);
++CFQ_CFQQ_FNS(must_alloc);
++CFQ_CFQQ_FNS(must_alloc_slice);
++CFQ_CFQQ_FNS(must_dispatch);
++CFQ_CFQQ_FNS(fifo_expire);
++CFQ_CFQQ_FNS(idle_window);
++CFQ_CFQQ_FNS(prio_changed);
++CFQ_CFQQ_FNS(expired);
++#undef CFQ_CFQQ_FNS
++
++enum cfq_rq_state_flags {
++ CFQ_CRQ_FLAG_in_flight = 0,
++ CFQ_CRQ_FLAG_in_driver,
++ CFQ_CRQ_FLAG_is_sync,
++ CFQ_CRQ_FLAG_requeued,
+ };
+
+-static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long);
++#define CFQ_CRQ_FNS(name) \
++static inline void cfq_mark_crq_##name(struct cfq_rq *crq) \
++{ \
++ crq->crq_flags |= (1 << CFQ_CRQ_FLAG_##name); \
++} \
++static inline void cfq_clear_crq_##name(struct cfq_rq *crq) \
++{ \
++ crq->crq_flags &= ~(1 << CFQ_CRQ_FLAG_##name); \
++} \
++static inline int cfq_crq_##name(const struct cfq_rq *crq) \
++{ \
++ return (crq->crq_flags & (1 << CFQ_CRQ_FLAG_##name)) != 0; \
++}
++
++CFQ_CRQ_FNS(in_flight);
++CFQ_CRQ_FNS(in_driver);
++CFQ_CRQ_FNS(is_sync);
++CFQ_CRQ_FNS(requeued);
++#undef CFQ_CRQ_FNS
++
++static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short);
+ 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);
+
+-/*
+- * what the fairness is based on (ie how processes are grouped and
+- * differentiated)
+- */
+-static inline unsigned long
+-cfq_hash_key(struct cfq_data *cfqd, struct task_struct *tsk)
+-{
+- /*
+- * optimize this so that ->key_type is the offset into the struct
+- */
+- switch (cfqd->key_type) {
+- case CFQ_KEY_PGID:
+- return process_group(tsk);
+- default:
+- case CFQ_KEY_TGID:
+- return tsk->tgid;
+- case CFQ_KEY_UID:
+- return tsk->uid;
+- case CFQ_KEY_GID:
+- return tsk->gid;
+- }
+-}
++#define process_sync(tsk) ((tsk)->flags & PF_SYNCWRITE)
+
+ /*
+ * lots of deadline iosched dupes, can be abstracted later...
+@@ -235,16 +317,12 @@
+
+ if (q->last_merge == crq->request)
+ q->last_merge = NULL;
+-
+- cfq_update_next_crq(crq);
+ }
+
+ static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq)
+ {
+ const int hash_idx = CFQ_MHASH_FN(rq_hash_key(crq->request));
+
+- BUG_ON(!hlist_unhashed(&crq->hash));
+-
+ hlist_add_head(&crq->hash, &cfqd->crq_hash[hash_idx]);
+ }
+
+@@ -257,8 +335,6 @@
+ struct cfq_rq *crq = list_entry_hash(entry);
+ struct request *__rq = crq->request;
+
+- BUG_ON(hlist_unhashed(&crq->hash));
+-
+ if (!rq_mergeable(__rq)) {
+ cfq_del_crq_hash(crq);
+ continue;
+@@ -271,6 +347,28 @@
+ return NULL;
+ }
+
++static inline int cfq_pending_requests(struct cfq_data *cfqd)
++{
++ return !list_empty(&cfqd->queue->queue_head) || cfqd->busy_queues;
++}
++
++/*
++ * scheduler run of queue, if there are requests pending and no one in the
++ * driver that will restart queueing
++ */
++static inline void cfq_schedule_dispatch(struct cfq_data *cfqd)
++{
++ if (!cfqd->rq_in_driver && cfq_pending_requests(cfqd))
++ kblockd_schedule_work(&cfqd->unplug_work);
++}
++
++static int cfq_queue_empty(request_queue_t *q)
++{
++ struct cfq_data *cfqd = q->elevator->elevator_data;
++
++ return !cfq_pending_requests(cfqd);
++}
++
+ /*
+ * Lifted from AS - choose which of crq1 and crq2 that is best served now.
+ * We choose the request that is closest to the head right now. Distance
+@@ -288,35 +386,21 @@
+ if (crq2 == NULL)
+ return crq1;
+
++ if (cfq_crq_requeued(crq1) && !cfq_crq_requeued(crq2))
++ return crq1;
++ else if (cfq_crq_requeued(crq2) && !cfq_crq_requeued(crq1))
++ return crq2;
++
++ if (cfq_crq_is_sync(crq1) && !cfq_crq_is_sync(crq2))
++ return crq1;
++ else if (cfq_crq_is_sync(crq2) && !cfq_crq_is_sync(crq1))
++ return crq2;
++
+ s1 = crq1->request->sector;
+ s2 = crq2->request->sector;
+
+ last = cfqd->last_sector;
+
+-#if 0
+- if (!list_empty(&cfqd->queue->queue_head)) {
+- struct list_head *entry = &cfqd->queue->queue_head;
+- unsigned long distance = ~0UL;
+- struct request *rq;
+-
+- while ((entry = entry->prev) != &cfqd->queue->queue_head) {
+- rq = list_entry_rq(entry);
+-
+- if (blk_barrier_rq(rq))
+- break;
+-
+- if (distance < abs(s1 - rq->sector + rq->nr_sectors)) {
+- distance = abs(s1 - rq->sector +rq->nr_sectors);
+- last = rq->sector + rq->nr_sectors;
+- }
+- if (distance < abs(s2 - rq->sector + rq->nr_sectors)) {
+- distance = abs(s2 - rq->sector +rq->nr_sectors);
+- last = rq->sector + rq->nr_sectors;
+- }
+- }
+- }
+-#endif
+-
+ /*
+ * by definition, 1KiB is 2 sectors
+ */
+@@ -377,11 +461,14 @@
+ struct cfq_rq *crq_next = NULL, *crq_prev = NULL;
+ struct rb_node *rbnext, *rbprev;
+
+- if (!ON_RB(&last->rb_node))
+- return NULL;
+-
+- if ((rbnext = rb_next(&last->rb_node)) == NULL)
++ rbnext = NULL;
++ if (ON_RB(&last->rb_node))
++ rbnext = rb_next(&last->rb_node);
++ if (!rbnext) {
+ rbnext = rb_first(&cfqq->sort_list);
++ if (rbnext == &last->rb_node)
++ rbnext = NULL;
++ }
+
+ rbprev = rb_prev(&last->rb_node);
+
+@@ -401,67 +488,53 @@
+ cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq);
+ }
+
+-static int cfq_check_sort_rr_list(struct cfq_queue *cfqq)
++static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted)
+ {
+- 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);
++ struct cfq_data *cfqd = cfqq->cfqd;
++ struct list_head *list, *entry;
+
+- if (cfqq->service_used > cnext->service_used)
+- return 1;
+- }
++ BUG_ON(!cfq_cfqq_on_rr(cfqq));
+
+- prev = cfqq->cfq_list.prev;
+- if (prev != head) {
+- struct cfq_queue *cprev = list_entry_cfqq(prev);
++ list_del(&cfqq->cfq_list);
+
+- if (cfqq->service_used < cprev->service_used)
+- return 1;
++ if (cfq_class_rt(cfqq))
++ list = &cfqd->cur_rr;
++ else if (cfq_class_idle(cfqq))
++ list = &cfqd->idle_rr;
++ else {
++ /*
++ * if cfqq has requests in flight, don't allow it to be
++ * found in cfq_set_active_queue before it has finished them.
++ * this is done to increase fairness between a process that
++ * has lots of io pending vs one that only generates one
++ * sporadically or synchronously
++ */
++ if (cfq_cfqq_dispatched(cfqq))
++ list = &cfqd->busy_rr;
++ else
++ list = &cfqd->rr_list[cfqq->ioprio];
+ }
+
+- return 0;
+-}
+-
+-static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue)
+-{
+- struct list_head *entry = &cfqq->cfqd->rr_list;
+-
+- if (!cfqq->on_rr)
+- return;
+- if (!new_queue && !cfq_check_sort_rr_list(cfqq))
++ /*
++ * if queue was preempted, just add to front to be fair. busy_rr
++ * isn't sorted.
++ */
++ if (preempted || list == &cfqd->busy_rr) {
++ list_add(&cfqq->cfq_list, list);
+ return;
+-
+- 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) {
++ entry = 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);
+@@ -469,28 +542,24 @@
+
+ /*
+ * add to busy list of queues for service, trying to be fair in ordering
+- * the pending list according to requests serviced
++ * the pending list according to last request service
+ */
+ 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;
++ BUG_ON(cfq_cfqq_on_rr(cfqq));
++ cfq_mark_cfqq_on_rr(cfqq);
+ cfqd->busy_queues++;
+
+- if (time_after(jiffies, cfqq->service_start + cfq_service))
+- cfqq->service_used >>= 3;
+-
+- cfq_sort_rr_list(cfqq, 1);
++ cfq_resort_rr_list(cfqq, requeue);
+ }
+
+ static inline void
+ cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+ {
++ BUG_ON(!cfq_cfqq_on_rr(cfqq));
++ cfq_clear_cfqq_on_rr(cfqq);
+ list_move(&cfqq->cfq_list, &cfqd->empty_list);
+- cfqq->on_rr = 0;
+
+ BUG_ON(!cfqd->busy_queues);
+ cfqd->busy_queues--;
+@@ -505,16 +574,17 @@
+
+ if (ON_RB(&crq->rb_node)) {
+ struct cfq_data *cfqd = cfqq->cfqd;
++ const int sync = cfq_crq_is_sync(crq);
+
+- BUG_ON(!cfqq->queued[crq->is_sync]);
++ BUG_ON(!cfqq->queued[sync]);
++ cfqq->queued[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);
+
+- if (RB_EMPTY(&cfqq->sort_list) && cfqq->on_rr)
++ if (cfq_cfqq_on_rr(cfqq) && RB_EMPTY(&cfqq->sort_list))
+ cfq_del_cfqq_rr(cfqd, cfqq);
+ }
+ }
+@@ -550,7 +620,7 @@
+ struct cfq_rq *__alias;
+
+ crq->rb_key = rq_rb_key(rq);
+- cfqq->queued[crq->is_sync]++;
++ cfqq->queued[cfq_crq_is_sync(crq)]++;
+
+ /*
+ * looks a little odd, but the first insert might return an alias.
+@@ -561,8 +631,8 @@
+
+ rb_insert_color(&crq->rb_node, &cfqq->sort_list);
+
+- if (!cfqq->on_rr)
+- cfq_add_cfqq_rr(cfqd, cfqq);
++ if (!cfq_cfqq_on_rr(cfqq))
++ cfq_add_cfqq_rr(cfqd, cfqq, cfq_crq_requeued(crq));
+
+ /*
+ * check if this request is a better next-serve candidate
+@@ -575,17 +645,16 @@
+ {
+ if (ON_RB(&crq->rb_node)) {
+ rb_erase(&crq->rb_node, &cfqq->sort_list);
+- cfqq->queued[crq->is_sync]--;
++ cfqq->queued[cfq_crq_is_sync(crq)]--;
+ }
+
+ cfq_add_crq_rb(crq);
+ }
+
+-static struct request *
+-cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector)
++static struct request *cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector)
++
+ {
+- const unsigned long key = cfq_hash_key(cfqd, current);
+- struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, key);
++ struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->pid, CFQ_KEY_ANY);
+ struct rb_node *n;
+
+ if (!cfqq)
+@@ -609,20 +678,25 @@
+
+ static void cfq_deactivate_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 (cfq_crq_in_driver(crq)) {
++ cfq_clear_crq_in_driver(crq);
++ WARN_ON(!cfqd->rq_in_driver);
++ cfqd->rq_in_driver--;
+ }
++ if (cfq_crq_in_flight(crq)) {
++ const int sync = cfq_crq_is_sync(crq);
+
+- if (crq->accounted) {
+- crq->accounted = 0;
+- cfqq->cfqd->rq_in_driver--;
++ cfq_clear_crq_in_flight(crq);
++ WARN_ON(!cfqq->on_dispatch[sync]);
++ cfqq->on_dispatch[sync]--;
+ }
++ cfq_mark_crq_requeued(crq);
+ }
+ }
+
+@@ -640,11 +714,10 @@
+ struct cfq_rq *crq = RQ_DATA(rq);
+
+ if (crq) {
+- cfq_remove_merge_hints(q, crq);
+ list_del_init(&rq->queuelist);
++ cfq_del_crq_rb(crq);
++ cfq_remove_merge_hints(q, crq);
+
+- if (crq->cfq_queue)
+- cfq_del_crq_rb(crq);
+ }
+ }
+
+@@ -662,21 +735,15 @@
+ }
+
+ __rq = cfq_find_rq_hash(cfqd, bio->bi_sector);
+- if (__rq) {
+- BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector);
+-
+- if (elv_rq_merge_ok(__rq, bio)) {
+- ret = ELEVATOR_BACK_MERGE;
+- goto out;
+- }
++ if (__rq && elv_rq_merge_ok(__rq, bio)) {
++ ret = ELEVATOR_BACK_MERGE;
++ goto out;
+ }
+
+ __rq = cfq_find_rq_rb(cfqd, bio->bi_sector + bio_sectors(bio));
+- if (__rq) {
+- if (elv_rq_merge_ok(__rq, bio)) {
+- ret = ELEVATOR_FRONT_MERGE;
+- goto out;
+- }
++ if (__rq && elv_rq_merge_ok(__rq, bio)) {
++ ret = ELEVATOR_FRONT_MERGE;
++ goto out;
+ }
+
+ return ELEVATOR_NO_MERGE;
+@@ -709,235 +776,496 @@
+ cfq_merged_requests(request_queue_t *q, struct request *rq,
+ struct request *next)
+ {
+- struct cfq_rq *crq = RQ_DATA(rq);
+- struct cfq_rq *cnext = RQ_DATA(next);
+-
+ cfq_merged_request(q, rq);
+
+- if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) {
+- if (time_before(cnext->queue_start, crq->queue_start)) {
+- list_move(&rq->queuelist, &next->queuelist);
+- crq->queue_start = cnext->queue_start;
+- }
+- }
++ /*
++ * reposition in fifo if next is older than rq
++ */
++ if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) &&
++ time_before(next->start_time, rq->start_time))
++ list_move(&rq->queuelist, &next->queuelist);
+
+- cfq_update_next_crq(cnext);
+ cfq_remove_request(q, next);
+ }
+
++static inline void
++__cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
++{
++ if (cfqq) {
++ /*
++ * stop potential idle class queues waiting service
++ */
++ del_timer(&cfqd->idle_class_timer);
++
++ cfqq->slice_start = jiffies;
++ cfqq->slice_end = 0;
++ cfqq->slice_left = 0;
++ cfq_clear_cfqq_must_alloc_slice(cfqq);
++ cfq_clear_cfqq_fifo_expire(cfqq);
++ cfq_clear_cfqq_expired(cfqq);
++ }
++
++ cfqd->active_queue = cfqq;
++}
++
+ /*
+- * 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
+- * at cfqd->last_sector, not 0.
++ * 0
++ * 0,1
++ * 0,1,2
++ * 0,1,2,3
++ * 0,1,2,3,4
++ * 0,1,2,3,4,5
++ * 0,1,2,3,4,5,6
++ * 0,1,2,3,4,5,6,7
+ */
+-static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq)
++static int cfq_get_next_prio_level(struct cfq_data *cfqd)
+ {
+- struct cfq_data *cfqd = q->elevator->elevator_data;
+- struct cfq_queue *cfqq = crq->cfq_queue;
+- struct list_head *head = &q->queue_head, *entry = head;
+- struct request *__rq;
+- sector_t last;
+-
+- cfq_del_crq_rb(crq);
+- cfq_remove_merge_hints(q, crq);
+- list_del(&crq->request->queuelist);
++ int prio, wrap;
+
+- last = cfqd->last_sector;
+- while ((entry = entry->prev) != head) {
+- __rq = list_entry_rq(entry);
++ prio = -1;
++ wrap = 0;
++ do {
++ int p;
+
+- if (blk_barrier_rq(crq->request))
+- break;
+- if (!blk_fs_request(crq->request))
+- break;
++ for (p = cfqd->cur_prio; p <= cfqd->cur_end_prio; p++) {
++ if (!list_empty(&cfqd->rr_list[p])) {
++ prio = p;
++ break;
++ }
++ }
+
+- if (crq->request->sector > __rq->sector)
+- break;
+- if (__rq->sector > last && crq->request->sector < last) {
+- last = crq->request->sector;
++ if (prio != -1)
+ break;
++ cfqd->cur_prio = 0;
++ if (++cfqd->cur_end_prio == CFQ_PRIO_LISTS) {
++ cfqd->cur_end_prio = 0;
++ if (wrap)
++ break;
++ wrap = 1;
+ }
+- }
++ } while (1);
+
+- cfqd->last_sector = last;
+- crq->in_flight = 1;
+- cfqq->in_flight++;
+- list_add(&crq->request->queuelist, entry);
+-}
++ if (unlikely(prio == -1))
++ return -1;
+
+-/*
+- * return expired entry, or NULL to just start from scratch in rbtree
+- */
+-static inline struct cfq_rq *cfq_check_fifo(struct cfq_queue *cfqq)
+-{
+- struct cfq_data *cfqd = cfqq->cfqd;
+- const int reads = !list_empty(&cfqq->fifo[0]);
+- const int writes = !list_empty(&cfqq->fifo[1]);
+- unsigned long now = jiffies;
+- struct cfq_rq *crq;
++ BUG_ON(prio >= CFQ_PRIO_LISTS);
+
+- if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire))
+- return NULL;
++ list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr);
+
+- 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;
++ cfqd->cur_prio = prio + 1;
++ if (cfqd->cur_prio > cfqd->cur_end_prio) {
++ cfqd->cur_end_prio = cfqd->cur_prio;
++ cfqd->cur_prio = 0;
+ }
+-
+- 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 (cfqd->cur_end_prio == CFQ_PRIO_LISTS) {
++ cfqd->cur_prio = 0;
++ cfqd->cur_end_prio = 0;
+ }
+
+- return NULL;
++ return prio;
+ }
+
+-/*
+- * dispatch a single request from given queue
+- */
+-static inline void
+-cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd,
+- struct cfq_queue *cfqq)
++static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd)
+ {
+- struct cfq_rq *crq;
++ struct cfq_queue *cfqq;
+
+ /*
+- * follow expired path, else get first next available
++ * if current queue is expired but not done with its requests yet,
++ * wait for that to happen
+ */
+- if ((crq = cfq_check_fifo(cfqq)) == NULL) {
+- if (cfqd->find_best_crq)
+- crq = cfqq->next_crq;
+- else
+- crq = rb_entry_crq(rb_first(&cfqq->sort_list));
++ if ((cfqq = cfqd->active_queue) != NULL) {
++ if (cfq_cfqq_expired(cfqq) && cfq_cfqq_dispatched(cfqq))
++ return NULL;
+ }
+
+- cfqd->last_sector = crq->request->sector + crq->request->nr_sectors;
++ /*
++ * if current list is non-empty, grab first entry. if it is empty,
++ * get next prio level and grab first entry then if any are spliced
++ */
++ if (!list_empty(&cfqd->cur_rr) || cfq_get_next_prio_level(cfqd) != -1)
++ cfqq = list_entry_cfqq(cfqd->cur_rr.next);
+
+ /*
+- * finally, insert request into driver list
++ * if we have idle queues and no rt or be queues had pending
++ * requests, either allow immediate service if the grace period
++ * has passed or arm the idle grace timer
+ */
+- cfq_dispatch_sort(q, crq);
++ if (!cfqq && !list_empty(&cfqd->idle_rr)) {
++ unsigned long end = cfqd->last_end_request + CFQ_IDLE_GRACE;
++
++ if (time_after_eq(jiffies, end))
++ cfqq = list_entry_cfqq(cfqd->idle_rr.next);
++ else
++ mod_timer(&cfqd->idle_class_timer, end);
++ }
++
++ __cfq_set_active_queue(cfqd, cfqq);
++ return cfqq;
+ }
+
+-static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch)
++/*
++ * current cfqq expired its slice (or was too idle), select new one
++ */
++static void
++__cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
++ int preempted)
+ {
+- 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))
+- return 0;
++ unsigned long now = jiffies;
+
+- queued = 0;
+- first_round = 1;
+-restart:
+- busy_queues = 0;
+- list_for_each_safe(entry, tmp, &cfqd->rr_list) {
+- cfqq = list_entry_cfqq(entry);
++ if (cfq_cfqq_wait_request(cfqq))
++ del_timer(&cfqd->idle_slice_timer);
+
+- BUG_ON(RB_EMPTY(&cfqq->sort_list));
++ if (!preempted && !cfq_cfqq_dispatched(cfqq))
++ cfqq->service_last = now;
+
+- /*
+- * first round of queueing, only select from queues that
+- * don't already have io in-flight
+- */
+- if (first_round && cfqq->in_flight)
+- continue;
++ cfq_clear_cfqq_must_dispatch(cfqq);
++ cfq_clear_cfqq_wait_request(cfqq);
+
+- cfq_dispatch_request(q, cfqd, cfqq);
++ /*
++ * store what was left of this slice, if the queue idled out
++ * or was preempted
++ */
++ if (time_after(cfqq->slice_end, now))
++ cfqq->slice_left = cfqq->slice_end - now;
++ else
++ cfqq->slice_left = 0;
+
+- if (!RB_EMPTY(&cfqq->sort_list))
+- busy_queues++;
++ if (cfq_cfqq_on_rr(cfqq))
++ cfq_resort_rr_list(cfqq, preempted);
+
+- queued++;
+- }
++ if (cfqq == cfqd->active_queue)
++ cfqd->active_queue = NULL;
+
+- if ((queued < max_dispatch) && (busy_queues || first_round)) {
+- first_round = 0;
+- goto restart;
++ if (cfqd->active_cic) {
++ put_io_context(cfqd->active_cic->ioc);
++ cfqd->active_cic = NULL;
+ }
+
+- return queued;
++ cfqd->dispatch_slice = 0;
+ }
+
+-static inline void cfq_account_dispatch(struct cfq_rq *crq)
++static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted)
+ {
+- struct cfq_queue *cfqq = crq->cfq_queue;
+- struct cfq_data *cfqd = cfqq->cfqd;
+- unsigned long now, elapsed;
++ struct cfq_queue *cfqq = cfqd->active_queue;
+
+- if (!blk_fs_request(crq->request))
+- return;
++ if (cfqq) {
++ /*
++ * use deferred expiry, if there are requests in progress as
++ * not to disturb the slice of the next queue
++ */
++ if (cfq_cfqq_dispatched(cfqq))
++ cfq_mark_cfqq_expired(cfqq);
++ else
++ __cfq_slice_expired(cfqd, cfqq, preempted);
++ }
++}
+
+- /*
+- * accounted bit is necessary since some drivers will call
+- * elv_next_request() many times for the same request (eg ide)
+- */
+- if (crq->accounted)
+- return;
++static int cfq_arm_slice_timer(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+
+- now = jiffies;
+- if (cfqq->service_start == ~0UL)
+- cfqq->service_start = now;
++{
++ WARN_ON(!RB_EMPTY(&cfqq->sort_list));
++ WARN_ON(cfqq != cfqd->active_queue);
+
+ /*
+- * 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);
+- }
++ * idle is disabled, either manually or by past process history
++ */
++ if (!cfqd->cfq_slice_idle)
++ return 0;
++ if (!cfq_cfqq_idle_window(cfqq))
++ return 0;
++ /*
++ * task has exited, don't wait
++ */
++ if (cfqd->active_cic && !cfqd->active_cic->ioc->task)
++ return 0;
++
++ cfq_mark_cfqq_must_dispatch(cfqq);
++ cfq_mark_cfqq_wait_request(cfqq);
++
++ if (!timer_pending(&cfqd->idle_slice_timer)) {
++ unsigned long slice_left = min(cfqq->slice_end - 1, (unsigned long) cfqd->cfq_slice_idle);
++
++ cfqd->idle_slice_timer.expires = jiffies + slice_left;
++ add_timer(&cfqd->idle_slice_timer);
++ }
++
++ return 1;
++}
++
++/*
++ * 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
++ * at cfqd->last_sector, not 0.
++ */
++static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq)
++{
++ struct cfq_data *cfqd = q->elevator->elevator_data;
++ struct cfq_queue *cfqq = crq->cfq_queue;
++ struct list_head *head = &q->queue_head, *entry = head;
++ struct request *__rq;
++ sector_t last;
++
++ list_del(&crq->request->queuelist);
++
++ last = cfqd->last_sector;
++ list_for_each_entry_reverse(__rq, head, queuelist) {
++ struct cfq_rq *__crq = RQ_DATA(__rq);
++
++ if (blk_barrier_rq(__rq))
++ break;
++ if (!blk_fs_request(__rq))
++ break;
++ if (cfq_crq_requeued(__crq))
++ break;
++
++ if (__rq->sector <= crq->request->sector)
++ break;
++ if (__rq->sector > last && crq->request->sector < last) {
++ last = crq->request->sector + crq->request->nr_sectors;
++ break;
++ }
++ entry = &__rq->queuelist;
++ }
++
++ cfqd->last_sector = last;
++
++ cfqq->next_crq = cfq_find_next_crq(cfqd, cfqq, crq);
+
+- elapsed = now - crq->queue_start;
+- if (elapsed > max_elapsed_dispatch)
+- max_elapsed_dispatch = elapsed;
++ cfq_del_crq_rb(crq);
++ cfq_remove_merge_hints(q, crq);
++
++ cfq_mark_crq_in_flight(crq);
++ cfq_clear_crq_requeued(crq);
++
++ cfqq->on_dispatch[cfq_crq_is_sync(crq)]++;
++ list_add_tail(&crq->request->queuelist, entry);
++}
++
++/*
++ * return expired entry, or NULL to just start from scratch in rbtree
++ */
++static inline struct cfq_rq *cfq_check_fifo(struct cfq_queue *cfqq)
++{
++ struct cfq_data *cfqd = cfqq->cfqd;
++ struct request *rq;
++ struct cfq_rq *crq;
+
+- crq->accounted = 1;
+- crq->service_start = now;
++ if (cfq_cfqq_fifo_expire(cfqq))
++ return NULL;
++
++ if (!list_empty(&cfqq->fifo)) {
++ int fifo = cfq_cfqq_class_sync(cfqq);
+
+- 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 = RQ_DATA(list_entry_fifo(cfqq->fifo.next));
++ rq = crq->request;
++ if (time_after(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo])) {
++ cfq_mark_cfqq_fifo_expire(cfqq);
++ return crq;
++ }
+ }
++
++ return NULL;
++}
++
++/*
++ * Scale schedule slice based on io priority. Use the sync time slice only
++ * if a queue is marked sync and has sync io queued. A sync queue with async
++ * io only, should not get full sync slice length.
++ */
++static inline int
++cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
++{
++ const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)];
++
++ WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR);
++
++ return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio));
++}
++
++static inline void
++cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
++{
++ cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies;
++}
++
++static inline int
++cfq_prio_to_maxrq(struct cfq_data *cfqd, struct cfq_queue *cfqq)
++{
++ const int base_rq = cfqd->cfq_slice_async_rq;
++
++ WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR);
++
++ return 2 * (base_rq + base_rq * (CFQ_PRIO_LISTS - 1 - cfqq->ioprio));
++}
++
++/*
++ * get next queue for service
++ */
++static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd, int force)
++{
++ unsigned long now = jiffies;
++ struct cfq_queue *cfqq;
++
++ cfqq = cfqd->active_queue;
++ if (!cfqq)
++ goto new_queue;
++
++ if (cfq_cfqq_expired(cfqq))
++ goto new_queue;
++
++ /*
++ * slice has expired
++ */
++ if (!cfq_cfqq_must_dispatch(cfqq) && time_after(now, cfqq->slice_end))
++ goto expire;
++
++ /*
++ * 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 (!force && cfq_cfqq_class_sync(cfqq) &&
++ time_before(now, cfqq->slice_end)) {
++ if (cfq_arm_slice_timer(cfqd, cfqq))
++ return NULL;
++ }
++
++expire:
++ cfq_slice_expired(cfqd, 0);
++new_queue:
++ cfqq = cfq_set_active_queue(cfqd);
++keep_queue:
++ return cfqq;
++}
++
++static int
++__cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq,
++ int max_dispatch)
++{
++ int dispatched = 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;
++
++ /*
++ * finally, insert request into driver dispatch list
++ */
++ cfq_dispatch_sort(cfqd->queue, crq);
++
++ cfqd->dispatch_slice++;
++ dispatched++;
++
++ 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)
++ cfq_set_prio_slice(cfqd, cfqq);
++
++ /*
++ * expire an async queue immediately if it has used up its slice. idle
++ * queue always expire after 1 dispatch round.
++ */
++ if ((!cfq_cfqq_sync(cfqq) &&
++ cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) ||
++ cfq_class_idle(cfqq))
++ cfq_slice_expired(cfqd, 0);
++
++ return dispatched;
++}
++
++static int
++cfq_dispatch_requests(request_queue_t *q, int max_dispatch, int force)
++{
++ struct cfq_data *cfqd = q->elevator->elevator_data;
++ struct cfq_queue *cfqq;
++
++ if (!cfqd->busy_queues)
++ return 0;
++
++ cfqq = cfq_select_queue(cfqd, force);
++ if (cfqq) {
++ cfq_clear_cfqq_must_dispatch(cfqq);
++ cfq_clear_cfqq_wait_request(cfqq);
++ del_timer(&cfqd->idle_slice_timer);
++
++ if (cfq_class_idle(cfqq))
++ max_dispatch = 1;
++
++ return __cfq_dispatch_requests(cfqd, cfqq, max_dispatch);
++ }
++
++ 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;
++
++ if (unlikely(!blk_fs_request(crq->request)))
++ return;
++
++ /*
++ * accounted bit is necessary since some drivers will call
++ * elv_next_request() many times for the same request (eg ide)
++ */
++ if (cfq_crq_in_driver(crq))
++ return;
++
++ cfq_mark_crq_in_driver(crq);
++ cfqd->rq_in_driver++;
+ }
+
+ static inline void
+ cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq)
+ {
+ struct cfq_data *cfqd = cfqq->cfqd;
++ unsigned long now;
+
+- if (!crq->accounted)
++ if (!cfq_crq_in_driver(crq))
+ return;
+
++ now = jiffies;
++
+ 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 (!cfq_class_idle(cfqq))
++ cfqd->last_end_request = now;
+
+- if (time_after(now, cfqq->service_start + cfq_service)) {
+- cfqq->service_start = now;
+- cfqq->service_used >>= 3;
++ if (!cfq_cfqq_dispatched(cfqq)) {
++ if (cfq_cfqq_on_rr(cfqq)) {
++ cfqq->service_last = now;
++ cfq_resort_rr_list(cfqq, 0);
++ }
++ if (cfq_cfqq_expired(cfqq)) {
++ __cfq_slice_expired(cfqd, cfqq, 0);
++ cfq_schedule_dispatch(cfqd);
+ }
+-
+- cfqq->service_used += duration;
+- cfq_sort_rr_list(cfqq, 0);
+-
+- if (duration > max_elapsed_crq)
+- max_elapsed_crq = duration;
+ }
++
++ if (cfq_crq_is_sync(crq))
++ crq->io_context->last_end_request = now;
+ }
+
+ static struct request *cfq_next_request(request_queue_t *q)
+@@ -950,7 +1278,19 @@
+ dispatch:
+ rq = list_entry_rq(q->queue_head.next);
+
+- if ((crq = RQ_DATA(rq)) != NULL) {
++ crq = RQ_DATA(rq);
++ if (crq) {
++ struct cfq_queue *cfqq = crq->cfq_queue;
++
++ /*
++ * if idle window is disabled, allow queue buildup
++ */
++ if (!cfq_crq_in_driver(crq) &&
++ !cfq_cfqq_idle_window(cfqq) &&
++ !blk_barrier_rq(rq) &&
++ cfqd->rq_in_driver >= cfqd->cfq_max_depth)
++ return NULL;
++
+ cfq_remove_merge_hints(q, crq);
+ cfq_account_dispatch(crq);
+ }
+@@ -958,7 +1298,7 @@
+ return rq;
+ }
+
+- if (cfq_dispatch_requests(q, cfqd->cfq_quantum))
++ if (cfq_dispatch_requests(q, cfqd->cfq_quantum, 0))
+ goto dispatch;
+
+ return NULL;
+@@ -972,13 +1312,21 @@
+ */
+ static void cfq_put_queue(struct cfq_queue *cfqq)
+ {
+- BUG_ON(!atomic_read(&cfqq->ref));
++ struct cfq_data *cfqd = cfqq->cfqd;
++
++ BUG_ON(atomic_read(&cfqq->ref) <= 0);
+
+ if (!atomic_dec_and_test(&cfqq->ref))
+ return;
+
+ BUG_ON(rb_first(&cfqq->sort_list));
+- BUG_ON(cfqq->on_rr);
++ BUG_ON(cfqq->allocated[READ] + cfqq->allocated[WRITE]);
++ BUG_ON(cfq_cfqq_on_rr(cfqq));
++
++ if (unlikely(cfqd->active_queue == cfqq)) {
++ __cfq_slice_expired(cfqd, cfqq, 0);
++ cfq_schedule_dispatch(cfqd);
++ }
+
+ cfq_put_cfqd(cfqq->cfqd);
+
+@@ -991,15 +1339,17 @@
+ }
+
+ static inline struct cfq_queue *
+-__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval)
++__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned int prio,
++ const int hashval)
+ {
+ struct hlist_head *hash_list = &cfqd->cfq_hash[hashval];
+ struct hlist_node *entry, *next;
+
+ hlist_for_each_safe(entry, next, hash_list) {
+ struct cfq_queue *__cfqq = list_entry_qhash(entry);
++ const unsigned short __p = IOPRIO_PRIO_VALUE(__cfqq->ioprio_class, __cfqq->ioprio);
+
+- if (__cfqq->key == key)
++ if (__cfqq->key == key && (__p == prio || prio == CFQ_KEY_ANY))
+ return __cfqq;
+ }
+
+@@ -1007,94 +1357,220 @@
+ }
+
+ static struct cfq_queue *
+-cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key)
++cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio)
+ {
+- return __cfq_find_cfq_hash(cfqd, key, hash_long(key, CFQ_QHASH_SHIFT));
++ return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT));
+ }
+
+-static inline void
+-cfq_rehash_cfqq(struct cfq_data *cfqd, struct cfq_queue **cfqq,
+- struct cfq_io_context *cic)
++static void cfq_free_io_context(struct cfq_io_context *cic)
+ {
+- unsigned long hashkey = cfq_hash_key(cfqd, current);
+- unsigned long hashval = hash_long(hashkey, CFQ_QHASH_SHIFT);
+- struct cfq_queue *__cfqq;
+- unsigned long flags;
+-
+- spin_lock_irqsave(cfqd->queue->queue_lock, flags);
++ struct cfq_io_context *__cic;
++ struct list_head *entry, *next;
+
+- hlist_del(&(*cfqq)->cfq_hash);
+-
+- __cfqq = __cfq_find_cfq_hash(cfqd, hashkey, hashval);
+- if (!__cfqq || __cfqq == *cfqq) {
+- __cfqq = *cfqq;
+- hlist_add_head(&__cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
+- __cfqq->key_type = cfqd->key_type;
+- } else {
+- atomic_inc(&__cfqq->ref);
+- cic->cfqq = __cfqq;
+- cfq_put_queue(*cfqq);
+- *cfqq = __cfqq;
++ list_for_each_safe(entry, next, &cic->list) {
++ __cic = list_entry(entry, struct cfq_io_context, list);
++ kmem_cache_free(cfq_ioc_pool, __cic);
+ }
+
+- cic->cfqq = __cfqq;
+- spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
++ kmem_cache_free(cfq_ioc_pool, cic);
+ }
+
+-static void cfq_free_io_context(struct cfq_io_context *cic)
++/*
++ * Called with interrupts disabled
++ */
++static void cfq_exit_single_io_context(struct cfq_io_context *cic)
+ {
+- kmem_cache_free(cfq_ioc_pool, cic);
++ struct cfq_data *cfqd = cic->cfqq->cfqd;
++ request_queue_t *q = cfqd->queue;
++
++ WARN_ON(!irqs_disabled());
++
++ spin_lock(q->queue_lock);
++
++ if (unlikely(cic->cfqq == cfqd->active_queue)) {
++ __cfq_slice_expired(cfqd, cic->cfqq, 0);
++ cfq_schedule_dispatch(cfqd);
++ }
++
++ cfq_put_queue(cic->cfqq);
++ cic->cfqq = NULL;
++ spin_unlock(q->queue_lock);
+ }
+
+ /*
+- * locking hierarchy is: io_context lock -> queue locks
++ * Another task may update the task cic list, if it is doing a queue lookup
++ * on its behalf. cfq_cic_lock excludes such concurrent updates
+ */
+ 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;
++ struct cfq_io_context *__cic;
++ struct list_head *entry;
+ unsigned long flags;
+
++ local_irq_save(flags);
++
+ /*
+ * 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;
+-
++ list_for_each(entry, &cic->list) {
+ __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);
++ 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);
++ local_irq_restore(flags);
+ }
+
+-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_mask)
+ {
+- struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags);
++ struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_mask);
+
+ if (cic) {
+- cic->dtor = cfq_free_io_context;
+- cic->exit = cfq_exit_io_context;
+ INIT_LIST_HEAD(&cic->list);
+ cic->cfqq = NULL;
++ cic->key = NULL;
++ cic->last_end_request = jiffies;
++ cic->ttime_total = 0;
++ cic->ttime_samples = 0;
++ cic->ttime_mean = 0;
++ cic->dtor = cfq_free_io_context;
++ cic->exit = cfq_exit_io_context;
+ }
+
+ return cic;
+ }
+
++static void cfq_init_prio_data(struct cfq_queue *cfqq)
++{
++ struct task_struct *tsk = current;
++ int ioprio_class;
++
++ if (!cfq_cfqq_prio_changed(cfqq))
++ return;
++
++ ioprio_class = IOPRIO_PRIO_CLASS(tsk->ioprio);
++ switch (ioprio_class) {
++ default:
++ printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class);
++ case IOPRIO_CLASS_NONE:
++ /*
++ * no prio set, place us in the middle of the BE classes
++ */
++ cfqq->ioprio = task_nice_ioprio(tsk);
++ cfqq->ioprio_class = IOPRIO_CLASS_BE;
++ break;
++ case IOPRIO_CLASS_RT:
++ cfqq->ioprio = task_ioprio(tsk);
++ cfqq->ioprio_class = IOPRIO_CLASS_RT;
++ break;
++ case IOPRIO_CLASS_BE:
++ cfqq->ioprio = task_ioprio(tsk);
++ cfqq->ioprio_class = IOPRIO_CLASS_BE;
++ break;
++ case IOPRIO_CLASS_IDLE:
++ cfqq->ioprio_class = IOPRIO_CLASS_IDLE;
++ cfqq->ioprio = 7;
++ cfq_clear_cfqq_idle_window(cfqq);
++ break;
++ }
++
++ /*
++ * keep track of original prio settings in case we have to temporarily
++ * elevate the priority of this queue
++ */
++ cfqq->org_ioprio = cfqq->ioprio;
++ cfqq->org_ioprio_class = cfqq->ioprio_class;
++
++ if (cfq_cfqq_on_rr(cfqq))
++ cfq_resort_rr_list(cfqq, 0);
++
++ cfq_clear_cfqq_prio_changed(cfqq);
++}
++
++static inline void changed_ioprio(struct cfq_queue *cfqq)
++{
++ if (cfqq) {
++ struct cfq_data *cfqd = cfqq->cfqd;
++
++ spin_lock(cfqd->queue->queue_lock);
++ cfq_mark_cfqq_prio_changed(cfqq);
++ cfq_init_prio_data(cfqq);
++ spin_unlock(cfqd->queue->queue_lock);
++ }
++}
++
++/*
++ * callback from sys_ioprio_set, irqs are disabled
++ */
++static int cfq_ioc_set_ioprio(struct io_context *ioc, unsigned int ioprio)
++{
++ struct cfq_io_context *cic = ioc->cic;
++
++ changed_ioprio(cic->cfqq);
++
++ list_for_each_entry(cic, &cic->list, list)
++ changed_ioprio(cic->cfqq);
++
++ return 0;
++}
++
++static struct cfq_queue *
++cfq_get_queue(struct cfq_data *cfqd, unsigned int key, unsigned short ioprio,
++ int gfp_mask)
++{
++ const int hashval = hash_long(key, CFQ_QHASH_SHIFT);
++ struct cfq_queue *cfqq, *new_cfqq = NULL;
++
++retry:
++ cfqq = __cfq_find_cfq_hash(cfqd, key, ioprio, hashval);
++
++ if (!cfqq) {
++ if (new_cfqq) {
++ cfqq = new_cfqq;
++ new_cfqq = NULL;
++ } else {
++ spin_unlock_irq(cfqd->queue->queue_lock);
++ new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask);
++ spin_lock_irq(cfqd->queue->queue_lock);
++
++ if (!new_cfqq && !(gfp_mask & __GFP_WAIT))
++ goto out;
++
++ goto retry;
++ }
++
++ memset(cfqq, 0, sizeof(*cfqq));
++
++ INIT_HLIST_NODE(&cfqq->cfq_hash);
++ INIT_LIST_HEAD(&cfqq->cfq_list);
++ RB_CLEAR_ROOT(&cfqq->sort_list);
++ INIT_LIST_HEAD(&cfqq->fifo);
++
++ cfqq->key = key;
++ hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
++ atomic_set(&cfqq->ref, 0);
++ cfqq->cfqd = cfqd;
++ atomic_inc(&cfqd->ref);
++ cfqq->service_last = 0;
++ /*
++ * set ->slice_left to allow preemption for a new process
++ */
++ cfqq->slice_left = 2 * cfqd->cfq_slice_idle;
++ cfq_mark_cfqq_idle_window(cfqq);
++ cfq_mark_cfqq_prio_changed(cfqq);
++ cfq_init_prio_data(cfqq);
++ }
++
++ if (new_cfqq)
++ kmem_cache_free(cfq_pool, new_cfqq);
++
++ atomic_inc(&cfqq->ref);
++out:
++ WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq);
++ return cfqq;
++}
++
+ /*
+ * Setup general io context and cfq io context. There can be several cfq
+ * io contexts per general io context, if this process is doing io to more
+@@ -1102,39 +1578,39 @@
+ * cfqq, so we don't need to worry about it disappearing
+ */
+ static struct cfq_io_context *
+-cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags)
++cfq_get_io_context(struct cfq_data *cfqd, pid_t pid, int gfp_mask)
+ {
+- struct cfq_data *cfqd = (*cfqq)->cfqd;
+- struct cfq_queue *__cfqq = *cfqq;
++ struct io_context *ioc = NULL;
+ struct cfq_io_context *cic;
+- struct io_context *ioc;
+
+- might_sleep_if(gfp_flags & __GFP_WAIT);
++ might_sleep_if(gfp_mask & __GFP_WAIT);
+
+- ioc = get_io_context(gfp_flags);
++ ioc = get_io_context(gfp_mask);
+ if (!ioc)
+ return NULL;
+
+ if ((cic = ioc->cic) == NULL) {
+- cic = cfq_alloc_io_context(gfp_flags);
++ cic = cfq_alloc_io_context(cfqd, gfp_mask);
+
+ 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;
++ ioc->set_ioprio = cfq_ioc_set_ioprio;
+ cic->ioc = ioc;
+- cic->cfqq = __cfqq;
+- atomic_inc(&__cfqq->ref);
++ cic->key = cfqd;
++ atomic_inc(&cfqd->ref);
+ } else {
+ struct cfq_io_context *__cic;
+- unsigned long flags;
+
+ /*
+- * since the first cic on the list is actually the head
+- * itself, need to check this here or we'll duplicate an
+- * cic per ioc for no reason
++ * the first cic on the list is actually the head itself
+ */
+- if (cic->cfqq == __cfqq)
++ if (cic->key == cfqd)
+ goto out;
+
+ /*
+@@ -1142,152 +1618,255 @@
+ * 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) {
++ if (__cic->key == cfqd) {
+ 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_mask);
+ if (__cic == NULL)
+ goto err;
+
+ __cic->ioc = ioc;
+- __cic->cfqq = __cfqq;
+- atomic_inc(&__cfqq->ref);
+- spin_lock_irqsave(&ioc->lock, flags);
++ __cic->key = cfqd;
++ atomic_inc(&cfqd->ref);
+ list_add(&__cic->list, &cic->list);
+- spin_unlock_irqrestore(&ioc->lock, flags);
+-
+ cic = __cic;
+- *cfqq = __cfqq;
+ }
+
+ out:
++ return cic;
++err:
++ put_io_context(ioc);
++ return NULL;
++}
++
++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;
++}
++
++#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 = cfq_cfqq_idle_window(cfqq);
++
++ if (!cic->ioc->task || !cfqd->cfq_slice_idle)
++ enable_idle = 0;
++ else if (sample_valid(cic->ttime_samples)) {
++ if (cic->ttime_mean > cfqd->cfq_slice_idle)
++ enable_idle = 0;
++ else
++ enable_idle = 1;
++ }
++
++ if (enable_idle)
++ cfq_mark_cfqq_idle_window(cfqq);
++ else
++ cfq_clear_cfqq_idle_window(cfqq);
++}
++
++
++/*
++ * 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 (cfq_class_idle(new_cfqq))
++ return 0;
++
++ if (!cfqq)
++ return 1;
++
++ if (cfq_class_idle(cfqq))
++ return 1;
++ if (!cfq_cfqq_wait_request(new_cfqq))
++ return 0;
+ /*
+- * if key_type has been changed on the fly, we lazily rehash
+- * each queue at lookup time
++ * if it doesn't have slice left, forget it
+ */
+- if ((*cfqq)->key_type != cfqd->key_type)
+- cfq_rehash_cfqq(cfqd, cfqq, cic);
++ if (new_cfqq->slice_left < cfqd->cfq_slice_idle)
++ return 0;
++ if (cfq_crq_is_sync(crq) && !cfq_cfqq_sync(cfqq))
++ return 1;
++
++ return 0;
++}
++
++/*
++ * cfqq preempts the active queue. if we allowed preempt with no slice left,
++ * let it have half of its nominal slice.
++ */
++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, 1);
+
+- return cic;
+-err:
+- put_io_context(ioc);
+- return NULL;
++ if (!cfqq->slice_left)
++ cfqq->slice_left = cfq_prio_to_slice(cfqd, cfqq) / 2;
++
++ cfqq->slice_end = cfqq->slice_left + jiffies;
++ __cfq_slice_expired(cfqd, cfqq, 1);
++ __cfq_set_active_queue(cfqd, cfqq);
+ }
+
+-static struct cfq_queue *
+-__cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask)
++/*
++ * should really be a ll_rw_blk.c helper
++ */
++static void cfq_start_queueing(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+ {
+- const int hashval = hash_long(key, CFQ_QHASH_SHIFT);
+- struct cfq_queue *cfqq, *new_cfqq = NULL;
+-
+-retry:
+- cfqq = __cfq_find_cfq_hash(cfqd, key, hashval);
++ request_queue_t *q = cfqd->queue;
+
+- if (!cfqq) {
+- if (new_cfqq) {
+- cfqq = new_cfqq;
+- new_cfqq = NULL;
+- } else {
+- spin_unlock_irq(cfqd->queue->queue_lock);
+- new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask);
+- spin_lock_irq(cfqd->queue->queue_lock);
++ if (!blk_queue_plugged(q))
++ q->request_fn(q);
++ else
++ __generic_unplug_device(q);
++}
+
+- if (!new_cfqq && !(gfp_mask & __GFP_WAIT))
+- goto out;
++/*
++ * 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)
++{
++ struct cfq_io_context *cic;
+
+- goto retry;
+- }
++ cfqq->next_crq = cfq_choose_req(cfqd, cfqq->next_crq, crq);
+
+- memset(cfqq, 0, sizeof(*cfqq));
++ /*
++ * we never wait for an async request and we don't allow preemption
++ * of an async request. so just return early
++ */
++ if (!cfq_crq_is_sync(crq))
++ return;
+
+- INIT_HLIST_NODE(&cfqq->cfq_hash);
+- INIT_LIST_HEAD(&cfqq->cfq_list);
+- RB_CLEAR_ROOT(&cfqq->sort_list);
+- INIT_LIST_HEAD(&cfqq->fifo[0]);
+- INIT_LIST_HEAD(&cfqq->fifo[1]);
++ cic = crq->io_context;
+
+- cfqq->key = key;
+- hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
+- atomic_set(&cfqq->ref, 0);
+- cfqq->cfqd = cfqd;
+- atomic_inc(&cfqd->ref);
+- cfqq->key_type = cfqd->key_type;
+- cfqq->service_start = ~0UL;
+- }
++ cfq_update_io_thinktime(cfqd, cic);
++ cfq_update_idle_window(cfqd, cfqq, cic);
+
+- if (new_cfqq)
+- kmem_cache_free(cfq_pool, new_cfqq);
++ cic->last_queue = jiffies;
+
+- atomic_inc(&cfqq->ref);
+-out:
+- WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq);
+- return cfqq;
++ 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 (cfq_cfqq_wait_request(cfqq)) {
++ cfq_mark_cfqq_must_dispatch(cfqq);
++ del_timer(&cfqd->idle_slice_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);
++ cfq_mark_cfqq_must_dispatch(cfqq);
++ 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_init_prio_data(cfqq);
+
+ 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);
++
++ 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:
+- while (cfq_dispatch_requests(q, cfqd->cfq_quantum))
++ while (cfq_dispatch_requests(q, INT_MAX, 1))
+ ;
+ list_add_tail(&rq->queuelist, &q->queue_head);
++ /*
++ * If we were idling with pending requests on
++ * inactive cfqqs, force dispatching will
++ * remove the idle timer and the queue won't
++ * be kicked by __make_request() afterward.
++ * Kick it here.
++ */
++ cfq_schedule_dispatch(cfqd);
+ break;
+ case ELEVATOR_INSERT_FRONT:
+ list_add(&rq->queuelist, &q->queue_head);
+ 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);
+ }
+
+ static void cfq_completed_request(request_queue_t *q, struct request *rq)
+@@ -1300,9 +1879,11 @@
+
+ cfqq = crq->cfq_queue;
+
+- if (crq->in_flight) {
+- WARN_ON(!cfqq->in_flight);
+- cfqq->in_flight--;
++ if (cfq_crq_in_flight(crq)) {
++ const int sync = cfq_crq_is_sync(crq);
++
++ WARN_ON(!cfqq->on_dispatch[sync]);
++ cfqq->on_dispatch[sync]--;
+ }
+
+ cfq_account_completion(cfqq, crq);
+@@ -1332,51 +1913,136 @@
+ return NULL;
+ }
+
+-static int cfq_may_queue(request_queue_t *q, int rw)
++/*
++ * we temporarily boost lower priority queues if they are holding fs exclusive
++ * resources. they are boosted to normal prio (CLASS_BE/4)
++ */
++static void cfq_prio_boost(struct cfq_queue *cfqq)
+ {
+- struct cfq_data *cfqd = q->elevator->elevator_data;
+- struct cfq_queue *cfqq;
+- int ret = ELV_MQUEUE_MAY;
++ const int ioprio_class = cfqq->ioprio_class;
++ const int ioprio = cfqq->ioprio;
+
+- if (current->flags & PF_MEMALLOC)
+- return ELV_MQUEUE_MAY;
++ if (has_fs_excl()) {
++ /*
++ * boost idle prio on transactions that would lock out other
++ * users of the filesystem
++ */
++ if (cfq_class_idle(cfqq))
++ cfqq->ioprio_class = IOPRIO_CLASS_BE;
++ if (cfqq->ioprio > IOPRIO_NORM)
++ cfqq->ioprio = IOPRIO_NORM;
++ } else {
++ /*
++ * check if we need to unboost the queue
++ */
++ if (cfqq->ioprio_class != cfqq->org_ioprio_class)
++ cfqq->ioprio_class = cfqq->org_ioprio_class;
++ if (cfqq->ioprio != cfqq->org_ioprio)
++ cfqq->ioprio = cfqq->org_ioprio;
++ }
+
+- cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current));
+- if (cfqq) {
+- int limit = cfqd->max_queued;
++ /*
++ * refile between round-robin lists if we moved the priority class
++ */
++ if ((ioprio_class != cfqq->ioprio_class || ioprio != cfqq->ioprio) &&
++ cfq_cfqq_on_rr(cfqq))
++ cfq_resort_rr_list(cfqq, 0);
++}
+
+- if (cfqq->allocated[rw] < cfqd->cfq_queued)
+- return ELV_MQUEUE_MUST;
++static inline pid_t cfq_queue_pid(struct task_struct *task, int rw)
++{
++ if (rw == READ || process_sync(task))
++ return task->pid;
++
++ return CFQ_KEY_ASYNC;
++}
+
+- if (cfqd->busy_queues)
+- limit = q->nr_requests / cfqd->busy_queues;
++static inline int
++__cfq_may_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq,
++ struct task_struct *task, int rw)
++{
++#if 1
++ if ((cfq_cfqq_wait_request(cfqq) || cfq_cfqq_must_alloc(cfqq)) &&
++ !cfq_cfqq_must_alloc_slice(cfqq)) {
++ cfq_mark_cfqq_must_alloc_slice(cfqq);
++ return ELV_MQUEUE_MUST;
++ }
+
+- 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;
++ return ELV_MQUEUE_MAY;
++#else
++ if (!cfqq || task->flags & PF_MEMALLOC)
++ return ELV_MQUEUE_MAY;
++ if (!cfqq->allocated[rw] || cfq_cfqq_must_alloc(cfqq)) {
++ if (cfq_cfqq_wait_request(cfqq))
++ return ELV_MQUEUE_MUST;
+
+- ret = ELV_MQUEUE_NO;
++ /*
++ * only allow 1 ELV_MQUEUE_MUST per slice, otherwise we
++ * can quickly flood the queue with writes from a single task
++ */
++ if (rw == READ || !cfq_cfqq_must_alloc_slice(cfqq)) {
++ cfq_mark_cfqq_must_alloc_slice(cfqq);
++ return ELV_MQUEUE_MUST;
+ }
++
++ return ELV_MQUEUE_MAY;
+ }
++ if (cfq_class_idle(cfqq))
++ return ELV_MQUEUE_NO;
++ if (cfqq->allocated[rw] >= cfqd->max_queued) {
++ struct io_context *ioc = get_io_context(GFP_ATOMIC);
++ int ret = ELV_MQUEUE_NO;
+
+- return ret;
++ if (ioc && ioc->nr_batch_requests)
++ ret = ELV_MQUEUE_MAY;
++
++ put_io_context(ioc);
++ return ret;
++ }
++
++ return ELV_MQUEUE_MAY;
++#endif
++}
++
++static int cfq_may_queue(request_queue_t *q, int rw, struct bio *bio)
++{
++ struct cfq_data *cfqd = q->elevator->elevator_data;
++ struct task_struct *tsk = current;
++ struct cfq_queue *cfqq;
++
++ /*
++ * don't force setup of a queue from here, as a call to may_queue
++ * does not necessarily imply that a request actually will be queued.
++ * so just lookup a possibly existing queue, or return 'may queue'
++ * if that fails
++ */
++ cfqq = cfq_find_cfq_hash(cfqd, cfq_queue_pid(tsk, rw), tsk->ioprio);
++ if (cfqq) {
++ cfq_init_prio_data(cfqq);
++ cfq_prio_boost(cfqq);
++
++ return __cfq_may_queue(cfqd, cfqq, tsk, rw);
++ }
++
++ return ELV_MQUEUE_MAY;
+ }
+
+ static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq)
+ {
++ struct cfq_data *cfqd = q->elevator->elevator_data;
+ struct request_list *rl = &q->rq;
+- const int write = waitqueue_active(&rl->wait[WRITE]);
+- const int read = waitqueue_active(&rl->wait[READ]);
+
+- if (read && cfqq->allocated[READ] < cfqq->alloc_limit[READ])
+- wake_up(&rl->wait[READ]);
+- if (write && cfqq->allocated[WRITE] < cfqq->alloc_limit[WRITE])
+- wake_up(&rl->wait[WRITE]);
++ if (cfqq->allocated[READ] <= cfqd->max_queued || cfqd->rq_starved) {
++ smp_mb();
++ if (waitqueue_active(&rl->wait[READ]))
++ wake_up(&rl->wait[READ]);
++ }
++
++ if (cfqq->allocated[WRITE] <= cfqd->max_queued || cfqd->rq_starved) {
++ smp_mb();
++ if (waitqueue_active(&rl->wait[WRITE]))
++ wake_up(&rl->wait[WRITE]);
++ }
+ }
+
+ /*
+@@ -1389,69 +2055,61 @@
+
+ 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;
+
+- smp_mb();
+ cfq_check_waiters(q, cfqq);
+ cfq_put_queue(cfqq);
+ }
+ }
+
+ /*
+- * Allocate cfq data structures associated with this request. A queue and
++ * Allocate cfq data structures associated with this request.
+ */
+-static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
++static int
++cfq_set_request(request_queue_t *q, struct request *rq, struct bio *bio,
++ int gfp_mask)
+ {
+ struct cfq_data *cfqd = q->elevator->elevator_data;
++ struct task_struct *tsk = current;
+ struct cfq_io_context *cic;
+ const int rw = rq_data_dir(rq);
+- struct cfq_queue *cfqq, *saved_cfqq;
++ pid_t key = cfq_queue_pid(tsk, rw);
++ struct cfq_queue *cfqq;
+ struct cfq_rq *crq;
+ unsigned long flags;
+
+ might_sleep_if(gfp_mask & __GFP_WAIT);
+
++ cic = cfq_get_io_context(cfqd, key, gfp_mask);
++
+ spin_lock_irqsave(q->queue_lock, flags);
+
+- cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask);
+- if (!cfqq)
+- goto out_lock;
++ if (!cic)
++ goto queue_fail;
+
+-repeat:
+- if (cfqq->allocated[rw] >= cfqd->max_queued)
+- goto out_lock;
++ if (!cic->cfqq) {
++ cfqq = cfq_get_queue(cfqd, key, tsk->ioprio, gfp_mask);
++ if (!cfqq)
++ goto queue_fail;
++
++ cic->cfqq = cfqq;
++ } else
++ cfqq = cic->cfqq;
+
+ cfqq->allocated[rw]++;
++ cfq_clear_cfqq_must_alloc(cfqq);
++ cfqd->rq_starved = 0;
++ atomic_inc(&cfqq->ref);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+- /*
+- * if hashing type has changed, the cfq_queue might change here.
+- */
+- saved_cfqq = cfqq;
+- cic = cfq_get_io_context(&cfqq, gfp_mask);
+- if (!cic)
+- goto err;
+-
+- /*
+- * repeat allocation checks on queue change
+- */
+- if (unlikely(saved_cfqq != cfqq)) {
+- spin_lock_irqsave(q->queue_lock, flags);
+- saved_cfqq->allocated[rw]--;
+- goto repeat;
+- }
+-
+ crq = mempool_alloc(cfqd->crq_pool, gfp_mask);
+ if (crq) {
+ RB_CLEAR(&crq->rb_node);
+@@ -1460,24 +2118,141 @@
+ INIT_HLIST_NODE(&crq->hash);
+ 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;
++ cfq_clear_crq_in_flight(crq);
++ cfq_clear_crq_in_driver(crq);
++ cfq_clear_crq_requeued(crq);
++
++ if (rw == READ || process_sync(tsk))
++ cfq_mark_crq_is_sync(crq);
++ else
++ cfq_clear_crq_is_sync(crq);
++
+ rq->elevator_private = crq;
+- cfqq->alloc_limit[rw] = 0;
+ return 0;
+ }
+
+- put_io_context(cic->ioc);
+-err:
+ spin_lock_irqsave(q->queue_lock, flags);
+ cfqq->allocated[rw]--;
++ if (!(cfqq->allocated[0] + cfqq->allocated[1]))
++ cfq_mark_cfqq_must_alloc(cfqq);
+ cfq_put_queue(cfqq);
+-out_lock:
++queue_fail:
++ if (cic)
++ put_io_context(cic->ioc);
++ /*
++ * mark us rq allocation starved. we need to kickstart the process
++ * ourselves if there are no pending requests that can do it for us.
++ * that would be an extremely rare OOM situation
++ */
++ cfqd->rq_starved = 1;
++ cfq_schedule_dispatch(cfqd);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ return 1;
+ }
+
++static void cfq_kick_queue(void *data)
++{
++ request_queue_t *q = data;
++ struct cfq_data *cfqd = q->elevator->elevator_data;
++ unsigned long flags;
++
++ spin_lock_irqsave(q->queue_lock, flags);
++
++ if (cfqd->rq_starved) {
++ struct request_list *rl = &q->rq;
++
++ /*
++ * we aren't guaranteed to get a request after this, but we
++ * have to be opportunistic
++ */
++ smp_mb();
++ if (waitqueue_active(&rl->wait[READ]))
++ wake_up(&rl->wait[READ]);
++ if (waitqueue_active(&rl->wait[WRITE]))
++ wake_up(&rl->wait[WRITE]);
++ }
++
++ blk_remove_plug(q);
++ q->request_fn(q);
++ spin_unlock_irqrestore(q->queue_lock, flags);
++}
++
++/*
++ * Timer running if the active_queue is currently idling inside its time slice
++ */
++static void cfq_idle_slice_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) {
++ unsigned long now = jiffies;
++
++ /*
++ * expired
++ */
++ if (time_after(now, cfqq->slice_end))
++ goto expire;
++
++ /*
++ * only expire and reinvoke request handler, if there are
++ * other queues with pending requests
++ */
++ if (!cfq_pending_requests(cfqd)) {
++ cfqd->idle_slice_timer.expires = min(now + cfqd->cfq_slice_idle, cfqq->slice_end);
++ add_timer(&cfqd->idle_slice_timer);
++ goto out_cont;
++ }
++
++ /*
++ * not expired and it has a request pending, let it dispatch
++ */
++ if (!RB_EMPTY(&cfqq->sort_list)) {
++ cfq_mark_cfqq_must_dispatch(cfqq);
++ goto out_kick;
++ }
++ }
++expire:
++ cfq_slice_expired(cfqd, 0);
++out_kick:
++ cfq_schedule_dispatch(cfqd);
++out_cont:
++ spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
++}
++
++/*
++ * Timer running if an idle class queue is waiting for service
++ */
++static void cfq_idle_class_timer(unsigned long data)
++{
++ struct cfq_data *cfqd = (struct cfq_data *) data;
++ unsigned long flags, end;
++
++ spin_lock_irqsave(cfqd->queue->queue_lock, flags);
++
++ /*
++ * race with a non-idle queue, reset timer
++ */
++ end = cfqd->last_end_request + CFQ_IDLE_GRACE;
++ if (!time_after_eq(jiffies, end)) {
++ cfqd->idle_class_timer.expires = end;
++ add_timer(&cfqd->idle_class_timer);
++ } else
++ cfq_schedule_dispatch(cfqd);
++
++ spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
++}
++
++static void cfq_shutdown_timer_wq(struct cfq_data *cfqd)
++{
++ del_timer_sync(&cfqd->idle_slice_timer);
++ del_timer_sync(&cfqd->idle_class_timer);
++ blk_sync_queue(cfqd->queue);
++}
++
+ static void cfq_put_cfqd(struct cfq_data *cfqd)
+ {
+ request_queue_t *q = cfqd->queue;
+@@ -1485,6 +2260,7 @@
+ if (!atomic_dec_and_test(&cfqd->ref))
+ return;
+
++ cfq_shutdown_timer_wq(cfqd);
+ blk_put_queue(q);
+
+ mempool_destroy(cfqd->crq_pool);
+@@ -1495,7 +2271,10 @@
+
+ static void cfq_exit_queue(elevator_t *e)
+ {
+- cfq_put_cfqd(e->elevator_data);
++ struct cfq_data *cfqd = e->elevator_data;
++
++ cfq_shutdown_timer_wq(cfqd);
++ cfq_put_cfqd(cfqd);
+ }
+
+ static int cfq_init_queue(request_queue_t *q, elevator_t *e)
+@@ -1508,7 +2287,13 @@
+ return -ENOMEM;
+
+ memset(cfqd, 0, sizeof(*cfqd));
+- INIT_LIST_HEAD(&cfqd->rr_list);
++
++ for (i = 0; i < CFQ_PRIO_LISTS; i++)
++ INIT_LIST_HEAD(&cfqd->rr_list[i]);
++
++ INIT_LIST_HEAD(&cfqd->busy_rr);
++ INIT_LIST_HEAD(&cfqd->cur_rr);
++ INIT_LIST_HEAD(&cfqd->idle_rr);
+ INIT_LIST_HEAD(&cfqd->empty_list);
+
+ cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL);
+@@ -1533,24 +2318,32 @@
+ cfqd->queue = q;
+ atomic_inc(&q->refcnt);
+
+- /*
+- * 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;
++
++ init_timer(&cfqd->idle_slice_timer);
++ cfqd->idle_slice_timer.function = cfq_idle_slice_timer;
++ cfqd->idle_slice_timer.data = (unsigned long) cfqd;
++
++ init_timer(&cfqd->idle_class_timer);
++ cfqd->idle_class_timer.function = cfq_idle_class_timer;
++ cfqd->idle_class_timer.data = (unsigned long) cfqd;
++
++ INIT_WORK(&cfqd->unplug_work, cfq_kick_queue, q);
++
+ atomic_set(&cfqd->ref, 1);
+
+ cfqd->cfq_queued = cfq_queued;
+ cfqd->cfq_quantum = cfq_quantum;
+- cfqd->cfq_fifo_expire_r = cfq_fifo_expire_r;
+- cfqd->cfq_fifo_expire_w = cfq_fifo_expire_w;
+- cfqd->cfq_fifo_batch_expire = cfq_fifo_rate;
++ cfqd->cfq_fifo_expire[0] = cfq_fifo_expire[0];
++ cfqd->cfq_fifo_expire[1] = cfq_fifo_expire[1];
+ 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_crqpool:
+@@ -1595,7 +2388,6 @@
+ return -ENOMEM;
+ }
+
+-
+ /*
+ * sysfs parts below -->
+ */
+@@ -1620,45 +2412,6 @@
+ return count;
+ }
+
+-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);
+- if (!strncmp(page, "pgid", 4))
+- cfqd->key_type = CFQ_KEY_PGID;
+- else if (!strncmp(page, "tgid", 4))
+- cfqd->key_type = CFQ_KEY_TGID;
+- else if (!strncmp(page, "uid", 3))
+- cfqd->key_type = CFQ_KEY_UID;
+- else if (!strncmp(page, "gid", 3))
+- cfqd->key_type = CFQ_KEY_GID;
+- spin_unlock_irq(cfqd->queue->queue_lock);
+- return count;
+-}
+-
+-static ssize_t
+-cfq_read_key_type(struct cfq_data *cfqd, char *page)
+-{
+- ssize_t len = 0;
+- int i;
+-
+- for (i = CFQ_KEY_PGID; i < CFQ_KEY_LAST; i++) {
+- if (cfqd->key_type == i)
+- len += sprintf(page+len, "[%s] ", cfq_key_types[i]);
+- else
+- len += sprintf(page+len, "%s ", cfq_key_types[i]);
+- }
+- len += sprintf(page+len, "\n");
+- return len;
+-}
+-
+ #define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
+ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \
+ { \
+@@ -1669,12 +2422,15 @@
+ }
+ SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum, 0);
+ SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued, 0);
+-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_fifo_expire_sync_show, cfqd->cfq_fifo_expire[1], 1);
++SHOW_FUNCTION(cfq_fifo_expire_async_show, cfqd->cfq_fifo_expire[0], 1);
+ 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) \
+@@ -1694,12 +2450,15 @@
+ }
+ STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX, 0);
+ STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX, 0);
+-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_fifo_expire_sync_store, &cfqd->cfq_fifo_expire[1], 1, UINT_MAX, 1);
++STORE_FUNCTION(cfq_fifo_expire_async_store, &cfqd->cfq_fifo_expire[0], 1, UINT_MAX, 1);
+ 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 = {
+@@ -1712,25 +2471,15 @@
+ .show = cfq_queued_show,
+ .store = cfq_queued_store,
+ };
+-static struct cfq_fs_entry cfq_fifo_expire_r_entry = {
++static struct cfq_fs_entry cfq_fifo_expire_sync_entry = {
+ .attr = {.name = "fifo_expire_sync", .mode = S_IRUGO | S_IWUSR },
+- .show = cfq_fifo_expire_r_show,
+- .store = cfq_fifo_expire_r_store,
++ .show = cfq_fifo_expire_sync_show,
++ .store = cfq_fifo_expire_sync_store,
+ };
+-static struct cfq_fs_entry cfq_fifo_expire_w_entry = {
++static struct cfq_fs_entry cfq_fifo_expire_async_entry = {
+ .attr = {.name = "fifo_expire_async", .mode = S_IRUGO | S_IWUSR },
+- .show = cfq_fifo_expire_w_show,
+- .store = cfq_fifo_expire_w_store,
+-};
+-static struct cfq_fs_entry cfq_fifo_batch_expire_entry = {
+- .attr = {.name = "fifo_batch_expire", .mode = S_IRUGO | S_IWUSR },
+- .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,
++ .show = cfq_fifo_expire_async_show,
++ .store = cfq_fifo_expire_async_store,
+ };
+ static struct cfq_fs_entry cfq_back_max_entry = {
+ .attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR },
+@@ -1742,27 +2491,44 @@
+ .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_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_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_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,
+ &cfq_queued_entry.attr,
+- &cfq_fifo_expire_r_entry.attr,
+- &cfq_fifo_expire_w_entry.attr,
+- &cfq_fifo_batch_expire_entry.attr,
+- &cfq_key_type_entry.attr,
+- &cfq_find_best_entry.attr,
++ &cfq_fifo_expire_sync_entry.attr,
++ &cfq_fifo_expire_async_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,
+ };
+
+@@ -1832,21 +2598,46 @@
+ {
+ 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 -Naur 2.6.12-5.0-org/drivers/block/deadline-iosched.c 2.6.12-5.0-patched/drivers/block/deadline-iosched.c
+--- 2.6.12-5.0-org/drivers/block/deadline-iosched.c 2007-07-26 00:53:20.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/block/deadline-iosched.c 2007-12-11 12:34:52.000000000 +0100
+@@ -758,7 +758,8 @@
+ }
+
+ static int
+-deadline_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
++deadline_set_request(request_queue_t *q, struct request *rq, struct bio *bio,
++ int gfp_mask)
+ {
+ struct deadline_data *dd = q->elevator->elevator_data;
+ struct deadline_rq *drq;
+diff -Naur 2.6.12-5.0-org/drivers/block/elevator.c 2.6.12-5.0-patched/drivers/block/elevator.c
+--- 2.6.12-5.0-org/drivers/block/elevator.c 2007-07-26 00:53:20.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/block/elevator.c 2007-12-11 12:34:52.000000000 +0100
+@@ -158,27 +158,6 @@
+
+ static char chosen_elevator[16];
+
+-static void elevator_setup_default(void)
+-{
+- /*
+- * check if default is set and exists
+- */
+- if (chosen_elevator[0] && elevator_find(chosen_elevator))
+- return;
+-
+-#if defined(CONFIG_IOSCHED_AS)
+- strcpy(chosen_elevator, "anticipatory");
+-#elif defined(CONFIG_IOSCHED_DEADLINE)
+- strcpy(chosen_elevator, "deadline");
+-#elif defined(CONFIG_IOSCHED_CFQ)
+- strcpy(chosen_elevator, "cfq");
+-#elif defined(CONFIG_IOSCHED_NOOP)
+- strcpy(chosen_elevator, "noop");
+-#else
+-#error "You must build at least 1 IO scheduler into the kernel"
+-#endif
+-}
+-
+ static int __init elevator_setup(char *str)
+ {
+ strncpy(chosen_elevator, str, sizeof(chosen_elevator) - 1);
+@@ -193,15 +172,17 @@
+ struct elevator_queue *eq;
+ int ret = 0;
+
+- elevator_setup_default();
+-
+- if (!name)
+- name = chosen_elevator;
+-
+- e = elevator_get(name);
+- if (!e)
++ if (name && !(e = elevator_get(name)))
+ return -EINVAL;
+-
++
++ if (!e && *chosen_elevator && !(e = elevator_get(chosen_elevator)))
++ printk("I/O scheduler %s not found\n", chosen_elevator);
++
++ if (!e && !(e = elevator_get(CONFIG_DEFAULT_IOSCHED))) {
++ printk("Default I/O scheduler not found, using no+op\n");
++ e = elevator_get("noop");
++ }
++
+ eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL);
+ if (!eq) {
+ elevator_put(e->elevator_type);
+@@ -480,12 +461,13 @@
+ return NULL;
+ }
+
+-int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
++int elv_set_request(request_queue_t *q, struct request *rq, struct bio *bio,
++ int gfp_mask)
+ {
+ elevator_t *e = q->elevator;
+
+ if (e->ops->elevator_set_req_fn)
+- return e->ops->elevator_set_req_fn(q, rq, gfp_mask);
++ return e->ops->elevator_set_req_fn(q, rq, bio, gfp_mask);
+
+ rq->elevator_private = NULL;
+ return 0;
+@@ -499,12 +481,12 @@
+ e->ops->elevator_put_req_fn(q, rq);
+ }
+
+-int elv_may_queue(request_queue_t *q, int rw)
++int elv_may_queue(request_queue_t *q, int rw, struct bio *bio)
+ {
+ elevator_t *e = q->elevator;
+
+ if (e->ops->elevator_may_queue_fn)
+- return e->ops->elevator_may_queue_fn(q, rw);
++ return e->ops->elevator_may_queue_fn(q, rw, bio);
+
+ return ELV_MQUEUE_MAY;
+ }
+diff -Naur 2.6.12-5.0-org/drivers/block/Kconfig.iosched 2.6.12-5.0-patched/drivers/block/Kconfig.iosched
+--- 2.6.12-5.0-org/drivers/block/Kconfig.iosched 2007-07-26 00:53:20.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/block/Kconfig.iosched 2007-12-11 12:34:52.000000000 +0100
+@@ -38,4 +38,32 @@
+ among all processes in the system. It should provide a fair
+ working environment, suitable for desktop systems.
+
++choice
++ prompt "Default I/O scheduler"
++ default DEFAULT_AS
++ help
++ Select the I/O scheduler which will be used by default for all
++ block devices.
++
++ config DEFAULT_AS
++ bool "Anticipatory" if IOSCHED_AS=y
++
++ config DEFAULT_DEADLINE
++ bool "Deadline" if IOSCHED_DEADLINE=y
++
++ config DEFAULT_CFQ
++ bool "CFQ" if IOSCHED_CFQ=y
++
++ config DEFAULT_NOOP
++ bool "No-op"
++
++endchoice
++
++config DEFAULT_IOSCHED
++ string
++ default "anticipatory" if DEFAULT_AS
++ default "deadline" if DEFAULT_DEADLINE
++ default "cfq" if DEFAULT_CFQ
++ default "noop" if DEFAULT_NOOP
++
+ endmenu
+diff -Naur 2.6.12-5.0-org/drivers/block/ll_rw_blk.c 2.6.12-5.0-patched/drivers/block/ll_rw_blk.c
+--- 2.6.12-5.0-org/drivers/block/ll_rw_blk.c 2007-07-26 00:53:21.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/block/ll_rw_blk.c 2007-12-11 12:37:54.000000000 +0100
+@@ -287,6 +287,7 @@
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->bio = rq->biotail = NULL;
++ rq->ioprio = 0;
+ rq->buffer = NULL;
+ rq->ref_count = 1;
+ rq->q = q;
+@@ -1522,11 +1523,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);
+
+@@ -1841,8 +1838,8 @@
+ mempool_free(rq, q->rq.rq_pool);
+ }
+
+-static inline struct request *blk_alloc_request(request_queue_t *q, int rw,
+- int gfp_mask)
++static inline struct request *
++blk_alloc_request(request_queue_t *q, int rw, struct bio *bio, int gfp_mask)
+ {
+ struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
+
+@@ -1855,7 +1852,7 @@
+ */
+ rq->flags = rw;
+
+- if (!elv_set_request(q, rq, gfp_mask))
++ if (!elv_set_request(q, rq, bio, gfp_mask))
+ return rq;
+
+ mempool_free(rq, q->rq.rq_pool);
+@@ -1938,7 +1935,8 @@
+ /*
+ * Get a free request, queue_lock must not be held
+ */
+-static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
++static struct request *get_request(request_queue_t *q, int rw, struct bio *bio,
++ int gfp_mask)
+ {
+ struct request *rq = NULL;
+ struct request_list *rl = &q->rq;
+@@ -1961,7 +1959,7 @@
+ }
+ }
+
+- switch (elv_may_queue(q, rw)) {
++ switch (elv_may_queue(q, rw, bio)) {
+ case ELV_MQUEUE_NO:
+ goto rq_starved;
+ case ELV_MQUEUE_MAY:
+@@ -1986,7 +1984,7 @@
+ set_queue_congested(q, rw);
+ spin_unlock_irq(q->queue_lock);
+
+- rq = blk_alloc_request(q, rw, gfp_mask);
++ rq = blk_alloc_request(q, rw, bio, gfp_mask);
+ if (!rq) {
+ /*
+ * Allocation failed presumably due to memory. Undo anything
+@@ -2027,7 +2025,8 @@
+ * No available requests for this queue, unplug the device and wait for some
+ * requests to become available.
+ */
+-static struct request *get_request_wait(request_queue_t *q, int rw)
++static struct request *get_request_wait(request_queue_t *q, int rw,
++ struct bio *bio)
+ {
+ DEFINE_WAIT(wait);
+ struct request *rq;
+@@ -2039,7 +2038,7 @@
+ prepare_to_wait_exclusive(&rl->wait[rw], &wait,
+ TASK_UNINTERRUPTIBLE);
+
+- rq = get_request(q, rw, GFP_NOIO);
++ rq = get_request(q, rw, bio, GFP_NOIO);
+
+ if (!rq) {
+ struct io_context *ioc;
+@@ -2069,9 +2068,9 @@
+ BUG_ON(rw != READ && rw != WRITE);
+
+ if (gfp_mask & __GFP_WAIT)
+- rq = get_request_wait(q, rw);
++ rq = get_request_wait(q, rw, NULL);
+ else
+- rq = get_request(q, rw, gfp_mask);
++ rq = get_request(q, rw, NULL, gfp_mask);
+
+ return rq;
+ }
+@@ -2445,7 +2444,6 @@
+ return;
+
+ req->rq_status = RQ_INACTIVE;
+- req->q = NULL;
+ req->rl = NULL;
+
+ /*
+@@ -2583,6 +2581,8 @@
+ req->rq_disk->in_flight--;
+ }
+
++ req->ioprio = ioprio_best(req->ioprio, next->ioprio);
++
+ __blk_put_request(q, next);
+ return 1;
+ }
+@@ -2645,11 +2645,13 @@
+ {
+ struct request *req, *freereq = NULL;
+ int el_ret, rw, nr_sectors, cur_nr_sectors, barrier, err, sync;
++ unsigned short prio;
+ sector_t sector;
+
+ sector = bio->bi_sector;
+ nr_sectors = bio_sectors(bio);
+ cur_nr_sectors = bio_cur_sectors(bio);
++ prio = bio_prio(bio);
+
+ rw = bio_data_dir(bio);
+ sync = bio_sync(bio);
+@@ -2696,6 +2698,7 @@
+ set_bit(__REQ_DIRECTIO, &req->flags);
+ }
+ #endif
++ req->ioprio = ioprio_best(req->ioprio, prio);
+ drive_stat_acct(req, nr_sectors, 0);
+ if (!attempt_back_merge(q, req))
+ elv_merged_request(q, req);
+@@ -2726,6 +2729,7 @@
+ set_bit(__REQ_DIRECTIO, &req->flags);
+ }
+ #endif
++ req->ioprio = ioprio_best(req->ioprio, prio);
+ drive_stat_acct(req, nr_sectors, 0);
+ if (!attempt_front_merge(q, req))
+ elv_merged_request(q, req);
+@@ -2753,7 +2757,7 @@
+ freereq = NULL;
+ } else {
+ spin_unlock_irq(q->queue_lock);
+- if ((freereq = get_request(q, rw, GFP_ATOMIC)) == NULL) {
++ if ((freereq = get_request(q, rw, bio, GFP_ATOMIC)) == NULL) {
+ /*
+ * READA bit set
+ */
+@@ -2761,7 +2765,7 @@
+ if (bio_rw_ahead(bio))
+ goto end_io;
+
+- freereq = get_request_wait(q, rw);
++ freereq = get_request_wait(q, rw, bio);
+ }
+ goto again;
+ }
+@@ -2789,6 +2793,7 @@
+ req->buffer = bio_data(bio); /* see ->buffer comment above */
+ req->waiting = NULL;
+ req->bio = req->biotail = bio;
++ req->ioprio = prio;
+ req->rq_disk = bio->bi_bdev->bd_disk;
+ req->start_time = jiffies;
+ #if defined (CONFIG_MIPS_BCM7440)
+@@ -2821,7 +2826,7 @@
+ if (bdev != bdev->bd_contains) {
+ struct hd_struct *p = bdev->bd_part;
+
+- switch (bio->bi_rw) {
++ switch (bio_data_dir(bio)) {
+ case READ:
+ p->read_sectors += bio_sectors(bio);
+ p->reads++;
+@@ -2840,6 +2845,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);
+@@ -2848,9 +2854,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]);
+@@ -3056,7 +3066,7 @@
+
+ BIO_BUG_ON(!bio->bi_size);
+ BIO_BUG_ON(!bio->bi_io_vec);
+- bio->bi_rw = rw;
++ bio->bi_rw |= rw;
+ if (rw & WRITE)
+ mod_page_state(pgpgout, count);
+ else
+@@ -3418,8 +3428,11 @@
+ struct io_context *ioc;
+
+ local_irq_save(flags);
++ task_lock(current);
+ ioc = current->io_context;
+ current->io_context = NULL;
++ ioc->task = NULL;
++ task_unlock(current);
+ local_irq_restore(flags);
+
+ if (ioc->aic && ioc->aic->exit)
+@@ -3454,12 +3467,12 @@
+ ret = kmem_cache_alloc(iocontext_cachep, gfp_flags);
+ if (ret) {
+ atomic_set(&ret->refcount, 1);
+- ret->pid = tsk->pid;
++ ret->task = current;
++ ret->set_ioprio = NULL;
+ 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);
+
+diff -Naur 2.6.12-5.0-org/fs/ioprio.c 2.6.12-5.0-patched/fs/ioprio.c
+--- 2.6.12-5.0-org/fs/ioprio.c 1970-01-01 01:00:00.000000000 +0100
++++ 2.6.12-5.0-patched/fs/ioprio.c 2007-12-11 12:34:52.000000000 +0100
+@@ -0,0 +1,172 @@
++/*
++ * fs/ioprio.c
++ *
++ * Copyright (C) 2004 Jens Axboe <axboe@suse.de>
++ *
++ * Helper functions for setting/querying io priorities of processes. The
++ * system calls closely mimmick getpriority/setpriority, see the man page for
++ * those. The prio argument is a composite of prio class and prio data, where
++ * the data argument has meaning within that class. The standard scheduling
++ * classes have 8 distinct prio levels, with 0 being the highest prio and 7
++ * being the lowest.
++ *
++ * IOW, setting BE scheduling class with prio 2 is done ala:
++ *
++ * unsigned int prio = (IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT) | 2;
++ *
++ * ioprio_set(PRIO_PROCESS, pid, prio);
++ *
++ * See also Documentation/block/ioprio.txt
++ *
++ */
++#include <linux/kernel.h>
++#include <linux/ioprio.h>
++#include <linux/blkdev.h>
++
++static int set_task_ioprio(struct task_struct *task, int ioprio)
++{
++ struct io_context *ioc;
++
++ if (task->uid != current->euid &&
++ task->uid != current->uid && !capable(CAP_SYS_NICE))
++ return -EPERM;
++
++ task_lock(task);
++
++ task->ioprio = ioprio;
++
++ ioc = task->io_context;
++ if (ioc && ioc->set_ioprio)
++ ioc->set_ioprio(ioc, ioprio);
++
++ task_unlock(task);
++ return 0;
++}
++
++asmlinkage int sys_ioprio_set(int which, int who, int ioprio)
++{
++ int class = IOPRIO_PRIO_CLASS(ioprio);
++ int data = IOPRIO_PRIO_DATA(ioprio);
++ struct task_struct *p, *g;
++ struct user_struct *user;
++ int ret;
++
++ switch (class) {
++ case IOPRIO_CLASS_RT:
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ /* fall through, rt has prio field too */
++ case IOPRIO_CLASS_BE:
++ if (data >= IOPRIO_BE_NR || data < 0)
++ return -EINVAL;
++
++ break;
++ case IOPRIO_CLASS_IDLE:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ ret = -ESRCH;
++ read_lock_irq(&tasklist_lock);
++ switch (which) {
++ case IOPRIO_WHO_PROCESS:
++ if (!who)
++ p = current;
++ else
++ p = find_task_by_pid(who);
++ if (p)
++ ret = set_task_ioprio(p, ioprio);
++ break;
++ case IOPRIO_WHO_PGRP:
++ if (!who)
++ who = process_group(current);
++ do_each_task_pid(who, PIDTYPE_PGID, p) {
++ ret = set_task_ioprio(p, ioprio);
++ if (ret)
++ break;
++ } while_each_task_pid(who, PIDTYPE_PGID, p);
++ break;
++ case IOPRIO_WHO_USER:
++ if (!who)
++ user = current->user;
++ else
++ user = find_user(who);
++
++ if (!user)
++ break;
++
++ do_each_thread(g, p) {
++ if (p->uid != who)
++ continue;
++ ret = set_task_ioprio(p, ioprio);
++ if (ret)
++ break;
++ } while_each_thread(g, p);
++
++ if (who)
++ free_uid(user);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ read_unlock_irq(&tasklist_lock);
++ return ret;
++}
++
++asmlinkage int sys_ioprio_get(int which, int who)
++{
++ struct task_struct *g, *p;
++ struct user_struct *user;
++ int ret = -ESRCH;
++
++ read_lock_irq(&tasklist_lock);
++ switch (which) {
++ case IOPRIO_WHO_PROCESS:
++ if (!who)
++ p = current;
++ else
++ p = find_task_by_pid(who);
++ if (p)
++ ret = p->ioprio;
++ break;
++ case IOPRIO_WHO_PGRP:
++ if (!who)
++ who = process_group(current);
++ do_each_task_pid(who, PIDTYPE_PGID, p) {
++ if (ret == -ESRCH)
++ ret = p->ioprio;
++ else
++ ret = ioprio_best(ret, p->ioprio);
++ } while_each_task_pid(who, PIDTYPE_PGID, p);
++ break;
++ case IOPRIO_WHO_USER:
++ if (!who)
++ user = current->user;
++ else
++ user = find_user(who);
++
++ if (!user)
++ break;
++
++ do_each_thread(g, p) {
++ if (p->uid != user->uid)
++ continue;
++ if (ret == -ESRCH)
++ ret = p->ioprio;
++ else
++ ret = ioprio_best(ret, p->ioprio);
++ } while_each_thread(g, p);
++
++ if (who)
++ free_uid(user);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ read_unlock_irq(&tasklist_lock);
++ return ret;
++}
++
+diff -Naur 2.6.12-5.0-org/fs/Makefile 2.6.12-5.0-patched/fs/Makefile
+--- 2.6.12-5.0-org/fs/Makefile 2007-07-26 00:55:01.000000000 +0200
++++ 2.6.12-5.0-patched/fs/Makefile 2007-12-11 12:34:52.000000000 +0100
+@@ -10,6 +10,7 @@
+ ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
+ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
+ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \
++ ioprio.o
+
+ obj-$(CONFIG_EPOLL) += eventpoll.o
+ obj-$(CONFIG_COMPAT) += compat.o
+diff -Naur 2.6.12-5.0-org/fs/reiserfs/journal.c 2.6.12-5.0-patched/fs/reiserfs/journal.c
+--- 2.6.12-5.0-org/fs/reiserfs/journal.c 2007-07-26 00:55:15.000000000 +0200
++++ 2.6.12-5.0-patched/fs/reiserfs/journal.c 2007-12-11 12:34:52.000000000 +0100
+@@ -645,18 +645,22 @@
+
+ static void write_chunk(struct buffer_chunk *chunk) {
+ int i;
++ get_fs_excl();
+ for (i = 0; i < chunk->nr ; i++) {
+ submit_logged_buffer(chunk->bh[i]) ;
+ }
+ chunk->nr = 0;
++ put_fs_excl();
+ }
+
+ static void write_ordered_chunk(struct buffer_chunk *chunk) {
+ int i;
++ get_fs_excl();
+ for (i = 0; i < chunk->nr ; i++) {
+ submit_ordered_buffer(chunk->bh[i]) ;
+ }
+ chunk->nr = 0;
++ put_fs_excl();
+ }
+
+ static int add_to_chunk(struct buffer_chunk *chunk, struct buffer_head *bh,
+@@ -918,6 +922,8 @@
+ return 0 ;
+ }
+
++ get_fs_excl();
++
+ /* before we can put our commit blocks on disk, we have to make sure everyone older than
+ ** us is on disk too
+ */
+@@ -1055,6 +1061,7 @@
+
+ if (retval)
+ reiserfs_abort (s, retval, "Journal write error in %s", __FUNCTION__);
++ put_fs_excl();
+ return retval;
+ }
+
+@@ -1251,6 +1258,8 @@
+ return 0 ;
+ }
+
++ get_fs_excl();
++
+ /* if all the work is already done, get out of here */
+ if (atomic_read(&(jl->j_nonzerolen)) <= 0 &&
+ atomic_read(&(jl->j_commit_left)) <= 0) {
+@@ -1450,6 +1459,7 @@
+ put_journal_list(s, jl);
+ if (flushall)
+ up(&journal->j_flush_sem);
++ put_fs_excl();
+ return err ;
+ }
+
+@@ -2717,6 +2727,7 @@
+ th->t_trans_id = journal->j_trans_id ;
+ unlock_journal(p_s_sb) ;
+ INIT_LIST_HEAD (&th->t_list);
++ get_fs_excl();
+ return 0 ;
+
+ out_fail:
+@@ -3524,6 +3535,7 @@
+ BUG_ON (th->t_refcount > 1);
+ BUG_ON (!th->t_trans_id);
+
++ put_fs_excl();
+ current->journal_info = th->t_handle_save;
+ reiserfs_check_lock_depth(p_s_sb, "journal end");
+ if (journal->j_len == 0) {
+diff -Naur 2.6.12-5.0-org/include/asm-mips/unistd.h 2.6.12-5.0-patched/include/asm-mips/unistd.h
+--- 2.6.12-5.0-org/include/asm-mips/unistd.h 2007-07-26 00:56:08.000000000 +0200
++++ 2.6.12-5.0-patched/include/asm-mips/unistd.h 2007-12-11 12:34:52.000000000 +0100
+@@ -304,16 +304,18 @@
+ #define __NR_request_key (__NR_Linux + 281)
+ #define __NR_keyctl (__NR_Linux + 282)
+ #define __NR_set_thread_area (__NR_Linux + 283)
++#define __NR_sys_ioprio_set (__NR_Linux + 284)
++#define __NR_sys_ioprio_get (__NR_Linux + 285)
+
+ /*
+ * Offset of the last Linux o32 flavoured syscall
+ */
+-#define __NR_Linux_syscalls 283
++#define __NR_Linux_syscalls 285
+
+ #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
+
+ #define __NR_O32_Linux 4000
+-#define __NR_O32_Linux_syscalls 283
++#define __NR_O32_Linux_syscalls 285
+
+ #if _MIPS_SIM == _MIPS_SIM_ABI64
+
+diff -Naur 2.6.12-5.0-org/include/linux/bio.h 2.6.12-5.0-patched/include/linux/bio.h
+--- 2.6.12-5.0-org/include/linux/bio.h 2007-07-26 00:57:02.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/bio.h 2007-12-11 12:34:52.000000000 +0100
+@@ -22,6 +22,7 @@
+
+ #include <linux/highmem.h>
+ #include <linux/mempool.h>
++#include <linux/ioprio.h>
+
+ /* Platforms may set this to teach the BIO layer about IOMMU hardware. */
+ #include <asm/io.h>
+@@ -153,6 +154,19 @@
+ #define BIO_RW_SYNC 4
+
+ /*
++ * upper 16 bits of bi_rw define the io priority of this bio
++ */
++#define BIO_PRIO_SHIFT (8 * sizeof(unsigned long) - IOPRIO_BITS)
++#define bio_prio(bio) ((bio)->bi_rw >> BIO_PRIO_SHIFT)
++#define bio_prio_valid(bio) ioprio_valid(bio_prio(bio))
++
++#define bio_set_prio(bio, prio) do { \
++ WARN_ON(prio >= (1 << IOPRIO_BITS)); \
++ (bio)->bi_rw &= ((1UL << BIO_PRIO_SHIFT) - 1); \
++ (bio)->bi_rw |= ((unsigned long) (prio) << BIO_PRIO_SHIFT); \
++} while (0)
++
++/*
+ * various member access, note that bio_data should of course not be used
+ * on highmem page vectors
+ */
+diff -Naur 2.6.12-5.0-org/include/linux/blkdev.h 2.6.12-5.0-patched/include/linux/blkdev.h
+--- 2.6.12-5.0-org/include/linux/blkdev.h 2007-07-26 00:57:02.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/blkdev.h 2007-12-11 12:34:52.000000000 +0100
+@@ -54,16 +54,23 @@
+
+ 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;
++ void *key;
++
++ 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;
++
++ void (*dtor)(struct cfq_io_context *);
++ void (*exit)(struct cfq_io_context *);
+ };
+
+ /*
+@@ -73,7 +80,9 @@
+ */
+ struct io_context {
+ atomic_t refcount;
+- pid_t pid;
++ struct task_struct *task;
++
++ int (*set_ioprio)(struct io_context *, unsigned int);
+
+ /*
+ * For request batching
+@@ -81,8 +90,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;
+ };
+@@ -134,6 +141,8 @@
+
+ void *elevator_private;
+
++ unsigned short ioprio;
++
+ int rq_status; /* should split this into a few status bits */
+ struct gendisk *rq_disk;
+ int errors;
+diff -Naur 2.6.12-5.0-org/include/linux/elevator.h 2.6.12-5.0-patched/include/linux/elevator.h
+--- 2.6.12-5.0-org/include/linux/elevator.h 2007-07-26 00:56:58.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/elevator.h 2007-12-11 12:34:52.000000000 +0100
+@@ -16,9 +16,9 @@
+ typedef void (elevator_requeue_req_fn) (request_queue_t *, struct request *);
+ typedef struct request *(elevator_request_list_fn) (request_queue_t *, struct request *);
+ typedef void (elevator_completed_req_fn) (request_queue_t *, struct request *);
+-typedef int (elevator_may_queue_fn) (request_queue_t *, int);
++typedef int (elevator_may_queue_fn) (request_queue_t *, int, struct bio *);
+
+-typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, int);
++typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, struct bio *, int);
+ typedef void (elevator_put_req_fn) (request_queue_t *, struct request *);
+ typedef void (elevator_deactivate_req_fn) (request_queue_t *, struct request *);
+
+@@ -96,9 +96,9 @@
+ extern struct request *elv_latter_request(request_queue_t *, struct request *);
+ extern int elv_register_queue(request_queue_t *q);
+ extern void elv_unregister_queue(request_queue_t *q);
+-extern int elv_may_queue(request_queue_t *, int);
++extern int elv_may_queue(request_queue_t *, int, struct bio *);
+ extern void elv_completed_request(request_queue_t *, struct request *);
+-extern int elv_set_request(request_queue_t *, struct request *, int);
++extern int elv_set_request(request_queue_t *, struct request *, struct bio *, int);
+ extern void elv_put_request(request_queue_t *, struct request *);
+
+ /*
+diff -Naur 2.6.12-5.0-org/include/linux/fs.h 2.6.12-5.0-patched/include/linux/fs.h
+--- 2.6.12-5.0-org/include/linux/fs.h 2007-07-26 00:57:01.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/fs.h 2007-12-11 12:34:52.000000000 +0100
+@@ -213,6 +213,7 @@
+ #include <linux/radix-tree.h>
+ #include <linux/prio_tree.h>
+ #include <linux/init.h>
++#include <linux/sched.h>
+
+ #include <asm/atomic.h>
+ #include <asm/semaphore.h>
+@@ -820,16 +821,34 @@
+ #define vfs_check_frozen(sb, level) \
+ wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level)))
+
++static inline void get_fs_excl(void)
++{
++ atomic_inc(&current->fs_excl);
++}
++
++static inline void put_fs_excl(void)
++{
++ atomic_dec(&current->fs_excl);
++}
++
++static inline int has_fs_excl(void)
++{
++ return atomic_read(&current->fs_excl);
++}
++
++
+ /*
+ * Superblock locking.
+ */
+ static inline void lock_super(struct super_block * sb)
+ {
++ get_fs_excl();
+ down(&sb->s_lock);
+ }
+
+ static inline void unlock_super(struct super_block * sb)
+ {
++ put_fs_excl();
+ up(&sb->s_lock);
+ }
+
+diff -Naur 2.6.12-5.0-org/include/linux/init_task.h 2.6.12-5.0-patched/include/linux/init_task.h
+--- 2.6.12-5.0-org/include/linux/init_task.h 2007-07-26 00:56:58.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/init_task.h 2007-12-11 12:34:52.000000000 +0100
+@@ -81,6 +81,7 @@
+ .mm = NULL, \
+ .active_mm = &init_mm, \
+ .run_list = LIST_HEAD_INIT(tsk.run_list), \
++ .ioprio = 0, \
+ .time_slice = HZ, \
+ .tasks = LIST_HEAD_INIT(tsk.tasks), \
+ .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \
+@@ -111,6 +112,7 @@
+ .switch_lock = SPIN_LOCK_UNLOCKED, \
+ .journal_info = NULL, \
+ .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \
++ .fs_excl = ATOMIC_INIT(0), \
+ }
+
+
+diff -Naur 2.6.12-5.0-org/include/linux/ioprio.h 2.6.12-5.0-patched/include/linux/ioprio.h
+--- 2.6.12-5.0-org/include/linux/ioprio.h 1970-01-01 01:00:00.000000000 +0100
++++ 2.6.12-5.0-patched/include/linux/ioprio.h 2007-12-11 12:34:52.000000000 +0100
+@@ -0,0 +1,88 @@
++#ifndef IOPRIO_H
++#define IOPRIO_H
++
++#include <linux/sched.h>
++
++/*
++ * Gives us 8 prio classes with 13-bits of data for each class
++ */
++#define IOPRIO_BITS (16)
++#define IOPRIO_CLASS_SHIFT (13)
++#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
++
++#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
++#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
++#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
++
++#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE)
++
++/*
++ * These are the io priority groups as implemented by CFQ. RT is the realtime
++ * class, it always gets premium service. BE is the best-effort scheduling
++ * class, the default for any process. IDLE is the idle scheduling class, it
++ * is only served when no one else is using the disk.
++ */
++enum {
++ IOPRIO_CLASS_NONE,
++ IOPRIO_CLASS_RT,
++ IOPRIO_CLASS_BE,
++ IOPRIO_CLASS_IDLE,
++};
++
++/*
++ * 8 best effort priority levels are supported
++ */
++#define IOPRIO_BE_NR (8)
++
++asmlinkage int sys_ioprio_set(int, int, int);
++asmlinkage int sys_ioprio_get(int, int);
++
++enum {
++ IOPRIO_WHO_PROCESS = 1,
++ IOPRIO_WHO_PGRP,
++ IOPRIO_WHO_USER,
++};
++
++/*
++ * if process has set io priority explicitly, use that. if not, convert
++ * the cpu scheduler nice value to an io priority
++ */
++#define IOPRIO_NORM (4)
++static inline int task_ioprio(struct task_struct *task)
++{
++ WARN_ON(!ioprio_valid(task->ioprio));
++ return IOPRIO_PRIO_DATA(task->ioprio);
++}
++
++static inline int task_nice_ioprio(struct task_struct *task)
++{
++ return (task_nice(task) + 20) / 5;
++}
++
++/*
++ * For inheritance, return the highest of the two given priorities
++ */
++static inline int ioprio_best(unsigned short aprio, unsigned short bprio)
++{
++ unsigned short aclass = IOPRIO_PRIO_CLASS(aprio);
++ unsigned short bclass = IOPRIO_PRIO_CLASS(bprio);
++
++ if (!ioprio_valid(aprio))
++ return bprio;
++ if (!ioprio_valid(bprio))
++ return aprio;
++
++ if (aclass == IOPRIO_CLASS_NONE)
++ aclass = IOPRIO_CLASS_BE;
++ if (bclass == IOPRIO_CLASS_NONE)
++ bclass = IOPRIO_CLASS_BE;
++
++ if (aclass == bclass)
++ return min(aprio, bprio);
++ if (aclass > bclass)
++ return bprio;
++ else
++ return aprio;
++}
++
++#endif
+diff -Naur 2.6.12-5.0-org/include/linux/sched.h 2.6.12-5.0-patched/include/linux/sched.h
+--- 2.6.12-5.0-org/include/linux/sched.h 2007-07-26 00:57:07.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/sched.h 2007-12-11 12:34:52.000000000 +0100
+@@ -584,6 +584,8 @@
+ struct list_head run_list;
+ prio_array_t *array;
+
++ unsigned short ioprio;
++
+ unsigned long sleep_avg;
+ unsigned long long timestamp, last_ran;
+ unsigned long long sched_time; /* sched_clock time spent running */
+@@ -740,6 +742,7 @@
+ nodemask_t mems_allowed;
+ int cpuset_mems_generation;
+ #endif
++ atomic_t fs_excl; /* holding fs exclusive resources */
+ };
+
+ static inline pid_t process_group(struct task_struct *tsk)
+@@ -1089,7 +1092,8 @@
+
+ /*
+ * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring
+- * subscriptions and synchronises with wait4(). Also used in procfs.
++ * subscriptions and synchronises with wait4(). Also used in procfs. Also
++ * pins the final release of task.io_context.
+ *
+ * Nests both inside and outside of read_lock(&tasklist_lock).
+ * It must not be nested with write_lock_irq(&tasklist_lock),
+diff -Naur 2.6.12-5.0-org/include/linux/time.h 2.6.12-5.0-patched/include/linux/time.h
+--- 2.6.12-5.0-org/include/linux/time.h 2007-07-26 00:57:01.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/time.h 2007-12-11 12:34:52.000000000 +0100
+@@ -84,6 +84,12 @@
+ )*60 + sec; /* finally seconds */
+ }
+
++/*
++ * Returns true if the timespec is nor, false is denorm:
++ */
++#define timespec_valid(ts) \
++ (((ts)->tv_sec >= 0) && (((unsigned long) (ts)->tv_nsec) < NSEC_PER_SEC))
++
+ extern struct timespec xtime;
+ extern struct timespec wall_to_monotonic;
+ extern seqlock_t xtime_lock;
+diff -Naur 2.6.12-5.0-org/include/linux/writeback.h 2.6.12-5.0-patched/include/linux/writeback.h
+--- 2.6.12-5.0-org/include/linux/writeback.h 2007-07-26 00:57:08.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/writeback.h 2007-12-11 12:34:52.000000000 +0100
+@@ -14,11 +14,13 @@
+ * Yes, writeback.h requires sched.h
+ * No, sched.h is not included from here.
+ */
+-static inline int current_is_pdflush(void)
++static inline int task_is_pdflush(struct task_struct *task)
+ {
+- return current->flags & PF_FLUSHER;
++ return task->flags & PF_FLUSHER;
+ }
+
++#define current_is_pdflush() task_is_pdflush(current)
++
+ /*
+ * fs/fs-writeback.c
+ */
+diff -Naur 2.6.12-5.0-org/kernel/exit.c 2.6.12-5.0-patched/kernel/exit.c
+--- 2.6.12-5.0-org/kernel/exit.c 2007-07-26 00:57:20.000000000 +0200
++++ 2.6.12-5.0-patched/kernel/exit.c 2007-12-11 12:34:52.000000000 +0100
+@@ -779,6 +779,8 @@
+
+ profile_task_exit(tsk);
+
++ WARN_ON(atomic_read(&tsk->fs_excl));
++
+ if (unlikely(in_interrupt()))
+ panic("Aiee, killing interrupt handler!");
+ if (unlikely(!tsk->pid))
+diff -Naur 2.6.12-5.0-org/kernel/fork.c 2.6.12-5.0-patched/kernel/fork.c
+--- 2.6.12-5.0-org/kernel/fork.c 2007-07-26 00:57:20.000000000 +0200
++++ 2.6.12-5.0-patched/kernel/fork.c 2007-12-11 12:34:52.000000000 +0100
+@@ -1084,6 +1084,11 @@
+ spin_unlock(&current->sighand->siglock);
+ }
+
++ /*
++ * inherit ioprio
++ */
++ p->ioprio = current->ioprio;
++
+ SET_LINKS(p);
+ if (unlikely(p->ptrace & PT_PTRACED))
+ __ptrace_link(p, current->parent);
+diff -Naur 2.6.12-5.0-org/kernel/sched.c 2.6.12-5.0-patched/kernel/sched.c
+--- 2.6.12-5.0-org/kernel/sched.c 2007-07-26 00:57:20.000000000 +0200
++++ 2.6.12-5.0-patched/kernel/sched.c 2007-12-11 12:34:52.000000000 +0100
+@@ -3302,15 +3302,7 @@
+ {
+ return TASK_NICE(p);
+ }
+-
+-/*
+- * The only users of task_nice are binfmt_elf and binfmt_elf32.
+- * binfmt_elf is no longer modular, but binfmt_elf32 still is.
+- * Therefore, task_nice is needed if there is a compat_mode.
+- */
+-#ifdef CONFIG_COMPAT
+ EXPORT_SYMBOL_GPL(task_nice);
+-#endif
+
+ /**
+ * idle_cpu - is a given cpu idle currently?
+diff -Naur 2.6.12-5.0-org/arch/mips/kernel/scall32-o32.S 2.6.12-5.0-patched/arch/mips/kernel/scall32-o32.S
+--- 2.6.12-5.0-org/arch/mips/kernel/scall32-o32.S 2007-07-26 00:51:08.000000000 +0200
++++ 2.6.12-5.0-patched/arch/mips/kernel/scall32-o32.S 2007-12-11 12:34:52.000000000 +0100
+@@ -624,6 +624,8 @@
+ sys sys_request_key 4
+ sys sys_keyctl 5
+ sys sys_set_thread_area 1
++ sys sys_ioprio_set 3
++ sys sys_ioprio_get 2
+
+ .endm
+
diff --git a/packages/linux/linux-dm800/linux-2.6.12-dream-misc.patch b/packages/linux/linux-dm800/linux-2.6.12-dream-misc.patch
new file mode 100644
index 0000000000..bf572daf01
--- /dev/null
+++ b/packages/linux/linux-dm800/linux-2.6.12-dream-misc.patch
@@ -0,0 +1,87 @@
+diff -Naur 2.6.12-5.0-org/arch/mips/kernel/reset.c 2.6.12-5.0-patched/arch/mips/kernel/reset.c
+--- 2.6.12-5.0-org/arch/mips/kernel/reset.c 2007-07-26 00:51:08.000000000 +0200
++++ 2.6.12-5.0-patched/arch/mips/kernel/reset.c 2007-12-11 12:34:52.000000000 +0100
+@@ -27,6 +27,7 @@
+ }
+
+ EXPORT_SYMBOL(machine_restart);
++EXPORT_SYMBOL(_machine_restart);
+
+ void machine_halt(void)
+ {
+@@ -34,6 +35,7 @@
+ }
+
+ EXPORT_SYMBOL(machine_halt);
++EXPORT_SYMBOL(_machine_halt);
+
+ void machine_power_off(void)
+ {
+@@ -41,3 +43,4 @@
+ }
+
+ EXPORT_SYMBOL(machine_power_off);
++EXPORT_SYMBOL(_machine_power_off);
+diff -Naur 2.6.12-5.0-org/drivers/video/Kconfig 2.6.12-5.0-patched/drivers/video/Kconfig
+--- 2.6.12-5.0-org/drivers/video/Kconfig 2007-07-26 00:54:49.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/video/Kconfig 2007-12-11 12:34:52.000000000 +0100
+@@ -39,7 +39,7 @@
+ device-aware may cause unexpected results. If unsure, say N.
+
+ config FB_CFB_FILLRECT
+- tristate
++ tristate "FB_CFB_FILLRECT"
+ depends on FB
+ default n
+ ---help---
+@@ -48,7 +48,7 @@
+ (accelerated) version.
+
+ config FB_CFB_COPYAREA
+- tristate
++ tristate "FB_CFB_COPYAREA"
+ depends on FB
+ default n
+ ---help---
+@@ -57,7 +57,7 @@
+ version.
+
+ config FB_CFB_IMAGEBLIT
+- tristate
++ tristate "FB_CFB_IMAGEBLIT"
+ depends on FB
+ default n
+ ---help---
+@@ -66,7 +66,7 @@
+ (accelerated) version.
+
+ config FB_SOFT_CURSOR
+- tristate
++ tristate "FB_SOFT_CURSOR"
+ depends on FB
+ default n
+ ---help---
+diff -Naur 2.6.12-5.0-org/drivers/char/keyboard.c 2.6.12-5.0-patched/drivers/char/keyboard.c
+--- 2.6.12-5.0-org/drivers/char/keyboard.c 2007-07-26 00:53:29.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/char/keyboard.c 2007-12-11 12:34:52.000000000 +0100
+@@ -1186,6 +1186,9 @@
+ for (i = KEY_RESERVED; i < BTN_MISC; i++)
+ if (test_bit(i, dev->keybit)) break;
+
++ if ( test_bit(EV_NO_CONSOLE, dev->evbit) )
++ return NULL;
++
+ if ((i == BTN_MISC) && !test_bit(EV_SND, dev->evbit))
+ return NULL;
+
+diff -Naur 2.6.12-5.0-org/include/linux/input.h 2.6.12-5.0-patched/include/linux/input.h
+--- 2.6.12-5.0-org/include/linux/input.h 2007-07-26 00:56:59.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/input.h 2007-12-11 12:34:52.000000000 +0100
+@@ -92,6 +92,7 @@
+ #define EV_FF 0x15
+ #define EV_PWR 0x16
+ #define EV_FF_STATUS 0x17
++#define EV_NO_CONSOLE 0x1e
+ #define EV_MAX 0x1f
+
+ /*
diff --git a/packages/linux/linux-dm800/linux-2.6.12-fix-serial.patch b/packages/linux/linux-dm800/linux-2.6.12-fix-serial.patch
new file mode 100644
index 0000000000..75a918c1f6
--- /dev/null
+++ b/packages/linux/linux-dm800/linux-2.6.12-fix-serial.patch
@@ -0,0 +1,32 @@
+Index: stblinux-2.6.12/drivers/char/brcmserial.c
+===================================================================
+--- stblinux-2.6.12.orig/drivers/char/brcmserial.c 2007-12-16 22:49:40.000000000 +0100
++++ stblinux-2.6.12/drivers/char/brcmserial.c 2007-12-16 22:56:00.000000000 +0100
+@@ -1208,6 +1208,6 @@
+ 600, 1200, 1800, 2400, 4800, 9600, 19200,
+ 38400, 57600, 115200, 230400, 460800, 0 };
+
+-static int tty_get_baud_rate(struct tty_struct *tty)
++int tty_get_baud_rate(struct tty_struct *tty)
+ {
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned int cflag, i;
+diff -Naur 2.6.12-5.0-org/drivers/char/tty_io.c 2.6.12-5.0-patched/drivers/char/tty_io.c
+--- 2.6.12-5.0-org/drivers/char/tty_io.c 2007-07-26 00:53:30.000000000 +0200
++++ 2.6.12-5.0-patched/drivers/char/tty_io.c 2007-12-11 12:34:52.000000000 +0100
+@@ -2592,6 +2592,7 @@
+ * flags may be updated.
+ */
+
++#if 0
+ int tty_get_baud_rate(struct tty_struct *tty)
+ {
+ int baud = tty_termios_baud_rate(tty->termios);
+@@ -2607,6 +2608,7 @@
+
+ return baud;
+ }
++#endif
+
+ EXPORT_SYMBOL(tty_get_baud_rate);
+
diff --git a/packages/linux/linux-dm800/linuxmips-2.6.12-fix-fadvise.patch b/packages/linux/linux-dm800/linuxmips-2.6.12-fix-fadvise.patch
new file mode 100644
index 0000000000..430f32c779
--- /dev/null
+++ b/packages/linux/linux-dm800/linuxmips-2.6.12-fix-fadvise.patch
@@ -0,0 +1,29 @@
+diff -Naur 2.6.12-5.0-org/arch/mips/kernel/syscall.c 2.6.12-5.0-patched/arch/mips/kernel/syscall.c
+--- 2.6.12-5.0-org/arch/mips/kernel/syscall.c 2007-07-26 00:51:09.000000000 +0200
++++ 2.6.12-5.0-patched/arch/mips/kernel/syscall.c 2007-12-11 12:34:52.000000000 +0100
+@@ -405,6 +405,13 @@
+ }
+ }
+
++asmlinkage long mips_fadvise64(int fd,
++ unsigned int low_off, unsigned int high_off,
++ unsigned int len, int advice, unsigned int dummy)
++{
++ return sys_fadvise64_64(fd, (((u64)high_off) << 32) | low_off, (u64)len, advice);
++}
++
+ /*
+ * No implemented yet ...
+ */
+diff -Naur 2.6.12-5.0-org/arch/mips/kernel/scall32-o32.S 2.6.12-5.0-patched/arch/mips/kernel/scall32-o32.S
+--- 2.6.12-5.0-org/arch/mips/kernel/scall32-o32.S 2007-07-26 00:51:08.000000000 +0200
++++ 2.6.12-5.0-patched/arch/mips/kernel/scall32-o32.S 2007-12-11 12:34:52.000000000 +0100
+@@ -594,7 +594,7 @@
+ sys sys_remap_file_pages 5
+ sys sys_set_tid_address 1
+ sys sys_restart_syscall 0
+- sys sys_fadvise64_64 7
++ sys mips_fadvise64 7
+ sys sys_statfs64 3 /* 4255 */
+ sys sys_fstatfs64 2
+ sys sys_timer_create 3
diff --git a/packages/linux/linux-dm800/linuxmips-2.6.12-fix-futex.patch b/packages/linux/linux-dm800/linuxmips-2.6.12-fix-futex.patch
new file mode 100644
index 0000000000..cb0bfe2cce
--- /dev/null
+++ b/packages/linux/linux-dm800/linuxmips-2.6.12-fix-futex.patch
@@ -0,0 +1,368 @@
+diff -Naur 2.6.12-5.0-org/include/asm-mips/futex.h 2.6.12-5.0-patched/include/asm-mips/futex.h
+--- 2.6.12-5.0-org/include/asm-mips/futex.h 1970-01-01 01:00:00.000000000 +0100
++++ 2.6.12-5.0-patched/include/asm-mips/futex.h 2007-12-11 12:34:52.000000000 +0100
+@@ -0,0 +1,134 @@
++#ifndef _ASM_FUTEX_H
++#define _ASM_FUTEX_H
++
++#ifdef __KERNEL__
++
++#include <linux/config.h>
++#include <linux/futex.h>
++#include <asm/errno.h>
++#include <asm/uaccess.h>
++#include <asm/war.h>
++
++#ifdef CONFIG_SMP
++#define __FUTEX_SMP_SYNC " sync \n"
++#else
++#define __FUTEX_SMP_SYNC
++#endif
++
++#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
++{ \
++ if (cpu_has_llsc && R10000_LLSC_WAR) { \
++ __asm__ __volatile__( \
++ " .set push \n" \
++ " .set noat \n" \
++ " .set mips3 \n" \
++ "1: ll %1, %4 # __futex_atomic_op \n" \
++ " .set mips0 \n" \
++ " " insn " \n" \
++ " .set mips3 \n" \
++ "2: sc $1, %2 \n" \
++ " beqzl $1, 1b \n" \
++ __FUTEX_SMP_SYNC \
++ "3: \n" \
++ " .set pop \n" \
++ " .set mips0 \n" \
++ " .section .fixup,\"ax\" \n" \
++ "4: li %0, %6 \n" \
++ " j 2b \n" \
++ " .previous \n" \
++ " .section __ex_table,\"a\" \n" \
++ " "__UA_ADDR "\t1b, 4b \n" \
++ " "__UA_ADDR "\t2b, 4b \n" \
++ " .previous \n" \
++ : "=r" (ret), "=&r" (oldval), "=R" (*uaddr) \
++ : "0" (0), "R" (*uaddr), "Jr" (oparg), "i" (-EFAULT) \
++ : "memory"); \
++ } else if (cpu_has_llsc) { \
++ __asm__ __volatile__( \
++ " .set push \n" \
++ " .set noat \n" \
++ " .set mips3 \n" \
++ "1: ll %1, %4 # __futex_atomic_op \n" \
++ " .set mips0 \n" \
++ " " insn " \n" \
++ " .set mips3 \n" \
++ "2: sc $1, %2 \n" \
++ " beqz $1, 1b \n" \
++ __FUTEX_SMP_SYNC \
++ "3: \n" \
++ " .set pop \n" \
++ " .set mips0 \n" \
++ " .section .fixup,\"ax\" \n" \
++ "4: li %0, %6 \n" \
++ " j 2b \n" \
++ " .previous \n" \
++ " .section __ex_table,\"a\" \n" \
++ " "__UA_ADDR "\t1b, 4b \n" \
++ " "__UA_ADDR "\t2b, 4b \n" \
++ " .previous \n" \
++ : "=r" (ret), "=&r" (oldval), "=R" (*uaddr) \
++ : "0" (0), "R" (*uaddr), "Jr" (oparg), "i" (-EFAULT) \
++ : "memory"); \
++ } else \
++ ret = -ENOSYS; \
++}
++
++static inline int
++futex_atomic_op_inuser (int encoded_op, int __user *uaddr)
++{
++ int op = (encoded_op >> 28) & 7;
++ int cmp = (encoded_op >> 24) & 15;
++ int oparg = (encoded_op << 8) >> 20;
++ int cmparg = (encoded_op << 20) >> 20;
++ int oldval = 0, ret;
++ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
++ oparg = 1 << oparg;
++
++ if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int)))
++ return -EFAULT;
++
++ inc_preempt_count();
++
++ switch (op) {
++ case FUTEX_OP_SET:
++ __futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg);
++ break;
++
++ case FUTEX_OP_ADD:
++ __futex_atomic_op("addu $1, %1, %z5",
++ ret, oldval, uaddr, oparg);
++ break;
++ case FUTEX_OP_OR:
++ __futex_atomic_op("or $1, %1, %z5",
++ ret, oldval, uaddr, oparg);
++ break;
++ case FUTEX_OP_ANDN:
++ __futex_atomic_op("and $1, %1, %z5",
++ ret, oldval, uaddr, ~oparg);
++ break;
++ case FUTEX_OP_XOR:
++ __futex_atomic_op("xor $1, %1, %z5",
++ ret, oldval, uaddr, oparg);
++ break;
++ default:
++ ret = -ENOSYS;
++ }
++
++ dec_preempt_count();
++
++ if (!ret) {
++ switch (cmp) {
++ case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
++ case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
++ case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
++ case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
++ case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
++ case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
++ default: ret = -ENOSYS;
++ }
++ }
++ return ret;
++}
++
++#endif
++#endif
+diff -Naur 2.6.12-5.0-org/include/linux/futex.h 2.6.12-5.0-patched/include/linux/futex.h
+--- 2.6.12-5.0-org/include/linux/futex.h 2007-07-26 00:57:03.000000000 +0200
++++ 2.6.12-5.0-patched/include/linux/futex.h 2007-12-11 12:34:52.000000000 +0100
+@@ -4,14 +4,40 @@
+ /* Second argument to futex syscall */
+
+
+-#define FUTEX_WAIT (0)
+-#define FUTEX_WAKE (1)
+-#define FUTEX_FD (2)
+-#define FUTEX_REQUEUE (3)
+-#define FUTEX_CMP_REQUEUE (4)
++#define FUTEX_WAIT 0
++#define FUTEX_WAKE 1
++#define FUTEX_FD 2
++#define FUTEX_REQUEUE 3
++#define FUTEX_CMP_REQUEUE 4
++#define FUTEX_WAKE_OP 5
+
+ long do_futex(unsigned long uaddr, int op, int val,
+ unsigned long timeout, unsigned long uaddr2, int val2,
+ int val3);
+
++#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */
++#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */
++#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */
++#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */
++#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */
++
++#define FUTEX_OP_OPARG_SHIFT 8 /* Use (1 << OPARG) instead of OPARG. */
++
++#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */
++#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */
++#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */
++#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */
++#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */
++#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */
++
++/* FUTEX_WAKE_OP will perform atomically
++ int oldval = *(int *)UADDR2;
++ *(int *)UADDR2 = oldval OP OPARG;
++ if (oldval CMP CMPARG)
++ wake UADDR2; */
++
++#define FUTEX_OP(op, oparg, cmp, cmparg) \
++ (((op & 0xf) << 28) | ((cmp & 0xf) << 24) \
++ | ((oparg & 0xfff) << 12) | (cmparg & 0xfff))
++
+ #endif
+diff -Naur 2.6.12-5.0-org/kernel/futex.c 2.6.12-5.0-patched/kernel/futex.c
+--- 2.6.12-5.0-org/kernel/futex.c 2007-07-26 00:57:20.000000000 +0200
++++ 2.6.12-5.0-patched/kernel/futex.c 2007-12-11 12:34:52.000000000 +0100
+@@ -40,6 +40,7 @@
+ #include <linux/pagemap.h>
+ #include <linux/syscalls.h>
+ #include <linux/signal.h>
++#include <asm/futex.h>
+
+ #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8)
+
+@@ -201,22 +202,6 @@
+ * for a rare case, so we simply fetch the page.
+ */
+
+- /*
+- * Do a quick atomic lookup first - this is the fastpath.
+- */
+- spin_lock(&current->mm->page_table_lock);
+- page = follow_page(mm, uaddr, 0);
+- if (likely(page != NULL)) {
+- key->shared.pgoff =
+- page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
+- spin_unlock(&current->mm->page_table_lock);
+- return 0;
+- }
+- spin_unlock(&current->mm->page_table_lock);
+-
+- /*
+- * Do it the general way.
+- */
+ err = get_user_pages(current, mm, uaddr, 1, 0, 0, &page, NULL);
+ if (err >= 0) {
+ key->shared.pgoff =
+@@ -327,6 +312,123 @@
+ }
+
+ /*
++ * Wake up all waiters hashed on the physical page that is mapped
++ * to this virtual address:
++ */
++static int futex_wake_op(unsigned long uaddr1, unsigned long uaddr2, int nr_wake, int nr_wake2, int op)
++{
++ union futex_key key1, key2;
++ struct futex_hash_bucket *bh1, *bh2;
++ struct list_head *head;
++ struct futex_q *this, *next;
++ int ret, op_ret, attempt = 0;
++
++retryfull:
++ down_read(&current->mm->mmap_sem);
++
++ ret = get_futex_key(uaddr1, &key1);
++ if (unlikely(ret != 0))
++ goto out;
++ ret = get_futex_key(uaddr2, &key2);
++ if (unlikely(ret != 0))
++ goto out;
++
++ bh1 = hash_futex(&key1);
++ bh2 = hash_futex(&key2);
++
++retry:
++ if (bh1 < bh2)
++ spin_lock(&bh1->lock);
++ spin_lock(&bh2->lock);
++ if (bh1 > bh2)
++ spin_lock(&bh1->lock);
++
++ op_ret = futex_atomic_op_inuser(op, (int __user *)uaddr2);
++ if (unlikely(op_ret < 0)) {
++ int dummy;
++
++ spin_unlock(&bh1->lock);
++ if (bh1 != bh2)
++ spin_unlock(&bh2->lock);
++
++ if (unlikely(op_ret != -EFAULT)) {
++ ret = op_ret;
++ goto out;
++ }
++
++ /* futex_atomic_op_inuser needs to both read and write
++ * *(int __user *)uaddr2, but we can't modify it
++ * non-atomically. Therefore, if get_user below is not
++ * enough, we need to handle the fault ourselves, while
++ * still holding the mmap_sem. */
++ if (attempt++) {
++ struct vm_area_struct * vma;
++ struct mm_struct *mm = current->mm;
++
++ ret = -EFAULT;
++ if (attempt >= 2 ||
++ !(vma = find_vma(mm, uaddr2)) ||
++ vma->vm_start > uaddr2 ||
++ !(vma->vm_flags & VM_WRITE))
++ goto out;
++
++ switch (handle_mm_fault(mm, vma, uaddr2, 1)) {
++ case VM_FAULT_MINOR:
++ current->min_flt++;
++ break;
++ case VM_FAULT_MAJOR:
++ current->maj_flt++;
++ break;
++ default:
++ goto out;
++ }
++ goto retry;
++ }
++
++ /* If we would have faulted, release mmap_sem,
++ * fault it in and start all over again. */
++ up_read(&current->mm->mmap_sem);
++
++ ret = get_user(dummy, (int __user *)uaddr2);
++ if (ret)
++ return ret;
++
++ goto retryfull;
++ }
++
++ head = &bh1->chain;
++
++ list_for_each_entry_safe(this, next, head, list) {
++ if (match_futex (&this->key, &key1)) {
++ wake_futex(this);
++ if (++ret >= nr_wake)
++ break;
++ }
++ }
++
++ if (op_ret > 0) {
++ head = &bh2->chain;
++
++ op_ret = 0;
++ list_for_each_entry_safe(this, next, head, list) {
++ if (match_futex (&this->key, &key2)) {
++ wake_futex(this);
++ if (++op_ret >= nr_wake2)
++ break;
++ }
++ }
++ ret += op_ret;
++ }
++
++ spin_unlock(&bh1->lock);
++ if (bh1 != bh2)
++ spin_unlock(&bh2->lock);
++out:
++ up_read(&current->mm->mmap_sem);
++ return ret;
++}
++
++/*
+ * Requeue all waiters hashed on one physical page to another
+ * physical page.
+ */
+@@ -740,6 +842,9 @@
+ case FUTEX_CMP_REQUEUE:
+ ret = futex_requeue(uaddr, uaddr2, val, val2, &val3);
+ break;
++ case FUTEX_WAKE_OP:
++ ret = futex_wake_op(uaddr, uaddr2, val, val2, val3);
++ break;
+ default:
+ ret = -ENOSYS;
+ }
+@@ -755,9 +860,11 @@
+ unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
+ int val2 = 0;
+
+- if ((op == FUTEX_WAIT) && utime) {
++ if (utime && (op == FUTEX_WAIT)) {
+ if (copy_from_user(&t, utime, sizeof(t)) != 0)
+ return -EFAULT;
++ if (!timespec_valid(&t))
++ return -EINVAL;
+ timeout = timespec_to_jiffies(&t) + 1;
+ }
+ /*
diff --git a/packages/linux/linux-dm800/linuxmips-2.6.12-gcc4-compile-fix.patch b/packages/linux/linux-dm800/linuxmips-2.6.12-gcc4-compile-fix.patch
new file mode 100644
index 0000000000..c1dc961926
--- /dev/null
+++ b/packages/linux/linux-dm800/linuxmips-2.6.12-gcc4-compile-fix.patch
@@ -0,0 +1,91 @@
+diff -Naur 2.6.12-5.0-org/include/asm-mips/uaccess.h 2.6.12-5.0-patched/include/asm-mips/uaccess.h
+--- 2.6.12-5.0-org/include/asm-mips/uaccess.h 2007-07-26 00:56:08.000000000 +0200
++++ 2.6.12-5.0-patched/include/asm-mips/uaccess.h 2007-12-11 12:34:52.000000000 +0100
+@@ -234,39 +234,72 @@
+
+ #define __get_user_nocheck(x,ptr,size) \
+ ({ \
+- __typeof(*(ptr)) __gu_val = (__typeof(*(ptr))) 0; \
+ long __gu_err = 0; \
+- \
+ might_sleep(); \
+ switch (size) { \
+- case 1: __get_user_asm("lb", ptr); break; \
+- case 2: __get_user_asm("lh", ptr); break; \
+- case 4: __get_user_asm("lw", ptr); break; \
+- case 8: __GET_USER_DW(ptr); break; \
++ case 1: { \
++ s8 __gu_val = (s8) 0; \
++ __get_user_asm("lb", ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
++ case 2: { \
++ s16 __gu_val = (s16) 0; \
++ __get_user_asm("lh", ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
++ case 4: { \
++ s32 __gu_val = (s32) 0; \
++ __get_user_asm("lw", ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
++ case 8: { \
++ s64 __gu_val = (s64) 0; \
++ __GET_USER_DW(ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
+ default: __get_user_unknown(); break; \
+ } \
+- (x) = (__typeof__(*(ptr))) __gu_val; \
+ __gu_err; \
+ })
+
+ #define __get_user_check(x,ptr,size) \
+ ({ \
+ const __typeof__(*(ptr)) __user * __gu_addr = (ptr); \
+- __typeof__(*(ptr)) __gu_val = 0; \
+ long __gu_err = -EFAULT; \
+- \
+ might_sleep(); \
+- \
+ if (likely(access_ok(VERIFY_READ, __gu_addr, size))) { \
+ switch (size) { \
+- case 1: __get_user_asm("lb", __gu_addr); break; \
+- case 2: __get_user_asm("lh", __gu_addr); break; \
+- case 4: __get_user_asm("lw", __gu_addr); break; \
+- case 8: __GET_USER_DW(__gu_addr); break; \
++ case 1: { \
++ s8 __gu_val = (s8) 0; \
++ __get_user_asm("lb", ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
++ case 2: { \
++ s16 __gu_val = (s16) 0; \
++ __get_user_asm("lh", ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
++ case 4: { \
++ s32 __gu_val = (s32) 0; \
++ __get_user_asm("lw", ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
++ case 8: { \
++ s64 __gu_val = (s64) 0; \
++ __GET_USER_DW(ptr); \
++ (x) = (__typeof__(*(ptr))) __gu_val; \
++ break; \
++ } \
+ default: __get_user_unknown(); break; \
+ } \
+ } \
+- (x) = (__typeof__(*(ptr))) __gu_val; \
+ __gu_err; \
+ })
+
diff --git a/packages/linux/linux-dm800/linuxmips-2.6.12-gdb-fix.patch b/packages/linux/linux-dm800/linuxmips-2.6.12-gdb-fix.patch
new file mode 100644
index 0000000000..d70e2dea41
--- /dev/null
+++ b/packages/linux/linux-dm800/linuxmips-2.6.12-gdb-fix.patch
@@ -0,0 +1,22 @@
+diff -Naur 2.6.12-5.0-org/arch/mips/kernel/gdb-low.S 2.6.12-5.0-patched/arch/mips/kernel/gdb-low.S
+--- 2.6.12-5.0-org/arch/mips/kernel/gdb-low.S 2007-07-26 00:51:07.000000000 +0200
++++ 2.6.12-5.0-patched/arch/mips/kernel/gdb-low.S 2007-12-11 12:34:52.000000000 +0100
+@@ -52,12 +52,14 @@
+ /*
+ * Called from user mode, go somewhere else.
+ */
+- lui k1, %hi(saved_vectors)
+ mfc0 k0, CP0_CAUSE
+ andi k0, k0, 0x7c
+- add k1, k1, k0
+- lw k0, %lo(saved_vectors)(k1)
+- jr k0
++
++#ifdef CONFIG_MIPS64
++ dsll k0, k0, 1
++#endif
++ lw k1, %lo(saved_vectors)(k0)
++ jr k1
+ nop
+ 1:
+ move k0, sp