From 9467808a044a22850c61b3b30d899f852bcc4733 Mon Sep 17 00:00:00 2001 From: Sergei Shtepa Date: Mon, 19 Dec 2022 15:45:40 +0100 Subject: [PATCH] v2 review - optimize source code * code from ctrl.c moved to main.c * remove params.h * version.h available only for standalone version --- module/Makefile | 1 - module/cbt_map.c | 4 +- module/chunk.c | 3 +- module/ctrl.c | 525 ---------------------------------- module/ctrl.h | 7 - module/diff_area.c | 5 +- module/diff_buffer.c | 3 +- module/diff_storage.c | 3 +- module/main.c | 649 +++++++++++++++++++++++++++++++++++++----- module/params.h | 12 - module/tracker.c | 1 - 11 files changed, 588 insertions(+), 625 deletions(-) delete mode 100644 module/ctrl.c delete mode 100644 module/ctrl.h delete mode 100644 module/params.h diff --git a/module/Makefile b/module/Makefile index 53d237df..1d97832d 100644 --- a/module/Makefile +++ b/module/Makefile @@ -6,7 +6,6 @@ include ${M}/Makefile-* blksnap-y := \ cbt_map.o \ chunk.o \ - ctrl.o \ diff_io.o \ diff_area.o \ diff_buffer.o \ diff --git a/module/cbt_map.c b/module/cbt_map.c index 8d226c13..d5ba3b25 100644 --- a/module/cbt_map.c +++ b/module/cbt_map.c @@ -10,11 +10,13 @@ #endif #include "memory_checker.h" #include "cbt_map.h" -#include "params.h" #ifdef STANDALONE_BDEVFILTER #include "log.h" #endif +extern int tracking_block_minimum_shift; +extern int tracking_block_maximum_count; + #ifndef HAVE_BDEV_NR_SECTORS static inline sector_t bdev_nr_sectors(struct block_device *bdev) { diff --git a/module/chunk.c b/module/chunk.c index dd6828db..0c17cce1 100644 --- a/module/chunk.c +++ b/module/chunk.c @@ -5,7 +5,6 @@ #include #include #include "memory_checker.h" -#include "params.h" #include "chunk.h" #include "diff_io.h" #include "diff_buffer.h" @@ -15,6 +14,8 @@ #include "log.h" #endif +extern int chunk_maximum_in_cache; + #ifdef BLK_SNAP_DEBUG_CHUNK_IO DEFINE_MUTEX(logging_lock); #endif diff --git a/module/ctrl.c b/module/ctrl.c deleted file mode 100644 index 78b0a784..00000000 --- a/module/ctrl.c +++ /dev/null @@ -1,525 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#define pr_fmt(fmt) KBUILD_MODNAME "-ctrl: " fmt - -#include -#include -#include -#ifdef STANDALONE_BDEVFILTER -#include "blksnap.h" -#else -#include -#endif -#include "memory_checker.h" -#include "ctrl.h" -#include "params.h" -#include "version.h" -#include "snapshot.h" -#include "snapimage.h" -#include "tracker.h" -#ifdef STANDALONE_BDEVFILTER -#include "log.h" -#endif - -static_assert(sizeof(uuid_t) == sizeof(struct blk_snap_uuid), - "Invalid size of struct blk_snap_uuid or uuid_t."); - -static const struct blk_snap_version version = { - .major = VERSION_MAJOR, - .minor = VERSION_MINOR, - .revision = VERSION_REVISION, - .build = VERSION_BUILD, -}; - -static int ioctl_version(unsigned long arg) -{ - if (copy_to_user((void *)arg, &version, sizeof(version))) { - pr_err("Unable to get version: invalid user buffer\n"); - return -ENODATA; - } - - return 0; -} - -static int ioctl_tracker_remove(unsigned long arg) -{ - struct blk_snap_tracker_remove karg; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg)) != 0) { - pr_err("Unable to remove device from tracking: invalid user buffer\n"); - return -ENODATA; - } - return tracker_remove(MKDEV(karg.dev_id.mj, karg.dev_id.mn)); -} - -static int ioctl_tracker_collect(unsigned long arg) -{ - int res; - struct blk_snap_tracker_collect karg; - struct blk_snap_cbt_info *cbt_info = NULL; - - pr_debug("Collecting tracking devices\n"); - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to collect tracking devices: invalid user buffer\n"); - return -ENODATA; - } - - if (!karg.cbt_info_array) { - /* - * If the buffer is empty, this is a request to determine - * the number of trackers. - */ - res = tracker_collect(0, NULL, &karg.count); - if (res) { - pr_err("Failed to execute tracker_collect. errno=%d\n", - abs(res)); - return res; - } - if (copy_to_user((void *)arg, (void *)&karg, sizeof(karg))) { - pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n"); - return -ENODATA; - } - return 0; - } - - cbt_info = kcalloc(karg.count, sizeof(struct blk_snap_cbt_info), - GFP_KERNEL); - if (!cbt_info) - return -ENOMEM; - memory_object_inc(memory_object_blk_snap_cbt_info); - - res = tracker_collect(karg.count, cbt_info, &karg.count); - if (res) { - pr_err("Failed to execute tracker_collect. errno=%d\n", - abs(res)); - goto fail; - } - - if (copy_to_user(karg.cbt_info_array, cbt_info, - karg.count * sizeof(struct blk_snap_cbt_info))) { - pr_err("Unable to collect tracking devices: invalid user buffer for CBT info\n"); - res = -ENODATA; - goto fail; - } - - if (copy_to_user((void *)arg, (void *)&karg, sizeof(karg))) { - pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n"); - res = -ENODATA; - goto fail; - } -fail: - kfree(cbt_info); - memory_object_dec(memory_object_blk_snap_cbt_info); - - return res; -} - -static int ioctl_tracker_read_cbt_map(unsigned long arg) -{ - struct blk_snap_tracker_read_cbt_bitmap karg; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to read CBT map: invalid user buffer\n"); - return -ENODATA; - } - - return tracker_read_cbt_bitmap(MKDEV(karg.dev_id.mj, karg.dev_id.mn), - karg.offset, karg.length, - (char __user *)karg.buff); -} - -static int ioctl_tracker_mark_dirty_blocks(unsigned long arg) -{ - int ret = 0; - struct blk_snap_tracker_mark_dirty_blocks karg; - struct blk_snap_block_range *dirty_blocks_array; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to mark dirty blocks: invalid user buffer\n"); - return -ENODATA; - } - - dirty_blocks_array = kcalloc( - karg.count, sizeof(struct blk_snap_block_range), GFP_KERNEL); - if (!dirty_blocks_array) - return -ENOMEM; - memory_object_inc(memory_object_blk_snap_block_range); - - if (copy_from_user(dirty_blocks_array, (void *)karg.dirty_blocks_array, - karg.count * sizeof(struct blk_snap_block_range))) { - pr_err("Unable to mark dirty blocks: invalid user buffer\n"); - ret = -ENODATA; - } else { - if (karg.dev_id.mj == snapimage_major()) - ret = snapshot_mark_dirty_blocks( - MKDEV(karg.dev_id.mj, karg.dev_id.mn), - dirty_blocks_array, karg.count); - else - ret = tracker_mark_dirty_blocks( - MKDEV(karg.dev_id.mj, karg.dev_id.mn), - dirty_blocks_array, karg.count); - } - - kfree(dirty_blocks_array); - memory_object_dec(memory_object_blk_snap_block_range); - - return ret; -} - -static int ioctl_snapshot_create(unsigned long arg) -{ - int ret; - struct blk_snap_snapshot_create karg; - struct blk_snap_dev *dev_id_array = NULL; - uuid_t new_id; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to create snapshot: invalid user buffer\n"); - return -ENODATA; - } - - dev_id_array = - kcalloc(karg.count, sizeof(struct blk_snap_dev), GFP_KERNEL); - if (dev_id_array == NULL) { - pr_err("Unable to create snapshot: too many devices %d\n", - karg.count); - return -ENOMEM; - } - memory_object_inc(memory_object_blk_snap_dev); - - if (copy_from_user(dev_id_array, (void *)karg.dev_id_array, - karg.count * sizeof(struct blk_snap_dev))) { - pr_err("Unable to create snapshot: invalid user buffer\n"); - ret = -ENODATA; - goto out; - } - - ret = snapshot_create(dev_id_array, karg.count, &new_id); - if (ret) - goto out; - - export_uuid(karg.id.b, &new_id); - if (copy_to_user((void *)arg, &karg, sizeof(karg))) { - pr_err("Unable to create snapshot: invalid user buffer\n"); - ret = -ENODATA; - } -out: - kfree(dev_id_array); - memory_object_dec(memory_object_blk_snap_dev); - - return ret; -} - -static int ioctl_snapshot_destroy(unsigned long arg) -{ - struct blk_snap_snapshot_destroy karg; - uuid_t id; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to destroy snapshot: invalid user buffer\n"); - return -ENODATA; - } - - import_uuid(&id, karg.id.b); - return snapshot_destroy(&id); -} - -static int ioctl_snapshot_append_storage(unsigned long arg) -{ - struct blk_snap_snapshot_append_storage karg; - uuid_t id; - - pr_debug("Append difference storage\n"); - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to append difference storage: invalid user buffer\n"); - return -EINVAL; - } - - import_uuid(&id, karg.id.b); - return snapshot_append_storage(&id, karg.dev_id, karg.ranges, - karg.count); -} - -static int ioctl_snapshot_take(unsigned long arg) -{ - struct blk_snap_snapshot_take karg; - uuid_t id; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to take snapshot: invalid user buffer\n"); - return -ENODATA; - } - - import_uuid(&id, karg.id.b); - return snapshot_take(&id); -} - -static int ioctl_snapshot_wait_event(unsigned long arg) -{ - int ret = 0; - struct blk_snap_snapshot_event *karg; - uuid_t id; - struct event *event; - - karg = kzalloc(sizeof(struct blk_snap_snapshot_event), GFP_KERNEL); - if (!karg) - return -ENOMEM; - memory_object_inc(memory_object_blk_snap_snapshot_event); - - /* Copy only snapshot ID */ - if (copy_from_user(&karg->id, - &((struct blk_snap_snapshot_event *)arg)->id, - sizeof(struct blk_snap_uuid))) { - pr_err("Unable to get snapshot event. Invalid user buffer\n"); - ret = -EINVAL; - goto out; - } - - import_uuid(&id, karg->id.b); - event = snapshot_wait_event(&id, karg->timeout_ms); - if (IS_ERR(event)) { - ret = PTR_ERR(event); - goto out; - } - - pr_debug("Received event=%lld code=%d data_size=%d\n", event->time, - event->code, event->data_size); - karg->code = event->code; - karg->time_label = event->time; - - if (event->data_size > sizeof(karg->data)) { - pr_err("Event size %d is too big\n", event->data_size); - ret = -ENOSPC; - /* If we can't copy all the data, we copy only part of it. */ - } - memcpy(karg->data, event->data, event->data_size); - event_free(event); - - if (copy_to_user((void *)arg, karg, - sizeof(struct blk_snap_snapshot_event))) { - pr_err("Unable to get snapshot event. Invalid user buffer\n"); - ret = -EINVAL; - } -out: - kfree(karg); - memory_object_dec(memory_object_blk_snap_snapshot_event); - - return ret; -} - -static int ioctl_snapshot_collect(unsigned long arg) -{ - int ret; - struct blk_snap_snapshot_collect karg; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to collect available snapshots: invalid user buffer\n"); - return -ENODATA; - } - - ret = snapshot_collect(&karg.count, karg.ids); - - if (copy_to_user((void *)arg, &karg, sizeof(karg))) { - pr_err("Unable to collect available snapshots: invalid user buffer\n"); - return -ENODATA; - } - - return ret; -} - -static int ioctl_snapshot_collect_images(unsigned long arg) -{ - int ret; - struct blk_snap_snapshot_collect_images karg; - uuid_t id; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to collect snapshot images: invalid user buffer\n"); - return -ENODATA; - } - - import_uuid(&id, karg.id.b); - ret = snapshot_collect_images(&id, karg.image_info_array, - &karg.count); - - if (copy_to_user((void *)arg, &karg, sizeof(karg))) { - pr_err("Unable to collect snapshot images: invalid user buffer\n"); - return -ENODATA; - } - - return ret; -} - -static int (*const blk_snap_ioctl_table[])(unsigned long arg) = { - ioctl_version, - ioctl_tracker_remove, - ioctl_tracker_collect, - ioctl_tracker_read_cbt_map, - ioctl_tracker_mark_dirty_blocks, - ioctl_snapshot_create, - ioctl_snapshot_destroy, - ioctl_snapshot_append_storage, - ioctl_snapshot_take, - ioctl_snapshot_collect, - ioctl_snapshot_collect_images, - ioctl_snapshot_wait_event, -}; - -static_assert( - sizeof(blk_snap_ioctl_table) == - ((blk_snap_ioctl_snapshot_wait_event + 1) * sizeof(void *)), - "The size of table blk_snap_ioctl_table does not match the enum blk_snap_ioctl."); - -#ifdef BLK_SNAP_MODIFICATION - -static const struct blk_snap_mod modification = { - .name = MOD_NAME, - .compatibility_flags = -#ifdef BLK_SNAP_DEBUG_SECTOR_STATE - (1ull << blk_snap_compat_flag_debug_sector_state) | -#endif -#ifdef BLK_SNAP_FILELOG - (1ull << blk_snap_compat_flag_setlog) | -#endif - 0 -}; - -int ioctl_mod(unsigned long arg) -{ - if (copy_to_user((void *)arg, &modification, sizeof(modification))) { - pr_err("Unable to get modification: invalid user buffer\n"); - return -ENODATA; - } - - return 0; -} - -int ioctl_setlog(unsigned long arg) -{ -#ifdef BLK_SNAP_FILELOG - struct blk_snap_setlog karg; - char *filepath = NULL; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to get log parameters: invalid user buffer\n"); - return -ENODATA; - } - - /* - * logging can be disabled - * To do this, it is enough not to specify a logging file or set - * a negative logging level. - */ - if ((karg.level < 0) || !karg.filepath) - return log_restart(-1, NULL, 0); - - if (karg.filepath_size == 0) { - pr_err("Invalid parameters. 'filepath_size' cannot be zero\n"); - return -EINVAL; - } - filepath = kzalloc(karg.filepath_size + 1, GFP_KERNEL); - if (!filepath) - return -ENOMEM; - memory_object_inc(memory_object_log_filepath); - - if (copy_from_user(filepath, (void *)karg.filepath, karg.filepath_size)) { - pr_err("Unable to get log filepath: invalid user buffer\n"); - - kfree(filepath); - memory_object_dec(memory_object_log_filepath); - return -ENODATA; - } - - return log_restart(karg.level, filepath, karg.tz_minuteswest); -#else - return -ENOTTY; -#endif -} - -static int ioctl_get_sector_state(unsigned long arg) -{ -#ifdef BLK_SNAP_DEBUG_SECTOR_STATE - int ret; - struct blk_snap_get_sector_state karg; - dev_t dev_id; - - if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { - pr_err("Unable to get sector state: invalid user buffer\n"); - return -ENODATA; - } - - dev_id = MKDEV(karg.image_dev_id.mj, karg.image_dev_id.mn); - ret = snapshot_get_chunk_state(dev_id, karg.sector, &karg.state); - if (unlikely(ret)) { - pr_err("Failed to get sector state: cannot get chunk state\n"); - return ret; - } - - if (copy_to_user((void *)arg, &karg, sizeof(karg))) { - pr_err("Unable to get sector state: invalid user buffer\n"); - return -ENODATA; - } - - return ret; -#else - return -ENOTTY; -#endif -} - -static int (*const blk_snap_ioctl_table_mod[])(unsigned long arg) = { - ioctl_mod, - ioctl_setlog, - ioctl_get_sector_state, -}; -static_assert( - sizeof(blk_snap_ioctl_table_mod) == - ((blk_snap_ioctl_end_mod - IOCTL_MOD) * sizeof(void *)), - "The size of table blk_snap_ioctl_table_mod does not match the enum blk_snap_ioctl."); -#endif /*BLK_SNAP_MODIFICATION*/ - -static long ctrl_unlocked_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - int nr = _IOC_NR(cmd); - - if (nr > (sizeof(blk_snap_ioctl_table) / sizeof(void *))) { -#ifdef BLK_SNAP_MODIFICATION - if ((nr >= IOCTL_MOD) && - (nr < (IOCTL_MOD + (sizeof(blk_snap_ioctl_table_mod) / - sizeof(void *))))) { - nr -= IOCTL_MOD; - if (blk_snap_ioctl_table_mod[nr]) - return blk_snap_ioctl_table_mod[nr](arg); - } -#endif - return -ENOTTY; - } - - if (!blk_snap_ioctl_table[nr]) - return -ENOTTY; - - return blk_snap_ioctl_table[nr](arg); -} - -static const struct file_operations blksnap_ctrl_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = ctrl_unlocked_ioctl, -}; - -static struct miscdevice blksnap_ctrl_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = BLK_SNAP_CTL, - .fops = &blksnap_ctrl_fops, -}; - -int ctrl_init(void) -{ - return misc_register(&blksnap_ctrl_misc); -} - -void ctrl_done(void) -{ - misc_deregister(&blksnap_ctrl_misc); -} - diff --git a/module/ctrl.h b/module/ctrl.h deleted file mode 100644 index 88f4b052..00000000 --- a/module/ctrl.h +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __BLK_SNAP_CTRL_H -#define __BLK_SNAP_CTRL_H - -int ctrl_init(void); -void ctrl_done(void); -#endif /* __BLK_SNAP_CTRL_H */ diff --git a/module/diff_area.c b/module/diff_area.c index 91466117..f6fc3b6e 100644 --- a/module/diff_area.c +++ b/module/diff_area.c @@ -12,7 +12,6 @@ #include #endif #include "memory_checker.h" -#include "params.h" #include "chunk.h" #include "diff_area.h" #include "diff_buffer.h" @@ -22,6 +21,10 @@ #include "log.h" #endif +extern int chunk_minimum_shift; +extern int chunk_maximum_count; +extern int chunk_maximum_in_cache; + #ifndef HAVE_BDEV_NR_SECTORS static inline sector_t bdev_nr_sectors(struct block_device *bdev) { diff --git a/module/diff_buffer.c b/module/diff_buffer.c index ab851ec4..0f81fc25 100644 --- a/module/diff_buffer.c +++ b/module/diff_buffer.c @@ -2,13 +2,14 @@ #define pr_fmt(fmt) KBUILD_MODNAME "-diff-buffer: " fmt #include "memory_checker.h" -#include "params.h" #include "diff_buffer.h" #include "diff_area.h" #ifdef STANDALONE_BDEVFILTER #include "log.h" #endif +extern int free_diff_buffer_pool_size; + #ifdef BLK_SNAP_DEBUG_DIFF_BUFFER static atomic_t diff_buffer_allocated_counter; diff --git a/module/diff_storage.c b/module/diff_storage.c index 13539713..08d4a09f 100644 --- a/module/diff_storage.c +++ b/module/diff_storage.c @@ -11,7 +11,6 @@ #include #endif #include "memory_checker.h" -#include "params.h" #include "chunk.h" #include "diff_io.h" #include "diff_buffer.h" @@ -20,6 +19,8 @@ #include "log.h" #endif +extern int diff_storage_minimum; + #ifndef PAGE_SECTORS #define PAGE_SECTORS (1 << (PAGE_SHIFT - SECTOR_SHIFT)) #endif diff --git a/module/main.c b/module/main.c index 756f29bc..578da918 100644 --- a/module/main.c +++ b/module/main.c @@ -2,24 +2,25 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #ifdef STANDALONE_BDEVFILTER #include "blksnap.h" #else #include #endif #include "memory_checker.h" -#include "version.h" -#include "params.h" -#include "ctrl.h" -#include "sysfs.h" #include "snapimage.h" #include "snapshot.h" #include "tracker.h" #include "diff_io.h" #ifdef STANDALONE_BDEVFILTER +#include "version.h" #include "log.h" #endif +static_assert(sizeof(uuid_t) == sizeof(struct blk_snap_uuid), + "Invalid size of struct blk_snap_uuid."); + #ifdef STANDALONE_BDEVFILTER #pragma message("Standalone bdevfilter") #endif @@ -60,6 +61,572 @@ #pragma message("The function blk_cleanup_disk() was found.") #endif +/* + * The power of 2 for minimum tracking block size. + * If we make the tracking block size small, we will get detailed information + * about the changes, but the size of the change tracker table will be too + * large, which will lead to inefficient memory usage. + */ +int tracking_block_minimum_shift = 16; + +/* + * The maximum number of tracking blocks. + * A table is created to store information about the status of all tracking + * blocks in RAM. So, if the size of the tracking block is small, then the size + * of the table turns out to be large and memory is consumed inefficiently. + * As the size of the block device grows, the size of the tracking block + * size should also grow. For this purpose, the limit of the maximum + * number of block size is set. + */ +int tracking_block_maximum_count = 2097152; + +/* + * The power of 2 for minimum chunk size. + * The size of the chunk depends on how much data will be copied to the + * difference storage when at least one sector of the block device is changed. + * If the size is small, then small I/O units will be generated, which will + * reduce performance. Too large a chunk size will lead to inefficient use of + * the difference storage. + */ +int chunk_minimum_shift = 18; + +/* + * The maximum number of chunks. + * To store information about the state of all the chunks, a table is created + * in RAM. So, if the size of the chunk is small, then the size of the table + * turns out to be large and memory is consumed inefficiently. + * As the size of the block device grows, the size of the chunk should also + * grow. For this purpose, the maximum number of chunks is set. + */ +int chunk_maximum_count = 2097152; + +/* + * The maximum number of chunks in memory cache. + * Since reading and writing to snapshots is performed in large chunks, + * a cache is implemented to optimize reading small portions of data + * from the snapshot image. As the number of chunks in the cache + * increases, memory consumption also increases. + * The minimum recommended value is four. + */ +int chunk_maximum_in_cache = 32; + +/* + * The size of the pool of preallocated difference buffers. + * A buffer can be allocated for each chunk. After use, this buffer is not + * released immediately, but is sent to the pool of free buffers. + * However, if there are too many free buffers in the pool, then these free + * buffers will be released immediately. + */ +int free_diff_buffer_pool_size = 128; + +/* + * The minimum allowable size of the difference storage in sectors. + * The difference storage is a part of the disk space allocated for storing + * snapshot data. If there is less free space in the storage than the minimum, + * an event is generated about the lack of free space. + */ +int diff_storage_minimum = 2097152; + +#ifdef STANDALONE_BDEVFILTER +static const struct blk_snap_version version = { + .major = VERSION_MAJOR, + .minor = VERSION_MINOR, + .revision = VERSION_REVISION, + .build = VERSION_BUILD, +}; +#else +#define VERSION_STR "1.0.0.0" +static const struct blk_snap_version version = { + .major = 1, + .minor = 0, + .revision = 0, + .build = 0, +}; +#endif + +static int ioctl_version(unsigned long arg) +{ + if (copy_to_user((void *)arg, &version, sizeof(version))) { + pr_err("Unable to get version: invalid user buffer\n"); + return -ENODATA; + } + + return 0; +} + +static int ioctl_tracker_remove(unsigned long arg) +{ + struct blk_snap_tracker_remove karg; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg)) != 0) { + pr_err("Unable to remove device from tracking: invalid user buffer\n"); + return -ENODATA; + } + return tracker_remove(MKDEV(karg.dev_id.mj, karg.dev_id.mn)); +} + +static int ioctl_tracker_collect(unsigned long arg) +{ + int res; + struct blk_snap_tracker_collect karg; + struct blk_snap_cbt_info *cbt_info = NULL; + + pr_debug("Collecting tracking devices\n"); + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to collect tracking devices: invalid user buffer\n"); + return -ENODATA; + } + + if (!karg.cbt_info_array) { + /* + * If the buffer is empty, this is a request to determine + * the number of trackers. + */ + res = tracker_collect(0, NULL, &karg.count); + if (res) { + pr_err("Failed to execute tracker_collect. errno=%d\n", + abs(res)); + return res; + } + if (copy_to_user((void *)arg, (void *)&karg, sizeof(karg))) { + pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n"); + return -ENODATA; + } + return 0; + } + + cbt_info = kcalloc(karg.count, sizeof(struct blk_snap_cbt_info), + GFP_KERNEL); + if (!cbt_info) + return -ENOMEM; + memory_object_inc(memory_object_blk_snap_cbt_info); + + res = tracker_collect(karg.count, cbt_info, &karg.count); + if (res) { + pr_err("Failed to execute tracker_collect. errno=%d\n", + abs(res)); + goto fail; + } + + if (copy_to_user(karg.cbt_info_array, cbt_info, + karg.count * sizeof(struct blk_snap_cbt_info))) { + pr_err("Unable to collect tracking devices: invalid user buffer for CBT info\n"); + res = -ENODATA; + goto fail; + } + + if (copy_to_user((void *)arg, (void *)&karg, sizeof(karg))) { + pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n"); + res = -ENODATA; + goto fail; + } +fail: + kfree(cbt_info); + memory_object_dec(memory_object_blk_snap_cbt_info); + + return res; +} + +static int ioctl_tracker_read_cbt_map(unsigned long arg) +{ + struct blk_snap_tracker_read_cbt_bitmap karg; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to read CBT map: invalid user buffer\n"); + return -ENODATA; + } + + return tracker_read_cbt_bitmap(MKDEV(karg.dev_id.mj, karg.dev_id.mn), + karg.offset, karg.length, + (char __user *)karg.buff); +} + +static int ioctl_tracker_mark_dirty_blocks(unsigned long arg) +{ + int ret = 0; + struct blk_snap_tracker_mark_dirty_blocks karg; + struct blk_snap_block_range *dirty_blocks_array; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to mark dirty blocks: invalid user buffer\n"); + return -ENODATA; + } + + dirty_blocks_array = kcalloc( + karg.count, sizeof(struct blk_snap_block_range), GFP_KERNEL); + if (!dirty_blocks_array) + return -ENOMEM; + memory_object_inc(memory_object_blk_snap_block_range); + + if (copy_from_user(dirty_blocks_array, (void *)karg.dirty_blocks_array, + karg.count * sizeof(struct blk_snap_block_range))) { + pr_err("Unable to mark dirty blocks: invalid user buffer\n"); + ret = -ENODATA; + } else { + if (karg.dev_id.mj == snapimage_major()) + ret = snapshot_mark_dirty_blocks( + MKDEV(karg.dev_id.mj, karg.dev_id.mn), + dirty_blocks_array, karg.count); + else + ret = tracker_mark_dirty_blocks( + MKDEV(karg.dev_id.mj, karg.dev_id.mn), + dirty_blocks_array, karg.count); + } + + kfree(dirty_blocks_array); + memory_object_dec(memory_object_blk_snap_block_range); + + return ret; +} + +static int ioctl_snapshot_create(unsigned long arg) +{ + int ret; + struct blk_snap_snapshot_create karg; + struct blk_snap_dev *dev_id_array = NULL; + uuid_t new_id; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to create snapshot: invalid user buffer\n"); + return -ENODATA; + } + + dev_id_array = + kcalloc(karg.count, sizeof(struct blk_snap_dev), GFP_KERNEL); + if (dev_id_array == NULL) { + pr_err("Unable to create snapshot: too many devices %d\n", + karg.count); + return -ENOMEM; + } + memory_object_inc(memory_object_blk_snap_dev); + + if (copy_from_user(dev_id_array, (void *)karg.dev_id_array, + karg.count * sizeof(struct blk_snap_dev))) { + pr_err("Unable to create snapshot: invalid user buffer\n"); + ret = -ENODATA; + goto out; + } + + ret = snapshot_create(dev_id_array, karg.count, &new_id); + if (ret) + goto out; + + export_uuid(karg.id.b, &new_id); + if (copy_to_user((void *)arg, &karg, sizeof(karg))) { + pr_err("Unable to create snapshot: invalid user buffer\n"); + ret = -ENODATA; + } +out: + kfree(dev_id_array); + memory_object_dec(memory_object_blk_snap_dev); + + return ret; +} + +static int ioctl_snapshot_destroy(unsigned long arg) +{ + struct blk_snap_snapshot_destroy karg; + uuid_t id; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to destroy snapshot: invalid user buffer\n"); + return -ENODATA; + } + + import_uuid(&id, karg.id.b); + return snapshot_destroy(&id); +} + +static int ioctl_snapshot_append_storage(unsigned long arg) +{ + struct blk_snap_snapshot_append_storage karg; + uuid_t id; + + pr_debug("Append difference storage\n"); + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to append difference storage: invalid user buffer\n"); + return -EINVAL; + } + + import_uuid(&id, karg.id.b); + return snapshot_append_storage(&id, karg.dev_id, karg.ranges, + karg.count); +} + +static int ioctl_snapshot_take(unsigned long arg) +{ + struct blk_snap_snapshot_take karg; + uuid_t id; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to take snapshot: invalid user buffer\n"); + return -ENODATA; + } + + import_uuid(&id, karg.id.b); + return snapshot_take(&id); +} + +static int ioctl_snapshot_wait_event(unsigned long arg) +{ + int ret = 0; + struct blk_snap_snapshot_event *karg; + uuid_t id; + struct event *event; + + karg = kzalloc(sizeof(struct blk_snap_snapshot_event), GFP_KERNEL); + if (!karg) + return -ENOMEM; + memory_object_inc(memory_object_blk_snap_snapshot_event); + + /* Copy only snapshot ID */ + if (copy_from_user(&karg->id, + &((struct blk_snap_snapshot_event *)arg)->id, + sizeof(struct blk_snap_uuid))) { + pr_err("Unable to get snapshot event. Invalid user buffer\n"); + ret = -EINVAL; + goto out; + } + + import_uuid(&id, karg->id.b); + event = snapshot_wait_event(&id, karg->timeout_ms); + if (IS_ERR(event)) { + ret = PTR_ERR(event); + goto out; + } + + pr_debug("Received event=%lld code=%d data_size=%d\n", event->time, + event->code, event->data_size); + karg->code = event->code; + karg->time_label = event->time; + + if (event->data_size > sizeof(karg->data)) { + pr_err("Event size %d is too big\n", event->data_size); + ret = -ENOSPC; + /* If we can't copy all the data, we copy only part of it. */ + } + memcpy(karg->data, event->data, event->data_size); + event_free(event); + + if (copy_to_user((void *)arg, karg, + sizeof(struct blk_snap_snapshot_event))) { + pr_err("Unable to get snapshot event. Invalid user buffer\n"); + ret = -EINVAL; + } +out: + kfree(karg); + memory_object_dec(memory_object_blk_snap_snapshot_event); + + return ret; +} + +static int ioctl_snapshot_collect(unsigned long arg) +{ + int ret; + struct blk_snap_snapshot_collect karg; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to collect available snapshots: invalid user buffer\n"); + return -ENODATA; + } + + ret = snapshot_collect(&karg.count, karg.ids); + + if (copy_to_user((void *)arg, &karg, sizeof(karg))) { + pr_err("Unable to collect available snapshots: invalid user buffer\n"); + return -ENODATA; + } + + return ret; +} + +static int ioctl_snapshot_collect_images(unsigned long arg) +{ + int ret; + struct blk_snap_snapshot_collect_images karg; + uuid_t id; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to collect snapshot images: invalid user buffer\n"); + return -ENODATA; + } + + import_uuid(&id, karg.id.b); + ret = snapshot_collect_images(&id, karg.image_info_array, + &karg.count); + + if (copy_to_user((void *)arg, &karg, sizeof(karg))) { + pr_err("Unable to collect snapshot images: invalid user buffer\n"); + return -ENODATA; + } + + return ret; +} + +static int (*const blk_snap_ioctl_table[])(unsigned long arg) = { + ioctl_version, + ioctl_tracker_remove, + ioctl_tracker_collect, + ioctl_tracker_read_cbt_map, + ioctl_tracker_mark_dirty_blocks, + ioctl_snapshot_create, + ioctl_snapshot_destroy, + ioctl_snapshot_append_storage, + ioctl_snapshot_take, + ioctl_snapshot_collect, + ioctl_snapshot_collect_images, + ioctl_snapshot_wait_event, +}; + +static_assert( + sizeof(blk_snap_ioctl_table) == + ((blk_snap_ioctl_snapshot_wait_event + 1) * sizeof(void *)), + "The size of table blk_snap_ioctl_table does not match the enum blk_snap_ioctl."); + +#ifdef BLK_SNAP_MODIFICATION + +static const struct blk_snap_mod modification = { + .name = MOD_NAME, + .compatibility_flags = +#ifdef BLK_SNAP_DEBUG_SECTOR_STATE + (1ull << blk_snap_compat_flag_debug_sector_state) | +#endif +#ifdef BLK_SNAP_FILELOG + (1ull << blk_snap_compat_flag_setlog) | +#endif + 0 +}; + +int ioctl_mod(unsigned long arg) +{ + if (copy_to_user((void *)arg, &modification, sizeof(modification))) { + pr_err("Unable to get modification: invalid user buffer\n"); + return -ENODATA; + } + + return 0; +} + +int ioctl_setlog(unsigned long arg) +{ +#ifdef BLK_SNAP_FILELOG + struct blk_snap_setlog karg; + char *filepath = NULL; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to get log parameters: invalid user buffer\n"); + return -ENODATA; + } + + /* + * logging can be disabled + * To do this, it is enough not to specify a logging file or set + * a negative logging level. + */ + if ((karg.level < 0) || !karg.filepath) + return log_restart(-1, NULL, 0); + + if (karg.filepath_size == 0) { + pr_err("Invalid parameters. 'filepath_size' cannot be zero\n"); + return -EINVAL; + } + filepath = kzalloc(karg.filepath_size + 1, GFP_KERNEL); + if (!filepath) + return -ENOMEM; + memory_object_inc(memory_object_log_filepath); + + if (copy_from_user(filepath, (void *)karg.filepath, karg.filepath_size)) { + pr_err("Unable to get log filepath: invalid user buffer\n"); + + kfree(filepath); + memory_object_dec(memory_object_log_filepath); + return -ENODATA; + } + + return log_restart(karg.level, filepath, karg.tz_minuteswest); +#else + return -ENOTTY; +#endif +} + +static int ioctl_get_sector_state(unsigned long arg) +{ +#ifdef BLK_SNAP_DEBUG_SECTOR_STATE + int ret; + struct blk_snap_get_sector_state karg; + dev_t dev_id; + + if (copy_from_user(&karg, (void *)arg, sizeof(karg))) { + pr_err("Unable to get sector state: invalid user buffer\n"); + return -ENODATA; + } + + dev_id = MKDEV(karg.image_dev_id.mj, karg.image_dev_id.mn); + ret = snapshot_get_chunk_state(dev_id, karg.sector, &karg.state); + if (unlikely(ret)) { + pr_err("Failed to get sector state: cannot get chunk state\n"); + return ret; + } + + if (copy_to_user((void *)arg, &karg, sizeof(karg))) { + pr_err("Unable to get sector state: invalid user buffer\n"); + return -ENODATA; + } + + return ret; +#else + return -ENOTTY; +#endif +} + +static int (*const blk_snap_ioctl_table_mod[])(unsigned long arg) = { + ioctl_mod, + ioctl_setlog, + ioctl_get_sector_state, +}; +static_assert( + sizeof(blk_snap_ioctl_table_mod) == + ((blk_snap_ioctl_end_mod - IOCTL_MOD) * sizeof(void *)), + "The size of table blk_snap_ioctl_table_mod does not match the enum blk_snap_ioctl."); +#endif /*BLK_SNAP_MODIFICATION*/ + +static long ctrl_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = _IOC_NR(cmd); + + if (nr > (sizeof(blk_snap_ioctl_table) / sizeof(void *))) { +#ifdef BLK_SNAP_MODIFICATION + if ((nr >= IOCTL_MOD) && + (nr < (IOCTL_MOD + (sizeof(blk_snap_ioctl_table_mod) / + sizeof(void *))))) { + nr -= IOCTL_MOD; + if (blk_snap_ioctl_table_mod[nr]) + return blk_snap_ioctl_table_mod[nr](arg); + } +#endif + return -ENOTTY; + } + + if (!blk_snap_ioctl_table[nr]) + return -ENOTTY; + + return blk_snap_ioctl_table[nr](arg); +} + +static const struct file_operations blksnap_ctrl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = ctrl_unlocked_ioctl, +}; + +static struct miscdevice blksnap_ctrl_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = BLK_SNAP_CTL, + .fops = &blksnap_ctrl_fops, +}; + static int __init blk_snap_init(void) { int ret; @@ -94,13 +661,13 @@ static int __init blk_snap_init(void) if (ret) goto fail_tracker_init; - ret = ctrl_init(); + ret = misc_register(&blksnap_ctrl_misc); if (ret) - goto fail_ctrl_init; + goto fail_misc_register; return 0; -fail_ctrl_init: +fail_misc_register: tracker_done(); fail_tracker_init: snapimage_done(); @@ -121,7 +688,7 @@ static void __exit blk_snap_exit(void) #else pr_debug("Unloading module\n"); #endif - ctrl_done(); + misc_deregister(&blksnap_ctrl_misc); diff_io_done(); snapshot_done(); @@ -140,72 +707,6 @@ static void __exit blk_snap_exit(void) module_init(blk_snap_init); module_exit(blk_snap_exit); -/* - * The power of 2 for minimum tracking block size. - * If we make the tracking block size small, we will get detailed information - * about the changes, but the size of the change tracker table will be too - * large, which will lead to inefficient memory usage. - */ -int tracking_block_minimum_shift = 16; - -/* - * The maximum number of tracking blocks. - * A table is created to store information about the status of all tracking - * blocks in RAM. So, if the size of the tracking block is small, then the size - * of the table turns out to be large and memory is consumed inefficiently. - * As the size of the block device grows, the size of the tracking block - * size should also grow. For this purpose, the limit of the maximum - * number of block size is set. - */ -int tracking_block_maximum_count = 2097152; - -/* - * The power of 2 for minimum chunk size. - * The size of the chunk depends on how much data will be copied to the - * difference storage when at least one sector of the block device is changed. - * If the size is small, then small I/O units will be generated, which will - * reduce performance. Too large a chunk size will lead to inefficient use of - * the difference storage. - */ -int chunk_minimum_shift = 18; - -/* - * The maximum number of chunks. - * To store information about the state of all the chunks, a table is created - * in RAM. So, if the size of the chunk is small, then the size of the table - * turns out to be large and memory is consumed inefficiently. - * As the size of the block device grows, the size of the chunk should also - * grow. For this purpose, the maximum number of chunks is set. - */ -int chunk_maximum_count = 2097152; - -/* - * The maximum number of chunks in memory cache. - * Since reading and writing to snapshots is performed in large chunks, - * a cache is implemented to optimize reading small portions of data - * from the snapshot image. As the number of chunks in the cache - * increases, memory consumption also increases. - * The minimum recommended value is four. - */ -int chunk_maximum_in_cache = 32; - -/* - * The size of the pool of preallocated difference buffers. - * A buffer can be allocated for each chunk. After use, this buffer is not - * released immediately, but is sent to the pool of free buffers. - * However, if there are too many free buffers in the pool, then these free - * buffers will be released immediately. - */ -int free_diff_buffer_pool_size = 128; - -/* - * The minimum allowable size of the difference storage in sectors. - * The difference storage is a part of the disk space allocated for storing - * snapshot data. If there is less free space in the storage than the minimum, - * an event is generated about the lack of free space. - */ -int diff_storage_minimum = 2097152; - module_param_named(tracking_block_minimum_shift, tracking_block_minimum_shift, int, 0644); MODULE_PARM_DESC(tracking_block_minimum_shift, diff --git a/module/params.h b/module/params.h deleted file mode 100644 index 91817975..00000000 --- a/module/params.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __BLK_SNAP_PARAMS_H -#define __BLK_SNAP_PARAMS_H - -extern int tracking_block_minimum_shift; -extern int tracking_block_maximum_count; -extern int chunk_minimum_shift; -extern int chunk_maximum_count; -extern int chunk_maximum_in_cache; -extern int free_diff_buffer_pool_size; -extern int diff_storage_minimum; -#endif /* __BLK_SNAP_PARAMS_H */ diff --git a/module/tracker.c b/module/tracker.c index bb29d9c5..0891536e 100644 --- a/module/tracker.c +++ b/module/tracker.c @@ -10,7 +10,6 @@ #include #endif #include "memory_checker.h" -#include "params.h" #include "tracker.h" #include "cbt_map.h" #include "diff_area.h"