From ecca145c972a940258e4cefb83b54673675ce6cf Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 5 Aug 2013 15:48:33 -0400 Subject: [PATCH 2/9] dm stats: mikulas' latest code drop, no idea how I missed it! --- Documentation/device-mapper/dm-statistics.txt | 52 ++++- drivers/md/dm-ioctl.c | 80 ++++++- drivers/md/dm-stats.c | 310 ++++++++++++++++++++++--- drivers/md/dm-stats.h | 19 ++- drivers/md/dm.c | 7 +- 5 files changed, 414 insertions(+), 54 deletions(-) diff --git a/Documentation/device-mapper/dm-statistics.txt b/Documentation/device-mapper/dm-statistics.txt index 8edf8b1..7049caa 100644 --- a/Documentation/device-mapper/dm-statistics.txt +++ b/Documentation/device-mapper/dm-statistics.txt @@ -17,7 +17,7 @@ and process statistics without stepping over each other's data. Messages ======== -@stats_create +@stats_create [ []] "-" - whole device "-" - a specified range in 512-byte sectors @@ -25,29 +25,65 @@ Messages "" - the number of sectors in each area "/" - the range is subdivided into the specified number of areas + + An optional parameter. The string that identifies a program that + created this range. The kernel returns this string back in the + output of @stats_list command, but it doesn't use this value for + anything. + + An optional parameter. The string that identifies parameters for + a program that created this range. The kernel returns this + string back in the output of @stats_list command, but it doesn't + use this value for anything. @stats_create message creates new region and returns the region id. -@stats_print +@stats_print [ ] - region id returned from @stats_create + Region id returned from @stats_create + + The index of the starting line in the output. If omitted, all + lines are returned. + + The number of lines in the output. If omitted, all lines are + returned. @stats_print message returns statistics, each area is represented by one line in this form: - counters -Counters have the same meaning as /sys/block/*/stat or /proc/diskstats -The counter of merged requests is always zero because merging has no -meaning in device mapper. +The first 11 counters have the same meaning as /sys/block/*/stat or +/proc/diskstats. +Additional counters: +12. the total time spent reading in milliseconds +13. the total time spent writing in milliseconds -@stats_print_clear +@stats_print_clear [ ] region id returned from @stats_create + + The index of the starting line in the output. If omitted, all + lines are returned. + + The number of lines in the output. If omitted, all lines are + returned. @stats_print_clear prints the counters (like @stats_print) and clears -all the counters except the in-flight i/o counters. +all the counters except the in-flight i/o counters. If +and are specified, only the statistics on the lines +that were returned are cleared. @stats_delete region id returned from @stats_create Deletes the range with the specified id. +@stats_list [] +Lists all regions registered with @stats_create. + + An optional parameter specifying the program that queries the + parameters. If this parameter is specified, only regions + belonging to this program are returned. If it is not specified, + all regions are returned. +Output format: +: - + Example ======= diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 13a45c8..a405529 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1468,11 +1468,12 @@ static int message_stats_create(struct mapped_device *md, char dummy; unsigned long long start, end, step; unsigned div; + const char *program, *aux; if (dm_request_based(md)) return -EOPNOTSUPP; - if (argc != 3) + if (argc < 3 || argc > 5) return -EINVAL; if (!strcmp(argv[1], "-")) { @@ -1497,6 +1498,15 @@ static int message_stats_create(struct mapped_device *md, step != (sector_t)step || !step) return -EINVAL; + program = "-"; + aux = "-"; + + if (argc > 3) + program = argv[3]; + + if (argc > 4) + aux = argv[4]; + /* * If a buffer overflow happens after we created the region, * it's too late (the userspace would retry with a larger @@ -1508,9 +1518,8 @@ static int message_stats_create(struct mapped_device *md, if (buffer_test_overflow(result, maxlen)) return 1; - id = dm_stats_create(dm_get_stats(md), start, end, step, - dm_internal_suspend, dm_internal_resume, - md); + id = dm_stats_create(dm_get_stats(md), start, end, step, program, aux, + dm_internal_suspend, dm_internal_resume, md); if (id < 0) return id; @@ -1538,23 +1547,78 @@ static int message_stats_delete(struct mapped_device *md, return dm_stats_delete(dm_get_stats(md), id); } +static int message_stats_list(struct mapped_device *md, + unsigned argc, char **argv, + char *result, unsigned maxlen) +{ + const char *program = NULL; + int r; + + if (dm_request_based(md)) + return -EOPNOTSUPP; + + if (argc < 1 || argc > 2) + return -EINVAL; + + if (argc > 1) { + program = kstrdup(argv[1], GFP_KERNEL); + if (!program) + return -ENOMEM; + } + + r = dm_stats_list(dm_get_stats(md), program, result, maxlen); + + kfree(program); + + return r; +} + static int message_stats_print(struct mapped_device *md, unsigned argc, char **argv, bool clear, char *result, unsigned maxlen) { int id; char dummy; + unsigned long idx_start = 0, idx_len = -1; if (dm_request_based(md)) return -EOPNOTSUPP; - if (argc != 2) + if (argc != 2 && argc != 4) + return -EINVAL; + + if (sscanf(argv[1], "%d%c", &id, &dummy) != 1 || id < 0) + return -EINVAL; + + if (argc > 3) { + if (strcmp(argv[2], "-") && + sscanf(argv[2], "%lu%c", &idx_start, &dummy) != 1) + return -EINVAL; + if (strcmp(argv[3], "-") && + sscanf(argv[3], "%lu%c", &idx_len, &dummy) != 1) + return -EINVAL; + } + + return dm_stats_print(dm_get_stats(md), id, idx_start, idx_len, clear, + result, maxlen); +} + +static int message_stats_set_aux(struct mapped_device *md, + unsigned argc, char **argv) +{ + int id; + char dummy; + + if (dm_request_based(md)) + return -EOPNOTSUPP; + + if (argc != 3) return -EINVAL; if (sscanf(argv[1], "%d%c", &id, &dummy) != 1 || id < 0) return -EINVAL; - return dm_stats_print(dm_get_stats(md), id, clear, result, maxlen); + return dm_stats_set_aux(dm_get_stats(md), id, argv[2]); } /* @@ -1571,10 +1635,14 @@ static int message_for_md(struct mapped_device *md, unsigned argc, char **argv, r = message_stats_create(md, argc, argv, result, maxlen); } else if (!strcasecmp(argv[0], "@stats_delete")) { r = message_stats_delete(md, argc, argv); + } else if (!strcasecmp(argv[0], "@stats_list")) { + r = message_stats_list(md, argc, argv, result, maxlen); } else if (!strcasecmp(argv[0], "@stats_print")) { r = message_stats_print(md, argc, argv, false, result, maxlen); } else if (!strcasecmp(argv[0], "@stats_print_clear")) { r = message_stats_print(md, argc, argv, true, result, maxlen); + } else if (!strcasecmp(argv[0], "@stats_set_aux")) { + r = message_stats_set_aux(md, argc, argv); } else { return 2; } diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 1e2d884..d043aed 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -14,11 +14,13 @@ static volatile int dm_stat_need_rcu_barrier; struct dm_stat_percpu { - unsigned long sectors[2]; - unsigned long ios[2]; - unsigned long ticks[2]; - unsigned long io_ticks; - unsigned long time_in_queue; + unsigned long long sectors[2]; + unsigned long long ios[2]; + unsigned long long ios_merged[2]; + unsigned long long ticks[2]; + unsigned long long io_ticks[2]; + unsigned long long io_ticks_total; + unsigned long long time_in_queue; }; struct dm_stat_shared { @@ -34,24 +36,117 @@ struct dm_stat { sector_t start; sector_t end; sector_t step; + const char *program; + const char *aux; struct rcu_head rcu_head; + size_t shared_alloc_size; + size_t percpu_alloc_size; struct dm_stat_percpu *stat_percpu[NR_CPUS]; struct dm_stat_shared stat_shared[0]; }; +struct dm_stats_last_position { + sector_t last_sector; + unsigned last_rw; +}; + +/* + * A typo on the command line could possibly make the kernel run out of memory + * and crash. To prevent the crash we account all used memory. We fail if we + * exhaust 1/4 of all memory or 1/2 of vmalloc space. + */ + +#define DM_STATS_MEMORY_RATIO 1 / 4 +#define DM_STATS_VMALLOC_RATIO 1 / 2 + +static DEFINE_SPINLOCK(shared_memory_lock); + +static size_t shared_memory_amount = 0; + +static bool _check_shared_memory(size_t alloc_size) +{ + size_t a; + a = shared_memory_amount + alloc_size; + if (a < shared_memory_amount) + return false; + if (a >> PAGE_SHIFT > totalram_pages * DM_STATS_MEMORY_RATIO) + return false; +#ifdef CONFIG_MMU + if (a > (VMALLOC_END - VMALLOC_START) * DM_STATS_VMALLOC_RATIO) + return false; +#endif + return true; +} + +static bool check_shared_memory(size_t alloc_size) +{ + bool ret; + + spin_lock_irq(&shared_memory_lock); + + ret = _check_shared_memory(alloc_size); + + spin_unlock_irq(&shared_memory_lock); + + return ret; +} + +static bool claim_shared_memory(size_t alloc_size) +{ + spin_lock_irq(&shared_memory_lock); + + if (!_check_shared_memory(alloc_size)) { + spin_unlock_irq(&shared_memory_lock); + return false; + } + + shared_memory_amount += alloc_size; + + spin_unlock_irq(&shared_memory_lock); + + return true; +} + +static void free_shared_memory(size_t alloc_size) +{ + unsigned long flags; + + spin_lock_irqsave(&shared_memory_lock, flags); + + BUG_ON(shared_memory_amount < alloc_size); + shared_memory_amount -= alloc_size; + + spin_unlock_irqrestore(&shared_memory_lock, flags); +} + static void *kvzalloc(size_t alloc_size, int node) { void *p; + + if (!claim_shared_memory(alloc_size)) + return NULL; + if (alloc_size <= KMALLOC_MAX_SIZE) { p = kzalloc_node(alloc_size, GFP_KERNEL | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN, node); if (p) return p; } - return vzalloc_node(alloc_size, node); + p = vzalloc_node(alloc_size, node); + if (p) + return p; + + free_shared_memory(alloc_size); + + return NULL; } -static void kvfree(void *ptr) +static void kvfree(void *ptr, size_t alloc_size) { + if (!ptr) + return; + + free_shared_memory(alloc_size); + if (is_vmalloc_addr(ptr)) vfree(ptr); else @@ -62,9 +157,11 @@ static void dm_stat_free(struct rcu_head *head) { struct dm_stat *m = container_of(head, struct dm_stat, rcu_head); int cpu; + kfree(m->program); + kfree(m->aux); for_each_possible_cpu(cpu) - kvfree(m->stat_percpu[cpu]); - kvfree(m); + kvfree(m->stat_percpu[cpu], m->percpu_alloc_size); + kvfree(m, m->percpu_alloc_size); } static int dm_stat_in_flight(struct dm_stat_shared *s) @@ -74,8 +171,16 @@ static int dm_stat_in_flight(struct dm_stat_shared *s) void dm_stats_init_device(struct dm_stats *st) { + int cpu; mutex_init(&st->mutex); INIT_LIST_HEAD(&st->list); + st->last = alloc_percpu(struct dm_stats_last_position); + for_each_possible_cpu(cpu) { + struct dm_stats_last_position *last; + last = per_cpu_ptr(st->last, cpu); + last->last_sector = -1; + last->last_rw = -1; + } } void dm_stats_exit_device(struct dm_stats *st) @@ -100,10 +205,12 @@ void dm_stats_exit_device(struct dm_stats *st) } dm_stat_free(&m->rcu_head); } + free_percpu(st->last); } int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, sector_t step, + const char *program, const char *aux, void (*suspend_callback)(struct mapped_device *), void (*resume_callback)(struct mapped_device *), struct mapped_device *md) @@ -116,6 +223,7 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, size_t percpu_alloc_size; int cpu; int ret_id; + int r; if (end < start || !step) return -EINVAL; @@ -135,6 +243,9 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, if (percpu_alloc_size / sizeof(struct dm_stat_percpu) != n_entries) return -EOVERFLOW; + if (!check_shared_memory(shared_alloc_size + num_possible_cpus() * percpu_alloc_size)) + return -ENOMEM; + s = kvzalloc(shared_alloc_size, NUMA_NO_NODE); if (!s) return -ENOMEM; @@ -143,7 +254,19 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, s->start = start; s->end = end; s->step = step; - s->id = 0; + s->shared_alloc_size = shared_alloc_size; + s->percpu_alloc_size = percpu_alloc_size; + + s->program = kstrdup(program, GFP_KERNEL); + if (!s->program) { + r = -ENOMEM; + goto free_ret; + } + s->aux = kstrdup(aux, GFP_KERNEL); + if (!s->aux) { + r = -ENOMEM; + goto free_ret; + } for (ni = 0; ni < n_entries; ni++) { atomic_set(&s->stat_shared[ni].in_flight[0], 0); @@ -153,8 +276,8 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, for_each_possible_cpu(cpu) { struct dm_stat_percpu *pc = kvzalloc(percpu_alloc_size, cpu_to_node(cpu)); if (!pc) { - dm_stat_free(&s->rcu_head); - return -ENOMEM; + r = -ENOMEM; + goto free_ret; } s->stat_percpu[cpu] = pc; } @@ -169,6 +292,7 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, suspend_callback(md); mutex_lock(&st->mutex); + s->id = 0; list_for_each(l, &st->list) { struct dm_stat *m = container_of(l, struct dm_stat, list_entry); if (m->id < s->id) @@ -178,7 +302,8 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, if (s->id == INT_MAX) { mutex_unlock(&st->mutex); resume_callback(md); - return -ENFILE; + r = -ENFILE; + goto free_ret; } s->id++; } @@ -189,6 +314,10 @@ int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, resume_callback(md); return ret_id; + +free_ret: + dm_stat_free(&s->rcu_head); + return -ENOMEM; } static struct dm_stat *dm_stats_find(struct dm_stats *st, int id) @@ -238,26 +367,53 @@ do_sync_free: return 0; } +int dm_stats_list(struct dm_stats *st, + const char *program, + char *result, unsigned maxlen) +{ + struct dm_stat *m; + unsigned sz = 0; + mutex_lock(&st->mutex); + list_for_each_entry(m, &st->list, list_entry) { + if (!program || !strcmp(program, m->program)) + DMEMIT("%d: %llu-%llu %llu %s %s\n", m->id, + (unsigned long long)m->start, + (unsigned long long)m->end, + (unsigned long long)m->step, + m->program, + m->aux); + } + mutex_unlock(&st->mutex); + return 1; +} + static void dm_stat_round(struct dm_stat_shared *s, struct dm_stat_percpu *p) { /* * This is racy, but so is part_round_stats_single. */ unsigned long now = jiffies; - unsigned inf; - if (now == s->stamp) + unsigned in_flight_read; + unsigned in_flight_write; + unsigned long difference = now - s->stamp; + if (!difference) return; - inf = dm_stat_in_flight(s); - if (inf) { - p->io_ticks += now - s->stamp; - p->time_in_queue += inf * (now - s->stamp); + in_flight_read = atomic_read(&s->in_flight[0]); + in_flight_write = atomic_read(&s->in_flight[1]); + if (in_flight_read) + p->io_ticks[0] += difference; + if (in_flight_write) + p->io_ticks[1] += difference; + if (in_flight_read + in_flight_write) { + p->io_ticks_total += difference; + p->time_in_queue += (in_flight_read + in_flight_write) * difference; } s->stamp = now; } static void dm_stat_for_entry(struct dm_stat *m, size_t entry, - unsigned long bi_rw, unsigned len, bool end, - unsigned long duration) + unsigned long bi_rw, unsigned len, bool merged, + bool end, unsigned long duration) { unsigned long idx = bi_rw & REQ_WRITE; struct dm_stat_shared *s = &m->stat_shared[entry]; @@ -287,6 +443,7 @@ static void dm_stat_for_entry(struct dm_stat *m, size_t entry, atomic_dec(&s->in_flight[idx]); p->sectors[idx] += len; p->ios[idx] += 1; + p->ios_merged[idx] += merged; p->ticks[idx] += duration; } @@ -295,16 +452,30 @@ static void dm_stat_for_entry(struct dm_stat *m, size_t entry, void dm_stats_bio(struct dm_stats *st, unsigned long bi_rw, sector_t bi_sector, unsigned bi_sectors, - bool end, unsigned long duration) + bool end, unsigned long duration, struct dm_stats_aux *aux) { struct dm_stat *m; sector_t end_sector; + struct dm_stats_last_position *last; if (unlikely(!bi_sectors)) return; end_sector = bi_sector + bi_sectors; + if (!end) { + /* + * A race condition can at worst cause that the merged flag is + * misrepresented, so we don't have to disable preempt here. + */ + last = __this_cpu_ptr(st->last); + aux->merged = bi_sector == ACCESS_ONCE(last->last_sector) && + (bi_rw & (REQ_WRITE | REQ_DISCARD)) == + (ACCESS_ONCE(last->last_rw) & (REQ_WRITE | REQ_DISCARD)); + ACCESS_ONCE(last->last_sector) = end_sector; + ACCESS_ONCE(last->last_rw) = bi_rw; + } + rcu_read_lock(); list_for_each_entry_rcu(m, &st->list, list_entry) { @@ -331,7 +502,7 @@ void dm_stats_bio(struct dm_stats *st, if (fragment_len > m->step - offset) fragment_len = m->step - offset; dm_stat_for_entry(m, entry, bi_rw, fragment_len, - end, duration); + aux->merged, end, duration); todo -= fragment_len; entry++; offset = 0; @@ -341,21 +512,52 @@ void dm_stats_bio(struct dm_stats *st, rcu_read_unlock(); } -int dm_stats_print(struct dm_stats *st, int id, bool clear, +/* + * This is like jiffies_to_msec, but works for 64-bit values. + */ +static unsigned long long jtom(unsigned long long j) +{ + unsigned long long result = 0; + unsigned mult; + if (j) { + result = jiffies_to_msecs(j & 0x3fffff); + } + if (j >= 1 << 22) { + mult = jiffies_to_msecs(1 << 22); + result += (unsigned long long)mult * (unsigned long long)jiffies_to_msecs((j >> 22) & 0x3fffff); + } + if (j >= 1ULL << 44) { + result += (unsigned long long)mult * (unsigned long long)mult * (unsigned long long)jiffies_to_msecs(j >> 44); + } + return result; +} + +int dm_stats_print(struct dm_stats *st, int id, + size_t idx_start, size_t idx_len, + bool clear, char *result, unsigned maxlen) { unsigned sz = 0; struct dm_stat *m; size_t x; sector_t start, end; + size_t idx_end; m = dm_stats_find(st, id); if (!m) return -ENOENT; - start = m->start; + idx_end = idx_start + idx_len; + if (idx_end < idx_start || + idx_end > m->n_entries) + idx_end = m->n_entries; - for (x = 0; x < m->n_entries; x++, start = end) { + if (idx_start > idx_end) + idx_start = idx_end; + + start = m->start + m->step * idx_start; + + for (x = idx_start; x < idx_end; x++, start = end) { int cpu; struct dm_stat_shared *s = &m->stat_shared[x]; struct dm_stat_percpu *p; @@ -376,33 +578,39 @@ int dm_stats_print(struct dm_stats *st, int id, bool clear, s->tmp.sectors[1] += p->sectors[1]; s->tmp.ios[0] += p->ios[0]; s->tmp.ios[1] += p->ios[1]; + s->tmp.ios_merged[0] += p->ios_merged[0]; + s->tmp.ios_merged[1] += p->ios_merged[1]; s->tmp.ticks[0] += p->ticks[0]; s->tmp.ticks[1] += p->ticks[1]; - s->tmp.io_ticks += p->io_ticks; + s->tmp.io_ticks[0] += p->io_ticks[0]; + s->tmp.io_ticks[1] += p->io_ticks[1]; + s->tmp.io_ticks_total += p->io_ticks_total; s->tmp.time_in_queue += p->time_in_queue; } - DMEMIT("%llu-%llu %lu %u %lu %lu %lu %u %lu %lu %d %lu %lu\n", + DMEMIT("%llu-%llu %llu %llu %llu %llu %llu %llu %llu %llu %d %llu %llu %llu %llu\n", (unsigned long long)start, (unsigned long long)end, s->tmp.ios[0], - 0U, + s->tmp.ios_merged[0], s->tmp.sectors[0], - s->tmp.ticks[0], + jtom(s->tmp.ticks[0]), s->tmp.ios[1], - 0U, + s->tmp.ios_merged[1], s->tmp.sectors[1], - s->tmp.ticks[1], + jtom(s->tmp.ticks[1]), dm_stat_in_flight(s), - s->tmp.io_ticks, - s->tmp.time_in_queue + jtom(s->tmp.io_ticks_total), + jtom(s->tmp.time_in_queue), + jtom(s->tmp.io_ticks[0]), + jtom(s->tmp.io_ticks[1]) ); if (unlikely(sz + 1 >= maxlen)) goto buffer_overflow; } if (clear) { - for (x = 0; x < m->n_entries; x++) { + for (x = idx_start; x < idx_end; x++) { struct dm_stat_shared *s = &m->stat_shared[x]; struct dm_stat_percpu *p; local_irq_disable(); @@ -411,9 +619,13 @@ int dm_stats_print(struct dm_stats *st, int id, bool clear, p->sectors[1] -= s->tmp.sectors[1]; p->ios[0] -= s->tmp.ios[0]; p->ios[1] -= s->tmp.ios[1]; + p->ios_merged[0] -= s->tmp.ios_merged[0]; + p->ios_merged[1] -= s->tmp.ios_merged[1]; p->ticks[0] -= s->tmp.ticks[0]; p->ticks[1] -= s->tmp.ticks[1]; - p->io_ticks -= s->tmp.io_ticks; + p->io_ticks[0] -= s->tmp.io_ticks[0]; + p->io_ticks[1] -= s->tmp.io_ticks[1]; + p->io_ticks_total -= s->tmp.io_ticks_total; p->time_in_queue -= s->tmp.time_in_queue; local_irq_enable(); } @@ -425,6 +637,28 @@ buffer_overflow: return 1; } +int dm_stats_set_aux(struct dm_stats *st, int id, const char *aux) +{ + struct dm_stat *m; + + m = dm_stats_find(st, id); + if (!m) + return -ENOENT; + + aux = kstrdup(aux, GFP_KERNEL); + if (!aux) { + mutex_unlock(&st->mutex); + return -ENOMEM; + } + + kfree(m->aux); + m->aux = aux; + + mutex_unlock(&st->mutex); + + return 0; +} + int __init dm_stats_init(void) { dm_stat_need_rcu_barrier = 0; @@ -435,4 +669,8 @@ void dm_stats_exit(void) { if (dm_stat_need_rcu_barrier) rcu_barrier(); + if (shared_memory_amount) { + printk(KERN_CRIT "dm_stats: shared_memory_amount leaked: %lu", + (unsigned long)shared_memory_amount); + } } diff --git a/drivers/md/dm-stats.h b/drivers/md/dm-stats.h index 2437986..81f9835 100644 --- a/drivers/md/dm-stats.h +++ b/drivers/md/dm-stats.h @@ -11,6 +11,13 @@ void dm_stats_exit(void); struct dm_stats { struct mutex mutex; struct list_head list; /* list of struct dm_stat */ + struct dm_stats_last_position __percpu *last; + sector_t last_sector; + unsigned last_rw; +}; + +struct dm_stats_aux { + bool merged; }; void dm_stats_init_device(struct dm_stats *st); @@ -20,18 +27,26 @@ struct mapped_device; int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, sector_t step, + const char *program, const char *aux, void (*suspend_callback)(struct mapped_device *), void (*resume_callback)(struct mapped_device *), struct mapped_device *md); int dm_stats_delete(struct dm_stats *st, int id); +int dm_stats_list(struct dm_stats *st, const char *program, + char *result, unsigned maxlen); + void dm_stats_bio(struct dm_stats *st, unsigned long bi_rw, sector_t bi_sector, unsigned bi_sectors, - bool end, unsigned long duration); + bool end, unsigned long duration, struct dm_stats_aux *aux); -int dm_stats_print(struct dm_stats *st, int id, bool clear, +int dm_stats_print(struct dm_stats *st, int id, + size_t idx_start, size_t idx_len, + bool clear, char *result, unsigned maxlen); +int dm_stats_set_aux(struct dm_stats *st, int id, const char *aux); + static inline bool dm_stats_used(struct dm_stats *st) { return !list_empty(&st->list); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index f88d9c1..7f80ecb 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -59,6 +59,7 @@ struct dm_io { atomic_t io_count; struct bio *bio; unsigned long start_time; + struct dm_stats_aux aux; spinlock_t endio_lock; }; @@ -494,7 +495,8 @@ static void start_io_acct(struct dm_io *io) if (unlikely(dm_stats_used(&md->stats))) { struct bio *bio = io->bio; dm_stats_bio(&md->stats, bio->bi_rw, bio->bi_sector, - bio_sectors(bio), false, 0); + bio_sectors(bio), false, 0, + &io->aux); } } @@ -513,7 +515,8 @@ static void end_io_acct(struct dm_io *io) if (unlikely(dm_stats_used(&md->stats))) dm_stats_bio(&md->stats, bio->bi_rw, bio->bi_sector, - bio_sectors(bio), true, duration); + bio_sectors(bio), true, duration, + &io->aux); /* * After this is decremented the bio must not be touched if it is -- 1.7.1