linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages()
@ 2011-06-03 21:06 Vivek Goyal
  2011-06-03 21:06 ` [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait Vivek Goyal
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

Hi,

I have been trying to find ways to solve two problems with block IO controller
cgroups.

- Current throttling logic in IO controller does not throttle buffered WRITES.
  Well it does throttle all the WRITEs at device and by that time buffered
  WRITE have lost the submitter's context and most of the IO comes in 
  flusher thread's context at device. Hence currently buffered write
  throttling is not supported.

- All WRITEs are throttled at device level and this can easily lead to
  filesystem serialization.

  One simple example is that if a process writes some pages to cache and
  then does fsync(), and process gets throttled then it locks up the
  filesystem. With ext4, I noticed that even a simple "ls" does not make
  progress. The reason boils down to the fact that filesystems are not
  aware of cgroups and one of the things which get serialized is journalling
  in ordered mode.

  So even if we do something to carry submitter's cgroup information
  to device and do throttling there, it will lead to serialization of
  filesystems and is not a good idea.

So how to go about fixing it. There seem to be two options.

- Throttling should still be done at device level. Make filesystems aware
  of cgroups so that multiple transactions can make progress in parallel
  (per cgroup) and there are no shared resources across cgroups in
  filesystems which can lead to serialization.

- Throttle WRITEs while they are entering the cache and not after that.
  Something like balance_dirty_pages(). Direct IO is still throttled
  at device level. That way, we can avoid these journalling related
  serialization issues w.r.t trottling.

  But the big issue with this approach is that we control the IO rate
  entering into the cache and not IO rate at the device. That way it
  can happen that flusher later submits lots of WRITEs to device and
  we will see a periodic IO spike on end node.

  So this mechanism helps a bit but is not the complete solution. It
  can primarily help those folks which have the system resources and
  plenty of IO bandwidth available but they don't want to give it to
  customer because it is not a premium customer etc.

Option 1 seem to be really hard to fix. Filesystems have not been written
keeping cgroups in mind. So I am really skeptical that I can convince file
system designers to make fundamental changes in filesystems and journalling
code to make them cgroup aware.

Hence with this patch series I have implemented option 2. Option 2 is not
the best solution but atleast it gives us some control then not having any
control on buffered writes. Andrea Righi did similar patches in the past
here.

https://lkml.org/lkml/2011/2/28/115

This patch series had issues w.r.t to interaction between bio and task
throttling, so I redid it.

Design
------

IO controller already has the capability to keep track of IO rates of
a group and enqueue the bio in internal queues if group exceeds the
rate and dispatch these bios later.

This patch series also introduce the capability to throttle a dirtying
task in balance_dirty_pages_ratelimited_nr(). Now no WRITES except
direct WRITES will be throttled at device level. If a dirtying task
exceeds its configured IO rate, it is put on a group wait queue and
woken up when it can dirty more pages.

No new interface has been introduced and both direct IO as well as buffered
IO make use of common IO rate limit.

How To
=====
- Create a cgroup and limit it to 1MB/s for writes.
  echo "8:16 1024000" > /cgroup/blk/test1/blkio.throttle.write_bps_device

- Launch dd thread in the cgroup
  dd if=/dev/zero of=zerofile bs=4K count=1K

 1024+0 records in
 1024+0 records out
 4194304 bytes (4.2 MB) copied, 4.00428 s, 1.0 MB/s

Any feedback is welcome.

Thanks
Vivek

Vivek Goyal (8):
  blk-throttle: convert wait routines to return jiffies to wait
  blk-throttle: do not enforce first queued bio check in
    tg_wait_dispatch
  blk-throttle: use IO size and direction as parameters to wait
    routines
  blk-throttle: Specify number of IOs during dispatch update
  blk-throttle: Get rid of extend slice trace message
  blk-throttle: core logic to throttle task while dirtying pages
  blk-throttle: Do not throttle WRITEs at device level except direct IO
  blk-throttle: enable throttling of task while dirtying pages

 block/blk-cgroup.c        |    6 +-
 block/blk-cgroup.h        |    2 +-
 block/blk-throttle.c      |  506 +++++++++++++++++++++++++++++++++++---------
 block/cfq-iosched.c       |    2 +-
 block/cfq.h               |    6 +-
 fs/direct-io.c            |    1 +
 include/linux/blk_types.h |    2 +
 include/linux/blkdev.h    |    5 +
 mm/page-writeback.c       |    3 +
 9 files changed, 421 insertions(+), 112 deletions(-)

-- 
1.7.4.4


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 2/8] blk-throttle: do not enforce first queued bio check in tg_wait_dispatch Vivek Goyal
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

Currently we return jiffies to wait for in a function parameter.
A cleaner way would be to return it as return value. Value 0 would mean
that there is no need to wait and anything > 0 means number of jiffies
to wait before bio can be dispatched.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c |   72 +++++++++++++++++++++-----------------------------
 1 files changed, 30 insertions(+), 42 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index a62be8d..791116e 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -590,8 +590,8 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
 			tg->slice_start[rw], tg->slice_end[rw], jiffies);
 }
 
-static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
-		struct bio *bio, unsigned long *wait)
+static unsigned long tg_wait_iops_limit(struct throtl_data *td,
+			struct throtl_grp *tg, struct bio *bio)
 {
 	bool rw = bio_data_dir(bio);
 	unsigned int io_allowed;
@@ -621,11 +621,8 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
 	else
 		io_allowed = tmp;
 
-	if (tg->io_disp[rw] + 1 <= io_allowed) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	if (tg->io_disp[rw] + 1 <= io_allowed)
+		return 0;
 
 	/* Calc approx time to dispatch */
 	jiffy_wait = ((tg->io_disp[rw] + 1) * HZ)/tg->iops[rw] + 1;
@@ -635,13 +632,15 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
 	else
 		jiffy_wait = 1;
 
-	if (wait)
-		*wait = jiffy_wait;
-	return 0;
+	return jiffy_wait;
 }
 
-static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
-		struct bio *bio, unsigned long *wait)
+/*
+ * Returns number of jiffies to wait to before IO can be dispatched according
+ * to bps limit.
+ */
+static unsigned long tg_wait_bps_limit(struct throtl_data *td,
+			struct throtl_grp *tg, struct bio *bio)
 {
 	bool rw = bio_data_dir(bio);
 	u64 bytes_allowed, extra_bytes, tmp;
@@ -659,11 +658,8 @@ static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
 	do_div(tmp, HZ);
 	bytes_allowed = tmp;
 
-	if (tg->bytes_disp[rw] + bio->bi_size <= bytes_allowed) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	if (tg->bytes_disp[rw] + bio->bi_size <= bytes_allowed)
+		return 0;
 
 	/* Calc approx time to dispatch */
 	extra_bytes = tg->bytes_disp[rw] + bio->bi_size - bytes_allowed;
@@ -677,9 +673,8 @@ static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
 	 * up we did. Add that time also.
 	 */
 	jiffy_wait = jiffy_wait + (jiffy_elapsed_rnd - jiffy_elapsed);
-	if (wait)
-		*wait = jiffy_wait;
-	return 0;
+
+	return jiffy_wait;
 }
 
 static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
@@ -691,9 +686,12 @@ static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
 /*
  * Returns whether one can dispatch a bio or not. Also returns approx number
  * of jiffies to wait before this bio is with-in IO rate and can be dispatched
+ *
+ * Retruns the number of jiffies one needs to wait before IO can be dispatched.
+ * 0 means, IO can be dispatched now.
  */
-static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
-				struct bio *bio, unsigned long *wait)
+static unsigned long
+tg_wait_dispatch(struct throtl_data *td, struct throtl_grp *tg, struct bio *bio)
 {
 	bool rw = bio_data_dir(bio);
 	unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
@@ -707,11 +705,8 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
 	BUG_ON(tg->nr_queued[rw] && bio != bio_list_peek(&tg->bio_lists[rw]));
 
 	/* If tg->bps = -1, then BW is unlimited */
-	if (tg->bps[rw] == -1 && tg->iops[rw] == -1) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	if (tg->bps[rw] == -1 && tg->iops[rw] == -1)
+		return 0;
 
 	/*
 	 * If previous slice expired, start a new one otherwise renew/extend
@@ -725,22 +720,15 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
 			throtl_extend_slice(td, tg, rw, jiffies + throtl_slice);
 	}
 
-	if (tg_with_in_bps_limit(td, tg, bio, &bps_wait)
-	    && tg_with_in_iops_limit(td, tg, bio, &iops_wait)) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	bps_wait = tg_wait_bps_limit(td, tg, bio);
+	iops_wait = tg_wait_iops_limit(td, tg, bio);
 
 	max_wait = max(bps_wait, iops_wait);
 
-	if (wait)
-		*wait = max_wait;
-
 	if (time_before(tg->slice_end[rw], jiffies + max_wait))
 		throtl_extend_slice(td, tg, rw, jiffies + max_wait);
 
-	return 0;
+	return max_wait;
 }
 
 static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio)
@@ -774,10 +762,10 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
 	struct bio *bio;
 
 	if ((bio = bio_list_peek(&tg->bio_lists[READ])))
-		tg_may_dispatch(td, tg, bio, &read_wait);
+		read_wait = tg_wait_dispatch(td, tg, bio);
 
 	if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
-		tg_may_dispatch(td, tg, bio, &write_wait);
+		write_wait = tg_wait_dispatch(td, tg, bio);
 
 	min_wait = min(read_wait, write_wait);
 	disptime = jiffies + min_wait;
@@ -819,7 +807,7 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
 	/* Try to dispatch 75% READS and 25% WRITES */
 
 	while ((bio = bio_list_peek(&tg->bio_lists[READ]))
-		&& tg_may_dispatch(td, tg, bio, NULL)) {
+		&& !tg_wait_dispatch(td, tg, bio)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
 		nr_reads++;
@@ -829,7 +817,7 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
 	}
 
 	while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
-		&& tg_may_dispatch(td, tg, bio, NULL)) {
+		&& !tg_wait_dispatch(td, tg, bio)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
 		nr_writes++;
@@ -1185,7 +1173,7 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 	}
 
 	/* Bio is with-in rate limit of group */
-	if (tg_may_dispatch(td, tg, bio, NULL)) {
+	if (!tg_wait_dispatch(td, tg, bio)) {
 		throtl_charge_bio(tg, bio);
 
 		/*
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/8] blk-throttle: do not enforce first queued bio check in tg_wait_dispatch
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
  2011-06-03 21:06 ` [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 3/8] blk-throttle: use IO size and direction as parameters to wait routines Vivek Goyal
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

