import dm-iostats.c --- diff/drivers/md/dm-iostats.c 1970-01-01 01:00:00.000000000 +0100 +++ source/drivers/md/dm-iostats.c 2002-12-16 10:45:56.000000000 +0000 @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2002 Sistina Software Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include +#include +#include + +#define MIN_IOSTATS 256 +static kmem_cache_t *_iostats_cache; +mempool_t *_iostats_pool; + +struct dm_iostats { + struct dm_target *ti; + + int rw; + typeof(jiffies) jiffies_start_io; + + void (*end_io) (struct buffer_head * bh, int uptodate); + void *context; +}; + +/* + * Linear: io status for a linear range of a device. + */ +struct iostats_c { + struct dm_dev *dev; + uint32_t reads; + uint32_t writes; +}; + +typedef uint16_t count_t; +#define MAX_COUNT ((count_t) - 1) + +struct iostats_avg_c { + struct dm_dev *dev; + uint32_t reads; + uint32_t writes; + typeof(jiffies) jiffies_r; + typeof(jiffies) jiffies_w; + count_t count_r; + count_t count_w; +}; + + +static inline struct dm_iostats *alloc_io(void) +{ + return mempool_alloc(_iostats_pool, GFP_NOIO); +} + +static inline void free_io(struct dm_iostats *io) +{ + mempool_free(io, _iostats_pool); +} + + +/* + * Construct a linear io status mapping: + */ +static int _iostats_ctr(struct dm_target *ti, int argc, char **argv, int size) +{ + struct iostats_c *ic; + + if (argc != 1) { + ti->error = "dm-iostats: Wrong argument count"; + return -EINVAL; + } + + ic = kmalloc(size, GFP_KERNEL); + if (ic == NULL) { + ti->error = "dm-iostats: Cannot allocate io status context"; + return -ENOMEM; + } + + if (dm_get_device(ti, argv[0], ti->begin, ti->len, + dm_table_get_mode(ti->table), &ic->dev)) { + ti->error = "dm-iostats: Device lookup failed"; + goto bad; + } + + ti->private = ic; + return 0; + + bad: + kfree(ic); + return -EINVAL; +} + +/* read/write statistics constructor */ +static int iostats_ctr(struct dm_target *ti, int argc, char **argv) +{ + int r = _iostats_ctr(ti, argc, argv, sizeof(struct iostats_c)); + + if (!r) { + struct iostats_c *ic = ti->private; + + ic->reads = 0; + ic->writes = 0; + } + + return r; +} + +/* average io latency statistics constuctor */ +static int iostats_avg_ctr(struct dm_target *ti, int argc, char **argv) +{ + int r = _iostats_ctr(ti, argc, argv, sizeof(struct iostats_avg_c)); + + if (!r) { + struct iostats_avg_c *ic = ti->private; + + ic->reads = 0; + ic->writes = 0; + ic->jiffies_r = 0; + ic->jiffies_w = 0; + ic->count_r = 0; + ic->count_w = 0; + } + + return r; +} + +/* common destructor */ +static void iostats_dtr(struct dm_target *ti) +{ + struct iostats_c *ic = (struct iostats_c *) ti->private; + + dm_put_device(ti, ic->dev); + kfree(ic); +} + +/* read/write statistics mapping */ +static int iostats_map(struct dm_target *ti, struct buffer_head *bh, int rw) +{ + struct iostats_c *ic = (struct iostats_c *) ti->private; + + if (rw == WRITE) + ic->writes++; + else + ic->reads++; + + /* map */ + bh->b_rdev = ic->dev->dev; + + return 1; +} + +/* FIXME: should be a target lock */ +static spinlock_t _io_lock = SPIN_LOCK_UNLOCKED; + +/* average latency stats end_io function */ +static void iostats_avg_end_io(struct buffer_head *bh, int uptodate) +{ + int flags; + typeof(jiffies) j; + struct dm_iostats *io = bh->b_private; + struct iostats_avg_c *ic = io->ti->private; + + spin_lock_irqsave(&_io_lock, flags); + + /* average io latency; ignore an io on jiffies overflow */ + if (jiffies >= io->jiffies_start_io) { + j = jiffies - io->jiffies_start_io; + + if (io->rw == WRITE) { + ic->writes++; + ic->jiffies_w += j; + ic->count_w++; + if (ic->count_w > MAX_COUNT) { + ic->jiffies_w /= ic->count_w; + ic->count_w = 1; + } + } else { + ic->reads++; + ic->jiffies_r += j; + ic->count_r++; + if (ic->count_r > MAX_COUNT) { + ic->jiffies_r /= ic->count_r; + ic->count_r = 1; + } + } + } + + spin_unlock_irqrestore(&_io_lock, flags); + + bh->b_end_io = io->end_io; + bh->b_private = io->context; + + free_io(io); + + bh->b_end_io(bh, uptodate); +} + +/* average io latency statistics mapping */ +static int iostats_avg_map(struct dm_target *ti, struct buffer_head *bh, int rw) +{ + struct iostats_avg_c *ic = (struct iostats_avg_c *) ti->private; + struct dm_iostats *io = alloc_io(); + + if (!io) + return -ENOMEM; + + io->ti = ti; + io->rw = rw; + io->jiffies_start_io = jiffies; + + /* hook the end io request fn */ + io->end_io = bh->b_end_io; + io->context = bh->b_private; + bh->b_end_io = iostats_avg_end_io; + bh->b_private = io; + + /* map */ + bh->b_rdev = ic->dev->dev; + + return 1; +} + +/* read/write statistics mapping */ +static int iostats_status(struct dm_target *ti, status_type_t type, + char *result, int maxlen) +{ + struct iostats_c *ic = (struct iostats_c *) ti->private; + + switch (type) { + case STATUSTYPE_INFO: + snprintf(result, maxlen, "%s %u %u", + kdevname(to_kdev_t(ic->dev->bdev->bd_dev)), + ic->reads, ic->writes); + break; + + case STATUSTYPE_TABLE: + snprintf(result, maxlen, "%s", + kdevname(to_kdev_t(ic->dev->bdev->bd_dev))); + break; + } + return 0; +} + +static inline uint _calc(typeof(jiffies) j, count_t c) +{ + typeof(jiffies) const m = 1000 / HZ; + + return (c ? (j * m / c) : (j * m)); +} + +/* average io status */ +static int iostats_avg_status(struct dm_target *ti, status_type_t type, + char *result, int maxlen) +{ + struct iostats_avg_c *ic = (struct iostats_avg_c *) ti->private; + + switch (type) { + case STATUSTYPE_INFO: + { + uint avg_r, avg_w; + + avg_r = _calc(ic->jiffies_r, ic->count_r); + avg_w = _calc(ic->jiffies_w, ic->count_w); + snprintf(result, maxlen, "%s %u %u %u %u", + kdevname(to_kdev_t(ic->dev->bdev->bd_dev)), + ic->reads, ic->writes, avg_r, avg_w); + + break; + } + break; + + case STATUSTYPE_TABLE: + snprintf(result, maxlen, "%s", + kdevname(to_kdev_t(ic->dev->bdev->bd_dev))); + } + return 0; +} + +static struct target_type iostats_target = { + .name = "iostats", + .module = THIS_MODULE, + .ctr = iostats_ctr, + .dtr = iostats_dtr, + .map = iostats_map, + .status = iostats_status, +}; + +static struct target_type iostats_avg_target = { + .name = "iostats_avg", + .module = THIS_MODULE, + .ctr = iostats_avg_ctr, + .dtr = iostats_dtr, + .map = iostats_avg_map, + .status = iostats_avg_status, +}; + +/* Keep the target pointers in an array for (de)registration */ +static struct target_type *target_type_init[] = { + &iostats_target, + &iostats_avg_target, +}; + +void __exit dm_iostats_exit(void) +{ + int i; + const int count = ARRAY_SIZE(target_type_init); + struct target_type **tti = &target_type_init[count-1]; + + for (i = count; i; i--, tti--) { + int r = dm_unregister_target(*tti); + + if (r) + DMERR("%s: unregister failed %d", (*tti)->name, r); + } + + if (_iostats_pool) { + mempool_destroy(_iostats_pool); + _iostats_pool = NULL; + } + + if (_iostats_cache) { + kmem_cache_destroy(_iostats_cache); + _iostats_cache = NULL; + } +} + +int __init dm_iostats_init(void) +{ + int i; + const int count = ARRAY_SIZE(target_type_init); + struct target_type **tti = target_type_init; + + /* allocate a slab for the dm_iostats */ + _iostats_cache = kmem_cache_create("dm iostats io", + sizeof(struct dm_iostats), 0, 0, + NULL, NULL); + + if (!_iostats_cache) + return -ENOMEM; + + /* Create _iostats_pool mempool */ + _iostats_pool = mempool_create(MIN_IOSTATS, mempool_alloc_slab, + mempool_free_slab, _iostats_cache); + + if (!_iostats_pool) { + kmem_cache_destroy(_iostats_cache); + return -ENOMEM; + } + + for (i = 0; i < count; i++, tti++) { + int r = dm_register_target(*tti); + + if (r) { + DMERR("%s: register failed %d", (*tti)->name, r); + + for (tti--; i; i--, tti--) { + r = dm_unregister_target(*tti); + + if (r) + DMERR("%s: unregister failed %d", + (*tti)->name, r); + } + return r; + } + } + + DMINFO("dm_iostats_init v0.0.29"); + return 0; +} + +/* + * module hooks + */ +#if 1 +module_init(dm_iostats_init); +module_exit(dm_iostats_exit); + +MODULE_DESCRIPTION(DM_NAME " iostats target"); +MODULE_AUTHOR("Heinz Mauelshagen "); +MODULE_LICENSE("GPL"); +#endif