/* 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);
}
}
/**
* handle_logical_growth_error() - Handle an error during the grow physical process.
* @completion: The admin completion.
*/
static void handle_logical_growth_error(struct vdo_completion *completion)
{
struct vdo *vdo = completion->vdo;
if (vdo->admin.phase == GROW_LOGICAL_PHASE_GROW_BLOCK_MAP) {
/*
* We've failed to write the new size in the super block, so set our in memory
* config back to the old size.
*/
vdo->states.vdo.config.logical_blocks = vdo->block_map->entry_count;
vdo_abandon_block_map_growth(vdo->block_map);
}
/**
* perform_grow_logical() - Grow the logical size of the vdo.
* @vdo: The vdo to grow.
* @new_logical_blocks: The size to which the vdo should be grown.
*
* Context: This method may only be called when the vdo has been suspended and must not be called
* from a base thread.
*
* Return: VDO_SUCCESS or an error.
*/
static int perform_grow_logical(struct vdo *vdo, block_count_t new_logical_blocks)
{ int result;
if (vdo->device_config->logical_blocks == new_logical_blocks) {
/*
* A table was loaded for which we prepared to grow, but a table without that
* growth was what we are resuming with.
*/
vdo_abandon_block_map_growth(vdo->block_map);
return VDO_SUCCESS;
}
vdo_log_info("Resizing logical to %llu",
(unsigned long long) new_logical_blocks); if (vdo->block_map->next_entry_count != new_logical_blocks)
return VDO_PARAMETER_MISMATCH;
result = perform_admin_operation(vdo, GROW_LOGICAL_PHASE_START,
grow_logical_callback,
handle_logical_growth_error, "grow logical"); if (result != VDO_SUCCESS)
return result;
vdo_log_info("Logical blocks now %llu", (unsigned long long) new_logical_blocks);
return VDO_SUCCESS;
}
static void copy_callback(int read_err, unsigned long write_err, void *context)
{
struct vdo_completion *completion = context; int result = (((read_err == 0) && (write_err == 0)) ? VDO_SUCCESS : -EIO);
/**
* copy_partition() - Copy a partition from the location specified in the current layout to that in
* the next layout.
* @vdo: The vdo preparing to grow.
* @id: The ID of the partition to copy.
* @parent: The completion to notify when the copy is complete.
*/
static void copy_partition(struct vdo *vdo, enum partition_id id,
struct vdo_completion *parent)
{
struct dm_io_region read_region, write_regions[1];
struct partition *from = vdo_get_known_partition(&vdo->layout, id);
struct partition *to = vdo_get_known_partition(&vdo->next_layout, id);
/**
* handle_physical_growth_error() - Handle an error during the grow physical process.
* @completion: The sub-task completion.
*/
static void handle_physical_growth_error(struct vdo_completion *completion)
{
completion->vdo->admin.phase = GROW_PHYSICAL_PHASE_ERROR;
grow_physical_callback(completion);
}
/**
* perform_grow_physical() - Grow the physical size of the vdo.
* @vdo: The vdo to resize.
* @new_physical_blocks: The new physical size in blocks.
*
* Context: This method may only be called when the vdo has been suspended and must not be called
* from a base thread.
*
* Return: VDO_SUCCESS or an error.
*/
static int perform_grow_physical(struct vdo *vdo, block_count_t new_physical_blocks)
{ int result;
block_count_t new_depot_size, prepared_depot_size;
block_count_t old_physical_blocks = vdo->states.vdo.config.physical_blocks;
/* Skip any noop grows. */ if (old_physical_blocks == new_physical_blocks)
return VDO_SUCCESS;
if (new_physical_blocks != vdo->next_layout.size) {
/*
* Either the VDO isn't prepared to grow, or it was prepared to grow to a different
* size. Doing this check here relies on the fact that the call to this method is
* done under the dmsetup message lock.
*/
vdo_uninitialize_layout(&vdo->next_layout);
vdo_abandon_new_slabs(vdo->depot);
return VDO_PARAMETER_MISMATCH;
}
/* Validate that we are prepared to grow appropriately. */
new_depot_size =
vdo_get_known_partition(&vdo->next_layout, VDO_SLAB_DEPOT_PARTITION)->count;
prepared_depot_size = (vdo->depot->new_slabs == NULL) ? 0 : vdo->depot->new_size; if (prepared_depot_size != new_depot_size)
return VDO_PARAMETER_MISMATCH;
result = perform_admin_operation(vdo, GROW_PHYSICAL_PHASE_START,
grow_physical_callback,
handle_physical_growth_error, "grow physical"); if (result != VDO_SUCCESS)
return result;
vdo_log_info("Physical block count was %llu, now %llu",
(unsigned long long) old_physical_blocks,
(unsigned long long) new_physical_blocks);
return VDO_SUCCESS;
}
/**
* apply_new_vdo_configuration() - Attempt to make any configuration changes from the table being
* resumed.
* @vdo: The vdo being resumed.
* @config: The new device configuration derived from the table with which the vdo is being
* resumed.
*
* Return: VDO_SUCCESS or an error.
*/
static int __must_check apply_new_vdo_configuration(struct vdo *vdo,
struct device_config *config)
{ int result;
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, (unsigned long long) backing_blocks,
(unsigned long long) config->physical_blocks);
return -EINVAL;
}
if (vdo_get_admin_state(vdo) == VDO_ADMIN_STATE_PRE_LOADED) {
vdo_log_info("starting device '%s'", device_name);
result = perform_admin_operation(vdo, LOAD_PHASE_START, load_callback,
handle_load_error, "load"); if (result == VDO_UNSUPPORTED_VERSION) {
/*
* A component version is not supported. This can happen when the
* recovery journal metadata is in an old version format. Abort the
* load without saving the state.
*/
vdo->suspend_type = VDO_ADMIN_STATE_SUSPENDING;
perform_admin_operation(vdo, SUSPEND_PHASE_START,
suspend_callback, suspend_callback, "suspend");
return result;
}
if ((result != VDO_SUCCESS) && (result != VDO_READ_ONLY)) {
/*
* Something has gone very wrong. Make sure everything has drained and
* leave the device in an unresumable state.
*/
vdo_log_error_strerror(result, "Start failed, could not load VDO metadata");
vdo->suspend_type = VDO_ADMIN_STATE_STOPPING;
perform_admin_operation(vdo, SUSPEND_PHASE_START,
suspend_callback, suspend_callback, "suspend");
return result;
}
/* Evenif the VDO is read-only, it is now able to handle read requests. */
vdo_log_info("device '%s' started", device_name);
}
/* Ifthis 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);
/*
* Now that we've tried to modify the vdo, the new config *is* the config, whether the
* modifications worked or not.
*/
vdo->device_config = config;
/*
* Any error here is highly unexpected and the state of the vdo is questionable, so we mark
* it read-only in memory. Because we are suspended, the read-only state will not be
* written to disk.
*/ 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) {
/* Evenif 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);
return result;
}
static int vdo_preresume(struct dm_target *ti)
{
struct registered_thread instance_thread;
struct vdo *vdo = get_vdo_for_target(ti); int result;
vdo_register_thread_device_id(&instance_thread, &vdo->instance);
result = vdo_preresume_registered(ti, vdo); if ((result == VDO_PARAMETER_MISMATCH) || (result == VDO_INVALID_ADMIN_STATE) ||
(result == VDO_UNSUPPORTED_VERSION))
result = -EINVAL;
vdo_unregister_thread_device_id();
return vdo_status_to_errno(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));
}
static int __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;
}
static void __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.89Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-11)
¤
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.