Soon we will be using same wait routines for checking the wait for
task also and no bio is involved. So get rid of any bio dependencies
and leave it to caller that if andy bio or task is queued in the group
in same direction, then don't call this routine.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c |    8 --------
 1 files changed, 0 insertions(+), 8 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 791116e..1259ce6 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -696,14 +696,6 @@ tg_wait_dispatch(struct throtl_data *td, struct throtl_grp *tg, struct bio *bio)
 	bool rw = bio_data_dir(bio);
 	unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
 
-	/*
- 	 * Currently whole state machine of group depends on first bio
-	 * queued in the group bio list. So one should not be calling
-	 * this function with a different bio if there are other bios
-	 * queued.
-	 */
-	BUG_ON(tg->nr_queued[rw] && bio != bio_list_peek(&tg->bio_lists[rw]));
-
 	/* If tg->bps = -1, then BW is unlimited */
 	if (tg->bps[rw] == -1 && tg->iops[rw] == -1)
 		return 0;
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/8] blk-throttle: use IO size and direction as parameters to wait routines
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
  2011-06-03 21:06 ` [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait Vivek Goyal
  2011-06-03 21:06 ` [PATCH 2/8] blk-throttle: do not enforce first queued bio check in tg_wait_dispatch Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 4/8] blk-throttle: Specify number of IOs during dispatch update Vivek Goyal
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

