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