summaryrefslogtreecommitdiffstats
path: root/block/cfq-iosched.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/cfq-iosched.c')
-rw-r--r--block/cfq-iosched.c361
1 files changed, 123 insertions, 238 deletions
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 9d6f04103f01..4838c2b16f2c 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -26,7 +26,16 @@ static int cfq_slice_async = HZ / 25;
static const int cfq_slice_async_rq = 2;
static int cfq_slice_idle = HZ / 125;
+/*
+ * grace period before allowing idle class to get disk access
+ */
#define CFQ_IDLE_GRACE (HZ / 10)
+
+/*
+ * below this threshold, we consider thinktime immediate
+ */
+#define CFQ_MIN_TT (2)
+
#define CFQ_SLICE_SCALE (5)
#define CFQ_KEY_ASYNC (0)
@@ -69,10 +78,9 @@ struct cfq_data {
/*
* rr list of queues with requests and the count of them
*/
- struct list_head rr_list[CFQ_PRIO_LISTS];
+ struct rb_root service_tree;
struct list_head cur_rr;
struct list_head idle_rr;
- unsigned long cur_rr_tick;
unsigned int busy_queues;
/*
@@ -91,8 +99,6 @@ struct cfq_data {
struct cfq_queue *active_queue;
struct cfq_io_context *active_cic;
- int cur_prio, cur_end_prio;
- unsigned long prio_time;
unsigned int dispatch_slice;
struct timer_list idle_class_timer;
@@ -131,8 +137,10 @@ struct cfq_queue {
unsigned int key;
/* member of the rr/busy/cur/idle cfqd list */
struct list_head cfq_list;
- /* in what tick we were last serviced */
- unsigned long rr_tick;
+ /* service_tree member */
+ struct rb_node rb_node;
+ /* service_tree key */
+ unsigned long rb_key;
/* sorted list of pending requests */
struct rb_root sort_list;
/* if fifo isn't expired, next request to serve */
@@ -147,8 +155,6 @@ struct cfq_queue {
struct list_head fifo;
unsigned long slice_end;
- unsigned long service_last;
- unsigned long slice_start;
long slice_resid;
/* number of requests that are on the dispatch list or inside driver */
@@ -240,30 +246,26 @@ static inline pid_t cfq_queue_pid(struct task_struct *task, int rw, int is_sync)
* if a queue is marked sync and has sync io queued. A sync queue with async
* io only, should not get full sync slice length.
*/
-static inline int
-cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+static inline int cfq_prio_slice(struct cfq_data *cfqd, int sync,
+ unsigned short prio)
{
- const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)];
+ const int base_slice = cfqd->cfq_slice[sync];
- WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR);
+ WARN_ON(prio >= IOPRIO_BE_NR);
+
+ return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - prio));
+}
- return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio));
+static inline int
+cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+{
+ return cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio);
}
static inline void
cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
{
cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies;
- cfqq->slice_end += cfqq->slice_resid;
-
- /*
- * Don't carry over residual for more than one slice, we only want
- * to slightly correct the fairness. Carrying over forever would
- * easily introduce oscillations.
- */
- cfqq->slice_resid = 0;
-
- cfqq->slice_start = jiffies;
}
/*
@@ -403,33 +405,50 @@ cfq_find_next_rq(struct cfq_data *cfqd, struct cfq_queue *cfqq,
return cfq_choose_req(cfqd, next, prev);
}
-/*
- * This function finds out where to insert a BE queue in the service hierarchy
- */
-static void cfq_resort_be_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq,
- int preempted)
+static unsigned long cfq_slice_offset(struct cfq_data *cfqd,
+ struct cfq_queue *cfqq)
{
- if (!cfq_cfqq_sync(cfqq))
- list_add_tail(&cfqq->cfq_list, &cfqd->rr_list[cfqq->ioprio]);
- else {
- struct list_head *n = &cfqd->rr_list[cfqq->ioprio];
+ /*
+ * just an approximation, should be ok.
+ */
+ return ((cfqd->busy_queues - 1) * cfq_prio_slice(cfqd, 1, 0));
+}
+
+static void cfq_service_tree_add(struct cfq_data *cfqd,
+ struct cfq_queue *cfqq)
+{
+ struct rb_node **p = &cfqd->service_tree.rb_node;
+ struct rb_node *parent = NULL;
+ struct cfq_queue *__cfqq;
+ unsigned long rb_key;
+
+ rb_key = cfq_slice_offset(cfqd, cfqq) + jiffies;
+ rb_key += cfqq->slice_resid;
+ cfqq->slice_resid = 0;
+ if (!RB_EMPTY_NODE(&cfqq->rb_node)) {
/*
- * sort by last service, but don't cross a new or async
- * queue. we don't cross a new queue because it hasn't
- * been service before, and we don't cross an async
- * queue because it gets added to the end on expire.
+ * same position, nothing more to do
*/
- while ((n = n->prev) != &cfqd->rr_list[cfqq->ioprio]) {
- struct cfq_queue *__c = list_entry_cfqq(n);
+ if (rb_key == cfqq->rb_key)
+ return;
- if (!cfq_cfqq_sync(__c) || !__c->service_last)
- break;
- if (time_before(__c->service_last, cfqq->service_last))
- break;
- }
- list_add(&cfqq->cfq_list, n);
+ rb_erase(&cfqq->rb_node, &cfqd->service_tree);
}
+
+ while (*p) {
+ parent = *p;
+ __cfqq = rb_entry(parent, struct cfq_queue, rb_node);
+
+ if (rb_key < __cfqq->rb_key)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ cfqq->rb_key = rb_key;
+ rb_link_node(&cfqq->rb_node, parent, p);
+ rb_insert_color(&cfqq->rb_node, &cfqd->service_tree);
}
static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted)
@@ -443,7 +462,7 @@ static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted)
if (!cfq_cfqq_on_rr(cfqq))
return;
- list_del(&cfqq->cfq_list);
+ list_del_init(&cfqq->cfq_list);
if (cfq_class_rt(cfqq)) {
/*
@@ -465,7 +484,7 @@ static void cfq_resort_rr_list(struct cfq_queue *cfqq, int preempted)
/*
* So we get here, ergo the queue is a regular best-effort queue
*/
- cfq_resort_be_queue(cfqd, cfqq, preempted);
+ cfq_service_tree_add(cfqd, cfqq);
}
}
@@ -490,6 +509,11 @@ cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
cfq_clear_cfqq_on_rr(cfqq);
list_del_init(&cfqq->cfq_list);
+ if (!RB_EMPTY_NODE(&cfqq->rb_node)) {
+ rb_erase(&cfqq->rb_node, &cfqd->service_tree);
+ RB_CLEAR_NODE(&cfqq->rb_node);
+ }
+
BUG_ON(!cfqd->busy_queues);
cfqd->busy_queues--;
}
@@ -684,7 +708,6 @@ __cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
cfq_clear_cfqq_fifo_expire(cfqq);
cfq_mark_cfqq_slice_new(cfqq);
cfq_clear_cfqq_queue_new(cfqq);
- cfqq->rr_tick = cfqd->cur_rr_tick;
}
cfqd->active_queue = cfqq;
@@ -732,114 +755,19 @@ static inline void cfq_slice_expired(struct cfq_data *cfqd, int preempted,
__cfq_slice_expired(cfqd, cfqq, preempted, timed_out);
}
-/*
- * 0
- * 0,1
- * 0,1,2
- * 0,1,2,3
- * 0,1,2,3,4
- * 0,1,2,3,4,5
- * 0,1,2,3,4,5,6
- * 0,1,2,3,4,5,6,7
- */
-static int cfq_get_next_prio_level(struct cfq_data *cfqd)
-{
- int prio, wrap;
-
- prio = -1;
- wrap = 0;
- do {
- int p;
-
- for (p = cfqd->cur_prio; p <= cfqd->cur_end_prio; p++) {
- if (!list_empty(&cfqd->rr_list[p])) {
- prio = p;
- break;
- }
- }
-
- if (prio != -1)
- break;
- cfqd->cur_prio = 0;
- if (++cfqd->cur_end_prio == CFQ_PRIO_LISTS) {
- cfqd->cur_end_prio = 0;
- if (wrap)
- break;
- wrap = 1;
- }
- } while (1);
-
- if (unlikely(prio == -1))
- return -1;
-
- BUG_ON(prio >= CFQ_PRIO_LISTS);
-
- list_splice_init(&cfqd->rr_list[prio], &cfqd->cur_rr);
-
- cfqd->cur_prio = prio + 1;
- if (cfqd->cur_prio > cfqd->cur_end_prio) {
- cfqd->cur_end_prio = cfqd->cur_prio;
- cfqd->cur_prio = 0;
- }
- if (cfqd->cur_end_prio == CFQ_PRIO_LISTS) {
- cfqd->cur_prio = 0;
- cfqd->cur_end_prio = 0;
- }
-
- cfqd->cur_rr_tick++;
- cfqd->prio_time = jiffies;
- return prio;
-}
-
-static inline sector_t cfq_dist_from_last(struct cfq_data *cfqd,
- struct request *rq)
-{
- if (rq->sector >= cfqd->last_position)
- return rq->sector - cfqd->last_position;
- else
- return cfqd->last_position - rq->sector;
-}
-
-static struct cfq_queue *cfq_get_best_queue(struct cfq_data *cfqd)
-{
- struct cfq_queue *cfqq = NULL, *__cfqq;
- sector_t best = -1, first = -1, dist;
-
- list_for_each_entry(__cfqq, &cfqd->cur_rr, cfq_list) {
- if (!__cfqq->next_rq || !cfq_cfqq_sync(__cfqq))
- continue;
-
- dist = cfq_dist_from_last(cfqd, __cfqq->next_rq);
- if (first == -1)
- first = dist;
- if (dist < best) {
- best = dist;
- cfqq = __cfqq;
- }
- }
-
- /*
- * Only async queue(s) available, grab first entry. Do the same
- * if the difference between the first and best isn't more than
- * twice, to obey fairness.
- */
- if (!cfqq || (best && first != best && ((first / best) < 4)))
- cfqq = list_entry_cfqq(cfqd->cur_rr.next);
-
- return cfqq;
-}
-
static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
{
struct cfq_queue *cfqq = NULL;
- if (!list_empty(&cfqd->cur_rr) || cfq_get_next_prio_level(cfqd) != -1) {
+ if (!list_empty(&cfqd->cur_rr)) {
/*
- * if current list is non-empty, grab first entry. if it is
- * empty, get next prio level and grab first entry then if any
- * are spliced
+ * if current list is non-empty, grab first entry.
*/
- cfqq = cfq_get_best_queue(cfqd);
+ cfqq = list_entry_cfqq(cfqd->cur_rr.next);
+ } else if (!RB_EMPTY_ROOT(&cfqd->service_tree)) {
+ struct rb_node *n = rb_first(&cfqd->service_tree);
+
+ cfqq = rb_entry(n, struct cfq_queue, rb_node);
} else if (!list_empty(&cfqd->idle_rr)) {
/*
* if we have idle queues and no rt or be queues had pending
@@ -861,27 +789,20 @@ static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd)
{
struct cfq_queue *cfqq;
- do {
- long prio;
-
- cfqq = cfq_get_next_queue(cfqd);
- if (!cfqq)
- break;
-
- prio = cfq_prio_to_slice(cfqd, cfqq);
- if (cfqq->slice_resid > -prio)
- break;
-
- cfqq->slice_resid += prio;
- list_del_init(&cfqq->cfq_list);
- list_add_tail(&cfqq->cfq_list, &cfqd->rr_list[cfqq->ioprio]);
- cfqq = NULL;
- } while (1);
-
+ cfqq = cfq_get_next_queue(cfqd);
__cfq_set_active_queue(cfqd, cfqq);
return cfqq;
}
+static inline sector_t cfq_dist_from_last(struct cfq_data *cfqd,
+ struct request *rq)
+{
+ if (rq->sector >= cfqd->last_position)
+ return rq->sector - cfqd->last_position;
+ else
+ return cfqd->last_position - rq->sector;
+}
+
static inline int cfq_rq_close(struct cfq_data *cfqd, struct request *rq)
{
struct cfq_io_context *cic = cfqd->active_cic;
@@ -892,45 +813,15 @@ static inline int cfq_rq_close(struct cfq_data *cfqd, struct request *rq)
return cfq_dist_from_last(cfqd, rq) <= cic->seek_mean;
}
-static struct cfq_queue *__cfq_close_cooperator(struct cfq_data *cfqd,
- struct cfq_queue *cur_cfqq,
- struct list_head *list)
-{
- struct cfq_queue *cfqq;
-
- list_for_each_entry(cfqq, list, cfq_list) {
- if (cfqq == cur_cfqq || !cfq_cfqq_sync(cfqq))
- continue;
-
- BUG_ON(!cfqq->next_rq);
-
- if (cfq_rq_close(cfqd, cfqq->next_rq))
- return cfqq;
- }
-
- return NULL;
-}
-
-static int cfq_close_cooperator(struct cfq_data *cfqd,
- struct cfq_queue *cur_cfqq)
+static int cfq_close_cooperator(struct cfq_data *cfq_data,
+ struct cfq_queue *cfqq)
{
- struct cfq_queue *cfqq;
-
- if (!cfqd->busy_queues)
- return 0;
-
/*
- * check cur_rr and same-prio rr_list for candidates
+ * We should notice if some of the queues are cooperating, eg
+ * working closely on the same area of the disk. In that case,
+ * we can group them together and don't waste time idling.
*/
- cfqq = __cfq_close_cooperator(cfqd, cur_cfqq, &cfqd->cur_rr);
- if (cfqq)
- return 1;
-
- cfqq = __cfq_close_cooperator(cfqd, cur_cfqq, &cfqd->rr_list[cur_cfqq->ioprio]);
- if (cfqq && (cfqq->rr_tick == cfqd->cur_rr_tick))
- cfqq = NULL;
-
- return cfqq != NULL;
+ return 0;
}
#define CIC_SEEKY(cic) ((cic)->seek_mean > (8 * 1024))
@@ -974,7 +865,7 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd)
*/
sl = cfqd->cfq_slice_idle;
if (sample_valid(cic->seek_samples) && CIC_SEEKY(cic))
- sl = min(sl, msecs_to_jiffies(2));
+ sl = min(sl, msecs_to_jiffies(CFQ_MIN_TT));
mod_timer(&cfqd->idle_slice_timer, jiffies + sl);
}
@@ -1115,31 +1006,41 @@ __cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq,
return dispatched;
}
-static int
-cfq_forced_dispatch_cfqqs(struct list_head *list)
+static inline int __cfq_forced_dispatch_cfqq(struct cfq_queue *cfqq)
+{
+ int dispatched = 0;
+
+ while (cfqq->next_rq) {
+ cfq_dispatch_insert(cfqq->cfqd->queue, cfqq->next_rq);
+ dispatched++;
+ }
+
+ BUG_ON(!list_empty(&cfqq->fifo));
+ return dispatched;
+}
+
+static int cfq_forced_dispatch_cfqqs(struct list_head *list)
{
struct cfq_queue *cfqq, *next;
int dispatched;
dispatched = 0;
- list_for_each_entry_safe(cfqq, next, list, cfq_list) {
- while (cfqq->next_rq) {
- cfq_dispatch_insert(cfqq->cfqd->queue, cfqq->next_rq);
- dispatched++;
- }
- BUG_ON(!list_empty(&cfqq->fifo));
- }
+ list_for_each_entry_safe(cfqq, next, list, cfq_list)
+ dispatched += __cfq_forced_dispatch_cfqq(cfqq);
return dispatched;
}
-static int
-cfq_forced_dispatch(struct cfq_data *cfqd)
+static int cfq_forced_dispatch(struct cfq_data *cfqd)
{
- int i, dispatched = 0;
+ int dispatched = 0;
+ struct rb_node *n;
+
+ while ((n = rb_first(&cfqd->service_tree)) != NULL) {
+ struct cfq_queue *cfqq = rb_entry(n, struct cfq_queue, rb_node);
- for (i = 0; i < CFQ_PRIO_LISTS; i++)
- dispatched += cfq_forced_dispatch_cfqqs(&cfqd->rr_list[i]);
+ dispatched += __cfq_forced_dispatch_cfqq(cfqq);
+ }
dispatched += cfq_forced_dispatch_cfqqs(&cfqd->cur_rr);
dispatched += cfq_forced_dispatch_cfqqs(&cfqd->idle_rr);
@@ -1151,8 +1052,7 @@ cfq_forced_dispatch(struct cfq_data *cfqd)
return dispatched;
}
-static int
-cfq_dispatch_requests(request_queue_t *q, int force)
+static int cfq_dispatch_requests(request_queue_t *q, int force)
{
struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_queue *cfqq;
@@ -1222,7 +1122,6 @@ static void cfq_put_queue(struct cfq_queue *cfqq)
/*
* it's on the empty list and still hashed
*/
- list_del(&cfqq->cfq_list);
hlist_del(&cfqq->cfq_hash);
kmem_cache_free(cfq_pool, cfqq);
}
@@ -1391,8 +1290,6 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq)
*/
cfqq->org_ioprio = cfqq->ioprio;
cfqq->org_ioprio_class = cfqq->ioprio_class;
-
- cfq_resort_rr_list(cfqq, 0);
cfq_clear_cfqq_prio_changed(cfqq);
}
@@ -1478,6 +1375,7 @@ retry:
INIT_HLIST_NODE(&cfqq->cfq_hash);
INIT_LIST_HEAD(&cfqq->cfq_list);
+ RB_CLEAR_NODE(&cfqq->rb_node);
INIT_LIST_HEAD(&cfqq->fifo);
cfqq->key = key;
@@ -1752,7 +1650,8 @@ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
* so we know that it will be selected next.
*/
BUG_ON(!cfq_cfqq_on_rr(cfqq));
- list_move(&cfqq->cfq_list, &cfqd->cur_rr);
+ list_del_init(&cfqq->cfq_list);
+ list_add(&cfqq->cfq_list, &cfqd->cur_rr);
cfqq->slice_end = 0;
cfq_mark_cfqq_slice_new(cfqq);
@@ -1828,13 +1727,10 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq)
WARN_ON(!cfqq->dispatched);
cfqd->rq_in_driver--;
cfqq->dispatched--;
- cfqq->service_last = now;
if (!cfq_class_idle(cfqq))
cfqd->last_end_request = now;
- cfq_resort_rr_list(cfqq, 0);
-
if (sync)
RQ_CIC(rq)->last_end_request = now;
@@ -1863,9 +1759,6 @@ static void cfq_completed_request(request_queue_t *q, struct request *rq)
*/
static void cfq_prio_boost(struct cfq_queue *cfqq)
{
- const int ioprio_class = cfqq->ioprio_class;
- const int ioprio = cfqq->ioprio;
-
if (has_fs_excl()) {
/*
* boost idle prio on transactions that would lock out other
@@ -1884,12 +1777,6 @@ static void cfq_prio_boost(struct cfq_queue *cfqq)
if (cfqq->ioprio != cfqq->org_ioprio)
cfqq->ioprio = cfqq->org_ioprio;
}
-
- /*
- * refile between round-robin lists if we moved the priority class
- */
- if ((ioprio_class != cfqq->ioprio_class || ioprio != cfqq->ioprio))
- cfq_resort_rr_list(cfqq, 0);
}
static inline int __cfq_may_queue(struct cfq_queue *cfqq)
@@ -2127,9 +2014,7 @@ static void *cfq_init_queue(request_queue_t *q)
memset(cfqd, 0, sizeof(*cfqd));
- for (i = 0; i < CFQ_PRIO_LISTS; i++)
- INIT_LIST_HEAD(&cfqd->rr_list[i]);
-
+ cfqd->service_tree = RB_ROOT;
INIT_LIST_HEAD(&cfqd->cur_rr);
INIT_LIST_HEAD(&cfqd->idle_rr);
INIT_LIST_HEAD(&cfqd->cic_list);