1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-30 17:18:21 +03:00

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.
This commit is contained in:
David Teigland 2024-08-05 13:20:58 -05:00
parent 07576f7e51
commit 78d14a805c
19 changed files with 392 additions and 44 deletions

View File

@ -1023,6 +1023,7 @@ struct integrity_settings {
uint32_t commit_time; uint32_t commit_time;
uint32_t bitmap_flush_interval; uint32_t bitmap_flush_interval;
uint64_t sectors_per_bit; uint64_t sectors_per_bit;
uint32_t allow_discards;
unsigned journal_sectors_set:1; unsigned journal_sectors_set:1;
unsigned interleave_sectors_set:1; unsigned interleave_sectors_set:1;
@ -1031,6 +1032,7 @@ struct integrity_settings {
unsigned commit_time_set:1; unsigned commit_time_set:1;
unsigned bitmap_flush_interval_set:1; unsigned bitmap_flush_interval_set:1;
unsigned sectors_per_bit_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, int dm_tree_node_add_integrity_target(struct dm_tree_node *node,

View File

@ -2868,6 +2868,8 @@ static int _integrity_emit_segment_line(struct dm_task *dmt,
count++; count++;
if (set->sectors_per_bit_set) if (set->sectors_per_bit_set)
count++; 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", EMIT_PARAMS(pos, "%s 0 %u %s %d fix_padding block_size:%u internal_hash:%s",
origin_dev, origin_dev,
@ -2904,6 +2906,9 @@ static int _integrity_emit_segment_line(struct dm_task *dmt,
if (set->sectors_per_bit_set) if (set->sectors_per_bit_set)
EMIT_PARAMS(pos, " sectors_per_bit:%llu", (unsigned long long)set->sectors_per_bit); 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)) if (!dm_task_secure_data(dmt))
stack; stack;

View File

@ -156,6 +156,12 @@ static int _integrity_text_import(struct lv_segment *seg,
set->sectors_per_bit_set = 1; 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->origin = origin_lv;
seg->integrity_meta_dev = meta_lv; seg->integrity_meta_dev = meta_lv;
seg->lv->status |= INTEGRITY; seg->lv->status |= INTEGRITY;
@ -217,6 +223,9 @@ static int _integrity_text_export(const struct lv_segment *seg,
if (set->sectors_per_bit) if (set->sectors_per_bit)
outf(f, "sectors_per_bit = %llu", (unsigned long long)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; return 1;
} }

View File

@ -50,7 +50,8 @@ int lv_is_integrity_origin(const struct logical_volume *lv)
* plus some initial space for journals. * plus some initial space for journals.
* (again from trial and error testing.) * (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 meta_bytes;
uint64_t initial_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. */ /* Every 500M of data needs 4M of metadata. */
meta_bytes = ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES); 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 <= 512M -> 4M
* lv_size <= 1G -> 8M * lv_size <= 1G -> 8M
* lv_size <= 4G -> 32M * 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; initial_bytes = 32 * ONE_MB_IN_BYTES;
else if (lv_size_bytes > (4ULL * ONE_GB_IN_BYTES)) else if (lv_size_bytes > (4ULL * ONE_GB_IN_BYTES))
initial_bytes = 64 * ONE_MB_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; 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, static int _lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg, struct volume_group *vg,
struct lvcreate_params *lp, struct lvcreate_params *lp,
struct integrity_settings *settings,
struct logical_volume **meta_lv) struct logical_volume **meta_lv)
{ {
char metaname[NAME_LEN] = { 0 }; char metaname[NAME_LEN] = { 0 };
@ -115,7 +128,7 @@ static int _lv_create_integrity_metadata(struct cmd_context *cmd,
lp_meta.pvh = lp->pvh; lp_meta.pvh = lp->pvh;
lv_size_bytes = (uint64_t)lp->extents * (uint64_t)vg->extent_size * 512; 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; meta_sectors = meta_bytes / 512;
lp_meta.extents = meta_sectors / vg->extent_size; 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; 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_sectors = meta_bytes / 512;
meta_extents = meta_sectors / vg->extent_size; 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.pvh = use_pvh;
lp.extents = lv_image->size / vg->extent_size; 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; goto_bad;
revert_meta_lvs++; revert_meta_lvs++;
@ -975,3 +988,29 @@ fail:
return 0; 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;
}

View File

@ -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); bool writecache_cleaner_supported(struct cmd_context *cmd);
int lv_is_integrity_origin(const struct logical_volume *lv); 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); int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size); 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_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 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 #endif

View File

@ -5279,3 +5279,24 @@ int lv_is_striped(struct logical_volume *lv)
return segtype_is_striped(seg->segtype); 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;
}

