/* * Copyright (C) 2013-2016 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * * 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 "base/memory/zalloc.h" #include "lib/misc/lib.h" #include "lib/commands/toolcontext.h" #include "lib/metadata/segtype.h" #include "lib/display/display.h" #include "lib/format_text/text_export.h" #include "lib/config/config.h" #include "lib/datastruct/str_list.h" #include "lib/misc/lvm-string.h" #include "lib/activate/activate.h" #include "lib/metadata/metadata.h" #include "lib/metadata/lv_alloc.h" #include "lib/config/defaults.h" static const char _cache_module[] = "cache"; #define CACHE_POLICY_WHEN_MISSING "mq" #define CACHE_MODE_WHEN_MISSING CACHE_MODE_WRITETHROUGH /* TODO: using static field here, maybe should be a part of segment_type */ static unsigned _feature_mask; #define SEG_LOG_ERROR(t, p...) \ log_error(t " segment %s of logical volume %s.", ## p, \ dm_config_parent_name(sn), seg->lv->name), 0; static int _cache_out_line(const char *line, void *_f) { log_print(" Setting\t\t%s", line); return 1; } static void _cache_display(const struct lv_segment *seg) { const struct dm_config_node *n; const struct lv_segment *setting_seg = NULL; if (seg_is_cache(seg) && lv_is_cache_vol(seg->pool_lv)) setting_seg = seg; else if (seg_is_cache_pool(seg)) setting_seg = seg; else if (seg_is_cache(seg)) setting_seg = first_seg(seg->pool_lv); else return; log_print(" Chunk size\t\t%s", display_size(seg->lv->vg->cmd, setting_seg->chunk_size)); if (setting_seg->cache_metadata_format != CACHE_METADATA_FORMAT_UNSELECTED) log_print(" Metadata format\t%u", setting_seg->cache_metadata_format); if (setting_seg->cache_mode != CACHE_MODE_UNSELECTED) log_print(" Mode\t\t%s", get_cache_mode_name(setting_seg)); if (setting_seg->policy_name) log_print(" Policy\t\t%s", setting_seg->policy_name); if (setting_seg->policy_settings && (n = setting_seg->policy_settings->child)) dm_config_write_node(n, _cache_out_line, NULL); log_print(" "); } /* * When older metadata are loaded without newer settings, * set then to default settings (the one that could have been * used implicitely at that time). * * Needs both segments cache and cache_pool to be loaded. */ static void _fix_missing_defaults(struct lv_segment *cpool_seg) { if (!cpool_seg->policy_name) { cpool_seg->policy_name = CACHE_POLICY_WHEN_MISSING; log_verbose("Cache pool %s is missing cache policy, using %s.", display_lvname(cpool_seg->lv), cpool_seg->policy_name); } if (cpool_seg->cache_metadata_format == CACHE_METADATA_FORMAT_UNSELECTED) { cpool_seg->cache_metadata_format = CACHE_METADATA_FORMAT_1; log_verbose("Cache pool %s uses implicit metadata format %u.", display_lvname(cpool_seg->lv), cpool_seg->cache_metadata_format); } if (cpool_seg->cache_mode == CACHE_MODE_UNSELECTED) { cpool_seg->cache_mode = CACHE_MODE_WHEN_MISSING; log_verbose("Cache pool %s is missing cache mode, using %s.", display_lvname(cpool_seg->lv), get_cache_mode_name(cpool_seg)); } } static int _settings_text_import(struct lv_segment *seg, const struct dm_config_node *sn) { const char *str = NULL; struct dm_pool *mem = seg->lv->vg->vgmem; if (dm_config_has_node(sn, "chunk_size")) { if (!dm_config_get_uint32(sn, "chunk_size", &seg->chunk_size)) return SEG_LOG_ERROR("Couldn't read cache chunk_size in"); } /* * Read in features: * cache_mode = {passthrough|writethrough|writeback} * * 'cache_mode' does not have to be present. */ if (dm_config_has_node(sn, "cache_mode")) { if (!(str = dm_config_find_str(sn, "cache_mode", NULL))) return SEG_LOG_ERROR("cache_mode must be a string in"); if (!set_cache_mode(&seg->cache_mode, str)) return SEG_LOG_ERROR("Unknown cache_mode in"); } if (dm_config_has_node(sn, "policy")) { if (!(str = dm_config_find_str(sn, "policy", NULL))) return SEG_LOG_ERROR("policy must be a string in"); if (!(seg->policy_name = dm_pool_strdup(mem, str))) return SEG_LOG_ERROR("Failed to duplicate policy in"); } /* * Read in policy args: * policy_settings { * migration_threshold=2048 * sequention_threashold=100 * random_threashold=200 * read_promote_adjustment=10 * write_promote_adjustment=20 * discard_promote_adjustment=40 * * = * = * ... * } * * If the policy is not present, default policy is used. */ if ((sn = dm_config_find_node(sn, "policy_settings"))) { if (!seg->policy_name) return SEG_LOG_ERROR("policy_settings must have a policy_name in"); if (sn->v) return SEG_LOG_ERROR("policy_settings must be a section in"); if (!(seg->policy_settings = dm_config_clone_node_with_mem(mem, sn, 0))) return_0; } return 1; } static int _settings_text_export(const struct lv_segment *seg, struct formatter *f) { if (seg->chunk_size) outf(f, "chunk_size = %" PRIu32, seg->chunk_size); if (seg->cache_mode != CACHE_MODE_UNSELECTED) { const char *cache_mode; if (!(cache_mode = cache_mode_num_to_str(seg->cache_mode))) return_0; outf(f, "cache_mode = \"%s\"", cache_mode); } if (seg->policy_name) { outf(f, "policy = \"%s\"", seg->policy_name); if (seg->policy_settings) { if (strcmp(seg->policy_settings->key, "policy_settings")) { log_error(INTERNAL_ERROR "Incorrect policy_settings tree, %s.", seg->policy_settings->key); return 0; } if (seg->policy_settings->child) out_config_node(f, seg->policy_settings); } } return 1; } static int _cache_pool_text_import(struct lv_segment *seg, const struct dm_config_node *sn, struct dm_hash_table *pv_hash __attribute__((unused))) { struct logical_volume *data_lv, *meta_lv; const char *str = NULL; if (!dm_config_has_node(sn, "data")) return SEG_LOG_ERROR("Cache data not specified in"); if (!(str = dm_config_find_str(sn, "data", NULL))) return SEG_LOG_ERROR("Cache data must be a string in"); if (!(data_lv = find_lv(seg->lv->vg, str))) return SEG_LOG_ERROR("Unknown logical volume %s specified for " "cache data in", str); if (!dm_config_has_node(sn, "metadata")) return SEG_LOG_ERROR("Cache metadata not specified in"); if (!(str = dm_config_find_str(sn, "metadata", NULL))) return SEG_LOG_ERROR("Cache metadata must be a string in"); if (!(meta_lv = find_lv(seg->lv->vg, str))) return SEG_LOG_ERROR("Unknown logical volume %s specified for " "cache metadata in", str); if (dm_config_has_node(sn, "metadata_format")) { if (!dm_config_get_uint32(sn, "metadata_format", &seg->cache_metadata_format) || ((seg->cache_metadata_format != CACHE_METADATA_FORMAT_1) && (seg->cache_metadata_format != CACHE_METADATA_FORMAT_2))) return SEG_LOG_ERROR("Unknown cache metadata format %u number in", seg->cache_metadata_format); if (seg->cache_metadata_format == CACHE_METADATA_FORMAT_2) seg->lv->status |= LV_METADATA_FORMAT; } if (!_settings_text_import(seg, sn)) return_0; if (!attach_pool_data_lv(seg, data_lv)) return_0; if (!attach_pool_metadata_lv(seg, meta_lv)) return_0; /* when cache pool is used, we require policy and mode to be defined */ if (!dm_list_empty(&seg->lv->segs_using_this_lv)) _fix_missing_defaults(seg); return 1; } static int _cache_pool_text_import_area_count(const struct dm_config_node *sn, uint32_t *area_count) { *area_count = 1; return 1; } static int _cache_pool_text_export(const struct lv_segment *seg, struct formatter *f) { outf(f, "data = \"%s\"", seg_lv(seg, 0)->name); outf(f, "metadata = \"%s\"", seg->metadata_lv->name); switch (seg->cache_metadata_format) { case CACHE_METADATA_FORMAT_UNSELECTED: /* Unselected format is not printed */ break; case CACHE_METADATA_FORMAT_1: /* If format 1 was already specified with cache pool, store it, * otherwise format gets stored when LV is cached. * NB: format 1 could be lost anytime, it's a default format. * Older lvm2 tool can easily drop it. */ case CACHE_METADATA_FORMAT_2: /* more in future ? */ outf(f, "metadata_format = " FMTu32, seg->cache_metadata_format); break; default: log_error(INTERNAL_ERROR "LV %s is using unknown cache metadada format %u.", display_lvname(seg->lv), seg->cache_metadata_format); return 0; } /* * Cache pool used by a cache LV holds data. Not ideal, * but not worth to break backward compatibility, by shifting * content to cache segment */ if (!_settings_text_export(seg, f)) return_0; return 1; } static void _destroy(struct segment_type *segtype) { free((void *) segtype); } #ifdef DEVMAPPER_SUPPORT static int _target_present(struct cmd_context *cmd, const struct lv_segment *seg __attribute__((unused)), unsigned *attributes) { /* List of features with their kernel target version */ static const struct feature { uint16_t maj; uint16_t min; uint16_t cache_feature; uint16_t cache_alias; const char feature[12]; const char module[12]; /* check dm-%s */ const char aliasing[24]; } _features[] = { { 1, 10, CACHE_FEATURE_METADATA2, 0, "metadata2" }, /* Assumption: cache >=1.9 always aliases MQ policy */ { 1, 9, CACHE_FEATURE_POLICY_SMQ, CACHE_FEATURE_POLICY_MQ, "policy_smq", "cache-smq", " and aliases cache-mq" }, { 1, 8, CACHE_FEATURE_POLICY_SMQ, 0, "policy_smq", "cache-smq" }, { 1, 3, CACHE_FEATURE_POLICY_MQ, 0, "policy_mq", "cache-mq" }, }; static const char _lvmconf[] = "global/cache_disabled_features"; static unsigned _attrs = 0; static int _cache_checked = 0; static int _cache_present = 0; uint32_t maj, min, patchlevel; unsigned i; const struct dm_config_node *cn; const struct dm_config_value *cv; const char *str; if (!activation()) return 0; if (!_cache_checked) { _cache_checked = 1; if (!(_cache_present = target_present_version(cmd, TARGET_NAME_CACHE, 1, &maj, &min, &patchlevel))) return_0; if ((maj < 1) || ((maj == 1) && (min < 3))) { _cache_present = 0; log_warn("WARNING: The cache kernel module is version %u.%u.%u. " "Version 1.3.0+ is required.", maj, min, patchlevel); return 0; } for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) { if (_attrs & _features[i].cache_feature) continue; /* already present */ if (!_features[i].module[0]) { if ((maj > _features[i].maj) || (maj == _features[i].maj && min >= _features[i].min)) { log_debug_activation("Cache supports %s.", _features[i].feature); _attrs |= _features[i].cache_feature; } continue; } if (((maj > _features[i].maj) || (maj == _features[i].maj && min >= _features[i].min)) && module_present(cmd, _features[i].module)) { log_debug_activation("Cache policy %s is available%s.", _features[i].module, _features[i].aliasing); _attrs |= (_features[i].cache_feature | _features[i].cache_alias); } else if (!_features[i].cache_alias) log_very_verbose("Target %s does not support %s.", _cache_module, _features[i].feature); } } if (attributes) { if (!_feature_mask) { /* Support runtime lvm.conf changes, N.B. avoid 32 feature */ if ((cn = find_config_tree_array(cmd, global_cache_disabled_features_CFG, NULL))) { for (cv = cn->v; cv; cv = cv->next) { if (cv->type != DM_CFG_STRING) { log_error("Ignoring invalid string in config file %s.", _lvmconf); continue; } str = cv->v.str; if (!*str) continue; for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) if (strcasecmp(str, _features[i].feature) == 0) _feature_mask |= _features[i].cache_feature; } } _feature_mask = ~_feature_mask; for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) if ((_attrs & _features[i].cache_feature) && !(_feature_mask & _features[i].cache_feature)) log_very_verbose("Target %s %s support disabled by %s", _cache_module, _features[i].feature, _lvmconf); } *attributes = _attrs & _feature_mask; } return _cache_present; } static int _modules_needed(struct dm_pool *mem, const struct lv_segment *seg __attribute__((unused)), struct dm_list *modules) { if (!str_list_add(mem, modules, MODULE_NAME_CACHE)) { log_error("String list allocation failed for cache module."); return 0; } return 1; } #endif /* DEVMAPPER_SUPPORT */ static const struct segtype_handler _cache_pool_ops = { .display = _cache_display, .text_import = _cache_pool_text_import, .text_import_area_count = _cache_pool_text_import_area_count, .text_export = _cache_pool_text_export, #ifdef DEVMAPPER_SUPPORT .target_present = _target_present, .modules_needed = _modules_needed, # ifdef DMEVENTD # endif /* DMEVENTD */ #endif .destroy = _destroy, }; static int _cache_text_import(struct lv_segment *seg, const struct dm_config_node *sn, struct dm_hash_table *pv_hash __attribute__((unused))) { struct logical_volume *pool_lv, *origin_lv; const char *name; const char *uuid; if (!dm_config_has_node(sn, "cache_pool")) return SEG_LOG_ERROR("cache_pool not specified in"); if (!(name = dm_config_find_str(sn, "cache_pool", NULL))) return SEG_LOG_ERROR("cache_pool must be a string in"); if (!(pool_lv = find_lv(seg->lv->vg, name))) return SEG_LOG_ERROR("Unknown logical volume %s specified for " "cache_pool in", name); if (!dm_config_has_node(sn, "origin")) return SEG_LOG_ERROR("Cache origin not specified in"); if (!(name = dm_config_find_str(sn, "origin", NULL))) return SEG_LOG_ERROR("Cache origin must be a string in"); if (!(origin_lv = find_lv(seg->lv->vg, name))) return SEG_LOG_ERROR("Unknown logical volume %s specified for " "cache origin in", name); if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0)) return_0; seg->cleaner_policy = 0; if (dm_config_has_node(sn, "cleaner") && !dm_config_get_uint32(sn, "cleaner", &seg->cleaner_policy)) return SEG_LOG_ERROR("Could not read cache cleaner in"); seg->lv->status |= strstr(seg->lv->name, "_corig") ? LV_PENDING_DELETE : 0; if (!_settings_text_import(seg, sn)) return_0; if (dm_config_has_node(sn, "metadata_format")) { if (!dm_config_get_uint32(sn, "metadata_format", &seg->cache_metadata_format)) return SEG_LOG_ERROR("Couldn't read cache metadata_format in"); if (seg->cache_metadata_format != CACHE_METADATA_FORMAT_2) return SEG_LOG_ERROR("Unknown cache metadata format %u number in", seg->cache_metadata_format); } if (dm_config_has_node(sn, "metadata_start")) { if (!dm_config_get_uint64(sn, "metadata_start", &seg->metadata_start)) return SEG_LOG_ERROR("Couldn't read metadata_start in"); if (!dm_config_get_uint64(sn, "metadata_len", &seg->metadata_len)) return SEG_LOG_ERROR("Couldn't read metadata_len in"); if (!dm_config_get_uint64(sn, "data_start", &seg->data_start)) return SEG_LOG_ERROR("Couldn't read data_start in"); if (!dm_config_get_uint64(sn, "data_len", &seg->data_len)) return SEG_LOG_ERROR("Couldn't read data_len in"); /* Will use CVOL ID, when metadata_id is not provided */ if (dm_config_has_node(sn, "metadata_id")) { if (!(seg->metadata_id = dm_pool_alloc(seg->lv->vg->vgmem, sizeof(*seg->metadata_id)))) return SEG_LOG_ERROR("Couldn't allocate metadata_id in"); if (!dm_config_get_str(sn, "metadata_id", &uuid)) return SEG_LOG_ERROR("Couldn't read metadata_id in"); if (!id_read_format(seg->metadata_id, uuid)) return SEG_LOG_ERROR("Couldn't format metadata_id in"); } /* Will use CVOL ID, when data_id is not provided */ if (dm_config_has_node(sn, "data_id")) { if (!(seg->data_id = dm_pool_alloc(seg->lv->vg->vgmem, sizeof(*seg->data_id)))) return SEG_LOG_ERROR("Couldn't allocate data_id in"); if (!dm_config_get_str(sn, "data_id", &uuid)) return SEG_LOG_ERROR("Couldn't read data_id in"); if (!id_read_format(seg->data_id, uuid)) return SEG_LOG_ERROR("Couldn't format data_id in"); } pool_lv->status |= LV_CACHE_VOL; /* Mark as cachevol LV */ } else { /* Do not call this when LV is cache_vol. */ /* load order is unknown, could be cache origin or pool LV, so check for both */ if (!dm_list_empty(&pool_lv->segments)) _fix_missing_defaults(first_seg(pool_lv)); } if (!attach_pool_lv(seg, pool_lv, NULL, NULL, NULL)) return_0; return 1; } static int _cache_text_import_area_count(const struct dm_config_node *sn, uint32_t *area_count) { *area_count = 1; return 1; } static int _cache_text_export(const struct lv_segment *seg, struct formatter *f) { char buffer[40]; if (!seg_lv(seg, 0)) return_0; outf(f, "cache_pool = \"%s\"", seg->pool_lv->name); outf(f, "origin = \"%s\"", seg_lv(seg, 0)->name); if (seg->cleaner_policy) outf(f, "cleaner = 1"); if (lv_is_cache_vol(seg->pool_lv)) { outf(f, "metadata_format = " FMTu32, seg->cache_metadata_format); if (!_settings_text_export(seg, f)) return_0; outf(f, "metadata_start = " FMTu64, seg->metadata_start); outf(f, "metadata_len = " FMTu64, seg->metadata_len); outf(f, "data_start = " FMTu64, seg->data_start); outf(f, "data_len = " FMTu64, seg->data_len); if (seg->metadata_id) { if (!id_write_format(seg->metadata_id, buffer, sizeof(buffer))) return_0; outf(f, "metadata_id = \"%s\"", buffer); } if (seg->data_id) { if (!id_write_format(seg->data_id, buffer, sizeof(buffer))) return_0; outf(f, "data_id = \"%s\"", buffer); } } return 1; } #ifdef DEVMAPPER_SUPPORT static int _cache_add_target_line(struct dev_manager *dm, struct dm_pool *mem, struct cmd_context *cmd __attribute__((unused)), void **target_state __attribute__((unused)), struct lv_segment *seg, const struct lv_activate_opts *laopts __attribute__((unused)), struct dm_tree_node *node, uint64_t len, uint32_t *pvmove_mirror_count __attribute__((unused))) { struct lv_segment *cache_pool_seg; struct lv_segment *setting_seg; struct dm_config_node *policy_settings; struct dm_config_node *cn; unsigned i, j; union lvid metadata_lvid; union lvid data_lvid; char *metadata_uuid, *data_uuid, *origin_uuid; uint64_t feature_flags = 0; unsigned attr; if (!seg->pool_lv || !seg_is_cache(seg)) { log_error(INTERNAL_ERROR "Passed segment is not cache."); return 0; } log_debug("cache_add_target_line lv %s pool %s", seg->lv->name, seg->pool_lv->name); cache_pool_seg = first_seg(seg->pool_lv); if (lv_is_cache_vol(seg->pool_lv)) setting_seg = seg; else setting_seg = cache_pool_seg; if (seg->cleaner_policy) /* With cleaner policy always pass writethrough */ feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; else switch (setting_seg->cache_mode) { default: log_error(INTERNAL_ERROR "LV %s has unknown cache mode %d.", display_lvname(seg->lv), setting_seg->cache_mode); /* Fall through */ case CACHE_MODE_WRITETHROUGH: feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; break; case CACHE_MODE_WRITEBACK: feature_flags |= DM_CACHE_FEATURE_WRITEBACK; break; case CACHE_MODE_PASSTHROUGH: feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH; break; } switch (setting_seg->cache_metadata_format) { case CACHE_METADATA_FORMAT_1: break; case CACHE_METADATA_FORMAT_2: if (!_target_present(cmd, NULL, &attr)) return_0; if (!(attr & CACHE_FEATURE_METADATA2)) { log_error("LV %s has metadata format %u unsuported by kernel.", display_lvname(seg->lv), setting_seg->cache_metadata_format); return 0; } feature_flags |= DM_CACHE_FEATURE_METADATA2; log_debug_activation("Using metadata2 format for %s.", display_lvname(seg->lv)); break; default: log_error(INTERNAL_ERROR "LV %s has unknown metadata format %u.", display_lvname(seg->lv), setting_seg->cache_metadata_format); return 0; } if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0), NULL))) return_0; if (!lv_is_cache_vol(seg->pool_lv)) { /* We don't use start/len when using separate data/meta devices. */ if (seg->metadata_len || seg->data_len) { log_error(INTERNAL_ERROR "LV %s using unsupported ranges with cache pool.", display_lvname(seg->lv)); return 0; } if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv, NULL))) return_0; if (!(data_uuid = build_dm_uuid(mem, seg_lv(cache_pool_seg, 0), NULL))) return_0; } else { if (!seg->metadata_len || !seg->data_len || (seg->metadata_start == seg->data_start)) { log_error(INTERNAL_ERROR "LV %s has invalid ranges metadata %llu %llu data %llu %llu.", display_lvname(seg->lv), (unsigned long long)seg->metadata_start, (unsigned long long)seg->metadata_len, (unsigned long long)seg->data_start, (unsigned long long)seg->data_len); return 0; } memset(&metadata_lvid, 0, sizeof(metadata_lvid)); memset(&data_lvid, 0, sizeof(data_lvid)); memcpy(&metadata_lvid.id[0], &seg->lv->vg->id, sizeof(struct id)); memcpy(&metadata_lvid.id[1], (seg->metadata_id) ? : &seg->pool_lv->lvid.id[1], sizeof(struct id)); memcpy(&data_lvid.id[0], &seg->lv->vg->id, sizeof(struct id)); memcpy(&data_lvid.id[1], (seg->data_id) ? : &seg->pool_lv->lvid.id[1], sizeof(struct id)); if (!(metadata_uuid = dm_build_dm_uuid(mem, UUID_PREFIX, (const char *)&metadata_lvid.s, "cmeta"))) return_0; if (!(data_uuid = dm_build_dm_uuid(mem, UUID_PREFIX, (const char *)&data_lvid.s, "cdata"))) return_0; } policy_settings = seg->cleaner_policy ? NULL : setting_seg->policy_settings; if (policy_settings && cache_pool_seg->policy_name) { static const struct act { const char *name; const char *settings[20]; } _accepted[] = { { "MQ", { "migration_threshold", "sequential_threshold", "random_threshold", "read_promote_adjustment", "write_promote_adjustment", "discard_promote_adjustment", NULL }, }, { "SMQ", { "migration_threshold", NULL } } }; /* Check if cache settings are acceptable to knownm policies */ for (i = 0; i < DM_ARRAY_SIZE(_accepted); i++) { if (strcasecmp(cache_pool_seg->policy_name, _accepted[i].name)) continue; for (cn = policy_settings->child; cn; cn = cn->sib) { for (j = 0; _accepted[i].settings[j]; j++) if (strcmp(cn->key, _accepted[i].settings[j]) == 0) break; /* -> Valid setting */ /* Have we found 'unsupported' cache setting? */ if (!_accepted[i].settings[j]) { /* Make a copy of policy settings a remove unsupported settings and Warn */ if (!(policy_settings = dm_config_clone_node_with_mem(mem, policy_settings, 0))) return_0; restart: for (cn = policy_settings->child; cn; cn = cn->sib) { for (j = 0; _accepted[i].settings[j]; j++) { if (strcmp(cn->key, _accepted[i].settings[j]) == 0) break; /* need to be dropped */ } if (!_accepted[i].settings[j]) { log_warn("WARNING: %s cache policy does not support \"%s=" FMTu64 "\" setting, " "remove with 'lvchange --cachesettings \"%s=default\" ...'.", _accepted[i].name, cn->key, cn->v->v.i, cn->key); dm_config_remove_node(policy_settings, cn); goto restart; } } break; } } break; } } if (!dm_tree_node_add_cache_target(node, len, feature_flags, metadata_uuid, data_uuid, origin_uuid, seg->cleaner_policy ? "cleaner" : /* undefined policy name -> likely an old "mq" */ cache_pool_seg->policy_name ? : "mq", policy_settings, seg->metadata_start, seg->metadata_len, seg->data_start, seg->data_len, setting_seg->chunk_size)) return_0; return 1; } #endif /* DEVMAPPER_SUPPORT */ static const struct segtype_handler _cache_ops = { .display = _cache_display, .text_import = _cache_text_import, .text_import_area_count = _cache_text_import_area_count, .text_export = _cache_text_export, #ifdef DEVMAPPER_SUPPORT .add_target_line = _cache_add_target_line, .target_present = _target_present, .modules_needed = _modules_needed, # ifdef DMEVENTD # endif /* DMEVENTD */ #endif .destroy = _destroy, }; #ifdef CACHE_INTERNAL /* Shared */ int init_cache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib) #else int init_cache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib); int init_cache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib) #endif { struct segment_type *segtype = zalloc(sizeof(*segtype)); if (!segtype) { log_error("Failed to allocate memory for cache_pool segtype"); return 0; } segtype->name = SEG_TYPE_NAME_CACHE_POOL; segtype->flags = SEG_CACHE_POOL | SEG_CANNOT_BE_ZEROED | SEG_ONLY_EXCLUSIVE; segtype->ops = &_cache_pool_ops; if (!lvm_register_segtype(seglib, segtype)) return_0; log_very_verbose("Initialised segtype: %s", segtype->name); segtype = zalloc(sizeof(*segtype)); if (!segtype) { log_error("Failed to allocate memory for cache segtype"); return 0; } segtype->name = SEG_TYPE_NAME_CACHE; segtype->flags = SEG_CACHE | SEG_ONLY_EXCLUSIVE; segtype->ops = &_cache_ops; if (!lvm_register_segtype(seglib, segtype)) return_0; log_very_verbose("Initialised segtype: %s", segtype->name); /* Reset mask for recalc */ _feature_mask = 0; return 1; }