From f4262026b671ac2604ec8143c6fe328e9ab159aa Mon Sep 17 00:00:00 2001 From: "Bryn M. Reeves" Date: Thu, 20 Aug 2015 11:55:06 +0100 Subject: libdm: add precise timestamps support to libdm-stats Add support for the kernel precise_timestamps feature. This allows regions to be created using counters with nanosecond precision. A new dm_stats method, dm_stats_set_precise_timestamps() causes all future regions created with this handle to attempt to enable precise counters. --- libdm/.exported_symbols.DM_1_02_106 | 2 + libdm/libdevmapper.h | 11 +++ libdm/libdm-stats.c | 155 +++++++++++++++++++++++++++++------- 3 files changed, 141 insertions(+), 27 deletions(-) (limited to 'libdm') diff --git a/libdm/.exported_symbols.DM_1_02_106 b/libdm/.exported_symbols.DM_1_02_106 index 0ce8924c9..b664cc6d0 100644 --- a/libdm/.exported_symbols.DM_1_02_106 +++ b/libdm/.exported_symbols.DM_1_02_106 @@ -1 +1,3 @@ dm_message_supports_precise_timestamps +dm_stats_create_region +dm_stats_driver_supports_precise diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index 2be2ce37b..71b8ce4c9 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -473,6 +473,13 @@ int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid); */ int dm_message_supports_precise_timestamps(void); +/* + * Precise timetamps support. + * + * Test for the presence of precise_timestamps support. + */ +int dm_stats_driver_supports_precise(void); + #define DM_STATS_ALL_PROGRAMS "" /* * Parse the response from a @stats_list message. dm_stats_list will @@ -535,6 +542,9 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id, * * dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a); * + * If precise is non-zero attempt to create a region with nanosecond + * precision counters using the kernel precise_timestamps feature. + * * program_id is an optional string argument that identifies the * program creating the region. If program_id is NULL or the empty * string the default program_id stored in the handle will be used. @@ -548,6 +558,7 @@ int dm_stats_populate(struct dm_stats *dms, const char *program_id, */ int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id, uint64_t start, uint64_t len, int64_t step, + int precise, const char *program_id, const char *aux_data); /* diff --git a/libdm/libdm-stats.c b/libdm/libdm-stats.c index 4b4135af8..b85300ff6 100644 --- a/libdm/libdm-stats.c +++ b/libdm/libdm-stats.c @@ -19,6 +19,8 @@ #define NSEC_PER_MSEC 1000000L #define NSEC_PER_SEC 1000000000L +#define PRECISE_ARG "precise_timestamps" + /* * See Documentation/device-mapper/statistics.txt for full descriptions * of the device-mapper statistics counter fields. @@ -61,7 +63,8 @@ struct dm_stats { uint64_t nr_regions; /* total number of present regions */ uint64_t max_region; /* size of the regions table */ uint64_t interval_ns; /* sampling interval in nanoseconds */ - uint64_t timescale; /* sample value multiplier */ + uint64_t timescale; /* default sample value multiplier */ + int precise; /* use precise_timestamps when creating regions */ struct dm_stats_region *regions; /* statistics cursor */ uint64_t cur_region; @@ -109,8 +112,9 @@ struct dm_stats *dm_stats_create(const char *program_id) dms->name = NULL; dms->uuid = NULL; - /* all regions currently use msec precision */ + /* by default all regions use msec precision */ dms->timescale = NSEC_PER_MSEC; + dms->precise = 0; dms->nr_regions = DM_STATS_REGION_NOT_PRESENT; dms->max_region = DM_STATS_REGION_NOT_PRESENT; @@ -225,6 +229,19 @@ int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid) return 1; } +static int _stats_check_precise_timestamps(const struct dm_stats *dms) +{ + /* Already checked? */ + if (dms && dms->precise) + return 1; + return dm_message_supports_precise_timestamps(); +} + +int dm_stats_driver_supports_precise(void) +{ + return _stats_check_precise_timestamps(NULL); +} + static struct dm_task *_stats_send_message(struct dm_stats *dms, char *msg) { struct dm_task *dmt; @@ -249,24 +266,55 @@ out: static int _stats_parse_list_region(struct dm_stats_region *region, char *line) { - /* FIXME: the kernel imposes no length limit here */ - char program_id[256], aux_data[256]; + char *p = NULL, string_data[4096]; /* FIXME: add dm_sscanf with %ms? */ + const char *program_id, *aux_data, *stats_args; + const char *empty_string = ""; int r; - /* line format: - * : + + memset(string_data, 0, sizeof(string_data)); + + /* + * Parse fixed fields, line format: + * + * : + + * + * Maximum string data size is 4096 - 1 bytes. */ - r = sscanf(line, FMTu64 ": " FMTu64 "+" FMTu64 " " FMTu64 "%255s %255s", - ®ion->region_id, ®ion->start, ®ion->len, ®ion->step, - program_id, aux_data); + r = sscanf(line, FMTu64 ": " FMTu64 "+" FMTu64 " " FMTu64 " %4095c", + ®ion->region_id, ®ion->start, ®ion->len, + ®ion->step, string_data); - if (r != 6) + if (r != 5) return_0; - if (!strcmp(program_id, "-")) - program_id[0] = '\0'; - if (!strcmp(aux_data, "-")) - aux_data[0] = '\0'; + /* program_id is guaranteed to be first. */ + program_id = string_data; + + /* + * FIXME: support embedded '\ ' in string data: + * s/strchr/_find_unescaped_space()/ + */ + if ((p = strchr(string_data, ' '))) { + /* terminate program_id string. */ + *p = '\0'; + if (!strcmp(program_id, "-")) + program_id = empty_string; + aux_data = p + 1; + if ((p = strchr(aux_data, ' '))) { + /* terminate aux_data string. */ + *p = '\0'; + if (!strcmp(aux_data, "-")) + aux_data = empty_string; + stats_args = p + 1; + } else + stats_args = empty_string; + } else + aux_data = stats_args = empty_string; + + if (strstr(stats_args, PRECISE_ARG)) + region->timescale = 1; + else + region->timescale = NSEC_PER_MSEC; if (!(region->program_id = dm_strdup(program_id))) return_0; @@ -621,14 +669,17 @@ uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms) return nr_areas; } -int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id, - uint64_t start, uint64_t len, int64_t step, - const char *program_id, const char *aux_data) +static int _stats_create_region(struct dm_stats *dms, uint64_t *region_id, + uint64_t start, uint64_t len, int64_t step, + int precise, + const char *program_id, const char *aux_data) { struct dm_task *dmt = NULL; char msg[1024], range[64]; const char *err_fmt = "Could not prepare @stats_create %s."; - const char *resp; + const char *precise_str = PRECISE_ARG; + const char *resp, *opt_args = NULL; + int nr_opt = 0, r = 0; /* number of optional args. */ if (!_stats_bound(dms)) return_0; @@ -640,16 +691,35 @@ int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id, if (!dm_snprintf(range, sizeof(range), FMTu64 "+" FMTu64, start, len)) { log_error(err_fmt, "range"); - goto out; + return 0; } } - if (!dm_snprintf(msg, sizeof(msg), "@stats_create %s %s" FMTu64 " %s %s", - (start || len) ? range : "-", + if (precise < 0) + precise = dms->precise; + + if (precise) + nr_opt++; + else + precise_str = ""; + + if (nr_opt) { + if ((dm_asprintf((char **)&opt_args, "%d %s", nr_opt, + precise_str)) < 0) { + log_error(err_fmt, PRECISE_ARG " option."); + return 0; + } + } else + opt_args = dm_strdup(""); + + if (!dm_snprintf(msg, sizeof(msg), "@stats_create %s %s" FMTu64 + " %s %s %s", (start || len) ? range : "-", (step < 0) ? "/" : "", - (uint64_t)llabs(step), program_id, aux_data)) { + (uint64_t)llabs(step), + opt_args, program_id, aux_data)) { log_error(err_fmt, "message"); - goto out; + dm_free((void *) opt_args); + return 0; } if (!(dmt = _stats_send_message(dms, msg))) @@ -668,14 +738,45 @@ int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id, goto_out; } - dm_task_destroy(dmt); + r = 1; - return 1; out: if(dmt) dm_task_destroy(dmt); - return 0; + dm_free((void *) opt_args); + return r; +} + +int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id, + uint64_t start, uint64_t len, int64_t step, + int precise, + const char *program_id, + const char *aux_data) +{ + /* Nanosecond counters need precise_timestamps. */ + if (precise && !_stats_check_precise_timestamps(dms)) + return 0; + + return _stats_create_region(dms, region_id, start, len, step, + precise, + program_id, aux_data); +} + +/* Backward compatible dm_stats_create_region() */ +#if defined(__GNUC__) +int dm_stats_create_region_v1_02_104(struct dm_stats *dms, uint64_t *region_id, + uint64_t start, uint64_t len, int64_t step, + const char *program_id, const char *aux_data); +int dm_stats_create_region_v1_02_104(struct dm_stats *dms, uint64_t *region_id, + uint64_t start, uint64_t len, int64_t step, + const char *program_id, const char *aux_data) +{ + return _stats_create_region(dms, region_id, start, len, step, + dms->precise, + program_id, aux_data); } +DM_EXPORT_SYMBOL(dm_stats_create_region, 1_02_104); +#endif int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id) { @@ -815,7 +916,7 @@ static int _dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id, if (!_stats_bound(dms)) return_0; - if (!_stats_parse_region(dms->mem, resp, region, dms->timescale)) { + if (!_stats_parse_region(dms->mem, resp, region, region->timescale)) { log_error("Could not parse @stats_print message response."); return 0; } -- cgit v1.2.1