diff options
Diffstat (limited to 'device_mapper/libdm-targets.c')
-rw-r--r-- | device_mapper/libdm-targets.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/device_mapper/libdm-targets.c b/device_mapper/libdm-targets.c new file mode 100644 index 0000000..bfe76c5 --- /dev/null +++ b/device_mapper/libdm-targets.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "misc/dmlib.h" +#include "libdm-common.h" + +int dm_get_status_snapshot(struct dm_pool *mem, const char *params, + struct dm_status_snapshot **status) +{ + struct dm_status_snapshot *s; + int r; + + if (!params) { + log_error("Failed to parse invalid snapshot params."); + return 0; + } + + if (!(s = dm_pool_zalloc(mem, sizeof(*s)))) { + log_error("Failed to allocate snapshot status structure."); + return 0; + } + + r = sscanf(params, FMTu64 "/" FMTu64 " " FMTu64, + &s->used_sectors, &s->total_sectors, + &s->metadata_sectors); + + if (r == 3 || r == 2) + s->has_metadata_sectors = (r == 3); + else if (!strcmp(params, "Invalid")) + s->invalid = 1; + else if (!strcmp(params, "Merge failed")) + s->merge_failed = 1; + else if (!strcmp(params, "Overflow")) + s->overflow = 1; + else { + dm_pool_free(mem, s); + log_error("Failed to parse snapshot params: %s.", params); + return 0; + } + + *status = s; + + return 1; +} + +/* + * Skip nr fields each delimited by a single space. + * FIXME Don't assume single space. + */ +static const char *_skip_fields(const char *p, unsigned nr) +{ + while (p && nr-- && (p = strchr(p, ' '))) + p++; + + return p; +} + +/* + * Count number of single-space delimited fields. + * Number of fields is number of spaces plus one. + */ +static unsigned _count_fields(const char *p) +{ + unsigned nr = 1; + + if (!p || !*p) + return 0; + + while ((p = _skip_fields(p, 1))) + nr++; + + return nr; +} + +/* + * Various RAID status versions include: + * Versions < 1.5.0 (4 fields): + * <raid_type> <#devs> <health_str> <sync_ratio> + * Versions 1.5.0+ (6 fields): + * <raid_type> <#devs> <health_str> <sync_ratio> <sync_action> <mismatch_cnt> + * Versions 1.9.0+ (7 fields): + * <raid_type> <#devs> <health_str> <sync_ratio> <sync_action> <mismatch_cnt> <data_offset> + */ +int dm_get_status_raid(struct dm_pool *mem, const char *params, + struct dm_status_raid **status) +{ + int i; + unsigned num_fields; + const char *p, *pp, *msg_fields = ""; + struct dm_status_raid *s = NULL; + unsigned a = 0; + + if ((num_fields = _count_fields(params)) < 4) + goto_bad; + + /* Second field holds the device count */ + msg_fields = "<#devs> "; + if (!(p = _skip_fields(params, 1)) || (sscanf(p, "%d", &i) != 1)) + goto_bad; + + msg_fields = ""; + if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_raid)))) + goto_bad; + + if (!(s->raid_type = dm_pool_zalloc(mem, p - params))) + goto_bad; /* memory is freed when pool is destroyed */ + + if (!(s->dev_health = dm_pool_zalloc(mem, i + 1))) /* Space for health chars */ + goto_bad; + + msg_fields = "<raid_type> <#devices> <health_chars> and <sync_ratio> "; + if (sscanf(params, "%s %u %s " FMTu64 "/" FMTu64, + s->raid_type, + &s->dev_count, + s->dev_health, + &s->insync_regions, + &s->total_regions) != 5) + goto_bad; + + /* + * All pre-1.5.0 version parameters are read. Now we check + * for additional 1.5.0+ parameters (i.e. num_fields at least 6). + * + * Note that 'sync_action' will be NULL (and mismatch_count + * will be 0) if the kernel returns a pre-1.5.0 status. + */ + if (num_fields < 6) + goto out; + + msg_fields = "<sync_action> and <mismatch_cnt> "; + + /* Skip pre-1.5.0 params */ + if (!(p = _skip_fields(params, 4)) || !(pp = _skip_fields(p, 1))) + goto_bad; + + if (!(s->sync_action = dm_pool_zalloc(mem, pp - p))) + goto_bad; + + if (sscanf(p, "%s " FMTu64, s->sync_action, &s->mismatch_count) != 2) + goto_bad; + + if (num_fields < 7) + goto out; + + /* + * All pre-1.9.0 version parameters are read. Now we check + * for additional 1.9.0+ parameters (i.e. nr_fields at least 7). + * + * Note that data_offset will be 0 if the + * kernel returns a pre-1.9.0 status. + */ + msg_fields = "<data_offset>"; + if (!(p = _skip_fields(params, 6))) /* skip pre-1.9.0 params */ + goto bad; + if (sscanf(p, FMTu64, &s->data_offset) != 1) + goto bad; + +out: + *status = s; + + if (s->insync_regions == s->total_regions) { + /* FIXME: kernel gives misleading info here + * Trying to recognize a true state */ + while (i-- > 0) + if (s->dev_health[i] == 'a') + a++; /* Count number of 'a' */ + + if (a && a < s->dev_count) { + /* SOME legs are in 'a' */ + if (!strcasecmp(s->sync_action, "recover") + || !strcasecmp(s->sync_action, "idle")) + /* Kernel may possibly start some action + * in near-by future, do not report 100% */ + s->insync_regions--; + } + } + + return 1; + +bad: + log_error("Failed to parse %sraid params: %s", msg_fields, params); + + if (s) + dm_pool_free(mem, s); + + *status = NULL; + + return 0; +} + +/* + * <metadata block size> <#used metadata blocks>/<#total metadata blocks> + * <cache block size> <#used cache blocks>/<#total cache blocks> + * <#read hits> <#read misses> <#write hits> <#write misses> + * <#demotions> <#promotions> <#dirty> <#features> <features>* + * <#core args> <core args>* <policy name> <#policy args> <policy args>* + * + * metadata block size : Fixed block size for each metadata block in + * sectors + * #used metadata blocks : Number of metadata blocks used + * #total metadata blocks : Total number of metadata blocks + * cache block size : Configurable block size for the cache device + * in sectors + * #used cache blocks : Number of blocks resident in the cache + * #total cache blocks : Total number of cache blocks + * #read hits : Number of times a READ bio has been mapped + * to the cache + * #read misses : Number of times a READ bio has been mapped + * to the origin + * #write hits : Number of times a WRITE bio has been mapped + * to the cache + * #write misses : Number of times a WRITE bio has been + * mapped to the origin + * #demotions : Number of times a block has been removed + * from the cache + * #promotions : Number of times a block has been moved to + * the cache + * #dirty : Number of blocks in the cache that differ + * from the origin + * #feature args : Number of feature args to follow + * feature args : 'writethrough' (optional) + * #core args : Number of core arguments (must be even) + * core args : Key/value pairs for tuning the core + * e.g. migration_threshold + * *policy name : Name of the policy + * #policy args : Number of policy arguments to follow (must be even) + * policy args : Key/value pairs + * e.g. sequential_threshold + */ +int dm_get_status_cache(struct dm_pool *mem, const char *params, + struct dm_status_cache **status) +{ + int i, feature_argc; + char *str; + const char *p, *pp; + struct dm_status_cache *s; + + if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_cache)))) + return_0; + + if (strstr(params, "Error")) { + s->error = 1; + s->fail = 1; /* This is also I/O fail state */ + goto out; + } + + if (strstr(params, "Fail")) { + s->fail = 1; + goto out; + } + + /* Read in args that have definitive placement */ + if (sscanf(params, + " " FMTu32 + " " FMTu64 "/" FMTu64 + " " FMTu32 + " " FMTu64 "/" FMTu64 + " " FMTu64 " " FMTu64 + " " FMTu64 " " FMTu64 + " " FMTu64 " " FMTu64 + " " FMTu64 + " %d", + &s->metadata_block_size, + &s->metadata_used_blocks, &s->metadata_total_blocks, + &s->block_size, /* AKA, chunk_size */ + &s->used_blocks, &s->total_blocks, + &s->read_hits, &s->read_misses, + &s->write_hits, &s->write_misses, + &s->demotions, &s->promotions, + &s->dirty_blocks, + &feature_argc) != 14) + goto bad; + + /* Now jump to "features" section */ + if (!(p = _skip_fields(params, 12))) + goto bad; + + /* Read in features */ + for (i = 0; i < feature_argc; i++) { + if (!strncmp(p, "writethrough ", 13)) + s->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; + else if (!strncmp(p, "writeback ", 10)) + s->feature_flags |= DM_CACHE_FEATURE_WRITEBACK; + else if (!strncmp(p, "passthrough ", 12)) + s->feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH; + else if (!strncmp(p, "metadata2 ", 10)) + s->feature_flags |= DM_CACHE_FEATURE_METADATA2; + else if (!strncmp(p, "no_discard_passdown ", 20)) + s->feature_flags |= DM_CACHE_FEATURE_NO_DISCARD_PASSDOWN; + else + log_error("Unknown feature in status: %s", params); + + if (!(p = _skip_fields(p, 1))) + goto bad; + } + + /* Read in core_args. */ + if (sscanf(p, "%d ", &s->core_argc) != 1) + goto bad; + if ((s->core_argc > 0) && + (!(s->core_argv = dm_pool_zalloc(mem, sizeof(char *) * s->core_argc)) || + !(p = _skip_fields(p, 1)) || + !(str = dm_pool_strdup(mem, p)) || + !(p = _skip_fields(p, (unsigned) s->core_argc)) || + (dm_split_words(str, s->core_argc, 0, s->core_argv) != s->core_argc))) + goto bad; + + /* Read in policy args */ + pp = p; + if (!(p = _skip_fields(p, 1)) || + !(s->policy_name = dm_pool_zalloc(mem, (p - pp)))) + goto bad; + if (sscanf(pp, "%s %d", s->policy_name, &s->policy_argc) != 2) + goto bad; + if (s->policy_argc && + (!(s->policy_argv = dm_pool_zalloc(mem, sizeof(char *) * s->policy_argc)) || + !(p = _skip_fields(p, 1)) || + !(str = dm_pool_strdup(mem, p)) || + (dm_split_words(str, s->policy_argc, 0, s->policy_argv) != s->policy_argc))) + goto bad; + + /* TODO: improve this parser */ + if (strstr(p, " ro")) + s->read_only = 1; + + if (strstr(p, " needs_check")) + s->needs_check = 1; +out: + *status = s; + return 1; + +bad: + log_error("Failed to parse cache params: %s", params); + dm_pool_free(mem, s); + *status = NULL; + + return 0; +} + +/* + * From linux/Documentation/device-mapper/writecache.txt + * + * Status: + * 1. error indicator - 0 if there was no error, otherwise error number + * 2. the number of blocks + * 3. the number of free blocks + * 4. the number of blocks under writeback + */ + +int dm_get_status_writecache(struct dm_pool *mem, const char *params, + struct dm_status_writecache **status) +{ + struct dm_status_writecache *s; + + if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_writecache)))) + return_0; + + if (sscanf(params, "%llu %llu %llu %llu", + (unsigned long long *)&s->error, + (unsigned long long *)&s->total_blocks, + (unsigned long long *)&s->free_blocks, + (unsigned long long *)&s->writeback_blocks) != 4) { + log_error("Failed to parse writecache params: %s.", params); + dm_pool_free(mem, s); + return 0; + } + + *status = s; + return 1; +} + +int dm_get_status_integrity(struct dm_pool *mem, const char *params, + struct dm_status_integrity **status) +{ + struct dm_status_integrity *s; + char recalc_str[16] = "\0"; + + if (!(s = dm_pool_zalloc(mem, sizeof(*s)))) + return_0; + + if (sscanf(params, "%llu %llu %s", + (unsigned long long *)&s->number_of_mismatches, + (unsigned long long *)&s->provided_data_sectors, + recalc_str) != 3) { + log_error("Failed to parse integrity params: %s.", params); + dm_pool_free(mem, s); + return 0; + } + + if (recalc_str[0] == '-') + s->recalc_sector = 0; + else + s->recalc_sector = strtoull(recalc_str, NULL, 0); + + *status = s; + return 1; +} + +int parse_thin_pool_status(const char *params, struct dm_status_thin_pool *s) +{ + int pos; + + memset(s, 0, sizeof(*s)); + + if (!params) { + log_error("Failed to parse invalid thin params."); + return 0; + } + + if (strstr(params, "Error")) { + s->error = 1; + s->fail = 1; /* This is also I/O fail state */ + return 1; + } + + if (strstr(params, "Fail")) { + s->fail = 1; + return 1; + } + + /* FIXME: add support for held metadata root */ + if (sscanf(params, FMTu64 " " FMTu64 "/" FMTu64 " " FMTu64 "/" FMTu64 "%n", + &s->transaction_id, + &s->used_metadata_blocks, + &s->total_metadata_blocks, + &s->used_data_blocks, + &s->total_data_blocks, &pos) < 5) { + log_error("Failed to parse thin pool params: %s.", params); + return 0; + } + + /* New status flags */ + if (strstr(params + pos, "no_discard_passdown")) + s->discards = DM_THIN_DISCARDS_NO_PASSDOWN; + else if (strstr(params + pos, "ignore_discard")) + s->discards = DM_THIN_DISCARDS_IGNORE; + else /* default discard_passdown */ + s->discards = DM_THIN_DISCARDS_PASSDOWN; + + /* Default is 'writable' (rw) data */ + if (strstr(params + pos, "out_of_data_space")) + s->out_of_data_space = 1; + else if (strstr(params + pos, "ro ")) + s->read_only = 1; + + /* Default is 'queue_if_no_space' */ + if (strstr(params + pos, "error_if_no_space")) + s->error_if_no_space = 1; + + if (strstr(params + pos, "needs_check")) + s->needs_check = 1; + + return 1; +} + +int dm_get_status_thin_pool(struct dm_pool *mem, const char *params, + struct dm_status_thin_pool **status) +{ + struct dm_status_thin_pool *s; + + if (!(s = dm_pool_alloc(mem, sizeof(struct dm_status_thin_pool)))) { + log_error("Failed to allocate thin_pool status structure."); + return 0; + } + + if (!parse_thin_pool_status(params, s)) { + dm_pool_free(mem, s); + return_0; + } + + *status = s; + + return 1; +} + +int dm_get_status_thin(struct dm_pool *mem, const char *params, + struct dm_status_thin **status) +{ + struct dm_status_thin *s; + + if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_thin)))) { + log_error("Failed to allocate thin status structure."); + return 0; + } + + if (strchr(params, '-')) { + /* nothing to parse */ + } else if (strstr(params, "Fail")) { + s->fail = 1; + } else if (sscanf(params, FMTu64 " " FMTu64, + &s->mapped_sectors, + &s->highest_mapped_sector) != 2) { + dm_pool_free(mem, s); + log_error("Failed to parse thin params: %s.", params); + return 0; + } + + *status = s; + + return 1; +} + +/* + * dm core parms: 0 409600 mirror + * Mirror core parms: 2 253:4 253:5 400/400 + * New-style failure params: 1 AA + * New-style log params: 3 cluster 253:3 A + * or 3 disk 253:3 A + * or 1 core + */ +#define DM_MIRROR_MAX_IMAGES 8 /* limited by kernel DM_KCOPYD_MAX_REGIONS */ + +int dm_get_status_mirror(struct dm_pool *mem, const char *params, + struct dm_status_mirror **status) +{ + struct dm_status_mirror *s; + const char *p, *pos = params; + unsigned num_devs, argc, i; + int used; + + if (!(s = dm_pool_zalloc(mem, sizeof(*s)))) { + log_error("Failed to alloc mem pool to parse mirror status."); + return 0; + } + + if (sscanf(pos, "%u %n", &num_devs, &used) != 1) + goto_out; + pos += used; + + if (num_devs > DM_MIRROR_MAX_IMAGES) { + log_error(INTERNAL_ERROR "More then " DM_TO_STRING(DM_MIRROR_MAX_IMAGES) + " reported in mirror status."); + goto out; + } + + if (!(s->devs = dm_pool_alloc(mem, num_devs * sizeof(*(s->devs))))) { + log_error("Allocation of devs failed."); + goto out; + } + + for (i = 0; i < num_devs; ++i, pos += used) + if (sscanf(pos, "%u:%u %n", + &(s->devs[i].major), &(s->devs[i].minor), &used) != 2) + goto_out; + + if (sscanf(pos, FMTu64 "/" FMTu64 "%n", + &s->insync_regions, &s->total_regions, &used) != 2) + goto_out; + pos += used; + + if (sscanf(pos, "%u %n", &argc, &used) != 1) + goto_out; + pos += used; + + for (i = 0; i < num_devs ; ++i) + s->devs[i].health = pos[i]; + + if (!(pos = _skip_fields(pos, argc))) + goto_out; + + if (strncmp(pos, "userspace", 9) == 0) { + pos += 9; + /* FIXME: support status of userspace mirror implementation */ + } + + if (sscanf(pos, "%u %n", &argc, &used) != 1) + goto_out; + pos += used; + + if (argc == 1) { + /* core, cluster-core */ + if (!(s->log_type = dm_pool_strdup(mem, pos))) { + log_error("Allocation of log type string failed."); + goto out; + } + } else { + if (!(p = _skip_fields(pos, 1))) + goto_out; + + /* disk, cluster-disk */ + if (!(s->log_type = dm_pool_strndup(mem, pos, p - pos - 1))) { + log_error("Allocation of log type string failed."); + goto out; + } + pos = p; + + if ((argc > 2) && !strcmp(s->log_type, "disk")) { + s->log_count = argc - 2; + + if (!(s->logs = dm_pool_alloc(mem, s->log_count * sizeof(*(s->logs))))) { + log_error("Allocation of logs failed."); + goto out; + } + + for (i = 0; i < s->log_count; ++i, pos += used) + if (sscanf(pos, "%u:%u %n", + &s->logs[i].major, &s->logs[i].minor, &used) != 2) + goto_out; + + for (i = 0; i < s->log_count; ++i) + s->logs[i].health = pos[i]; + } + } + + s->dev_count = num_devs; + *status = s; + + return 1; +out: + log_error("Failed to parse mirror status %s.", params); + dm_pool_free(mem, s); + *status = NULL; + + return 0; +} |