From: Jerome Marchand <jmarchan@redhat.com>
To: Vivek Goyal <vgoyal@redhat.com>
Cc: Jens Axboe <jaxboe@fusionio.com>,
Satoru Takeuchi <takeuchi_satoru@jp.fujitsu.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
Greg Kroah-Hartman <greg@kroah.com>
Subject: [PATCH 2/2] block: fix accounting bug on cross partition merges
Date: Tue, 04 Jan 2011 16:55:13 +0100 [thread overview]
Message-ID: <4D2342E1.8010405@redhat.com> (raw)
In-Reply-To: <4D23423A.60707@redhat.com>
/proc/diskstats would display a strange output as follows.
$ cat /proc/diskstats |grep sda
8 0 sda 90524 7579 102154 20464 0 0 0 0 0 14096 20089
8 1 sda1 19085 1352 21841 4209 0 0 0 0 4294967064 15689 4293424691
~~~~~~~~~~
8 2 sda2 71252 3624 74891 15950 0 0 0 0 232 23995 1562390
8 3 sda3 54 487 2188 92 0 0 0 0 0 88 92
8 4 sda4 4 0 8 0 0 0 0 0 0 0 0
8 5 sda5 81 2027 2130 138 0 0 0 0 0 87 137
Its reason is the wrong way of accounting hd_struct->in_flight. When a bio is
merged into a request belongs to different partition by ELEVATOR_FRONT_MERGE.
The detailed root cause is as follows.
Assuming that there are two partition, sda1 and sda2.
1. A request for sda2 is in request_queue. Hence sda1's hd_struct->in_flight
is 0 and sda2's one is 1.
| hd_struct->in_flight
---------------------------
sda1 | 0
sda2 | 1
---------------------------
2. A bio belongs to sda1 is issued and is merged into the request mentioned on
step1 by ELEVATOR_BACK_MERGE. The first sector of the request is changed
from sda2 region to sda1 region. However the two partition's
hd_struct->in_flight are not changed.
| hd_struct->in_flight
---------------------------
sda1 | 0
sda2 | 1
---------------------------
3. The request is finished and blk_account_io_done() is called. In this case,
sda2's hd_struct->in_flight, not a sda1's one, is decremented.
| hd_struct->in_flight
---------------------------
sda1 | -1
sda2 | 1
---------------------------
The patch fixes the problem by caching the partition lookup
inside the request structure, hence making sure that the increment
and decrement will always happen on the same partition struct. This
also speeds up IO with accounting enabled, since it cuts down on
the number of lookups we have to do.
Also add a refcount to struct hd_struct to keep the partition in
memory as long as users exist. We use kref_test_and_get() to ensure
we don't add a reference to a partition which is going away.
Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
---
block/blk-core.c | 26 +++++++++++++++++++++-----
block/blk-merge.c | 3 ++-
block/genhd.c | 1 +
fs/partitions/check.c | 10 +++++++++-
include/linux/blkdev.h | 1 +
include/linux/genhd.h | 2 ++
6 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 4ce953f..409ad36 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -64,13 +64,27 @@ static void drive_stat_acct(struct request *rq, int new_io)
return;
cpu = part_stat_lock();
- part = disk_map_sector_rcu(rq->rq_disk, blk_rq_pos(rq));
- if (!new_io)
+ if (!new_io) {
+ part = rq->part;
part_stat_inc(cpu, part, merges[rw]);
- else {
+ } else {
+ part = disk_map_sector_rcu(rq->rq_disk, blk_rq_pos(rq));
+ if (!kref_test_and_get(&part->ref)) {
+ /*
+ * The partition is already being removed,
+ * the request will be accounted on the disk only
+ *
+ * We take a reference on disk->part0 although that
+ * partition will never be deleted, so we can treat
+ * it as any other partition.
+ */
+ part = &rq->rq_disk->part0;
+ kref_get(&part->ref);
+ }
part_round_stats(cpu, part);
part_inc_in_flight(part, rw);
+ rq->part = part;
}
part_stat_unlock();
@@ -128,6 +142,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq)
rq->ref_count = 1;
rq->start_time = jiffies;
set_start_time_ns(rq);
+ rq->part = NULL;
}
EXPORT_SYMBOL(blk_rq_init);
@@ -1776,7 +1791,7 @@ static void blk_account_io_completion(struct request *req, unsigned int bytes)
int cpu;
cpu = part_stat_lock();
- part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req));
+ part = req->part;
part_stat_add(cpu, part, sectors[rw], bytes >> 9);
part_stat_unlock();
}
@@ -1796,13 +1811,14 @@ static void blk_account_io_done(struct request *req)
int cpu;
cpu = part_stat_lock();
- part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req));
+ part = req->part;
part_stat_inc(cpu, part, ios[rw]);
part_stat_add(cpu, part, ticks[rw], duration);
part_round_stats(cpu, part);
part_dec_in_flight(part, rw);
+ kref_put(&part->ref, __delete_partition);
part_stat_unlock();
}
}
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 74bc4a7..23ea74b 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -351,11 +351,12 @@ static void blk_account_io_merge(struct request *req)
int cpu;
cpu = part_stat_lock();
- part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req));
+ part = req->part;
part_round_stats(cpu, part);
part_dec_in_flight(part, rq_data_dir(req));
+ kref_put(&part->ref, __delete_partition);
part_stat_unlock();
}
}
diff --git a/block/genhd.c b/block/genhd.c
index 5fa2b44..0c55eae 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1192,6 +1192,7 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
return NULL;
}
disk->part_tbl->part[0] = &disk->part0;
+ kref_init(&disk->part0.ref);
disk->minors = minors;
rand_initialize_disk(disk);
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 0a8b0ad..0123717 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -372,6 +372,13 @@ static void delete_partition_rcu_cb(struct rcu_head *head)
put_device(part_to_dev(part));
}
+void __delete_partition(struct kref *ref)
+{
+ struct hd_struct *part = container_of(ref, struct hd_struct, ref);
+
+ call_rcu(&part->rcu_head, delete_partition_rcu_cb);
+}
+
void delete_partition(struct gendisk *disk, int partno)
{
struct disk_part_tbl *ptbl = disk->part_tbl;
@@ -390,7 +397,7 @@ void delete_partition(struct gendisk *disk, int partno)
kobject_put(part->holder_dir);
device_del(part_to_dev(part));
- call_rcu(&part->rcu_head, delete_partition_rcu_cb);
+ kref_put(&part->ref, __delete_partition);
}
static ssize_t whole_disk_show(struct device *dev,
@@ -489,6 +496,7 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno,
if (!dev_get_uevent_suppress(ddev))
kobject_uevent(&pdev->kobj, KOBJ_ADD);
+ kref_init(&p->ref);
return p;
out_free_info:
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 36ab42c..7572b19 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -115,6 +115,7 @@ struct request {
void *elevator_private3;
struct gendisk *rq_disk;
+ struct hd_struct *part;
unsigned long start_time;
#ifdef CONFIG_BLK_CGROUP
unsigned long long start_time_ns;
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 7a7b9c1..2ba2792 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -116,6 +116,7 @@ struct hd_struct {
struct disk_stats dkstats;
#endif
struct rcu_head rcu_head;
+ struct kref ref;
};
#define GENHD_FL_REMOVABLE 1
@@ -583,6 +584,7 @@ extern struct hd_struct * __must_check add_partition(struct gendisk *disk,
sector_t len, int flags,
struct partition_meta_info
*info);
+extern void __delete_partition(struct kref *ref);
extern void delete_partition(struct gendisk *, int);
extern void printk_all_partitions(void);
next prev parent reply other threads:[~2011-01-04 15:55 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-12-06 9:44 [PATCH 1/2] Don't merge different partition's IOs Yasuaki Ishimatsu
2010-12-06 16:08 ` Linus Torvalds
2010-12-07 7:18 ` Satoru Takeuchi
2010-12-07 18:39 ` Vivek Goyal
2010-12-08 7:33 ` Jens Axboe
2010-12-08 7:59 ` Satoru Takeuchi
2010-12-08 8:06 ` Jens Axboe
2010-12-08 8:11 ` Satoru Takeuchi
2010-12-08 14:46 ` Jens Axboe
2010-12-08 15:51 ` Vivek Goyal
2010-12-08 15:58 ` Vivek Goyal
2010-12-10 11:22 ` Jerome Marchand
2010-12-10 16:12 ` Jerome Marchand
2010-12-10 16:55 ` Vivek Goyal
2010-12-14 20:25 ` Jens Axboe
2010-12-17 13:42 ` [PATCH] block: fix accounting bug on cross partition merges Jerome Marchand
2010-12-17 19:06 ` Jens Axboe
2010-12-17 22:32 ` Vivek Goyal
2010-12-23 15:10 ` Jerome Marchand
2010-12-23 15:39 ` Vivek Goyal
2010-12-23 17:04 ` Jerome Marchand
2010-12-24 19:29 ` Vivek Goyal
2011-01-04 15:52 ` [PATCH 1/2] kref: add kref_test_and_get Jerome Marchand
2011-01-04 15:55 ` Jerome Marchand [this message]
2011-01-04 21:00 ` [PATCH 2/2] block: fix accounting bug on cross partition merges Greg KH
2011-01-05 13:51 ` Jerome Marchand
2011-01-05 16:00 ` Greg KH
2011-01-05 16:19 ` Jerome Marchand
2011-01-05 16:27 ` Greg KH
2011-01-05 13:55 ` Jens Axboe
2011-01-05 15:58 ` Greg KH
2011-01-05 18:46 ` Jens Axboe
2011-01-05 20:08 ` Greg KH
2011-01-05 21:38 ` Jens Axboe
2011-01-05 22:16 ` Greg KH
2011-01-06 9:46 ` Jens Axboe
2011-01-05 14:00 ` Jens Axboe
2011-01-05 14:09 ` Jerome Marchand
2011-01-05 14:17 ` Jens Axboe
2011-01-04 16:05 ` [PATCH 1/2] kref: add kref_test_and_get Eric Dumazet
2011-01-05 15:02 ` [PATCH 1/2 v2] " Jerome Marchand
2011-01-05 15:43 ` Alexey Dobriyan
2011-01-05 15:57 ` Greg KH
2011-01-05 15:56 ` Greg KH
2011-01-04 20:57 ` [PATCH 1/2] " Greg KH
2011-01-05 13:35 ` Jerome Marchand
2011-01-05 15:55 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4D2342E1.8010405@redhat.com \
--to=jmarchan@redhat.com \
--cc=greg@kroah.com \
--cc=isimatu.yasuaki@jp.fujitsu.com \
--cc=jaxboe@fusionio.com \
--cc=linux-kernel@vger.kernel.org \
--cc=takeuchi_satoru@jp.fujitsu.com \
--cc=torvalds@linux-foundation.org \
--cc=vgoyal@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox