* [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2]
@ 2012-10-08 21:44 Vivek Goyal
2012-10-08 21:44 ` [PATCH 1/8] cfq-iosched: Make cfq_scale_slice() usable for both queues and groups Vivek Goyal
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
Hi,
This is V2 of the patch series to use same scheduling logic for cfq
queues as we use for cfq groups. This applies on top of cfq cleanup
changes I posted here.
http://lkml.indiana.edu/hypermail/linux/kernel/1210.0/01966.html
Bot the patch series have been generated on top of 3.6 in linus tree.
Why to change scheduling algorithm
==================================
Currently we use two scheduling algorithms at two different layers.
vdisktime based algorithm for groups and round robin for cfq queues.
Now we are planning to do more development in cfqq so that it can
handle group hierarchies. And I think before we do that we first need
to change the code so that both queues and groups are treated same way
when it comes to scheduling. Otherwise the whole thing is a mess.
This patch series does not merge the queue and group scheduling code.
It just tries to make these similar enough so that merging of code
becomes easier in future patches.
What's the functionality impact
===============================
Total disk share (time slices) allocated to each prio queue should
become predictable and every queue gets its fair share of disk
in proportion to its prio/weight.
This works only if we idle on the cfq queue (rotational disk and
low end SSD). For SSD with queue depth more than certain requests,
we don't idle on queues and there will be no priority differentiation
between various queues.
In did my testing on a SATA rotational disk and lauched 8 processes
with prio 0-7, all doing sequential reads. Here are the results.
0 1 3 4 4 5 6 7
vanilla(MB/s) 14.0 9.8 7.6 6.4 5.0 3.4 2.2 1.6
patched(MB/s) 27.5 15.2 8.0 4.8 3.1 2.1 1.3 .8
Notice that service differentiation of IO between different prio
level has significantly on this disk. I guess that's a good thing.
Roughly each prio level should get 1.6 times more time slice as
compared to previous prio level.
This is easily modifiable in code if people find this kind of
service differentiation is too much.
Also note that total throughput of disk has increased. I think it
has happened because low prio queue gets scheduled less number
of times hence resulting in less number of seeks.
Thanks
Vivek
Vivek Goyal (8):
cfq-iosched: Make cfq_scale_slice() usable for both queues and groups
cfq-iosched: make new_cfqq variable bool
cfq-iosced: Do the round robin selection of workload type
cfq-iosched: Put new queue at the end of servie tree always
cfq-iosched: Remove residual slice logic
cfq-iosched: put cooperating queue at the front of service tree
cfq-iosched: Use same scheduling algorithm for groups and queues
cfq-iosched: Wait for queue to get busy even if this is not last
queue in group
block/blk-cgroup.h | 2 +-
block/cfq-iosched.c | 313 ++++++++++++++++++++++++++++++---------------------
2 files changed, 187 insertions(+), 128 deletions(-)
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/8] cfq-iosched: Make cfq_scale_slice() usable for both queues and groups
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:44 ` [PATCH 2/8] cfq-iosched: make new_cfqq variable bool Vivek Goyal
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
Make cfq_scale_slice() usable both for queues and groups by taking in
weight as a parameter.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 2f2b215..c0b492e 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -851,12 +851,12 @@ 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 u64 cfq_scale_slice(unsigned long delta, struct cfq_group *cfqg)
+static inline u64 cfq_scale_slice(unsigned long delta, unsigned int weight)
{
u64 d = delta << CFQ_SERVICE_SHIFT;
d = d * CFQ_WEIGHT_DEFAULT;
- do_div(d, cfqg->weight);
+ do_div(d, weight);
return d;
}
@@ -1299,7 +1299,7 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg,
/* Can't update vdisktime while group is on service tree */
cfq_group_service_tree_del(st, cfqg);
- cfqg->vdisktime += cfq_scale_slice(charge, cfqg);
+ cfqg->vdisktime += cfq_scale_slice(charge, cfqg->weight);
/* If a new weight was requested, update now, off tree */
cfq_group_service_tree_add(st, cfqg);
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/8] cfq-iosched: make new_cfqq variable bool
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
2012-10-08 21:44 ` [PATCH 1/8] cfq-iosched: Make cfq_scale_slice() usable for both queues and groups Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:44 ` [PATCH 3/8] cfq-iosced: Do the round robin selection of workload type Vivek Goyal
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
Make new_cfqq bool. Also set the variable in the beginning of function.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index c0b492e..ad5f9b6 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1618,7 +1618,7 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
unsigned long rb_key;
struct cfq_rb_root *st;
int left;
- int new_cfqq = 1;
+ bool new_cfqq = RB_EMPTY_NODE(&cfqq->rb_node);
st = st_for(cfqq->cfqg, cfqq_class(cfqq), cfqq_type(cfqq));
if (cfq_class_idle(cfqq)) {
@@ -1645,8 +1645,7 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
rb_key += __cfqq ? __cfqq->rb_key : jiffies;
}
- if (!RB_EMPTY_NODE(&cfqq->rb_node)) {
- new_cfqq = 0;
+ if (!new_cfqq) {
/*
* same position, nothing more to do
*/
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/8] cfq-iosced: Do the round robin selection of workload type
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
2012-10-08 21:44 ` [PATCH 1/8] cfq-iosched: Make cfq_scale_slice() usable for both queues and groups Vivek Goyal
2012-10-08 21:44 ` [PATCH 2/8] cfq-iosched: make new_cfqq variable bool Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:44 ` [PATCH 4/8] cfq-iosched: Put new queue at the end of servie tree always Vivek Goyal
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
We have three subclass of workloads (SYNC_NODILE, SYNC and ASYNC) for
prio class RT and BE. And cfq needs to select a workload to dispatch
from before it selects a cfqq.
Current workload selection seems to be selecting a workload which has
the lowest key for a cfqq. So effectively all three service tree are
kind of related using that cfqq->rb_key. And that cfqq->rb_key is
influenced by time (apart from other factors). So basically service
tree keys are influenced by time of queuing as well as prio of queue
and service trees are related.
I want to change the workload selection logic a bit for following
reason.
I am moving away from the notion of time for rb_key. The reason
being that I am bringing queue scheduling logic closer to group
scheduling logic where every service tree keeps track of virtual
time (vdisktime) based on disk share used by that group.
That means we can't use real time on queue service tree. And that
also means that virtual time of every service tree will move
independently and I can't use current logic of workload selection
which assumes that cfqq->rb_key of all three service tree are
co-related.
I think one simple way to select workload is do the round robin
among active workloads. That way each workload gets it fair share.
(Though we override that later by allowing preemption of of async
queue by sync queue).
In case a group is freshly queued, we always start with sync-noidle
workload first as that seems to be most important.
So making this change allows us to bring closer to group scheduling
logic, simplifies the workload selection logic and makes the workload
selection more predictable. I am not expecting any serious adverse effects
of this change.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 51 ++++++++++++++++++++++++++++++++-------------------
1 files changed, 32 insertions(+), 19 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index ad5f9b6..58f1bdc 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -166,9 +166,10 @@ enum wl_class_t {
* Second index in the service_trees.
*/
enum wl_type_t {
- ASYNC_WORKLOAD = 0,
- SYNC_NOIDLE_WORKLOAD = 1,
- SYNC_WORKLOAD = 2
+ SYNC_NOIDLE_WORKLOAD = 0,
+ SYNC_WORKLOAD = 1,
+ ASYNC_WORKLOAD = 2,
+ WL_TYPE_NR,
};
struct cfqg_stats {
@@ -248,10 +249,14 @@ struct cfq_group {
struct cfq_rb_root service_trees[2][3];
struct cfq_rb_root service_tree_idle;
+ /* Saved state when group is scheduled out */
unsigned long saved_wl_slice;
enum wl_type_t saved_wl_type;
enum wl_class_t saved_wl_class;
+ /* Last workload type chosen to run in this group */
+ enum wl_type_t last_run_wl_type;
+
/* number of requests that are on the dispatch list or inside driver */
int dispatched;
struct cfq_ttime ttime;
@@ -703,7 +708,7 @@ static inline void cfqg_stats_update_completion(struct cfq_group *cfqg,
for (i = 0; i <= IDLE_WORKLOAD; i++) \
for (j = 0, st = i < IDLE_WORKLOAD ? &cfqg->service_trees[i][j]\
: &cfqg->service_tree_idle; \
- (i < IDLE_WORKLOAD && j <= SYNC_WORKLOAD) || \
+ (i < IDLE_WORKLOAD && j < WL_TYPE_NR) || \
(i == IDLE_WORKLOAD && j == 0); \
j++, st = i < IDLE_WORKLOAD ? \
&cfqg->service_trees[i][j]: NULL) \
@@ -1246,6 +1251,7 @@ cfq_group_notify_queue_del(struct cfq_data *cfqd, struct cfq_group *cfqg)
cfq_log_cfqg(cfqd, cfqg, "del_from_rr group");
cfq_group_service_tree_del(st, cfqg);
cfqg->saved_wl_slice = 0;
+ cfqg->last_run_wl_type = WL_TYPE_NR;
cfqg_stats_update_dequeue(cfqg);
}
@@ -1339,6 +1345,7 @@ static void cfq_init_cfqg_base(struct cfq_group *cfqg)
RB_CLEAR_NODE(&cfqg->rb_node);
cfqg->ttime.last_end_request = jiffies;
+ cfqg->last_run_wl_type = WL_TYPE_NR;
}
#ifdef CONFIG_CFQ_GROUP_IOSCHED
@@ -2488,27 +2495,33 @@ static void cfq_setup_merge(struct cfq_queue *cfqq, struct cfq_queue *new_cfqq)
}
}
+static inline enum wl_type_t next_wl_type(enum wl_type_t wl_type)
+{
+ wl_type++;
+ if (wl_type >= WL_TYPE_NR)
+ wl_type = 0;
+ return wl_type;
+}
+
static enum wl_type_t cfq_choose_wl_type(struct cfq_data *cfqd,
struct cfq_group *cfqg, enum wl_class_t wl_class)
{
- struct cfq_queue *queue;
+ enum wl_type_t new_wl_type, old_wl_type;
+ struct cfq_rb_root *st;
int i;
- bool key_valid = false;
- unsigned long lowest_key = 0;
- enum wl_type_t cur_best = SYNC_NOIDLE_WORKLOAD;
-
- for (i = 0; i <= SYNC_WORKLOAD; ++i) {
- /* select the one with lowest rb_key */
- queue = cfq_rb_first(st_for(cfqg, wl_class, i));
- if (queue &&
- (!key_valid || time_before(queue->rb_key, lowest_key))) {
- lowest_key = queue->rb_key;
- cur_best = i;
- key_valid = true;
- }
+
+ old_wl_type = cfqg->last_run_wl_type;
+
+ for (i = 0; i < WL_TYPE_NR; i++) {
+ new_wl_type = next_wl_type(old_wl_type);
+ st = st_for(cfqg, wl_class, new_wl_type);
+ if (st->count)
+ break;
+ old_wl_type = new_wl_type;
}
- return cur_best;
+ cfqg->last_run_wl_type = new_wl_type;
+ return new_wl_type;
}
static void
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/8] cfq-iosched: Put new queue at the end of servie tree always
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
` (2 preceding siblings ...)
2012-10-08 21:44 ` [PATCH 3/8] cfq-iosced: Do the round robin selection of workload type Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:44 ` [PATCH 5/8] cfq-iosched: Remove residual slice logic Vivek Goyal
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
Currently cfq does round robin among cfqq and allocates bigger slices
to higher prio queue. But it also does additional logic of putting
higher priority queues ahead of lower priority queues in the service
tree. cfq_slice_offset() determines the postion of a queue in the
service tree.
I think it was done so that higher prio queues can get even higher
share of disk. Other advantage of it could be trying to provide service
differentiation on SSD where we don't idle on queues. So instead of
trying to provide bigger slice lenght for higher prio queue, one can
try to schedule the queue more number of times.
I don't think in practice it will work very well and reason being that
there are not many queues on service tree. As we don't idle, we dispatch
the request and expire the queue. So if queue depth is 32, ideally you
need to have more than 32 cfqq doing IO (assuming each queue dipatches
one read and waits for it to finish). And after that one can hope to
see some service differentiaton and that too very unpredictable. So
I would not count on it and rather keep it simple that on SSD we don't
get ioprio differentiation.
Even after we move to vdisktime logic, one can introduce above kind
of appriximations where higher prio/weight queue is not put at the
end but instead we give it some vdisktime boost.
So this patch puts every new queue at the end of service tree by
default. Existing queues get their position in the tree depending
on how much slice did they use recently and what's their prio/weight.
This patch only introduces the functionality of adding queues at
the end of service tree. Later patches will introduce the functionality
of determining vdisktime (hence position in service tree) based on
slice used and weight.
If a queue is being requeued, then it will already be on service tree
and we can't determine the rb_key of last element using cfq_rb_last().
So we always remove the queue from service tree first.
This is just an intermediate patch to show clearly how I am chaning
existing functionality. Did not want to lump it together with bigger
patches.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 49 ++++++++++++++++---------------------------------
1 files changed, 16 insertions(+), 33 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 58f1bdc..7136ede 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1139,16 +1139,6 @@ cfq_find_next_rq(struct cfq_data *cfqd, struct cfq_queue *cfqq,
return cfq_choose_req(cfqd, next, prev, blk_rq_pos(last));
}
-static unsigned long cfq_slice_offset(struct cfq_data *cfqd,
- struct cfq_queue *cfqq)
-{
- /*
- * just an approximation, should be ok.
- */
- return (cfqq->cfqg->nr_cfqq - 1) * (cfq_prio_slice(cfqd, 1, 0) -
- cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio));
-}
-
static inline s64
cfqg_key(struct cfq_rb_root *st, struct cfq_group *cfqg)
{
@@ -1628,41 +1618,34 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
bool new_cfqq = RB_EMPTY_NODE(&cfqq->rb_node);
st = st_for(cfqq->cfqg, cfqq_class(cfqq), cfqq_type(cfqq));
- if (cfq_class_idle(cfqq)) {
+ if (!new_cfqq) {
+ cfq_rb_erase(&cfqq->rb_node, cfqq->service_tree);
+ cfqq->service_tree = NULL;
+ }
+ if (!add_front) {
rb_key = CFQ_IDLE_DELAY;
parent = rb_last(&st->rb);
- if (parent && parent != &cfqq->rb_node) {
+ if (parent) {
__cfqq = rb_entry(parent, struct cfq_queue, rb_node);
rb_key += __cfqq->rb_key;
} else
rb_key += jiffies;
- } else if (!add_front) {
- /*
- * Get our rb key offset. Subtract any residual slice
- * value carried from last service. A negative resid
- * count indicates slice overrun, and this should position
- * the next service time further away in the tree.
- */
- rb_key = cfq_slice_offset(cfqd, cfqq) + jiffies;
- rb_key -= cfqq->slice_resid;
- cfqq->slice_resid = 0;
+ if (!cfq_class_idle(cfqq)) {
+ /*
+ * Subtract any residual slice * value carried from
+ * last service. A negative resid count indicates
+ * slice overrun, and this should position
+ * the next service time further away in the tree.
+ */
+ rb_key -= cfqq->slice_resid;
+ cfqq->slice_resid = 0;
+ }
} else {
rb_key = -HZ;
__cfqq = cfq_rb_first(st);
rb_key += __cfqq ? __cfqq->rb_key : jiffies;
}
- if (!new_cfqq) {
- /*
- * same position, nothing more to do
- */
- if (rb_key == cfqq->rb_key && cfqq->service_tree == st)
- return;
-
- cfq_rb_erase(&cfqq->rb_node, cfqq->service_tree);
- cfqq->service_tree = NULL;
- }
-
left = 1;
parent = NULL;
cfqq->service_tree = st;
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 5/8] cfq-iosched: Remove residual slice logic
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
` (3 preceding siblings ...)
2012-10-08 21:44 ` [PATCH 4/8] cfq-iosched: Put new queue at the end of servie tree always Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:44 ` [PATCH 6/8] cfq-iosched: put cooperating queue at the front of service tree Vivek Goyal
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
CFQ has this logic of residual slice so that a queue does not lose its
allocated share due to preemption. When we move to vdisktime logic,
a queue will not lose its share even if it preempted. (It will get queued
back into service tree with smaller key and get selected to run again).
So scheduling algorithm will take care of making sure preempted queue
still gets the fair share. Hence remove the logic of residual slice.
Note, vdisktime patches for queues are ahead in the series. I am first
cleaning up the code.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 53 +++++++++++++-------------------------------------
1 files changed, 14 insertions(+), 39 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 7136ede..6930eed 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -128,7 +128,6 @@ struct cfq_queue {
/* time when first request from queue completed and slice started. */
unsigned long slice_start;
unsigned long slice_end;
- long slice_resid;
/* pending priority requests */
int prio_pending;
@@ -1630,16 +1629,6 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
rb_key += __cfqq->rb_key;
} else
rb_key += jiffies;
- if (!cfq_class_idle(cfqq)) {
- /*
- * Subtract any residual slice * value carried from
- * last service. A negative resid count indicates
- * slice overrun, and this should position
- * the next service time further away in the tree.
- */
- rb_key -= cfqq->slice_resid;
- cfqq->slice_resid = 0;
- }
} else {
rb_key = -HZ;
__cfqq = cfq_rb_first(st);
@@ -2042,10 +2031,9 @@ static void __cfq_set_active_queue(struct cfq_data *cfqd,
* current cfqq expired its slice (or was too idle), select new one
*/
static void
-__cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
- bool timed_out)
+__cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq)
{
- cfq_log_cfqq(cfqd, cfqq, "slice expired t=%d", timed_out);
+ cfq_log_cfqq(cfqd, cfqq, "slice expired");
if (cfq_cfqq_wait_request(cfqq))
cfq_del_timer(cfqd, cfqq);
@@ -2062,17 +2050,6 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
if (cfq_cfqq_coop(cfqq) && CFQQ_SEEKY(cfqq))
cfq_mark_cfqq_split_coop(cfqq);
- /*
- * store what was left of this slice, if the queue idled/timed out
- */
- if (timed_out) {
- if (cfq_cfqq_slice_new(cfqq))
- cfqq->slice_resid = cfq_scaled_cfqq_slice(cfqd, cfqq);
- else
- cfqq->slice_resid = cfqq->slice_end - jiffies;
- cfq_log_cfqq(cfqd, cfqq, "resid=%ld", cfqq->slice_resid);
- }
-
cfq_group_served(cfqd, cfqq->cfqg, cfqq);
if (cfq_cfqq_on_rr(cfqq) && RB_EMPTY_ROOT(&cfqq->sort_list))
@@ -2089,12 +2066,12 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
}
}
-static inline void cfq_slice_expired(struct cfq_data *cfqd, bool timed_out)
+static inline void cfq_slice_expired(struct cfq_data *cfqd)
{
struct cfq_queue *cfqq = cfqd->active_queue;
if (cfqq)
- __cfq_slice_expired(cfqd, cfqq, timed_out);
+ __cfq_slice_expired(cfqd, cfqq);
}
/*
@@ -2720,7 +2697,7 @@ check_group_idle:
}
expire:
- cfq_slice_expired(cfqd, 0);
+ cfq_slice_expired(cfqd);
new_queue:
/*
* Current queue expired. Check if we have to switch to a new
@@ -2746,7 +2723,7 @@ static int __cfq_forced_dispatch_cfqq(struct cfq_queue *cfqq)
BUG_ON(!list_empty(&cfqq->fifo));
/* By default cfqq is not expired if it is empty. Do it explicitly */
- __cfq_slice_expired(cfqq->cfqd, cfqq, 0);
+ __cfq_slice_expired(cfqq->cfqd, cfqq);
return dispatched;
}
@@ -2760,7 +2737,7 @@ static int cfq_forced_dispatch(struct cfq_data *cfqd)
int dispatched = 0;
/* Expire the timeslice of the current active queue first */
- cfq_slice_expired(cfqd, 0);
+ cfq_slice_expired(cfqd);
while ((cfqq = cfq_get_next_queue_forced(cfqd)) != NULL) {
__cfq_set_active_queue(cfqd, cfqq);
dispatched += __cfq_forced_dispatch_cfqq(cfqq);
@@ -2941,7 +2918,7 @@ static int cfq_dispatch_requests(struct request_queue *q, int force)
cfqq->slice_dispatch >= cfq_prio_to_maxrq(cfqd, cfqq)) ||
cfq_class_idle(cfqq))) {
cfqq->slice_end = jiffies + 1;
- cfq_slice_expired(cfqd, 0);
+ cfq_slice_expired(cfqd);
}
cfq_log_cfqq(cfqd, cfqq, "dispatched a request");
@@ -2972,7 +2949,7 @@ static void cfq_put_queue(struct cfq_queue *cfqq)
cfqg = cfqq->cfqg;
if (unlikely(cfqd->active_queue == cfqq)) {
- __cfq_slice_expired(cfqd, cfqq, 0);
+ __cfq_slice_expired(cfqd, cfqq);
cfq_schedule_dispatch(cfqd);
}
@@ -3005,7 +2982,7 @@ static void cfq_put_cooperator(struct cfq_queue *cfqq)
static void cfq_exit_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq)
{
if (unlikely(cfqq == cfqd->active_queue)) {
- __cfq_slice_expired(cfqd, cfqq, 0);
+ __cfq_slice_expired(cfqd, cfqq);
cfq_schedule_dispatch(cfqd);
}
@@ -3440,7 +3417,7 @@ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
enum wl_type_t old_type = cfqq_type(cfqd->active_queue);
cfq_log_cfqq(cfqd, cfqq, "preempt");
- cfq_slice_expired(cfqd, 1);
+ cfq_slice_expired(cfqd);
/*
* workload type is changed, don't save slice, otherwise preempt
@@ -3682,7 +3659,7 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
* - when there is a close cooperator
*/
if (cfq_slice_used(cfqq) || cfq_class_idle(cfqq))
- cfq_slice_expired(cfqd, 1);
+ cfq_slice_expired(cfqd);
else if (sync && cfqq_empty &&
!cfq_close_cooperator(cfqd, cfqq)) {
cfq_arm_slice_timer(cfqd);
@@ -3858,7 +3835,6 @@ static void cfq_idle_slice_timer(unsigned long data)
struct cfq_data *cfqd = (struct cfq_data *) data;
struct cfq_queue *cfqq;
unsigned long flags;
- int timed_out = 1;
cfq_log(cfqd, "idle timer fired");
@@ -3866,7 +3842,6 @@ static void cfq_idle_slice_timer(unsigned long data)
cfqq = cfqd->active_queue;
if (cfqq) {
- timed_out = 0;
/*
* We saw a request before the queue expired, let it through
@@ -3899,7 +3874,7 @@ static void cfq_idle_slice_timer(unsigned long data)
cfq_clear_cfqq_deep(cfqq);
}
expire:
- cfq_slice_expired(cfqd, timed_out);
+ cfq_slice_expired(cfqd);
out_kick:
cfq_schedule_dispatch(cfqd);
out_cont:
@@ -3937,7 +3912,7 @@ static void cfq_exit_queue(struct elevator_queue *e)
spin_lock_irq(q->queue_lock);
if (cfqd->active_queue)
- __cfq_slice_expired(cfqd, cfqd->active_queue, 0);
+ __cfq_slice_expired(cfqd, cfqd->active_queue);
cfq_put_async_queues(cfqd);
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 6/8] cfq-iosched: put cooperating queue at the front of service tree
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
` (4 preceding siblings ...)
2012-10-08 21:44 ` [PATCH 5/8] cfq-iosched: Remove residual slice logic Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:44 ` [PATCH 7/8] cfq-iosched: Use same scheduling algorithm for groups and queues Vivek Goyal
2012-10-08 21:45 ` [PATCH 8/8] cfq-iosched: Wait for queue to get busy even if this is not last queue in group Vivek Goyal
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
Currently during select_queue(), if a cfqq runs out of requests, cfq
checks if there is a cooperating queue doing IO nearby. If yes, it
lets that queue run out of turn.
But while doing so, CFQ does not put that queue at the front of service
tree and select it. It just forces it to be active queue.
This will not play very nice with new algorithm where we keep track
of min_vdisktime on service tree and always select first queue on
the service tree to run.
So instead of force setting active queue, put desired cooperating
queue at the front of service tree (like preemption), and then
go through get_next_cfqq() to select first queue on service tree.
So end result still remains the same, just that this method will
play better with new algorithm.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 15 ++++++++++-----
1 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 6930eed..9ab6b4b 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -2117,12 +2117,11 @@ static struct cfq_queue *cfq_get_next_queue_forced(struct cfq_data *cfqd)
/*
* Get and set a new active queue for service.
*/
-static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd,
- struct cfq_queue *cfqq)
+static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd)
{
- if (!cfqq)
- cfqq = cfq_get_next_queue(cfqd);
+ struct cfq_queue *cfqq;
+ cfqq = cfq_get_next_queue(cfqd);
__cfq_set_active_queue(cfqd, cfqq);
return cfqq;
}
@@ -2653,6 +2652,12 @@ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
*/
new_cfqq = cfq_close_cooperator(cfqd, cfqq);
if (new_cfqq) {
+ /*
+ * This close cooperator queue will be selected next. Put
+ * it at the front of servie tree and then go through normal
+ * get_next_queue() logic.
+ */
+ cfq_service_tree_add(cfqd, new_cfqq, 1);
if (!cfqq->new_cfqq)
cfq_setup_merge(cfqq, new_cfqq);
goto expire;
@@ -2706,7 +2711,7 @@ new_queue:
if (!new_cfqq)
cfq_choose_cfqg(cfqd);
- cfqq = cfq_set_active_queue(cfqd, new_cfqq);
+ cfqq = cfq_set_active_queue(cfqd);
keep_queue:
return cfqq;
}
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 7/8] cfq-iosched: Use same scheduling algorithm for groups and queues
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
` (5 preceding siblings ...)
2012-10-08 21:44 ` [PATCH 6/8] cfq-iosched: put cooperating queue at the front of service tree Vivek Goyal
@ 2012-10-08 21:44 ` Vivek Goyal
2012-10-08 21:45 ` [PATCH 8/8] cfq-iosched: Wait for queue to get busy even if this is not last queue in group Vivek Goyal
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:44 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
Ok, finally, this patch introduces the notion of vdisktime for queues
and uses same scheduling algorithm for queues as groups.
It does not merge the two code paths yet, but philosophy of scheduling
is same. Once this gets stablized, then we will require some more
changes to the code to actually share the scheduling code between
queue and groups.
One important change here is mapping of io priority to weights.
prio levels 0-7 are now mapped over weight range as follows.
prio 0 1 2 3 4 5 6 7
weight 3435 2147 1342 838 524 327 204 128
This basically makes prio to weight mapping exponential using
following.
weight ~= 80 * pow(1.6, (8-prio))
This effectively also means that every prio level gets 1.6 times
of total time on disk as compared to previous one. So difference
between prio 0 and prio7 is around 26 times.
This patch does not treat queue and groups at same level. Existing
scheme of flat hierarchy continues. This series is just preparing CFQ
for more changes.
After this patch, CFQ queues get disk time share in proportion to
their weight.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/blk-cgroup.h | 2 +-
block/cfq-iosched.c | 166 +++++++++++++++++++++++++++++++++++++++------------
2 files changed, 128 insertions(+), 40 deletions(-)
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h
index 2459730..95254dd 100644
--- a/block/blk-cgroup.h
+++ b/block/blk-cgroup.h
@@ -24,7 +24,7 @@
/* CFQ specific, out here for blkcg->cfq_weight */
#define CFQ_WEIGHT_MIN 10
-#define CFQ_WEIGHT_MAX 1000
+#define CFQ_WEIGHT_MAX 3500
#define CFQ_WEIGHT_DEFAULT 500
#ifdef CONFIG_BLK_CGROUP
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 9ab6b4b..b0316f0 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -67,6 +67,20 @@ static struct kmem_cache *cfq_pool;
#define sample_valid(samples) ((samples) > 80)
#define rb_entry_cfqg(node) rb_entry((node), struct cfq_group, rb_node)
+/*
+ * Following table maps task prio to weight using approximately following
+ * formula.
+ *
+ * weight ~= 80 * pow(1.6, (8-prio))
+ *
+ * This is basically exponential function with base of 1.6. So every level
+ * of prio gets 60% more time slice than previous level.
+ */
+static unsigned int prio_to_weight[8] = {
+ /* prio 0 */ 3435, 2147, 1342, 838,
+ /* prio 4 */ 524, 327, 204, 128
+};
+
struct cfq_ttime {
unsigned long last_end_request;
@@ -105,7 +119,7 @@ struct cfq_queue {
/* service_tree member */
struct rb_node rb_node;
/* service_tree key */
- unsigned long rb_key;
+ u64 vdisktime;
/* prio tree member */
struct rb_node p_node;
/* prio tree root we belong to, if any */
@@ -355,6 +369,17 @@ struct cfq_data {
unsigned long last_delayed_sync;
};
+/*
+ * Map cfqq prio to weights.
+ * Prio 0-7 is mapped to weight range 0 - CFQ_WEIGHT_MAX
+ */
+static inline int cfq_prio_to_weight(unsigned short ioprio)
+{
+ WARN_ON(ioprio >= IOPRIO_BE_NR);
+
+ return prio_to_weight[ioprio];
+}
+
static struct cfq_group *cfq_get_next_cfqg(struct cfq_data *cfqd);
static struct cfq_rb_root *st_for(struct cfq_group *cfqg,
@@ -845,7 +870,6 @@ static inline int cfq_prio_slice(struct cfq_data *cfqd, bool sync,
const int base_slice = cfqd->cfq_slice[sync];
WARN_ON(prio >= IOPRIO_BE_NR);
-
return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - prio));
}
@@ -882,7 +906,7 @@ static inline u64 min_vdisktime(u64 min_vdisktime, u64 vdisktime)
return min_vdisktime;
}
-static void update_min_vdisktime(struct cfq_rb_root *st)
+static void update_min_vdisktime_group(struct cfq_rb_root *st)
{
struct cfq_group *cfqg;
@@ -893,6 +917,17 @@ static void update_min_vdisktime(struct cfq_rb_root *st)
}
}
+static void update_min_vdisktime_queue(struct cfq_rb_root *st)
+{
+ struct cfq_queue *cfqq;
+
+ if (st->left) {
+ cfqq = rb_entry(st->left, struct cfq_queue, rb_node);
+ st->min_vdisktime = max_vdisktime(st->min_vdisktime,
+ cfqq->vdisktime);
+ }
+}
+
/*
* get averaged number of queues of RT/BE priority.
* average is updated, with a formula that gives more weight to higher numbers,
@@ -1143,6 +1178,11 @@ cfqg_key(struct cfq_rb_root *st, struct cfq_group *cfqg)
{
return cfqg->vdisktime - st->min_vdisktime;
}
+static inline s64
+cfqq_key(struct cfq_rb_root *st, struct cfq_queue *cfqq)
+{
+ return cfqq->vdisktime - st->min_vdisktime;
+}
static void
__cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg)
@@ -1601,52 +1641,73 @@ cfq_link_cfqq_cfqg(struct cfq_queue *cfqq, struct cfq_group *cfqg) {
#endif /* GROUP_IOSCHED */
-/*
- * The cfqd->service_trees holds all pending cfq_queue's that have
- * requests waiting to be processed. It is sorted in the order that
- * we will service the queues.
- */
-static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
- bool add_front)
+static u64 calc_st_last_entry_vdisktime(struct cfq_rb_root *st)
{
- struct rb_node **p, *parent;
struct cfq_queue *__cfqq;
- unsigned long rb_key;
+ struct rb_node *parent;
+
+ parent = rb_last(&st->rb);
+ if (parent) {
+ __cfqq = rb_entry(parent, struct cfq_queue, rb_node);
+ return (__cfqq->vdisktime + CFQ_IDLE_DELAY);
+ } else
+ return st->min_vdisktime;
+}
+
+static u64 calc_cfqq_vdisktime(struct cfq_queue *cfqq, bool add_front,
+ bool new_cfqq, struct cfq_rb_root *old_st)
+{
+
+ unsigned int charge, unaccounted_sl = 0, weight;
struct cfq_rb_root *st;
- int left;
- bool new_cfqq = RB_EMPTY_NODE(&cfqq->rb_node);
st = st_for(cfqq->cfqg, cfqq_class(cfqq), cfqq_type(cfqq));
- if (!new_cfqq) {
- cfq_rb_erase(&cfqq->rb_node, cfqq->service_tree);
- cfqq->service_tree = NULL;
- }
- if (!add_front) {
- rb_key = CFQ_IDLE_DELAY;
- parent = rb_last(&st->rb);
- if (parent) {
- __cfqq = rb_entry(parent, struct cfq_queue, rb_node);
- rb_key += __cfqq->rb_key;
- } else
- rb_key += jiffies;
- } else {
- rb_key = -HZ;
- __cfqq = cfq_rb_first(st);
- rb_key += __cfqq ? __cfqq->rb_key : jiffies;
- }
+
+ /*
+ * This queue is being added to the front. This is overriding
+ * fairness algorithm so charging for disk time does not make
+ * any difference
+ */
+ if (add_front)
+ return st->min_vdisktime;
+
+ /* A new queue is being added. Just add it to end of service tree */
+ if (new_cfqq)
+ return calc_st_last_entry_vdisktime(st);
+
+ /*
+ * A queue is being requeued. If service tree has changed, then
+ * just put the queue at the end of current entries.
+ * */
+ if (st != old_st)
+ return calc_st_last_entry_vdisktime(st);
+
+ /*
+ * A cfqq is being requeued on same st. Charge the amount of slice
+ * used
+ */
+ weight = cfq_prio_to_weight(cfqq->ioprio);
+ charge = cfq_cfqq_slice_usage(cfqq, &unaccounted_sl);
+ return cfqq->vdisktime + cfq_scale_slice(charge, weight);
+}
+
+static void __cfq_service_tree_add(struct cfq_queue *cfqq, bool add_front)
+{
+ int left;
+ struct cfq_rb_root *st = cfqq->service_tree;
+ struct rb_node **p, *parent;
+ struct cfq_queue *__cfqq;
+ s64 key = cfqq_key(st, cfqq);
left = 1;
parent = NULL;
- cfqq->service_tree = st;
p = &st->rb.rb_node;
while (*p) {
parent = *p;
__cfqq = rb_entry(parent, struct cfq_queue, rb_node);
- /*
- * sort by key, that represents service time.
- */
- if (time_before(rb_key, __cfqq->rb_key))
+ if (key < cfqq_key(st, __cfqq) ||
+ ((add_front == true) && key == cfqq_key(st, __cfqq)))
p = &parent->rb_left;
else {
p = &parent->rb_right;
@@ -1657,10 +1718,34 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
if (left)
st->left = &cfqq->rb_node;
- cfqq->rb_key = rb_key;
rb_link_node(&cfqq->rb_node, parent, p);
rb_insert_color(&cfqq->rb_node, &st->rb);
st->count++;
+}
+
+/*
+ * The cfqd->st holds all pending cfq_queue's that have
+ * requests waiting to be processed. It is sorted in the order that
+ * we will service the queues.
+ */
+static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq,
+ bool add_front)
+{
+ struct cfq_rb_root *st, *old_st;
+ bool new_cfqq = RB_EMPTY_NODE(&cfqq->rb_node);
+
+ st = st_for(cfqq->cfqg, cfqq_class(cfqq), cfqq_type(cfqq));
+ old_st = cfqq->service_tree;
+
+ if (!new_cfqq) {
+ cfq_rb_erase(&cfqq->rb_node, cfqq->service_tree);
+ cfqq->service_tree = NULL;
+ }
+
+ cfqq->vdisktime = calc_cfqq_vdisktime(cfqq, add_front, new_cfqq,
+ old_st);
+ cfqq->service_tree = st;
+ __cfq_service_tree_add(cfqq, add_front);
if (add_front || !new_cfqq)
return;
cfq_group_notify_queue_add(cfqd, cfqq->cfqg);
@@ -2082,6 +2167,7 @@ static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
{
struct cfq_rb_root *st = st_for(cfqd->serving_group,
cfqd->serving_wl_class, cfqd->serving_wl_type);
+ struct cfq_queue *cfqq;
if (!cfqd->rq_queued)
return NULL;
@@ -2091,7 +2177,9 @@ static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
return NULL;
if (RB_EMPTY_ROOT(&st->rb))
return NULL;
- return cfq_rb_first(st);
+ cfqq = cfq_rb_first(st);
+ update_min_vdisktime_queue(st);
+ return cfqq;
}
static struct cfq_queue *cfq_get_next_queue_forced(struct cfq_data *cfqd)
@@ -2574,7 +2662,7 @@ static struct cfq_group *cfq_get_next_cfqg(struct cfq_data *cfqd)
if (RB_EMPTY_ROOT(&st->rb))
return NULL;
cfqg = cfq_rb_first_group(st);
- update_min_vdisktime(st);
+ update_min_vdisktime_group(st);
return cfqg;
}
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 8/8] cfq-iosched: Wait for queue to get busy even if this is not last queue in group
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
` (6 preceding siblings ...)
2012-10-08 21:44 ` [PATCH 7/8] cfq-iosched: Use same scheduling algorithm for groups and queues Vivek Goyal
@ 2012-10-08 21:45 ` Vivek Goyal
7 siblings, 0 replies; 9+ messages in thread
From: Vivek Goyal @ 2012-10-08 21:45 UTC (permalink / raw)
To: linux-kernel, axboe; +Cc: vgoyal, jmoyer, tj
With new algorithm, to maintain fairness we need to make sure that queue
does not get deleted from tree at slice expiry. Otherwise when new
request comes in very shortly after deletion, queue gets queued at
the end of service tree.
In general it is not too much of a problem as we had scaled slice
length based on prio/weight scaling to begin with. But low_latency
logic might introduce some imperfections.
We intoroduced wait for queue to get backlogged logic already. Just
that we trigger it only when queue is last queue in the group (In
an attempt to provide group its fair share). Just extend same wait
busy logic to queue too.
Because this little extra wait happens only if we have been idling
all along on the queue, I am not expecting any serious impact of this
little extra idling.
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
block/cfq-iosched.c | 8 ++------
1 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index b0316f0..e050ba3 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -2699,7 +2699,7 @@ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
return NULL;
/*
- * We were waiting for group to get backlogged. Expire the queue
+ * We were waiting for queue to get backlogged. Expire the queue
*/
if (cfq_cfqq_wait_busy(cfqq) && !RB_EMPTY_ROOT(&cfqq->sort_list))
goto expire;
@@ -2717,7 +2717,7 @@ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
* have been idling all along on this queue and it should be
* ok to wait for this request to complete.
*/
- if (cfqq->cfqg->nr_cfqq == 1 && RB_EMPTY_ROOT(&cfqq->sort_list)
+ if (RB_EMPTY_ROOT(&cfqq->sort_list)
&& cfqq->dispatched && cfq_should_idle(cfqd, cfqq)) {
cfqq = NULL;
goto keep_queue;
@@ -3646,10 +3646,6 @@ static bool cfq_should_wait_busy(struct cfq_data *cfqd, struct cfq_queue *cfqq)
if (!RB_EMPTY_ROOT(&cfqq->sort_list))
return false;
- /* If there are other queues in the group, don't wait */
- if (cfqq->cfqg->nr_cfqq > 1)
- return false;
-
/* the only queue in the group, but think time is big */
if (cfq_io_thinktime_big(cfqd, &cfqq->cfqg->ttime, true))
return false;
--
1.7.7.6
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-10-08 21:47 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-08 21:44 [PATCH 0/8] cfq-iosched: Use vdisktime based scheduling logic for cfq queues [V2] Vivek Goyal
2012-10-08 21:44 ` [PATCH 1/8] cfq-iosched: Make cfq_scale_slice() usable for both queues and groups Vivek Goyal
2012-10-08 21:44 ` [PATCH 2/8] cfq-iosched: make new_cfqq variable bool Vivek Goyal
2012-10-08 21:44 ` [PATCH 3/8] cfq-iosced: Do the round robin selection of workload type Vivek Goyal
2012-10-08 21:44 ` [PATCH 4/8] cfq-iosched: Put new queue at the end of servie tree always Vivek Goyal
2012-10-08 21:44 ` [PATCH 5/8] cfq-iosched: Remove residual slice logic Vivek Goyal
2012-10-08 21:44 ` [PATCH 6/8] cfq-iosched: put cooperating queue at the front of service tree Vivek Goyal
2012-10-08 21:44 ` [PATCH 7/8] cfq-iosched: Use same scheduling algorithm for groups and queues Vivek Goyal
2012-10-08 21:45 ` [PATCH 8/8] cfq-iosched: Wait for queue to get busy even if this is not last queue in group Vivek Goyal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®