uint32_t commit_time;
uint32_t bitmap_flush_interval;
uint64_t sectors_per_bit;
+ uint32_t allow_discards;
unsigned journal_sectors_set:1;
unsigned interleave_sectors_set:1;
unsigned commit_time_set:1;
unsigned bitmap_flush_interval_set:1;
unsigned sectors_per_bit_set:1;
+ unsigned allow_discards_set:1;
};
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
count++;
if (set->sectors_per_bit_set)
count++;
+ if (set->allow_discards_set && set->allow_discards)
+ count++;
EMIT_PARAMS(pos, "%s 0 %u %s %d fix_padding block_size:%u internal_hash:%s",
origin_dev,
if (set->sectors_per_bit_set)
EMIT_PARAMS(pos, " sectors_per_bit:%llu", (unsigned long long)set->sectors_per_bit);
+ if (set->allow_discards_set && set->allow_discards)
+ EMIT_PARAMS(pos, " allow_discards");
+
if (!dm_task_secure_data(dmt))
stack;
set->sectors_per_bit_set = 1;
}
+ if (dm_config_has_node(sn, "allow_discards")) {
+ if (!dm_config_get_uint32(sn, "allow_discards", &set->allow_discards))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->allow_discards_set = 1;
+ }
+
seg->origin = origin_lv;
seg->integrity_meta_dev = meta_lv;
seg->lv->status |= INTEGRITY;
if (set->sectors_per_bit)
outf(f, "sectors_per_bit = %llu", (unsigned long long)set->sectors_per_bit);
+ if (set->allow_discards_set)
+ outf(f, "allow_discards = %u", set->allow_discards);
+
return 1;
}
* plus some initial space for journals.
* (again from trial and error testing.)
*/
-static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes)
+static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes, uint32_t journal_sectors,
+ uint32_t extent_size)
{
uint64_t meta_bytes;
uint64_t initial_bytes;
/* Every 500M of data needs 4M of metadata. */
meta_bytes = ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);
+ if (journal_sectors) {
+ /* for calculating the metadata LV size for the specified
+ journal size, round the specified journal size up to the
+ nearest extent. extent_size is in sectors. */
+ initial_bytes = dm_round_up(journal_sectors, extent_size) * 512;
+ goto out;
+ }
+
/*
- * initial space used for journals
+ * initial space used for journals (when journal size is not specified):
* lv_size <= 512M -> 4M
* lv_size <= 1G -> 8M
* lv_size <= 4G -> 32M
initial_bytes = 32 * ONE_MB_IN_BYTES;
else if (lv_size_bytes > (4ULL * ONE_GB_IN_BYTES))
initial_bytes = 64 * ONE_MB_IN_BYTES;
-
+ out:
+ log_debug("integrity_meta_bytes %llu from lv_size_bytes %llu meta_bytes %llu initial_bytes %llu journal_sectors %u",
+ (unsigned long long)(meta_bytes+initial_bytes), (unsigned long long)lv_size_bytes,
+ (unsigned long long)meta_bytes, (unsigned long long)initial_bytes, journal_sectors);
return meta_bytes + initial_bytes;
}
static int _lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg,
struct lvcreate_params *lp,
+ struct integrity_settings *settings,
struct logical_volume **meta_lv)
{
char metaname[NAME_LEN] = { 0 };
lp_meta.pvh = lp->pvh;
lv_size_bytes = (uint64_t)lp->extents * (uint64_t)vg->extent_size * 512;
- meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
+ meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes, settings->journal_sectors, vg->extent_size);
meta_sectors = meta_bytes / 512;
lp_meta.extents = meta_sectors / vg->extent_size;
}
lv_size_bytes = lv_iorig->size * 512;
- meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
+ meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes, 0, 0);
meta_sectors = meta_bytes / 512;
meta_extents = meta_sectors / vg->extent_size;
lp.pvh = use_pvh;
lp.extents = lv_image->size / vg->extent_size;
- if (!_lv_create_integrity_metadata(cmd, vg, &lp, &meta_lv))
+ if (!_lv_create_integrity_metadata(cmd, vg, &lp, settings, &meta_lv))
goto_bad;
revert_meta_lvs++;
return 0;
}
+int integrity_settings_to_str_list(struct integrity_settings *settings, struct dm_list *result, struct dm_pool *mem)
+{
+ int errors = 0;
+
+ if (settings->journal_watermark_set)
+ if (!setting_str_list_add("journal_watermark", settings->journal_watermark, NULL, result, mem))
+ errors++;
+
+ if (settings->commit_time_set)
+ if (!setting_str_list_add("commit_time", settings->commit_time, NULL, result, mem))
+ errors++;
+
+ if (settings->bitmap_flush_interval_set)
+ if (!setting_str_list_add("bitmap_flush_interval", settings->bitmap_flush_interval, NULL, result, mem))
+ errors++;
+
+ if (settings->allow_discards_set)
+ if (!setting_str_list_add("allow_discards", settings->allow_discards, NULL, result, mem))
+ errors++;
+ if (errors)
+ log_warn("Failed to create list of integrity settings.");
+
+ return 1;
+}
+
+
bool writecache_cleaner_supported(struct cmd_context *cmd);
int lv_is_integrity_origin(const struct logical_volume *lv);
+int integrity_settings_to_str_list(struct integrity_settings *settings, struct dm_list *result, struct dm_pool *mem);
int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
int lv_integrity_mismatches(struct cmd_context *cmd, const struct logical_volume *lv, uint64_t *mismatches);
int lv_raid_integrity_total_mismatches(struct cmd_context *cmd, const struct logical_volume *lv, uint64_t *mismatches);
+int setting_str_list_add(const char *field, uint64_t val, char *val_str, struct dm_list *result, struct dm_pool *mem);
+
#endif
return segtype_is_striped(seg->segtype);
}
+int setting_str_list_add(const char *field, uint64_t val, char *val_str, struct dm_list *result, struct dm_pool *mem)
+{
+ char buf[128];
+ char *list_item;
+
+ if (val_str) {
+ if (dm_snprintf(buf, sizeof(buf), "%s=%s", field, val_str) < 0)
+ return_0;
+ } else {
+ if (dm_snprintf(buf, sizeof(buf), "%s=%llu", field, (unsigned long long)val) < 0)
+ return_0;
+ }
+
+ if (!(list_item = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!str_list_add_no_dup_check(mem, result, list_item))
+ return_0;
+
+ return 1;
+}
return 1;
}
-static int _writecache_setting_str_list_add(const char *field, uint64_t val, char *val_str, struct dm_list *result, struct dm_pool *mem)
-{
- char buf[128];
- char *list_item;
-
- if (val_str) {
- if (dm_snprintf(buf, sizeof(buf), "%s=%s", field, val_str) < 0)
- return_0;
- } else {
- if (dm_snprintf(buf, sizeof(buf), "%s=%llu", field, (unsigned long long)val) < 0)
- return_0;
- }
-
- if (!(list_item = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!str_list_add_no_dup_check(mem, result, list_item))
- return_0;
-
- return 1;
-}
-
int writecache_settings_to_str_list(struct writecache_settings *settings, struct dm_list *result, struct dm_pool *mem)
{
int errors = 0;
if (settings->high_watermark_set)
- if (!_writecache_setting_str_list_add("high_watermark", settings->high_watermark, NULL, result, mem))
+ if (!setting_str_list_add("high_watermark", settings->high_watermark, NULL, result, mem))
errors++;
if (settings->low_watermark_set)
- if (!_writecache_setting_str_list_add("low_watermark", settings->low_watermark, NULL, result, mem))
+ if (!setting_str_list_add("low_watermark", settings->low_watermark, NULL, result, mem))
errors++;
if (settings->writeback_jobs_set)
- if (!_writecache_setting_str_list_add("writeback_jobs", settings->writeback_jobs, NULL, result, mem))
+ if (!setting_str_list_add("writeback_jobs", settings->writeback_jobs, NULL, result, mem))
errors++;
if (settings->autocommit_blocks_set)
- if (!_writecache_setting_str_list_add("autocommit_blocks", settings->autocommit_blocks, NULL, result, mem))
+ if (!setting_str_list_add("autocommit_blocks", settings->autocommit_blocks, NULL, result, mem))
errors++;
if (settings->autocommit_time_set)
- if (!_writecache_setting_str_list_add("autocommit_time", settings->autocommit_time, NULL, result, mem))
+ if (!setting_str_list_add("autocommit_time", settings->autocommit_time, NULL, result, mem))
errors++;
if (settings->fua_set)
- if (!_writecache_setting_str_list_add("fua", (uint64_t)settings->fua, NULL, result, mem))
+ if (!setting_str_list_add("fua", (uint64_t)settings->fua, NULL, result, mem))
errors++;
if (settings->nofua_set)
- if (!_writecache_setting_str_list_add("nofua", (uint64_t)settings->nofua, NULL, result, mem))
+ if (!setting_str_list_add("nofua", (uint64_t)settings->nofua, NULL, result, mem))
errors++;
if (settings->cleaner_set && settings->cleaner)
- if (!_writecache_setting_str_list_add("cleaner", (uint64_t)settings->cleaner, NULL, result, mem))
+ if (!setting_str_list_add("cleaner", (uint64_t)settings->cleaner, NULL, result, mem))
errors++;
if (settings->max_age_set)
- if (!_writecache_setting_str_list_add("max_age", (uint64_t)settings->max_age, NULL, result, mem))
+ if (!setting_str_list_add("max_age", (uint64_t)settings->max_age, NULL, result, mem))
errors++;
if (settings->metadata_only_set)
- if (!_writecache_setting_str_list_add("metadata_only", (uint64_t)settings->metadata_only, NULL, result, mem))
+ if (!setting_str_list_add("metadata_only", (uint64_t)settings->metadata_only, NULL, result, mem))
errors++;
if (settings->pause_writeback_set)
- if (!_writecache_setting_str_list_add("pause_writeback", (uint64_t)settings->pause_writeback, NULL, result, mem))
+ if (!setting_str_list_add("pause_writeback", (uint64_t)settings->pause_writeback, NULL, result, mem))
errors++;
if (settings->new_key && settings->new_val)
- if (!_writecache_setting_str_list_add(settings->new_key, 0, settings->new_val, result, mem))
+ if (!setting_str_list_add(settings->new_key, 0, settings->new_val, result, mem))
errors++;
if (errors)
FIELD(SEGS, seg, STR, "Monitor", list, 0, segmonitor, seg_monitor, "Dmeventd monitoring status of the segment.", 0)
FIELD(SEGS, seg, STR, "CachePolicy", list, 0, cache_policy, cache_policy, "The cache policy (cached segments only).", 0)
FIELD(SEGS, seg, STR_LIST, "CacheSettings", list, 0, cache_settings, cache_settings, "Cache settings/parameters (cached segments only).", 0)
+FIELD(SEGS, seg, STR_LIST, "IntegSettings", list, 0, integrity_settings, integrity_settings, "Integrity settings.", 0)
FIELD(SEGS, seg, BIN, "VDOCompression", list, 0, vdo_compression, vdo_compression, "Set for compressed LV (vdopool).", 0)
FIELD(SEGS, seg, BIN, "VDODeduplication", list, 0, vdo_deduplication, vdo_deduplication, "Set for deduplicated LV (vdopool).", 0)
#define _kernel_cache_policy_set prop_not_implemented_set
#define _kernel_metadata_format_get prop_not_implemented_get
#define _kernel_metadata_format_set prop_not_implemented_set
+#define _integrity_settings_get prop_not_implemented_get
+#define _integrity_settings_set prop_not_implemented_set
/* PVSEG */
GET_PVSEG_NUM_PROPERTY_FN(pvseg_start, pvseg->pe)
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
+static int _integrity_settings_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+ struct dm_list *result;
+ struct dm_list dummy_list; /* dummy list to display "nothing" */
+
+ if (seg_is_integrity(seg)) {
+ if (!(result = str_list_create(mem)))
+ return_0;
+
+ if (!integrity_settings_to_str_list((struct integrity_settings *)&seg->integrity_settings, result, mem))
+ return_0;
+
+ return _field_set_string_list(rh, field, result, private, 0, NULL);
+ } else {
+ dm_list_init(&dummy_list);
+ return _field_set_string_list(rh, field, &dummy_list, private, 0, NULL);
+ /* TODO: once we have support for STR_LIST reserved values, replace with:
+ * return _field_set_value(field, GET_FIRST_RESERVED_NAME(integrity_settings_undef), GET_FIELD_RESERVED_VALUE(integrity_settings_undef));
+ */
+ }
+}
+
static int _writecache_block_size_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
sector/block size, but not less than the device logical block size.
Possible values: 512, 1024, 2048, 4096.
.
+.TP
+.BR --integritysettings " " key=val
+dm-integrity kernel tunable options can be specified here.
+Settings can be included with lvcreate or lvconvert when integrity is first
+enabled, or changed with lvchange on an existing, inactive LV.
+See kernel documentation for descriptions of tunable options.
+Repeat the option to set multiple values.
+Use lvs -a -o integritysettings VG/LV_rimage_N to display configured values.
+Use lvchange --integritysettings "" to clear
+all configured values (dm-integrity will use its defaults.)
+.P
+.
.SS Integrity initialization
.
When integrity is added to an LV, the kernel needs to initialize the
not lvconvert --raidintegrity y $vg/${lv2}_cpool_cmeta
lvremove -y $vg/$lv1
+lvcreate --type raid1 -m1 -n $lv1 -l 8 --raidintegrity y --integritysettings journal_watermark=10 $vg
+lvs -a -o integritysettings $vg/${lv1}_rimage_0 | grep journal_watermark=10
+not lvchange --integritysettings journal_watermark=20 $vg/$lv1
+lvchange -an $vg/$lv1
+lvchange --integritysettings journal_watermark=80 $vg/$lv1
+lvchange -ay $vg/$lv1
+lvs -a -o integritysettings $vg/${lv1}_rimage_0 | grep journal_watermark=80
+# The journal_watermark value reported in the table may be
+# slightly different from the value set due to the kernel
+# reporting it as a computed value that may include rounding.
+dmsetup table $vg-${lv1}_rimage_0 | tee out
+grep journal_watermark out
+lvchange -an $vg/$lv1
+lvremove -y $vg/$lv1
+
vgremove -ff $vg
"Ignore the \"activation skip\" LV flag during activation\n"
"to allow LVs with the flag set to be activated.\n")
+arg(integritysettings_ARG, '\0', "integritysettings", string_VAL, ARG_GROUPABLE, 0,
+ "Specifies tunable kernel options for dm-integrity.\n"
+ "See \\fBlvmraid\\fP(7) for more information.\n")
+
arg(maps_ARG, 'm', "maps", 0, 0, 0,
"#lvdisplay\n"
"Display the mapping of logical extents to PVs and physical extents.\n"
--setautoactivation Bool, --errorwhenfull Bool, --discards Discards, --zero Bool,
--cachemode CacheMode, --cachepolicy String, --cachesettings String,
--minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
---vdosettings String,
+--vdosettings String, --integritysettings String,
--writebehind Number, --writemostly WriteMostlyPV, --persistent n
# It's unfortunate that activate needs to be optionally allowed here;
---
lvconvert --raidintegrity Bool LV_raid
-OO: --raidintegritymode String, --raidintegrityblocksize Number, OO_LVCONVERT
+OO: --raidintegritymode String, --raidintegrityblocksize Number, --integritysettings String, OO_LVCONVERT
OP: PV ...
ID: lvconvert_integrity
DESC: Add or remove data integrity checksums to raid images.
OO_LVCREATE_THINPOOL: --discards Discards, --errorwhenfull Bool, --pooldatavdo Bool, OO_LVCREATE_VDO, OO_LVCREATE_POOL
OO_LVCREATE_RAID: --regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
---raidintegrity Bool, --raidintegritymode String, --raidintegrityblocksize Number
+--raidintegrity Bool, --raidintegritymode String, --raidintegrityblocksize Number, --integritysettings String
---
return 1;
}
+static int _lvchange_integrity(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ uint32_t *mr)
+{
+ struct integrity_settings settings = { 0 };
+ struct logical_volume *lv_image;
+ struct lv_segment *seg, *seg_image;
+ uint32_t s;
+ int set_count = 0;
+
+ if (!lv_is_raid(lv)) {
+ log_error("A raid LV with integrity is required.");
+ return 0;
+ }
+
+ if (!lv_raid_has_integrity(lv)) {
+ log_error("No integrity found in specified raid LV.");
+ return 0;
+ }
+
+ /*
+ * In the case of dm-integrity, a new dm table line does not trigger a
+ * table reload (see skip_reload_params_compare), so new settings are
+ * not applied to an active integrity device. We could add a flag to
+ * override skip_reload_params_compare through all the layers to lift
+ * this restriction.
+ */
+ if (lv_is_active(lv)) {
+ log_error("LV must be inactive to change integrity settings.");
+ return 0;
+ }
+
+ if (!get_integrity_settings(cmd, &settings))
+ return_0;
+
+ /*
+ * The new specified settings modify the current settings.
+ * A current setting is not changed if a new value is not
+ * specified. Only certain settings can be changed.
+ */
+ seg = first_seg(lv);
+
+ for (s = 0; s < seg->area_count; s++) {
+ lv_image = seg_lv(seg, s);
+ seg_image = first_seg(lv_image);
+
+ if (seg_is_integrity(seg_image)) {
+ if (settings.journal_watermark_set) {
+ seg_image->integrity_settings.journal_watermark_set = 1;
+ seg_image->integrity_settings.journal_watermark = settings.journal_watermark;
+ set_count++;
+ }
+ if (settings.commit_time_set) {
+ seg_image->integrity_settings.commit_time_set = 1;
+ seg_image->integrity_settings.commit_time = settings.commit_time;
+ set_count++;
+ }
+ if (settings.bitmap_flush_interval_set) {
+ seg_image->integrity_settings.bitmap_flush_interval_set = 1;
+ seg_image->integrity_settings.bitmap_flush_interval = settings.bitmap_flush_interval;
+ set_count++;
+ }
+ if (settings.allow_discards_set) {
+ seg_image->integrity_settings.allow_discards_set = 1;
+ seg_image->integrity_settings.allow_discards = settings.allow_discards;
+ set_count++;
+ }
+ }
+ }
+
+ /*
+ * --integritysettings "" clears all previously configured settings,
+ * so dm-integrity kernel code will revert to using its defaults.
+ */
+
+ if (set_count)
+ goto out;
+
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Clear all integrity settings? ") == 'n') {
+ log_print("No settings changed.");
+ return 1;
+ }
+
+ for (s = 0; s < seg->area_count; s++) {
+ lv_image = seg_lv(seg, s);
+ seg_image = first_seg(lv_image);
+
+ if (seg_is_integrity(seg_image)) {
+ seg_image->integrity_settings.journal_watermark_set = 0;
+ seg_image->integrity_settings.commit_time_set = 0;
+ seg_image->integrity_settings.bitmap_flush_interval_set = 0;
+ seg_image->integrity_settings.allow_discards_set = 0;
+ }
+ }
+
+ out:
+ /* Request caller to commit and reload metadata */
+ *mr |= MR_RELOAD;
+
+ return 1;
+}
+
static int _lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv,
int arg, uint32_t *mr)
{
cachepolicy_ARG,
cachesettings_ARG,
vdosettings_ARG,
+ integritysettings_ARG,
-1
};
docmds++;
doit += _lvchange_vdo(cmd, lv, &mr);
break;
+ case integritysettings_ARG:
+ docmds++;
+ doit += _lvchange_integrity(cmd, lv, &mr);
+ break;
default:
log_error(INTERNAL_ERROR "Failed to check for option %s",
arg_long_option_name(i));
struct integrity_settings settings = { .tag_size = 0 };
int ret;
+ if (arg_is_set(cmd, integritysettings_ARG)) {
+ if (!get_integrity_settings(cmd, &settings))
+ return_ECMD_FAILED;
+ }
+
if (!integrity_mode_set(arg_str_value(cmd, raidintegritymode_ARG, NULL), &settings))
return_ECMD_FAILED;
if (!integrity_mode_set(arg_str_value(cmd, raidintegritymode_ARG, NULL), &lp->integrity_settings))
return_0;
}
+ if (arg_is_set(cmd, integritysettings_ARG)) {
+ if (!get_integrity_settings(cmd, &lp->integrity_settings))
+ return_0;
+ }
}
return 1;
raidminrecoveryrate_ARG, \
raidintegrity_ARG, \
raidintegritymode_ARG, \
- raidintegrityblocksize_ARG
+ raidintegrityblocksize_ARG, \
+ integritysettings_ARG
#define SIZE_ARGS \
extents_ARG,\
return 1;
}
+static int _get_one_integrity_setting(struct cmd_context *cmd, struct integrity_settings *settings,
+ char *key, char *val)
+{
+ /*
+ * Some settings handled by other options:
+ * settings->mode from --raidintegritymode
+ * settings->block_size from --raidintegrityblocksize
+ */
+
+ /* always set in metadata and on table line */
+
+ if (!strncmp(key, "journal_sectors", sizeof("journal_sectors") - 1)) {
+ uint32_t size_mb;
+
+ if (sscanf(val, "%u", &settings->journal_sectors) != 1)
+ goto_bad;
+
+ size_mb = settings->journal_sectors / 2048;
+ if (size_mb < 4 || size_mb > 1024) {
+ log_error("Invalid raid integrity journal size %d MiB (use 4-1024 MiB).", size_mb);
+ goto_bad;
+ }
+ settings->journal_sectors_set = 1;
+ return 1;
+ }
+
+
+ /* optional, not included in metadata or table line unless set */
+
+ if (!strncmp(key, "journal_watermark", sizeof("journal_watermark") - 1)) {
+ if (sscanf(val, "%u", &settings->journal_watermark) != 1)
+ goto_bad;
+ if (settings->journal_watermark > 100)
+ goto_bad;
+ settings->journal_watermark_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "commit_time", sizeof("commit_time") - 1)) {
+ if (sscanf(val, "%u", &settings->commit_time) != 1)
+ goto_bad;
+ settings->commit_time_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "bitmap_flush_interval", sizeof("bitmap_flush_interval") - 1)) {
+ if (sscanf(val, "%u", &settings->bitmap_flush_interval) != 1)
+ goto_bad;
+ settings->bitmap_flush_interval_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "allow_discards", sizeof("allow_discards") - 1)) {
+ if (sscanf(val, "%u", &settings->allow_discards) != 1)
+ goto_bad;
+ if (settings->allow_discards != 0 && settings->allow_discards != 1)
+ goto_bad;
+ settings->allow_discards_set = 1;
+ return 1;
+ }
+
+ return 1;
+
+ bad:
+ log_error("Invalid setting: %s", key);
+ return 0;
+}
+
+int get_integrity_settings(struct cmd_context *cmd, struct integrity_settings *settings)
+{
+ struct arg_value_group_list *group;
+ const char *str;
+ char key[64];
+ char val[64];
+ int num;
+ unsigned pos;
+
+ /*
+ * "grouped" means that multiple --integritysettings options can be used.
+ * Each option is also allowed to contain multiple key = val pairs.
+ */
+
+ dm_list_iterate_items(group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(group->arg_values, integritysettings_ARG))
+ continue;
+
+ if (!(str = grouped_arg_str_value(group->arg_values, integritysettings_ARG, NULL)))
+ break;
+
+ pos = 0;
+
+ while (pos < strlen(str)) {
+ /* scan for "key1=val1 key2 = val2 key3= val3" */
+
+ memset(key, 0, sizeof(key));
+ memset(val, 0, sizeof(val));
+
+ if (sscanf(str + pos, " %63[^=]=%63s %n", key, val, &num) != 2) {
+ log_error("Invalid setting at: %s", str+pos);
+ return 0;
+ }
+
+ pos += num;
+
+ if (!_get_one_integrity_setting(cmd, settings, key, val))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
/* FIXME move to lib */
static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag)
{
int get_writecache_settings(struct cmd_context *cmd, struct writecache_settings *settings,
uint32_t *block_size_sectors);
+int get_integrity_settings(struct cmd_context *cmd, struct integrity_settings *settings);
+
int change_tag(struct cmd_context *cmd, struct volume_group *vg,
struct logical_volume *lv, struct physical_volume *pv, int arg);