View File

@ -449,78 +449,56 @@ int lv_writecache_set_cleaner(struct logical_volume *lv)
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 writecache_settings_to_str_list(struct writecache_settings *settings, struct dm_list *result, struct dm_pool *mem)
{ {
int errors = 0; int errors = 0;
if (settings->high_watermark_set) 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++; errors++;
if (settings->low_watermark_set) 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++; errors++;
if (settings->writeback_jobs_set) 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++; errors++;
if (settings->autocommit_blocks_set) 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++; errors++;
if (settings->autocommit_time_set) 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++; errors++;
if (settings->fua_set) 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++; errors++;
if (settings->nofua_set) 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++; errors++;
if (settings->cleaner_set && settings->cleaner) 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++; errors++;
if (settings->max_age_set) 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++; errors++;
if (settings->metadata_only_set) 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++; errors++;
if (settings->pause_writeback_set) 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++; errors++;
if (settings->new_key && settings->new_val) 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++; errors++;
if (errors) if (errors)

View File

@ -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, "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, "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, "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, "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) FIELD(SEGS, seg, BIN, "VDODeduplication", list, 0, vdo_deduplication, vdo_deduplication, "Set for deduplicated LV (vdopool).", 0)

View File

@ -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_cache_policy_set prop_not_implemented_set
#define _kernel_metadata_format_get prop_not_implemented_get #define _kernel_metadata_format_get prop_not_implemented_get
#define _kernel_metadata_format_set prop_not_implemented_set #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 */ /* PVSEG */
GET_PVSEG_NUM_PROPERTY_FN(pvseg_start, pvseg->pe) GET_PVSEG_NUM_PROPERTY_FN(pvseg_start, pvseg->pe)

View File

@ -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)); 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)), static int _writecache_block_size_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem, struct dm_pool *mem,
struct dm_report_field *field, struct dm_report_field *field,

View File

@ -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. sector/block size, but not less than the device logical block size.
Possible values: 512, 1024, 2048, 4096. 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 .SS Integrity initialization
. .
When integrity is added to an LV, the kernel needs to initialize the When integrity is added to an LV, the kernel needs to initialize the

View File

@ -769,4 +769,19 @@ not lvconvert --raidintegrity y $vg/${lv2}_cpool_cdata
not lvconvert --raidintegrity y $vg/${lv2}_cpool_cmeta not lvconvert --raidintegrity y $vg/${lv2}_cpool_cmeta
lvremove -y $vg/$lv1 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 vgremove -ff $vg

View File

