/* If we bump this, update the arrays below */ #define TABLE_VERSION 4
/* arrays for handling different table versions */ staticconst u8 REQUIRED_ARGC[] = { 10, 12, 9, 7, 6 }; /* pool name no longer used. only here for verification of older versions */ staticconst u8 POOL_NAME_ARG_INDEX[] = { 8, 10, 8 };
/* *Thisminimumsizeforthebitarraycreatesanumberingspaceof0-999,whichallows *successivestartsofthesamevolumetohavedifferentinstancenumbersinany *reasonably-sizedtest.ChanginginstancesonrestartallowsvdoMonReporttodetectthat *theephemeralstatshaveresettozero.
*/ #define BIT_COUNT_MINIMUM 1000 /* Grow the bit array by this many bits when needed */ #define BIT_COUNT_INCREMENT 100
/* Reduce the chance a use-after-free (as in BZ 1669960) happens to work. */
memset(config, 0, sizeof(*config));
vdo_free(config);
}
/** *get_version_number()-Decidetheversionnumberfromargv. * *@argc:Thenumberoftablevalues. *@argv:Thearrayoftablevalues. *@error_ptr:Apointertoreturnaerrorstringin. *@version_ptr:Apointertoreturntheversion. * *Return:VDO_SUCCESSoranerrorcode.
*/ staticint get_version_number(int argc, char **argv, char **error_ptr, unsignedint *version_ptr)
{ /* version, if it exists, is in a form of V<n> */ if (sscanf(argv[0], "V%u", version_ptr) == 1) { if (*version_ptr < 1 || *version_ptr > TABLE_VERSION) {
*error_ptr = "Unknown version number detected"; return VDO_BAD_CONFIGURATION;
}
} else { /* V0 actually has no version number in the table string */
*version_ptr = 0;
}
/* *V0andV1havenooptionalparameters.Therewillalwaysbeaparameterforthread *config,evenifit'sa"."toshowit'sanemptylist.
*/ if (*version_ptr <= 1) { if (argc != REQUIRED_ARGC[*version_ptr]) {
*error_ptr = "Incorrect number of arguments for version"; return VDO_BAD_CONFIGURATION;
}
} elseif (argc < REQUIRED_ARGC[*version_ptr]) {
*error_ptr = "Incorrect number of arguments for version"; return VDO_BAD_CONFIGURATION;
}
if (*version_ptr != TABLE_VERSION) {
vdo_log_warning("Detected version mismatch between kernel module and tools kernel: %d, tool: %d",
TABLE_VERSION, *version_ptr);
vdo_log_warning("Please consider upgrading management tools to match kernel.");
} return VDO_SUCCESS;
}
/* Free a list of non-NULL string pointers, and then the list itself. */ staticvoid free_string_array(char **string_array)
{ unsignedint offset;
if (strcmp(key, "deduplication") == 0) return parse_bool(value, "on", "off", &config->deduplication);
if (strcmp(key, "compression") == 0) return parse_bool(value, "on", "off", &config->compression);
/* The remaining arguments must have integral values. */
result = kstrtouint(value, 10, &count); if (result) {
vdo_log_error("optional config string error: integer value needed, found \"%s\"",
value); return result;
} return process_one_key_value_pair(key, count, config);
}
if ((logical_bytes % VDO_BLOCK_SIZE) != 0) {
handle_parse_error(config, error_ptr, "Logical size must be a multiple of 4096"); return VDO_BAD_CONFIGURATION;
}
if (argc == 0) {
handle_parse_error(config, error_ptr, "Incorrect number of arguments"); return VDO_BAD_CONFIGURATION;
}
result = vdo_allocate(1, struct device_config, "device_config", &config); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Could not allocate config structure"); return VDO_BAD_CONFIGURATION;
}
/* Save the original string. */
result = join_strings(argv, argc, ' ', &config->original_string); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Could not populate string"); return VDO_BAD_CONFIGURATION;
}
result = get_version_number(argc, argv, error_ptr, &config->version); if (result != VDO_SUCCESS) { /* get_version_number sets error_ptr itself. */
handle_parse_error(config, error_ptr, *error_ptr); return result;
} /* Move the arg pointer forward only if the argument was there. */ if (config->version >= 1)
dm_shift_arg(&arg_set);
result = vdo_duplicate_string(dm_shift_arg(&arg_set), "parent device name",
&config->parent_device_name); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Could not copy parent device name"); return VDO_BAD_CONFIGURATION;
}
/* Get the physical blocks, if known. */ if (config->version >= 1) {
result = kstrtoull(dm_shift_arg(&arg_set), 10, &config->physical_blocks); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Invalid physical block count"); return VDO_BAD_CONFIGURATION;
}
}
/* Get the logical block size and validate */
result = parse_bool(dm_shift_arg(&arg_set), "512", "4096", &enable_512e); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Invalid logical block size"); return VDO_BAD_CONFIGURATION;
}
config->logical_block_size = (enable_512e ? 512 : 4096);
/* Skip past the two no longer used read cache options. */ if (config->version <= 1)
dm_consume_args(&arg_set, 2);
/* Get the page cache size. */
result = kstrtouint(dm_shift_arg(&arg_set), 10, &config->cache_size); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Invalid block map page cache size"); return VDO_BAD_CONFIGURATION;
}
/* Get the block map era length. */
result = kstrtouint(dm_shift_arg(&arg_set), 10, &config->block_map_maximum_age); if (result != VDO_SUCCESS) {
handle_parse_error(config, error_ptr, "Invalid block map maximum age"); return VDO_BAD_CONFIGURATION;
}
/* Skip past the no longer used MD RAID5 optimization mode */ if (config->version <= 2)
dm_consume_args(&arg_set, 1);
/* Skip past the no longer used write policy setting */ if (config->version <= 3)
dm_consume_args(&arg_set, 1);
/* Skip past the no longer used pool name for older table lines */ if (config->version <= 2) { /* *Makesuretheenumtogetthepoolnamefromargvdirectlyisstillinsyncwith *theparsingofthetableline.
*/ if (&arg_set.argv[0] != &argv[POOL_NAME_ARG_INDEX[config->version]]) {
handle_parse_error(config, error_ptr, "Pool name not in expected location"); return VDO_BAD_CONFIGURATION;
}
dm_shift_arg(&arg_set);
}
/* Get the optional arguments and validate. */
result = parse_optional_arguments(&arg_set, error_ptr, config); if (result != VDO_SUCCESS) { /* parse_optional_arguments sets error_ptr itself. */
handle_parse_error(config, error_ptr, *error_ptr); return result;
}
/* *Logical,physical,andhashzonecountscanallbezero;thenwegetonethreaddoing *everything,ourolderconfiguration.Ifanyzonecountisnon-zero,theothersmustbe *aswell.
*/ if (((config->thread_counts.logical_zones == 0) !=
(config->thread_counts.physical_zones == 0)) ||
((config->thread_counts.physical_zones == 0) !=
(config->thread_counts.hash_zones == 0))) {
handle_parse_error(config, error_ptr, "Logical, physical, and hash zones counts must all be zero or all non-zero"); return VDO_BAD_CONFIGURATION;
}
/* The minimum io size for random io */
limits->io_min = VDO_BLOCK_SIZE; /* The optimal io size for streamed/sequential io */
limits->io_opt = VDO_BLOCK_SIZE;
switch (status_type) { case STATUSTYPE_INFO: /* Report info for dmsetup status */
mutex_lock(&vdo->stats_mutex);
vdo_fetch_statistics(vdo, &vdo->stats_buffer);
stats = &vdo->stats_buffer;
/* *Usingthe"interruptible"interfacemeansthatLinuxwillnotlogamessagewhenwewait *formorethan120seconds.
*/ while (wait_for_completion_interruptible(&admin->callback_sync)) { /* However, if we get a signal in a user-mode process, we could spin... */
fsleep(1000);
}
result = admin->completion.result; /* pairs with implicit barrier in cmpxchg above */
smp_wmb();
atomic_set(&admin->busy, 0); return result;
}
/* Assert that we are operating on the correct thread for the current phase. */ staticvoid assert_admin_phase_thread(struct vdo *vdo, constchar *what)
{
VDO_ASSERT_LOG_ONLY(vdo_get_callback_thread_id() == get_thread_id_for_phase(vdo), "%s on correct thread for %s", what,
ADMIN_PHASE_NAMES[vdo->admin.phase]);
}
/* *Ifthedeviceconfigspecifiesalargerlogicalsizethanwasrecordedinthesuper *block,justacceptit.
*/ if (vdo->states.vdo.config.logical_blocks < config->logical_blocks) {
vdo_log_warning("Growing logical size: a logical size of %llu blocks was specified, but that differs from the %llu blocks configured in the vdo super block",
(unsignedlonglong) config->logical_blocks,
(unsignedlonglong) vdo->states.vdo.config.logical_blocks);
vdo->states.vdo.config.logical_blocks = config->logical_blocks;
}
result = vdo_validate_component_states(&vdo->states, vdo->geometry.nonce,
config->physical_blocks,
config->logical_blocks); if (result != VDO_SUCCESS) return result;
if (maximum_age == 0) { return vdo_log_error_strerror(VDO_BAD_CONFIGURATION, "maximum age must be greater than 0");
}
result = vdo_enable_read_only_entry(vdo); if (result != VDO_SUCCESS) return result;
partition = vdo_get_known_partition(&vdo->layout,
VDO_RECOVERY_JOURNAL_PARTITION);
result = vdo_decode_recovery_journal(vdo->states.recovery_journal,
vdo->states.vdo.nonce, vdo, partition,
vdo->states.vdo.complete_recoveries,
vdo->states.vdo.config.recovery_journal_size,
&vdo->recovery_journal); if (result != VDO_SUCCESS) return result;
partition = vdo_get_known_partition(&vdo->layout, VDO_SLAB_SUMMARY_PARTITION);
result = vdo_decode_slab_depot(vdo->states.slab_depot, vdo, partition,
&vdo->depot); if (result != VDO_SUCCESS) return result;
result = vdo_decode_block_map(vdo->states.block_map,
vdo->states.vdo.config.logical_blocks, vdo,
vdo->recovery_journal, vdo->states.vdo.nonce,
vdo->device_config->cache_size, maximum_age,
&vdo->block_map); if (result != VDO_SUCCESS) return result;
result = vdo_make_physical_zones(vdo, &vdo->physical_zones); if (result != VDO_SUCCESS) return result;
/* The logical zones depend on the physical zones already existing. */
result = vdo_make_logical_zones(vdo, &vdo->logical_zones); if (result != VDO_SUCCESS) return result;
staticvoid release_instance(unsignedint instance)
{
mutex_lock(&instances_lock); if (instance >= instances.bit_count) {
VDO_ASSERT_LOG_ONLY(false, "instance number %u must be less than bit count %u",
instance, instances.bit_count);
} elseif (test_bit(instance, instances.words) == 0) {
VDO_ASSERT_LOG_ONLY(false, "instance number %u must be allocated", instance);
} else {
__clear_bit(instance, instances.words);
instances.count -= 1;
}
mutex_unlock(&instances_lock);
}
/** *get_bit_array_size()-Returnthenumberofbytesneededtostoreabitarrayofthespecified *capacityinanarrayofunsignedlongs. *@bit_count:Thenumberofbitsthearraymusthold. * *Return:thenumberofbytesneededforthearrayrepresentation.
*/ static size_t get_bit_array_size(unsignedint bit_count)
{ /* Round up to a multiple of the word size and convert to a byte count. */ return (BITS_TO_LONGS(bit_count) * sizeof(unsignedlong));
}
result = vdo_reallocate_memory(instances.words,
get_bit_array_size(instances.bit_count),
get_bit_array_size(new_count), "instance number bit array", &new_words); if (result != VDO_SUCCESS) return result;
/* If there are no unallocated instances, grow the bit array. */ if (instances.count >= instances.bit_count) {
result = grow_bit_array(); if (result != VDO_SUCCESS) return result;
}
/* *Theremustbeazerobitsomewherenow.Findit,startingjustafterthelastinstance *allocated.
*/
instance = find_next_zero_bit(instances.words, instances.bit_count,
instances.next); if (instance >= instances.bit_count) { /* Nothing free after next, so wrap around to instance zero. */
instance = find_first_zero_bit(instances.words, instances.bit_count);
result = VDO_ASSERT(instance < instances.bit_count, "impossibly, no zero bit found"); if (result != VDO_SUCCESS) return result;
}
result = parse_device_config(argc, argv, ti, &config); if (result != VDO_SUCCESS) {
vdo_log_error_strerror(result, "parsing failed: %s", ti->error);
release_instance(instance); return -EINVAL;
}
/* Beyond this point, the instance number will be cleaned up for us if needed */
result = vdo_initialize(ti, instance, config); if (result != VDO_SUCCESS) {
release_instance(instance);
free_device_config(config); return vdo_status_to_errno(result);
}
if (vdo->next_layout.size == new_size) { /* We are already prepared to grow to the new size, so we're done. */ return VDO_SUCCESS;
}
/* Make a copy completion if there isn't one */ if (vdo->partition_copier == NULL) {
vdo->partition_copier = dm_kcopyd_client_create(NULL); if (IS_ERR(vdo->partition_copier)) {
result = PTR_ERR(vdo->partition_copier);
vdo->partition_copier = NULL; return result;
}
}
/* Free any unused preparation. */
vdo_uninitialize_layout(&vdo->next_layout);
/* Ensure the new journal and summary are entirely within the added blocks. */
min_new_size = (old_size +
get_partition_size(&vdo->next_layout,
VDO_SLAB_SUMMARY_PARTITION) +
get_partition_size(&vdo->next_layout,
VDO_RECOVERY_JOURNAL_PARTITION)); if (min_new_size > new_size) { /* Copying the journal and summary would destroy some old metadata. */
vdo_uninitialize_layout(&vdo->next_layout);
dm_kcopyd_client_destroy(vdo_forget(vdo->partition_copier)); return VDO_INCREMENT_TOO_SMALL;
}
vdo_log_info("Preparing to resize physical to %llu",
(unsignedlonglong) new_physical_blocks);
VDO_ASSERT_LOG_ONLY((new_physical_blocks > current_physical_blocks), "New physical size is larger than current physical size");
result = perform_admin_operation(vdo, PREPARE_GROW_PHYSICAL_PHASE_START,
check_may_grow_physical,
finish_operation_callback, "prepare grow-physical"); if (result != VDO_SUCCESS) return result;
result = grow_layout(vdo, current_physical_blocks, new_physical_blocks); if (result != VDO_SUCCESS) return result;
result = vdo_prepare_to_grow_slab_depot(vdo->depot,
vdo_get_known_partition(&vdo->next_layout,
VDO_SLAB_DEPOT_PARTITION)); if (result != VDO_SUCCESS) {
vdo_uninitialize_layout(&vdo->next_layout); return result;
}
vdo_log_info("Done preparing to resize physical"); return VDO_SUCCESS;
}
if (to_validate->physical_blocks < config->physical_blocks) {
*error_ptr = "Removing physical storage from a VDO is not supported"; return VDO_NOT_IMPLEMENTED;
}
if (!may_grow && (to_validate->physical_blocks > config->physical_blocks)) {
*error_ptr = "VDO physical size may not grow in current state"; return VDO_NOT_IMPLEMENTED;
}
result = validate_new_device_config(config, vdo->device_config, may_grow,
&ti->error); if (result != VDO_SUCCESS) return -EINVAL;
if (config->logical_blocks > vdo->device_config->logical_blocks) {
block_count_t logical_blocks = vdo->states.vdo.config.logical_blocks;
vdo_log_info("Preparing to resize logical to %llu",
(unsignedlonglong) config->logical_blocks);
VDO_ASSERT_LOG_ONLY((config->logical_blocks > logical_blocks), "New logical size is larger than current size");
result = vdo_prepare_to_grow_block_map(vdo->block_map,
config->logical_blocks); if (result != VDO_SUCCESS) {
ti->error = "Device vdo_prepare_to_grow_logical failed"; return result;
}
vdo_log_info("Done preparing to resize logical");
}
if (config->physical_blocks > vdo->device_config->physical_blocks) {
result = prepare_to_grow_physical(vdo, config->physical_blocks); if (result != VDO_SUCCESS) { if (result == VDO_PARAMETER_MISMATCH) { /* *Ifwedon'ttrapthiscase,vdo_status_to_errno()willremap *itto-EIO,whichismisleadingandahistorical.
*/
result = -EINVAL;
}
if (result == VDO_TOO_MANY_SLABS)
ti->error = "Device vdo_prepare_to_grow_physical failed (specified physical size too big based on formatted slab size)"; else
ti->error = "Device vdo_prepare_to_grow_physical failed";
return result;
}
}
if (strcmp(config->parent_device_name, vdo->device_config->parent_device_name) != 0) { constchar *device_name = vdo_get_device_name(config->owning_target);
vdo_log_info("Updating backing device of %s from %s to %s", device_name,
vdo->device_config->parent_device_name,
config->parent_device_name);
}
list_del_init(&config->config_list); if (list_empty(&vdo->device_config_list)) { constchar *device_name;
/* This was the last config referencing the VDO. Free it. */ unsignedint instance = vdo->instance; struct registered_thread allocating_thread, instance_thread;
case SUSPEND_PHASE_BLOCK_MAP:
vdo_drain_block_map(vdo->block_map, vdo_get_admin_state_code(state),
completion); return;
case SUSPEND_PHASE_JOURNAL:
vdo_drain_recovery_journal(vdo->recovery_journal,
vdo_get_admin_state_code(state), completion); return;
case SUSPEND_PHASE_DEPOT:
vdo_drain_slab_depot(vdo->depot, vdo_get_admin_state_code(state),
completion); return;
case SUSPEND_PHASE_READ_ONLY_WAIT:
vdo_wait_until_not_entering_read_only_mode(completion); return;
case SUSPEND_PHASE_WRITE_SUPER_BLOCK: if (vdo_is_state_suspending(state) || (completion->result != VDO_SUCCESS)) { /* If we didn't save the VDO or there was an error, we're done. */ break;
}
switch (advance_phase(vdo)) { case LOAD_PHASE_START:
result = vdo_start_operation(&vdo->admin.state, VDO_ADMIN_STATE_LOADING); if (result != VDO_SUCCESS) {
vdo_continue_completion(completion, result); return;
}
/* Prepare the recovery journal for new entries. */
vdo_open_recovery_journal(vdo->recovery_journal, vdo->depot,
vdo->block_map);
vdo_allow_read_only_mode_entry(completion); return;
case LOAD_PHASE_LOAD_DEPOT:
vdo_set_dedupe_state_normal(vdo->hash_zones); if (vdo_is_read_only(vdo)) { /* *Inread-onlymodewedon'tusetheallocatoranditmaynotevenbe *readable,sodon'tbothertryingtoloadit.
*/
vdo_set_completion_result(completion, VDO_READ_ONLY); break;
}
if (requires_repair(vdo)) {
vdo_repair(completion); return;
}
switch (vdo_get_state(vdo)) { case VDO_CLEAN: case VDO_NEW:
vdo_set_state(vdo, VDO_DIRTY);
vdo_save_components(vdo, completion); return;
case VDO_DIRTY: case VDO_READ_ONLY_MODE: case VDO_FORCE_REBUILD: case VDO_RECOVERING: case VDO_REBUILD_FOR_UPGRADE: /* No need to write the super block in these cases */
vdo_launch_completion(completion); return;
case VDO_REPLAYING: default:
vdo_continue_completion(completion, UDS_BAD_STATE);
}
}
result = perform_grow_logical(vdo, config->logical_blocks); if (result != VDO_SUCCESS) {
vdo_log_error("grow logical operation failed, result = %d", result); return result;
}
result = perform_grow_physical(vdo, config->physical_blocks); if (result != VDO_SUCCESS)
vdo_log_error("resize operation failed, result = %d", result);
backing_blocks = get_underlying_device_block_count(vdo); if (backing_blocks < config->physical_blocks) { /* FIXME: can this still happen? */
vdo_log_error("resume of device '%s' failed: backing device has %llu blocks but VDO physical size is %llu blocks",
device_name, (unsignedlonglong) backing_blocks,
(unsignedlonglong) config->physical_blocks); return -EINVAL;
}
/* If this fails, the VDO was not in a state to be resumed. This should never happen. */
result = apply_new_vdo_configuration(vdo, config);
BUG_ON(result == VDO_INVALID_ADMIN_STATE);
/* *Anyerrorhereishighlyunexpectedandthestateofthevdoisquestionable,sowemark *itread-onlyinmemory.Becausewearesuspended,theread-onlystatewillnotbe *writtentodisk.
*/ if (result != VDO_SUCCESS) {
vdo_log_error_strerror(result, "Commit of modifications to device '%s' failed",
device_name);
vdo_enter_read_only_mode(vdo, result); return result;
}
if (vdo_get_admin_state(vdo)->normal) { /* The VDO was just started, so we don't need to resume it. */ return VDO_SUCCESS;
}
result = perform_admin_operation(vdo, RESUME_PHASE_START, resume_callback,
resume_callback, "resume");
BUG_ON(result == VDO_INVALID_ADMIN_STATE); if (result == VDO_READ_ONLY) { /* Even if the vdo is read-only, it has still resumed. */
result = VDO_SUCCESS;
}
if (result != VDO_SUCCESS)
vdo_log_error("resume of device '%s' failed with error: %d", device_name,
result);
if (dm_registered)
dm_unregister_target(&vdo_target_bio);
VDO_ASSERT_LOG_ONLY(instances.count == 0, "should have no instance numbers still in use, but have %u",
instances.count);
vdo_free(instances.words);
memset(&instances, 0, sizeof(struct instance_tracker));
}
staticint __init vdo_init(void)
{ int result = 0;
/* Memory tracking must be initialized first for accurate accounting. */
vdo_memory_init();
vdo_initialize_threads_mutex();
vdo_initialize_thread_device_registry();
vdo_initialize_device_registry_once();
/* Add VDO errors to the set of errors registered by the indexer. */
result = vdo_register_status_codes(); if (result != VDO_SUCCESS) {
vdo_log_error("vdo_register_status_codes failed %d", result);
vdo_module_destroy(); return result;
}
result = dm_register_target(&vdo_target_bio); if (result < 0) {
vdo_log_error("dm_register_target failed %d", result);
vdo_module_destroy(); return result;
}
dm_registered = true;
return result;
}
staticvoid __exit vdo_exit(void)
{
vdo_module_destroy(); /* Memory tracking cleanup must be done last. */
vdo_memory_exit();
}
module_init(vdo_init);
module_exit(vdo_exit);
module_param_named(log_level, vdo_log_level, uint, 0644);
MODULE_PARM_DESC(log_level, "Log level for log messages");
MODULE_DESCRIPTION(DM_NAME " target for transparent deduplication");
MODULE_AUTHOR("Red Hat, Inc.");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.85 Sekunden
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.