From 3164600160d04f70a9e1287ff1cd69d7113e47e6 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Fri, 2 Aug 2013 16:08:34 -0400 Subject: [PATCH 3/4] dm stats: moved statistics message handlers into dm-stats.c No reason to have so much statistics specific parsing/handling in dm-ioctl.c --- drivers/md/dm-ioctl.c | 120 +------------------------------------------- drivers/md/dm-stats.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++--- drivers/md/dm-stats.h | 11 +--- drivers/md/dm.h | 8 +++ 4 files changed, 137 insertions(+), 134 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 3e7c055..606dab7 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1455,108 +1455,6 @@ static int table_status(struct dm_ioctl *param, size_t param_size) return 0; } -static bool buffer_test_overflow(char *result, unsigned maxlen) -{ - return !maxlen || strlen(result) + 1 >= maxlen; -} - -static int message_stats_create(struct mapped_device *md, - unsigned argc, char **argv, - char *result, unsigned maxlen) -{ - int id; - char dummy; - unsigned long long start, end, step; - unsigned div; - - if (dm_request_based(md)) - return -EOPNOTSUPP; - - if (argc != 3) - return -EINVAL; - - if (!strcmp(argv[1], "-")) { - start = 0; - end = dm_get_size(md); - if (!end) - end = 1; - } else if (sscanf(argv[1], "%llu-%llu%c", &start, &end, &dummy) != 2 || - start != (sector_t)start || end != (sector_t)end) - return -EINVAL; - - if (start >= end) - return -EINVAL; - - if (sscanf(argv[2], "/%u%c", &div, &dummy) == 1) { - step = end - start; - if (do_div(step, div)) - step++; - if (!step) - step = 1; - } else if (sscanf(argv[2], "%llu%c", &step, &dummy) != 1 || - step != (sector_t)step || !step) - return -EINVAL; - - /* - * If a buffer overflow happens after we created the region, - * it's too late (the userspace would retry with a larger - * buffer, but the region id that caused the overflow is already - * leaked). - * So we must detect buffer overflow in advance. - */ - snprintf(result, maxlen, "%d", INT_MAX); - 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); - - if (id < 0) - return id; - - snprintf(result, maxlen, "%d", id); - - return 1; -} - -static int message_stats_delete(struct mapped_device *md, - unsigned argc, char **argv) -{ - int id; - char dummy; - - if (dm_request_based(md)) - return -EOPNOTSUPP; - - if (argc != 2) - return -EINVAL; - - if (sscanf(argv[1], "%d%c", &id, &dummy) != 1 || id < 0) - return -EINVAL; - - return dm_stats_delete(dm_get_stats(md), id); -} - -static int message_stats_print(struct mapped_device *md, - unsigned argc, char **argv, bool clear, - char *result, unsigned maxlen) -{ - int id; - char dummy; - - if (dm_request_based(md)) - return -EOPNOTSUPP; - - if (argc != 2) - 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); -} - /* * Process device-mapper dependent messages. * Returns a number <= 1 if message was processed by device mapper. @@ -1567,23 +1465,11 @@ static int message_for_md(struct mapped_device *md, unsigned argc, char **argv, { int r; - if (!strcasecmp(argv[0], "@stats_create")) - 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_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); + if ((r = dm_stats_message(md, argc, argv, result, maxlen)) < 2) + return r; else return 2; - if (r == -EOPNOTSUPP) - DMWARN("Statistics are only supported for bio based devices"); - - if (r == -EINVAL) - DMWARN("Invalid parameters for message %s", argv[0]); - return r; } @@ -1658,7 +1544,7 @@ static int target_message(struct dm_ioctl *param, size_t param_size) if (r == 1) { param->flags |= DM_DATA_OUT_FLAG; - if (buffer_test_overflow(result, maxlen)) + if (dm_message_test_buffer_overflow(result, maxlen)) param->flags |= DM_BUFFER_FULL_FLAG; else param->data_size = param->data_start + strlen(result) + 1; diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index a954a6f..9b9ccd7 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -9,6 +9,7 @@ #include #include +#include "dm.h" #include "dm-stats.h" #define DM_MSG_PREFIX "stats" @@ -103,11 +104,11 @@ void dm_stats_exit_device(struct dm_stats *st) } } -int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, - sector_t step, - void (*suspend_callback)(struct mapped_device *), - void (*resume_callback)(struct mapped_device *), - struct mapped_device *md) +static int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, + sector_t step, + void (*suspend_callback)(struct mapped_device *), + void (*resume_callback)(struct mapped_device *), + struct mapped_device *md) { struct list_head *l; struct dm_stat *s; @@ -210,7 +211,7 @@ static struct dm_stat *dm_stats_find(struct dm_stats *st, int id) return NULL; } -int dm_stats_delete(struct dm_stats *st, int id) +static int dm_stats_delete(struct dm_stats *st, int id) { struct dm_stat *m; int cpu; @@ -342,8 +343,8 @@ void dm_stats_bio(struct dm_stats *st, rcu_read_unlock(); } -int dm_stats_print(struct dm_stats *st, int id, bool clear, - char *result, unsigned maxlen) +static int dm_stats_print(struct dm_stats *st, int id, bool clear, + char *result, unsigned maxlen) { unsigned sz = 0; struct dm_stat *m; @@ -426,6 +427,121 @@ buffer_overflow: return 1; } +static int message_stats_create(struct mapped_device *md, + unsigned argc, char **argv, + char *result, unsigned maxlen) +{ + int id; + char dummy; + unsigned long long start, end, step; + unsigned div; + + if (argc != 3) + return -EINVAL; + + if (!strcmp(argv[1], "-")) { + start = 0; + end = dm_get_size(md); + if (!end) + end = 1; + } else if (sscanf(argv[1], "%llu-%llu%c", &start, &end, &dummy) != 2 || + start != (sector_t)start || end != (sector_t)end) + return -EINVAL; + + if (start >= end) + return -EINVAL; + + if (sscanf(argv[2], "/%u%c", &div, &dummy) == 1) { + step = end - start; + if (do_div(step, div)) + step++; + if (!step) + step = 1; + } else if (sscanf(argv[2], "%llu%c", &step, &dummy) != 1 || + step != (sector_t)step || !step) + return -EINVAL; + + /* + * If a buffer overflow happens after we created the region, + * it's too late (the userspace would retry with a larger + * buffer, but the region id that caused the overflow is already + * leaked). + * So we must detect buffer overflow in advance. + */ + snprintf(result, maxlen, "%d", INT_MAX); + if (dm_message_test_buffer_overflow(result, maxlen)) + return 1; + + id = dm_stats_create(dm_get_stats(md), start, end, step, + dm_internal_suspend, dm_internal_resume, + md); + + if (id < 0) + return id; + + snprintf(result, maxlen, "%d", id); + + return 1; +} + +static int message_stats_delete(struct mapped_device *md, + unsigned argc, char **argv) +{ + int id; + char dummy; + + if (argc != 2) + return -EINVAL; + + if (sscanf(argv[1], "%d%c", &id, &dummy) != 1 || id < 0) + return -EINVAL; + + return dm_stats_delete(dm_get_stats(md), id); +} + +static int message_stats_print(struct mapped_device *md, + unsigned argc, char **argv, bool clear, + char *result, unsigned maxlen) +{ + int id; + char dummy; + + if (argc != 2) + 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); +} + +int dm_stats_message(struct mapped_device *md, unsigned argc, char **argv, + char *result, unsigned maxlen) +{ + int r; + + if (dm_request_based(md)) { + DMWARN("Statistics are only supported for bio-based devices"); + return -EOPNOTSUPP; + } + + if (!strcasecmp(argv[0], "@stats_create")) + 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_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 + return 2; /* this wasn't a stats message */ + + if (r == -EINVAL) + DMWARN("Invalid parameters for message %s", argv[0]); + + return r; +} + int __init dm_stats_init(void) { dm_stat_need_rcu_barrier = 0; diff --git a/drivers/md/dm-stats.h b/drivers/md/dm-stats.h index 2437986..bc1ec44 100644 --- a/drivers/md/dm-stats.h +++ b/drivers/md/dm-stats.h @@ -18,20 +18,13 @@ void dm_stats_exit_device(struct dm_stats *st); struct mapped_device; -int dm_stats_create(struct dm_stats *st, sector_t start, sector_t end, - sector_t step, - 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_message(struct mapped_device *md, unsigned argc, char **argv, + 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); -int dm_stats_print(struct dm_stats *st, int id, bool clear, - char *result, unsigned maxlen); - static inline bool dm_stats_used(struct dm_stats *st) { return !list_empty(&st->list); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 4cb22ed..97633cd 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -170,4 +170,12 @@ void dm_kcopyd_exit(void); struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, unsigned per_bio_data_size); void dm_free_md_mempools(struct dm_md_mempools *pools); +/* + * Helpers that are used by DM core + */ +static inline bool dm_message_test_buffer_overflow(char *result, unsigned maxlen) +{ + return !maxlen || strlen(result) + 1 >= maxlen; +} + #endif -- 1.7.1