@ -1392,6 +1392,10 @@ arg(ignoreactivationskip_ARG, 'K', "ignoreactivationskip", 0, 0, 0,
"Ignore the \"activation skip\" LV flag during activation\n" "Ignore the \"activation skip\" LV flag during activation\n"
"to allow LVs with the flag set to be activated.\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, arg(maps_ARG, 'm', "maps", 0, 0, 0,
"#lvdisplay\n" "#lvdisplay\n"
"Display the mapping of logical extents to PVs and physical extents.\n" "Display the mapping of logical extents to PVs and physical extents.\n"

View File

@ -244,7 +244,7 @@ OO_LVCHANGE_META: --addtag Tag, --deltag Tag,
--setautoactivation Bool, --errorwhenfull Bool, --discards Discards, --zero Bool, --setautoactivation Bool, --errorwhenfull Bool, --discards Discards, --zero Bool,
--cachemode CacheMode, --cachepolicy String, --cachesettings String, --cachemode CacheMode, --cachepolicy String, --cachesettings String,
--minrecoveryrate SizeKB, --maxrecoveryrate SizeKB, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
--vdosettings String, --vdosettings String, --integritysettings String,
--writebehind Number, --writemostly WriteMostlyPV, --persistent n --writebehind Number, --writemostly WriteMostlyPV, --persistent n
# It's unfortunate that activate needs to be optionally allowed here; # It's unfortunate that activate needs to be optionally allowed here;
@ -843,7 +843,7 @@ FLAGS: SECONDARY_SYNTAX
--- ---
lvconvert --raidintegrity Bool LV_raid lvconvert --raidintegrity Bool LV_raid
OO: --raidintegritymode String, --raidintegrityblocksize Number, OO_LVCONVERT OO: --raidintegritymode String, --raidintegrityblocksize Number, --integritysettings String, OO_LVCONVERT
OP: PV ... OP: PV ...
ID: lvconvert_integrity ID: lvconvert_integrity
DESC: Add or remove data integrity checksums to raid images. 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_THINPOOL: --discards Discards, --errorwhenfull Bool, --pooldatavdo Bool, OO_LVCREATE_VDO, OO_LVCREATE_POOL
OO_LVCREATE_RAID: --regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB, OO_LVCREATE_RAID: --regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
--raidintegrity Bool, --raidintegritymode String, --raidintegrityblocksize Number --raidintegrity Bool, --raidintegritymode String, --raidintegrityblocksize Number, --integritysettings String
--- ---

View File

@ -810,6 +810,109 @@ static int _lvchange_vdo(struct cmd_context *cmd,
return 1; 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, static int _lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv,
int arg, uint32_t *mr) int arg, uint32_t *mr)
{ {
@ -1210,6 +1313,7 @@ static int _option_requires_direct_commit(int opt_enum)
cachepolicy_ARG, cachepolicy_ARG,
cachesettings_ARG, cachesettings_ARG,
vdosettings_ARG, vdosettings_ARG,
integritysettings_ARG,
-1 -1
}; };
@ -1414,6 +1518,10 @@ static int _lvchange_properties_single(struct cmd_context *cmd,
docmds++; docmds++;
doit += _lvchange_vdo(cmd, lv, &mr); doit += _lvchange_vdo(cmd, lv, &mr);
break; break;
case integritysettings_ARG:
docmds++;
doit += _lvchange_integrity(cmd, lv, &mr);
break;
default: default:
log_error(INTERNAL_ERROR "Failed to check for option %s", log_error(INTERNAL_ERROR "Failed to check for option %s",
arg_long_option_name(i)); arg_long_option_name(i));

View File

@ -6519,6 +6519,11 @@ static int _lvconvert_integrity_single(struct cmd_context *cmd,
struct integrity_settings settings = { .tag_size = 0 }; struct integrity_settings settings = { .tag_size = 0 };
int ret; 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)) if (!integrity_mode_set(arg_str_value(cmd, raidintegritymode_ARG, NULL), &settings))
return_ECMD_FAILED; return_ECMD_FAILED;

View File

@ -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)) if (!integrity_mode_set(arg_str_value(cmd, raidintegritymode_ARG, NULL), &lp->integrity_settings))
return_0; return_0;
} }
if (arg_is_set(cmd, integritysettings_ARG)) {
if (!get_integrity_settings(cmd, &lp->integrity_settings))
return_0;
}
} }
return 1; return 1;
@ -936,7 +940,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
raidminrecoveryrate_ARG, \ raidminrecoveryrate_ARG, \
raidintegrity_ARG, \ raidintegrity_ARG, \
raidintegritymode_ARG, \ raidintegritymode_ARG, \
raidintegrityblocksize_ARG raidintegrityblocksize_ARG, \
integritysettings_ARG
#define SIZE_ARGS \ #define SIZE_ARGS \
extents_ARG,\ extents_ARG,\

View File

@ -1627,6 +1627,118 @@ int get_writecache_settings(struct cmd_context *cmd, struct writecache_settings
return 1; 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 */ /* FIXME move to lib */
static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag) static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag)
{ {

View File

@ -227,6 +227,8 @@ int get_vdo_settings(struct cmd_context *cmd,
int get_writecache_settings(struct cmd_context *cmd, struct writecache_settings *settings, int get_writecache_settings(struct cmd_context *cmd, struct writecache_settings *settings,
uint32_t *block_size_sectors); 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, int change_tag(struct cmd_context *cmd, struct volume_group *vg,
struct logical_volume *lv, struct physical_volume *pv, int arg); struct logical_volume *lv, struct physical_volume *pv, int arg);