From 441e6a008d49bb02c23d76641fc77bd2e648896e Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 5 Aug 2013 16:12:18 -0400 Subject: [PATCH 4/9] 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 | 188 +----------------------------------------- drivers/md/dm-stats.c | 220 +++++++++++++++++++++++++++++++++++++++++++++---- drivers/md/dm-stats.h | 19 +---- drivers/md/dm.h | 9 ++ 4 files changed, 219 insertions(+), 217 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index de4ad28..606dab7 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1455,172 +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; - const char *program, *aux; - - if (dm_request_based(md)) - return -EOPNOTSUPP; - - if (argc < 3 || argc > 5) - 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; - - 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 - * 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, program, aux, - 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_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 && 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_set_aux(dm_get_stats(md), id, argv[2]); -} - /* * Process device-mapper dependent messages. * Returns a number <= 1 if message was processed by device mapper. @@ -1631,27 +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_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); + 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; } @@ -1726,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 e4e397c..a6f5b89 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" @@ -209,12 +210,11 @@ void dm_stats_exit_device(struct dm_stats *st) 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) +static 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) { struct list_head *l; struct dm_stat *s; @@ -339,7 +339,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; @@ -368,9 +368,8 @@ do_sync_free: return 0; } -int dm_stats_list(struct dm_stats *st, - const char *program, - char *result, unsigned maxlen) +static int dm_stats_list(struct dm_stats *st, const char *program, + char *result, unsigned maxlen) { struct dm_stat *m; unsigned sz = 0; @@ -533,10 +532,9 @@ static unsigned long long jtom(unsigned long long j) 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) +static 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; @@ -638,7 +636,7 @@ buffer_overflow: return 1; } -int dm_stats_set_aux(struct dm_stats *st, int id, const char *aux) +static int dm_stats_set_aux(struct dm_stats *st, int id, const char *aux) { struct dm_stat *m; @@ -660,6 +658,198 @@ int dm_stats_set_aux(struct dm_stats *st, int id, const char *aux) return 0; } +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; + const char *program, *aux; + + if (dm_request_based(md)) + return -EOPNOTSUPP; + + if (argc < 3 || argc > 5) + 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; + + 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 + * 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, program, aux, + 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_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 && 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_set_aux(dm_get_stats(md), id, argv[2]); +} + +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_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; /* 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 81f9835..2330fcf 100644 --- a/drivers/md/dm-stats.h +++ b/drivers/md/dm-stats.h @@ -25,28 +25,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, - 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); +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, struct dm_stats_aux *aux); -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.h b/drivers/md/dm.h index 4cb22ed..9236e7c 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -170,4 +170,13 @@ 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