diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2013-08-16 10:54:23 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2013-09-05 20:46:06 -0400 |
commit | fd2ed4d252701d3bbed4cd3e3d267ad469bb832a (patch) | |
tree | 264ff043406894bd447eb6e9976b9a2b4d69bd9f /drivers/md/dm.c | |
parent | 94563badaf41f9291ff0bad94a443a4319b9e312 (diff) |
dm: add statistics support
Support the collection of I/O statistics on user-defined regions of
a DM device. If no regions are defined no statistics are collected so
there isn't any performance impact. Only bio-based DM devices are
currently supported.
Each user-defined region specifies a starting sector, length and step.
Individual statistics will be collected for each step-sized area within
the range specified.
The I/O statistics counters for each step-sized area of a region are
in the same format as /sys/block/*/stat or /proc/diskstats but extra
counters (12 and 13) are provided: total time spent reading and
writing in milliseconds. All these counters may be accessed by sending
the @stats_print message to the appropriate DM device via dmsetup.
The creation of DM statistics will allocate memory via kmalloc or
fallback to using vmalloc space. At most, 1/4 of the overall system
memory may be allocated by DM statistics. The admin can see how much
memory is used by reading
/sys/module/dm_mod/parameters/stats_current_allocated_bytes
See Documentation/device-mapper/statistics.txt for more details.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm.c')
-rw-r--r-- | drivers/md/dm.c | 65 |
1 files changed, 62 insertions, 3 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 7faeaa3d4835..6a5e9ed2fcc3 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -60,6 +60,7 @@ struct dm_io { | |||
60 | struct bio *bio; | 60 | struct bio *bio; |
61 | unsigned long start_time; | 61 | unsigned long start_time; |
62 | spinlock_t endio_lock; | 62 | spinlock_t endio_lock; |
63 | struct dm_stats_aux stats_aux; | ||
63 | }; | 64 | }; |
64 | 65 | ||
65 | /* | 66 | /* |
@@ -198,6 +199,8 @@ struct mapped_device { | |||
198 | 199 | ||
199 | /* zero-length flush that will be cloned and submitted to targets */ | 200 | /* zero-length flush that will be cloned and submitted to targets */ |
200 | struct bio flush_bio; | 201 | struct bio flush_bio; |
202 | |||
203 | struct dm_stats stats; | ||
201 | }; | 204 | }; |
202 | 205 | ||
203 | /* | 206 | /* |
@@ -269,6 +272,7 @@ static int (*_inits[])(void) __initdata = { | |||
269 | dm_io_init, | 272 | dm_io_init, |
270 | dm_kcopyd_init, | 273 | dm_kcopyd_init, |
271 | dm_interface_init, | 274 | dm_interface_init, |
275 | dm_statistics_init, | ||
272 | }; | 276 | }; |
273 | 277 | ||
274 | static void (*_exits[])(void) = { | 278 | static void (*_exits[])(void) = { |
@@ -279,6 +283,7 @@ static void (*_exits[])(void) = { | |||
279 | dm_io_exit, | 283 | dm_io_exit, |
280 | dm_kcopyd_exit, | 284 | dm_kcopyd_exit, |
281 | dm_interface_exit, | 285 | dm_interface_exit, |
286 | dm_statistics_exit, | ||
282 | }; | 287 | }; |
283 | 288 | ||
284 | static int __init dm_init(void) | 289 | static int __init dm_init(void) |
@@ -384,6 +389,16 @@ int dm_lock_for_deletion(struct mapped_device *md) | |||
384 | return r; | 389 | return r; |
385 | } | 390 | } |
386 | 391 | ||
392 | sector_t dm_get_size(struct mapped_device *md) | ||
393 | { | ||
394 | return get_capacity(md->disk); | ||
395 | } | ||
396 | |||
397 | struct dm_stats *dm_get_stats(struct mapped_device *md) | ||
398 | { | ||
399 | return &md->stats; | ||
400 | } | ||
401 | |||
387 | static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) | 402 | static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) |
388 | { | 403 | { |
389 | struct mapped_device *md = bdev->bd_disk->private_data; | 404 | struct mapped_device *md = bdev->bd_disk->private_data; |
@@ -466,8 +481,9 @@ static int md_in_flight(struct mapped_device *md) | |||
466 | static void start_io_acct(struct dm_io *io) | 481 | static void start_io_acct(struct dm_io *io) |
467 | { | 482 | { |
468 | struct mapped_device *md = io->md; | 483 | struct mapped_device *md = io->md; |
484 | struct bio *bio = io->bio; | ||
469 | int cpu; | 485 | int cpu; |
470 | int rw = bio_data_dir(io->bio); | 486 | int rw = bio_data_dir(bio); |
471 | 487 | ||
472 | io->start_time = jiffies; | 488 | io->start_time = jiffies; |
473 | 489 | ||
@@ -476,6 +492,10 @@ static void start_io_acct(struct dm_io *io) | |||
476 | part_stat_unlock(); | 492 | part_stat_unlock(); |
477 | atomic_set(&dm_disk(md)->part0.in_flight[rw], | 493 | atomic_set(&dm_disk(md)->part0.in_flight[rw], |
478 | atomic_inc_return(&md->pending[rw])); | 494 | atomic_inc_return(&md->pending[rw])); |
495 | |||
496 | if (unlikely(dm_stats_used(&md->stats))) | ||
497 | dm_stats_account_io(&md->stats, bio->bi_rw, bio->bi_sector, | ||
498 | bio_sectors(bio), false, 0, &io->stats_aux); | ||
479 | } | 499 | } |
480 | 500 | ||
481 | static void end_io_acct(struct dm_io *io) | 501 | static void end_io_acct(struct dm_io *io) |
@@ -491,6 +511,10 @@ static void end_io_acct(struct dm_io *io) | |||
491 | part_stat_add(cpu, &dm_disk(md)->part0, ticks[rw], duration); | 511 | part_stat_add(cpu, &dm_disk(md)->part0, ticks[rw], duration); |
492 | part_stat_unlock(); | 512 | part_stat_unlock(); |
493 | 513 | ||
514 | if (unlikely(dm_stats_used(&md->stats))) | ||
515 | dm_stats_account_io(&md->stats, bio->bi_rw, bio->bi_sector, | ||
516 | bio_sectors(bio), true, duration, &io->stats_aux); | ||
517 | |||
494 | /* | 518 | /* |
495 | * After this is decremented the bio must not be touched if it is | 519 | * After this is decremented the bio must not be touched if it is |
496 | * a flush. | 520 | * a flush. |
@@ -1519,7 +1543,7 @@ static void _dm_request(struct request_queue *q, struct bio *bio) | |||
1519 | return; | 1543 | return; |
1520 | } | 1544 | } |
1521 | 1545 | ||
1522 | static int dm_request_based(struct mapped_device *md) | 1546 | int dm_request_based(struct mapped_device *md) |
1523 | { | 1547 | { |
1524 | return blk_queue_stackable(md->queue); | 1548 | return blk_queue_stackable(md->queue); |
1525 | } | 1549 | } |
@@ -1958,6 +1982,8 @@ static struct mapped_device *alloc_dev(int minor) | |||
1958 | md->flush_bio.bi_bdev = md->bdev; | 1982 | md->flush_bio.bi_bdev = md->bdev; |
1959 | md->flush_bio.bi_rw = WRITE_FLUSH; | 1983 | md->flush_bio.bi_rw = WRITE_FLUSH; |
1960 | 1984 | ||
1985 | dm_stats_init(&md->stats); | ||
1986 | |||
1961 | /* Populate the mapping, nobody knows we exist yet */ | 1987 | /* Populate the mapping, nobody knows we exist yet */ |
1962 | spin_lock(&_minor_lock); | 1988 | spin_lock(&_minor_lock); |
1963 | old_md = idr_replace(&_minor_idr, md, minor); | 1989 | old_md = idr_replace(&_minor_idr, md, minor); |
@@ -2009,6 +2035,7 @@ static void free_dev(struct mapped_device *md) | |||
2009 | 2035 | ||
2010 | put_disk(md->disk); | 2036 | put_disk(md->disk); |
2011 | blk_cleanup_queue(md->queue); | 2037 | blk_cleanup_queue(md->queue); |
2038 | dm_stats_cleanup(&md->stats); | ||
2012 | module_put(THIS_MODULE); | 2039 | module_put(THIS_MODULE); |
2013 | kfree(md); | 2040 | kfree(md); |
2014 | } | 2041 | } |
@@ -2150,7 +2177,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t, | |||
2150 | /* | 2177 | /* |
2151 | * Wipe any geometry if the size of the table changed. | 2178 | * Wipe any geometry if the size of the table changed. |
2152 | */ | 2179 | */ |
2153 | if (size != get_capacity(md->disk)) | 2180 | if (size != dm_get_size(md)) |
2154 | memset(&md->geometry, 0, sizeof(md->geometry)); | 2181 | memset(&md->geometry, 0, sizeof(md->geometry)); |
2155 | 2182 | ||
2156 | __set_size(md, size); | 2183 | __set_size(md, size); |
@@ -2696,6 +2723,38 @@ out: | |||
2696 | return r; | 2723 | return r; |
2697 | } | 2724 | } |
2698 | 2725 | ||
2726 | /* | ||
2727 | * Internal suspend/resume works like userspace-driven suspend. It waits | ||
2728 | * until all bios finish and prevents issuing new bios to the target drivers. | ||
2729 | * It may be used only from the kernel. | ||
2730 | * | ||
2731 | * Internal suspend holds md->suspend_lock, which prevents interaction with | ||
2732 | * userspace-driven suspend. | ||
2733 | */ | ||
2734 | |||
2735 | void dm_internal_suspend(struct mapped_device *md) | ||
2736 | { | ||
2737 | mutex_lock(&md->suspend_lock); | ||
2738 | if (dm_suspended_md(md)) | ||
2739 | return; | ||
2740 | |||
2741 | set_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags); | ||
2742 | synchronize_srcu(&md->io_barrier); | ||
2743 | flush_workqueue(md->wq); | ||
2744 | dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE); | ||
2745 | } | ||
2746 | |||
2747 | void dm_internal_resume(struct mapped_device *md) | ||
2748 | { | ||
2749 | if (dm_suspended_md(md)) | ||
2750 | goto done; | ||
2751 | |||
2752 | dm_queue_flush(md); | ||
2753 | |||
2754 | done: | ||
2755 | mutex_unlock(&md->suspend_lock); | ||
2756 | } | ||
2757 | |||
2699 | /*----------------------------------------------------------------- | 2758 | /*----------------------------------------------------------------- |
2700 | * Event notification. | 2759 | * Event notification. |
2701 | *---------------------------------------------------------------*/ | 2760 | *---------------------------------------------------------------*/ |