I want to reuse wait routines for task wait also. Hence get rid of
dependency of bio being passed in. Instead pass in direction of IO
and size of IO.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c |   52 +++++++++++++++++++++++--------------------------
 1 files changed, 24 insertions(+), 28 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 1259ce6..541830c 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -591,9 +591,8 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
 }
 
 static unsigned long tg_wait_iops_limit(struct throtl_data *td,
-			struct throtl_grp *tg, struct bio *bio)
+			struct throtl_grp *tg, bool rw, unsigned int nr_ios)
 {
-	bool rw = bio_data_dir(bio);
 	unsigned int io_allowed;
 	unsigned long jiffy_elapsed, jiffy_wait, jiffy_elapsed_rnd;
 	u64 tmp;
@@ -621,11 +620,11 @@ static unsigned long tg_wait_iops_limit(struct throtl_data *td,
 	else
 		io_allowed = tmp;
 
-	if (tg->io_disp[rw] + 1 <= io_allowed)
+	if (tg->io_disp[rw] + nr_ios <= io_allowed)
 		return 0;
 
 	/* Calc approx time to dispatch */
-	jiffy_wait = ((tg->io_disp[rw] + 1) * HZ)/tg->iops[rw] + 1;
+	jiffy_wait = ((tg->io_disp[rw] + nr_ios) * HZ)/tg->iops[rw] + 1;
 
 	if (jiffy_wait > jiffy_elapsed)
 		jiffy_wait = jiffy_wait - jiffy_elapsed;
@@ -640,9 +639,8 @@ static unsigned long tg_wait_iops_limit(struct throtl_data *td,
  * to bps limit.
  */
 static unsigned long tg_wait_bps_limit(struct throtl_data *td,
-			struct throtl_grp *tg, struct bio *bio)
+			struct throtl_grp *tg, bool rw, unsigned int sz)
 {
-	bool rw = bio_data_dir(bio);
 	u64 bytes_allowed, extra_bytes, tmp;
 	unsigned long jiffy_elapsed, jiffy_wait, jiffy_elapsed_rnd;
 
@@ -658,11 +656,11 @@ static unsigned long tg_wait_bps_limit(struct throtl_data *td,
 	do_div(tmp, HZ);
 	bytes_allowed = tmp;
 
-	if (tg->bytes_disp[rw] + bio->bi_size <= bytes_allowed)
+	if (tg->bytes_disp[rw] + sz <= bytes_allowed)
 		return 0;
 
 	/* Calc approx time to dispatch */
-	extra_bytes = tg->bytes_disp[rw] + bio->bi_size - bytes_allowed;
+	extra_bytes = tg->bytes_disp[rw] + sz - bytes_allowed;
 	jiffy_wait = div64_u64(extra_bytes * HZ, tg->bps[rw]);
 
 	if (!jiffy_wait)
@@ -690,10 +688,9 @@ static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
  * Retruns the number of jiffies one needs to wait before IO can be dispatched.
  * 0 means, IO can be dispatched now.
  */
-static unsigned long
-tg_wait_dispatch(struct throtl_data *td, struct throtl_grp *tg, struct bio *bio)
+static unsigned long tg_wait_dispatch(struct throtl_data *td,
+	struct throtl_grp *tg, bool rw, unsigned int sz, unsigned int nr_ios)
 {
-	bool rw = bio_data_dir(bio);
 	unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
 
 	/* If tg->bps = -1, then BW is unlimited */
@@ -712,8 +709,8 @@ tg_wait_dispatch(struct throtl_data *td, struct throtl_grp *tg, struct bio *bio)
 			throtl_extend_slice(td, tg, rw, jiffies + throtl_slice);
 	}
 
-	bps_wait = tg_wait_bps_limit(td, tg, bio);
-	iops_wait = tg_wait_iops_limit(td, tg, bio);
+	bps_wait = tg_wait_bps_limit(td, tg, rw, sz);
+	iops_wait = tg_wait_iops_limit(td, tg, rw, nr_ios);
 
 	max_wait = max(bps_wait, iops_wait);
 
@@ -723,16 +720,14 @@ tg_wait_dispatch(struct throtl_data *td, struct throtl_grp *tg, struct bio *bio)
 	return max_wait;
 }
 
-static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio)
+static void throtl_charge_io(struct throtl_grp *tg, bool rw, unsigned int sz,
+				unsigned int nr_ios, bool sync)
 {
-	bool rw = bio_data_dir(bio);
-	bool sync = bio->bi_rw & REQ_SYNC;
-
-	/* Charge the bio to the group */
-	tg->bytes_disp[rw] += bio->bi_size;
-	tg->io_disp[rw]++;
+	/* Charge the io to the group */
+	tg->bytes_disp[rw] += sz;
+	tg->io_disp[rw] += nr_ios;
 
-	blkiocg_update_dispatch_stats(&tg->blkg, bio->bi_size, rw, sync);
+	blkiocg_update_dispatch_stats(&tg->blkg, sz, rw, sync);
 }
 
 static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
@@ -754,10 +749,10 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
 	struct bio *bio;
 
 	if ((bio = bio_list_peek(&tg->bio_lists[READ])))
-		read_wait = tg_wait_dispatch(td, tg, bio);
+		read_wait = tg_wait_dispatch(td, tg, READ, bio->bi_size, 1);
 
 	if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
-		write_wait = tg_wait_dispatch(td, tg, bio);
+		write_wait = tg_wait_dispatch(td, tg, WRITE, bio->bi_size, 1);
 
 	min_wait = min(read_wait, write_wait);
 	disptime = jiffies + min_wait;
@@ -781,7 +776,7 @@ static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
 	BUG_ON(td->nr_queued[rw] <= 0);
 	td->nr_queued[rw]--;
 
-	throtl_charge_bio(tg, bio);
+	throtl_charge_io(tg, rw, bio->bi_size, 1, bio->bi_rw & REQ_SYNC);
 	bio_list_add(bl, bio);
 	bio->bi_rw |= REQ_THROTTLED;
 
@@ -799,7 +794,7 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
 	/* Try to dispatch 75% READS and 25% WRITES */
 
 	while ((bio = bio_list_peek(&tg->bio_lists[READ]))
-		&& !tg_wait_dispatch(td, tg, bio)) {
+		&& !tg_wait_dispatch(td, tg, READ, bio->bi_size, 1)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
 		nr_reads++;
@@ -809,7 +804,7 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
 	}
 
 	while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
-		&& !tg_wait_dispatch(td, tg, bio)) {
+		&& !tg_wait_dispatch(td, tg, WRITE, bio->bi_size, 1)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
 		nr_writes++;
@@ -1165,8 +1160,9 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 	}
 
 	/* Bio is with-in rate limit of group */
-	if (!tg_wait_dispatch(td, tg, bio)) {
-		throtl_charge_bio(tg, bio);
+	if (!tg_wait_dispatch(td, tg, rw, bio->bi_size, 1)) {
+		throtl_charge_io(tg, rw, bio->bi_size, 1,
+					bio->bi_rw & REQ_SYNC);
 
 		/*
 		 * We need to trim slice even when bios are not being queued
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/8] blk-throttle: Specify number of IOs during dispatch update
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
                   ` (2 preceding siblings ...)
  2011-06-03 21:06 ` [PATCH 3/8] blk-throttle: use IO size and direction as parameters to wait routines Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 5/8] blk-throttle: Get rid of extend slice trace message Vivek Goyal
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

currently we assume the number of IOs to be 1 while dispatch stats
are being updated. It could happen that we are clubbing multiple
dispatches and updating in one shot. Throttling logic currently will
count one page as one IO for dirty page throttling. Hence modify the
logic to be able to specify number of IOs to associate with the
update.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-cgroup.c   |    6 +++---
 block/blk-cgroup.h   |    2 +-
 block/blk-throttle.c |    2 +-
 block/cfq-iosched.c  |    2 +-
 block/cfq.h          |    6 +++---
 5 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index bcaf16e..c0815c5 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -394,8 +394,8 @@ EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used);
  * should be called under rcu read lock or queue lock to make sure blkg pointer
  * is valid.
  */
-void blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-				uint64_t bytes, bool direction, bool sync)
+void blkiocg_update_dispatch_stats(struct blkio_group *blkg, uint64_t bytes,
+			unsigned int nr_ios, bool direction, bool sync)
 {
 	struct blkio_group_stats_cpu *stats_cpu;
 	unsigned long flags;
@@ -412,7 +412,7 @@ void blkiocg_update_dispatch_stats(struct blkio_group *blkg,
 	u64_stats_update_begin(&stats_cpu->syncp);
 	stats_cpu->sectors += bytes >> 9;
 	blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_SERVICED],
-			1, direction, sync);
+			nr_ios, direction, sync);
 	blkio_add_stat(stats_cpu->stat_arr_cpu[BLKIO_STAT_CPU_SERVICE_BYTES],
 			bytes, direction, sync);
 	u64_stats_update_end(&stats_cpu->syncp);
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h
index a71d290..7202dcc 100644
--- a/block/blk-cgroup.h
+++ b/block/blk-cgroup.h
@@ -318,7 +318,7 @@ void blkiocg_update_timeslice_used(struct blkio_group *blkg,
 					unsigned long time,
 					unsigned long unaccounted_time);
 void blkiocg_update_dispatch_stats(struct blkio_group *blkg, uint64_t bytes,
-						bool direction, bool sync);
+				unsigned int nr_ios, bool direction, bool sync);
 void blkiocg_update_completion_stats(struct blkio_group *blkg,
 	uint64_t start_time, uint64_t io_start_time, bool direction, bool sync);
 void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction,
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 541830c..81df3ea 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -727,7 +727,7 @@ static void throtl_charge_io(struct throtl_grp *tg, bool rw, unsigned int sz,
 	tg->bytes_disp[rw] += sz;
 	tg->io_disp[rw] += nr_ios;
 
-	blkiocg_update_dispatch_stats(&tg->blkg, sz, rw, sync);
+	blkiocg_update_dispatch_stats(&tg->blkg, sz, nr_ios, rw, sync);
 }
 
 static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 7c52d68..9a1e335 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -2064,7 +2064,7 @@ static void cfq_dispatch_insert(struct request_queue *q, struct request *rq)
 	cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]++;
 	cfqq->nr_sectors += blk_rq_sectors(rq);
 	cfq_blkiocg_update_dispatch_stats(&cfqq->cfqg->blkg, blk_rq_bytes(rq),
-					rq_data_dir(rq), rq_is_sync(rq));
+					1, rq_data_dir(rq), rq_is_sync(rq));
 }
 
 /*
diff --git a/block/cfq.h b/block/cfq.h
index 2a15592..1795b3d 100644
--- a/block/cfq.h
+++ b/block/cfq.h
@@ -56,9 +56,9 @@ cfq_blkiocg_update_set_idle_time_stats(struct blkio_group *blkg)
 }
 
 static inline void cfq_blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-				uint64_t bytes, bool direction, bool sync)
+		uint64_t bytes, unsigned int nr_ios, bool direction, bool sync)
 {
-	blkiocg_update_dispatch_stats(blkg, bytes, direction, sync);
+	blkiocg_update_dispatch_stats(blkg, bytes, nr_ios, direction, sync);
 }
 
 static inline void cfq_blkiocg_update_completion_stats(struct blkio_group *blkg, uint64_t start_time, uint64_t io_start_time, bool direction, bool sync)
@@ -101,7 +101,7 @@ static inline void
 cfq_blkiocg_update_set_idle_time_stats(struct blkio_group *blkg) {}
 
 static inline void cfq_blkiocg_update_dispatch_stats(struct blkio_group *blkg,
-				uint64_t bytes, bool direction, bool sync) {}
+	uint64_t bytes, unsigned int nr_ios, bool direction, bool sync) {}
 static inline void cfq_blkiocg_update_completion_stats(struct blkio_group *blkg, uint64_t start_time, uint64_t io_start_time, bool direction, bool sync) {}
 
 static inline void cfq_blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/8] blk-throttle: Get rid of extend slice trace message
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
                   ` (3 preceding siblings ...)
  2011-06-03 21:06 ` [PATCH 4/8] blk-throttle: Specify number of IOs during dispatch update Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 6/8] blk-throttle: core logic to throttle task while dirtying pages Vivek Goyal
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

I am about to introduce more tracing messages for task throttling. There
seem to be too many messages and I thought that this extend slice one
is not that useful. so kill it.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 81df3ea..e52f630 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -515,9 +515,6 @@ static inline void throtl_extend_slice(struct throtl_data *td,
 		struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
 {
 	tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
-	throtl_log_tg(td, tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu",
-			rw == READ ? 'R' : 'W', tg->slice_start[rw],
-			tg->slice_end[rw], jiffies);
 }
 
 /* Determine if previously allocated or extended slice is complete or not */
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/8] blk-throttle: core logic to throttle task while dirtying pages
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
                   ` (4 preceding siblings ...)
  2011-06-03 21:06 ` [PATCH 5/8] blk-throttle: Get rid of extend slice trace message Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 7/8] blk-throttle: Do not throttle WRITEs at device level except direct IO Vivek Goyal
  2011-06-03 21:06 ` [PATCH 8/8] blk-throttle: enable throttling of task while dirtying pages Vivek Goyal
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

This is the core logic which enables throttling logic to also
throttle tasks while dirtying pages. I basically create the
infrastructure where a wait queue is maintained per group
and if task exceeds its rate limit, task is put to sleep on
wait queue and woken up later.

Though currently we throttle tasks for WRITE requests and
not for READ requests, I have built the logic to support
task throttling for both direction (similar to bio throttling).
This will allow us to do any task READ throttling also easily,
if needed in future.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c |  399 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 358 insertions(+), 41 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index e52f630..c8bdc59 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -32,6 +32,12 @@ struct throtl_rb_root {
 	unsigned long min_disptime;
 };
 
+/* Whether to dispatch a bio or task in next round of a dispatch from a group */
+enum dispatch_type {
+	DISPATCH_BIO,
+	DISPATCH_TASK,
+};
+
 #define THROTL_RB_ROOT	(struct throtl_rb_root) { .rb = RB_ROOT, .left = NULL, \
 			.count = 0, .min_disptime = 0}
 
@@ -59,7 +65,10 @@ struct throtl_grp {
 	struct bio_list bio_lists[2];
 
 	/* Number of queued bios on READ and WRITE lists */
-	unsigned int nr_queued[2];
+	unsigned int nr_queued_bio[2];
+
+	/* Number of queued tasks */
+	unsigned int nr_queued_tsk[2];
 
 	/* bytes per second rate limits */
 	uint64_t bps[2];
@@ -80,6 +89,21 @@ struct throtl_grp {
 	int limits_changed;
 
 	struct rcu_head rcu_head;
+
+	/* READ and WRITE wait lists for task throttle */
+	wait_queue_head_t wait[2];
+
+	/* Number of unaccounted dirty pages in this group */
+	unsigned int unaccounted_dirty;
+
+	/* The number of pages to be charged to first task on wait queue */
+	unsigned int active_task_charge[2];
+
+	/*
+	 * Keeps track of whether we will dispatch a bio or task in next
+	 * round of dispatch (for each dir)
+	 */
+	enum dispatch_type active_dispatch[2];
 };
 
 struct throtl_data
@@ -94,7 +118,10 @@ struct throtl_data
 	struct request_queue *queue;
 
 	/* Total Number of queued bios on READ and WRITE lists */
-	unsigned int nr_queued[2];
+	unsigned int nr_queued_bio[2];
+
+	/* Total Number of queued tasks on READ and WRITE lists */
+	unsigned int nr_queued_tsk[2];
 
 	/*
 	 * number of total undestroyed groups
@@ -142,9 +169,19 @@ static inline struct throtl_grp *tg_of_blkg(struct blkio_group *blkg)
 	return NULL;
 }
 
+static inline int total_queued_bios(struct throtl_data *td)
+{
+	return (td->nr_queued_bio[0] + td->nr_queued_bio[1]);
+}
+
+static inline int total_queued_tasks(struct throtl_data *td)
+{
+	return (td->nr_queued_tsk[0] + td->nr_queued_tsk[1]);
+}
+
 static inline int total_nr_queued(struct throtl_data *td)
 {
-	return (td->nr_queued[0] + td->nr_queued[1]);
+	return total_queued_bios(td) + total_queued_tasks(td);
 }
 
 static inline struct throtl_grp *throtl_ref_get_tg(struct throtl_grp *tg)
@@ -192,6 +229,9 @@ static void throtl_init_group(struct throtl_grp *tg)
 	tg->bps[0] = tg->bps[1] = -1;
 	tg->iops[0] = tg->iops[1] = -1;
 
+	init_waitqueue_head(&tg->wait[READ]);
+	init_waitqueue_head(&tg->wait[WRITE]);
+
 	/*
 	 * Take the initial reference that will be released on destroy
 	 * This can be thought of a joint reference by cgroup and
@@ -727,6 +767,14 @@ static void throtl_charge_io(struct throtl_grp *tg, bool rw, unsigned int sz,
 	blkiocg_update_dispatch_stats(&tg->blkg, sz, nr_ios, rw, sync);
 }
 
+static void throtl_charge_dirty_io(struct throtl_grp *tg, bool rw,
+		unsigned int sz, unsigned int nr_ios, bool sync)
+{
+	tg->unaccounted_dirty -= nr_ios;
+	BUG_ON(tg->unaccounted_dirty < 0);
+	throtl_charge_io(tg, rw, sz, nr_ios, sync);
+}
+
 static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
 			struct bio *bio)
 {
@@ -735,21 +783,38 @@ static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
 	bio_list_add(&tg->bio_lists[rw], bio);
 	/* Take a bio reference on tg */
 	throtl_ref_get_tg(tg);
-	tg->nr_queued[rw]++;
-	td->nr_queued[rw]++;
+	tg->nr_queued_bio[rw]++;
+	td->nr_queued_bio[rw]++;
 	throtl_enqueue_tg(td, tg);
 }
 
-static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
+/*
+ * Returns the wait time of first bio or task in the specified direction. If
+ * nothing is queued then wait time is -1.
+ */
+static unsigned long
+tg_active_wait_time(struct throtl_data *td, struct throtl_grp *tg, bool rw)
 {
-	unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime;
 	struct bio *bio;
+	unsigned long sz;
 
-	if ((bio = bio_list_peek(&tg->bio_lists[READ])))
-		read_wait = tg_wait_dispatch(td, tg, READ, bio->bi_size, 1);
+	bio = bio_list_peek(&tg->bio_lists[rw]);
+	if (bio && tg->active_dispatch[rw] == DISPATCH_BIO)
+		return tg_wait_dispatch(td, tg, rw, bio->bi_size, 1);
 
-	if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
-		write_wait = tg_wait_dispatch(td, tg, WRITE, bio->bi_size, 1);
+	if (!waitqueue_active(&tg->wait[rw]))
+		return -1;
+
+	sz = tg->active_task_charge[rw] << PAGE_SHIFT;
+	return tg_wait_dispatch(td, tg, rw, sz, tg->active_task_charge[rw]);
+}
+
+static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
+{
+	unsigned long read_wait, write_wait, min_wait, disptime;
+
+	read_wait = tg_active_wait_time(td, tg, READ);
+	write_wait = tg_active_wait_time(td, tg, WRITE);
 
 	min_wait = min(read_wait, write_wait);
 	disptime = jiffies + min_wait;
@@ -760,18 +825,39 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
 	throtl_enqueue_tg(td, tg);
 }
 
+/* Calculate how many pages must be charged to a task at this point of time. */
+static unsigned int tg_calc_charge_pages(struct throtl_grp *tg, bool rw)
+{
+	unsigned int nr_pages;
+
+	/* Divide unaccounted number of pages among queued tasks */
+	nr_pages = tg->unaccounted_dirty/tg->nr_queued_tsk[rw];
+
+	return max_t(unsigned int, nr_pages, 1);
+}
+
+/* set the active task charge for the first task in the queue */
+static void tg_set_active_task_charge(struct throtl_grp *tg, bool rw)
+{
+	/* If there are more tasks queued, calculate the charge */
+	if (tg->nr_queued_tsk[rw])
+		tg->active_task_charge[rw] = tg_calc_charge_pages(tg, rw);
+	else
+		tg->active_task_charge[rw] = 0;
+}
+
 static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
 				bool rw, struct bio_list *bl)
 {
 	struct bio *bio;
 
 	bio = bio_list_pop(&tg->bio_lists[rw]);
-	tg->nr_queued[rw]--;
+	tg->nr_queued_bio[rw]--;
 	/* Drop bio reference on tg */
 	throtl_put_tg(tg);
 
-	BUG_ON(td->nr_queued[rw] <= 0);
-	td->nr_queued[rw]--;
+	BUG_ON(td->nr_queued_bio[rw] <= 0);
+	td->nr_queued_bio[rw]--;
 
 	throtl_charge_io(tg, rw, bio->bi_size, 1, bio->bi_rw & REQ_SYNC);
 	bio_list_add(bl, bio);
@@ -780,39 +866,137 @@ static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
 	throtl_trim_slice(td, tg, rw);
 }
 
-static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
-				struct bio_list *bl)
+static int throtl_dispatch_tg_bio(struct throtl_data *td, struct throtl_grp *tg,
+			bool rw, struct bio_list *bl, unsigned int max_disp)
 {
-	unsigned int nr_reads = 0, nr_writes = 0;
-	unsigned int max_nr_reads = throtl_grp_quantum*3/4;
-	unsigned int max_nr_writes = throtl_grp_quantum - max_nr_reads;
 	struct bio *bio;
+	unsigned int nr_disp = 0;
 
-	/* Try to dispatch 75% READS and 25% WRITES */
-
-	while ((bio = bio_list_peek(&tg->bio_lists[READ]))
-		&& !tg_wait_dispatch(td, tg, READ, bio->bi_size, 1)) {
+	while ((bio = bio_list_peek(&tg->bio_lists[rw]))
+		&& !tg_wait_dispatch(td, tg, rw, bio->bi_size, 1)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
-		nr_reads++;
+		nr_disp++;
 
-		if (nr_reads >= max_nr_reads)
+		if (nr_disp >= max_disp)
 			break;
 	}
 
-	while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
-		&& !tg_wait_dispatch(td, tg, WRITE, bio->bi_size, 1)) {
+	return nr_disp;
+}
 
-		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
-		nr_writes++;
+static void throtl_dispatch_tg_task(struct throtl_data *td,
+		struct throtl_grp *tg, bool rw, unsigned int max_disp)
+{
+	unsigned int sz;
 
-		if (nr_writes >= max_nr_writes)
-			break;
+	if (!waitqueue_active(&tg->wait[rw]))
+		return;
+
+	/* Wake up a task */
+	wake_up(&tg->wait[rw]);
+
+	tg->nr_queued_tsk[rw]--;
+	td->nr_queued_tsk[rw]--;
+
+	/* Charge the woken up task for IO */
+	sz = tg->active_task_charge[rw] << PAGE_SHIFT;
+	throtl_charge_dirty_io(tg, rw, sz, tg->active_task_charge[rw], false);
+	throtl_trim_slice(td, tg, rw);
+
+	/*
+	 * We dispatched one task. Set the charge for other queued tasks,
+	 * if any.
+	 */
+	tg_set_active_task_charge(tg, rw);
+	throtl_log_tg(td, tg, "[%c] Wake up a task. bdisp=%u sz=%u bps=%llu"
+			" iodisp=%u iops=%u bioq=%d/%d taskq=%d/%d",
+			rw == READ ? 'R' : 'W', tg->bytes_disp[rw], sz,
+			tg->bps[rw], tg->io_disp[rw], tg->iops[rw],
+			tg->nr_queued_bio[READ], tg->nr_queued_bio[WRITE],
+			tg->nr_queued_tsk[READ], tg->nr_queued_tsk[WRITE]);
+}
+
+static void tg_switch_active_dispatch(struct throtl_data *td,
+			struct throtl_grp *tg, bool rw)
+{
+	unsigned int nr_tasks = tg->nr_queued_tsk[rw];
+	unsigned int nr_bios = tg->nr_queued_bio[rw];
+	enum dispatch_type curr_dispatch = tg->active_dispatch[rw];
+
+	/* Nothing queued. Whoever gets queued next first, sets dispatch type */
+	if (!nr_bios && !nr_tasks)
+		return;
+
+	if (curr_dispatch == DISPATCH_BIO && nr_tasks) {
+		tg->active_dispatch[rw] = DISPATCH_TASK;
+		return;
+	}
+
+	if (curr_dispatch == DISPATCH_TASK && nr_bios)
+		tg->active_dispatch[rw] = DISPATCH_BIO;
+}
+
+static void tg_update_active_dispatch(struct throtl_data *td,
+			struct throtl_grp *tg, bool rw)
+{
+	unsigned int nr_tasks = tg->nr_queued_tsk[rw];
+	unsigned int nr_bios = tg->nr_queued_bio[rw];
+	enum dispatch_type curr_dispatch = tg->active_dispatch[rw];
+
+	BUG_ON(nr_bios < 0 || nr_tasks < 0);
+
+	if (curr_dispatch == DISPATCH_BIO && !nr_bios) {
+		tg->active_dispatch[rw] = DISPATCH_TASK;
+		return;
 	}
 
+	if (curr_dispatch == DISPATCH_TASK && !nr_tasks)
+		tg->active_dispatch[rw] = DISPATCH_BIO;
+}
+
+static int throtl_dispatch_tg_rw(struct throtl_data *td, struct throtl_grp *tg,
+			bool rw, struct bio_list *bl, unsigned int max)
+{
+	unsigned int nr_disp = 0;
+
+	if (tg->active_dispatch[rw] == DISPATCH_BIO)
+		nr_disp = throtl_dispatch_tg_bio(td, tg, rw, bl, max);
+	else
+		/* Only number of bios dispatched is kept track of here */
+		throtl_dispatch_tg_task(td, tg, rw, max);
+
+	tg_switch_active_dispatch(td, tg, rw);
+	return nr_disp;
+}
+
+static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
+				struct bio_list *bl)
+{
+	/* Try to dispatch 75% READS and 25% WRITES */
+	unsigned int nr_reads = 0, nr_writes = 0;
+	unsigned int max_nr_reads = throtl_grp_quantum*3/4;
+	unsigned int max_nr_writes = throtl_grp_quantum - max_nr_reads;
+
+	nr_reads = throtl_dispatch_tg_rw(td, tg, READ, bl, max_nr_reads);
+	nr_writes = throtl_dispatch_tg_rw(td, tg, WRITE, bl, max_nr_writes);
+
 	return nr_reads + nr_writes;
 }
 
+static bool tg_should_requeue(struct throtl_grp *tg)
+{
+	/* If there are queued bios, requeue */
+	if (tg->nr_queued_bio[0] || tg->nr_queued_bio[1])
+		return 1;
+
+	/* If there are queued tasks reueue */
+	if (tg->nr_queued_tsk[0] || tg->nr_queued_tsk[1])
+		return 1;
+
+	return 0;
+}
+
 static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl)
 {
 	unsigned int nr_disp = 0;
@@ -832,7 +1016,7 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl)
 
 		nr_disp += throtl_dispatch_tg(td, tg, bl);
 
-		if (tg->nr_queued[0] || tg->nr_queued[1]) {
+		if (tg_should_requeue(tg)) {
 			tg_update_disptime(td, tg);
 			throtl_enqueue_tg(td, tg);
 		}
@@ -899,9 +1083,9 @@ static int throtl_dispatch(struct request_queue *q)
 
 	bio_list_init(&bio_list_on_stack);
 
-	throtl_log(td, "dispatch nr_queued=%lu read=%u write=%u",
-			total_nr_queued(td), td->nr_queued[READ],
-			td->nr_queued[WRITE]);
+	throtl_log(td, "dispatch bioq=%u/%u tskq=%u/%u",
+			td->nr_queued_bio[READ], td->nr_queued_bio[WRITE],
+			td->nr_queued_tsk[READ], td->nr_queued_tsk[WRITE]);
 
 	nr_disp = throtl_select_dispatch(td, &bio_list_on_stack);
 
@@ -1122,7 +1306,7 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 
 		if (tg_no_rule_group(tg, rw)) {
 			blkiocg_update_dispatch_stats(&tg->blkg, bio->bi_size,
-					rw, bio->bi_rw & REQ_SYNC);
+					1, rw, bio->bi_rw & REQ_SYNC);
 			rcu_read_unlock();
 			return 0;
 		}
@@ -1146,14 +1330,14 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 		}
 	}
 
-	if (tg->nr_queued[rw]) {
+	/* If there are already queued bio or task in same dir, queue bio */
+	if (tg->nr_queued_bio[rw] || tg->nr_queued_tsk[rw]) {
 		/*
-		 * There is already another bio queued in same dir. No
+		 * There is already another bio/task queued in same dir. No
 		 * need to update dispatch time.
 		 */
 		update_disptime = false;
 		goto queue_bio;
-
 	}
 
 	/* Bio is with-in rate limit of group */
@@ -1178,16 +1362,18 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 
 queue_bio:
 	throtl_log_tg(td, tg, "[%c] bio. bdisp=%u sz=%u bps=%llu"
-			" iodisp=%u iops=%u queued=%d/%d",
+			" iodisp=%u iops=%u bioq=%d/%d taskq=%d/%d",
 			rw == READ ? 'R' : 'W',
 			tg->bytes_disp[rw], bio->bi_size, tg->bps[rw],
 			tg->io_disp[rw], tg->iops[rw],
-			tg->nr_queued[READ], tg->nr_queued[WRITE]);
+			tg->nr_queued_bio[READ], tg->nr_queued_bio[WRITE],
+			tg->nr_queued_tsk[READ], tg->nr_queued_tsk[WRITE]);
 
 	throtl_add_bio_tg(q->td, tg, bio);
 	*biop = NULL;
 
 	if (update_disptime) {
+		tg_update_active_dispatch(td, tg, rw);
 		tg_update_disptime(td, tg);
 		throtl_schedule_next_dispatch(td);
 	}
@@ -1197,6 +1383,137 @@ out:
 	return 0;
 }
 
+static void
+__blk_throtl_dirty_pages(struct request_queue *q, unsigned long nr_dirty)
+{
+	struct throtl_data *td = q->td;
+	struct throtl_grp *tg;
+	struct blkio_cgroup *blkcg;
+	bool rw = WRITE, update_disptime = true, first_task = false;
+	unsigned int sz = nr_dirty << PAGE_SHIFT;
+	DEFINE_WAIT(wait);
+
+	/*
+	 * A throtl_grp pointer retrieved under rcu can be used to access
+	 * basic fields like stats and io rates. If a group has no rules,
+	 * just update the dispatch stats in lockless manner and return.
+	 */
+
+	rcu_read_lock();
+	blkcg = task_blkio_cgroup(current);
+	tg = throtl_find_tg(td, blkcg);
+	if (tg) {
+		throtl_tg_fill_dev_details(td, tg);
+
+		if (tg_no_rule_group(tg, rw)) {
+			blkiocg_update_dispatch_stats(&tg->blkg, sz, nr_dirty,
+							rw, 0);
+			rcu_read_unlock();
+			return;
+		}
+	}
+	rcu_read_unlock();
+
+	spin_lock_irq(q->queue_lock);
+
+	tg = throtl_get_tg(td);
+
+	/*  Queue is gone. No queue lock held here. */
+	if (IS_ERR(tg))
+		return;
+
+	tg->unaccounted_dirty += nr_dirty;
+
+	/* If there are already queued task, put this task also on waitq */
+	if (tg->nr_queued_tsk[rw]) {
+		update_disptime = false;
+		goto queue_task;
+	} else
+		first_task = true;
+
+	/* If there are bios already throttled in same dir, queue task */
+	if (!bio_list_empty(&tg->bio_lists[rw])) {
+		update_disptime = false;
+		goto queue_task;
+	}
+
+	/*
+	 * Task is with-in rate limit of group.
+	 *
+	 * Note: How many IOPS we should charge for this operation. For
+	 * the time being I am sticking to number of pages as number of
+	 * IOPS.
+	 */
+	if (!tg_wait_dispatch(td, tg, rw, sz, nr_dirty)) {
+		throtl_charge_dirty_io(tg, rw, sz, nr_dirty, 0);
+		throtl_trim_slice(td, tg, rw);
+		goto out;
+	}
+
+queue_task:
+	throtl_log_tg(td, tg, "[%c] task. bdisp=%u sz=%u bps=%llu"
+			" iodisp=%u iops=%u bioq=%d/%d taskq=%d/%d",
+			rw == READ ? 'R' : 'W',
+			tg->bytes_disp[rw], sz, tg->bps[rw],
+			tg->io_disp[rw], tg->iops[rw],
+			tg->nr_queued_bio[READ], tg->nr_queued_bio[WRITE],
+			tg->nr_queued_tsk[READ], tg->nr_queued_tsk[WRITE]);
+
+	/* Take a task reference on tg */
+	throtl_ref_get_tg(tg);
+	tg->nr_queued_tsk[rw]++;
+	td->nr_queued_tsk[rw]++;
+	prepare_to_wait_exclusive(&tg->wait[rw], &wait, TASK_UNINTERRUPTIBLE);
+
+	if (first_task)
+		tg_set_active_task_charge(tg, rw);
+
+	if (update_disptime) {
+		tg_update_active_dispatch(td, tg, rw);
+		tg_update_disptime(td, tg);
+		throtl_schedule_next_dispatch(td);
+	} else
+		throtl_enqueue_tg(td, tg);
+
+	spin_unlock_irq(q->queue_lock);
+
+	/* Task has been put on a wait queue */
+	io_schedule();
+
+	/*
+	 * Task has woken up. Finish wait, drop reference to the group.
+	 */
+	spin_lock_irq(q->queue_lock);
+	finish_wait(&tg->wait[rw], &wait);
+
+	/* Drop task reference on group */
+	throtl_put_tg(tg);
+out:
+	spin_unlock_irq(q->queue_lock);
+	return;
+}
+
+void
+blk_throtl_dirty_pages(struct address_space *mapping, unsigned long nr_dirty)
+{
+	struct request_queue *q;
+	struct block_device *bdev;
+
+	bdev = mapping->host->i_sb->s_bdev;
+	if (!bdev)
+		return;
+	q = bdev_get_queue(bdev);
+
+	if (unlikely(!q))
+		return;
+
+	if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
+		return;
+
+	__blk_throtl_dirty_pages(q, nr_dirty);
+}
+
+
 int blk_throtl_init(struct request_queue *q)
 {
 	struct throtl_data *td;
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 7/8] blk-throttle: Do not throttle WRITEs at device level except direct IO
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
                   ` (5 preceding siblings ...)
  2011-06-03 21:06 ` [PATCH 6/8] blk-throttle: core logic to throttle task while dirtying pages Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  2011-06-03 21:06 ` [PATCH 8/8] blk-throttle: enable throttling of task while dirtying pages Vivek Goyal
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

Now WRITES are not throttled at device level except any WRITES
which are marked as DIRECT. This patch I have lifted from Andrea
Righi's previously posted series on lkml.

This will mean that any filesystem internally generated WRITES
also all not be throttled. If need be, down the line situation can
be improved if file system can give a hint to throttling logic that
it is ok to throttle certain WRITES without risk of being
serialized.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c      |    8 ++++++++
 fs/direct-io.c            |    1 +
 include/linux/blk_types.h |    2 ++
 3 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index c8bdc59..0d63e3b 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1293,6 +1293,14 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 	}
 
 	/*
+	 * WRITES bio are throttled here only if they are directIO. Otherwise
+	 * these are coming from page cache and these must have been subjected
+	 * to throttling rules while process was writting to page cache.
+	 */
+	if (rw == WRITE && !(bio->bi_rw & REQ_DIRECT_IO))
+		return 0;
+
+	/*
 	 * A throtl_grp pointer retrieved under rcu can be used to access
 	 * basic fields like stats and io rates. If a group has no rules,
 	 * just update the dispatch stats in lockless manner and return.
diff --git a/fs/direct-io.c b/fs/direct-io.c
index ac5f164..7858828 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -361,6 +361,7 @@ static void dio_bio_submit(struct dio *dio)
 	unsigned long flags;
 
 	bio->bi_private = dio;
+	bio->bi_rw |= REQ_DIRECT_IO;
 
 	spin_lock_irqsave(&dio->bio_lock, flags);
 	dio->refcount++;
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 2a7cea5..f46e8a4 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -131,6 +131,7 @@ enum rq_flag_bits {
 	__REQ_RAHEAD,		/* read ahead, can fail anytime */
 	__REQ_THROTTLED,	/* This bio has already been subjected to
 				 * throttling rules. Don't do it again. */
