// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2023 Red Hat */ #include <linux/atomic.h> #include <linux/bitops.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/device-mapper.h> #include <linux/err.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include "admin-state.h" #include "block-map.h" #include "completion.h" #include "constants.h" #include "data-vio.h" #include "dedupe.h" #include "dump.h" #include "encodings.h" #include "errors.h" #include "flush.h" #include "io-submitter.h" #include "logger.h" #include "memory-alloc.h" #include "message-stats.h" #include "recovery-journal.h" #include "repair.h" #include "slab-depot.h" #include "status-codes.h" #include "string-utils.h" #include "thread-device.h" #include "thread-registry.h" #include "thread-utils.h" #include "types.h" #include "vdo.h" #include "vio.h" enum admin_phases { … }; static const char * const ADMIN_PHASE_NAMES[] = …; /* If we bump this, update the arrays below */ #define TABLE_VERSION … /* arrays for handling different table versions */ static const u8 REQUIRED_ARGC[] = …; /* pool name no longer used. only here for verification of older versions */ static const u8 POOL_NAME_ARG_INDEX[] = …; /* * Track in-use instance numbers using a flat bit array. * * O(n) run time isn't ideal, but if we have 1000 VDO devices in use simultaneously we still only * need to scan 16 words, so it's not likely to be a big deal compared to other resource usage. */ /* * This minimum size for the bit array creates a numbering space of 0-999, which allows * successive starts of the same volume to have different instance numbers in any * reasonably-sized test. Changing instances on restart allows vdoMonReport to detect that * the ephemeral stats have reset to zero. */ #define BIT_COUNT_MINIMUM … /* Grow the bit array by this many bits when needed */ #define BIT_COUNT_INCREMENT … struct instance_tracker { … }; static DEFINE_MUTEX(instances_lock); static struct instance_tracker instances; /** * free_device_config() - Free a device config created by parse_device_config(). * @config: The config to free. */ static void free_device_config(struct device_config *config) { … } /** * get_version_number() - Decide the version number from argv. * * @argc: The number of table values. * @argv: The array of table values. * @error_ptr: A pointer to return a error string in. * @version_ptr: A pointer to return the version. * * Return: VDO_SUCCESS or an error code. */ static int get_version_number(int argc, char **argv, char **error_ptr, unsigned int *version_ptr) { … } /* Free a list of non-NULL string pointers, and then the list itself. */ static void free_string_array(char **string_array) { … } /* * Split the input string into substrings, separated at occurrences of the indicated character, * returning a null-terminated list of string pointers. * * The string pointers and the pointer array itself should both be freed with vdo_free() when no * longer needed. This can be done with vdo_free_string_array (below) if the pointers in the array * are not changed. Since the array and copied strings are allocated by this function, it may only * be used in contexts where allocation is permitted. * * Empty substrings are not ignored; that is, returned substrings may be empty strings if the * separator occurs twice in a row. */ static int split_string(const char *string, char separator, char ***substring_array_ptr) { … } /* * Join the input substrings into one string, joined with the indicated character, returning a * string. array_length is a bound on the number of valid elements in substring_array, in case it * is not NULL-terminated. */ static int join_strings(char **substring_array, size_t array_length, char separator, char **string_ptr) { … } /** * parse_bool() - Parse a two-valued option into a bool. * @bool_str: The string value to convert to a bool. * @true_str: The string value which should be converted to true. * @false_str: The string value which should be converted to false. * @bool_ptr: A pointer to return the bool value in. * * Return: VDO_SUCCESS or an error if bool_str is neither true_str nor false_str. */ static inline int __must_check parse_bool(const char *bool_str, const char *true_str, const char *false_str, bool *bool_ptr) { … } /** * process_one_thread_config_spec() - Process one component of a thread parameter configuration * string and update the configuration data structure. * @thread_param_type: The type of thread specified. * @count: The thread count requested. * @config: The configuration data structure to update. * * If the thread count requested is invalid, a message is logged and -EINVAL returned. If the * thread name is unknown, a message is logged but no error is returned. * * Return: VDO_SUCCESS or -EINVAL */ static int process_one_thread_config_spec(const char *thread_param_type, unsigned int count, struct thread_count_config *config) { … } /** * parse_one_thread_config_spec() - Parse one component of a thread parameter configuration string * and update the configuration data structure. * @spec: The thread parameter specification string. * @config: The configuration data to be updated. */ static int parse_one_thread_config_spec(const char *spec, struct thread_count_config *config) { … } /** * parse_thread_config_string() - Parse the configuration string passed and update the specified * counts and other parameters of various types of threads to be * created. * @string: Thread parameter configuration string. * @config: The thread configuration data to update. * * The configuration string should contain one or more comma-separated specs of the form * "typename=number"; the supported type names are "cpu", "ack", "bio", "bioRotationInterval", * "logical", "physical", and "hash". * * If an error occurs during parsing of a single key/value pair, we deem it serious enough to stop * further parsing. * * This function can't set the "reason" value the caller wants to pass back, because we'd want to * format it to say which field was invalid, and we can't allocate the "reason" strings * dynamically. So if an error occurs, we'll log the details and pass back an error. * * Return: VDO_SUCCESS or -EINVAL or -ENOMEM */ static int parse_thread_config_string(const char *string, struct thread_count_config *config) { … } /** * process_one_key_value_pair() - Process one component of an optional parameter string and update * the configuration data structure. * @key: The optional parameter key name. * @value: The optional parameter value. * @config: The configuration data structure to update. * * If the value requested is invalid, a message is logged and -EINVAL returned. If the key is * unknown, a message is logged but no error is returned. * * Return: VDO_SUCCESS or -EINVAL */ static int process_one_key_value_pair(const char *key, unsigned int value, struct device_config *config) { … } /** * parse_one_key_value_pair() - Parse one key/value pair and update the configuration data * structure. * @key: The optional key name. * @value: The optional value. * @config: The configuration data to be updated. * * Return: VDO_SUCCESS or error. */ static int parse_one_key_value_pair(const char *key, const char *value, struct device_config *config) { … } /** * parse_key_value_pairs() - Parse all key/value pairs from a list of arguments. * @argc: The total number of arguments in list. * @argv: The list of key/value pairs. * @config: The device configuration data to update. * * If an error occurs during parsing of a single key/value pair, we deem it serious enough to stop * further parsing. * * This function can't set the "reason" value the caller wants to pass back, because we'd want to * format it to say which field was invalid, and we can't allocate the "reason" strings * dynamically. So if an error occurs, we'll log the details and return the error. * * Return: VDO_SUCCESS or error */ static int parse_key_value_pairs(int argc, char **argv, struct device_config *config) { … } /** * parse_optional_arguments() - Parse the configuration string passed in for optional arguments. * @arg_set: The structure holding the arguments to parse. * @error_ptr: Pointer to a buffer to hold the error string. * @config: Pointer to device configuration data to update. * * For V0/V1 configurations, there will only be one optional parameter; the thread configuration. * The configuration string should contain one or more comma-separated specs of the form * "typename=number"; the supported type names are "cpu", "ack", "bio", "bioRotationInterval", * "logical", "physical", and "hash". * * For V2 configurations and beyond, there could be any number of arguments. They should contain * one or more key/value pairs separated by a space. * * Return: VDO_SUCCESS or error */ static int parse_optional_arguments(struct dm_arg_set *arg_set, char **error_ptr, struct device_config *config) { … } /** * handle_parse_error() - Handle a parsing error. * @config: The config to free. * @error_ptr: A place to store a constant string about the error. * @error_str: A constant string to store in error_ptr. */ static void handle_parse_error(struct device_config *config, char **error_ptr, char *error_str) { … } /** * parse_device_config() - Convert the dmsetup table into a struct device_config. * @argc: The number of table values. * @argv: The array of table values. * @ti: The target structure for this table. * @config_ptr: A pointer to return the allocated config. * * Return: VDO_SUCCESS or an error code. */ static int parse_device_config(int argc, char **argv, struct dm_target *ti, struct device_config **config_ptr) { … } static struct vdo *get_vdo_for_target(struct dm_target *ti) { … } static int vdo_map_bio(struct dm_target *ti, struct bio *bio) { … } static void vdo_io_hints(struct dm_target *ti, struct queue_limits *limits) { … } static int vdo_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { … } /* * Status line is: * <device> <operating mode> <in recovery> <index state> <compression state> * <used physical blocks> <total physical blocks> */ static void vdo_status(struct dm_target *ti, status_type_t status_type, unsigned int status_flags, char *result, unsigned int maxlen) { … } static block_count_t __must_check get_underlying_device_block_count(const struct vdo *vdo) { … } static int __must_check process_vdo_message_locked(struct vdo *vdo, unsigned int argc, char **argv) { … } /* * If the message is a dump, just do it. Otherwise, check that no other message is being processed, * and only proceed if so. * Returns -EBUSY if another message is being processed */ static int __must_check process_vdo_message(struct vdo *vdo, unsigned int argc, char **argv) { … } static int vdo_message(struct dm_target *ti, unsigned int argc, char **argv, char *result_buffer, unsigned int maxlen) { … } static void configure_target_capabilities(struct dm_target *ti) { … } /* * Implements vdo_filter_fn. */ static bool vdo_uses_device(struct vdo *vdo, const void *context) { … } /** * get_thread_id_for_phase() - Get the thread id for the current phase of the admin operation in * progress. */ static thread_id_t __must_check get_thread_id_for_phase(struct vdo *vdo) { … } static struct vdo_completion *prepare_admin_completion(struct vdo *vdo, vdo_action_fn callback, vdo_action_fn error_handler) { … } /** * advance_phase() - Increment the phase of the current admin operation and prepare the admin * completion to run on the thread for the next phase. * @vdo: The on which an admin operation is being performed * * Return: The current phase */ static u32 advance_phase(struct vdo *vdo) { … } /* * Perform an administrative operation (load, suspend, grow logical, or grow physical). This method * should not be called from vdo threads. */ static int perform_admin_operation(struct vdo *vdo, u32 starting_phase, vdo_action_fn callback, vdo_action_fn error_handler, const char *type) { … } /* Assert that we are operating on the correct thread for the current phase. */ static void assert_admin_phase_thread(struct vdo *vdo, const char *what) { … } /** * finish_operation_callback() - Callback to finish an admin operation. * @completion: The admin_completion. */ static void finish_operation_callback(struct vdo_completion *completion) { … } /** * decode_from_super_block() - Decode the VDO state from the super block and validate that it is * correct. * @vdo: The vdo being loaded. * * On error from this method, the component states must be destroyed explicitly. If this method * returns successfully, the component states must not be destroyed. * * Return: VDO_SUCCESS or an error. */ static int __must_check decode_from_super_block(struct vdo *vdo) { … } /** * decode_vdo() - Decode the component data portion of a super block and fill in the corresponding * portions of the vdo being loaded. * @vdo: The vdo being loaded. * * This will also allocate the recovery journal and slab depot. If this method is called with an * asynchronous layer (i.e. a thread config which specifies at least one base thread), the block * map and packer will be constructed as well. * * Return: VDO_SUCCESS or an error. */ static int __must_check decode_vdo(struct vdo *vdo) { … } /** * pre_load_callback() - Callback to initiate a pre-load, registered in vdo_initialize(). * @completion: The admin completion. */ static void pre_load_callback(struct vdo_completion *completion) { … } static void release_instance(unsigned int instance) { … } static void set_device_config(struct dm_target *ti, struct vdo *vdo, struct device_config *config) { … } static int vdo_initialize(struct dm_target *ti, unsigned int instance, struct device_config *config) { … } /* Implements vdo_filter_fn. */ static bool __must_check vdo_is_named(struct vdo *vdo, const void *context) { … } /** * get_bit_array_size() - Return the number of bytes needed to store a bit array of the specified * capacity in an array of unsigned longs. * @bit_count: The number of bits the array must hold. * * Return: the number of bytes needed for the array representation. */ static size_t get_bit_array_size(unsigned int bit_count) { … } /** * grow_bit_array() - Re-allocate the bitmap word array so there will more instance numbers that * can be allocated. * * Since the array is initially NULL, this also initializes the array the first time we allocate an * instance number. * * Return: VDO_SUCCESS or an error code from the allocation */ static int grow_bit_array(void) { … } /** * allocate_instance() - Allocate an instance number. * @instance_ptr: A point to hold the instance number * * Return: VDO_SUCCESS or an error code * * This function must be called while holding the instances lock. */ static int allocate_instance(unsigned int *instance_ptr) { … } static int construct_new_vdo_registered(struct dm_target *ti, unsigned int argc, char **argv, unsigned int instance) { … } static int construct_new_vdo(struct dm_target *ti, unsigned int argc, char **argv) { … } /** * check_may_grow_physical() - Callback to check that we're not in recovery mode, used in * vdo_prepare_to_grow_physical(). * @completion: The admin completion. */ static void check_may_grow_physical(struct vdo_completion *completion) { … } static block_count_t get_partition_size(struct layout *layout, enum partition_id id) { … } /** * grow_layout() - Make the layout for growing a vdo. * @vdo: The vdo preparing to grow. * @old_size: The current size of the vdo. * @new_size: The size to which the vdo will be grown. * * Return: VDO_SUCCESS or an error code. */ static int grow_layout(struct vdo *vdo, block_count_t old_size, block_count_t new_size) { … } static int prepare_to_grow_physical(struct vdo *vdo, block_count_t new_physical_blocks) { … } /** * validate_new_device_config() - Check whether a new device config represents a valid modification * to an existing config. * @to_validate: The new config to validate. * @config: The existing config. * @may_grow: Set to true if growing the logical and physical size of the vdo is currently * permitted. * @error_ptr: A pointer to hold the reason for any error. * * Return: VDO_SUCCESS or an error. */ static int validate_new_device_config(struct device_config *to_validate, struct device_config *config, bool may_grow, char **error_ptr) { … } static int prepare_to_modify(struct dm_target *ti, struct device_config *config, struct vdo *vdo) { … } static int update_existing_vdo(const char *device_name, struct dm_target *ti, unsigned int argc, char **argv, struct vdo *vdo) { … } static int vdo_ctr(struct dm_target *ti, unsigned int argc, char **argv) { … } static void vdo_dtr(struct dm_target *ti) { … } static void vdo_presuspend(struct dm_target *ti) { … } /** * write_super_block_for_suspend() - Update the VDO state and save the super block. * @completion: The admin completion */ static void write_super_block_for_suspend(struct vdo_completion *completion) { … } /** * suspend_callback() - Callback to initiate a suspend, registered in vdo_postsuspend(). * @completion: The sub-task completion. */ static void suspend_callback(struct vdo_completion *completion) { … } static void vdo_postsuspend(struct dm_target *ti) { … } /** * was_new() - Check whether the vdo was new when it was loaded. * @vdo: The vdo to query. * * Return: true if the vdo was new. */ static bool was_new(const struct vdo *vdo) { … } /** * requires_repair() - Check whether a vdo requires recovery or rebuild. * @vdo: The vdo to query. * * Return: true if the vdo must be repaired. */ static bool __must_check requires_repair(const struct vdo *vdo) { … } /** * get_load_type() - Determine how the slab depot was loaded. * @vdo: The vdo. * * Return: How the depot was loaded. */ static enum slab_depot_load_type get_load_type(struct vdo *vdo) { … } /** * load_callback() - Callback to do the destructive parts of loading a VDO. * @completion: The sub-task completion. */ static void load_callback(struct vdo_completion *completion) { … } /** * handle_load_error() - Handle an error during the load operation. * @completion: The admin completion. * * If at all possible, brings the vdo online in read-only mode. This handler is registered in * vdo_preresume_registered(). */ static void handle_load_error(struct vdo_completion *completion) { … } /** * write_super_block_for_resume() - Update the VDO state and save the super block. * @completion: The admin completion */ static void write_super_block_for_resume(struct vdo_completion *completion) { … } /** * resume_callback() - Callback to resume a VDO. * @completion: The admin completion. */ static void resume_callback(struct vdo_completion *completion) { … } /** * grow_logical_callback() - Callback to initiate a grow logical. * @completion: The admin completion. * * Registered in perform_grow_logical(). */ static void grow_logical_callback(struct vdo_completion *completion) { … } /** * 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) { … } /** * 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) { … } static void copy_callback(int read_err, unsigned long write_err, void *context) { … } static void partition_to_region(struct partition *partition, struct vdo *vdo, struct dm_io_region *region) { … } /** * 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) { … } /** * grow_physical_callback() - Callback to initiate a grow physical. * @completion: The admin completion. * * Registered in perform_grow_physical(). */ static void grow_physical_callback(struct vdo_completion *completion) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } static int vdo_preresume_registered(struct dm_target *ti, struct vdo *vdo) { … } static int vdo_preresume(struct dm_target *ti) { … } static void vdo_resume(struct dm_target *ti) { … } /* * If anything changes that affects how user tools will interact with vdo, update the version * number and make sure documentation about the change is complete so tools can properly update * their management code. */ static struct target_type vdo_target_bio = …; static bool dm_registered; static void vdo_module_destroy(void) { … } static int __init vdo_init(void) { … } static void __exit vdo_exit(void) { … } module_init(…) …; module_exit(vdo_exit); module_param_named(log_level, vdo_log_level, uint, 0644); MODULE_PARM_DESC(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;