From 78d14a805c3133c9a633a61c7751a81ebfae4d99 Mon Sep 17 00:00:00 2001 From: David Teigland Date: Mon, 5 Aug 2024 13:20:58 -0500 Subject: [PATCH] integrity: add --integritysettings for tuning The option can be used in multiple ways (like --cachesettings): --integritysettings key=val --integritysettings 'key1=val1 key2=val2' --integritysettings key1=val1 --integritysettings key2=val2 Use with lvcreate or lvconvert when integrity is first enabled to configure: journal_sectors journal_watermark commit_time bitmap_flush_interval allow_discards Use with lvchange to configure (only while inactive): journal_watermark commit_time bitmap_flush_interval allow_discards lvchange --integritysettings "" clears any previously configured settings, so dm-integrity will use its own defaults. lvs -a -o integritysettings displays configured settings. --- device_mapper/all.h | 2 + device_mapper/libdm-deptree.c | 5 ++ lib/integrity/integrity.c | 9 +++ lib/metadata/integrity_manip.c | 51 ++++++++++++-- lib/metadata/metadata-exported.h | 3 + lib/metadata/metadata.c | 21 ++++++ lib/metadata/writecache_manip.c | 46 ++++--------- lib/report/columns.h | 1 + lib/report/properties.c | 2 + lib/report/report.c | 25 +++++++ man/lvmraid.7_main | 12 ++++ test/shell/integrity.sh | 15 +++++ tools/args.h | 4 ++ tools/command-lines.in | 6 +- tools/lvchange.c | 108 +++++++++++++++++++++++++++++ tools/lvconvert.c | 5 ++ tools/lvcreate.c | 7 +- tools/toollib.c | 112 +++++++++++++++++++++++++++++++ tools/toollib.h | 2 + 19 files changed, 392 insertions(+), 44 deletions(-) diff --git a/device_mapper/all.h b/device_mapper/all.h index 2bea150af..fb6030419 100644 --- a/device_mapper/all.h +++ b/device_mapper/all.h @@ -1023,6 +1023,7 @@ struct integrity_settings { 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; @@ -1031,6 +1032,7 @@ struct integrity_settings { 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, diff --git a/device_mapper/libdm-deptree.c b/device_mapper/libdm-deptree.c index 3140bbbf2..a99ac5543 100644 --- a/device_mapper/libdm-deptree.c +++ b/device_mapper/libdm-deptree.c @@ -2868,6 +2868,8 @@ static int _integrity_emit_segment_line(struct dm_task *dmt, 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, @@ -2904,6 +2906,9 @@ static int _integrity_emit_segment_line(struct dm_task *dmt, 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; diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c index 1576c856f..b630f8e09 100644 --- a/lib/integrity/integrity.c +++ b/lib/integrity/integrity.c @@ -156,6 +156,12 @@ static int _integrity_text_import(struct lv_segment *seg, 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; @@ -217,6 +223,9 @@ static int _integrity_text_export(const struct lv_segment *seg, 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; } diff --git a/lib/metadata/integrity_manip.c b/lib/metadata/integrity_manip.c index 4ac517d63..3a186bec1 100644 --- a/lib/metadata/integrity_manip.c +++ b/lib/metadata/integrity_manip.c @@ -50,7 +50,8 @@ int lv_is_integrity_origin(const struct logical_volume *lv) * 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; @@ -58,8 +59,16 @@ static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_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 @@ -73,7 +82,10 @@ static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes) 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; } @@ -84,6 +96,7 @@ static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_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 }; @@ -115,7 +128,7 @@ static int _lv_create_integrity_metadata(struct cmd_context *cmd, 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; @@ -181,7 +194,7 @@ int lv_extend_integrity_in_raid(struct logical_volume *lv, struct dm_list *pvh) } 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; @@ -597,7 +610,7 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting 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++; @@ -975,3 +988,29 @@ fail: 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; +} + + diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index e7cf8cc0d..b8ebca62a 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -1169,6 +1169,7 @@ bool lv_writecache_is_clean(struct cmd_context *cmd, struct logical_volume *lv, 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); @@ -1511,4 +1512,6 @@ int integrity_mode_set(const char *mode, struct integrity_settings *settings); 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 diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index d21df54c5..785350f5e 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -5279,3 +5279,24 @@ int lv_is_striped(struct logical_volume *lv) 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; +} diff --git a/lib/metadata/writecache_manip.c b/lib/metadata/writecache_manip.c index aa43ae082..fd26c9b58 100644 --- a/lib/metadata/writecache_manip.c +++ b/lib/metadata/writecache_manip.c @@ -449,78 +449,56 @@ int lv_writecache_set_cleaner(struct logical_volume *lv) 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) diff --git a/lib/report/columns.h b/lib/report/columns.h index 1bb36b9a3..f200a3041 100644 --- a/lib/report/columns.h +++ b/lib/report/columns.h @@ -291,6 +291,7 @@ FIELD(SEGS, seg, STR_LIST, "Metadata Devs", list, 0, metadatadevices, metadata_d 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) diff --git a/lib/report/properties.c b/lib/report/properties.c index 6f302360f..111dbb1c7 100644 --- a/lib/report/properties.c +++ b/lib/report/properties.c @@ -651,6 +651,8 @@ GET_LVSEG_STR_PROPERTY_FN(seg_monitor, lvseg_monitor_dup(lvseg->lv->vg->vgmem, l #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) diff --git a/lib/report/report.c b/lib/report/report.c index 1f2de23f0..b1139e5b1 100644 --- a/lib/report/report.c +++ b/lib/report/report.c @@ -3376,6 +3376,31 @@ static int _integritymismatches_disp(struct dm_report *rh __attribute__((unused) 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, diff --git a/man/lvmraid.7_main b/man/lvmraid.7_main index 086f53491..c73403e71 100644 --- a/man/lvmraid.7_main +++ b/man/lvmraid.7_main @@ -809,6 +809,18 @@ system sector/block sizes. It may be less than the file system 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 diff --git a/test/shell/integrity.sh b/test/shell/integrity.sh index 940b5fde8..16d8930e8 100644 --- a/test/shell/integrity.sh +++ b/test/shell/integrity.sh @@ -769,4 +769,19 @@ not lvconvert --raidintegrity y $vg/${lv2}_cpool_cdata 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 diff --git a/tools/args.h b/tools/args.h index ae4fd05a2..7fc225171 100644 --- a/tools/args.h +++ b/tools/args.h @@ -1392,6 +1392,10 @@ arg(ignoreactivationskip_ARG, 'K', "ignoreactivationskip", 0, 0, 0, "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" diff --git a/tools/command-lines.in b/tools/command-lines.in index 3ad5d3c46..e9b12f30e 100644 --- a/tools/command-lines.in +++ b/tools/command-lines.in @@ -244,7 +244,7 @@ OO_LVCHANGE_META: --addtag Tag, --deltag Tag, --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; @@ -843,7 +843,7 @@ FLAGS: SECONDARY_SYNTAX --- 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. @@ -869,7 +869,7 @@ OO_LVCREATE_VDO: --compression Bool, --deduplication Bool, --vdosettings String 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 --- diff --git a/tools/lvchange.c b/tools/lvchange.c index 662be0109..783bb0b1e 100644 --- a/tools/lvchange.c +++ b/tools/lvchange.c @@ -810,6 +810,109 @@ static int _lvchange_vdo(struct cmd_context *cmd, 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) { @@ -1210,6 +1313,7 @@ static int _option_requires_direct_commit(int opt_enum) cachepolicy_ARG, cachesettings_ARG, vdosettings_ARG, + integritysettings_ARG, -1 }; @@ -1414,6 +1518,10 @@ static int _lvchange_properties_single(struct cmd_context *cmd, 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)); diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 65e0d51cd..c575f823b 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -6519,6 +6519,11 @@ static int _lvconvert_integrity_single(struct cmd_context *cmd, 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; diff --git a/tools/lvcreate.c b/tools/lvcreate.c index bfe4035a4..3c24c050d 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -615,6 +615,10 @@ static int _read_raid_params(struct cmd_context *cmd, 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; @@ -936,7 +940,8 @@ static int _lvcreate_params(struct cmd_context *cmd, raidminrecoveryrate_ARG, \ raidintegrity_ARG, \ raidintegritymode_ARG, \ - raidintegrityblocksize_ARG + raidintegrityblocksize_ARG, \ + integritysettings_ARG #define SIZE_ARGS \ extents_ARG,\ diff --git a/tools/toollib.c b/tools/toollib.c index 62cc5cf65..5031b2cc2 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -1627,6 +1627,118 @@ int get_writecache_settings(struct cmd_context *cmd, struct writecache_settings 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) { diff --git a/tools/toollib.h b/tools/toollib.h index abf3726a3..5671229ed 100644 --- a/tools/toollib.h +++ b/tools/toollib.h @@ -227,6 +227,8 @@ int get_vdo_settings(struct cmd_context *cmd, 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); -- 2.43.5