+	__REQ_DIRECT_IO,	/* It is a direct IO operation */
 
 	/* request only flags */
 	__REQ_SORTED,		/* elevator knows about this request */
@@ -172,6 +173,7 @@ enum rq_flag_bits {
 
 #define REQ_RAHEAD		(1 << __REQ_RAHEAD)
 #define REQ_THROTTLED		(1 << __REQ_THROTTLED)
+#define REQ_DIRECT_IO		(1 << __REQ_DIRECT_IO)
 
 #define REQ_SORTED		(1 << __REQ_SORTED)
 #define REQ_SOFTBARRIER		(1 << __REQ_SOFTBARRIER)
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 8/8] blk-throttle: enable throttling of task while dirtying pages
  2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
                   ` (6 preceding siblings ...)
  2011-06-03 21:06 ` [PATCH 7/8] blk-throttle: Do not throttle WRITEs at device level except direct IO Vivek Goyal
@ 2011-06-03 21:06 ` Vivek Goyal
  7 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-03 21:06 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, axboe
  Cc: vgoyal, arighi, fengguang.wu, jack, akpm

Put the blk_throtl_dirty_pages() hook in
balance_dirty_pages_ratelimited_nr() to enable task throttling.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 include/linux/blkdev.h |    5 +++++
 mm/page-writeback.c    |    3 +++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index ae9091a..3ad8f5f 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1180,12 +1180,17 @@ static inline uint64_t rq_io_start_time_ns(struct request *req)
 extern int blk_throtl_init(struct request_queue *q);
 extern void blk_throtl_exit(struct request_queue *q);
 extern int blk_throtl_bio(struct request_queue *q, struct bio **bio);
+extern void blk_throtl_dirty_pages(struct address_space *mapping,
+				unsigned long nr_dirty);
 #else /* CONFIG_BLK_DEV_THROTTLING */
 static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio)
 {
 	return 0;
 }
 
+static inline void blk_throtl_dirty_pages(struct address_space *mapping,
+				unsigned long nr_dirty) {}
+
 static inline int blk_throtl_init(struct request_queue *q) { return 0; }
 static inline int blk_throtl_exit(struct request_queue *q) { return 0; }
 #endif /* CONFIG_BLK_DEV_THROTTLING */
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 31f6988..943e551 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -629,6 +629,9 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping,
 	unsigned long ratelimit;
 	unsigned long *p;
 
+	/* Subject writes to IO controller throttling */
+	blk_throtl_dirty_pages(mapping, nr_pages_dirtied);
+
 	ratelimit = ratelimit_pages;
 	if (mapping->backing_dev_info->dirty_exceeded)
 		ratelimit = 8;
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait
  2011-06-28 15:35 [PATCH 0/8][V2] blk-throttle: Throttle buffered WRITEs in balance_dirty_pages() Vivek Goyal
@ 2011-06-28 15:35 ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2011-06-28 15:35 UTC (permalink / raw)
  To: linux-kernel, jaxboe, linux-fsdevel; +Cc: andrea, vgoyal

Currently we return jiffies to wait for in a function parameter.
A cleaner way would be to return it as return value. Value 0 would mean
that there is no need to wait and anything > 0 means number of jiffies
to wait before bio can be dispatched.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 block/blk-throttle.c |   72 +++++++++++++++++++++-----------------------------
 1 files changed, 30 insertions(+), 42 deletions(-)

diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index f6a7941..d76717a 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -590,8 +590,8 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
 			tg->slice_start[rw], tg->slice_end[rw], jiffies);
 }
 
-static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
-		struct bio *bio, unsigned long *wait)
+static unsigned long tg_wait_iops_limit(struct throtl_data *td,
+			struct throtl_grp *tg, struct bio *bio)
 {
 	bool rw = bio_data_dir(bio);
 	unsigned int io_allowed;
@@ -621,11 +621,8 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
 	else
 		io_allowed = tmp;
 
-	if (tg->io_disp[rw] + 1 <= io_allowed) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	if (tg->io_disp[rw] + 1 <= io_allowed)
+		return 0;
 
 	/* Calc approx time to dispatch */
 	jiffy_wait = ((tg->io_disp[rw] + 1) * HZ)/tg->iops[rw] + 1;
@@ -635,13 +632,15 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
 	else
 		jiffy_wait = 1;
 
-	if (wait)
-		*wait = jiffy_wait;
-	return 0;
+	return jiffy_wait;
 }
 
-static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
-		struct bio *bio, unsigned long *wait)
+/*
+ * Returns number of jiffies to wait to before IO can be dispatched according
+ * to bps limit.
+ */
+static unsigned long tg_wait_bps_limit(struct throtl_data *td,
+			struct throtl_grp *tg, struct bio *bio)
 {
 	bool rw = bio_data_dir(bio);
 	u64 bytes_allowed, extra_bytes, tmp;
@@ -659,11 +658,8 @@ static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
 	do_div(tmp, HZ);
 	bytes_allowed = tmp;
 
-	if (tg->bytes_disp[rw] + bio->bi_size <= bytes_allowed) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	if (tg->bytes_disp[rw] + bio->bi_size <= bytes_allowed)
+		return 0;
 
 	/* Calc approx time to dispatch */
 	extra_bytes = tg->bytes_disp[rw] + bio->bi_size - bytes_allowed;
@@ -677,9 +673,8 @@ static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
 	 * up we did. Add that time also.
 	 */
 	jiffy_wait = jiffy_wait + (jiffy_elapsed_rnd - jiffy_elapsed);
-	if (wait)
-		*wait = jiffy_wait;
-	return 0;
+
+	return jiffy_wait;
 }
 
 static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
@@ -691,9 +686,12 @@ static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
 /*
  * Returns whether one can dispatch a bio or not. Also returns approx number
  * of jiffies to wait before this bio is with-in IO rate and can be dispatched
+ *
+ * Retruns the number of jiffies one needs to wait before IO can be dispatched.
+ * 0 means, IO can be dispatched now.
  */
-static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
-				struct bio *bio, unsigned long *wait)
+static unsigned long
+tg_wait_dispatch(struct throtl_data *td, struct throtl_grp *tg, struct bio *bio)
 {
 	bool rw = bio_data_dir(bio);
 	unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
@@ -707,11 +705,8 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
 	BUG_ON(tg->nr_queued[rw] && bio != bio_list_peek(&tg->bio_lists[rw]));
 
 	/* If tg->bps = -1, then BW is unlimited */
-	if (tg->bps[rw] == -1 && tg->iops[rw] == -1) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	if (tg->bps[rw] == -1 && tg->iops[rw] == -1)
+		return 0;
 
 	/*
 	 * If previous slice expired, start a new one otherwise renew/extend
@@ -725,22 +720,15 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
 			throtl_extend_slice(td, tg, rw, jiffies + throtl_slice);
 	}
 
-	if (tg_with_in_bps_limit(td, tg, bio, &bps_wait)
-	    && tg_with_in_iops_limit(td, tg, bio, &iops_wait)) {
-		if (wait)
-			*wait = 0;
-		return 1;
-	}
+	bps_wait = tg_wait_bps_limit(td, tg, bio);
+	iops_wait = tg_wait_iops_limit(td, tg, bio);
 
 	max_wait = max(bps_wait, iops_wait);
 
-	if (wait)
-		*wait = max_wait;
-
 	if (time_before(tg->slice_end[rw], jiffies + max_wait))
 		throtl_extend_slice(td, tg, rw, jiffies + max_wait);
 
-	return 0;
+	return max_wait;
 }
 
 static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio)
@@ -774,10 +762,10 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
 	struct bio *bio;
 
 	if ((bio = bio_list_peek(&tg->bio_lists[READ])))
-		tg_may_dispatch(td, tg, bio, &read_wait);
+		read_wait = tg_wait_dispatch(td, tg, bio);
 
 	if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
-		tg_may_dispatch(td, tg, bio, &write_wait);
+		write_wait = tg_wait_dispatch(td, tg, bio);
 
 	min_wait = min(read_wait, write_wait);
 	disptime = jiffies + min_wait;
@@ -819,7 +807,7 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
 	/* Try to dispatch 75% READS and 25% WRITES */
 
 	while ((bio = bio_list_peek(&tg->bio_lists[READ]))
-		&& tg_may_dispatch(td, tg, bio, NULL)) {
+		&& !tg_wait_dispatch(td, tg, bio)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
 		nr_reads++;
@@ -829,7 +817,7 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
 	}
 
 	while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
-		&& tg_may_dispatch(td, tg, bio, NULL)) {
+		&& !tg_wait_dispatch(td, tg, bio)) {
 
 		tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
 		nr_writes++;
@@ -1185,7 +1173,7 @@ int blk_throtl_bio(struct request_queue *q, struct bio **biop)
 	}
 
 	/* Bio is with-in rate limit of group */
-	if (tg_may_dispatch(td, tg, bio, NULL)) {
+	if (!tg_wait_dispatch(td, tg, bio)) {
 		throtl_charge_bio(tg, bio);
 
 		/*
-- 
1.7.4.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-06-28 15:37 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-06-03 21:06 [RFC PATCH 0/8] blk-throttle: Throttle buffered WRITE in balance_dirty_pages() Vivek Goyal
2011-06-03 21:06 ` [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait Vivek Goyal
2011-06-03 21:06 ` [PATCH 2/8] blk-throttle: do not enforce first queued bio check in tg_wait_dispatch Vivek Goyal
2011-06-03 21:06 ` [PATCH 3/8] blk-throttle: use IO size and direction as parameters to wait routines Vivek Goyal
2011-06-03 21:06 ` [PATCH 4/8] blk-throttle: Specify number of IOs during dispatch update Vivek Goyal
2011-06-03 21:06 ` [PATCH 5/8] blk-throttle: Get rid of extend slice trace message Vivek Goyal
2011-06-03 21:06 ` [PATCH 6/8] blk-throttle: core logic to throttle task while dirtying pages Vivek Goyal
2011-06-03 21:06 ` [PATCH 7/8] blk-throttle: Do not throttle WRITEs at device level except direct IO Vivek Goyal
2011-06-03 21:06 ` [PATCH 8/8] blk-throttle: enable throttling of task while dirtying pages Vivek Goyal
2011-06-28 15:35 [PATCH 0/8][V2] blk-throttle: Throttle buffered WRITEs in balance_dirty_pages() Vivek Goyal
2011-06-28 15:35 ` [PATCH 1/8] blk-throttle: convert wait routines to return jiffies to wait Vivek Goyal

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).