No commit message... And this is a 8600+ lines patch. Can you split this into manageable pieces ? I do not think anybody will review such a huge patch. On 2020/10/21 18:05, Sergei Shtepa wrote: > Signed-off-by: Sergei Shtepa <sergei.shtepa@xxxxxxxxx> > --- > drivers/block/Kconfig | 2 + > drivers/block/Makefile | 1 + > drivers/block/blk-snap/Kconfig | 24 + > drivers/block/blk-snap/Makefile | 28 + > drivers/block/blk-snap/big_buffer.c | 193 ++++ > drivers/block/blk-snap/big_buffer.h | 24 + > drivers/block/blk-snap/blk-snap-ctl.h | 190 ++++ > drivers/block/blk-snap/blk_deferred.c | 566 +++++++++++ > drivers/block/blk-snap/blk_deferred.h | 67 ++ > drivers/block/blk-snap/blk_descr_file.c | 82 ++ > drivers/block/blk-snap/blk_descr_file.h | 26 + > drivers/block/blk-snap/blk_descr_mem.c | 66 ++ > drivers/block/blk-snap/blk_descr_mem.h | 14 + > drivers/block/blk-snap/blk_descr_multidev.c | 86 ++ > drivers/block/blk-snap/blk_descr_multidev.h | 25 + > drivers/block/blk-snap/blk_descr_pool.c | 190 ++++ > drivers/block/blk-snap/blk_descr_pool.h | 38 + > drivers/block/blk-snap/blk_redirect.c | 507 ++++++++++ > drivers/block/blk-snap/blk_redirect.h | 73 ++ > drivers/block/blk-snap/blk_util.c | 33 + > drivers/block/blk-snap/blk_util.h | 33 + > drivers/block/blk-snap/cbt_map.c | 210 +++++ > drivers/block/blk-snap/cbt_map.h | 62 ++ > drivers/block/blk-snap/common.h | 31 + > drivers/block/blk-snap/ctrl_fops.c | 691 ++++++++++++++ > drivers/block/blk-snap/ctrl_fops.h | 19 + > drivers/block/blk-snap/ctrl_pipe.c | 562 +++++++++++ > drivers/block/blk-snap/ctrl_pipe.h | 34 + > drivers/block/blk-snap/ctrl_sysfs.c | 73 ++ > drivers/block/blk-snap/ctrl_sysfs.h | 5 + > drivers/block/blk-snap/defer_io.c | 397 ++++++++ > drivers/block/blk-snap/defer_io.h | 39 + > drivers/block/blk-snap/main.c | 82 ++ > drivers/block/blk-snap/params.c | 58 ++ > drivers/block/blk-snap/params.h | 29 + > drivers/block/blk-snap/rangevector.c | 85 ++ > drivers/block/blk-snap/rangevector.h | 31 + > drivers/block/blk-snap/snapimage.c | 982 ++++++++++++++++++++ > drivers/block/blk-snap/snapimage.h | 16 + > drivers/block/blk-snap/snapshot.c | 225 +++++ > drivers/block/blk-snap/snapshot.h | 17 + > drivers/block/blk-snap/snapstore.c | 929 ++++++++++++++++++ > drivers/block/blk-snap/snapstore.h | 68 ++ > drivers/block/blk-snap/snapstore_device.c | 532 +++++++++++ > drivers/block/blk-snap/snapstore_device.h | 63 ++ > drivers/block/blk-snap/snapstore_file.c | 52 ++ > drivers/block/blk-snap/snapstore_file.h | 15 + > drivers/block/blk-snap/snapstore_mem.c | 91 ++ > drivers/block/blk-snap/snapstore_mem.h | 20 + > drivers/block/blk-snap/snapstore_multidev.c | 118 +++ > drivers/block/blk-snap/snapstore_multidev.h | 22 + > drivers/block/blk-snap/tracker.c | 449 +++++++++ > drivers/block/blk-snap/tracker.h | 38 + > drivers/block/blk-snap/tracking.c | 270 ++++++ > drivers/block/blk-snap/tracking.h | 13 + > drivers/block/blk-snap/version.h | 7 + > 56 files changed, 8603 insertions(+) > create mode 100644 drivers/block/blk-snap/Kconfig > create mode 100644 drivers/block/blk-snap/Makefile > create mode 100644 drivers/block/blk-snap/big_buffer.c > create mode 100644 drivers/block/blk-snap/big_buffer.h > create mode 100644 drivers/block/blk-snap/blk-snap-ctl.h > create mode 100644 drivers/block/blk-snap/blk_deferred.c > create mode 100644 drivers/block/blk-snap/blk_deferred.h > create mode 100644 drivers/block/blk-snap/blk_descr_file.c > create mode 100644 drivers/block/blk-snap/blk_descr_file.h > create mode 100644 drivers/block/blk-snap/blk_descr_mem.c > create mode 100644 drivers/block/blk-snap/blk_descr_mem.h > create mode 100644 drivers/block/blk-snap/blk_descr_multidev.c > create mode 100644 drivers/block/blk-snap/blk_descr_multidev.h > create mode 100644 drivers/block/blk-snap/blk_descr_pool.c > create mode 100644 drivers/block/blk-snap/blk_descr_pool.h > create mode 100644 drivers/block/blk-snap/blk_redirect.c > create mode 100644 drivers/block/blk-snap/blk_redirect.h > create mode 100644 drivers/block/blk-snap/blk_util.c > create mode 100644 drivers/block/blk-snap/blk_util.h > create mode 100644 drivers/block/blk-snap/cbt_map.c > create mode 100644 drivers/block/blk-snap/cbt_map.h > create mode 100644 drivers/block/blk-snap/common.h > create mode 100644 drivers/block/blk-snap/ctrl_fops.c > create mode 100644 drivers/block/blk-snap/ctrl_fops.h > create mode 100644 drivers/block/blk-snap/ctrl_pipe.c > create mode 100644 drivers/block/blk-snap/ctrl_pipe.h > create mode 100644 drivers/block/blk-snap/ctrl_sysfs.c > create mode 100644 drivers/block/blk-snap/ctrl_sysfs.h > create mode 100644 drivers/block/blk-snap/defer_io.c > create mode 100644 drivers/block/blk-snap/defer_io.h > create mode 100644 drivers/block/blk-snap/main.c > create mode 100644 drivers/block/blk-snap/params.c > create mode 100644 drivers/block/blk-snap/params.h > create mode 100644 drivers/block/blk-snap/rangevector.c > create mode 100644 drivers/block/blk-snap/rangevector.h > create mode 100644 drivers/block/blk-snap/snapimage.c > create mode 100644 drivers/block/blk-snap/snapimage.h > create mode 100644 drivers/block/blk-snap/snapshot.c > create mode 100644 drivers/block/blk-snap/snapshot.h > create mode 100644 drivers/block/blk-snap/snapstore.c > create mode 100644 drivers/block/blk-snap/snapstore.h > create mode 100644 drivers/block/blk-snap/snapstore_device.c > create mode 100644 drivers/block/blk-snap/snapstore_device.h > create mode 100644 drivers/block/blk-snap/snapstore_file.c > create mode 100644 drivers/block/blk-snap/snapstore_file.h > create mode 100644 drivers/block/blk-snap/snapstore_mem.c > create mode 100644 drivers/block/blk-snap/snapstore_mem.h > create mode 100644 drivers/block/blk-snap/snapstore_multidev.c > create mode 100644 drivers/block/blk-snap/snapstore_multidev.h > create mode 100644 drivers/block/blk-snap/tracker.c > create mode 100644 drivers/block/blk-snap/tracker.h > create mode 100644 drivers/block/blk-snap/tracking.c > create mode 100644 drivers/block/blk-snap/tracking.h > create mode 100644 drivers/block/blk-snap/version.h > > diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig > index ecceaaa1a66f..c53ef661110f 100644 > --- a/drivers/block/Kconfig > +++ b/drivers/block/Kconfig > @@ -460,4 +460,6 @@ config BLK_DEV_RSXX > > source "drivers/block/rnbd/Kconfig" > > +source "drivers/block/blk-snap/Kconfig" > + > endif # BLK_DEV > diff --git a/drivers/block/Makefile b/drivers/block/Makefile > index e1f63117ee94..312000598944 100644 > --- a/drivers/block/Makefile > +++ b/drivers/block/Makefile > @@ -40,6 +40,7 @@ obj-$(CONFIG_BLK_DEV_PCIESSD_MTIP32XX) += mtip32xx/ > obj-$(CONFIG_BLK_DEV_RSXX) += rsxx/ > obj-$(CONFIG_ZRAM) += zram/ > obj-$(CONFIG_BLK_DEV_RNBD) += rnbd/ > +obj-$(CONFIG_BLK_SNAP) += blk-snap/ > > obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_blk.o > null_blk-objs := null_blk_main.o > diff --git a/drivers/block/blk-snap/Kconfig b/drivers/block/blk-snap/Kconfig > new file mode 100644 > index 000000000000..7a2db99a80dd > --- /dev/null > +++ b/drivers/block/blk-snap/Kconfig > @@ -0,0 +1,24 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# > +# blk-snap block io layer filter module configuration > +# > +# > +#select BLK_FILTER > + > +config BLK_SNAP > + tristate "Block device snapshot filter" > + depends on BLK_FILTER > + help > + > + Allow to create snapshots and track block changes for a block > + devices. Designed for creating backups for any block devices > + (without device mapper). Snapshots are temporary and are released > + then backup is completed. Change block tracking allows you to > + create incremental or differential backups. > + > +config BLK_SNAP_SNAPSTORE_MULTIDEV > + bool "Multi device snapstore configuration support" > + depends on BLK_SNAP > + help > + > + Allow to create snapstore on multiple block devices. > diff --git a/drivers/block/blk-snap/Makefile b/drivers/block/blk-snap/Makefile > new file mode 100644 > index 000000000000..1d628e8e1862 > --- /dev/null > +++ b/drivers/block/blk-snap/Makefile > @@ -0,0 +1,28 @@ > +# SPDX-License-Identifier: GPL-2.0 > +blk-snap-y += blk_deferred.o > +blk-snap-y += blk_descr_file.o > +blk-snap-y += blk_descr_mem.o > +blk-snap-y += blk_descr_multidev.o > +blk-snap-y += blk_descr_pool.o > +blk-snap-y += blk_redirect.o > +blk-snap-y += blk_util.o > +blk-snap-y += cbt_map.o > +blk-snap-y += ctrl_fops.o > +blk-snap-y += ctrl_pipe.o > +blk-snap-y += ctrl_sysfs.o > +blk-snap-y += defer_io.o > +blk-snap-y += main.o > +blk-snap-y += params.o > +blk-snap-y += big_buffer.o > +blk-snap-y += rangevector.o > +blk-snap-y += snapimage.o > +blk-snap-y += snapshot.o > +blk-snap-y += snapstore.o > +blk-snap-y += snapstore_device.o > +blk-snap-y += snapstore_file.o > +blk-snap-y += snapstore_mem.o > +blk-snap-y += snapstore_multidev.o > +blk-snap-y += tracker.o > +blk-snap-y += tracking.o > + > +obj-$(CONFIG_BLK_SNAP) += blk-snap.o > diff --git a/drivers/block/blk-snap/big_buffer.c b/drivers/block/blk-snap/big_buffer.c > new file mode 100644 > index 000000000000..c0a75255a807 > --- /dev/null > +++ b/drivers/block/blk-snap/big_buffer.c > @@ -0,0 +1,193 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include "common.h" > +#include <linux/mm.h> > +#include "big_buffer.h" > + > +static inline size_t page_count_calc(size_t buffer_size) > +{ > + size_t page_count = buffer_size / PAGE_SIZE; > + > + if (buffer_size & (PAGE_SIZE - 1)) > + page_count += 1; > + return page_count; > +} > + > +struct big_buffer *big_buffer_alloc(size_t buffer_size, int gfp_opt) > +{ > + int res = SUCCESS; > + struct big_buffer *bbuff; > + size_t count; > + size_t inx; > + > + count = page_count_calc(buffer_size); > + > + bbuff = kzalloc(sizeof(struct big_buffer) + count * sizeof(void *), gfp_opt); > + if (bbuff == NULL) > + return NULL; > + > + bbuff->pg_cnt = count; > + for (inx = 0; inx < bbuff->pg_cnt; ++inx) { > + struct page *pg = alloc_page(gfp_opt); > + > + if (!pg) { > + res = -ENOMEM; > + break; > + } > + bbuff->pg[inx] = page_address(pg); > + } > + > + if (res != SUCCESS) { > + big_buffer_free(bbuff); > + return NULL; > + } > + > + return bbuff; > +} > + > +void big_buffer_free(struct big_buffer *bbuff) > +{ > + size_t inx; > + size_t count = bbuff->pg_cnt; > + > + if (bbuff == NULL) > + return; > + > + for (inx = 0; inx < count; ++inx) > + if (bbuff->pg[inx] != NULL) > + free_page((unsigned long)bbuff->pg[inx]); > + > + kfree(bbuff); > +} > + > +size_t big_buffer_copy_to_user(char __user *dst_user, size_t offset, struct big_buffer *bbuff, > + size_t length) > +{ > + size_t left_data_length; > + int page_inx = offset / PAGE_SIZE; > + size_t processed_len = 0; > + size_t unordered = offset & (PAGE_SIZE - 1); > + > + if (unordered != 0) { //first > + size_t page_len = min_t(size_t, (PAGE_SIZE - unordered), length); > + > + left_data_length = copy_to_user(dst_user + processed_len, > + bbuff->pg[page_inx] + unordered, page_len); > + if (left_data_length != 0) { > + pr_err("Failed to copy data from big_buffer to user buffer\n"); > + return processed_len; > + } > + > + ++page_inx; > + processed_len += page_len; > + } > + > + while ((processed_len < length) && (page_inx < bbuff->pg_cnt)) { > + size_t page_len = min_t(size_t, PAGE_SIZE, (length - processed_len)); > + > + left_data_length = > + copy_to_user(dst_user + processed_len, bbuff->pg[page_inx], page_len); > + if (left_data_length != 0) { > + pr_err("Failed to copy data from big_buffer to user buffer\n"); > + break; > + } > + > + ++page_inx; > + processed_len += page_len; > + } > + > + return processed_len; > +} > + > +size_t big_buffer_copy_from_user(const char __user *src_user, size_t offset, > + struct big_buffer *bbuff, size_t length) > +{ > + size_t left_data_length; > + int page_inx = offset / PAGE_SIZE; > + size_t processed_len = 0; > + size_t unordered = offset & (PAGE_SIZE - 1); > + > + if (unordered != 0) { //first > + size_t page_len = min_t(size_t, (PAGE_SIZE - unordered), length); > + > + left_data_length = copy_from_user(bbuff->pg[page_inx] + unordered, > + src_user + processed_len, page_len); > + if (left_data_length != 0) { > + pr_err("Failed to copy data from user buffer to big_buffer\n"); > + return processed_len; > + } > + > + ++page_inx; > + processed_len += page_len; > + } > + > + while ((processed_len < length) && (page_inx < bbuff->pg_cnt)) { > + size_t page_len = min_t(size_t, PAGE_SIZE, (length - processed_len)); > + > + left_data_length = > + copy_from_user(bbuff->pg[page_inx], src_user + processed_len, page_len); > + if (left_data_length != 0) { > + pr_err("Failed to copy data from user buffer to big_buffer\n"); > + break; > + } > + > + ++page_inx; > + processed_len += page_len; > + } > + > + return processed_len; > +} > + > +void *big_buffer_get_element(struct big_buffer *bbuff, size_t index, size_t sizeof_element) > +{ > + size_t elements_in_page = PAGE_SIZE / sizeof_element; > + size_t pg_inx = index / elements_in_page; > + size_t pg_ofs = (index - (pg_inx * elements_in_page)) * sizeof_element; > + > + if (pg_inx >= bbuff->pg_cnt) > + return NULL; > + > + return bbuff->pg[pg_inx] + pg_ofs; > +} > + > +void big_buffer_memset(struct big_buffer *bbuff, int value) > +{ > + size_t inx; > + > + for (inx = 0; inx < bbuff->pg_cnt; ++inx) > + memset(bbuff->pg[inx], value, PAGE_SIZE); > +} > + > +void big_buffer_memcpy(struct big_buffer *dst, struct big_buffer *src) > +{ > + size_t inx; > + size_t count = min_t(size_t, dst->pg_cnt, src->pg_cnt); > + > + for (inx = 0; inx < count; ++inx) > + memcpy(dst->pg[inx], src->pg[inx], PAGE_SIZE); > +} > + > +int big_buffer_byte_get(struct big_buffer *bbuff, size_t inx, u8 *value) > +{ > + size_t page_inx = inx >> PAGE_SHIFT; > + size_t byte_pos = inx & (PAGE_SIZE - 1); > + > + if (page_inx >= bbuff->pg_cnt) > + return -ENODATA; > + > + *value = bbuff->pg[page_inx][byte_pos]; > + > + return SUCCESS; > +} > + > +int big_buffer_byte_set(struct big_buffer *bbuff, size_t inx, u8 value) > +{ > + size_t page_inx = inx >> PAGE_SHIFT; > + size_t byte_pos = inx & (PAGE_SIZE - 1); > + > + if (page_inx >= bbuff->pg_cnt) > + return -ENODATA; > + > + bbuff->pg[page_inx][byte_pos] = value; > + > + return SUCCESS; > +} > diff --git a/drivers/block/blk-snap/big_buffer.h b/drivers/block/blk-snap/big_buffer.h > new file mode 100644 > index 000000000000..f38ab5288b05 > --- /dev/null > +++ b/drivers/block/blk-snap/big_buffer.h > @@ -0,0 +1,24 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +struct big_buffer { > + size_t pg_cnt; > + u8 *pg[0]; > +}; > + > +struct big_buffer *big_buffer_alloc(size_t count, int gfp_opt); > +void big_buffer_free(struct big_buffer *bbuff); > + > +size_t big_buffer_copy_to_user(char __user *dst_user_buffer, size_t offset, > + struct big_buffer *bbuff, size_t length); > +size_t big_buffer_copy_from_user(const char __user *src_user_buffer, size_t offset, > + struct big_buffer *bbuff, size_t length); > + > +void *big_buffer_get_element(struct big_buffer *bbuff, size_t index, size_t sizeof_element); > + > +void big_buffer_memset(struct big_buffer *bbuff, int value); > +void big_buffer_memcpy(struct big_buffer *dst, struct big_buffer *src); > + > +//byte access > +int big_buffer_byte_get(struct big_buffer *bbuff, size_t inx, u8 *value); > +int big_buffer_byte_set(struct big_buffer *bbuff, size_t inx, u8 value); > diff --git a/drivers/block/blk-snap/blk-snap-ctl.h b/drivers/block/blk-snap/blk-snap-ctl.h > new file mode 100644 > index 000000000000..4ffd836836b1 > --- /dev/null > +++ b/drivers/block/blk-snap/blk-snap-ctl.h > @@ -0,0 +1,190 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#define MODULE_NAME "blk-snap" > +#define SNAP_IMAGE_NAME "blk-snap-image" > + > +#define SUCCESS 0 > + > +#define MAX_TRACKING_DEVICE_COUNT 256 > + > +#define BLK_SNAP 'V' > + > +#pragma pack(push, 1) > +////////////////////////////////////////////////////////////////////////// > +// version > + > +#define BLK_SNAP_COMPATIBILITY_SNAPSTORE 0x0000000000000001ull /* rudiment */ > +//#define BLK_SNAP_COMPATIBILITY_BTRFS 0x0000000000000002ull /* rudiment */ > +#define BLK_SNAP_COMPATIBILITY_MULTIDEV 0x0000000000000004ull > + > +struct ioctl_compatibility_flags_s { > + unsigned long long flags; > +}; > +#define IOCTL_COMPATIBILITY_FLAGS _IOW(BLK_SNAP, 0, struct ioctl_compatibility_flags_s) > + > +struct ioctl_getversion_s { > + unsigned short major; > + unsigned short minor; > + unsigned short revision; > + unsigned short build; > +}; > +#define IOCTL_GETVERSION _IOW(BLK_SNAP, 1, struct ioctl_getversion_s) > + > +////////////////////////////////////////////////////////////////////////// > +// tracking > +struct ioctl_dev_id_s { > + int major; > + int minor; > +}; > +#define IOCTL_TRACKING_ADD _IOW(BLK_SNAP, 2, struct ioctl_dev_id_s) > + > +#define IOCTL_TRACKING_REMOVE _IOW(BLK_SNAP, 3, struct ioctl_dev_id_s) > + > +struct cbt_info_s { > + struct ioctl_dev_id_s dev_id; > + unsigned long long dev_capacity; > + unsigned int cbt_map_size; > + unsigned char snap_number; > + unsigned char generationId[16]; > +}; > +struct ioctl_tracking_collect_s { > + unsigned int count; > + union { > + struct cbt_info_s *p_cbt_info; > + unsigned long long ull_cbt_info; > + }; > +}; > +#define IOCTL_TRACKING_COLLECT _IOW(BLK_SNAP, 4, struct ioctl_tracking_collect_s) > + > +#define IOCTL_TRACKING_BLOCK_SIZE _IOW(BLK_SNAP, 5, unsigned int) > + > +struct ioctl_tracking_read_cbt_bitmap_s { > + struct ioctl_dev_id_s dev_id; > + unsigned int offset; > + unsigned int length; > + union { > + unsigned char *buff; > + unsigned long long ull_buff; > + }; > +}; > +#define IOCTL_TRACKING_READ_CBT_BITMAP _IOR(BLK_SNAP, 6, struct ioctl_tracking_read_cbt_bitmap_s) > + > +struct block_range_s { > + unsigned long long ofs; //sectors > + unsigned long long cnt; //sectors > +}; > + > +struct ioctl_tracking_mark_dirty_blocks_s { > + struct ioctl_dev_id_s image_dev_id; > + unsigned int count; > + union { > + struct block_range_s *p_dirty_blocks; > + unsigned long long ull_dirty_blocks; > + }; > +}; > +#define IOCTL_TRACKING_MARK_DIRTY_BLOCKS \ > + _IOR(BLK_SNAP, 7, struct ioctl_tracking_mark_dirty_blocks_s) > +////////////////////////////////////////////////////////////////////////// > +// snapshot > + > +struct ioctl_snapshot_create_s { > + unsigned long long snapshot_id; > + unsigned int count; > + union { > + struct ioctl_dev_id_s *p_dev_id; > + unsigned long long ull_dev_id; > + }; > +}; > +#define IOCTL_SNAPSHOT_CREATE _IOW(BLK_SNAP, 0x10, struct ioctl_snapshot_create_s) > + > +#define IOCTL_SNAPSHOT_DESTROY _IOR(BLK_SNAP, 0x11, unsigned long long) > + > +struct ioctl_snapshot_errno_s { > + struct ioctl_dev_id_s dev_id; > + int err_code; > +}; > +#define IOCTL_SNAPSHOT_ERRNO _IOW(BLK_SNAP, 0x12, struct ioctl_snapshot_errno_s) > + > +struct ioctl_range_s { > + unsigned long long left; > + unsigned long long right; > +}; > + > +////////////////////////////////////////////////////////////////////////// > +// snapstore > +struct ioctl_snapstore_create_s { > + unsigned char id[16]; > + struct ioctl_dev_id_s snapstore_dev_id; > + unsigned int count; > + union { > + struct ioctl_dev_id_s *p_dev_id; > + unsigned long long ull_dev_id; > + }; > +}; > +#define IOCTL_SNAPSTORE_CREATE _IOR(BLK_SNAP, 0x28, struct ioctl_snapstore_create_s) > + > +struct ioctl_snapstore_file_add_s { > + unsigned char id[16]; > + unsigned int range_count; > + union { > + struct ioctl_range_s *ranges; > + unsigned long long ull_ranges; > + }; > +}; > +#define IOCTL_SNAPSTORE_FILE _IOR(BLK_SNAP, 0x29, struct ioctl_snapstore_file_add_s) > + > +struct ioctl_snapstore_memory_limit_s { > + unsigned char id[16]; > + unsigned long long size; > +}; > +#define IOCTL_SNAPSTORE_MEMORY _IOR(BLK_SNAP, 0x2A, struct ioctl_snapstore_memory_limit_s) > + > +struct ioctl_snapstore_cleanup_s { > + unsigned char id[16]; > + unsigned long long filled_bytes; > +}; > +#define IOCTL_SNAPSTORE_CLEANUP _IOW(BLK_SNAP, 0x2B, struct ioctl_snapstore_cleanup_s) > + > +struct ioctl_snapstore_file_add_multidev_s { > + unsigned char id[16]; > + struct ioctl_dev_id_s dev_id; > + unsigned int range_count; > + union { > + struct ioctl_range_s *ranges; > + unsigned long long ull_ranges; > + }; > +}; > +#define IOCTL_SNAPSTORE_FILE_MULTIDEV \ > + _IOR(BLK_SNAP, 0x2C, struct ioctl_snapstore_file_add_multidev_s) > +////////////////////////////////////////////////////////////////////////// > +// collect snapshot images > + > +struct image_info_s { > + struct ioctl_dev_id_s original_dev_id; > + struct ioctl_dev_id_s snapshot_dev_id; > +}; > + > +struct ioctl_collect_snapshot_images_s { > + int count; // > + union { > + struct image_info_s *p_image_info; > + unsigned long long ull_image_info; > + }; > +}; > +#define IOCTL_COLLECT_SNAPSHOT_IMAGES _IOW(BLK_SNAP, 0x30, struct ioctl_collect_snapshot_images_s) > + > +#pragma pack(pop) > + > +// commands for character device interface > +#define BLK_SNAP_CHARCMD_UNDEFINED 0x00 > +#define BLK_SNAP_CHARCMD_ACKNOWLEDGE 0x01 > +#define BLK_SNAP_CHARCMD_INVALID 0xFF > +// to module commands > +#define BLK_SNAP_CHARCMD_INITIATE 0x21 > +#define BLK_SNAP_CHARCMD_NEXT_PORTION 0x22 > +#define BLK_SNAP_CHARCMD_NEXT_PORTION_MULTIDEV 0x23 > +// from module commands > +#define BLK_SNAP_CHARCMD_HALFFILL 0x41 > +#define BLK_SNAP_CHARCMD_OVERFLOW 0x42 > +#define BLK_SNAP_CHARCMD_TERMINATE 0x43 > diff --git a/drivers/block/blk-snap/blk_deferred.c b/drivers/block/blk-snap/blk_deferred.c > new file mode 100644 > index 000000000000..1d0b7d2c4d71 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_deferred.c > @@ -0,0 +1,566 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-deferred" > +#include "common.h" > + > +#include "blk_deferred.h" > +#include "blk_util.h" > +#include "snapstore.h" > +#include "params.h" > + > +struct bio_set blk_deferred_bioset = { 0 }; > + > +struct dio_bio_complete { > + struct blk_deferred_request *dio_req; > + sector_t bio_sect_len; > +}; > + > +struct dio_deadlocked_list { > + struct list_head link; > + > + struct blk_deferred_request *dio_req; > +}; > + > +LIST_HEAD(dio_deadlocked_list); > +DEFINE_RWLOCK(dio_deadlocked_list_lock); > + > +atomic64_t dio_alloc_count = ATOMIC64_INIT(0); > +atomic64_t dio_free_count = ATOMIC64_INIT(0); > + > +void blk_deferred_done(void) > +{ > + struct dio_deadlocked_list *dio_locked; > + > + do { > + dio_locked = NULL; > + > + write_lock(&dio_deadlocked_list_lock); > + if (!list_empty(&dio_deadlocked_list)) { > + dio_locked = list_entry(dio_deadlocked_list.next, > + struct dio_deadlocked_list, link); > + > + list_del(&dio_locked->link); > + } > + write_unlock(&dio_deadlocked_list_lock); > + > + if (dio_locked) { > + if (dio_locked->dio_req->sect_len == > + atomic64_read(&dio_locked->dio_req->sect_processed)) > + blk_deferred_request_free(dio_locked->dio_req); > + else > + pr_err("Locked defer IO is still in memory\n"); > + > + kfree(dio_locked); > + } > + } while (dio_locked); > +} > + > +void blk_deferred_request_deadlocked(struct blk_deferred_request *dio_req) > +{ > + struct dio_deadlocked_list *dio_locked = > + kzalloc(sizeof(struct dio_deadlocked_list), GFP_KERNEL); > + > + dio_locked->dio_req = dio_req; > + > + write_lock(&dio_deadlocked_list_lock); > + list_add_tail(&dio_locked->link, &dio_deadlocked_list); > + write_unlock(&dio_deadlocked_list_lock); > + > + pr_warn("Deadlock with defer IO\n"); > +} > + > +void blk_deferred_free(struct blk_deferred_io *dio) > +{ > + size_t inx = 0; > + > + if (dio->page_array != NULL) { > + while (dio->page_array[inx] != NULL) { > + __free_page(dio->page_array[inx]); > + dio->page_array[inx] = NULL; > + > + ++inx; > + } > + > + kfree(dio->page_array); > + dio->page_array = NULL; > + } > + kfree(dio); > +} > + > +struct blk_deferred_io *blk_deferred_alloc(unsigned long block_index, > + union blk_descr_unify blk_descr) > +{ > + size_t inx; > + size_t page_count; > + struct blk_deferred_io *dio = kmalloc(sizeof(struct blk_deferred_io), GFP_NOIO); > + > + if (dio == NULL) > + return NULL; > + > + INIT_LIST_HEAD(&dio->link); > + > + dio->blk_descr = blk_descr; > + dio->blk_index = block_index; > + > + dio->sect.ofs = block_index << snapstore_block_shift(); > + dio->sect.cnt = snapstore_block_size(); > + > + page_count = snapstore_block_size() / (PAGE_SIZE / SECTOR_SIZE); > + /* > + * empty pointer on the end > + */ > + dio->page_array = kzalloc((page_count + 1) * sizeof(struct page *), GFP_NOIO); > + if (dio->page_array == NULL) { > + blk_deferred_free(dio); > + return NULL; > + } > + > + for (inx = 0; inx < page_count; inx++) { > + dio->page_array[inx] = alloc_page(GFP_NOIO); > + if (dio->page_array[inx] == NULL) { > + pr_err("Failed to allocate page\n"); > + blk_deferred_free(dio); > + return NULL; > + } > + } > + > + return dio; > +} > + > +int blk_deferred_bioset_create(void) > +{ > + return bioset_init(&blk_deferred_bioset, 64, sizeof(struct dio_bio_complete), > + BIOSET_NEED_BVECS | BIOSET_NEED_RESCUER); > +} > + > +void blk_deferred_bioset_free(void) > +{ > + bioset_exit(&blk_deferred_bioset); > +} > + > +struct bio *_blk_deferred_bio_alloc(int nr_iovecs) > +{ > + struct bio *new_bio = bio_alloc_bioset(GFP_NOIO, nr_iovecs, &blk_deferred_bioset); > + > + if (new_bio == NULL) > + return NULL; > + > + new_bio->bi_end_io = blk_deferred_bio_endio; > + new_bio->bi_private = ((void *)new_bio) - sizeof(struct dio_bio_complete); > + > + return new_bio; > +} > + > +static void blk_deferred_complete(struct blk_deferred_request *dio_req, sector_t portion_sect_cnt, > + int result) > +{ > + atomic64_add(portion_sect_cnt, &dio_req->sect_processed); > + > + if (dio_req->sect_len == atomic64_read(&dio_req->sect_processed)) > + complete(&dio_req->complete); > + > + if (result != SUCCESS) { > + dio_req->result = result; > + pr_err("Failed to process defer IO request. errno=%d\n", result); > + } > +} > + > +void blk_deferred_bio_endio(struct bio *bio) > +{ > + int local_err; > + struct dio_bio_complete *complete_param = (struct dio_bio_complete *)bio->bi_private; > + > + if (complete_param == NULL) { > + //bio already complete > + } else { > + if (bio->bi_status != BLK_STS_OK) > + local_err = -EIO; > + else > + local_err = SUCCESS; > + > + blk_deferred_complete(complete_param->dio_req, complete_param->bio_sect_len, > + local_err); > + bio->bi_private = NULL; > + } > + > + bio_put(bio); > +} > + > +static inline size_t _page_count_calculate(sector_t size_sector) > +{ > + size_t page_count = size_sector / (PAGE_SIZE / SECTOR_SIZE); > + > + if (unlikely(size_sector & ((PAGE_SIZE / SECTOR_SIZE) - 1))) > + page_count += 1; > + > + return page_count; > +} > + > +sector_t _blk_deferred_submit_pages(struct block_device *blk_dev, > + struct blk_deferred_request *dio_req, int direction, > + sector_t arr_ofs, struct page **page_array, sector_t ofs_sector, > + sector_t size_sector) > +{ > + struct bio *bio = NULL; > + int nr_iovecs; > + int page_inx = arr_ofs >> (PAGE_SHIFT - SECTOR_SHIFT); > + sector_t process_sect = 0; > + > + nr_iovecs = _page_count_calculate(size_sector); > + > + while (NULL == (bio = _blk_deferred_bio_alloc(nr_iovecs))) { > + size_sector = (size_sector >> 1) & ~((PAGE_SIZE / SECTOR_SIZE) - 1); > + if (size_sector == 0) > + return 0; > + > + nr_iovecs = _page_count_calculate(size_sector); > + } > + > + bio_set_dev(bio, blk_dev); > + > + if (direction == READ) > + bio_set_op_attrs(bio, REQ_OP_READ, 0); > + else > + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); > + > + bio->bi_iter.bi_sector = ofs_sector; > + > + { //add first > + sector_t unordered = arr_ofs & ((PAGE_SIZE / SECTOR_SIZE) - 1); > + sector_t bvec_len_sect = > + min_t(sector_t, ((PAGE_SIZE / SECTOR_SIZE) - unordered), size_sector); > + struct page *page = page_array[page_inx]; > + unsigned int len = (unsigned int)from_sectors(bvec_len_sect); > + unsigned int offset = (unsigned int)from_sectors(unordered); > + > + if (unlikely(page == NULL)) { > + pr_err("NULL found in page array"); > + bio_put(bio); > + return 0; > + } > + if (unlikely(bio_add_page(bio, page, len, offset) != len)) { > + bio_put(bio); > + return 0; > + } > + ++page_inx; > + process_sect += bvec_len_sect; > + } > + > + while (process_sect < size_sector) { > + sector_t bvec_len_sect = > + min_t(sector_t, (PAGE_SIZE / SECTOR_SIZE), (size_sector - process_sect)); > + struct page *page = page_array[page_inx]; > + unsigned int len = (unsigned int)from_sectors(bvec_len_sect); > + > + > + if (unlikely(page == NULL)) { > + pr_err("NULL found in page array"); > + break; > + } > + if (unlikely(bio_add_page(bio, page, len, 0) != len)) > + break; > + > + ++page_inx; > + process_sect += bvec_len_sect; > + } > + > + ((struct dio_bio_complete *)bio->bi_private)->dio_req = dio_req; > + ((struct dio_bio_complete *)bio->bi_private)->bio_sect_len = process_sect; > + > + submit_bio_direct(bio); > + > + return process_sect; > +} > + > +sector_t blk_deferred_submit_pages(struct block_device *blk_dev, > + struct blk_deferred_request *dio_req, int direction, > + sector_t arr_ofs, struct page **page_array, sector_t ofs_sector, > + sector_t size_sector) > +{ > + sector_t process_sect = 0; > + > + do { > + sector_t portion_sect = _blk_deferred_submit_pages( > + blk_dev, dio_req, direction, arr_ofs + process_sect, page_array, > + ofs_sector + process_sect, size_sector - process_sect); > + if (portion_sect == 0) { > + pr_err("Failed to submit defer IO pages. Only [%lld] sectors processed\n", > + process_sect); > + break; > + } > + process_sect += portion_sect; > + } while (process_sect < size_sector); > + > + return process_sect; > +} > + > +struct blk_deferred_request *blk_deferred_request_new(void) > +{ > + struct blk_deferred_request *dio_req = NULL; > + > + dio_req = kzalloc(sizeof(struct blk_deferred_request), GFP_NOIO); > + if (dio_req == NULL) > + return NULL; > + > + INIT_LIST_HEAD(&dio_req->dios); > + > + dio_req->result = SUCCESS; > + atomic64_set(&dio_req->sect_processed, 0); > + dio_req->sect_len = 0; > + init_completion(&dio_req->complete); > + > + return dio_req; > +} > + > +bool blk_deferred_request_already_added(struct blk_deferred_request *dio_req, > + unsigned long block_index) > +{ > + bool result = false; > + struct list_head *_list_head; > + > + if (list_empty(&dio_req->dios)) > + return result; > + > + list_for_each(_list_head, &dio_req->dios) { > + struct blk_deferred_io *dio = list_entry(_list_head, struct blk_deferred_io, link); > + > + if (dio->blk_index == block_index) { > + result = true; > + break; > + } > + } > + > + return result; > +} > + > +int blk_deferred_request_add(struct blk_deferred_request *dio_req, struct blk_deferred_io *dio) > +{ > + list_add_tail(&dio->link, &dio_req->dios); > + dio_req->sect_len += dio->sect.cnt; > + > + return SUCCESS; > +} > + > +void blk_deferred_request_free(struct blk_deferred_request *dio_req) > +{ > + if (dio_req != NULL) { > + while (!list_empty(&dio_req->dios)) { > + struct blk_deferred_io *dio = > + list_entry(dio_req->dios.next, struct blk_deferred_io, link); > + > + list_del(&dio->link); > + > + blk_deferred_free(dio); > + } > + kfree(dio_req); > + } > +} > + > +void blk_deferred_request_waiting_skip(struct blk_deferred_request *dio_req) > +{ > + init_completion(&dio_req->complete); > + atomic64_set(&dio_req->sect_processed, 0); > +} > + > +int blk_deferred_request_wait(struct blk_deferred_request *dio_req) > +{ > + u64 start_jiffies = get_jiffies_64(); > + u64 current_jiffies; > + > + while (wait_for_completion_timeout(&dio_req->complete, (HZ * 1)) == 0) { > + current_jiffies = get_jiffies_64(); > + if (jiffies_to_msecs(current_jiffies - start_jiffies) > 60 * 1000) { > + pr_warn("Defer IO request timeout\n"); > + return -EDEADLK; > + } > + } > + > + return dio_req->result; > +} > + > +int blk_deferred_request_read_original(struct block_device *original_blk_dev, > + struct blk_deferred_request *dio_copy_req) > +{ > + int res = -ENODATA; > + struct list_head *_list_head; > + > + blk_deferred_request_waiting_skip(dio_copy_req); > + > + if (list_empty(&dio_copy_req->dios)) > + return res; > + > + list_for_each(_list_head, &dio_copy_req->dios) { > + struct blk_deferred_io *dio = list_entry(_list_head, struct blk_deferred_io, link); > + > + sector_t ofs = dio->sect.ofs; > + sector_t cnt = dio->sect.cnt; > + > + if (cnt != blk_deferred_submit_pages(original_blk_dev, dio_copy_req, READ, 0, > + dio->page_array, ofs, cnt)) { > + pr_err("Failed to submit reading defer IO request. offset=%lld\n", > + dio->sect.ofs); > + res = -EIO; > + break; > + } > + res = SUCCESS; > + } > + > + if (res == SUCCESS) > + res = blk_deferred_request_wait(dio_copy_req); > + > + return res; > +} > + > + > +static int _store_file(struct block_device *blk_dev, struct blk_deferred_request *dio_copy_req, > + struct blk_descr_file *blk_descr, struct page **page_array) > +{ > + struct list_head *_rangelist_head; > + sector_t page_array_ofs = 0; > + > + if (unlikely(list_empty(&blk_descr->rangelist))) { > + pr_err("Invalid block descriptor"); > + return -EINVAL; > + } > + list_for_each(_rangelist_head, &blk_descr->rangelist) { > + struct blk_range_link *range_link; > + sector_t process_sect; > + > + range_link = list_entry(_rangelist_head, struct blk_range_link, link); > + process_sect = blk_deferred_submit_pages(blk_dev, dio_copy_req, WRITE, > + page_array_ofs, page_array, > + range_link->rg.ofs, range_link->rg.cnt); > + if (range_link->rg.cnt != process_sect) { > + pr_err("Failed to submit defer IO request for storing\n"); > + return -EIO; > + } > + page_array_ofs += range_link->rg.cnt; > + } > + return SUCCESS; > +} > + > +int blk_deferred_request_store_file(struct block_device *blk_dev, > + struct blk_deferred_request *dio_copy_req) > +{ > + struct list_head *_dio_list_head; > + > + blk_deferred_request_waiting_skip(dio_copy_req); > + > + if (unlikely(list_empty(&dio_copy_req->dios))) { > + pr_err("Invalid deferred io request"); > + return -EINVAL; > + } > + list_for_each(_dio_list_head, &dio_copy_req->dios) { > + int res; > + struct blk_deferred_io *dio; > + > + dio = list_entry(_dio_list_head, struct blk_deferred_io, link); > + res = _store_file(blk_dev, dio_copy_req, dio->blk_descr.file, dio->page_array); > + if (res != SUCCESS) > + return res; > + } > + > + return blk_deferred_request_wait(dio_copy_req); > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + > +static int _store_multidev(struct blk_deferred_request *dio_copy_req, > + struct blk_descr_multidev *blk_descr, struct page **page_array) > +{ > + struct list_head *_ranges_list_head; > + sector_t page_array_ofs = 0; > + > + if (unlikely(list_empty(&blk_descr->rangelist))) { > + pr_err("Invalid block descriptor"); > + return -EINVAL; > + } > + list_for_each(_ranges_list_head, &blk_descr->rangelist) { > + sector_t process_sect; > + struct blk_range_link_ex *range_link; > + > + range_link = list_entry(_ranges_list_head, struct blk_range_link_ex, link); > + process_sect = blk_deferred_submit_pages(range_link->blk_dev, dio_copy_req, WRITE, > + page_array_ofs, page_array, > + range_link->rg.ofs, range_link->rg.cnt); > + if (range_link->rg.cnt != process_sect) { > + pr_err("Failed to submit defer IO request for storing\n"); > + return -EIO; > + } > + > + page_array_ofs += range_link->rg.cnt; > + } > + > + return SUCCESS; > +} > + > +int blk_deferred_request_store_multidev(struct blk_deferred_request *dio_copy_req) > +{ > + struct list_head *_dio_list_head; > + > + blk_deferred_request_waiting_skip(dio_copy_req); > + > + if (unlikely(list_empty(&dio_copy_req->dios))) { > + pr_err("Invalid deferred io request"); > + return -EINVAL; > + } > + list_for_each(_dio_list_head, &dio_copy_req->dios) { > + int res; > + struct blk_deferred_io *dio; > + > + dio = list_entry(_dio_list_head, struct blk_deferred_io, link); > + res = _store_multidev(dio_copy_req, dio->blk_descr.multidev, dio->page_array); > + if (res != SUCCESS) > + return res; > + } > + > + return blk_deferred_request_wait(dio_copy_req); > +} > +#endif > + > +static size_t _store_pages(void *dst, struct page **page_array, size_t length) > +{ > + size_t page_inx = 0; > + size_t processed_len = 0; > + > + while ((processed_len < length) && (page_array[page_inx] != NULL)) { > + void *src; > + size_t page_len = min_t(size_t, PAGE_SIZE, (length - processed_len)); > + > + src = page_address(page_array[page_inx]); > + memcpy(dst + processed_len, src, page_len); > + > + ++page_inx; > + processed_len += page_len; > + } > + > + return processed_len; > +} > + > +int blk_deferred_request_store_mem(struct blk_deferred_request *dio_copy_req) > +{ > + int res = SUCCESS; > + sector_t processed = 0; > + > + if (!list_empty(&dio_copy_req->dios)) { > + struct list_head *_list_head; > + > + list_for_each(_list_head, &dio_copy_req->dios) { > + size_t length; > + size_t portion; > + struct blk_deferred_io *dio; > + > + dio = list_entry(_list_head, struct blk_deferred_io, link); > + length = snapstore_block_size() * SECTOR_SIZE; > + > + portion = _store_pages(dio->blk_descr.mem->buff, dio->page_array, length); > + if (unlikely(portion != length)) { > + res = -EIO; > + break; > + } > + processed += (sector_t)to_sectors(portion); > + } > + } > + > + blk_deferred_complete(dio_copy_req, processed, res); > + return res; > +} > diff --git a/drivers/block/blk-snap/blk_deferred.h b/drivers/block/blk-snap/blk_deferred.h > new file mode 100644 > index 000000000000..3c516a835c25 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_deferred.h > @@ -0,0 +1,67 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include "blk_descr_file.h" > +#include "blk_descr_mem.h" > +#include "blk_descr_multidev.h" > + > +#define DEFER_IO_DIO_REQUEST_LENGTH 250 > +#define DEFER_IO_DIO_REQUEST_SECTORS_COUNT (10 * 1024 * 1024 / SECTOR_SIZE) > + > +struct blk_deferred_io { > + struct list_head link; > + > + unsigned long blk_index; > + union blk_descr_unify blk_descr; > + > + struct blk_range sect; > + > + struct page **page_array; //null pointer on tail > +}; > + > +struct blk_deferred_request { > + struct completion complete; > + sector_t sect_len; > + atomic64_t sect_processed; > + int result; > + > + struct list_head dios; > +}; > + > +void blk_deferred_done(void); > + > +struct blk_deferred_io *blk_deferred_alloc(unsigned long block_index, > + union blk_descr_unify blk_descr); > +void blk_deferred_free(struct blk_deferred_io *dio); > + > +void blk_deferred_bio_endio(struct bio *bio); > + > +sector_t blk_deferred_submit_pages(struct block_device *blk_dev, > + struct blk_deferred_request *dio_req, int direction, > + sector_t arr_ofs, struct page **page_array, sector_t ofs_sector, > + sector_t size_sector); > + > +struct blk_deferred_request *blk_deferred_request_new(void); > + > +bool blk_deferred_request_already_added(struct blk_deferred_request *dio_req, > + unsigned long block_index); > + > +int blk_deferred_request_add(struct blk_deferred_request *dio_req, struct blk_deferred_io *dio); > +void blk_deferred_request_free(struct blk_deferred_request *dio_req); > +void blk_deferred_request_deadlocked(struct blk_deferred_request *dio_req); > + > +void blk_deferred_request_waiting_skip(struct blk_deferred_request *dio_req); > +int blk_deferred_request_wait(struct blk_deferred_request *dio_req); > + > +int blk_deferred_bioset_create(void); > +void blk_deferred_bioset_free(void); > + > +int blk_deferred_request_read_original(struct block_device *original_blk_dev, > + struct blk_deferred_request *dio_copy_req); > + > +int blk_deferred_request_store_file(struct block_device *blk_dev, > + struct blk_deferred_request *dio_copy_req); > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +int blk_deferred_request_store_multidev(struct blk_deferred_request *dio_copy_req); > +#endif > +int blk_deferred_request_store_mem(struct blk_deferred_request *dio_copy_req); > diff --git a/drivers/block/blk-snap/blk_descr_file.c b/drivers/block/blk-snap/blk_descr_file.c > new file mode 100644 > index 000000000000..fca298d35744 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_file.c > @@ -0,0 +1,82 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-blk_descr" > +#include "common.h" > + > +#include "blk_descr_file.h" > + > +static inline void list_assign(struct list_head *dst, struct list_head *src) > +{ > + dst->next = src->next; > + dst->prev = src->prev; > + > + src->next->prev = dst; > + src->prev->next = dst; > +} > + > +static inline void blk_descr_file_init(struct blk_descr_file *blk_descr, > + struct list_head *rangelist) > +{ > + list_assign(&blk_descr->rangelist, rangelist); > +} > + > +static inline void blk_descr_file_done(struct blk_descr_file *blk_descr) > +{ > + struct blk_range_link *range_link; > + > + while (!list_empty(&blk_descr->rangelist)) { > + range_link = list_entry(blk_descr->rangelist.next, struct blk_range_link, link); > + > + list_del(&range_link->link); > + kfree(range_link); > + } > +} > + > +void blk_descr_file_pool_init(struct blk_descr_pool *pool) > +{ > + blk_descr_pool_init(pool, 0); > +} > + > +void _blk_descr_file_cleanup(void *descr_array, size_t count) > +{ > + size_t inx; > + struct blk_descr_file *file_blocks = descr_array; > + > + for (inx = 0; inx < count; ++inx) > + blk_descr_file_done(file_blocks + inx); > +} > + > +void blk_descr_file_pool_done(struct blk_descr_pool *pool) > +{ > + blk_descr_pool_done(pool, _blk_descr_file_cleanup); > +} > + > +static union blk_descr_unify _blk_descr_file_allocate(void *descr_array, size_t index, void *arg) > +{ > + union blk_descr_unify blk_descr; > + struct blk_descr_file *file_blocks = descr_array; > + > + blk_descr.file = &file_blocks[index]; > + > + blk_descr_file_init(blk_descr.file, (struct list_head *)arg); > + > + return blk_descr; > +} > + > +int blk_descr_file_pool_add(struct blk_descr_pool *pool, struct list_head *rangelist) > +{ > + union blk_descr_unify blk_descr; > + > + blk_descr = blk_descr_pool_alloc(pool, sizeof(struct blk_descr_file), > + _blk_descr_file_allocate, (void *)rangelist); > + if (blk_descr.ptr == NULL) { > + pr_err("Failed to allocate block descriptor\n"); > + return -ENOMEM; > + } > + > + return SUCCESS; > +} > + > +union blk_descr_unify blk_descr_file_pool_take(struct blk_descr_pool *pool) > +{ > + return blk_descr_pool_take(pool, sizeof(struct blk_descr_file)); > +} > diff --git a/drivers/block/blk-snap/blk_descr_file.h b/drivers/block/blk-snap/blk_descr_file.h > new file mode 100644 > index 000000000000..0e9a5e3efdea > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_file.h > @@ -0,0 +1,26 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include "blk_descr_pool.h" > + > +struct blk_descr_file { > + struct list_head rangelist; > +}; > + > +struct blk_range_link { > + struct list_head link; > + struct blk_range rg; > +}; > + > +void blk_descr_file_pool_init(struct blk_descr_pool *pool); > +void blk_descr_file_pool_done(struct blk_descr_pool *pool); > + > +/* > + * allocate new empty block in pool > + */ > +int blk_descr_file_pool_add(struct blk_descr_pool *pool, struct list_head *rangelist); > + > +/* > + * take empty block from pool > + */ > +union blk_descr_unify blk_descr_file_pool_take(struct blk_descr_pool *pool); > diff --git a/drivers/block/blk-snap/blk_descr_mem.c b/drivers/block/blk-snap/blk_descr_mem.c > new file mode 100644 > index 000000000000..cd326ac150b6 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_mem.c > @@ -0,0 +1,66 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-blk_descr" > +#include "common.h" > +#include "blk_descr_mem.h" > + > +#define SECTION "blk_descr " > + > +static inline void blk_descr_mem_init(struct blk_descr_mem *blk_descr, void *ptr) > +{ > + blk_descr->buff = ptr; > +} > + > +static inline void blk_descr_mem_done(struct blk_descr_mem *blk_descr) > +{ > + blk_descr->buff = NULL; > +} > + > +void blk_descr_mem_pool_init(struct blk_descr_pool *pool, size_t available_blocks) > +{ > + blk_descr_pool_init(pool, available_blocks); > +} > + > +void blk_descr_mem_cleanup(void *descr_array, size_t count) > +{ > + size_t inx; > + struct blk_descr_mem *mem_blocks = descr_array; > + > + for (inx = 0; inx < count; ++inx) > + blk_descr_mem_done(mem_blocks + inx); > +} > + > +void blk_descr_mem_pool_done(struct blk_descr_pool *pool) > +{ > + blk_descr_pool_done(pool, blk_descr_mem_cleanup); > +} > + > +static union blk_descr_unify blk_descr_mem_alloc(void *descr_array, size_t index, void *arg) > +{ > + union blk_descr_unify blk_descr; > + struct blk_descr_mem *mem_blocks = descr_array; > + > + blk_descr.mem = &mem_blocks[index]; > + > + blk_descr_mem_init(blk_descr.mem, (void *)arg); > + > + return blk_descr; > +} > + > +int blk_descr_mem_pool_add(struct blk_descr_pool *pool, void *buffer) > +{ > + union blk_descr_unify blk_descr; > + > + blk_descr = blk_descr_pool_alloc(pool, sizeof(struct blk_descr_mem), > + blk_descr_mem_alloc, buffer); > + if (blk_descr.ptr == NULL) { > + pr_err("Failed to allocate block descriptor\n"); > + return -ENOMEM; > + } > + > + return SUCCESS; > +} > + > +union blk_descr_unify blk_descr_mem_pool_take(struct blk_descr_pool *pool) > +{ > + return blk_descr_pool_take(pool, sizeof(struct blk_descr_mem)); > +} > diff --git a/drivers/block/blk-snap/blk_descr_mem.h b/drivers/block/blk-snap/blk_descr_mem.h > new file mode 100644 > index 000000000000..43e8de76c07c > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_mem.h > @@ -0,0 +1,14 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include "blk_descr_pool.h" > + > +struct blk_descr_mem { > + void *buff; //pointer to snapstore block in memory > +}; > + > +void blk_descr_mem_pool_init(struct blk_descr_pool *pool, size_t available_blocks); > +void blk_descr_mem_pool_done(struct blk_descr_pool *pool); > + > +int blk_descr_mem_pool_add(struct blk_descr_pool *pool, void *buffer); > +union blk_descr_unify blk_descr_mem_pool_take(struct blk_descr_pool *pool); > diff --git a/drivers/block/blk-snap/blk_descr_multidev.c b/drivers/block/blk-snap/blk_descr_multidev.c > new file mode 100644 > index 000000000000..cf5e0ed6f781 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_multidev.c > @@ -0,0 +1,86 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-blk_descr" > +#include "common.h" > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +#include "blk_descr_multidev.h" > + > +static inline void list_assign(struct list_head *dst, struct list_head *src) > +{ > + dst->next = src->next; > + dst->prev = src->prev; > + > + src->next->prev = dst; > + src->prev->next = dst; > +} > + > +static inline void blk_descr_multidev_init(struct blk_descr_multidev *blk_descr, > + struct list_head *rangelist) > +{ > + list_assign(&blk_descr->rangelist, rangelist); > +} > + > +static inline void blk_descr_multidev_done(struct blk_descr_multidev *blk_descr) > +{ > + struct blk_range_link_ex *rangelist; > + > + while (!list_empty(&blk_descr->rangelist)) { > + rangelist = list_entry(blk_descr->rangelist.next, > + struct blk_range_link_ex, link); > + > + list_del(&rangelist->link); > + kfree(rangelist); > + } > +} > + > +void blk_descr_multidev_pool_init(struct blk_descr_pool *pool) > +{ > + blk_descr_pool_init(pool, 0); > +} > + > +static void blk_descr_multidev_cleanup(void *descr_array, size_t count) > +{ > + size_t inx; > + struct blk_descr_multidev *descr_multidev = descr_array; > + > + for (inx = 0; inx < count; ++inx) > + blk_descr_multidev_done(descr_multidev + inx); > +} > + > +void blk_descr_multidev_pool_done(struct blk_descr_pool *pool) > +{ > + blk_descr_pool_done(pool, blk_descr_multidev_cleanup); > +} > + > +static union blk_descr_unify blk_descr_multidev_allocate(void *descr_array, size_t index, void *arg) > +{ > + union blk_descr_unify blk_descr; > + struct blk_descr_multidev *multidev_blocks = descr_array; > + > + blk_descr.multidev = &multidev_blocks[index]; > + > + blk_descr_multidev_init(blk_descr.multidev, (struct list_head *)arg); > + > + return blk_descr; > +} > + > +int blk_descr_multidev_pool_add(struct blk_descr_pool *pool, struct list_head *rangelist) > +{ > + union blk_descr_unify blk_descr; > + > + blk_descr = blk_descr_pool_alloc(pool, sizeof(struct blk_descr_multidev), > + blk_descr_multidev_allocate, (void *)rangelist); > + if (blk_descr.ptr == NULL) { > + pr_err("Failed to allocate block descriptor\n"); > + return -ENOMEM; > + } > + > + return SUCCESS; > +} > + > +union blk_descr_unify blk_descr_multidev_pool_take(struct blk_descr_pool *pool) > +{ > + return blk_descr_pool_take(pool, sizeof(struct blk_descr_multidev)); > +} > + > +#endif //CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > diff --git a/drivers/block/blk-snap/blk_descr_multidev.h b/drivers/block/blk-snap/blk_descr_multidev.h > new file mode 100644 > index 000000000000..0145b0d78b10 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_multidev.h > @@ -0,0 +1,25 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + > +#include "blk_descr_pool.h" > + > +struct blk_descr_multidev { > + struct list_head rangelist; > +}; > + > +struct blk_range_link_ex { > + struct list_head link; > + struct blk_range rg; > + struct block_device *blk_dev; > +}; > + > +void blk_descr_multidev_pool_init(struct blk_descr_pool *pool); > +void blk_descr_multidev_pool_done(struct blk_descr_pool *pool); > + > +int blk_descr_multidev_pool_add(struct blk_descr_pool *pool, > + struct list_head *rangelist); //allocate new empty block > +union blk_descr_unify blk_descr_multidev_pool_take(struct blk_descr_pool *pool); //take empty > + > +#endif //CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > diff --git a/drivers/block/blk-snap/blk_descr_pool.c b/drivers/block/blk-snap/blk_descr_pool.c > new file mode 100644 > index 000000000000..b1fe2ba9c2d0 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_pool.c > @@ -0,0 +1,190 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-blk_descr" > +#include "common.h" > +#include "blk_descr_pool.h" > +#include "params.h" > + > +struct pool_el { > + struct list_head link; > + > + size_t used_cnt; // used blocks > + size_t capacity; // blocks array capacity > + > + u8 descr_array[0]; > +}; > + > +static void *kmalloc_huge(size_t max_size, size_t min_size, gfp_t flags, size_t *p_allocated_size) > +{ > + void *ptr = NULL; > + > + do { > + ptr = kmalloc(max_size, flags | __GFP_NOWARN | __GFP_RETRY_MAYFAIL); > + > + if (ptr != NULL) { > + *p_allocated_size = max_size; > + return ptr; > + } > + pr_err("Failed to allocate buffer size=%zu\n", max_size); > + max_size = max_size >> 1; > + } while (max_size >= min_size); > + > + pr_err("Failed to allocate buffer."); > + return NULL; > +} > + > +static struct pool_el *pool_el_alloc(size_t blk_descr_size) > +{ > + size_t el_size; > + struct pool_el *el; > + > + el = kmalloc_huge(8 * PAGE_SIZE, PAGE_SIZE, GFP_NOIO, &el_size); > + if (el == NULL) > + return NULL; > + > + el->capacity = (el_size - sizeof(struct pool_el)) / blk_descr_size; > + el->used_cnt = 0; > + > + INIT_LIST_HEAD(&el->link); > + > + return el; > +} > + > +static void _pool_el_free(struct pool_el *el) > +{ > + if (el != NULL) > + kfree(el); > +} > + > +void blk_descr_pool_init(struct blk_descr_pool *pool, size_t available_blocks) > +{ > + mutex_init(&pool->lock); > + > + INIT_LIST_HEAD(&pool->head); > + > + pool->blocks_cnt = 0; > + > + pool->total_cnt = available_blocks; > + pool->take_cnt = 0; > +} > + > +void blk_descr_pool_done(struct blk_descr_pool *pool, > + void (*blocks_cleanup_cb)(void *descr_array, size_t count)) > +{ > + mutex_lock(&pool->lock); > + while (!list_empty(&pool->head)) { > + struct pool_el *el; > + > + el = list_entry(pool->head.next, struct pool_el, link); > + if (el == NULL) > + break; > + > + list_del(&el->link); > + --pool->blocks_cnt; > + > + pool->total_cnt -= el->used_cnt; > + > + blocks_cleanup_cb(el->descr_array, el->used_cnt); > + > + _pool_el_free(el); > + } > + mutex_unlock(&pool->lock); > +} > + > +union blk_descr_unify blk_descr_pool_alloc( > + struct blk_descr_pool *pool, size_t blk_descr_size, > + union blk_descr_unify (*block_alloc_cb)(void *descr_array, size_t index, void *arg), > + void *arg) > +{ > + union blk_descr_unify blk_descr = { NULL }; > + > + mutex_lock(&pool->lock); > + do { > + struct pool_el *el = NULL; > + > + if (!list_empty(&pool->head)) { > + el = list_entry(pool->head.prev, struct pool_el, link); > + if (el->used_cnt == el->capacity) > + el = NULL; > + } > + > + if (el == NULL) { > + el = pool_el_alloc(blk_descr_size); > + if (el == NULL) > + break; > + > + list_add_tail(&el->link, &pool->head); > + > + ++pool->blocks_cnt; > + } > + > + blk_descr = block_alloc_cb(el->descr_array, el->used_cnt, arg); > + > + ++el->used_cnt; > + ++pool->total_cnt; > + > + } while (false); > + mutex_unlock(&pool->lock); > + > + return blk_descr; > +} > + > +static union blk_descr_unify __blk_descr_pool_at(struct blk_descr_pool *pool, size_t blk_descr_size, > + size_t index) > +{ > + union blk_descr_unify bkl_descr = { NULL }; > + size_t curr_inx = 0; > + struct pool_el *el; > + struct list_head *_list_head; > + > + if (list_empty(&(pool)->head)) > + return bkl_descr; > + > + list_for_each(_list_head, &(pool)->head) { > + el = list_entry(_list_head, struct pool_el, link); > + > + if ((index >= curr_inx) && (index < (curr_inx + el->used_cnt))) { > + bkl_descr.ptr = el->descr_array + (index - curr_inx) * blk_descr_size; > + break; > + } > + curr_inx += el->used_cnt; > + } > + > + return bkl_descr; > +} > + > +union blk_descr_unify blk_descr_pool_take(struct blk_descr_pool *pool, size_t blk_descr_size) > +{ > + union blk_descr_unify result = { NULL }; > + > + mutex_lock(&pool->lock); > + do { > + if (pool->take_cnt >= pool->total_cnt) { > + pr_err("Unable to get block descriptor: "); > + pr_err("not enough descriptors, already took %zu, total %zu\n", > + pool->take_cnt, pool->total_cnt); > + break; > + } > + > + result = __blk_descr_pool_at(pool, blk_descr_size, pool->take_cnt); > + if (result.ptr == NULL) { > + pr_err("Unable to get block descriptor: "); > + pr_err("not enough descriptors, already took %zu, total %zu\n", > + pool->take_cnt, pool->total_cnt); > + break; > + } > + > + ++pool->take_cnt; > + } while (false); > + mutex_unlock(&pool->lock); > + return result; > +} > + > +bool blk_descr_pool_check_halffill(struct blk_descr_pool *pool, sector_t empty_limit, > + sector_t *fill_status) > +{ > + size_t empty_blocks = (pool->total_cnt - pool->take_cnt); > + > + *fill_status = (sector_t)(pool->take_cnt) << snapstore_block_shift(); > + > + return (empty_blocks < (size_t)(empty_limit >> snapstore_block_shift())); > +} > diff --git a/drivers/block/blk-snap/blk_descr_pool.h b/drivers/block/blk-snap/blk_descr_pool.h > new file mode 100644 > index 000000000000..32f8b8c4103e > --- /dev/null > +++ b/drivers/block/blk-snap/blk_descr_pool.h > @@ -0,0 +1,38 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +struct blk_descr_mem; > +struct blk_descr_file; > +struct blk_descr_multidev; > + > +union blk_descr_unify { > + void *ptr; > + struct blk_descr_mem *mem; > + struct blk_descr_file *file; > + struct blk_descr_multidev *multidev; > +}; > + > +struct blk_descr_pool { > + struct list_head head; > + struct mutex lock; > + > + size_t blocks_cnt; // count of struct pool_el > + > + size_t total_cnt; // total count of block descriptors > + size_t take_cnt; // take count of block descriptors > +}; > + > +void blk_descr_pool_init(struct blk_descr_pool *pool, size_t available_blocks); > + > +void blk_descr_pool_done(struct blk_descr_pool *pool, > + void (*blocks_cleanup_cb)(void *descr_array, size_t count)); > + > +union blk_descr_unify blk_descr_pool_alloc( > + struct blk_descr_pool *pool, size_t blk_descr_size, > + union blk_descr_unify (*block_alloc_cb)(void *descr_array, size_t index, void *arg), > + void *arg); > + > +union blk_descr_unify blk_descr_pool_take(struct blk_descr_pool *pool, size_t blk_descr_size); > + > +bool blk_descr_pool_check_halffill(struct blk_descr_pool *pool, sector_t empty_limit, > + sector_t *fill_status); > diff --git a/drivers/block/blk-snap/blk_redirect.c b/drivers/block/blk-snap/blk_redirect.c > new file mode 100644 > index 000000000000..4c28a8cb4275 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_redirect.c > @@ -0,0 +1,507 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-redirect" > +#include "common.h" > +#include "blk_util.h" > +#include "blk_redirect.h" > + > +#define bio_vec_sectors(bv) (bv.bv_len >> SECTOR_SHIFT) > + > +struct bio_set blk_redirect_bioset = { 0 }; > + > +int blk_redirect_bioset_create(void) > +{ > + return bioset_init(&blk_redirect_bioset, 64, 0, BIOSET_NEED_BVECS | BIOSET_NEED_RESCUER); > +} > + > +void blk_redirect_bioset_free(void) > +{ > + bioset_exit(&blk_redirect_bioset); > +} > + > +void blk_redirect_bio_endio(struct bio *bb) > +{ > + struct blk_redirect_bio *rq_redir = (struct blk_redirect_bio *)bb->bi_private; > + > + if (rq_redir != NULL) { > + int err = SUCCESS; > + > + if (bb->bi_status != BLK_STS_OK) > + err = -EIO; > + > + if (err != SUCCESS) { > + pr_err("Failed to process redirect IO request. errno=%d\n", 0 - err); > + > + if (rq_redir->err == SUCCESS) > + rq_redir->err = err; > + } > + > + if (atomic64_dec_and_test(&rq_redir->bio_count)) > + blk_redirect_complete(rq_redir, rq_redir->err); > + } > + bio_put(bb); > +} > + > +struct bio *_blk_dev_redirect_bio_alloc(int nr_iovecs, void *bi_private) > +{ > + struct bio *new_bio; > + > + new_bio = bio_alloc_bioset(GFP_NOIO, nr_iovecs, &blk_redirect_bioset); > + if (new_bio == NULL) > + return NULL; > + > + new_bio->bi_end_io = blk_redirect_bio_endio; > + new_bio->bi_private = bi_private; > + > + return new_bio; > +} > + > +struct blk_redirect_bio_list *_redirect_bio_allocate_list(struct bio *new_bio) > +{ > + struct blk_redirect_bio_list *next; > + > + next = kzalloc(sizeof(struct blk_redirect_bio_list), GFP_NOIO); > + if (next == NULL) > + return NULL; > + > + next->next = NULL; > + next->this = new_bio; > + > + return next; > +} > + > +int bio_endio_list_push(struct blk_redirect_bio *rq_redir, struct bio *new_bio) > +{ > + struct blk_redirect_bio_list *head; > + > + if (rq_redir->bio_list_head == NULL) { > + //list is empty, add first bio > + rq_redir->bio_list_head = _redirect_bio_allocate_list(new_bio); > + if (rq_redir->bio_list_head == NULL) > + return -ENOMEM; > + return SUCCESS; > + } > + > + // seek end of list > + head = rq_redir->bio_list_head; > + while (head->next != NULL) > + head = head->next; > + > + //append new bio to the end of list > + head->next = _redirect_bio_allocate_list(new_bio); > + if (head->next == NULL) > + return -ENOMEM; > + > + return SUCCESS; > +} > + > +void bio_endio_list_cleanup(struct blk_redirect_bio_list *curr) > +{ > + while (curr != NULL) { > + struct blk_redirect_bio_list *next; > + > + next = curr->next; > + kfree(curr); > + curr = next; > + } > +} > + > +static unsigned int get_max_sect(struct block_device *blk_dev) > +{ > + struct request_queue *q = bdev_get_queue(blk_dev); > + > + return min((unsigned int)(BIO_MAX_PAGES << (PAGE_SHIFT - SECTOR_SHIFT)), > + q->limits.max_sectors); > +} > + > +static int _blk_dev_redirect_part_fast(struct blk_redirect_bio *rq_redir, int direction, > + struct block_device *blk_dev, sector_t target_pos, > + sector_t rq_ofs, sector_t rq_count) > +{ > + __label__ _fail_out; > + __label__ _reprocess_bv; > + > + int res = SUCCESS; > + > + struct bio_vec bvec; > + struct bvec_iter iter; > + > + struct bio *new_bio = NULL; > + > + sector_t sect_ofs = 0; > + sector_t processed_sectors = 0; > + int nr_iovecs; > + struct blk_redirect_bio_list *bio_endio_rec; > + > + nr_iovecs = get_max_sect(blk_dev) >> (PAGE_SHIFT - SECTOR_SHIFT); > + > + bio_for_each_segment(bvec, rq_redir->bio, iter) { > + sector_t bvec_ofs; > + sector_t bvec_sectors; > + > + unsigned int len; > + unsigned int offset; > + > + if ((sect_ofs + bio_vec_sectors(bvec)) <= rq_ofs) { > + sect_ofs += bio_vec_sectors(bvec); > + continue; > + } > + if (sect_ofs >= (rq_ofs + rq_count)) > + break; > + > + bvec_ofs = 0; > + if (sect_ofs < rq_ofs) > + bvec_ofs = rq_ofs - sect_ofs; > + > + bvec_sectors = bio_vec_sectors(bvec) - bvec_ofs; > + if (bvec_sectors > (rq_count - processed_sectors)) > + bvec_sectors = rq_count - processed_sectors; > + > + if (bvec_sectors == 0) { > + res = -EIO; > + goto _fail_out; > + } > + > +_reprocess_bv: > + if (new_bio == NULL) { > + new_bio = _blk_dev_redirect_bio_alloc(nr_iovecs, rq_redir); > + while (new_bio == NULL) { > + pr_err("Unable to allocate new bio for redirect IO.\n"); > + res = -ENOMEM; > + goto _fail_out; > + } > + > + bio_set_dev(new_bio, blk_dev); > + > + if (direction == READ) > + bio_set_op_attrs(new_bio, REQ_OP_READ, 0); > + > + if (direction == WRITE) > + bio_set_op_attrs(new_bio, REQ_OP_WRITE, 0); > + > + new_bio->bi_iter.bi_sector = target_pos + processed_sectors; > + } > + > + len = (unsigned int)from_sectors(bvec_sectors); > + offset = bvec.bv_offset + (unsigned int)from_sectors(bvec_ofs); > + if (unlikely(bio_add_page(new_bio, bvec.bv_page, len, offset) != len)) { > + if (bio_sectors(new_bio) == 0) { > + res = -EIO; > + goto _fail_out; > + } > + > + res = bio_endio_list_push(rq_redir, new_bio); > + if (res != SUCCESS) { > + pr_err("Failed to add bio into bio_endio_list\n"); > + goto _fail_out; > + } > + > + atomic64_inc(&rq_redir->bio_count); > + new_bio = NULL; > + > + goto _reprocess_bv; > + } > + processed_sectors += bvec_sectors; > + > + sect_ofs += bio_vec_sectors(bvec); > + } > + > + if (new_bio != NULL) { > + res = bio_endio_list_push(rq_redir, new_bio); > + if (res != SUCCESS) { > + pr_err("Failed to add bio into bio_endio_list\n"); > + goto _fail_out; > + } > + > + atomic64_inc(&rq_redir->bio_count); > + new_bio = NULL; > + } > + > + return SUCCESS; > + > +_fail_out: > + bio_endio_rec = rq_redir->bio_list_head; > + while (bio_endio_rec != NULL) { > + if (bio_endio_rec->this != NULL) > + bio_put(bio_endio_rec->this); > + > + bio_endio_rec = bio_endio_rec->next; > + } > + > + bio_endio_list_cleanup(bio_endio_rec); > + > + pr_err("Failed to process part of redirect IO request. rq_ofs=%lld, rq_count=%lld\n", > + rq_ofs, rq_count); > + return res; > +} > + > +int blk_dev_redirect_part(struct blk_redirect_bio *rq_redir, int direction, > + struct block_device *blk_dev, sector_t target_pos, sector_t rq_ofs, > + sector_t rq_count) > +{ > + struct request_queue *q = bdev_get_queue(blk_dev); > + sector_t logical_block_size_mask = > + (sector_t)((q->limits.logical_block_size >> SECTOR_SHIFT) - 1); > + > + if (likely(logical_block_size_mask == 0)) > + return _blk_dev_redirect_part_fast(rq_redir, direction, blk_dev, target_pos, rq_ofs, > + rq_count); > + > + if (likely((0 == (target_pos & logical_block_size_mask)) && > + (0 == (rq_count & logical_block_size_mask)))) > + return _blk_dev_redirect_part_fast(rq_redir, direction, blk_dev, target_pos, rq_ofs, > + rq_count); > + > + return -EFAULT; > +} > + > +void blk_dev_redirect_submit(struct blk_redirect_bio *rq_redir) > +{ > + struct blk_redirect_bio_list *head; > + struct blk_redirect_bio_list *curr; > + > + head = curr = rq_redir->bio_list_head; > + rq_redir->bio_list_head = NULL; > + > + while (curr != NULL) { > + submit_bio_direct(curr->this); > + > + curr = curr->next; > + } > + > + bio_endio_list_cleanup(head); > +} > + > +int blk_dev_redirect_memcpy_part(struct blk_redirect_bio *rq_redir, int direction, void *buff, > + sector_t rq_ofs, sector_t rq_count) > +{ > + struct bio_vec bvec; > + struct bvec_iter iter; > + > + sector_t sect_ofs = 0; > + sector_t processed_sectors = 0; > + > + bio_for_each_segment(bvec, rq_redir->bio, iter) { > + void *mem; > + sector_t bvec_ofs; > + sector_t bvec_sectors; > + > + if ((sect_ofs + bio_vec_sectors(bvec)) <= rq_ofs) { > + sect_ofs += bio_vec_sectors(bvec); > + continue; > + } > + > + if (sect_ofs >= (rq_ofs + rq_count)) > + break; > + > + bvec_ofs = 0; > + if (sect_ofs < rq_ofs) > + bvec_ofs = rq_ofs - sect_ofs; > + > + bvec_sectors = bio_vec_sectors(bvec) - bvec_ofs; > + if (bvec_sectors > (rq_count - processed_sectors)) > + bvec_sectors = rq_count - processed_sectors; > + > + mem = kmap_atomic(bvec.bv_page); > + if (direction == READ) { > + memcpy(mem + bvec.bv_offset + (unsigned int)from_sectors(bvec_ofs), > + buff + (unsigned int)from_sectors(processed_sectors), > + (unsigned int)from_sectors(bvec_sectors)); > + } else { > + memcpy(buff + (unsigned int)from_sectors(processed_sectors), > + mem + bvec.bv_offset + (unsigned int)from_sectors(bvec_ofs), > + (unsigned int)from_sectors(bvec_sectors)); > + } > + kunmap_atomic(mem); > + > + processed_sectors += bvec_sectors; > + > + sect_ofs += bio_vec_sectors(bvec); > + } > + > + return SUCCESS; > +} > + > +int blk_dev_redirect_zeroed_part(struct blk_redirect_bio *rq_redir, sector_t rq_ofs, > + sector_t rq_count) > +{ > + struct bio_vec bvec; > + struct bvec_iter iter; > + > + sector_t sect_ofs = 0; > + sector_t processed_sectors = 0; > + > + bio_for_each_segment(bvec, rq_redir->bio, iter) { > + void *mem; > + sector_t bvec_ofs; > + sector_t bvec_sectors; > + > + if ((sect_ofs + bio_vec_sectors(bvec)) <= rq_ofs) { > + sect_ofs += bio_vec_sectors(bvec); > + continue; > + } > + > + if (sect_ofs >= (rq_ofs + rq_count)) > + break; > + > + bvec_ofs = 0; > + if (sect_ofs < rq_ofs) > + bvec_ofs = rq_ofs - sect_ofs; > + > + bvec_sectors = bio_vec_sectors(bvec) - bvec_ofs; > + if (bvec_sectors > (rq_count - processed_sectors)) > + bvec_sectors = rq_count - processed_sectors; > + > + mem = kmap_atomic(bvec.bv_page); > + memset(mem + bvec.bv_offset + (unsigned int)from_sectors(bvec_ofs), 0, > + (unsigned int)from_sectors(bvec_sectors)); > + kunmap_atomic(mem); > + > + processed_sectors += bvec_sectors; > + > + sect_ofs += bio_vec_sectors(bvec); > + } > + > + return SUCCESS; > +} > + > +int blk_dev_redirect_read_zeroed(struct blk_redirect_bio *rq_redir, struct block_device *blk_dev, > + sector_t rq_pos, sector_t blk_ofs_start, sector_t blk_ofs_count, > + struct rangevector *zero_sectors) > +{ > + int res = SUCCESS; > + struct blk_range_tree_node *range_node; > + > + sector_t ofs = 0; > + > + sector_t from = rq_pos + blk_ofs_start; > + sector_t to = rq_pos + blk_ofs_start + blk_ofs_count - 1; > + > + down_read(&zero_sectors->lock); > + range_node = blk_range_rb_iter_first(&zero_sectors->root, from, to); > + while (range_node) { > + struct blk_range *zero_range = &range_node->range; > + sector_t current_portion; > + > + if (zero_range->ofs > rq_pos + blk_ofs_start + ofs) { > + sector_t pre_zero_cnt = zero_range->ofs - (rq_pos + blk_ofs_start + ofs); > + > + res = blk_dev_redirect_part(rq_redir, READ, blk_dev, > + rq_pos + blk_ofs_start + ofs, > + blk_ofs_start + ofs, pre_zero_cnt); > + if (res != SUCCESS) > + break; > + > + ofs += pre_zero_cnt; > + } > + > + current_portion = min_t(sector_t, zero_range->cnt, blk_ofs_count - ofs); > + > + res = blk_dev_redirect_zeroed_part(rq_redir, blk_ofs_start + ofs, current_portion); > + if (res != SUCCESS) > + break; > + > + ofs += current_portion; > + > + range_node = blk_range_rb_iter_next(range_node, from, to); > + } > + up_read(&zero_sectors->lock); > + > + if (res == SUCCESS) > + if ((blk_ofs_count - ofs) > 0) > + res = blk_dev_redirect_part(rq_redir, READ, blk_dev, > + rq_pos + blk_ofs_start + ofs, > + blk_ofs_start + ofs, blk_ofs_count - ofs); > + > + return res; > +} > +void blk_redirect_complete(struct blk_redirect_bio *rq_redir, int res) > +{ > + rq_redir->complete_cb(rq_redir->complete_param, rq_redir->bio, res); > + redirect_bio_queue_free(rq_redir); > +} > + > +void redirect_bio_queue_init(struct redirect_bio_queue *queue) > +{ > + INIT_LIST_HEAD(&queue->list); > + > + spin_lock_init(&queue->lock); > + > + atomic_set(&queue->in_queue_cnt, 0); > + atomic_set(&queue->alloc_cnt, 0); > + > + atomic_set(&queue->active_state, true); > +} > + > +struct blk_redirect_bio *redirect_bio_queue_new(struct redirect_bio_queue *queue) > +{ > + struct blk_redirect_bio *rq_redir = kzalloc(sizeof(struct blk_redirect_bio), GFP_NOIO); > + > + if (rq_redir == NULL) > + return NULL; > + > + atomic_inc(&queue->alloc_cnt); > + > + INIT_LIST_HEAD(&rq_redir->link); > + rq_redir->queue = queue; > + > + return rq_redir; > +} > + > +void redirect_bio_queue_free(struct blk_redirect_bio *rq_redir) > +{ > + if (rq_redir) { > + if (rq_redir->queue) > + atomic_dec(&rq_redir->queue->alloc_cnt); > + > + kfree(rq_redir); > + } > +} > + > +int redirect_bio_queue_push_back(struct redirect_bio_queue *queue, > + struct blk_redirect_bio *rq_redir) > +{ > + int res = SUCCESS; > + > + spin_lock(&queue->lock); > + > + if (atomic_read(&queue->active_state)) { > + INIT_LIST_HEAD(&rq_redir->link); > + list_add_tail(&rq_redir->link, &queue->list); > + atomic_inc(&queue->in_queue_cnt); > + } else > + res = -EACCES; > + > + spin_unlock(&queue->lock); > + return res; > +} > + > +struct blk_redirect_bio *redirect_bio_queue_get_first(struct redirect_bio_queue *queue) > +{ > + struct blk_redirect_bio *rq_redir = NULL; > + > + spin_lock(&queue->lock); > + > + if (!list_empty(&queue->list)) { > + rq_redir = list_entry(queue->list.next, struct blk_redirect_bio, link); > + list_del(&rq_redir->link); > + atomic_dec(&queue->in_queue_cnt); > + } > + > + spin_unlock(&queue->lock); > + > + return rq_redir; > +} > + > +bool redirect_bio_queue_active(struct redirect_bio_queue *queue, bool state) > +{ > + bool prev_state; > + > + spin_lock(&queue->lock); > + > + prev_state = atomic_read(&queue->active_state); > + atomic_set(&queue->active_state, state); > + > + spin_unlock(&queue->lock); > + > + return prev_state; > +} > diff --git a/drivers/block/blk-snap/blk_redirect.h b/drivers/block/blk-snap/blk_redirect.h > new file mode 100644 > index 000000000000..aae23e78ebe2 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_redirect.h > @@ -0,0 +1,73 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include "rangevector.h" > + > +int blk_redirect_bioset_create(void); > +void blk_redirect_bioset_free(void); > + > +void blk_redirect_bio_endio(struct bio *bb); > + > +struct blk_redirect_bio_list { > + struct blk_redirect_bio_list *next; > + struct bio *this; > +}; > + > +struct redirect_bio_queue { > + struct list_head list; > + spinlock_t lock; > + > + atomic_t active_state; > + atomic_t in_queue_cnt; > + atomic_t alloc_cnt; > +}; > + > +struct blk_redirect_bio { > + struct list_head link; > + struct redirect_bio_queue *queue; > + > + struct bio *bio; > + int err; > + struct blk_redirect_bio_list *bio_list_head; //list of created bios > + atomic64_t bio_count; > + > + void *complete_param; > + void (*complete_cb)(void *complete_param, struct bio *rq, int err); > +}; > + > +int blk_dev_redirect_part(struct blk_redirect_bio *rq_redir, int direction, > + struct block_device *blk_dev, sector_t target_pos, sector_t rq_ofs, > + sector_t rq_count); > + > +void blk_dev_redirect_submit(struct blk_redirect_bio *rq_redir); > + > +int blk_dev_redirect_memcpy_part(struct blk_redirect_bio *rq_redir, int direction, void *src_buff, > + sector_t rq_ofs, sector_t rq_count); > + > +int blk_dev_redirect_zeroed_part(struct blk_redirect_bio *rq_redir, sector_t rq_ofs, > + sector_t rq_count); > + > +int blk_dev_redirect_read_zeroed(struct blk_redirect_bio *rq_redir, struct block_device *blk_dev, > + sector_t rq_pos, sector_t blk_ofs_start, sector_t blk_ofs_count, > + struct rangevector *zero_sectors); > + > +void blk_redirect_complete(struct blk_redirect_bio *rq_redir, int res); > + > +void redirect_bio_queue_init(struct redirect_bio_queue *queue); > + > +struct blk_redirect_bio *redirect_bio_queue_new(struct redirect_bio_queue *queue); > + > +void redirect_bio_queue_free(struct blk_redirect_bio *rq_redir); > + > +int redirect_bio_queue_push_back(struct redirect_bio_queue *queue, > + struct blk_redirect_bio *rq_redir); > + > +struct blk_redirect_bio *redirect_bio_queue_get_first(struct redirect_bio_queue *queue); > + > +bool redirect_bio_queue_active(struct redirect_bio_queue *queue, bool state); > + > +#define redirect_bio_queue_empty(queue) (atomic_read(&(queue).in_queue_cnt) == 0) > + > +#define redirect_bio_queue_unactive(queue) \ > + ((atomic_read(&((queue).active_state)) == false) && \ > + (atomic_read(&((queue).alloc_cnt)) == 0)) > diff --git a/drivers/block/blk-snap/blk_util.c b/drivers/block/blk-snap/blk_util.c > new file mode 100644 > index 000000000000..57db70b86516 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_util.c > @@ -0,0 +1,33 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include "common.h" > +#include "blk_util.h" > + > +int blk_dev_open(dev_t dev_id, struct block_device **p_blk_dev) > +{ > + int result = SUCCESS; > + struct block_device *blk_dev; > + int refCount; > + > + blk_dev = bdget(dev_id); > + if (blk_dev == NULL) { > + pr_err("Unable to open device [%d:%d]: bdget return NULL\n", MAJOR(dev_id), > + MINOR(dev_id)); > + return -ENODEV; > + } > + > + refCount = blkdev_get(blk_dev, FMODE_READ | FMODE_WRITE, NULL); > + if (refCount < 0) { > + pr_err("Unable to open device [%d:%d]: blkdev_get return error code %d\n", > + MAJOR(dev_id), MINOR(dev_id), 0 - refCount); > + result = refCount; > + } > + > + if (result == SUCCESS) > + *p_blk_dev = blk_dev; > + return result; > +} > + > +void blk_dev_close(struct block_device *blk_dev) > +{ > + blkdev_put(blk_dev, FMODE_READ); > +} > diff --git a/drivers/block/blk-snap/blk_util.h b/drivers/block/blk-snap/blk_util.h > new file mode 100644 > index 000000000000..0776f2faa668 > --- /dev/null > +++ b/drivers/block/blk-snap/blk_util.h > @@ -0,0 +1,33 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +int blk_dev_open(dev_t dev_id, struct block_device **p_blk_dev); > +void blk_dev_close(struct block_device *blk_dev); > + > +/* > + * this function was copied from block/blk.h > + */ > +static inline sector_t part_nr_sects_read(struct hd_struct *part) > +{ > +#if (BITS_PER_LONG == 32) && defined(CONFIG_SMP) > + sector_t nr_sects; > + unsigned int seq; > + > + do { > + seq = read_seqcount_begin(&part->nr_sects_seq); > + nr_sects = part->nr_sects; > + } while (read_seqcount_retry(&part->nr_sects_seq, seq)); > + > + return nr_sects; > +#elif (BITS_PER_LONG == 32) && defined(CONFIG_PREEMPTION) > + sector_t nr_sects; > + > + preempt_disable(); > + nr_sects = part->nr_sects; > + preempt_enable(); > + > + return nr_sects; > +#else > + return part->nr_sects; > +#endif > +} > diff --git a/drivers/block/blk-snap/cbt_map.c b/drivers/block/blk-snap/cbt_map.c > new file mode 100644 > index 000000000000..e913069d1a57 > --- /dev/null > +++ b/drivers/block/blk-snap/cbt_map.c > @@ -0,0 +1,210 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-cbt_map" > +#include "common.h" > +#include "cbt_map.h" > + > +int cbt_map_allocate(struct cbt_map *cbt_map, unsigned int cbt_sect_in_block_degree, > + sector_t device_capacity) > +{ > + sector_t size_mod; > + > + cbt_map->sect_in_block_degree = cbt_sect_in_block_degree; > + cbt_map->device_capacity = device_capacity; > + cbt_map->map_size = (device_capacity >> (sector_t)cbt_sect_in_block_degree); > + > + pr_info("Allocate CBT map of %zu\n", cbt_map->map_size); > + > + size_mod = (device_capacity & ((sector_t)(1 << cbt_sect_in_block_degree) - 1)); > + if (size_mod) > + cbt_map->map_size++; > + > + cbt_map->read_map = big_buffer_alloc(cbt_map->map_size, GFP_KERNEL); > + if (cbt_map->read_map != NULL) > + big_buffer_memset(cbt_map->read_map, 0); > + > + cbt_map->write_map = big_buffer_alloc(cbt_map->map_size, GFP_KERNEL); > + if (cbt_map->write_map != NULL) > + big_buffer_memset(cbt_map->write_map, 0); > + > + if ((cbt_map->read_map == NULL) || (cbt_map->write_map == NULL)) { > + pr_err("Cannot allocate CBT map. map_size=%zu\n", cbt_map->map_size); > + return -ENOMEM; > + } > + > + cbt_map->snap_number_previous = 0; > + cbt_map->snap_number_active = 1; > + generate_random_uuid(cbt_map->generationId.b); > + cbt_map->active = true; > + > + cbt_map->state_changed_sectors = 0; > + cbt_map->state_dirty_sectors = 0; > + > + return SUCCESS; > +} > + > +void cbt_map_deallocate(struct cbt_map *cbt_map) > +{ > + if (cbt_map->read_map != NULL) { > + big_buffer_free(cbt_map->read_map); > + cbt_map->read_map = NULL; > + } > + > + if (cbt_map->write_map != NULL) { > + big_buffer_free(cbt_map->write_map); > + cbt_map->write_map = NULL; > + } > + > + cbt_map->active = false; > +} > + > +static void cbt_map_destroy(struct cbt_map *cbt_map) > +{ > + pr_info("CBT map destroy\n"); > + if (cbt_map != NULL) { > + cbt_map_deallocate(cbt_map); > + > + kfree(cbt_map); > + } > +} > + > +struct cbt_map *cbt_map_create(unsigned int cbt_sect_in_block_degree, sector_t device_capacity) > +{ > + struct cbt_map *cbt_map = NULL; > + > + pr_info("CBT map create\n"); > + > + cbt_map = kzalloc(sizeof(struct cbt_map), GFP_KERNEL); > + if (cbt_map == NULL) > + return NULL; > + > + if (cbt_map_allocate(cbt_map, cbt_sect_in_block_degree, device_capacity) != SUCCESS) { > + cbt_map_destroy(cbt_map); > + return NULL; > + } > + > + spin_lock_init(&cbt_map->locker); > + init_rwsem(&cbt_map->rw_lock); > + kref_init(&cbt_map->refcount); > + > + return cbt_map; > +} > + > +void cbt_map_destroy_cb(struct kref *kref) > +{ > + cbt_map_destroy(container_of(kref, struct cbt_map, refcount)); > +} > + > +struct cbt_map *cbt_map_get_resource(struct cbt_map *cbt_map) > +{ > + if (cbt_map) > + kref_get(&cbt_map->refcount); > + > + return cbt_map; > +} > + > +void cbt_map_put_resource(struct cbt_map *cbt_map) > +{ > + if (cbt_map) > + kref_put(&cbt_map->refcount, cbt_map_destroy_cb); > +} > + > +void cbt_map_switch(struct cbt_map *cbt_map) > +{ > + pr_info("CBT map switch\n"); > + spin_lock(&cbt_map->locker); > + > + big_buffer_memcpy(cbt_map->read_map, cbt_map->write_map); > + > + cbt_map->snap_number_previous = cbt_map->snap_number_active; > + ++cbt_map->snap_number_active; > + if (cbt_map->snap_number_active == 256) { > + cbt_map->snap_number_active = 1; > + > + big_buffer_memset(cbt_map->write_map, 0); > + > + generate_random_uuid(cbt_map->generationId.b); > + > + pr_info("CBT reset\n"); > + } > + spin_unlock(&cbt_map->locker); > +} > + > +int _cbt_map_set(struct cbt_map *cbt_map, sector_t sector_start, sector_t sector_cnt, > + u8 snap_number, struct big_buffer *map) > +{ > + int res = SUCCESS; > + size_t cbt_block; > + size_t cbt_block_first = (size_t)(sector_start >> cbt_map->sect_in_block_degree); > + size_t cbt_block_last = (size_t)((sector_start + sector_cnt - 1) >> > + cbt_map->sect_in_block_degree); //inclusive > + > + for (cbt_block = cbt_block_first; cbt_block <= cbt_block_last; ++cbt_block) { > + if (cbt_block < cbt_map->map_size) { > + u8 num; > + > + res = big_buffer_byte_get(map, cbt_block, &num); > + if (res == SUCCESS) > + if (num < snap_number) > + res = big_buffer_byte_set(map, cbt_block, snap_number); > + } else > + res = -EINVAL; > + > + if (res != SUCCESS) { > + pr_err("Block index is too large. #%zu was demanded, map size %zu\n", > + cbt_block, cbt_map->map_size); > + break; > + } > + } > + return res; > +} > + > +int cbt_map_set(struct cbt_map *cbt_map, sector_t sector_start, sector_t sector_cnt) > +{ > + int res = SUCCESS; > + > + spin_lock(&cbt_map->locker); > + > + res = _cbt_map_set(cbt_map, sector_start, sector_cnt, (u8)cbt_map->snap_number_active, > + cbt_map->write_map); > + cbt_map->state_changed_sectors += sector_cnt; > + > + spin_unlock(&cbt_map->locker); > + return res; > +} > + > +int cbt_map_set_both(struct cbt_map *cbt_map, sector_t sector_start, sector_t sector_cnt) > +{ > + int res = SUCCESS; > + > + spin_lock(&cbt_map->locker); > + > + res = _cbt_map_set(cbt_map, sector_start, sector_cnt, > + (u8)cbt_map->snap_number_active, cbt_map->write_map); > + if (res == SUCCESS) > + res = _cbt_map_set(cbt_map, sector_start, sector_cnt, > + (u8)cbt_map->snap_number_previous, cbt_map->read_map); > + cbt_map->state_dirty_sectors += sector_cnt; > + > + spin_unlock(&cbt_map->locker); > + return res; > +} > + > +size_t cbt_map_read_to_user(struct cbt_map *cbt_map, void __user *user_buff, size_t offset, > + size_t size) > +{ > + size_t readed = 0; > + size_t left_size; > + size_t real_size = min((cbt_map->map_size - offset), size); > + > + left_size = real_size - > + big_buffer_copy_to_user(user_buff, offset, cbt_map->read_map, real_size); > + > + if (left_size == 0) > + readed = real_size; > + else { > + pr_err("Not all CBT data was read. Left [%zu] bytes\n", left_size); > + readed = real_size - left_size; > + } > + > + return readed; > +} > diff --git a/drivers/block/blk-snap/cbt_map.h b/drivers/block/blk-snap/cbt_map.h > new file mode 100644 > index 000000000000..cb52b09531fe > --- /dev/null > +++ b/drivers/block/blk-snap/cbt_map.h > @@ -0,0 +1,62 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include "big_buffer.h" > +#include <linux/kref.h> > +#include <linux/uuid.h> > + > +struct cbt_map { > + struct kref refcount; > + > + spinlock_t locker; > + > + size_t sect_in_block_degree; > + sector_t device_capacity; > + size_t map_size; > + > + struct big_buffer *read_map; > + struct big_buffer *write_map; > + > + unsigned long snap_number_active; > + unsigned long snap_number_previous; > + uuid_t generationId; > + > + bool active; > + > + struct rw_semaphore rw_lock; > + > + sector_t state_changed_sectors; > + sector_t state_dirty_sectors; > +}; > + > +struct cbt_map *cbt_map_create(unsigned int cbt_sect_in_block_degree, sector_t device_capacity); > + > +struct cbt_map *cbt_map_get_resource(struct cbt_map *cbt_map); > +void cbt_map_put_resource(struct cbt_map *cbt_map); > + > +void cbt_map_switch(struct cbt_map *cbt_map); > +int cbt_map_set(struct cbt_map *cbt_map, sector_t sector_start, sector_t sector_cnt); > +int cbt_map_set_both(struct cbt_map *cbt_map, sector_t sector_start, sector_t sector_cnt); > + > +size_t cbt_map_read_to_user(struct cbt_map *cbt_map, void __user *user_buffer, size_t offset, > + size_t size); > + > +static inline void cbt_map_read_lock(struct cbt_map *cbt_map) > +{ > + down_read(&cbt_map->rw_lock); > +}; > + > +static inline void cbt_map_read_unlock(struct cbt_map *cbt_map) > +{ > + up_read(&cbt_map->rw_lock); > +}; > + > +static inline void cbt_map_write_lock(struct cbt_map *cbt_map) > +{ > + down_write(&cbt_map->rw_lock); > +}; > + > +static inline void cbt_map_write_unlock(struct cbt_map *cbt_map) > +{ > + up_write(&cbt_map->rw_lock); > +}; > diff --git a/drivers/block/blk-snap/common.h b/drivers/block/blk-snap/common.h > new file mode 100644 > index 000000000000..bbd5e98ab2a6 > --- /dev/null > +++ b/drivers/block/blk-snap/common.h > @@ -0,0 +1,31 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#ifndef BLK_SNAP_SECTION > +#define BLK_SNAP_SECTION "" > +#endif > +#define pr_fmt(fmt) KBUILD_MODNAME BLK_SNAP_SECTION ": " fmt > + > +#include <linux/version.h> /*rudiment - needed for using KERNEL_VERSION */ > + > +#include <linux/types.h> > +#include <linux/errno.h> > +#include <linux/mutex.h> > +#include <linux/rwsem.h> > +#include <linux/spinlock.h> > +#include <linux/slab.h> > +#include <linux/list.h> > +#include <linux/atomic.h> > +#include <linux/blkdev.h> > + > +#define from_sectors(_sectors) (_sectors << SECTOR_SHIFT) > +#define to_sectors(_byte_size) (_byte_size >> SECTOR_SHIFT) > + > +struct blk_range { > + sector_t ofs; > + blkcnt_t cnt; > +}; > + > +#ifndef SUCCESS > +#define SUCCESS 0 > +#endif > diff --git a/drivers/block/blk-snap/ctrl_fops.c b/drivers/block/blk-snap/ctrl_fops.c > new file mode 100644 > index 000000000000..b7b18539ee96 > --- /dev/null > +++ b/drivers/block/blk-snap/ctrl_fops.c > @@ -0,0 +1,691 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-ctrl" > +#include "common.h" > +#include "blk-snap-ctl.h" > +#include "ctrl_fops.h" > +#include "version.h" > +#include "tracking.h" > +#include "snapshot.h" > +#include "snapstore.h" > +#include "snapimage.h" > +#include "tracker.h" > +#include "blk_deferred.h" > +#include "big_buffer.h" > +#include "params.h" > + > +#include <linux/module.h> > +#include <linux/poll.h> > +#include <linux/uaccess.h> > + > +int blk_snap_major; //zero by default > + > +const struct file_operations ctrl_fops = { .owner = THIS_MODULE, > + .read = ctrl_read, > + .write = ctrl_write, > + .open = ctrl_open, > + .release = ctrl_release, > + .poll = ctrl_poll, > + .unlocked_ioctl = ctrl_unlocked_ioctl }; > + > +atomic_t dev_open_cnt = ATOMIC_INIT(0); > + > +const struct ioctl_getversion_s version = { .major = FILEVER_MAJOR, > + .minor = FILEVER_MINOR, > + .revision = FILEVER_REVISION, > + .build = 0 }; > + > +int get_blk_snap_major(void) > +{ > + return blk_snap_major; > +} > + > +int ctrl_init(void) > +{ > + int ret; > + > + ret = register_chrdev(0, MODULE_NAME, &ctrl_fops); > + if (ret < 0) { > + pr_err("Failed to register a character device. errno=%d\n", blk_snap_major); > + return ret; > + } > + > + blk_snap_major = ret; > + pr_info("Module major [%d]\n", blk_snap_major); > + return SUCCESS; > +} > + > +void ctrl_done(void) > +{ > + unregister_chrdev(blk_snap_major, MODULE_NAME); > + ctrl_pipe_done(); > +} > + > +ssize_t ctrl_read(struct file *fl, char __user *buffer, size_t length, loff_t *offset) > +{ > + ssize_t bytes_read = 0; > + struct ctrl_pipe *pipe = (struct ctrl_pipe *)fl->private_data; > + > + if (pipe == NULL) { > + pr_err("Unable to read from pipe: invalid pipe pointer\n"); > + return -EINVAL; > + } > + > + bytes_read = ctrl_pipe_read(pipe, buffer, length); > + if (bytes_read == 0) > + if (fl->f_flags & O_NONBLOCK) > + bytes_read = -EAGAIN; > + > + return bytes_read; > +} > + > +ssize_t ctrl_write(struct file *fl, const char __user *buffer, size_t length, loff_t *offset) > +{ > + struct ctrl_pipe *pipe = (struct ctrl_pipe *)fl->private_data; > + > + if (pipe == NULL) { > + pr_err("Unable to write into pipe: invalid pipe pointer\n"); > + return -EINVAL; > + } > + > + return ctrl_pipe_write(pipe, buffer, length); > +} > + > +unsigned int ctrl_poll(struct file *fl, struct poll_table_struct *wait) > +{ > + struct ctrl_pipe *pipe = (struct ctrl_pipe *)fl->private_data; > + > + if (pipe == NULL) { > + pr_err("Unable to poll pipe: invalid pipe pointer\n"); > + return -EINVAL; > + } > + > + return ctrl_pipe_poll(pipe); > +} > + > +int ctrl_open(struct inode *inode, struct file *fl) > +{ > + fl->f_pos = 0; > + > + if (false == try_module_get(THIS_MODULE)) > + return -EINVAL; > + > + fl->private_data = (void *)ctrl_pipe_new(); > + if (fl->private_data == NULL) { > + pr_err("Failed to open ctrl file\n"); > + return -ENOMEM; > + } > + > + atomic_inc(&dev_open_cnt); > + > + return SUCCESS; > +} > + > +int ctrl_release(struct inode *inode, struct file *fl) > +{ > + int result = SUCCESS; > + > + if (atomic_read(&dev_open_cnt) > 0) { > + module_put(THIS_MODULE); > + ctrl_pipe_put_resource((struct ctrl_pipe *)fl->private_data); > + > + atomic_dec(&dev_open_cnt); > + } else { > + pr_err("Unable to close ctrl file: the file is already closed\n"); > + result = -EALREADY; > + } > + > + return result; > +} > + > +int ioctl_compatibility_flags(unsigned long arg) > +{ > + unsigned long len; > + struct ioctl_compatibility_flags_s param; > + > + param.flags = 0; > + param.flags |= BLK_SNAP_COMPATIBILITY_SNAPSTORE; > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + param.flags |= BLK_SNAP_COMPATIBILITY_MULTIDEV; > +#endif > + len = copy_to_user((void *)arg, ¶m, sizeof(struct ioctl_compatibility_flags_s)); > + if (len != 0) { > + pr_err("Unable to get compatibility flags: invalid user buffer\n"); > + return -EINVAL; > + } > + > + return SUCCESS; > +} > + > +int ioctl_get_version(unsigned long arg) > +{ > + unsigned long len; > + > + pr_info("Get version\n"); > + > + len = copy_to_user((void *)arg, &version, sizeof(struct ioctl_getversion_s)); > + if (len != 0) { > + pr_err("Unable to get version: invalid user buffer\n"); > + return -ENODATA; > + } > + > + return SUCCESS; > +} > + > +int ioctl_tracking_add(unsigned long arg) > +{ > + unsigned long len; > + struct ioctl_dev_id_s dev; > + > + len = copy_from_user(&dev, (void *)arg, sizeof(struct ioctl_dev_id_s)); > + if (len != 0) { > + pr_err("Unable to add device under tracking: invalid user buffer\n"); > + return -ENODATA; > + } > + > + return tracking_add(MKDEV(dev.major, dev.minor), 0ull); > +} > + > +int ioctl_tracking_remove(unsigned long arg) > +{ > + struct ioctl_dev_id_s dev; > + > + if (copy_from_user(&dev, (void *)arg, sizeof(struct ioctl_dev_id_s)) != 0) { > + pr_err("Unable to remove device from tracking: invalid user buffer\n"); > + return -ENODATA; > + } > + return tracking_remove(MKDEV(dev.major, dev.minor)); > + ; > +} > + > +int ioctl_tracking_collect(unsigned long arg) > +{ > + unsigned long len; > + int res; > + struct ioctl_tracking_collect_s get; > + > + pr_info("Collecting tracking devices:\n"); > + > + len = copy_from_user(&get, (void *)arg, sizeof(struct ioctl_tracking_collect_s)); > + if (len != 0) { > + pr_err("Unable to collect tracking devices: invalid user buffer\n"); > + return -ENODATA; > + } > + > + if (get.p_cbt_info == NULL) { > + res = tracking_collect(0x7FFFffff, NULL, &get.count); > + if (res == SUCCESS) { > + len = copy_to_user((void *)arg, (void *)&get, > + sizeof(struct ioctl_tracking_collect_s)); > + if (len != 0) { > + pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n"); > + res = -ENODATA; > + } > + } else { > + pr_err("Failed to execute tracking_collect. errno=%d\n", res); > + } > + } else { > + struct cbt_info_s *p_cbt_info = NULL; > + > + p_cbt_info = kcalloc(get.count, sizeof(struct cbt_info_s), GFP_KERNEL); > + if (p_cbt_info == NULL) > + return -ENOMEM; > + > + do { > + res = tracking_collect(get.count, p_cbt_info, &get.count); > + if (res != SUCCESS) { > + pr_err("Failed to execute tracking_collect. errno=%d\n", res); > + break; > + } > + len = copy_to_user(get.p_cbt_info, p_cbt_info, > + get.count * sizeof(struct cbt_info_s)); > + if (len != 0) { > + pr_err("Unable to collect tracking devices: invalid user buffer for CBT info\n"); > + res = -ENODATA; > + break; > + } > + > + len = copy_to_user((void *)arg, (void *)&get, > + sizeof(struct ioctl_tracking_collect_s)); > + if (len != 0) { > + pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n"); > + res = -ENODATA; > + break; > + } > + > + } while (false); > + > + kfree(p_cbt_info); > + p_cbt_info = NULL; > + } > + return res; > +} > + > +int ioctl_tracking_block_size(unsigned long arg) > +{ > + unsigned long len; > + unsigned int blk_sz = change_tracking_block_size(); > + > + len = copy_to_user((void *)arg, &blk_sz, sizeof(unsigned int)); > + if (len != 0) { > + pr_err("Unable to get tracking block size: invalid user buffer for arguments\n"); > + return -ENODATA; > + } > + return SUCCESS; > +} > + > +int ioctl_tracking_read_cbt_map(unsigned long arg) > +{ > + dev_t dev_id; > + unsigned long len; > + struct ioctl_tracking_read_cbt_bitmap_s readbitmap; > + > + len = copy_from_user(&readbitmap, (void *)arg, > + sizeof(struct ioctl_tracking_read_cbt_bitmap_s)); > + if (len != 0) { > + pr_err("Unable to read CBT map: invalid user buffer\n"); > + return -ENODATA; > + } > + > + dev_id = MKDEV(readbitmap.dev_id.major, readbitmap.dev_id.minor); > + return tracking_read_cbt_bitmap(dev_id, readbitmap.offset, readbitmap.length, > + (void *)readbitmap.buff); > +} > + > +int ioctl_tracking_mark_dirty_blocks(unsigned long arg) > +{ > + unsigned long len; > + struct ioctl_tracking_mark_dirty_blocks_s param; > + struct block_range_s *p_dirty_blocks; > + size_t buffer_size; > + int result = SUCCESS; > + > + len = copy_from_user(¶m, (void *)arg, > + sizeof(struct ioctl_tracking_mark_dirty_blocks_s)); > + if (len != 0) { > + pr_err("Unable to mark dirty blocks: invalid user buffer\n"); > + return -ENODATA; > + } > + > + buffer_size = param.count * sizeof(struct block_range_s); > + p_dirty_blocks = kzalloc(buffer_size, GFP_KERNEL); > + if (p_dirty_blocks == NULL) { > + pr_err("Unable to mark dirty blocks: cannot allocate [%zu] bytes\n", buffer_size); > + return -ENOMEM; > + } > + > + do { > + dev_t image_dev_id; > + > + len = copy_from_user(p_dirty_blocks, (void *)param.p_dirty_blocks, buffer_size); > + if (len != 0) { > + pr_err("Unable to mark dirty blocks: invalid user buffer\n"); > + result = -ENODATA; > + break; > + } > + > + image_dev_id = MKDEV(param.image_dev_id.major, param.image_dev_id.minor); > + result = snapimage_mark_dirty_blocks(image_dev_id, p_dirty_blocks, param.count); > + } while (false); > + kfree(p_dirty_blocks); > + > + return result; > +} > + > +int ioctl_snapshot_create(unsigned long arg) > +{ > + unsigned long len; > + size_t dev_id_buffer_size; > + int status; > + struct ioctl_snapshot_create_s param; > + struct ioctl_dev_id_s *pk_dev_id = NULL; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_snapshot_create_s)); > + if (len != 0) { > + pr_err("Unable to create snapshot: invalid user buffer\n"); > + return -ENODATA; > + } > + > + dev_id_buffer_size = sizeof(struct ioctl_dev_id_s) * param.count; > + pk_dev_id = kzalloc(dev_id_buffer_size, GFP_KERNEL); > + if (pk_dev_id == NULL) { > + pr_err("Unable to create snapshot: cannot allocate [%zu] bytes\n", > + dev_id_buffer_size); > + return -ENOMEM; > + } > + > + do { > + size_t dev_buffer_size; > + dev_t *p_dev = NULL; > + int inx = 0; > + > + len = copy_from_user(pk_dev_id, (void *)param.p_dev_id, > + param.count * sizeof(struct ioctl_dev_id_s)); > + if (len != 0) { > + pr_err("Unable to create snapshot: invalid user buffer for parameters\n"); > + status = -ENODATA; > + break; > + } > + > + dev_buffer_size = sizeof(dev_t) * param.count; > + p_dev = kzalloc(dev_buffer_size, GFP_KERNEL); > + if (p_dev == NULL) { > + pr_err("Unable to create snapshot: cannot allocate [%zu] bytes\n", > + dev_buffer_size); > + status = -ENOMEM; > + break; > + } > + > + for (inx = 0; inx < param.count; ++inx) > + p_dev[inx] = MKDEV(pk_dev_id[inx].major, pk_dev_id[inx].minor); > + > + status = snapshot_create(p_dev, param.count, ¶m.snapshot_id); > + > + kfree(p_dev); > + p_dev = NULL; > + > + } while (false); > + kfree(pk_dev_id); > + pk_dev_id = NULL; > + > + if (status == SUCCESS) { > + len = copy_to_user((void *)arg, ¶m, sizeof(struct ioctl_snapshot_create_s)); > + if (len != 0) { > + pr_err("Unable to create snapshot: invalid user buffer\n"); > + status = -ENODATA; > + } > + } > + > + return status; > +} > + > +int ioctl_snapshot_destroy(unsigned long arg) > +{ > + unsigned long len; > + unsigned long long param; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(unsigned long long)); > + if (len != 0) { > + pr_err("Unable to destroy snapshot: invalid user buffer\n"); > + return -ENODATA; > + } > + > + return snapshot_destroy(param); > +} > + > +static inline dev_t _snapstore_dev(struct ioctl_dev_id_s *dev_id) > +{ > + if ((dev_id->major == 0) && (dev_id->minor == 0)) > + return 0; //memory snapstore > + > + if ((dev_id->major == -1) && (dev_id->minor == -1)) > + return 0xFFFFffff; //multidevice snapstore > + > + return MKDEV(dev_id->major, dev_id->minor); > +} > + > +int ioctl_snapstore_create(unsigned long arg) > +{ > + unsigned long len; > + int res = SUCCESS; > + struct ioctl_snapstore_create_s param; > + size_t inx = 0; > + dev_t *dev_id_set = NULL; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_snapstore_create_s)); > + if (len != 0) { > + pr_err("Unable to create snapstore: invalid user buffer\n"); > + return -EINVAL; > + } > + > + dev_id_set = kcalloc(param.count, sizeof(dev_t), GFP_KERNEL); > + if (dev_id_set == NULL) > + return -ENOMEM; > + > + for (inx = 0; inx < param.count; ++inx) { > + struct ioctl_dev_id_s dev_id; > + > + len = copy_from_user(&dev_id, param.p_dev_id + inx, sizeof(struct ioctl_dev_id_s)); > + if (len != 0) { > + pr_err("Unable to create snapstore: "); > + pr_err("invalid user buffer for parameters\n"); > + > + res = -ENODATA; > + break; > + } > + > + dev_id_set[inx] = MKDEV(dev_id.major, dev_id.minor); > + } > + > + if (res == SUCCESS) > + res = snapstore_create((uuid_t *)param.id, _snapstore_dev(¶m.snapstore_dev_id), > + dev_id_set, (size_t)param.count); > + > + kfree(dev_id_set); > + > + return res; > +} > + > +int ioctl_snapstore_file(unsigned long arg) > +{ > + unsigned long len; > + int res = SUCCESS; > + struct ioctl_snapstore_file_add_s param; > + struct big_buffer *ranges = NULL; > + size_t ranges_buffer_size; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_snapstore_file_add_s)); > + if (len != 0) { > + pr_err("Unable to add file to snapstore: invalid user buffer\n"); > + return -EINVAL; > + } > + > + ranges_buffer_size = sizeof(struct ioctl_range_s) * param.range_count; > + > + ranges = big_buffer_alloc(ranges_buffer_size, GFP_KERNEL); > + if (ranges == NULL) { > + pr_err("Unable to add file to snapstore: cannot allocate [%zu] bytes\n", > + ranges_buffer_size); > + return -ENOMEM; > + } > + > + if (big_buffer_copy_from_user((void *)param.ranges, 0, ranges, ranges_buffer_size) > + != ranges_buffer_size) { > + > + pr_err("Unable to add file to snapstore: invalid user buffer for parameters\n"); > + res = -ENODATA; > + } else > + res = snapstore_add_file((uuid_t *)(param.id), ranges, (size_t)param.range_count); > + > + big_buffer_free(ranges); > + > + return res; > +} > + > +int ioctl_snapstore_memory(unsigned long arg) > +{ > + unsigned long len; > + int res = SUCCESS; > + struct ioctl_snapstore_memory_limit_s param; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_snapstore_memory_limit_s)); > + if (len != 0) { > + pr_err("Unable to add memory block to snapstore: invalid user buffer\n"); > + return -EINVAL; > + } > + > + res = snapstore_add_memory((uuid_t *)param.id, param.size); > + > + return res; > +} > +int ioctl_snapstore_cleanup(unsigned long arg) > +{ > + unsigned long len; > + int res = SUCCESS; > + struct ioctl_snapstore_cleanup_s param; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_snapstore_cleanup_s)); > + if (len != 0) { > + pr_err("Unable to perform snapstore cleanup: invalid user buffer\n"); > + return -EINVAL; > + } > + > + pr_info("Cleanup snapstore %pUB\n", (uuid_t *)param.id); > + res = snapstore_cleanup((uuid_t *)param.id, ¶m.filled_bytes); > + > + if (res == SUCCESS) { > + if (0 != > + copy_to_user((void *)arg, ¶m, sizeof(struct ioctl_snapstore_cleanup_s))) { > + pr_err("Unable to perform snapstore cleanup: invalid user buffer\n"); > + res = -ENODATA; > + } > + } > + > + return res; > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +int ioctl_snapstore_file_multidev(unsigned long arg) > +{ > + unsigned long len; > + int res = SUCCESS; > + struct ioctl_snapstore_file_add_multidev_s param; > + struct big_buffer *ranges = NULL; //struct ioctl_range_s* ranges = NULL; > + size_t ranges_buffer_size; > + > + len = copy_from_user(¶m, (void *)arg, > + sizeof(struct ioctl_snapstore_file_add_multidev_s)); > + if (len != 0) { > + pr_err("Unable to add file to multidev snapstore: invalid user buffer\n"); > + return -EINVAL; > + } > + > + ranges_buffer_size = sizeof(struct ioctl_range_s) * param.range_count; > + > + ranges = big_buffer_alloc(ranges_buffer_size, GFP_KERNEL); > + if (ranges == NULL) { > + pr_err("Unable to add file to multidev snapstore: cannot allocate [%zu] bytes\n", > + ranges_buffer_size); > + return -ENOMEM; > + } > + > + do { > + uuid_t *id = (uuid_t *)(param.id); > + dev_t snapstore_device = MKDEV(param.dev_id.major, param.dev_id.minor); > + size_t ranges_cnt = (size_t)param.range_count; > + > + if (ranges_buffer_size != big_buffer_copy_from_user((void *)param.ranges, 0, ranges, > + ranges_buffer_size)) { > + pr_err("Unable to add file to snapstore: invalid user buffer for parameters\n"); > + res = -ENODATA; > + break; > + } > + > + res = snapstore_add_multidev(id, snapstore_device, ranges, ranges_cnt); > + } while (false); > + big_buffer_free(ranges); > + > + return res; > +} > + > +#endif > +////////////////////////////////////////////////////////////////////////// > + > +/* > + * Snapshot get errno for device > + */ > +int ioctl_snapshot_errno(unsigned long arg) > +{ > + unsigned long len; > + int res; > + struct ioctl_snapshot_errno_s param; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_dev_id_s)); > + if (len != 0) { > + pr_err("Unable failed to get snapstore error code: invalid user buffer\n"); > + return -EINVAL; > + } > + > + res = snapstore_device_errno(MKDEV(param.dev_id.major, param.dev_id.minor), > + ¶m.err_code); > + > + if (res != SUCCESS) > + return res; > + > + len = copy_to_user((void *)arg, ¶m, sizeof(struct ioctl_snapshot_errno_s)); > + if (len != 0) { > + pr_err("Unable to get snapstore error code: invalid user buffer\n"); > + return -EINVAL; > + } > + > + return SUCCESS; > +} > + > +int ioctl_collect_snapimages(unsigned long arg) > +{ > + unsigned long len; > + int status = SUCCESS; > + struct ioctl_collect_snapshot_images_s param; > + > + len = copy_from_user(¶m, (void *)arg, sizeof(struct ioctl_collect_snapshot_images_s)); > + if (len != 0) { > + pr_err("Unable to collect snapshot images: invalid user buffer\n"); > + return -ENODATA; > + } > + > + status = snapimage_collect_images(param.count, param.p_image_info, ¶m.count); > + > + len = copy_to_user((void *)arg, ¶m, sizeof(struct ioctl_collect_snapshot_images_s)); > + if (len != 0) { > + pr_err("Unable to collect snapshot images: invalid user buffer\n"); > + return -ENODATA; > + } > + > + return status; > +} > + > +struct blk_snap_ioctl_table { > + unsigned int cmd; > + int (*fn)(unsigned long arg); > +}; > + > +static struct blk_snap_ioctl_table blk_snap_ioctl_table[] = { > + { (IOCTL_COMPATIBILITY_FLAGS), ioctl_compatibility_flags }, > + { (IOCTL_GETVERSION), ioctl_get_version }, > + > + { (IOCTL_TRACKING_ADD), ioctl_tracking_add }, > + { (IOCTL_TRACKING_REMOVE), ioctl_tracking_remove }, > + { (IOCTL_TRACKING_COLLECT), ioctl_tracking_collect }, > + { (IOCTL_TRACKING_BLOCK_SIZE), ioctl_tracking_block_size }, > + { (IOCTL_TRACKING_READ_CBT_BITMAP), ioctl_tracking_read_cbt_map }, > + { (IOCTL_TRACKING_MARK_DIRTY_BLOCKS), ioctl_tracking_mark_dirty_blocks }, > + > + { (IOCTL_SNAPSHOT_CREATE), ioctl_snapshot_create }, > + { (IOCTL_SNAPSHOT_DESTROY), ioctl_snapshot_destroy }, > + { (IOCTL_SNAPSHOT_ERRNO), ioctl_snapshot_errno }, > + > + { (IOCTL_SNAPSTORE_CREATE), ioctl_snapstore_create }, > + { (IOCTL_SNAPSTORE_FILE), ioctl_snapstore_file }, > + { (IOCTL_SNAPSTORE_MEMORY), ioctl_snapstore_memory }, > + { (IOCTL_SNAPSTORE_CLEANUP), ioctl_snapstore_cleanup }, > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + { (IOCTL_SNAPSTORE_FILE_MULTIDEV), ioctl_snapstore_file_multidev }, > +#endif > + { (IOCTL_COLLECT_SNAPSHOT_IMAGES), ioctl_collect_snapimages }, > + { 0, NULL } > +}; > + > +long ctrl_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > +{ > + long status = -ENOTTY; > + size_t inx = 0; > + > + while (blk_snap_ioctl_table[inx].cmd != 0) { > + if (blk_snap_ioctl_table[inx].cmd == cmd) { > + status = blk_snap_ioctl_table[inx].fn(arg); > + break; > + } > + ++inx; > + } > + > + return status; > +} > diff --git a/drivers/block/blk-snap/ctrl_fops.h b/drivers/block/blk-snap/ctrl_fops.h > new file mode 100644 > index 000000000000..98072b61aa96 > --- /dev/null > +++ b/drivers/block/blk-snap/ctrl_fops.h > @@ -0,0 +1,19 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include <linux/fs.h> > + > +int get_blk_snap_major(void); > + > +int ctrl_init(void); > +void ctrl_done(void); > + > +int ctrl_open(struct inode *inode, struct file *file); > +int ctrl_release(struct inode *inode, struct file *file); > + > +ssize_t ctrl_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset); > +ssize_t ctrl_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset); > + > +unsigned int ctrl_poll(struct file *filp, struct poll_table_struct *wait); > + > +long ctrl_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg); > diff --git a/drivers/block/blk-snap/ctrl_pipe.c b/drivers/block/blk-snap/ctrl_pipe.c > new file mode 100644 > index 000000000000..73cfbca93487 > --- /dev/null > +++ b/drivers/block/blk-snap/ctrl_pipe.c > @@ -0,0 +1,562 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-ctrl" > +#include "common.h" > +#include "ctrl_pipe.h" > +#include "version.h" > +#include "blk-snap-ctl.h" > +#include "snapstore.h" > +#include "big_buffer.h" > + > +#include <linux/poll.h> > +#include <linux/uuid.h> > + > +#define CMD_TO_USER_FIFO_SIZE 1024 > + > +LIST_HEAD(ctl_pipes); > +DECLARE_RWSEM(ctl_pipes_lock); > + > + > +static void ctrl_pipe_push_request(struct ctrl_pipe *pipe, unsigned int *cmd, size_t cmd_len) > +{ > + kfifo_in_spinlocked(&pipe->cmd_to_user, cmd, (cmd_len * sizeof(unsigned int)), > + &pipe->cmd_to_user_lock); > + > + wake_up(&pipe->readq); > +} > + > +static void ctrl_pipe_request_acknowledge(struct ctrl_pipe *pipe, unsigned int result) > +{ > + unsigned int cmd[2]; > + > + cmd[0] = BLK_SNAP_CHARCMD_ACKNOWLEDGE; > + cmd[1] = result; > + > + ctrl_pipe_push_request(pipe, cmd, 2); > +} > + > +static inline dev_t _snapstore_dev(struct ioctl_dev_id_s *dev_id) > +{ > + if ((dev_id->major == 0) && (dev_id->minor == 0)) > + return 0; //memory snapstore > + > + if ((dev_id->major == -1) && (dev_id->minor == -1)) > + return 0xFFFFffff; //multidevice snapstore > + > + return MKDEV(dev_id->major, dev_id->minor); > +} > + > +static ssize_t ctrl_pipe_command_initiate(struct ctrl_pipe *pipe, const char __user *buffer, > + size_t length) > +{ > + unsigned long len; > + int result = SUCCESS; > + ssize_t processed = 0; > + char *kernel_buffer; > + > + kernel_buffer = kmalloc(length, GFP_KERNEL); > + if (kernel_buffer == NULL) > + return -ENOMEM; > + > + len = copy_from_user(kernel_buffer, buffer, length); > + if (len != 0) { > + kfree(kernel_buffer); > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + return -EINVAL; > + } > + > + do { > + u64 stretch_empty_limit; > + unsigned int dev_id_list_length; > + uuid_t *unique_id; > + struct ioctl_dev_id_s *snapstore_dev_id; > + struct ioctl_dev_id_s *dev_id_list; > + > + //get snapstore uuid > + if ((length - processed) < 16) { > + pr_err("Unable to get snapstore uuid: invalid ctrl pipe initiate command. length=%zu\n", > + length); > + break; > + } > + unique_id = (uuid_t *)(kernel_buffer + processed); > + processed += 16; > + > + //get snapstore empty limit > + if ((length - processed) < sizeof(u64)) { > + pr_err("Unable to get stretch snapstore limit: invalid ctrl pipe initiate command. length=%zu\n", > + length); > + break; > + } > + stretch_empty_limit = *(u64 *)(kernel_buffer + processed); > + processed += sizeof(u64); > + > + //get snapstore device id > + if ((length - processed) < sizeof(struct ioctl_dev_id_s)) { > + pr_err("Unable to get snapstore device id: invalid ctrl pipe initiate command. length=%zu\n", > + length); > + break; > + } > + snapstore_dev_id = (struct ioctl_dev_id_s *)(kernel_buffer + processed); > + processed += sizeof(struct ioctl_dev_id_s); > + > + //get device id list length > + if ((length - processed) < 4) { > + pr_err("Unable to get device id list length: ivalid ctrl pipe initiate command. length=%zu\n", > + length); > + break; > + } > + dev_id_list_length = *(unsigned int *)(kernel_buffer + processed); > + processed += sizeof(unsigned int); > + > + //get devices id list > + if ((length - processed) < (dev_id_list_length * sizeof(struct ioctl_dev_id_s))) { > + pr_err("Unable to get all devices from device id list: invalid ctrl pipe initiate command. length=%zu\n", > + length); > + break; > + } > + dev_id_list = (struct ioctl_dev_id_s *)(kernel_buffer + processed); > + processed += (dev_id_list_length * sizeof(struct ioctl_dev_id_s)); > + > + { > + size_t inx; > + dev_t *dev_set; > + size_t dev_id_set_length = (size_t)dev_id_list_length; > + > + dev_set = kcalloc(dev_id_set_length, sizeof(dev_t), GFP_KERNEL); > + if (dev_set == NULL) { > + result = -ENOMEM; > + break; > + } > + > + for (inx = 0; inx < dev_id_set_length; ++inx) > + dev_set[inx] = > + MKDEV(dev_id_list[inx].major, dev_id_list[inx].minor); > + > + result = snapstore_create(unique_id, _snapstore_dev(snapstore_dev_id), > + dev_set, dev_id_set_length); > + kfree(dev_set); > + if (result != SUCCESS) { > + pr_err("Failed to create snapstore\n"); > + break; > + } > + > + result = snapstore_stretch_initiate( > + unique_id, pipe, (sector_t)to_sectors(stretch_empty_limit)); > + if (result != SUCCESS) { > + pr_err("Failed to initiate stretch snapstore %pUB\n", unique_id); > + break; > + } > + } > + } while (false); > + kfree(kernel_buffer); > + ctrl_pipe_request_acknowledge(pipe, result); > + > + if (result == SUCCESS) > + return processed; > + return result; > +} > + > +static ssize_t ctrl_pipe_command_next_portion(struct ctrl_pipe *pipe, const char __user *buffer, > + size_t length) > +{ > + unsigned long len; > + int result = SUCCESS; > + ssize_t processed = 0; > + struct big_buffer *ranges = NULL; > + > + do { > + uuid_t unique_id; > + unsigned int ranges_length; > + size_t ranges_buffer_size; > + > + //get snapstore id > + if ((length - processed) < 16) { > + pr_err("Unable to get snapstore id: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", > + length); > + break; > + } > + len = copy_from_user(&unique_id, buffer + processed, sizeof(uuid_t)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += 16; > + > + //get ranges length > + if ((length - processed) < 4) { > + pr_err("Unable to get device id list length: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", > + length); > + break; > + } > + len = copy_from_user(&ranges_length, buffer + processed, sizeof(unsigned int)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += sizeof(unsigned int); > + > + ranges_buffer_size = ranges_length * sizeof(struct ioctl_range_s); > + > + // ranges > + if ((length - processed) < (ranges_buffer_size)) { > + pr_err("Unable to get all ranges: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", > + length); > + break; > + } > + ranges = big_buffer_alloc(ranges_buffer_size, GFP_KERNEL); > + if (ranges == NULL) { > + pr_err("Unable to allocate page array buffer: "); > + pr_err("failed to process next portion command\n"); > + processed = -ENOMEM; > + break; > + } > + if (ranges_buffer_size != > + big_buffer_copy_from_user(buffer + processed, 0, ranges, ranges_buffer_size)) { > + pr_err("Unable to process next portion command: "); > + pr_err("invalid user buffer for parameters\n"); > + processed = -EINVAL; > + break; > + } > + processed += ranges_buffer_size; > + > + { > + result = snapstore_add_file(&unique_id, ranges, ranges_length); > + > + if (result != SUCCESS) { > + pr_err("Failed to add file to snapstore\n"); > + result = -ENODEV; > + break; > + } > + } > + } while (false); > + if (ranges) > + big_buffer_free(ranges); > + > + if (result == SUCCESS) > + return processed; > + return result; > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +static ssize_t ctrl_pipe_command_next_portion_multidev(struct ctrl_pipe *pipe, > + const char __user *buffer, size_t length) > +{ > + unsigned long len; > + int result = SUCCESS; > + ssize_t processed = 0; > + struct big_buffer *ranges = NULL; > + > + do { > + uuid_t unique_id; > + int snapstore_major; > + int snapstore_minor; > + unsigned int ranges_length; > + size_t ranges_buffer_size; > + > + //get snapstore id > + if ((length - processed) < 16) { > + pr_err("Unable to get snapstore id: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", > + length); > + break; > + } > + len = copy_from_user(&unique_id, buffer + processed, sizeof(uuid_t)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += 16; > + > + //get device id > + if ((length - processed) < 8) { > + pr_err("Unable to get device id list length: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", length); > + break; > + } > + len = copy_from_user(&snapstore_major, buffer + processed, sizeof(unsigned int)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += sizeof(unsigned int); > + > + len = copy_from_user(&snapstore_minor, buffer + processed, sizeof(unsigned int)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += sizeof(unsigned int); > + > + //get ranges length > + if ((length - processed) < 4) { > + pr_err("Unable to get device id list length: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", > + length); > + break; > + } > + len = copy_from_user(&ranges_length, buffer + processed, sizeof(unsigned int)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += sizeof(unsigned int); > + > + ranges_buffer_size = ranges_length * sizeof(struct ioctl_range_s); > + > + // ranges > + if ((length - processed) < (ranges_buffer_size)) { > + pr_err("Unable to get all ranges: "); > + pr_err("invalid ctrl pipe next portion command. length=%zu\n", > + length); > + break; > + } > + ranges = big_buffer_alloc(ranges_buffer_size, GFP_KERNEL); > + if (ranges == NULL) { > + pr_err("Unable to process next portion command: "); > + pr_err("failed to allocate page array buffer\n"); > + processed = -ENOMEM; > + break; > + } > + if (ranges_buffer_size != > + big_buffer_copy_from_user(buffer + processed, 0, ranges, ranges_buffer_size)) { > + pr_err("Unable to process next portion command: "); > + pr_err("invalid user buffer from parameters\n"); > + processed = -EINVAL; > + break; > + } > + processed += ranges_buffer_size; > + > + { > + result = snapstore_add_multidev(&unique_id, > + MKDEV(snapstore_major, snapstore_minor), > + ranges, ranges_length); > + > + if (result != SUCCESS) { > + pr_err("Failed to add file to snapstore\n"); > + result = -ENODEV; > + break; > + } > + } > + } while (false); > + if (ranges) > + big_buffer_free(ranges); > + > + if (result == SUCCESS) > + return processed; > + > + return result; > +} > +#endif > + > +static void ctrl_pipe_release_cb(struct kref *kref) > +{ > + struct ctrl_pipe *pipe = container_of(kref, struct ctrl_pipe, refcount); > + > + down_write(&ctl_pipes_lock); > + list_del(&pipe->link); > + up_write(&ctl_pipes_lock); > + > + kfifo_free(&pipe->cmd_to_user); > + > + kfree(pipe); > +} > + > +struct ctrl_pipe *ctrl_pipe_get_resource(struct ctrl_pipe *pipe) > +{ > + if (pipe) > + kref_get(&pipe->refcount); > + > + return pipe; > +} > + > +void ctrl_pipe_put_resource(struct ctrl_pipe *pipe) > +{ > + if (pipe) > + kref_put(&pipe->refcount, ctrl_pipe_release_cb); > +} > + > +void ctrl_pipe_done(void) > +{ > + bool is_empty; > + > + pr_info("Ctrl pipes - done\n"); > + > + down_write(&ctl_pipes_lock); > + is_empty = list_empty(&ctl_pipes); > + up_write(&ctl_pipes_lock); > + > + if (!is_empty) > + pr_err("Unable to perform ctrl pipes cleanup: container is not empty\n"); > +} > + > +struct ctrl_pipe *ctrl_pipe_new(void) > +{ > + int ret; > + struct ctrl_pipe *pipe; > + > + pipe = kzalloc(sizeof(struct ctrl_pipe), GFP_KERNEL); > + if (pipe == NULL) > + return NULL; > + > + INIT_LIST_HEAD(&pipe->link); > + > + ret = kfifo_alloc(&pipe->cmd_to_user, CMD_TO_USER_FIFO_SIZE, GFP_KERNEL); > + if (ret) { > + pr_err("Failed to allocate fifo. errno=%d.\n", ret); > + kfree(pipe); > + return NULL; > + } > + spin_lock_init(&pipe->cmd_to_user_lock); > + > + kref_init(&pipe->refcount); > + > + init_waitqueue_head(&pipe->readq); > + > + down_write(&ctl_pipes_lock); > + list_add_tail(&pipe->link, &ctl_pipes); > + up_write(&ctl_pipes_lock); > + > + return pipe; > +} > + > +ssize_t ctrl_pipe_read(struct ctrl_pipe *pipe, char __user *buffer, size_t length) > +{ > + int ret; > + unsigned int processed = 0; > + > + if (kfifo_is_empty_spinlocked(&pipe->cmd_to_user, &pipe->cmd_to_user_lock)) { > + //nothing to read > + ret = wait_event_interruptible(pipe->readq, > + !kfifo_is_empty_spinlocked(&pipe->cmd_to_user, > + &pipe->cmd_to_user_lock)); > + if (ret) { > + pr_err("Unable to wait for pipe read queue: interrupt signal was received\n"); > + return -ERESTARTSYS; > + } > + } > + > + ret = kfifo_to_user(&pipe->cmd_to_user, buffer, length, &processed); > + if (ret) { > + pr_err("Failed to read command from ctrl pipe\n"); > + return ret; > + } > + > + return (ssize_t)processed; > +} > + > +ssize_t ctrl_pipe_write(struct ctrl_pipe *pipe, const char __user *buffer, size_t length) > +{ > + ssize_t processed = 0; > + > + do { > + unsigned long len; > + unsigned int command; > + > + if ((length - processed) < 4) { > + pr_err("Unable to write command to ctrl pipe: invalid command length=%zu\n", > + length); > + break; > + } > + len = copy_from_user(&command, buffer + processed, sizeof(unsigned int)); > + if (len != 0) { > + pr_err("Unable to write to pipe: invalid user buffer\n"); > + processed = -EINVAL; > + break; > + } > + processed += sizeof(unsigned int); > + //+4 > + switch (command) { > + case BLK_SNAP_CHARCMD_INITIATE: { > + ssize_t res = ctrl_pipe_command_initiate(pipe, buffer + processed, > + length - processed); > + if (res >= 0) > + processed += res; > + else > + processed = res; > + } break; > + case BLK_SNAP_CHARCMD_NEXT_PORTION: { > + ssize_t res = ctrl_pipe_command_next_portion(pipe, buffer + processed, > + length - processed); > + if (res >= 0) > + processed += res; > + else > + processed = res; > + } break; > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + case BLK_SNAP_CHARCMD_NEXT_PORTION_MULTIDEV: { > + ssize_t res = ctrl_pipe_command_next_portion_multidev( > + pipe, buffer + processed, length - processed); > + if (res >= 0) > + processed += res; > + else > + processed = res; > + } break; > +#endif > + default: > + pr_err("Ctrl pipe write error: invalid command [0x%x] received\n", command); > + break; > + } > + } while (false); > + return processed; > +} > + > +unsigned int ctrl_pipe_poll(struct ctrl_pipe *pipe) > +{ > + unsigned int mask = 0; > + > + if (!kfifo_is_empty_spinlocked(&pipe->cmd_to_user, &pipe->cmd_to_user_lock)) > + mask |= (POLLIN | POLLRDNORM); /* readable */ > + > + mask |= (POLLOUT | POLLWRNORM); /* writable */ > + > + return mask; > +} > + > +void ctrl_pipe_request_halffill(struct ctrl_pipe *pipe, unsigned long long filled_status) > +{ > + unsigned int cmd[3]; > + > + pr_info("Snapstore is half-full\n"); > + > + cmd[0] = (unsigned int)BLK_SNAP_CHARCMD_HALFFILL; > + cmd[1] = (unsigned int)(filled_status & 0xFFFFffff); //lo > + cmd[2] = (unsigned int)(filled_status >> 32); > + > + ctrl_pipe_push_request(pipe, cmd, 3); > +} > + > +void ctrl_pipe_request_overflow(struct ctrl_pipe *pipe, unsigned int error_code, > + unsigned long long filled_status) > +{ > + unsigned int cmd[4]; > + > + pr_info("Snapstore overflow\n"); > + > + cmd[0] = (unsigned int)BLK_SNAP_CHARCMD_OVERFLOW; > + cmd[1] = error_code; > + cmd[2] = (unsigned int)(filled_status & 0xFFFFffff); //lo > + cmd[3] = (unsigned int)(filled_status >> 32); > + > + ctrl_pipe_push_request(pipe, cmd, 4); > +} > + > +void ctrl_pipe_request_terminate(struct ctrl_pipe *pipe, unsigned long long filled_status) > +{ > + unsigned int cmd[3]; > + > + pr_info("Snapstore termination\n"); > + > + cmd[0] = (unsigned int)BLK_SNAP_CHARCMD_TERMINATE; > + cmd[1] = (unsigned int)(filled_status & 0xFFFFffff); //lo > + cmd[2] = (unsigned int)(filled_status >> 32); > + > + ctrl_pipe_push_request(pipe, cmd, 3); > +} > diff --git a/drivers/block/blk-snap/ctrl_pipe.h b/drivers/block/blk-snap/ctrl_pipe.h > new file mode 100644 > index 000000000000..1aa1099eec25 > --- /dev/null > +++ b/drivers/block/blk-snap/ctrl_pipe.h > @@ -0,0 +1,34 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include <linux/kref.h> > +#include <linux/wait.h> > +#include <linux/kfifo.h> > + > +struct ctrl_pipe { > + struct list_head link; > + > + struct kref refcount; > + > + wait_queue_head_t readq; > + > + struct kfifo cmd_to_user; > + spinlock_t cmd_to_user_lock; > +}; > + > +struct ctrl_pipe *ctrl_pipe_get_resource(struct ctrl_pipe *pipe); > +void ctrl_pipe_put_resource(struct ctrl_pipe *pipe); > + > +void ctrl_pipe_done(void); > + > +struct ctrl_pipe *ctrl_pipe_new(void); > + > +ssize_t ctrl_pipe_read(struct ctrl_pipe *pipe, char __user *buffer, size_t length); > +ssize_t ctrl_pipe_write(struct ctrl_pipe *pipe, const char __user *buffer, size_t length); > + > +unsigned int ctrl_pipe_poll(struct ctrl_pipe *pipe); > + > +void ctrl_pipe_request_halffill(struct ctrl_pipe *pipe, unsigned long long filled_status); > +void ctrl_pipe_request_overflow(struct ctrl_pipe *pipe, unsigned int error_code, > + unsigned long long filled_status); > +void ctrl_pipe_request_terminate(struct ctrl_pipe *pipe, unsigned long long filled_status); > diff --git a/drivers/block/blk-snap/ctrl_sysfs.c b/drivers/block/blk-snap/ctrl_sysfs.c > new file mode 100644 > index 000000000000..4ec78e85b510 > --- /dev/null > +++ b/drivers/block/blk-snap/ctrl_sysfs.c > @@ -0,0 +1,73 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-ctrl" > +#include "common.h" > +#include "ctrl_sysfs.h" > +#include "ctrl_fops.h" > +#include "blk-snap-ctl.h" > + > +#include <linux/blkdev.h> > +#include <linux/sysfs.h> > + > +static ssize_t major_show(struct class *class, struct class_attribute *attr, char *buf) > +{ > + sprintf(buf, "%d", get_blk_snap_major()); > + return strlen(buf); > +} > + > +CLASS_ATTR_RO(major); // declare class_attr_major > +static struct class *blk_snap_class; > + > +static struct device *blk_snap_device; > + > +int ctrl_sysfs_init(void) > +{ > + struct device *dev; > + int res; > + > + blk_snap_class = class_create(THIS_MODULE, MODULE_NAME); > + if (IS_ERR(blk_snap_class)) { > + res = PTR_ERR(blk_snap_class); > + > + pr_err("Bad class create. errno=%d\n", 0 - res); > + return res; > + } > + > + pr_info("Create 'major' sysfs attribute\n"); > + res = class_create_file(blk_snap_class, &class_attr_major); > + if (res != SUCCESS) { > + pr_err("Failed to create 'major' sysfs file\n"); > + > + class_destroy(blk_snap_class); > + blk_snap_class = NULL; > + return res; > + } > + > + dev = device_create(blk_snap_class, NULL, MKDEV(get_blk_snap_major(), 0), NULL, > + MODULE_NAME); > + if (IS_ERR(dev)) { > + res = PTR_ERR(dev); > + pr_err("Failed to create device, errno=%d\n", res); > + > + class_remove_file(blk_snap_class, &class_attr_major); > + class_destroy(blk_snap_class); > + blk_snap_class = NULL; > + return res; > + } > + > + blk_snap_device = dev; > + return res; > +} > + > +void ctrl_sysfs_done(void) > +{ > + if (blk_snap_device) { > + device_unregister(blk_snap_device); > + blk_snap_device = NULL; > + } > + > + if (blk_snap_class != NULL) { > + class_remove_file(blk_snap_class, &class_attr_major); > + class_destroy(blk_snap_class); > + blk_snap_class = NULL; > + } > +} > diff --git a/drivers/block/blk-snap/ctrl_sysfs.h b/drivers/block/blk-snap/ctrl_sysfs.h > new file mode 100644 > index 000000000000..27a2a4d3da4c > --- /dev/null > +++ b/drivers/block/blk-snap/ctrl_sysfs.h > @@ -0,0 +1,5 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +int ctrl_sysfs_init(void); > +void ctrl_sysfs_done(void); > diff --git a/drivers/block/blk-snap/defer_io.c b/drivers/block/blk-snap/defer_io.c > new file mode 100644 > index 000000000000..309216fe7319 > --- /dev/null > +++ b/drivers/block/blk-snap/defer_io.c > @@ -0,0 +1,397 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-defer_io" > +#include "common.h" > +#include "defer_io.h" > +#include "blk_deferred.h" > +#include "tracker.h" > +#include "blk_util.h" > + > +#include <linux/kthread.h> > + > +#define BLK_IMAGE_THROTTLE_TIMEOUT (1 * HZ) //delay 1 sec > +//#define BLK_IMAGE_THROTTLE_TIMEOUT ( HZ/1000 * 10 ) //delay 10 ms > + > + > + > +struct defer_io_orig_rq { > + struct list_head link; > + struct defer_io_queue *queue; > + > + struct bio *bio; > + struct tracker *tracker; > +}; > + > +static inline void defer_io_queue_init(struct defer_io_queue *queue) > +{ > + INIT_LIST_HEAD(&queue->list); > + > + spin_lock_init(&queue->lock); > + > + atomic_set(&queue->in_queue_cnt, 0); > + atomic_set(&queue->active_state, true); > +} > + > +static inline struct defer_io_orig_rq *defer_io_queue_new(struct defer_io_queue *queue, struct bio *bio) > +{ > + struct defer_io_orig_rq *dio_rq; > + > + dio_rq = kzalloc(sizeof(struct defer_io_orig_rq), GFP_NOIO); > + if (dio_rq == NULL) > + return NULL; > + > + dio_rq->bio = bio; > + bio_get(dio_rq->bio); > + > + INIT_LIST_HEAD(&dio_rq->link); > + dio_rq->queue = queue; > + > + return dio_rq; > +} > + > +static inline void defer_io_queue_free(struct defer_io_orig_rq *dio_rq) > +{ > + if (likely(dio_rq)) { > + if (likely(dio_rq->bio)) { > + bio_put(dio_rq->bio); > + dio_rq->bio = NULL; > + } > + kfree(dio_rq); > + } > +} > + > +static int defer_io_queue_push_back(struct defer_io_queue *queue, struct defer_io_orig_rq *dio_rq) > +{ > + int res = SUCCESS; > + > + spin_lock(&queue->lock); > + > + if (atomic_read(&queue->active_state)) { > + list_add_tail(&dio_rq->link, &queue->list); > + atomic_inc(&queue->in_queue_cnt); > + } else > + res = -EACCES; > + > + spin_unlock(&queue->lock); > + return res; > +} > + > +static struct defer_io_orig_rq *defer_io_queue_get_first(struct defer_io_queue *queue) > +{ > + struct defer_io_orig_rq *dio_rq = NULL; > + > + spin_lock(&queue->lock); > + > + if (!list_empty(&queue->list)) { > + dio_rq = list_entry(queue->list.next, struct defer_io_orig_rq, link); > + list_del(&dio_rq->link); > + atomic_dec(&queue->in_queue_cnt); > + } > + > + spin_unlock(&queue->lock); > + > + return dio_rq; > +} > + > +static bool defer_io_queue_active(struct defer_io_queue *queue, bool state) > +{ > + bool prev_state; > + > + spin_lock(&queue->lock); > + > + prev_state = atomic_read(&queue->active_state); > + atomic_set(&queue->active_state, state); > + > + spin_unlock(&queue->lock); > + > + return prev_state; > +} > + > +#define defer_io_queue_empty(queue) (atomic_read(&(queue).in_queue_cnt) == 0) > + > +static void _defer_io_finish(struct defer_io *defer_io, struct defer_io_queue *queue_in_progress) > +{ > + while (!defer_io_queue_empty(*queue_in_progress)) { > + struct tracker *tracker = NULL; > + bool cbt_locked = false; > + bool is_write_bio; > + sector_t sectCount = 0; > + > + struct defer_io_orig_rq *orig_req = defer_io_queue_get_first(queue_in_progress); > + > + is_write_bio = bio_data_dir(orig_req->bio) && bio_has_data(orig_req->bio); > + > + if (orig_req->tracker && is_write_bio) { > + tracker = orig_req->tracker; > + cbt_locked = tracker_cbt_bitmap_lock(tracker); > + if (cbt_locked) { > + sectCount = bio_sectors(orig_req->bio); > + tracker_cbt_bitmap_set(tracker, orig_req->bio->bi_iter.bi_sector, > + sectCount); > + } > + } > + > + submit_bio_direct(orig_req->bio); > + > + if (cbt_locked) > + tracker_cbt_bitmap_unlock(tracker); > + > + defer_io_queue_free(orig_req); > + } > +} > + > +static int _defer_io_copy_prepare(struct defer_io *defer_io, > + struct defer_io_queue *queue_in_process, > + struct blk_deferred_request **dio_copy_req) > +{ > + int res = SUCCESS; > + int dios_count = 0; > + sector_t dios_sectors_count = 0; > + > + //fill copy_request set > + while (!defer_io_queue_empty(defer_io->dio_queue) && > + (dios_count < DEFER_IO_DIO_REQUEST_LENGTH) && > + (dios_sectors_count < DEFER_IO_DIO_REQUEST_SECTORS_COUNT)) { > + struct defer_io_orig_rq *dio_orig_req = > + (struct defer_io_orig_rq *)defer_io_queue_get_first(&defer_io->dio_queue); > + atomic_dec(&defer_io->queue_filling_count); > + > + defer_io_queue_push_back(queue_in_process, dio_orig_req); > + > + if (!kthread_should_stop() && > + !snapstore_device_is_corrupted(defer_io->snapstore_device)) { > + if (bio_data_dir(dio_orig_req->bio) && bio_has_data(dio_orig_req->bio)) { > + struct blk_range copy_range; > + > + copy_range.ofs = dio_orig_req->bio->bi_iter.bi_sector; > + copy_range.cnt = bio_sectors(dio_orig_req->bio); > + res = snapstore_device_prepare_requests(defer_io->snapstore_device, > + ©_range, dio_copy_req); > + if (res != SUCCESS) { > + pr_err("Unable to execute Copy On Write algorithm: failed to add ranges to copy to snapstore request. errno=%d\n", > + res); > + break; > + } > + > + dios_sectors_count += copy_range.cnt; > + } > + } > + ++dios_count; > + } > + return res; > +} > + > +static int defer_io_work_thread(void *p) > +{ > + struct defer_io_queue queue_in_process = { 0 }; > + struct defer_io *defer_io = NULL; > + > + //set_user_nice( current, -20 ); //MIN_NICE > + defer_io_queue_init(&queue_in_process); > + > + defer_io = defer_io_get_resource((struct defer_io *)p); > + pr_info("Defer IO thread for original device [%d:%d] started\n", > + MAJOR(defer_io->original_dev_id), MINOR(defer_io->original_dev_id)); > + > + while (!kthread_should_stop() || !defer_io_queue_empty(defer_io->dio_queue)) { > + if (defer_io_queue_empty(defer_io->dio_queue)) { > + int res = wait_event_interruptible_timeout( > + defer_io->queue_add_event, > + (!defer_io_queue_empty(defer_io->dio_queue)), > + BLK_IMAGE_THROTTLE_TIMEOUT); > + if (-ERESTARTSYS == res) > + pr_err("Signal received in defer IO thread. Waiting for completion with code ERESTARTSYS\n"); > + } > + > + if (!defer_io_queue_empty(defer_io->dio_queue)) { > + int dio_copy_result = SUCCESS; > + struct blk_deferred_request *dio_copy_req = NULL; > + > + mutex_lock(&defer_io->snapstore_device->store_block_map_locker); > + do { > + dio_copy_result = _defer_io_copy_prepare( > + defer_io, &queue_in_process, &dio_copy_req); > + if (dio_copy_result != SUCCESS) { > + pr_err("Unable to process defer IO request: failed to prepare copy request. erro=%d\n", > + dio_copy_result); > + break; > + } > + if (dio_copy_req == NULL) > + break; //nothing to copy > + > + dio_copy_result = blk_deferred_request_read_original( > + defer_io->original_blk_dev, dio_copy_req); > + if (dio_copy_result != SUCCESS) { > + pr_err("Unable to process defer IO request: failed to read data to copy request. errno=%d\n", > + dio_copy_result); > + break; > + } > + dio_copy_result = snapstore_device_store(defer_io->snapstore_device, > + dio_copy_req); > + if (dio_copy_result != SUCCESS) { > + pr_err("Unable to process defer IO request: failed to write data from copy request. errno=%d\n", > + dio_copy_result); > + break; > + } > + > + } while (false); > + _defer_io_finish(defer_io, &queue_in_process); > + mutex_unlock(&defer_io->snapstore_device->store_block_map_locker); > + > + if (dio_copy_req) { > + if (dio_copy_result == -EDEADLK) > + blk_deferred_request_deadlocked(dio_copy_req); > + else > + blk_deferred_request_free(dio_copy_req); > + } > + } > + > + //wake up snapimage if defer io queue empty > + if (defer_io_queue_empty(defer_io->dio_queue)) > + wake_up_interruptible(&defer_io->queue_throttle_waiter); > + } > + defer_io_queue_active(&defer_io->dio_queue, false); > + > + //waiting for all sent request complete > + _defer_io_finish(defer_io, &defer_io->dio_queue); > + > + pr_info("Defer IO thread for original device [%d:%d] completed\n", > + MAJOR(defer_io->original_dev_id), MINOR(defer_io->original_dev_id)); > + defer_io_put_resource(defer_io); > + return SUCCESS; > +} > + > +static void _defer_io_destroy(struct defer_io *defer_io) > +{ > + if (defer_io == NULL) > + return; > + > + if (defer_io->dio_thread) > + defer_io_stop(defer_io); > + > + if (defer_io->snapstore_device) > + snapstore_device_put_resource(defer_io->snapstore_device); > + > + kfree(defer_io); > + pr_info("Defer IO processor was destroyed\n"); > +} > + > +static void defer_io_destroy_cb(struct kref *kref) > +{ > + _defer_io_destroy(container_of(kref, struct defer_io, refcount)); > +} > + > +struct defer_io *defer_io_get_resource(struct defer_io *defer_io) > +{ > + if (defer_io) > + kref_get(&defer_io->refcount); > + > + return defer_io; > +} > + > +void defer_io_put_resource(struct defer_io *defer_io) > +{ > + if (defer_io) > + kref_put(&defer_io->refcount, defer_io_destroy_cb); > +} > + > +int defer_io_create(dev_t dev_id, struct block_device *blk_dev, struct defer_io **pp_defer_io) > +{ > + int res = SUCCESS; > + struct defer_io *defer_io = NULL; > + struct snapstore_device *snapstore_device; > + > + pr_info("Defer IO processor was created for device [%d:%d]\n", MAJOR(dev_id), > + MINOR(dev_id)); > + > + defer_io = kzalloc(sizeof(struct defer_io), GFP_KERNEL); > + if (defer_io == NULL) > + return -ENOMEM; > + > + snapstore_device = snapstore_device_find_by_dev_id(dev_id); > + if (snapstore_device == NULL) { > + pr_err("Unable to create defer IO processor: failed to initialize snapshot data for device [%d:%d]\n", > + MAJOR(dev_id), MINOR(dev_id)); > + > + kfree(defer_io); > + return -ENODATA; > + } > + > + defer_io->snapstore_device = snapstore_device_get_resource(snapstore_device); > + defer_io->original_dev_id = dev_id; > + defer_io->original_blk_dev = blk_dev; > + > + kref_init(&defer_io->refcount); > + > + defer_io_queue_init(&defer_io->dio_queue); > + > + init_waitqueue_head(&defer_io->queue_add_event); > + > + atomic_set(&defer_io->queue_filling_count, 0); > + > + init_waitqueue_head(&defer_io->queue_throttle_waiter); > + > + defer_io->dio_thread = kthread_create(defer_io_work_thread, (void *)defer_io, > + "blksnapdeferio%d:%d", MAJOR(dev_id), MINOR(dev_id)); > + if (IS_ERR(defer_io->dio_thread)) { > + res = PTR_ERR(defer_io->dio_thread); > + pr_err("Unable to create defer IO processor: failed to create thread. errno=%d\n", > + res); > + > + _defer_io_destroy(defer_io); > + defer_io = NULL; > + *pp_defer_io = NULL; > + > + return res; > + } > + > + wake_up_process(defer_io->dio_thread); > + > + *pp_defer_io = defer_io; > + pr_info("Defer IO processor was created\n"); > + > + return SUCCESS; > +} > + > +int defer_io_stop(struct defer_io *defer_io) > +{ > + int res = SUCCESS; > + > + pr_info("Defer IO thread for the device stopped [%d:%d]\n", > + MAJOR(defer_io->original_dev_id), MINOR(defer_io->original_dev_id)); > + > + if (defer_io->dio_thread != NULL) { > + struct task_struct *dio_thread = defer_io->dio_thread; > + > + defer_io->dio_thread = NULL; > + res = kthread_stop(dio_thread); //stopping and waiting. > + if (res != SUCCESS) > + pr_err("Failed to stop defer IO thread. errno=%d\n", res); > + } > + > + return res; > +} > + > +int defer_io_redirect_bio(struct defer_io *defer_io, struct bio *bio, void *tracker) > +{ > + struct defer_io_orig_rq *dio_orig_req; > + > + if (snapstore_device_is_corrupted(defer_io->snapstore_device)) > + return -ENODATA; > + > + dio_orig_req = defer_io_queue_new(&defer_io->dio_queue, bio); > + if (dio_orig_req == NULL) > + return -ENOMEM; > + > + dio_orig_req->tracker = (struct tracker *)tracker; > + > + if (defer_io_queue_push_back(&defer_io->dio_queue, dio_orig_req) != SUCCESS) { > + defer_io_queue_free(dio_orig_req); > + return -EFAULT; > + } > + > + atomic_inc(&defer_io->queue_filling_count); > + > + wake_up_interruptible(&defer_io->queue_add_event); > + > + return SUCCESS; > +} > diff --git a/drivers/block/blk-snap/defer_io.h b/drivers/block/blk-snap/defer_io.h > new file mode 100644 > index 000000000000..27c3bb03241f > --- /dev/null > +++ b/drivers/block/blk-snap/defer_io.h > @@ -0,0 +1,39 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +#include <linux/kref.h> > +#include "snapstore_device.h" > + > +struct defer_io_queue { > + struct list_head list; > + spinlock_t lock; > + > + atomic_t active_state; > + atomic_t in_queue_cnt; > +}; > + > +struct defer_io { > + struct kref refcount; > + > + wait_queue_head_t queue_add_event; > + > + atomic_t queue_filling_count; > + wait_queue_head_t queue_throttle_waiter; > + > + dev_t original_dev_id; > + struct block_device *original_blk_dev; > + > + struct snapstore_device *snapstore_device; > + > + struct task_struct *dio_thread; > + > + struct defer_io_queue dio_queue; > +}; > + > +int defer_io_create(dev_t dev_id, struct block_device *blk_dev, struct defer_io **pp_defer_io); > +int defer_io_stop(struct defer_io *defer_io); > + > +struct defer_io *defer_io_get_resource(struct defer_io *defer_io); > +void defer_io_put_resource(struct defer_io *defer_io); > + > +int defer_io_redirect_bio(struct defer_io *defer_io, struct bio *bio, void *tracker); > diff --git a/drivers/block/blk-snap/main.c b/drivers/block/blk-snap/main.c > new file mode 100644 > index 000000000000..d1d4e08a4890 > --- /dev/null > +++ b/drivers/block/blk-snap/main.c > @@ -0,0 +1,82 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include "common.h" > +#include "version.h" > +#include "blk-snap-ctl.h" > +#include "params.h" > +#include "ctrl_fops.h" > +#include "ctrl_pipe.h" > +#include "ctrl_sysfs.h" > +#include "snapimage.h" > +#include "snapstore.h" > +#include "snapstore_device.h" > +#include "snapshot.h" > +#include "tracker.h" > +#include "tracking.h" > +#include <linux/module.h> > + > +int __init blk_snap_init(void) > +{ > + int result = SUCCESS; > + > + pr_info("Loading\n"); > + > + params_check(); > + > + result = ctrl_init(); > + if (result != SUCCESS) > + return result; > + > + result = blk_redirect_bioset_create(); > + if (result != SUCCESS) > + return result; > + > + result = blk_deferred_bioset_create(); > + if (result != SUCCESS) > + return result; > + > + result = snapimage_init(); > + if (result != SUCCESS) > + return result; > + > + result = ctrl_sysfs_init(); > + if (result != SUCCESS) > + return result; > + > + result = tracking_init(); > + if (result != SUCCESS) > + return result; > + > + return result; > +} > + > +void __exit blk_snap_exit(void) > +{ > + pr_info("Unloading module\n"); > + > + ctrl_sysfs_done(); > + > + snapshot_done(); > + > + snapstore_device_done(); > + snapstore_done(); > + > + tracker_done(); > + tracking_done(); > + > + snapimage_done(); > + > + blk_deferred_bioset_free(); > + blk_deferred_done(); > + > + blk_redirect_bioset_free(); > + > + ctrl_done(); > +} > + > +module_init(blk_snap_init); > +module_exit(blk_snap_exit); > + > +MODULE_DESCRIPTION("Block Layer Snapshot Kernel Module"); > +MODULE_VERSION(FILEVER_STR); > +MODULE_AUTHOR("Veeam Software Group GmbH"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/block/blk-snap/params.c b/drivers/block/blk-snap/params.c > new file mode 100644 > index 000000000000..7eba3c8bf395 > --- /dev/null > +++ b/drivers/block/blk-snap/params.c > @@ -0,0 +1,58 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include "common.h" > +#include "params.h" > +#include <linux/module.h> > + > +int snapstore_block_size_pow = 14; > +int change_tracking_block_size_pow = 18; > + > +int get_snapstore_block_size_pow(void) > +{ > + return snapstore_block_size_pow; > +} > + > +int inc_snapstore_block_size_pow(void) > +{ > + if (snapstore_block_size_pow > 30) > + return -EFAULT; > + > + ++snapstore_block_size_pow; > + return SUCCESS; > +} > + > +int get_change_tracking_block_size_pow(void) > +{ > + return change_tracking_block_size_pow; > +} > + > +void params_check(void) > +{ > + pr_info("snapstore_block_size_pow: %d\n", snapstore_block_size_pow); > + pr_info("change_tracking_block_size_pow: %d\n", change_tracking_block_size_pow); > + > + if (snapstore_block_size_pow > 23) { > + snapstore_block_size_pow = 23; > + pr_info("Limited snapstore_block_size_pow: %d\n", snapstore_block_size_pow); > + } else if (snapstore_block_size_pow < 12) { > + snapstore_block_size_pow = 12; > + pr_info("Limited snapstore_block_size_pow: %d\n", snapstore_block_size_pow); > + } > + > + if (change_tracking_block_size_pow > 23) { > + change_tracking_block_size_pow = 23; > + pr_info("Limited change_tracking_block_size_pow: %d\n", > + change_tracking_block_size_pow); > + } else if (change_tracking_block_size_pow < 12) { > + change_tracking_block_size_pow = 12; > + pr_info("Limited change_tracking_block_size_pow: %d\n", > + change_tracking_block_size_pow); > + } > +} > + > +module_param_named(snapstore_block_size_pow, snapstore_block_size_pow, int, 0644); > +MODULE_PARM_DESC(snapstore_block_size_pow, > + "Snapstore block size binary pow. 20 for 1MiB block size"); > + > +module_param_named(change_tracking_block_size_pow, change_tracking_block_size_pow, int, 0644); > +MODULE_PARM_DESC(change_tracking_block_size_pow, > + "Change-tracking block size binary pow. 18 for 256 KiB block size"); > diff --git a/drivers/block/blk-snap/params.h b/drivers/block/blk-snap/params.h > new file mode 100644 > index 000000000000..c1b853a1363b > --- /dev/null > +++ b/drivers/block/blk-snap/params.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +#pragma once > + > +int get_snapstore_block_size_pow(void); > +int inc_snapstore_block_size_pow(void); > + > +static inline sector_t snapstore_block_shift(void) > +{ > + return get_snapstore_block_size_pow() - SECTOR_SHIFT; > +}; > + > +static inline sector_t snapstore_block_size(void) > +{ > + return 1ull << snapstore_block_shift(); > +}; > + > +static inline sector_t snapstore_block_mask(void) > +{ > + return snapstore_block_size() - 1ull; > +}; > + > +int get_change_tracking_block_size_pow(void); > + > +static inline unsigned int change_tracking_block_size(void) > +{ > + return 1 << get_change_tracking_block_size_pow(); > +}; > + > +void params_check(void); > diff --git a/drivers/block/blk-snap/rangevector.c b/drivers/block/blk-snap/rangevector.c > new file mode 100644 > index 000000000000..49fe4589b6f7 > --- /dev/null > +++ b/drivers/block/blk-snap/rangevector.c > @@ -0,0 +1,85 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include "common.h" > +#include "rangevector.h" > + > +#define SECTION "ranges " > + > +static inline sector_t range_node_start(struct blk_range_tree_node *range_node) > +{ > + return range_node->range.ofs; > +} > + > +static inline sector_t range_node_last(struct blk_range_tree_node *range_node) > +{ > + return range_node->range.ofs + range_node->range.cnt - 1; > +} > + > +#ifndef INTERVAL_TREE_DEFINE > +#pragma message("INTERVAL_TREE_DEFINE is undefined") > +#endif > +INTERVAL_TREE_DEFINE(struct blk_range_tree_node, _node, sector_t, _subtree_last, > + range_node_start, range_node_last, static inline, _blk_range_rb) > + > +void blk_range_rb_insert(struct blk_range_tree_node *node, struct rb_root_cached *root) > +{ > + _blk_range_rb_insert(node, root); > +} > + > +void blk_range_rb_remove(struct blk_range_tree_node *node, struct rb_root_cached *root) > +{ > + _blk_range_rb_remove(node, root); > +} > + > +struct blk_range_tree_node *blk_range_rb_iter_first(struct rb_root_cached *root, sector_t start, > + sector_t last) > +{ > + return _blk_range_rb_iter_first(root, start, last); > +} > + > +struct blk_range_tree_node *blk_range_rb_iter_next(struct blk_range_tree_node *node, sector_t start, > + sector_t last) > +{ > + return _blk_range_rb_iter_next(node, start, last); > +} > + > +void rangevector_init(struct rangevector *rangevector) > +{ > + init_rwsem(&rangevector->lock); > + > + rangevector->root = RB_ROOT_CACHED; > +} > + > +void rangevector_done(struct rangevector *rangevector) > +{ > + struct rb_node *rb_node = NULL; > + > + down_write(&rangevector->lock); > + rb_node = rb_first_cached(&rangevector->root); > + while (rb_node) { > + struct blk_range_tree_node *range_node = (struct blk_range_tree_node *) > + rb_node; //container_of(rb_node, struct blk_range_tree_node, node); > + > + blk_range_rb_remove(range_node, &rangevector->root); > + kfree(range_node); > + > + rb_node = rb_first_cached(&rangevector->root); > + } > + up_write(&rangevector->lock); > +} > + > +int rangevector_add(struct rangevector *rangevector, struct blk_range *rg) > +{ > + struct blk_range_tree_node *range_node; > + > + range_node = kzalloc(sizeof(struct blk_range_tree_node), GFP_KERNEL); > + if (range_node) > + return -ENOMEM; > + > + range_node->range = *rg; > + > + down_write(&rangevector->lock); > + blk_range_rb_insert(range_node, &rangevector->root); > + up_write(&rangevector->lock); > + > + return SUCCESS; > +} > diff --git a/drivers/block/blk-snap/rangevector.h b/drivers/block/blk-snap/rangevector.h > new file mode 100644 > index 000000000000..5ff439423178 > --- /dev/null > +++ b/drivers/block/blk-snap/rangevector.h > @@ -0,0 +1,31 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#include <linux/interval_tree_generic.h> > + > +struct blk_range_tree_node { > + struct rb_node _node; > + struct blk_range range; > + sector_t _subtree_last; > +}; > + > +void blk_range_rb_insert(struct blk_range_tree_node *node, struct rb_root_cached *root); > + > +void blk_range_rb_remove(struct blk_range_tree_node *node, struct rb_root_cached *root); > + > +struct blk_range_tree_node *blk_range_rb_iter_first(struct rb_root_cached *root, sector_t start, > + sector_t last); > + > +struct blk_range_tree_node *blk_range_rb_iter_next(struct blk_range_tree_node *node, sector_t start, > + sector_t last); > + > +struct rangevector { > + struct rb_root_cached root; > + struct rw_semaphore lock; > +}; > + > +void rangevector_init(struct rangevector *rangevector); > + > +void rangevector_done(struct rangevector *rangevector); > + > +int rangevector_add(struct rangevector *rangevector, struct blk_range *rg); > diff --git a/drivers/block/blk-snap/snapimage.c b/drivers/block/blk-snap/snapimage.c > new file mode 100644 > index 000000000000..da971486cbef > --- /dev/null > +++ b/drivers/block/blk-snap/snapimage.c > @@ -0,0 +1,982 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapimage" > +#include "common.h" > +#include "snapimage.h" > +#include "blk_util.h" > +#include "defer_io.h" > +#include "cbt_map.h" > +#include "tracker.h" > + > +#include <asm/div64.h> > +#include <linux/cdrom.h> > +#include <linux/blk-mq.h> > +#include <linux/hdreg.h> > +#include <linux/kthread.h> > + > +#define SNAPIMAGE_MAX_DEVICES 2048 > + > +int snapimage_major; > +unsigned long *snapimage_minors; > +DEFINE_SPINLOCK(snapimage_minors_lock); > + > +LIST_HEAD(snap_images); > +DECLARE_RWSEM(snap_images_lock); > + > +DECLARE_RWSEM(snap_image_destroy_lock); > + > +struct snapimage { > + struct list_head link; > + > + sector_t capacity; > + dev_t original_dev; > + > + struct defer_io *defer_io; > + struct cbt_map *cbt_map; > + > + dev_t image_dev; > + > + struct request_queue *queue; > + struct gendisk *disk; > + > + atomic_t own_cnt; > + > + struct redirect_bio_queue image_queue; > + > + struct task_struct *rq_processor; > + > + wait_queue_head_t rq_proc_event; > + wait_queue_head_t rq_complete_event; > + > + struct mutex open_locker; > + struct block_device *open_bdev; > + > + size_t open_cnt; > +}; > + > +int _snapimage_open(struct block_device *bdev, fmode_t mode) > +{ > + int res = SUCCESS; > + > + if (bdev->bd_disk == NULL) { > + pr_err("Unable to open snapshot image: bd_disk is NULL. Device [%d:%d]\n", > + MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); > + pr_err("Block device object %p\n", bdev); > + return -ENODEV; > + } > + > + down_read(&snap_image_destroy_lock); > + do { > + struct snapimage *image = bdev->bd_disk->private_data; > + > + if (image == NULL) { > + pr_err("Unable to open snapshot image: private data is not initialized. Block device object %p\n", > + bdev); > + res = -ENODEV; > + break; > + } > + > + mutex_lock(&image->open_locker); > + { > + if (image->open_cnt == 0) > + image->open_bdev = bdev; > + > + image->open_cnt++; > + } > + mutex_unlock(&image->open_locker); > + } while (false); > + up_read(&snap_image_destroy_lock); > + return res; > +} > + > +static inline uint64_t do_div_inline(uint64_t division, uint32_t divisor) > +{ > + do_div(division, divisor); > + return division; > +} > + > +int _snapimage_getgeo(struct block_device *bdev, struct hd_geometry *geo) > +{ > + int res = SUCCESS; > + sector_t quotient; > + > + down_read(&snap_image_destroy_lock); > + do { > + struct snapimage *image = bdev->bd_disk->private_data; > + > + if (image == NULL) { > + pr_err("Unable to open snapshot image: private data is not initialized. Block device object %p\n", > + bdev); > + res = -ENODEV; > + break; > + } > + > + pr_info("Getting geo for snapshot image device [%d:%d]\n", MAJOR(image->image_dev), > + MINOR(image->image_dev)); > + > + geo->start = 0; > + if (image->capacity > 63) { > + geo->sectors = 63; > + quotient = do_div_inline(image->capacity + (63 - 1), 63); > + > + if (quotient > 255ULL) { > + geo->heads = 255; > + geo->cylinders = > + (unsigned short)do_div_inline(quotient + (255 - 1), 255); > + } else { > + geo->heads = (unsigned char)quotient; > + geo->cylinders = 1; > + } > + } else { > + geo->sectors = (unsigned char)image->capacity; > + geo->cylinders = 1; > + geo->heads = 1; > + } > + > + pr_info("Image device geo: capacity=%lld, heads=%d, cylinders=%d, sectors=%d\n", > + image->capacity, geo->heads, geo->cylinders, geo->sectors); > + } while (false); > + up_read(&snap_image_destroy_lock); > + > + return res; > +} > + > +void _snapimage_close(struct gendisk *disk, fmode_t mode) > +{ > + if (disk->private_data != NULL) { > + down_read(&snap_image_destroy_lock); > + do { > + struct snapimage *image = disk->private_data; > + > + mutex_lock(&image->open_locker); > + { > + if (image->open_cnt > 0) > + image->open_cnt--; > + > + if (image->open_cnt == 0) > + image->open_bdev = NULL; > + } > + mutex_unlock(&image->open_locker); > + } while (false); > + up_read(&snap_image_destroy_lock); > + } else > + pr_err("Unable to close snapshot image: private data is not initialized\n"); > +} > + > +int _snapimage_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) > +{ > + int res = -ENOTTY; > + > + down_read(&snap_image_destroy_lock); > + { > + struct snapimage *image = bdev->bd_disk->private_data; > + > + switch (cmd) { > + /* > + * The only command we need to interpret is HDIO_GETGEO, since > + * we can't partition the drive otherwise. We have no real > + * geometry, of course, so make something up. > + */ > + case HDIO_GETGEO: { > + unsigned long len; > + struct hd_geometry geo; > + > + res = _snapimage_getgeo(bdev, &geo); > + > + len = copy_to_user((void *)arg, &geo, sizeof(geo)); > + if (len != 0) > + res = -EFAULT; > + else > + res = SUCCESS; > + } break; > + case CDROM_GET_CAPABILITY: //0x5331 / * get capabilities * / > + { > + struct gendisk *disk = bdev->bd_disk; > + > + if (bdev->bd_disk && (disk->flags & GENHD_FL_CD)) > + res = SUCCESS; > + else > + res = -EINVAL; > + } break; > + > + default: > + pr_info("Snapshot image ioctl receive unsupported command\n"); > + pr_info("Device [%d:%d], command 0x%x, arg 0x%lx\n", > + MAJOR(image->image_dev), MINOR(image->image_dev), cmd, arg); > + > + res = -ENOTTY; /* unknown command */ > + } > + } > + up_read(&snap_image_destroy_lock); > + return res; > +} > + > +blk_qc_t _snapimage_submit_bio(struct bio *bio); > + > +const struct block_device_operations snapimage_ops = { > + .owner = THIS_MODULE, > + .submit_bio = _snapimage_submit_bio, > + .open = _snapimage_open, > + .ioctl = _snapimage_ioctl, > + .release = _snapimage_close, > +}; > + > +static inline int _snapimage_request_read(struct snapimage *image, > + struct blk_redirect_bio *rq_redir) > +{ > + struct snapstore_device *snapstore_device = image->defer_io->snapstore_device; > + > + return snapstore_device_read(snapstore_device, rq_redir); > +} > + > +int _snapimage_request_write(struct snapimage *image, struct blk_redirect_bio *rq_redir) > +{ > + struct snapstore_device *snapstore_device; > + struct cbt_map *cbt_map; > + int res = SUCCESS; > + > + if (unlikely((image->defer_io == NULL) || (image->cbt_map == NULL))) { > + pr_err("Invalid snapshot image structure"); > + return -EINVAL; > + } > + > + > + snapstore_device = image->defer_io->snapstore_device; > + cbt_map = image->cbt_map; > + > + if (snapstore_device_is_corrupted(snapstore_device)) > + return -ENODATA; > + > + if (!bio_has_data(rq_redir->bio)) { > + pr_warn("Snapshot image receive empty block IO. flags=%u\n", > + rq_redir->bio->bi_flags); > + > + blk_redirect_complete(rq_redir, SUCCESS); > + return SUCCESS; > + } > + > + if (cbt_map != NULL) { > + sector_t ofs = rq_redir->bio->bi_iter.bi_sector; > + sector_t cnt = bio_sectors(rq_redir->bio); > + > + res = cbt_map_set_both(cbt_map, ofs, cnt); > + if (res != SUCCESS) > + pr_err("Unable to write data to snapshot image: failed to set CBT map. errno=%d\n", > + res); > + } > + > + res = snapstore_device_write(snapstore_device, rq_redir); > + > + if (res != SUCCESS) { > + pr_err("Failed to write data to snapshot image\n"); > + return res; > + } > + > + return res; > +} > + > +void _snapimage_processing(struct snapimage *image) > +{ > + int res = SUCCESS; > + struct blk_redirect_bio *rq_redir; > + > + rq_redir = redirect_bio_queue_get_first(&image->image_queue); > + > + if (bio_data_dir(rq_redir->bio) == READ) { > + res = _snapimage_request_read(image, rq_redir); > + if (res != SUCCESS) > + pr_err("Failed to read data from snapshot image. errno=%d\n", res); > + > + } else { > + res = _snapimage_request_write(image, rq_redir); > + if (res != SUCCESS) > + pr_err("Failed to write data to snapshot image. errno=%d\n", res); > + } > + > + if (res != SUCCESS) > + blk_redirect_complete(rq_redir, res); > +} > + > +int snapimage_processor_waiting(struct snapimage *image) > +{ > + int res = SUCCESS; > + > + if (redirect_bio_queue_empty(image->image_queue)) { > + res = wait_event_interruptible_timeout( > + image->rq_proc_event, > + (!redirect_bio_queue_empty(image->image_queue) || kthread_should_stop()), > + 5 * HZ); > + if (res > 0) > + res = SUCCESS; > + else if (res == 0) > + res = -ETIME; > + } > + return res; > +} > + > +int snapimage_processor_thread(void *data) > +{ > + struct snapimage *image = data; > + > + pr_info("Snapshot image thread for device [%d:%d] start\n", MAJOR(image->image_dev), > + MINOR(image->image_dev)); > + > + add_disk(image->disk); > + > + //priority > + set_user_nice(current, -20); //MIN_NICE > + > + while (!kthread_should_stop()) { > + int res = snapimage_processor_waiting(image); > + > + if (res == SUCCESS) { > + if (!redirect_bio_queue_empty(image->image_queue)) > + _snapimage_processing(image); > + } else if (res != -ETIME) { > + pr_err("Failed to wait snapshot image thread queue. errno=%d\n", res); > + return res; > + } > + schedule(); > + } > + pr_info("Snapshot image disk delete\n"); > + del_gendisk(image->disk); > + > + while (!redirect_bio_queue_empty(image->image_queue)) > + _snapimage_processing(image); > + > + pr_info("Snapshot image thread for device [%d:%d] complete", MAJOR(image->image_dev), > + MINOR(image->image_dev)); > + return 0; > +} > + > +static inline void _snapimage_bio_complete(struct bio *bio, int err) > +{ > + if (err == SUCCESS) > + bio->bi_status = BLK_STS_OK; > + else > + bio->bi_status = BLK_STS_IOERR; > + > + bio_endio(bio); > +} > + > +void _snapimage_bio_complete_cb(void *complete_param, struct bio *bio, int err) > +{ > + struct snapimage *image = (struct snapimage *)complete_param; > + > + _snapimage_bio_complete(bio, err); > + > + if (redirect_bio_queue_unactive(image->image_queue)) > + wake_up_interruptible(&image->rq_complete_event); > + > + atomic_dec(&image->own_cnt); > +} > + > +int _snapimage_throttling(struct defer_io *defer_io) > +{ > + return wait_event_interruptible(defer_io->queue_throttle_waiter, > + redirect_bio_queue_empty(defer_io->dio_queue)); > +} > + > +blk_qc_t _snapimage_submit_bio(struct bio *bio) > +{ > + blk_qc_t result = SUCCESS; > + struct request_queue *q = bio->bi_disk->queue; > + struct snapimage *image = q->queuedata; > + > + if (unlikely(blk_mq_queue_stopped(q))) { > + pr_info("Failed to make snapshot image request. Queue already is not active."); > + pr_info("Queue flags=%lx\n", q->queue_flags); > + > + _snapimage_bio_complete(bio, -ENODEV); > + > + return result; > + } > + > + atomic_inc(&image->own_cnt); > + do { > + int res; > + struct blk_redirect_bio *rq_redir; > + > + if (false == atomic_read(&(image->image_queue.active_state))) { > + _snapimage_bio_complete(bio, -ENODEV); > + break; > + } > + > + if (snapstore_device_is_corrupted(image->defer_io->snapstore_device)) { > + _snapimage_bio_complete(bio, -ENODATA); > + break; > + } > + > + res = _snapimage_throttling(image->defer_io); > + if (res != SUCCESS) { > + pr_err("Failed to throttle snapshot image device. errno=%d\n", res); > + _snapimage_bio_complete(bio, res); > + break; > + } > + > + rq_redir = redirect_bio_queue_new(&image->image_queue); > + if (rq_redir == NULL) { > + pr_err("Unable to make snapshot image request: failed to allocate redirect bio structure\n"); > + _snapimage_bio_complete(bio, -ENOMEM); > + break; > + } > + rq_redir->bio = bio; > + rq_redir->complete_cb = _snapimage_bio_complete_cb; > + rq_redir->complete_param = (void *)image; > + atomic_inc(&image->own_cnt); > + > + res = redirect_bio_queue_push_back(&image->image_queue, rq_redir); > + if (res == SUCCESS) > + wake_up(&image->rq_proc_event); > + else { > + redirect_bio_queue_free(rq_redir); > + _snapimage_bio_complete(bio, -EIO); > + > + if (redirect_bio_queue_unactive(image->image_queue)) > + wake_up_interruptible(&image->rq_complete_event); > + } > + > + } while (false); > + atomic_dec(&image->own_cnt); > + > + return result; > +} > + > +struct blk_dev_info { > + size_t blk_size; > + sector_t start_sect; > + sector_t count_sect; > + > + unsigned int io_min; > + unsigned int physical_block_size; > + unsigned short logical_block_size; > +}; > + > +static int _blk_dev_get_info(struct block_device *blk_dev, struct blk_dev_info *pdev_info) > +{ > + sector_t SectorStart; > + sector_t SectorsCapacity; > + > + if (blk_dev->bd_part) > + SectorsCapacity = blk_dev->bd_part->nr_sects; > + else if (blk_dev->bd_disk) > + SectorsCapacity = get_capacity(blk_dev->bd_disk); > + else > + return -EINVAL; > + > + SectorStart = get_start_sect(blk_dev); > + > + pdev_info->physical_block_size = blk_dev->bd_disk->queue->limits.physical_block_size; > + pdev_info->logical_block_size = blk_dev->bd_disk->queue->limits.logical_block_size; > + pdev_info->io_min = blk_dev->bd_disk->queue->limits.io_min; > + > + pdev_info->blk_size = block_size(blk_dev); > + pdev_info->start_sect = SectorStart; > + pdev_info->count_sect = SectorsCapacity; > + return SUCCESS; > +} > + > +static int blk_dev_get_info(dev_t dev_id, struct blk_dev_info *pdev_info) > +{ > + int result = SUCCESS; > + struct block_device *blk_dev; > + > + result = blk_dev_open(dev_id, &blk_dev); > + if (result != SUCCESS) { > + pr_err("Failed to open device [%d:%d]\n", MAJOR(dev_id), MINOR(dev_id)); > + return result; > + } > + > + result = _blk_dev_get_info(blk_dev, pdev_info); > + if (result != SUCCESS) > + pr_err("Failed to identify block device [%d:%d]\n", MAJOR(dev_id), MINOR(dev_id)); > + > + blk_dev_close(blk_dev); > + > + return result; > +} > + > +static inline void _snapimage_free(struct snapimage *image) > +{ > + defer_io_put_resource(image->defer_io); > + cbt_map_put_resource(image->cbt_map); > + image->defer_io = NULL; > +} > + > +static void _snapimage_stop(struct snapimage *image) > +{ > + if (image->rq_processor != NULL) { > + if (redirect_bio_queue_active(&image->image_queue, false)) { > + struct request_queue *q = image->queue; > + > + pr_info("Snapshot image request processing stop\n"); > + > + if (!blk_queue_stopped(q)) { > + blk_sync_queue(q); > + blk_mq_stop_hw_queues(q); > + } > + } > + > + pr_info("Snapshot image thread stop\n"); > + kthread_stop(image->rq_processor); > + image->rq_processor = NULL; > + > + while (!redirect_bio_queue_unactive(image->image_queue)) > + wait_event_interruptible(image->rq_complete_event, > + redirect_bio_queue_unactive(image->image_queue)); > + } > +} > + > +static void _snapimage_destroy(struct snapimage *image) > +{ > + if (image->rq_processor != NULL) > + _snapimage_stop(image); > + > + if (image->queue) { > + pr_info("Snapshot image queue cleanup\n"); > + blk_cleanup_queue(image->queue); > + image->queue = NULL; > + } > + > + if (image->disk != NULL) { > + struct gendisk *disk; > + > + disk = image->disk; > + image->disk = NULL; > + > + pr_info("Snapshot image disk structure release\n"); > + > + disk->private_data = NULL; > + put_disk(disk); > + } > + > + spin_lock(&snapimage_minors_lock); > + bitmap_clear(snapimage_minors, MINOR(image->image_dev), 1u); > + spin_unlock(&snapimage_minors_lock); > +} > + > +int snapimage_create(dev_t original_dev) > +{ > + int res = SUCCESS; > + struct tracker *tracker = NULL; > + struct snapimage *image = NULL; > + struct gendisk *disk = NULL; > + int minor; > + struct blk_dev_info original_dev_info; > + > + pr_info("Create snapshot image for device [%d:%d]\n", MAJOR(original_dev), > + MINOR(original_dev)); > + > + res = blk_dev_get_info(original_dev, &original_dev_info); > + if (res != SUCCESS) { > + pr_err("Failed to obtain original device info\n"); > + return res; > + } > + > + res = tracker_find_by_dev_id(original_dev, &tracker); > + if (res != SUCCESS) { > + pr_err("Unable to create snapshot image: cannot find tracker for device [%d:%d]\n", > + MAJOR(original_dev), MINOR(original_dev)); > + return res; > + } > + > + image = kzalloc(sizeof(struct snapimage), GFP_KERNEL); > + if (image == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&image->link); > + > + do { > + spin_lock(&snapimage_minors_lock); > + minor = bitmap_find_free_region(snapimage_minors, SNAPIMAGE_MAX_DEVICES, 0); > + spin_unlock(&snapimage_minors_lock); > + > + if (minor < SUCCESS) { > + pr_err("Failed to allocate minor for snapshot image device. errno=%d\n", > + 0 - minor); > + break; > + } > + > + image->rq_processor = NULL; > + > + image->capacity = original_dev_info.count_sect; > + > + image->defer_io = defer_io_get_resource(tracker->defer_io); > + image->cbt_map = cbt_map_get_resource(tracker->cbt_map); > + image->original_dev = original_dev; > + > + image->image_dev = MKDEV(snapimage_major, minor); > + pr_info("Snapshot image device id [%d:%d]\n", MAJOR(image->image_dev), > + MINOR(image->image_dev)); > + > + atomic_set(&image->own_cnt, 0); > + > + mutex_init(&image->open_locker); > + image->open_bdev = NULL; > + image->open_cnt = 0; > + > + image->queue = blk_alloc_queue(NUMA_NO_NODE); > + if (image->queue == NULL) { > + res = -ENOMEM; > + break; > + } > + image->queue->queuedata = image; > + > + blk_queue_max_segment_size(image->queue, 1024 * PAGE_SIZE); > + > + { > + unsigned int physical_block_size = original_dev_info.physical_block_size; > + unsigned short logical_block_size = original_dev_info.logical_block_size; > + > + pr_info("Snapshot image physical block size %d\n", physical_block_size); > + pr_info("Snapshot image logical block size %d\n", logical_block_size); > + > + blk_queue_physical_block_size(image->queue, physical_block_size); > + blk_queue_logical_block_size(image->queue, logical_block_size); > + } > + disk = alloc_disk(1); //only one partition on disk > + if (disk == NULL) { > + pr_err("Failed to allocate disk for snapshot image device\n"); > + res = -ENOMEM; > + break; > + } > + image->disk = disk; > + > + if (snprintf(disk->disk_name, DISK_NAME_LEN, "%s%d", SNAP_IMAGE_NAME, minor) < 0) { > + pr_err("Unable to set disk name for snapshot image device: invalid minor %d\n", > + minor); > + res = -EINVAL; > + break; > + } > + > + pr_info("Snapshot image disk name [%s]", disk->disk_name); > + > + disk->flags |= GENHD_FL_NO_PART_SCAN; > + disk->flags |= GENHD_FL_REMOVABLE; > + > + disk->major = snapimage_major; > + disk->minors = 1; // one disk have only one partition. > + disk->first_minor = minor; > + > + disk->private_data = image; > + > + disk->fops = &snapimage_ops; > + disk->queue = image->queue; > + > + set_capacity(disk, image->capacity); > + pr_info("Snapshot image device capacity %lld bytes", > + (u64)from_sectors(image->capacity)); > + > + //res = -ENOMEM; > + redirect_bio_queue_init(&image->image_queue); > + > + { > + struct task_struct *task = > + kthread_create(snapimage_processor_thread, image, disk->disk_name); > + if (IS_ERR(task)) { > + res = PTR_ERR(task); > + pr_err("Failed to create request processing thread for snapshot image device. errno=%d\n", > + res); > + break; > + } > + image->rq_processor = task; > + } > + init_waitqueue_head(&image->rq_complete_event); > + > + init_waitqueue_head(&image->rq_proc_event); > + wake_up_process(image->rq_processor); > + } while (false); > + > + if (res == SUCCESS) { > + down_write(&snap_images_lock); > + list_add_tail(&image->link, &snap_images); > + up_write(&snap_images_lock); > + } else { > + _snapimage_destroy(image); > + _snapimage_free(image); > + > + kfree(image); > + image = NULL; > + } > + return res; > +} > + > +static struct snapimage *snapimage_find(dev_t original_dev) > +{ > + struct snapimage *image = NULL; > + > + down_read(&snap_images_lock); > + if (!list_empty(&snap_images)) { > + struct list_head *_list_head; > + > + list_for_each(_list_head, &snap_images) { > + struct snapimage *_image = list_entry(_list_head, struct snapimage, link); > + > + if (_image->original_dev == original_dev) { > + image = _image; > + break; > + } > + } > + } > + up_read(&snap_images_lock); > + > + return image; > +} > + > +void snapimage_stop(dev_t original_dev) > +{ > + struct snapimage *image; > + > + pr_info("Snapshot image processing stop for original device [%d:%d]\n", MAJOR(original_dev), > + MINOR(original_dev)); > + > + down_read(&snap_image_destroy_lock); > + > + image = snapimage_find(original_dev); > + if (image != NULL) > + _snapimage_stop(image); > + else > + pr_err("Snapshot image [%d:%d] not found\n", MAJOR(original_dev), > + MINOR(original_dev)); > + > + up_read(&snap_image_destroy_lock); > +} > + > +void snapimage_destroy(dev_t original_dev) > +{ > + struct snapimage *image = NULL; > + > + pr_info("Destroy snapshot image for device [%d:%d]\n", MAJOR(original_dev), > + MINOR(original_dev)); > + > + down_write(&snap_images_lock); > + if (!list_empty(&snap_images)) { > + struct list_head *_list_head; > + > + list_for_each(_list_head, &snap_images) { > + struct snapimage *_image = list_entry(_list_head, struct snapimage, link); > + > + if (_image->original_dev == original_dev) { > + image = _image; > + list_del(&image->link); > + break; > + } > + } > + } > + up_write(&snap_images_lock); > + > + if (image != NULL) { > + down_write(&snap_image_destroy_lock); > + > + _snapimage_destroy(image); > + _snapimage_free(image); > + > + kfree(image); > + image = NULL; > + > + up_write(&snap_image_destroy_lock); > + } else > + pr_err("Snapshot image [%d:%d] not found\n", MAJOR(original_dev), > + MINOR(original_dev)); > +} > + > +void snapimage_destroy_for(dev_t *p_dev, int count) > +{ > + int inx = 0; > + > + for (; inx < count; ++inx) > + snapimage_destroy(p_dev[inx]); > +} > + > +int snapimage_create_for(dev_t *p_dev, int count) > +{ > + int res = SUCCESS; > + int inx = 0; > + > + for (; inx < count; ++inx) { > + res = snapimage_create(p_dev[inx]); > + if (res != SUCCESS) { > + pr_err("Failed to create snapshot image for original device [%d:%d]\n", > + MAJOR(p_dev[inx]), MINOR(p_dev[inx])); > + break; > + } > + } > + if (res != SUCCESS) > + if (inx > 0) > + snapimage_destroy_for(p_dev, inx - 1); > + return res; > +} > + > +int snapimage_init(void) > +{ > + int res = SUCCESS; > + > + res = register_blkdev(snapimage_major, SNAP_IMAGE_NAME); > + if (res >= SUCCESS) { > + snapimage_major = res; > + pr_info("Snapshot image block device major %d was registered\n", snapimage_major); > + res = SUCCESS; > + > + spin_lock(&snapimage_minors_lock); > + snapimage_minors = bitmap_zalloc(SNAPIMAGE_MAX_DEVICES, GFP_KERNEL); > + spin_unlock(&snapimage_minors_lock); > + > + if (snapimage_minors == NULL) > + pr_err("Failed to initialize bitmap of minors\n"); > + } else > + pr_err("Failed to register snapshot image block device. errno=%d\n", res); > + > + return res; > +} > + > +void snapimage_done(void) > +{ > + down_write(&snap_image_destroy_lock); > + while (true) { > + struct snapimage *image = NULL; > + > + down_write(&snap_images_lock); > + if (!list_empty(&snap_images)) { > + image = list_entry(snap_images.next, struct snapimage, link); > + > + list_del(&image->link); > + } > + up_write(&snap_images_lock); > + > + if (image == NULL) > + break; > + > + pr_err("Snapshot image for device was unexpectedly removed [%d:%d]\n", > + MAJOR(image->original_dev), MINOR(image->original_dev)); > + > + _snapimage_destroy(image); > + _snapimage_free(image); > + > + kfree(image); > + image = NULL; > + } > + > + spin_lock(&snapimage_minors_lock); > + bitmap_free(snapimage_minors); > + snapimage_minors = NULL; > + spin_unlock(&snapimage_minors_lock); > + > + if (!list_empty(&snap_images)) > + pr_err("Failed to release snapshot images container\n"); > + > + unregister_blkdev(snapimage_major, SNAP_IMAGE_NAME); > + pr_info("Snapshot image block device [%d] was unregistered\n", snapimage_major); > + > + up_write(&snap_image_destroy_lock); > +} > + > +int snapimage_collect_images(int count, struct image_info_s *p_user_image_info, int *p_real_count) > +{ > + int res = SUCCESS; > + int real_count = 0; > + > + down_read(&snap_images_lock); > + if (!list_empty(&snap_images)) { > + struct list_head *_list_head; > + > + list_for_each(_list_head, &snap_images) > + real_count++; > + } > + up_read(&snap_images_lock); > + *p_real_count = real_count; > + > + if (count < real_count) > + res = -ENODATA; > + > + real_count = min(count, real_count); > + if (real_count > 0) { > + unsigned long len; > + struct image_info_s *p_kernel_image_info = NULL; > + size_t buff_size; > + > + buff_size = sizeof(struct image_info_s) * real_count; > + p_kernel_image_info = kzalloc(buff_size, GFP_KERNEL); > + if (p_kernel_image_info == NULL) { > + pr_err("Unable to collect snapshot images: not enough memory. size=%zu\n", > + buff_size); > + return res = -ENOMEM; > + } > + > + down_read(&snap_image_destroy_lock); > + down_read(&snap_images_lock); > + > + if (!list_empty(&snap_images)) { > + size_t inx = 0; > + struct list_head *_list_head; > + > + list_for_each(_list_head, &snap_images) { > + struct snapimage *img = > + list_entry(_list_head, struct snapimage, link); > + > + real_count++; > + > + p_kernel_image_info[inx].original_dev_id.major = > + MAJOR(img->original_dev); > + p_kernel_image_info[inx].original_dev_id.minor = > + MINOR(img->original_dev); > + > + p_kernel_image_info[inx].snapshot_dev_id.major = > + MAJOR(img->image_dev); > + p_kernel_image_info[inx].snapshot_dev_id.minor = > + MINOR(img->image_dev); > + > + ++inx; > + if (inx > real_count) > + break; > + } > + } > + > + up_read(&snap_images_lock); > + up_read(&snap_image_destroy_lock); > + > + len = copy_to_user(p_user_image_info, p_kernel_image_info, buff_size); > + if (len != 0) { > + pr_err("Unable to collect snapshot images: failed to copy data to user buffer\n"); > + res = -ENODATA; > + } > + > + kfree(p_kernel_image_info); > + } > + > + return res; > +} > + > +int snapimage_mark_dirty_blocks(dev_t image_dev_id, struct block_range_s *block_ranges, > + unsigned int count) > +{ > + size_t inx = 0; > + int res = SUCCESS; > + > + pr_info("Marking [%d] dirty blocks for image device [%d:%d]\n", count, MAJOR(image_dev_id), > + MINOR(image_dev_id)); > + > + down_read(&snap_image_destroy_lock); > + do { > + struct snapimage *image = snapimage_find(image_dev_id); > + > + if (image == NULL) { > + pr_err("Cannot find device [%d:%d]\n", MAJOR(image_dev_id), > + MINOR(image_dev_id)); > + res = -ENODEV; > + break; > + } > + > + for (inx = 0; inx < count; ++inx) { > + sector_t ofs = (sector_t)block_ranges[inx].ofs; > + sector_t cnt = (sector_t)block_ranges[inx].cnt; > + > + res = cbt_map_set_both(image->cbt_map, ofs, cnt); > + if (res != SUCCESS) { > + pr_err("Failed to set CBT table. errno=%d\n", res); > + break; > + } > + } > + } while (false); > + up_read(&snap_image_destroy_lock); > + > + return res; > +} > diff --git a/drivers/block/blk-snap/snapimage.h b/drivers/block/blk-snap/snapimage.h > new file mode 100644 > index 000000000000..67995c321496 > --- /dev/null > +++ b/drivers/block/blk-snap/snapimage.h > @@ -0,0 +1,16 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#include "blk-snap-ctl.h" > + > +int snapimage_init(void); > +void snapimage_done(void); > +int snapimage_create_for(dev_t *p_dev, int count); > + > +void snapimage_stop(dev_t original_dev); > +void snapimage_destroy(dev_t original_dev); > + > +int snapimage_collect_images(int count, struct image_info_s *p_user_image_info, int *p_real_count); > + > +int snapimage_mark_dirty_blocks(dev_t image_dev_id, struct block_range_s *block_ranges, > + unsigned int count); > diff --git a/drivers/block/blk-snap/snapshot.c b/drivers/block/blk-snap/snapshot.c > new file mode 100644 > index 000000000000..fdef713103d2 > --- /dev/null > +++ b/drivers/block/blk-snap/snapshot.c > @@ -0,0 +1,225 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapshot" > +#include "common.h" > +#include "snapshot.h" > +#include "tracker.h" > +#include "snapimage.h" > +#include "tracking.h" > + > +LIST_HEAD(snapshots); > +DECLARE_RWSEM(snapshots_lock); > + > + > +static int _snapshot_remove_device(dev_t dev_id) > +{ > + int result; > + struct tracker *tracker = NULL; > + > + result = tracker_find_by_dev_id(dev_id, &tracker); > + if (result != SUCCESS) { > + if (result == -ENODEV) > + pr_err("Cannot find device by device id=[%d:%d]\n", MAJOR(dev_id), > + MINOR(dev_id)); > + else > + pr_err("Failed to find device by device id=[%d:%d]\n", MAJOR(dev_id), > + MINOR(dev_id)); > + return SUCCESS; > + } > + > + if (result != SUCCESS) > + return result; > + > + tracker->snapshot_id = 0ull; > + > + pr_info("Device [%d:%d] successfully removed from snapshot\n", MAJOR(dev_id), > + MINOR(dev_id)); > + return SUCCESS; > +} > + > +static void _snapshot_cleanup(struct snapshot *snapshot) > +{ > + int inx; > + > + for (inx = 0; inx < snapshot->dev_id_set_size; ++inx) { > + > + if (_snapshot_remove_device(snapshot->dev_id_set[inx]) != SUCCESS) > + pr_err("Failed to remove device [%d:%d] from snapshot\n", > + MAJOR(snapshot->dev_id_set[inx]), MINOR(snapshot->dev_id_set[inx])); > + } > + > + if (snapshot->dev_id_set != NULL) > + kfree(snapshot->dev_id_set); > + kfree(snapshot); > +} > + > +static void _snapshot_destroy(struct snapshot *snapshot) > +{ > + size_t inx; > + > + for (inx = 0; inx < snapshot->dev_id_set_size; ++inx) > + snapimage_stop(snapshot->dev_id_set[inx]); > + > + pr_info("Release snapshot [0x%llx]\n", snapshot->id); > + > + tracker_release_snapshot(snapshot->dev_id_set, snapshot->dev_id_set_size); > + > + for (inx = 0; inx < snapshot->dev_id_set_size; ++inx) > + snapimage_destroy(snapshot->dev_id_set[inx]); > + > + _snapshot_cleanup(snapshot); > +} > + > + > +static int _snapshot_new(dev_t *p_dev, int count, struct snapshot **pp_snapshot) > +{ > + struct snapshot *p_snapshot = NULL; > + dev_t *snap_set = NULL; > + > + p_snapshot = kzalloc(sizeof(struct snapshot), GFP_KERNEL); > + if (p_snapshot == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&p_snapshot->link); > + > + p_snapshot->id = (unsigned long)(p_snapshot); > + > + snap_set = kcalloc(count, sizeof(dev_t), GFP_KERNEL); > + if (snap_set == NULL) { > + kfree(p_snapshot); > + > + pr_err("Unable to create snapshot: faile to allocate memory for snapshot map\n"); > + return -ENOMEM; > + } > + memcpy(snap_set, p_dev, sizeof(dev_t) * count); > + > + p_snapshot->dev_id_set_size = count; > + p_snapshot->dev_id_set = snap_set; > + > + down_write(&snapshots_lock); > + list_add_tail(&snapshots, &p_snapshot->link); > + up_write(&snapshots_lock); > + > + *pp_snapshot = p_snapshot; > + > + return SUCCESS; > +} > + > +void snapshot_done(void) > +{ > + struct snapshot *snap; > + > + pr_info("Removing all snapshots\n"); > + do { > + snap = NULL; > + down_write(&snapshots_lock); > + if (!list_empty(&snapshots)) { > + struct snapshot *snap = list_entry(snapshots.next, struct snapshot, link); > + > + list_del(&snap->link); > + } > + up_write(&snapshots_lock); > + > + if (snap) > + _snapshot_destroy(snap); > + > + } while (snap); > +} > + > +int snapshot_create(dev_t *dev_id_set, unsigned int dev_id_set_size, > + unsigned long long *p_snapshot_id) > +{ > + struct snapshot *snapshot = NULL; > + int result = SUCCESS; > + unsigned int inx; > + > + pr_info("Create snapshot for devices:\n"); > + for (inx = 0; inx < dev_id_set_size; ++inx) > + pr_info("\t%d:%d\n", MAJOR(dev_id_set[inx]), MINOR(dev_id_set[inx])); > + > + result = _snapshot_new(dev_id_set, dev_id_set_size, &snapshot); > + if (result != SUCCESS) { > + pr_err("Unable to create snapshot: failed to allocate snapshot structure\n"); > + return result; > + } > + > + do { > + result = -ENODEV; > + for (inx = 0; inx < snapshot->dev_id_set_size; ++inx) { > + dev_t dev_id = snapshot->dev_id_set[inx]; > + > + result = tracking_add(dev_id, snapshot->id); > + if (result == -EALREADY) > + result = SUCCESS; > + else if (result != SUCCESS) { > + pr_err("Unable to create snapshot\n"); > + pr_err("Failed to add device [%d:%d] to snapshot tracking\n", > + MAJOR(dev_id), MINOR(dev_id)); > + break; > + } > + } > + if (result != SUCCESS) > + break; > + > + result = tracker_capture_snapshot(snapshot->dev_id_set, snapshot->dev_id_set_size); > + if (result != SUCCESS) { > + pr_err("Unable to create snapshot: failed to capture snapshot [0x%llx]\n", > + snapshot->id); > + break; > + } > + > + result = snapimage_create_for(snapshot->dev_id_set, snapshot->dev_id_set_size); > + if (result != SUCCESS) { > + pr_err("Unable to create snapshot\n"); > + pr_err("Failed to create snapshot image devices\n"); > + > + tracker_release_snapshot(snapshot->dev_id_set, snapshot->dev_id_set_size); > + break; > + } > + > + *p_snapshot_id = snapshot->id; > + pr_info("Snapshot [0x%llx] was created\n", snapshot->id); > + } while (false); > + > + if (result != SUCCESS) { > + pr_info("Snapshot [0x%llx] cleanup\n", snapshot->id); > + > + down_write(&snapshots_lock); > + list_del(&snapshot->link); > + up_write(&snapshots_lock); > + > + _snapshot_cleanup(snapshot); > + } > + return result; > +} > + > +int snapshot_destroy(unsigned long long snapshot_id) > +{ > + struct snapshot *snapshot = NULL; > + > + pr_info("Destroy snapshot [0x%llx]\n", snapshot_id); > + > + down_read(&snapshots_lock); > + if (!list_empty(&snapshots)) { > + struct list_head *_head; > + > + list_for_each(_head, &snapshots) { > + struct snapshot *_snap = list_entry(_head, struct snapshot, link); > + > + if (_snap->id == snapshot_id) { > + snapshot = _snap; > + list_del(&snapshot->link); > + break; > + } > + } > + } > + up_read(&snapshots_lock); > + > + if (snapshot == NULL) { > + pr_err("Unable to destroy snapshot [0x%llx]: cannot find snapshot by id\n", > + snapshot_id); > + return -ENODEV; > + } > + > + _snapshot_destroy(snapshot); > + return SUCCESS; > +} > diff --git a/drivers/block/blk-snap/snapshot.h b/drivers/block/blk-snap/snapshot.h > new file mode 100644 > index 000000000000..59fb4dba0241 > --- /dev/null > +++ b/drivers/block/blk-snap/snapshot.h > @@ -0,0 +1,17 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +struct snapshot { > + struct list_head link; > + unsigned long long id; > + > + dev_t *dev_id_set; //array of assigned devices > + int dev_id_set_size; > +}; > + > +void snapshot_done(void); > + > +int snapshot_create(dev_t *dev_id_set, unsigned int dev_id_set_size, > + unsigned long long *p_snapshot_id); > + > +int snapshot_destroy(unsigned long long snapshot_id); > diff --git a/drivers/block/blk-snap/snapstore.c b/drivers/block/blk-snap/snapstore.c > new file mode 100644 > index 000000000000..0bedeaeec021 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore.c > @@ -0,0 +1,929 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapstore" > +#include "common.h" > +#include "snapstore.h" > +#include "snapstore_device.h" > +#include "big_buffer.h" > +#include "params.h" > + > +LIST_HEAD(snapstores); > +DECLARE_RWSEM(snapstores_lock); > + > +bool _snapstore_check_halffill(struct snapstore *snapstore, sector_t *fill_status) > +{ > + struct blk_descr_pool *pool = NULL; > + > + if (snapstore->file) > + pool = &snapstore->file->pool; > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + else if (snapstore->multidev) > + pool = &snapstore->multidev->pool; > +#endif > + else if (snapstore->mem) > + pool = &snapstore->mem->pool; > + > + if (pool) > + return blk_descr_pool_check_halffill(pool, snapstore->empty_limit, fill_status); > + > + return false; > +} > + > +void _snapstore_destroy(struct snapstore *snapstore) > +{ > + sector_t fill_status; > + > + pr_info("Destroy snapstore with id %pUB\n", &snapstore->id); > + > + _snapstore_check_halffill(snapstore, &fill_status); > + > + down_write(&snapstores_lock); > + list_del(&snapstore->link); > + up_write(&snapstores_lock); > + > + if (snapstore->mem != NULL) > + snapstore_mem_destroy(snapstore->mem); > + if (snapstore->multidev != NULL) > + snapstore_multidev_destroy(snapstore->multidev); > + if (snapstore->file != NULL) > + snapstore_file_destroy(snapstore->file); > + > + if (snapstore->ctrl_pipe) { > + struct ctrl_pipe *pipe; > + > + pipe = snapstore->ctrl_pipe; > + snapstore->ctrl_pipe = NULL; > + > + ctrl_pipe_request_terminate(pipe, fill_status); > + > + ctrl_pipe_put_resource(pipe); > + } > + > + kfree(snapstore); > +} > + > +static void _snapstore_destroy_cb(struct kref *kref) > +{ > + struct snapstore *snapstore = container_of(kref, struct snapstore, refcount); > + > + _snapstore_destroy(snapstore); > +} > + > +struct snapstore *snapstore_get(struct snapstore *snapstore) > +{ > + if (snapstore) > + kref_get(&snapstore->refcount); > + > + return snapstore; > +} > + > +void snapstore_put(struct snapstore *snapstore) > +{ > + if (snapstore) > + kref_put(&snapstore->refcount, _snapstore_destroy_cb); > +} > + > +void snapstore_done(void) > +{ > + bool is_empty; > + > + down_read(&snapstores_lock); > + is_empty = list_empty(&snapstores); > + up_read(&snapstores_lock); > + > + if (!is_empty) > + pr_err("Unable to perform snapstore cleanup: container is not empty\n"); > +} > + > +int snapstore_create(uuid_t *id, dev_t snapstore_dev_id, dev_t *dev_id_set, > + size_t dev_id_set_length) > +{ > + int res = SUCCESS; > + size_t dev_id_inx; > + struct snapstore *snapstore = NULL; > + > + if (dev_id_set_length == 0) > + return -EINVAL; > + > + snapstore = kzalloc(sizeof(struct snapstore), GFP_KERNEL); > + if (snapstore == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&snapstore->link); > + uuid_copy(&snapstore->id, id); > + > + pr_info("Create snapstore with id %pUB\n", &snapstore->id); > + > + snapstore->mem = NULL; > + snapstore->multidev = NULL; > + snapstore->file = NULL; > + > + snapstore->ctrl_pipe = NULL; > + snapstore->empty_limit = (sector_t)(64 * (1024 * 1024 / SECTOR_SIZE)); //by default value > + snapstore->halffilled = false; > + snapstore->overflowed = false; > + > + if (snapstore_dev_id == 0) > + pr_info("Memory snapstore create\n"); > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + else if (snapstore_dev_id == 0xFFFFffff) { > + struct snapstore_multidev *multidev = NULL; > + > + res = snapstore_multidev_create(&multidev); > + if (res != SUCCESS) { > + kfree(snapstore); > + > + pr_err("Failed to create multidevice snapstore %pUB\n", id); > + return res; > + } > + snapstore->multidev = multidev; > + } > +#endif > + else { > + struct snapstore_file *file = NULL; > + > + res = snapstore_file_create(snapstore_dev_id, &file); > + if (res != SUCCESS) { > + kfree(snapstore); > + > + pr_err("Failed to create snapstore file for snapstore %pUB\n", id); > + return res; > + } > + snapstore->file = file; > + } > + > + down_write(&snapstores_lock); > + list_add_tail(&snapstores, &snapstore->link); > + up_write(&snapstores_lock); > + > + kref_init(&snapstore->refcount); > + > + for (dev_id_inx = 0; dev_id_inx < dev_id_set_length; ++dev_id_inx) { > + res = snapstore_device_create(dev_id_set[dev_id_inx], snapstore); > + if (res != SUCCESS) > + break; > + } > + > + if (res != SUCCESS) > + snapstore_device_cleanup(id); > + > + snapstore_put(snapstore); > + return res; > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +int snapstore_create_multidev(uuid_t *id, dev_t *dev_id_set, size_t dev_id_set_length) > +{ > + int res = SUCCESS; > + size_t dev_id_inx; > + struct snapstore *snapstore = NULL; > + struct snapstore_multidev *multidev = NULL; > + > + if (dev_id_set_length == 0) > + return -EINVAL; > + > + snapstore = kzalloc(sizeof(struct snapstore), GFP_KERNEL); > + if (snapstore == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&snapstore->link); > + > + uuid_copy(&snapstore->id, id); > + > + pr_info("Create snapstore with id %pUB\n", &snapstore->id); > + > + snapstore->mem = NULL; > + snapstore->file = NULL; > + snapstore->multidev = NULL; > + > + snapstore->ctrl_pipe = NULL; > + snapstore->empty_limit = (sector_t)(64 * (1024 * 1024 / SECTOR_SIZE)); //by default value > + snapstore->halffilled = false; > + snapstore->overflowed = false; > + > + res = snapstore_multidev_create(&multidev); > + if (res != SUCCESS) { > + kfree(snapstore); > + > + pr_err("Failed to create snapstore file for snapstore %pUB\n", id); > + return res; > + } > + snapstore->multidev = multidev; > + > + down_write(&snapstores_lock); > + list_add_tail(&snapstore->link, &snapstores); > + up_write(&snapstores_lock); > + > + kref_init(&snapstore->refcount); > + > + for (dev_id_inx = 0; dev_id_inx < dev_id_set_length; ++dev_id_inx) { > + res = snapstore_device_create(dev_id_set[dev_id_inx], snapstore); > + if (res != SUCCESS) > + break; > + } > + > + if (res != SUCCESS) > + snapstore_device_cleanup(id); > + > + snapstore_put(snapstore); > + return res; > +} > +#endif > + > +int snapstore_cleanup(uuid_t *id, u64 *filled_bytes) > +{ > + int res; > + sector_t filled; > + > + res = snapstore_check_halffill(id, &filled); > + if (res == SUCCESS) { > + *filled_bytes = (u64)from_sectors(filled); > + > + pr_info("Snapstore fill size: %lld MiB\n", (*filled_bytes >> 20)); > + } else { > + *filled_bytes = -1; > + pr_err("Failed to obtain snapstore data filled size\n"); > + } > + > + return snapstore_device_cleanup(id); > +} > + > +struct snapstore *_snapstore_find(uuid_t *id) > +{ > + struct snapstore *result = NULL; > + > + down_read(&snapstores_lock); > + if (!list_empty(&snapstores)) { > + struct list_head *_head; > + > + list_for_each(_head, &snapstores) { > + struct snapstore *snapstore = list_entry(_head, struct snapstore, link); > + > + if (uuid_equal(&snapstore->id, id)) { > + result = snapstore; > + break; > + } > + } > + } > + up_read(&snapstores_lock); > + > + return result; > +} > + > +int snapstore_stretch_initiate(uuid_t *unique_id, struct ctrl_pipe *ctrl_pipe, sector_t empty_limit) > +{ > + struct snapstore *snapstore; > + > + snapstore = _snapstore_find(unique_id); > + if (snapstore == NULL) { > + pr_err("Unable to initiate stretch snapstore: "); > + pr_err("cannot find snapstore by uuid %pUB\n", unique_id); > + return -ENODATA; > + } > + > + snapstore->ctrl_pipe = ctrl_pipe_get_resource(ctrl_pipe); > + snapstore->empty_limit = empty_limit; > + > + return SUCCESS; > +} > + > +int snapstore_add_memory(uuid_t *id, unsigned long long sz) > +{ > + int res = SUCCESS; > + struct snapstore *snapstore = NULL; > + size_t available_blocks = (size_t)(sz >> (snapstore_block_shift() + SECTOR_SHIFT)); > + size_t current_block = 0; > + > + pr_info("Adding %lld bytes to the snapstore\n", sz); > + > + snapstore = _snapstore_find(id); > + if (snapstore == NULL) { > + pr_err("Unable to add memory block to the snapstore: "); > + pr_err("cannot found snapstore by id %pUB\n", id); > + return -ENODATA; > + } > + > + if (snapstore->file != NULL) { > + pr_err("Unable to add memory block to the snapstore: "); > + pr_err("snapstore file is already created\n"); > + return -EINVAL; > + } > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + if (snapstore->multidev != NULL) { > + pr_err("Unable to add memory block to the snapstore: "); > + pr_err("snapstore multidevice is already created\n"); > + return -EINVAL; > + } > +#endif > + if (snapstore->mem != NULL) { > + pr_err("Unable to add memory block to the snapstore: "); > + pr_err("snapstore memory buffer is already created\n"); > + return -EINVAL; > + } > + > + snapstore->mem = snapstore_mem_create(available_blocks); > + for (current_block = 0; current_block < available_blocks; ++current_block) { > + void *buffer = snapstore_mem_get_block(snapstore->mem); > + > + if (buffer == NULL) { > + pr_err("Unable to add memory block to the snapstore: "); > + pr_err("not enough memory\n"); > + res = -ENOMEM; > + break; > + } > + > + res = blk_descr_mem_pool_add(&snapstore->mem->pool, buffer); > + if (res != SUCCESS) { > + pr_err("Unable to add memory block to the snapstore: "); > + pr_err("failed to initialize new block\n"); > + break; > + } > + } > + if (res != SUCCESS) { > + snapstore_mem_destroy(snapstore->mem); > + snapstore->mem = NULL; > + } > + > + return res; > +} > + > +int rangelist_add(struct list_head *rglist, struct blk_range *rg) > +{ > + struct blk_range_link *range_link; > + > + range_link = kzalloc(sizeof(struct blk_range_link), GFP_KERNEL); > + if (range_link == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&range_link->link); > + > + range_link->rg.ofs = rg->ofs; > + range_link->rg.cnt = rg->cnt; > + > + list_add_tail(&range_link->link, rglist); > + > + return SUCCESS; > +} > + > +int snapstore_add_file(uuid_t *id, struct big_buffer *ranges, size_t ranges_cnt) > +{ > + int res = SUCCESS; > + struct snapstore *snapstore = NULL; > + struct snapstore_device *snapstore_device = NULL; > + sector_t current_blk_size = 0; > + LIST_HEAD(blk_rangelist); > + size_t inx; > + > + pr_info("Snapstore add %zu ranges\n", ranges_cnt); > + > + if ((ranges_cnt == 0) || (ranges == NULL)) > + return -EINVAL; > + > + snapstore = _snapstore_find(id); > + if (snapstore == NULL) { > + pr_err("Unable to add file to snapstore: "); > + pr_err("cannot find snapstore by id %pUB\n", id); > + return -ENODATA; > + } > + > + if (snapstore->file == NULL) { > + pr_err("Unable to add file to snapstore: "); > + pr_err("snapstore file was not initialized\n"); > + return -EFAULT; > + } > + > + snapstore_device = > + snapstore_device_find_by_dev_id(snapstore->file->blk_dev_id); //for zeroed > + > + for (inx = 0; inx < ranges_cnt; ++inx) { > + size_t blocks_count = 0; > + sector_t range_offset = 0; > + > + struct blk_range range; > + struct ioctl_range_s *ioctl_range; > + > + ioctl_range = big_buffer_get_element(ranges, inx, sizeof(struct ioctl_range_s)); > + if (ioctl_range == NULL) { > + pr_err("Invalid count of ranges\n"); > + res = -ENODATA; > + break; > + } > + > + range.ofs = (sector_t)to_sectors(ioctl_range->left); > + range.cnt = (blkcnt_t)to_sectors(ioctl_range->right) - range.ofs; > + > + while (range_offset < range.cnt) { > + struct blk_range rg; > + > + rg.ofs = range.ofs + range_offset; > + rg.cnt = min_t(sector_t, (range.cnt - range_offset), > + (snapstore_block_size() - current_blk_size)); > + > + range_offset += rg.cnt; > + > + res = rangelist_add(&blk_rangelist, &rg); > + if (res != SUCCESS) { > + pr_err("Unable to add file to snapstore: "); > + pr_err("cannot add range to rangelist\n"); > + break; > + } > + > + //zero sectors logic > + if (snapstore_device != NULL) { > + res = rangevector_add(&snapstore_device->zero_sectors, &rg); > + if (res != SUCCESS) { > + pr_err("Unable to add file to snapstore: "); > + pr_err("cannot add range to zero_sectors tree\n"); > + break; > + } > + } > + > + current_blk_size += rg.cnt; > + > + if (current_blk_size == snapstore_block_size()) { //allocate block > + res = blk_descr_file_pool_add(&snapstore->file->pool, > + &blk_rangelist); > + if (res != SUCCESS) { > + pr_err("Unable to add file to snapstore: "); > + pr_err("cannot initialize new block\n"); > + break; > + } > + > + snapstore->halffilled = false; > + > + current_blk_size = 0; > + INIT_LIST_HEAD(&blk_rangelist); //renew list > + ++blocks_count; > + } > + } > + if (res != SUCCESS) > + break; > + } > + > + if ((res == SUCCESS) && (current_blk_size != 0)) > + pr_warn("Snapstore portion was not ordered by Copy-on-Write block size\n"); > + > + return res; > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +static int rangelist_ex_add(struct list_head *list, struct blk_range *rg, > + struct block_device *blk_dev) > +{ > + struct blk_range_link_ex *range_link = > + kzalloc(sizeof(struct blk_range_link_ex), GFP_KERNEL); > + if (range_link == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&range_link->link); > + > + range_link->rg.ofs = rg->ofs; > + range_link->rg.cnt = rg->cnt; > + range_link->blk_dev = blk_dev; > + > + list_add_tail(&range_link->link, list); > + > + return SUCCESS; > +} > + > +int snapstore_add_multidev(uuid_t *id, dev_t dev_id, struct big_buffer *ranges, size_t ranges_cnt) > +{ > + int res = SUCCESS; > + struct snapstore *snapstore = NULL; > + sector_t current_blk_size = 0; > + size_t inx; > + LIST_HEAD(blk_rangelist); > + > + pr_info("Snapstore add %zu ranges for device [%d:%d]\n", ranges_cnt, MAJOR(dev_id), > + MINOR(dev_id)); > + > + if ((ranges_cnt == 0) || (ranges == NULL)) > + return -EINVAL; > + > + snapstore = _snapstore_find(id); > + if (snapstore == NULL) { > + pr_err("Unable to add file to multidevice snapstore: "); > + pr_err("cannot find snapstore by id %pUB\n", id); > + return -ENODATA; > + } > + > + if (snapstore->multidev == NULL) { > + pr_err("Unable to add file to multidevice snapstore: "); > + pr_err("it was not initialized\n"); > + return -EFAULT; > + } > + > + for (inx = 0; inx < ranges_cnt; ++inx) { > + size_t blocks_count = 0; > + sector_t range_offset = 0; > + struct blk_range range; > + struct ioctl_range_s *data; > + > + data = big_buffer_get_element(ranges, inx, sizeof(struct ioctl_range_s)); > + if (data == NULL) { > + pr_err("Invalid count of ranges\n"); > + res = -ENODATA; > + break; > + } > + > + range.ofs = (sector_t)to_sectors(data->left); > + range.cnt = (blkcnt_t)to_sectors(data->right) - range.ofs; > + > + while (range_offset < range.cnt) { > + struct blk_range rg; > + struct block_device *blk_dev = NULL; > + > + rg.ofs = range.ofs + range_offset; > + rg.cnt = min_t(sector_t, > + range.cnt - range_offset, > + snapstore_block_size() - current_blk_size); > + > + range_offset += rg.cnt; > + > + blk_dev = snapstore_multidev_get_device(snapstore->multidev, dev_id); > + if (blk_dev == NULL) { > + pr_err("Cannot find or open device [%d:%d] for multidevice snapstore\n", > + MAJOR(dev_id), MINOR(dev_id)); > + res = -ENODEV; > + break; > + } > + > + res = rangelist_ex_add(&blk_rangelist, &rg, blk_dev); > + if (res != SUCCESS) { > + pr_err("Unable to add file to multidevice snapstore: "); > + pr_err("failed to add range to rangelist\n"); > + break; > + } > + > + /* > + * zero sectors logic is not implemented for multidevice snapstore > + */ > + > + current_blk_size += rg.cnt; > + > + if (current_blk_size == snapstore_block_size()) { //allocate block > + res = blk_descr_multidev_pool_add(&snapstore->multidev->pool, > + &blk_rangelist); > + if (res != SUCCESS) { > + pr_err("Unable to add file to multidevice snapstore: "); > + pr_err("failed to initialize new block\n"); > + break; > + } > + > + snapstore->halffilled = false; > + > + current_blk_size = 0; > + INIT_LIST_HEAD(&blk_rangelist); > + ++blocks_count; > + } > + } > + if (res != SUCCESS) > + break; > + } > + > + if ((res == SUCCESS) && (current_blk_size != 0)) > + pr_warn("Snapstore portion was not ordered by Copy-on-Write block size\n"); > + > + return res; > +} > +#endif > + > +void snapstore_order_border(struct blk_range *in, struct blk_range *out) > +{ > + struct blk_range unorder; > + > + unorder.ofs = in->ofs & snapstore_block_mask(); > + out->ofs = in->ofs & ~snapstore_block_mask(); > + out->cnt = in->cnt + unorder.ofs; > + > + unorder.cnt = out->cnt & snapstore_block_mask(); > + if (unorder.cnt != 0) > + out->cnt += (snapstore_block_size() - unorder.cnt); > +} > + > +union blk_descr_unify snapstore_get_empty_block(struct snapstore *snapstore) > +{ > + union blk_descr_unify result = { NULL }; > + > + if (snapstore->overflowed) > + return result; > + > + if (snapstore->file != NULL) > + result = blk_descr_file_pool_take(&snapstore->file->pool); > + else if (snapstore->multidev != NULL) > + result = blk_descr_multidev_pool_take(&snapstore->multidev->pool); > + else if (snapstore->mem != NULL) > + result = blk_descr_mem_pool_take(&snapstore->mem->pool); > + > + if (result.ptr == NULL) { > + if (snapstore->ctrl_pipe) { > + sector_t fill_status; > + > + _snapstore_check_halffill(snapstore, &fill_status); > + ctrl_pipe_request_overflow(snapstore->ctrl_pipe, -EINVAL, > + (u64)from_sectors(fill_status)); > + } > + snapstore->overflowed = true; > + } > + > + return result; > +} > + > +int snapstore_check_halffill(uuid_t *unique_id, sector_t *fill_status) > +{ > + struct snapstore *snapstore; > + > + snapstore = _snapstore_find(unique_id); > + if (snapstore == NULL) { > + pr_err("Cannot find snapstore by uuid %pUB\n", unique_id); > + return -ENODATA; > + } > + > + _snapstore_check_halffill(snapstore, fill_status); > + > + return SUCCESS; > +} > + > +int snapstore_request_store(struct snapstore *snapstore, struct blk_deferred_request *dio_copy_req) > +{ > + int res = SUCCESS; > + > + if (snapstore->ctrl_pipe) { > + if (!snapstore->halffilled) { > + sector_t fill_status = 0; > + > + if (_snapstore_check_halffill(snapstore, &fill_status)) { > + snapstore->halffilled = true; > + ctrl_pipe_request_halffill(snapstore->ctrl_pipe, > + (u64)from_sectors(fill_status)); > + } > + } > + } > + > + if (snapstore->file) > + res = blk_deferred_request_store_file(snapstore->file->blk_dev, dio_copy_req); > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + else if (snapstore->multidev) > + res = blk_deferred_request_store_multidev(dio_copy_req); > +#endif > + else if (snapstore->mem) > + res = blk_deferred_request_store_mem(dio_copy_req); > + else > + res = -EINVAL; > + > + return res; > +} > + > +static int _snapstore_redirect_read_file(struct blk_redirect_bio *rq_redir, > + struct block_device *snapstore_blk_dev, > + struct blk_descr_file *file, > + sector_t block_ofs, > + sector_t rq_ofs, sector_t rq_count) > +{ > + int res = SUCCESS; > + sector_t current_ofs = 0; > + struct list_head *_list_head; > + > + if (unlikely(list_empty(&file->rangelist))) { > + pr_err("Invalid file block descriptor"); > + return -EINVAL; > + } > + > + list_for_each(_list_head, &file->rangelist) { > + struct blk_range_link *range_link; > + > + range_link = list_entry(_list_head, struct blk_range_link, link); > + if (current_ofs >= rq_count) > + break; > + > + if (range_link->rg.cnt > block_ofs) { > + sector_t pos = range_link->rg.ofs + block_ofs; > + sector_t len = min_t(sector_t, > + range_link->rg.cnt - block_ofs, > + rq_count - current_ofs); > + > + res = blk_dev_redirect_part(rq_redir, READ, snapstore_blk_dev, pos, > + rq_ofs + current_ofs, len); > + if (res != SUCCESS) { > + pr_err("Failed to read from snapstore file. Sector #%lld\n", > + pos); > + break; > + } > + > + current_ofs += len; > + block_ofs = 0; > + } else > + block_ofs -= range_link->rg.cnt; > + } > + > + if (res != SUCCESS) > + pr_err("Failed to read from file snapstore\n"); > + return res; > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +static int _snapstore_redirect_read_multidev(struct blk_redirect_bio *rq_redir, > + struct blk_descr_multidev *multidev, > + sector_t block_ofs, > + sector_t rq_ofs, sector_t rq_count) > +{ > + int res = SUCCESS; > + sector_t current_ofs = 0; > + struct list_head *_list_head; > + > + if (unlikely(list_empty(&multidev->rangelist))) { > + pr_err("Invalid multidev block descriptor"); > + return -EINVAL; > + } > + > + list_for_each(_list_head, &multidev->rangelist) { > + struct blk_range_link_ex *range_link = > + list_entry(_list_head, struct blk_range_link_ex, link); > + > + if (current_ofs >= rq_count) > + break; > + > + if (range_link->rg.cnt > block_ofs) { > + sector_t pos = range_link->rg.ofs + block_ofs; > + sector_t len = min_t(sector_t, > + range_link->rg.cnt - block_ofs, > + rq_count - current_ofs); > + > + res = blk_dev_redirect_part(rq_redir, READ, range_link->blk_dev, pos, > + rq_ofs + current_ofs, len); > + > + if (res != SUCCESS) { > + pr_err("Failed to read from snapstore file. Sector #%lld\n", pos); > + break; > + } > + > + current_ofs += len; > + block_ofs = 0; > + } else > + block_ofs -= range_link->rg.cnt; > + } > + > + if (res != SUCCESS) > + pr_err("Failed to read from multidev snapstore\n"); > + return res; > +} > +#endif > + > +int snapstore_redirect_read(struct blk_redirect_bio *rq_redir, struct snapstore *snapstore, > + union blk_descr_unify blk_descr, sector_t target_pos, sector_t rq_ofs, > + sector_t rq_count) > +{ > + int res = SUCCESS; > + sector_t block_ofs = target_pos & snapstore_block_mask(); > + > + if (snapstore->file) > + res = _snapstore_redirect_read_file(rq_redir, snapstore->file->blk_dev, > + blk_descr.file, block_ofs, rq_ofs, rq_count); > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + else if (snapstore->multidev) > + res = _snapstore_redirect_read_multidev(rq_redir, blk_descr.multidev, block_ofs, > + rq_ofs, rq_count); > +#endif > + else if (snapstore->mem) { > + res = blk_dev_redirect_memcpy_part( > + rq_redir, READ, blk_descr.mem->buff + (size_t)from_sectors(block_ofs), > + rq_ofs, rq_count); > + > + if (res != SUCCESS) > + pr_err("Failed to read from snapstore memory\n"); > + } else > + res = -EINVAL; > + > + if (res != SUCCESS) > + pr_err("Failed to read from snapstore. Offset %lld sector\n", target_pos); > + return res; > +} > + > +static int _snapstore_redirect_write_file(struct blk_redirect_bio *rq_redir, > + struct block_device *snapstore_blk_dev, > + struct blk_descr_file *file, > + sector_t block_ofs, > + sector_t rq_ofs, sector_t rq_count) > +{ > + int res = SUCCESS; > + sector_t current_ofs = 0; > + struct list_head *_list_head; > + > + if (unlikely(list_empty(&file->rangelist))) { > + pr_err("Invalid file block descriptor"); > + return -EINVAL; > + } > + > + list_for_each(_list_head, &file->rangelist) { > + struct blk_range_link *range_link; > + > + range_link = list_entry(_list_head, struct blk_range_link, link); > + if (current_ofs >= rq_count) > + break; > + > + if (range_link->rg.cnt > block_ofs) { > + sector_t pos = range_link->rg.ofs + block_ofs; > + sector_t len = min_t(sector_t, > + range_link->rg.cnt - block_ofs, > + rq_count - current_ofs); > + > + res = blk_dev_redirect_part(rq_redir, WRITE, snapstore_blk_dev, pos, > + rq_ofs + current_ofs, len); > + > + if (res != SUCCESS) { > + pr_err("Failed to write to snapstore file. Sector #%lld\n", > + pos); > + break; > + } > + > + current_ofs += len; > + block_ofs = 0; > + } else > + block_ofs -= range_link->rg.cnt; > + } > + if (res != SUCCESS) > + pr_err("Failed to write to file snapstore\n"); > + return res; > +} > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +static int _snapstore_redirect_write_multidev(struct blk_redirect_bio *rq_redir, > + struct blk_descr_multidev *multidev, > + sector_t block_ofs, > + sector_t rq_ofs, sector_t rq_count) > +{ > + int res = SUCCESS; > + sector_t current_ofs = 0; > + struct list_head *_list_head; > + > + if (unlikely(list_empty(&multidev->rangelist))) { > + pr_err("Invalid multidev block descriptor"); > + return -EINVAL; > + } > + > + list_for_each(_list_head, &multidev->rangelist) { > + struct blk_range_link_ex *range_link; > + > + range_link = list_entry(_list_head, struct blk_range_link_ex, link); > + if (current_ofs >= rq_count) > + break; > + > + if (range_link->rg.cnt > block_ofs) { > + sector_t pos = range_link->rg.ofs + block_ofs; > + sector_t len = min_t(sector_t, > + range_link->rg.cnt - block_ofs, > + rq_count - current_ofs); > + > + res = blk_dev_redirect_part(rq_redir, WRITE, range_link->blk_dev, pos, > + rq_ofs + current_ofs, len); > + > + if (res != SUCCESS) { > + pr_err("Failed to write to snapstore file. Sector #%lld\n", > + pos); > + break; > + } > + > + current_ofs += len; > + block_ofs = 0; > + } else > + block_ofs -= range_link->rg.cnt; > + } > + > + if (res != SUCCESS) > + pr_err("Failed to write to multidevice snapstore\n"); > + return res; > +} > +#endif > + > +int snapstore_redirect_write(struct blk_redirect_bio *rq_redir, struct snapstore *snapstore, > + union blk_descr_unify blk_descr, sector_t target_pos, sector_t rq_ofs, > + sector_t rq_count) > +{ > + int res = SUCCESS; > + sector_t block_ofs = target_pos & snapstore_block_mask(); > + > + if (snapstore->file) > + res = _snapstore_redirect_write_file(rq_redir, snapstore->file->blk_dev, > + blk_descr.file, block_ofs, rq_ofs, rq_count); > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + else if (snapstore->multidev) > + res = _snapstore_redirect_write_multidev(rq_redir, blk_descr.multidev, > + block_ofs, rq_ofs, rq_count); > +#endif > + else if (snapstore->mem) { > + res = blk_dev_redirect_memcpy_part( > + rq_redir, WRITE, blk_descr.mem->buff + (size_t)from_sectors(block_ofs), > + rq_ofs, rq_count); > + > + if (res != SUCCESS) > + pr_err("Failed to write to memory snapstore\n"); > + } else { > + pr_err("Unable to write to snapstore: invalid type of snapstore device\n"); > + res = -EINVAL; > + } > + > + if (res != SUCCESS) > + pr_err("Failed to write to snapstore. Offset %lld sector\n", target_pos); > + return res; > +} > diff --git a/drivers/block/blk-snap/snapstore.h b/drivers/block/blk-snap/snapstore.h > new file mode 100644 > index 000000000000..db34ad2e2c58 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore.h > @@ -0,0 +1,68 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#include <linux/uuid.h> > +#include <linux/kref.h> > +#include "blk-snap-ctl.h" > +#include "rangevector.h" > +#include "snapstore_mem.h" > +#include "snapstore_file.h" > +#include "snapstore_multidev.h" > +#include "blk_redirect.h" > +#include "ctrl_pipe.h" > +#include "big_buffer.h" > + > +struct snapstore { > + struct list_head link; > + struct kref refcount; > + > + uuid_t id; > + > + struct snapstore_mem *mem; > + struct snapstore_file *file; > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + struct snapstore_multidev *multidev; > +#endif > + > + struct ctrl_pipe *ctrl_pipe; > + sector_t empty_limit; > + > + bool halffilled; > + bool overflowed; > +}; > + > +void snapstore_done(void); > + > +int snapstore_create(uuid_t *id, dev_t snapstore_dev_id, dev_t *dev_id_set, > + size_t dev_id_set_length); > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +int snapstore_create_multidev(uuid_t *id, dev_t *dev_id_set, size_t dev_id_set_length); > +#endif > +int snapstore_cleanup(uuid_t *id, u64 *filled_bytes); > + > +struct snapstore *snapstore_get(struct snapstore *snapstore); > +void snapstore_put(struct snapstore *snapstore); > + > +int snapstore_stretch_initiate(uuid_t *unique_id, struct ctrl_pipe *ctrl_pipe, > + sector_t empty_limit); > + > +int snapstore_add_memory(uuid_t *id, unsigned long long sz); > +int snapstore_add_file(uuid_t *id, struct big_buffer *ranges, size_t ranges_cnt); > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > +int snapstore_add_multidev(uuid_t *id, dev_t dev_id, struct big_buffer *ranges, size_t ranges_cnt); > +#endif > + > +void snapstore_order_border(struct blk_range *in, struct blk_range *out); > + > +union blk_descr_unify snapstore_get_empty_block(struct snapstore *snapstore); > + > +int snapstore_request_store(struct snapstore *snapstore, struct blk_deferred_request *dio_copy_req); > + > +int snapstore_redirect_read(struct blk_redirect_bio *rq_redir, struct snapstore *snapstore, > + union blk_descr_unify blk_descr, sector_t target_pos, sector_t rq_ofs, > + sector_t rq_count); > +int snapstore_redirect_write(struct blk_redirect_bio *rq_redir, struct snapstore *snapstore, > + union blk_descr_unify blk_descr, sector_t target_pos, sector_t rq_ofs, > + sector_t rq_count); > + > +int snapstore_check_halffill(uuid_t *unique_id, sector_t *fill_status); > diff --git a/drivers/block/blk-snap/snapstore_device.c b/drivers/block/blk-snap/snapstore_device.c > new file mode 100644 > index 000000000000..6fdeebacce22 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_device.c > @@ -0,0 +1,532 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapstore" > +#include "common.h" > +#include "snapstore_device.h" > +#include "snapstore.h" > +#include "params.h" > +#include "blk_util.h" > + > +LIST_HEAD(snapstore_devices); > +DECLARE_RWSEM(snapstore_devices_lock); > + > +static inline void _snapstore_device_descr_write_lock(struct snapstore_device *snapstore_device) > +{ > + mutex_lock(&snapstore_device->store_block_map_locker); > +} > +static inline void _snapstore_device_descr_write_unlock(struct snapstore_device *snapstore_device) > +{ > + mutex_unlock(&snapstore_device->store_block_map_locker); > +} > + > +void snapstore_device_done(void) > +{ > + struct snapstore_device *snapstore_device = NULL; > + > + do { > + down_write(&snapstore_devices_lock); > + if (!list_empty(&snapstore_devices)) { > + snapstore_device = > + list_entry(snapstore_devices.next, struct snapstore_device, link); > + list_del(&snapstore_device->link); > + } > + up_write(&snapstore_devices_lock); > + > + if (snapstore_device) > + snapstore_device_put_resource(snapstore_device); > + } while (snapstore_device); > +} > + > +struct snapstore_device *snapstore_device_find_by_dev_id(dev_t dev_id) > +{ > + struct snapstore_device *result = NULL; > + > + down_read(&snapstore_devices_lock); > + if (!list_empty(&snapstore_devices)) { > + struct list_head *_head; > + > + list_for_each(_head, &snapstore_devices) { > + struct snapstore_device *snapstore_device = > + list_entry(_head, struct snapstore_device, link); > + > + if (dev_id == snapstore_device->dev_id) { > + result = snapstore_device; > + break; > + } > + } > + } > + up_read(&snapstore_devices_lock); > + > + return result; > +} > + > +struct snapstore_device *_snapstore_device_get_by_snapstore_id(uuid_t *id) > +{ > + struct snapstore_device *result = NULL; > + > + down_write(&snapstore_devices_lock); > + if (!list_empty(&snapstore_devices)) { > + struct list_head *_head; > + > + list_for_each(_head, &snapstore_devices) { > + struct snapstore_device *snapstore_device = > + list_entry(_head, struct snapstore_device, link); > + > + if (uuid_equal(id, &snapstore_device->snapstore->id)) { > + result = snapstore_device; > + list_del(&snapstore_device->link); > + break; > + } > + } > + } > + up_write(&snapstore_devices_lock); > + > + return result; > +} > + > +static void _snapstore_device_destroy(struct snapstore_device *snapstore_device) > +{ > + pr_info("Destroy snapstore device\n"); > + > + xa_destroy(&snapstore_device->store_block_map); > + > + if (snapstore_device->orig_blk_dev != NULL) > + blk_dev_close(snapstore_device->orig_blk_dev); > + > + rangevector_done(&snapstore_device->zero_sectors); > + > + if (snapstore_device->snapstore) { > + pr_info("Snapstore uuid %pUB\n", &snapstore_device->snapstore->id); > + > + snapstore_put(snapstore_device->snapstore); > + snapstore_device->snapstore = NULL; > + } > + > + kfree(snapstore_device); > +} > + > +static void snapstore_device_free_cb(struct kref *kref) > +{ > + struct snapstore_device *snapstore_device = > + container_of(kref, struct snapstore_device, refcount); > + > + _snapstore_device_destroy(snapstore_device); > +} > + > +struct snapstore_device *snapstore_device_get_resource(struct snapstore_device *snapstore_device) > +{ > + if (snapstore_device) > + kref_get(&snapstore_device->refcount); > + > + return snapstore_device; > +}; > + > +void snapstore_device_put_resource(struct snapstore_device *snapstore_device) > +{ > + if (snapstore_device) > + kref_put(&snapstore_device->refcount, snapstore_device_free_cb); > +}; > + > +int snapstore_device_cleanup(uuid_t *id) > +{ > + int result = SUCCESS; > + struct snapstore_device *snapstore_device = NULL; > + > + while (NULL != (snapstore_device = _snapstore_device_get_by_snapstore_id(id))) { > + pr_info("Cleanup snapstore device for device [%d:%d]\n", > + MAJOR(snapstore_device->dev_id), MINOR(snapstore_device->dev_id)); > + > + snapstore_device_put_resource(snapstore_device); > + } > + return result; > +} > + > +int snapstore_device_create(dev_t dev_id, struct snapstore *snapstore) > +{ > + int res = SUCCESS; > + struct snapstore_device *snapstore_device = > + kzalloc(sizeof(struct snapstore_device), GFP_KERNEL); > + > + if (snapstore_device == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&snapstore_device->link); > + snapstore_device->dev_id = dev_id; > + > + res = blk_dev_open(dev_id, &snapstore_device->orig_blk_dev); > + if (res != SUCCESS) { > + kfree(snapstore_device); > + > + pr_err("Unable to create snapstore device: failed to open original device [%d:%d]\n", > + MAJOR(dev_id), MINOR(dev_id)); > + return res; > + } > + > + kref_init(&snapstore_device->refcount); > + > + snapstore_device->snapstore = NULL; > + snapstore_device->err_code = SUCCESS; > + snapstore_device->corrupted = false; > + atomic_set(&snapstore_device->req_failed_cnt, 0); > + > + mutex_init(&snapstore_device->store_block_map_locker); > + > + rangevector_init(&snapstore_device->zero_sectors); > + > + xa_init(&snapstore_device->store_block_map); > + > + snapstore_device->snapstore = snapstore_get(snapstore); > + > + down_write(&snapstore_devices_lock); > + list_add_tail(&snapstore_device->link, &snapstore_devices); > + up_write(&snapstore_devices_lock); > + > + return SUCCESS; > +} > + > +int snapstore_device_add_request(struct snapstore_device *snapstore_device, > + unsigned long block_index, > + struct blk_deferred_request **dio_copy_req) > +{ > + int res = SUCCESS; > + union blk_descr_unify blk_descr = { NULL }; > + struct blk_deferred_io *dio = NULL; > + bool req_new = false; > + > + blk_descr = snapstore_get_empty_block(snapstore_device->snapstore); > + if (blk_descr.ptr == NULL) { > + pr_err("Unable to add block to defer IO request: failed to allocate next block\n"); > + return -ENODATA; > + } > + > + res = xa_err( > + xa_store(&snapstore_device->store_block_map, block_index, blk_descr.ptr, GFP_NOIO)); > + if (res != SUCCESS) { > + pr_err("Unable to add block to defer IO request: failed to set block descriptor to descriptors array. errno=%d\n", > + res); > + return res; > + } > + > + if (*dio_copy_req == NULL) { > + *dio_copy_req = blk_deferred_request_new(); > + if (*dio_copy_req == NULL) { > + pr_err("Unable to add block to defer IO request: failed to allocate defer IO request\n"); > + return -ENOMEM; > + } > + req_new = true; > + } > + > + do { > + dio = blk_deferred_alloc(block_index, blk_descr); > + if (dio == NULL) { > + pr_err("Unabled to add block to defer IO request: failed to allocate defer IO\n"); > + res = -ENOMEM; > + break; > + } > + > + res = blk_deferred_request_add(*dio_copy_req, dio); > + if (res != SUCCESS) > + pr_err("Unable to add block to defer IO request: failed to add defer IO to request\n"); > + } while (false); > + > + if (res != SUCCESS) { > + if (dio != NULL) { > + blk_deferred_free(dio); > + dio = NULL; > + } > + if (req_new) { > + blk_deferred_request_free(*dio_copy_req); > + *dio_copy_req = NULL; > + } > + } > + > + return res; > +} > + > +int snapstore_device_prepare_requests(struct snapstore_device *snapstore_device, > + struct blk_range *copy_range, > + struct blk_deferred_request **dio_copy_req) > +{ > + int res = SUCCESS; > + unsigned long inx = 0; > + unsigned long first = (unsigned long)(copy_range->ofs >> snapstore_block_shift()); > + unsigned long last = > + (unsigned long)((copy_range->ofs + copy_range->cnt - 1) >> snapstore_block_shift()); > + > + for (inx = first; inx <= last; inx++) { > + if (xa_load(&snapstore_device->store_block_map, inx) == NULL) { > + res = snapstore_device_add_request(snapstore_device, inx, dio_copy_req); > + if (res != SUCCESS) { > + pr_err("Failed to create copy defer IO request. errno=%d\n", res); > + break; > + } > + } > + /* > + * If xa_load() return not NULL, then block already stored. > + */ > + } > + if (res != SUCCESS) > + snapstore_device_set_corrupted(snapstore_device, res); > + > + return res; > +} > + > +int snapstore_device_store(struct snapstore_device *snapstore_device, > + struct blk_deferred_request *dio_copy_req) > +{ > + int res; > + > + res = snapstore_request_store(snapstore_device->snapstore, dio_copy_req); > + if (res != SUCCESS) > + snapstore_device_set_corrupted(snapstore_device, res); > + > + return res; > +} > + > +int snapstore_device_read(struct snapstore_device *snapstore_device, > + struct blk_redirect_bio *rq_redir) > +{ > + int res = SUCCESS; > + > + unsigned long block_index; > + unsigned long block_index_last; > + unsigned long block_index_first; > + > + sector_t blk_ofs_start = 0; //device range start > + sector_t blk_ofs_count = 0; //device range length > + > + struct blk_range rq_range; > + struct rangevector *zero_sectors = &snapstore_device->zero_sectors; > + > + if (snapstore_device_is_corrupted(snapstore_device)) > + return -ENODATA; > + > + rq_range.cnt = bio_sectors(rq_redir->bio); > + rq_range.ofs = rq_redir->bio->bi_iter.bi_sector; > + > + if (!bio_has_data(rq_redir->bio)) { > + pr_warn("Empty bio was found during reading from snapstore device. flags=%u\n", > + rq_redir->bio->bi_flags); > + > + blk_redirect_complete(rq_redir, SUCCESS); > + return SUCCESS; > + } > + > + block_index_first = (unsigned long)(rq_range.ofs >> snapstore_block_shift()); > + block_index_last = > + (unsigned long)((rq_range.ofs + rq_range.cnt - 1) >> snapstore_block_shift()); > + > + _snapstore_device_descr_write_lock(snapstore_device); > + for (block_index = block_index_first; block_index <= block_index_last; ++block_index) { > + union blk_descr_unify blk_descr; > + > + blk_ofs_count = min_t(sector_t, > + (((sector_t)(block_index + 1)) << snapstore_block_shift()) - > + (rq_range.ofs + blk_ofs_start), > + rq_range.cnt - blk_ofs_start); > + > + blk_descr = (union blk_descr_unify)xa_load(&snapstore_device->store_block_map, > + block_index); > + if (blk_descr.ptr) { > + //push snapstore read > + res = snapstore_redirect_read(rq_redir, snapstore_device->snapstore, > + blk_descr, rq_range.ofs + blk_ofs_start, > + blk_ofs_start, blk_ofs_count); > + if (res != SUCCESS) { > + pr_err("Failed to read from snapstore device\n"); > + break; > + } > + } else { > + //device read with zeroing > + if (zero_sectors) > + res = blk_dev_redirect_read_zeroed(rq_redir, > + snapstore_device->orig_blk_dev, > + rq_range.ofs, blk_ofs_start, > + blk_ofs_count, zero_sectors); > + else > + res = blk_dev_redirect_part(rq_redir, READ, > + snapstore_device->orig_blk_dev, > + rq_range.ofs + blk_ofs_start, > + blk_ofs_start, blk_ofs_count); > + > + if (res != SUCCESS) { > + pr_err("Failed to redirect read request to the original device [%d:%d]\n", > + MAJOR(snapstore_device->dev_id), > + MINOR(snapstore_device->dev_id)); > + break; > + } > + } > + > + blk_ofs_start += blk_ofs_count; > + } > + > + if (res == SUCCESS) { > + if (atomic64_read(&rq_redir->bio_count) > 0ll) //async direct access needed > + blk_dev_redirect_submit(rq_redir); > + else > + blk_redirect_complete(rq_redir, res); > + } else { > + pr_err("Failed to read from snapstore device. errno=%d\n", res); > + pr_err("Position %lld sector, length %lld sectors\n", rq_range.ofs, rq_range.cnt); > + } > + _snapstore_device_descr_write_unlock(snapstore_device); > + > + return res; > +} > + > +int _snapstore_device_copy_on_write(struct snapstore_device *snapstore_device, > + struct blk_range *rq_range) > +{ > + int res = SUCCESS; > + struct blk_deferred_request *dio_copy_req = NULL; > + > + mutex_lock(&snapstore_device->store_block_map_locker); > + do { > + res = snapstore_device_prepare_requests(snapstore_device, rq_range, &dio_copy_req); > + if (res != SUCCESS) { > + pr_err("Failed to create defer IO request for range. errno=%d\n", res); > + break; > + } > + > + if (dio_copy_req == NULL) > + break; //nothing to copy > + > + res = blk_deferred_request_read_original(snapstore_device->orig_blk_dev, > + dio_copy_req); > + if (res != SUCCESS) { > + pr_err("Failed to read data from the original device. errno=%d\n", res); > + break; > + } > + > + res = snapstore_device_store(snapstore_device, dio_copy_req); > + if (res != SUCCESS) { > + pr_err("Failed to write data to snapstore. errno=%d\n", res); > + break; > + } > + } while (false); > + mutex_unlock(&snapstore_device->store_block_map_locker); > + > + if (dio_copy_req) { > + if (res == -EDEADLK) > + blk_deferred_request_deadlocked(dio_copy_req); > + else > + blk_deferred_request_free(dio_copy_req); > + } > + > + return res; > +} > + > +int snapstore_device_write(struct snapstore_device *snapstore_device, > + struct blk_redirect_bio *rq_redir) > +{ > + int res = SUCCESS; > + unsigned long block_index; > + unsigned long block_index_last; > + unsigned long block_index_first; > + sector_t blk_ofs_start = 0; //device range start > + sector_t blk_ofs_count = 0; //device range length > + struct blk_range rq_range; > + > + if (snapstore_device_is_corrupted(snapstore_device)) > + return -ENODATA; > + > + rq_range.cnt = bio_sectors(rq_redir->bio); > + rq_range.ofs = rq_redir->bio->bi_iter.bi_sector; > + > + if (!bio_has_data(rq_redir->bio)) { > + pr_warn("Empty bio was found during reading from snapstore device. flags=%u\n", > + rq_redir->bio->bi_flags); > + > + blk_redirect_complete(rq_redir, SUCCESS); > + return SUCCESS; > + } > + > + // do copy to snapstore previously > + res = _snapstore_device_copy_on_write(snapstore_device, &rq_range); > + > + block_index_first = (unsigned long)(rq_range.ofs >> snapstore_block_shift()); > + block_index_last = > + (unsigned long)((rq_range.ofs + rq_range.cnt - 1) >> snapstore_block_shift()); > + > + _snapstore_device_descr_write_lock(snapstore_device); > + for (block_index = block_index_first; block_index <= block_index_last; ++block_index) { > + union blk_descr_unify blk_descr; > + > + blk_ofs_count = min_t(sector_t, > + (((sector_t)(block_index + 1)) << snapstore_block_shift()) - > + (rq_range.ofs + blk_ofs_start), > + rq_range.cnt - blk_ofs_start); > + > + blk_descr = (union blk_descr_unify)xa_load(&snapstore_device->store_block_map, > + block_index); > + if (blk_descr.ptr == NULL) { > + pr_err("Unable to write from snapstore device: invalid snapstore block descriptor\n"); > + res = -EIO; > + break; > + } > + > + res = snapstore_redirect_write(rq_redir, snapstore_device->snapstore, blk_descr, > + rq_range.ofs + blk_ofs_start, blk_ofs_start, > + blk_ofs_count); > + if (res != SUCCESS) { > + pr_err("Unable to write from snapstore device: failed to redirect write request to snapstore\n"); > + break; > + } > + > + blk_ofs_start += blk_ofs_count; > + } > + if (res == SUCCESS) { > + if (atomic64_read(&rq_redir->bio_count) > 0) { //async direct access needed > + blk_dev_redirect_submit(rq_redir); > + } else { > + blk_redirect_complete(rq_redir, res); > + } > + } else { > + pr_err("Failed to write from snapstore device. errno=%d\n", res); > + pr_err("Position %lld sector, length %lld sectors\n", rq_range.ofs, rq_range.cnt); > + > + snapstore_device_set_corrupted(snapstore_device, res); > + } > + _snapstore_device_descr_write_unlock(snapstore_device); > + return res; > +} > + > +bool snapstore_device_is_corrupted(struct snapstore_device *snapstore_device) > +{ > + if (snapstore_device == NULL) > + return true; > + > + if (snapstore_device->corrupted) { > + if (atomic_read(&snapstore_device->req_failed_cnt) == 0) > + pr_err("Snapshot device is corrupted for [%d:%d]\n", > + MAJOR(snapstore_device->dev_id), MINOR(snapstore_device->dev_id)); > + > + atomic_inc(&snapstore_device->req_failed_cnt); > + return true; > + } > + > + return false; > +} > + > +void snapstore_device_set_corrupted(struct snapstore_device *snapstore_device, int err_code) > +{ > + if (!snapstore_device->corrupted) { > + atomic_set(&snapstore_device->req_failed_cnt, 0); > + snapstore_device->corrupted = true; > + snapstore_device->err_code = abs(err_code); > + > + pr_err("Set snapshot device is corrupted for [%d:%d]\n", > + MAJOR(snapstore_device->dev_id), MINOR(snapstore_device->dev_id)); > + } > +} > + > +int snapstore_device_errno(dev_t dev_id, int *p_err_code) > +{ > + struct snapstore_device *snapstore_device; > + > + snapstore_device = snapstore_device_find_by_dev_id(dev_id); > + if (snapstore_device == NULL) > + return -ENODATA; > + > + *p_err_code = snapstore_device->err_code; > + return SUCCESS; > +} > diff --git a/drivers/block/blk-snap/snapstore_device.h b/drivers/block/blk-snap/snapstore_device.h > new file mode 100644 > index 000000000000..729b3c05ef70 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_device.h > @@ -0,0 +1,63 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#include "rangevector.h" > +#include "blk_deferred.h" > +#include "blk_redirect.h" > +#include "snapstore.h" > +#include <linux/xarray.h> > +#include <linux/kref.h> > + > +struct snapstore_device { > + struct list_head link; > + struct kref refcount; > + > + dev_t dev_id; > + struct snapstore *snapstore; > + > + struct block_device *orig_blk_dev; > + > + struct xarray store_block_map; // map block index to read block offset > + struct mutex store_block_map_locker; > + > + struct rangevector zero_sectors; > + > + atomic_t req_failed_cnt; > + int err_code; > + bool corrupted; > +}; > + > +void snapstore_device_done(void); > + > +struct snapstore_device *snapstore_device_get_resource(struct snapstore_device *snapstore_device); > +void snapstore_device_put_resource(struct snapstore_device *snapstore_device); > + > +struct snapstore_device *snapstore_device_find_by_dev_id(dev_t dev_id); > + > +int snapstore_device_create(dev_t dev_id, struct snapstore *snapstore); > + > +int snapstore_device_cleanup(uuid_t *id); > + > +int snapstore_device_prepare_requests(struct snapstore_device *snapstore_device, > + struct blk_range *copy_range, > + struct blk_deferred_request **dio_copy_req); > +int snapstore_device_store(struct snapstore_device *snapstore_device, > + struct blk_deferred_request *dio_copy_req); > + > +int snapstore_device_read(struct snapstore_device *snapstore_device, > + struct blk_redirect_bio *rq_redir); //request from image > +int snapstore_device_write(struct snapstore_device *snapstore_device, > + struct blk_redirect_bio *rq_redir); //request from image > + > +bool snapstore_device_is_corrupted(struct snapstore_device *snapstore_device); > +void snapstore_device_set_corrupted(struct snapstore_device *snapstore_device, int err_code); > +int snapstore_device_errno(dev_t dev_id, int *p_err_code); > + > +static inline void _snapstore_device_descr_read_lock(struct snapstore_device *snapstore_device) > +{ > + mutex_lock(&snapstore_device->store_block_map_locker); > +} > +static inline void _snapstore_device_descr_read_unlock(struct snapstore_device *snapstore_device) > +{ > + mutex_unlock(&snapstore_device->store_block_map_locker); > +} > diff --git a/drivers/block/blk-snap/snapstore_file.c b/drivers/block/blk-snap/snapstore_file.c > new file mode 100644 > index 000000000000..a5c959a8070c > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_file.c > @@ -0,0 +1,52 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapstore" > +#include "common.h" > +#include "snapstore_file.h" > +#include "blk_util.h" > + > +int snapstore_file_create(dev_t dev_id, struct snapstore_file **pfile) > +{ > + int res = SUCCESS; > + struct snapstore_file *file; > + > + pr_info("Single device file snapstore was created on device [%d:%d]\n", MAJOR(dev_id), > + MINOR(dev_id)); > + > + file = kzalloc(sizeof(struct snapstore_file), GFP_KERNEL); > + if (file == NULL) > + return -ENOMEM; > + > + res = blk_dev_open(dev_id, &file->blk_dev); > + if (res != SUCCESS) { > + kfree(file); > + pr_err("Unable to create snapstore file: failed to open device [%d:%d]. errno=%d", > + MAJOR(dev_id), MINOR(dev_id), res); > + return res; > + } > + { > + struct request_queue *q = bdev_get_queue(file->blk_dev); > + > + pr_info("snapstore device logical block size %d\n", q->limits.logical_block_size); > + pr_info("snapstore device physical block size %d\n", q->limits.physical_block_size); > + } > + > + file->blk_dev_id = dev_id; > + blk_descr_file_pool_init(&file->pool); > + > + *pfile = file; > + return res; > +} > + > +void snapstore_file_destroy(struct snapstore_file *file) > +{ > + if (file) { > + blk_descr_file_pool_done(&file->pool); > + > + if (file->blk_dev != NULL) { > + blk_dev_close(file->blk_dev); > + file->blk_dev = NULL; > + } > + > + kfree(file); > + } > +} > diff --git a/drivers/block/blk-snap/snapstore_file.h b/drivers/block/blk-snap/snapstore_file.h > new file mode 100644 > index 000000000000..effd9d888781 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_file.h > @@ -0,0 +1,15 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#include "blk_deferred.h" > + > +struct snapstore_file { > + dev_t blk_dev_id; > + struct block_device *blk_dev; > + > + struct blk_descr_pool pool; > +}; > + > +int snapstore_file_create(dev_t dev_id, struct snapstore_file **pfile); > + > +void snapstore_file_destroy(struct snapstore_file *file); > diff --git a/drivers/block/blk-snap/snapstore_mem.c b/drivers/block/blk-snap/snapstore_mem.c > new file mode 100644 > index 000000000000..29a607617d99 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_mem.c > @@ -0,0 +1,91 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapstore" > +#include "common.h" > +#include "snapstore_mem.h" > +#include "params.h" > + > +#include <linux/vmalloc.h> > + > +struct buffer_el { > + struct list_head link; > + void *buff; > +}; > + > +struct snapstore_mem *snapstore_mem_create(size_t available_blocks) > +{ > + struct snapstore_mem *mem = kzalloc(sizeof(struct snapstore_mem), GFP_KERNEL); > + > + if (mem == NULL) > + return NULL; > + > + blk_descr_mem_pool_init(&mem->pool, available_blocks); > + > + mem->blocks_limit = available_blocks; > + > + INIT_LIST_HEAD(&mem->blocks); > + mutex_init(&mem->blocks_lock); > + > + return mem; > +} > + > +void snapstore_mem_destroy(struct snapstore_mem *mem) > +{ > + struct buffer_el *buffer_el; > + > + if (mem == NULL) > + return; > + > + do { > + buffer_el = NULL; > + > + mutex_lock(&mem->blocks_lock); > + if (!list_empty(&mem->blocks)) { > + buffer_el = list_entry(mem->blocks.next, struct buffer_el, link); > + > + list_del(&buffer_el->link); > + } > + mutex_unlock(&mem->blocks_lock); > + > + if (buffer_el) { > + vfree(buffer_el->buff); > + kfree(buffer_el); > + } > + } while (buffer_el); > + > + blk_descr_mem_pool_done(&mem->pool); > + > + kfree(mem); > +} > + > +void *snapstore_mem_get_block(struct snapstore_mem *mem) > +{ > + struct buffer_el *buffer_el; > + > + if (mem->blocks_allocated >= mem->blocks_limit) { > + pr_err("Unable to get block from snapstore in memory\n"); > + pr_err("Block limit is reached, allocated %zu, limit %zu\n", mem->blocks_allocated, > + mem->blocks_limit); > + return NULL; > + } > + > + buffer_el = kzalloc(sizeof(struct buffer_el), GFP_KERNEL); > + if (buffer_el == NULL) > + return NULL; > + INIT_LIST_HEAD(&buffer_el->link); > + > + buffer_el->buff = vmalloc(snapstore_block_size() * SECTOR_SIZE); > + if (buffer_el->buff == NULL) { > + kfree(buffer_el); > + return NULL; > + } > + > + ++mem->blocks_allocated; > + if (0 == (mem->blocks_allocated & 0x7F)) > + pr_info("%zu MiB was allocated\n", mem->blocks_allocated); > + > + mutex_lock(&mem->blocks_lock); > + list_add_tail(&buffer_el->link, &mem->blocks); > + mutex_unlock(&mem->blocks_lock); > + > + return buffer_el->buff; > +} > diff --git a/drivers/block/blk-snap/snapstore_mem.h b/drivers/block/blk-snap/snapstore_mem.h > new file mode 100644 > index 000000000000..9044a6525966 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_mem.h > @@ -0,0 +1,20 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#include "blk_descr_mem.h" > + > +struct snapstore_mem { > + struct list_head blocks; > + struct mutex blocks_lock; > + > + size_t blocks_limit; > + size_t blocks_allocated; > + > + struct blk_descr_pool pool; > +}; > + > +struct snapstore_mem *snapstore_mem_create(size_t available_blocks); > + > +void snapstore_mem_destroy(struct snapstore_mem *mem); > + > +void *snapstore_mem_get_block(struct snapstore_mem *mem); > diff --git a/drivers/block/blk-snap/snapstore_multidev.c b/drivers/block/blk-snap/snapstore_multidev.c > new file mode 100644 > index 000000000000..bb6bfefa68d7 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_multidev.c > @@ -0,0 +1,118 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-snapstore" > +#include "common.h" > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + > +#include "snapstore_multidev.h" > +#include "blk_util.h" > + > +struct multidev_el { > + struct list_head link; > + > + dev_t dev_id; > + struct block_device *blk_dev; > +}; > + > +int snapstore_multidev_create(struct snapstore_multidev **p_multidev) > +{ > + int res = SUCCESS; > + struct snapstore_multidev *multidev; > + > + pr_info("Multidevice file snapstore create\n"); > + > + multidev = kzalloc(sizeof(struct snapstore_multidev), GFP_KERNEL); > + if (multidev == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&multidev->devicelist); > + spin_lock_init(&multidev->devicelist_lock); > + > + blk_descr_multidev_pool_init(&multidev->pool); > + > + *p_multidev = multidev; > + return res; > +} > + > +void snapstore_multidev_destroy(struct snapstore_multidev *multidev) > +{ > + struct multidev_el *el; > + > + blk_descr_multidev_pool_done(&multidev->pool); > + > + do { > + el = NULL; > + spin_lock(&multidev->devicelist_lock); > + if (!list_empty(&multidev->devicelist)) { > + el = list_entry(multidev->devicelist.next, struct multidev_el, link); > + > + list_del(&el->link); > + } > + spin_unlock(&multidev->devicelist_lock); > + > + if (el) { > + blk_dev_close(el->blk_dev); > + > + pr_info("Close device for multidevice snapstore [%d:%d]\n", > + MAJOR(el->dev_id), MINOR(el->dev_id)); > + > + kfree(el); > + } > + } while (el); > + > + kfree(multidev); > +} > + > +struct multidev_el *snapstore_multidev_find(struct snapstore_multidev *multidev, dev_t dev_id) > +{ > + struct multidev_el *el = NULL; > + > + spin_lock(&multidev->devicelist_lock); > + if (!list_empty(&multidev->devicelist)) { > + struct list_head *_head; > + > + list_for_each(_head, &multidev->devicelist) { > + struct multidev_el *_el = list_entry(_head, struct multidev_el, link); > + > + if (_el->dev_id == dev_id) { > + el = _el; > + break; > + } > + } > + } > + spin_unlock(&multidev->devicelist_lock); > + > + return el; > +} > + > +struct block_device *snapstore_multidev_get_device(struct snapstore_multidev *multidev, > + dev_t dev_id) > +{ > + int res; > + struct block_device *blk_dev = NULL; > + struct multidev_el *el = snapstore_multidev_find(multidev, dev_id); > + > + if (el) > + return el->blk_dev; > + > + res = blk_dev_open(dev_id, &blk_dev); > + if (res != SUCCESS) { > + pr_err("Unable to add device to snapstore multidevice file\n"); > + pr_err("Failed to open [%d:%d]. errno=%d", MAJOR(dev_id), MINOR(dev_id), res); > + return NULL; > + } > + > + el = kzalloc(sizeof(struct multidev_el), GFP_KERNEL); > + INIT_LIST_HEAD(&el->link); > + > + el->blk_dev = blk_dev; > + el->dev_id = dev_id; > + > + spin_lock(&multidev->devicelist_lock); > + list_add_tail(&el->link, &multidev->devicelist); > + spin_unlock(&multidev->devicelist_lock); > + > + return el->blk_dev; > +} > + > +#endif > diff --git a/drivers/block/blk-snap/snapstore_multidev.h b/drivers/block/blk-snap/snapstore_multidev.h > new file mode 100644 > index 000000000000..40c1c3a41b08 > --- /dev/null > +++ b/drivers/block/blk-snap/snapstore_multidev.h > @@ -0,0 +1,22 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#ifdef CONFIG_BLK_SNAP_SNAPSTORE_MULTIDEV > + > +#include "blk_deferred.h" > +#include "blk_descr_multidev.h" > + > +struct snapstore_multidev { > + struct list_head devicelist; //for mapping device id to opened device struct pointer > + spinlock_t devicelist_lock; > + > + struct blk_descr_pool pool; > +}; > + > +int snapstore_multidev_create(struct snapstore_multidev **p_file); > + > +void snapstore_multidev_destroy(struct snapstore_multidev *file); > + > +struct block_device *snapstore_multidev_get_device(struct snapstore_multidev *multidev, > + dev_t dev_id); > +#endif > diff --git a/drivers/block/blk-snap/tracker.c b/drivers/block/blk-snap/tracker.c > new file mode 100644 > index 000000000000..3cda996d3f0a > --- /dev/null > +++ b/drivers/block/blk-snap/tracker.c > @@ -0,0 +1,449 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-tracker" > +#include "common.h" > +#include "tracker.h" > +#include "blk_util.h" > +#include "params.h" > + > +LIST_HEAD(trackers); > +DEFINE_RWLOCK(trackers_lock); > + > +void tracker_done(void) > +{ > + tracker_remove_all(); > +} > + > +int tracker_find_by_bio(struct bio *bio, struct tracker **ptracker) > +{ > + int result = -ENODATA; > + > + read_lock(&trackers_lock); > + if (!list_empty(&trackers)) { > + struct list_head *_head; > + > + list_for_each(_head, &trackers) { > + struct tracker *_tracker = list_entry(_head, struct tracker, link); > + > + if ((bio->bi_disk == _tracker->target_dev->bd_disk) && > + (bio->bi_partno == _tracker->target_dev->bd_partno)) { > + if (ptracker != NULL) > + *ptracker = _tracker; > + > + result = SUCCESS; > + break; > + } > + } > + } > + read_unlock(&trackers_lock); > + > + return result; > +} > + > +int tracker_find_by_dev_id(dev_t dev_id, struct tracker **ptracker) > +{ > + int result = -ENODATA; > + > + read_lock(&trackers_lock); > + if (!list_empty(&trackers)) { > + struct list_head *_head; > + > + list_for_each(_head, &trackers) { > + struct tracker *_tracker = list_entry(_head, struct tracker, link); > + > + if (_tracker->original_dev_id == dev_id) { > + if (ptracker != NULL) > + *ptracker = _tracker; > + > + result = SUCCESS; > + break; > + } > + } > + } > + read_unlock(&trackers_lock); > + > + return result; > +} > + > +int tracker_enum_cbt_info(int max_count, struct cbt_info_s *p_cbt_info, int *p_count) > +{ > + int result = SUCCESS; > + int count = 0; > + > + read_lock(&trackers_lock); > + if (!list_empty(&trackers)) { > + struct list_head *_head; > + > + list_for_each(_head, &trackers) { > + struct tracker *tracker = list_entry(_head, struct tracker, link); > + > + if (count >= max_count) { > + result = -ENOBUFS; > + break; //don`t continue > + } > + > + if (p_cbt_info != NULL) { > + p_cbt_info[count].dev_id.major = MAJOR(tracker->original_dev_id); > + p_cbt_info[count].dev_id.minor = MINOR(tracker->original_dev_id); > + > + if (tracker->cbt_map) { > + p_cbt_info[count].cbt_map_size = tracker->cbt_map->map_size; > + p_cbt_info[count].snap_number = > + (unsigned char) > + tracker->cbt_map->snap_number_previous; > + uuid_copy((uuid_t *)(p_cbt_info[count].generationId), > + &tracker->cbt_map->generationId); > + } else { > + p_cbt_info[count].cbt_map_size = 0; > + p_cbt_info[count].snap_number = 0; > + } > + > + p_cbt_info[count].dev_capacity = (u64)from_sectors( > + part_nr_sects_read(tracker->target_dev->bd_part)); > + } > + > + ++count; > + } > + } > + read_unlock(&trackers_lock); > + > + if (result == SUCCESS) > + if (count == 0) > + result = -ENODATA; > + > + *p_count = count; > + return result; > +} > + > +static void blk_thaw_bdev(dev_t dev_id, struct block_device *device, > + struct super_block *superblock) > +{ > + if (superblock == NULL) > + return; > + > + if (thaw_bdev(device, superblock) == SUCCESS) > + pr_info("Device [%d:%d] was unfrozen\n", MAJOR(dev_id), MINOR(dev_id)); > + else > + pr_err("Failed to unfreeze device [%d:%d]\n", MAJOR(dev_id), MINOR(dev_id)); > +} > + > +static int blk_freeze_bdev(dev_t dev_id, struct block_device *device, > + struct super_block **psuperblock) > +{ > + struct super_block *superblock; > + > + if (device->bd_super == NULL) { > + pr_warn("Unable to freeze device [%d:%d]: no superblock was found\n", > + MAJOR(dev_id), MINOR(dev_id)); > + return SUCCESS; > + } > + > + superblock = freeze_bdev(device); > + if (IS_ERR_OR_NULL(superblock)) { > + int result; > + > + pr_err("Failed to freeze device [%d:%d]\n", MAJOR(dev_id), MINOR(dev_id)); > + > + if (superblock == NULL) > + result = -ENODEV; > + else { > + result = PTR_ERR(superblock); > + pr_err("Error code: %d\n", result); > + } > + return result; > + } > + > + pr_info("Device [%d:%d] was frozen\n", MAJOR(dev_id), MINOR(dev_id)); > + *psuperblock = superblock; > + > + return SUCCESS; > +} > + > +int _tracker_create(struct tracker *tracker, void *filter, bool attach_filter) > +{ > + int result = SUCCESS; > + unsigned int sect_in_block_degree; > + sector_t capacity; > + struct super_block *superblock = NULL; > + > + result = blk_dev_open(tracker->original_dev_id, &tracker->target_dev); > + if (result != SUCCESS) > + return ENODEV; > + > + pr_info("Create tracker for device [%d:%d]. Capacity 0x%llx sectors\n", > + MAJOR(tracker->original_dev_id), MINOR(tracker->original_dev_id), > + (unsigned long long)part_nr_sects_read(tracker->target_dev->bd_part)); > + > + sect_in_block_degree = get_change_tracking_block_size_pow() - SECTOR_SHIFT; > + capacity = part_nr_sects_read(tracker->target_dev->bd_part); > + > + tracker->cbt_map = cbt_map_create(sect_in_block_degree, capacity); > + if (tracker->cbt_map == NULL) { > + pr_err("Failed to create tracker for device [%d:%d]\n", > + MAJOR(tracker->original_dev_id), MINOR(tracker->original_dev_id)); > + tracker_remove(tracker); > + return -ENOMEM; > + } > + > + tracker->snapshot_id = 0ull; > + > + if (attach_filter) { > + blk_freeze_bdev(tracker->original_dev_id, tracker->target_dev, &superblock); > + > + blk_filter_attach(tracker->original_dev_id, filter, tracker); > + > + blk_thaw_bdev(tracker->original_dev_id, tracker->target_dev, superblock); > + } > + > + return SUCCESS; > +} > + > +int tracker_create(dev_t dev_id, void *filter, struct tracker **ptracker) > +{ > + int ret; > + struct tracker *tracker = NULL; > + > + *ptracker = NULL; > + > + tracker = kzalloc(sizeof(struct tracker), GFP_KERNEL); > + if (tracker == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&tracker->link); > + atomic_set(&tracker->is_captured, false); > + tracker->original_dev_id = dev_id; > + > + write_lock(&trackers_lock); > + list_add_tail(&tracker->link, &trackers); > + write_unlock(&trackers_lock); > + > + ret = _tracker_create(tracker, filter, true); > + if (ret < 0) { > + tracker_remove(tracker); > + return ret; > + } > + > + *ptracker = tracker; > + if (ret == ENODEV) > + pr_info("Cannot attach to unknown device [%d:%d]", > + MAJOR(tracker->original_dev_id), MINOR(tracker->original_dev_id)); > + > + return ret; > +} > + > +void _tracker_remove(struct tracker *tracker, bool detach_filter) > +{ > + struct super_block *superblock = NULL; > + > + if (tracker->target_dev != NULL) { > + if (detach_filter) { > + blk_freeze_bdev(tracker->original_dev_id, tracker->target_dev, &superblock); > + > + blk_filter_detach(tracker->original_dev_id); > + > + blk_thaw_bdev(tracker->original_dev_id, tracker->target_dev, superblock); > + } > + > + blk_dev_close(tracker->target_dev); > + tracker->target_dev = NULL; > + } > + > + if (tracker->cbt_map != NULL) { > + cbt_map_put_resource(tracker->cbt_map); > + tracker->cbt_map = NULL; > + } > +} > + > +void tracker_remove(struct tracker *tracker) > +{ > + _tracker_remove(tracker, true); > + > + write_lock(&trackers_lock); > + list_del(&tracker->link); > + write_unlock(&trackers_lock); > + > + kfree(tracker); > +} > + > +void tracker_remove_all(void) > +{ > + struct tracker *tracker; > + > + pr_info("Removing all devices from tracking\n"); > + > + do { > + tracker = NULL; > + > + write_lock(&trackers_lock); > + if (!list_empty(&trackers)) { > + tracker = list_entry(trackers.next, struct tracker, link); > + > + list_del(&tracker->link); > + } > + write_unlock(&trackers_lock); > + > + if (tracker) { > + _tracker_remove(tracker, true); > + kfree(tracker); > + } > + } while (tracker); > +} > + > +void tracker_cbt_bitmap_set(struct tracker *tracker, sector_t sector, sector_t sector_cnt) > +{ > + if (tracker->cbt_map == NULL) > + return; > + > + if (tracker->cbt_map->device_capacity != part_nr_sects_read(tracker->target_dev->bd_part)) { > + pr_warn("Device resize detected\n"); > + tracker->cbt_map->active = false; > + return; > + } > + > + if (cbt_map_set(tracker->cbt_map, sector, sector_cnt) != SUCCESS) { //cbt corrupt > + pr_warn("CBT fault detected\n"); > + tracker->cbt_map->active = false; > + return; > + } > +} > + > +bool tracker_cbt_bitmap_lock(struct tracker *tracker) > +{ > + if (tracker->cbt_map == NULL) > + return false; > + > + cbt_map_read_lock(tracker->cbt_map); > + if (!tracker->cbt_map->active) { > + cbt_map_read_unlock(tracker->cbt_map); > + return false; > + } > + > + return true; > +} > + > +void tracker_cbt_bitmap_unlock(struct tracker *tracker) > +{ > + if (tracker->cbt_map) > + cbt_map_read_unlock(tracker->cbt_map); > +} > + > +int _tracker_capture_snapshot(struct tracker *tracker) > +{ > + int result = SUCCESS; > + > + result = defer_io_create(tracker->original_dev_id, tracker->target_dev, &tracker->defer_io); > + if (result != SUCCESS) { > + pr_err("Failed to create defer IO processor\n"); > + return result; > + } > + > + atomic_set(&tracker->is_captured, true); > + > + if (tracker->cbt_map != NULL) { > + cbt_map_write_lock(tracker->cbt_map); > + cbt_map_switch(tracker->cbt_map); > + cbt_map_write_unlock(tracker->cbt_map); > + > + pr_info("Snapshot captured for device [%d:%d]. New snap number %ld\n", > + MAJOR(tracker->original_dev_id), MINOR(tracker->original_dev_id), > + tracker->cbt_map->snap_number_active); > + } > + > + return result; > +} > + > +int tracker_capture_snapshot(dev_t *dev_id_set, int dev_id_set_size) > +{ > + int result = SUCCESS; > + int inx = 0; > + > + for (inx = 0; inx < dev_id_set_size; ++inx) { > + struct super_block *superblock = NULL; > + struct tracker *tracker = NULL; > + dev_t dev_id = dev_id_set[inx]; > + > + result = tracker_find_by_dev_id(dev_id, &tracker); > + if (result != SUCCESS) { > + pr_err("Unable to capture snapshot: cannot find device [%d:%d]\n", > + MAJOR(dev_id), MINOR(dev_id)); > + break; > + } > + > + > + blk_freeze_bdev(tracker->original_dev_id, tracker->target_dev, &superblock); > + blk_filter_freeze(tracker->target_dev); > + > + result = _tracker_capture_snapshot(tracker); > + if (result != SUCCESS) > + pr_err("Failed to capture snapshot for device [%d:%d]\n", > + MAJOR(dev_id), MINOR(dev_id)); > + > + blk_filter_thaw(tracker->target_dev); > + blk_thaw_bdev(tracker->original_dev_id, tracker->target_dev, superblock); > + } > + if (result != SUCCESS) > + return result; > + > + for (inx = 0; inx < dev_id_set_size; ++inx) { > + struct tracker *tracker = NULL; > + dev_t dev_id = dev_id_set[inx]; > + > + result = tracker_find_by_dev_id(dev_id, &tracker); > + if (result != SUCCESS) { > + pr_err("Unable to capture snapshot: cannot find device [%d:%d]\n", > + MAJOR(dev_id), MINOR(dev_id)); > + continue; > + } > + > + if (snapstore_device_is_corrupted(tracker->defer_io->snapstore_device)) { > + pr_err("Unable to freeze devices [%d:%d]: snapshot data is corrupted\n", > + MAJOR(dev_id), MINOR(dev_id)); > + result = -EDEADLK; > + break; > + } > + } > + > + if (result != SUCCESS) { > + pr_err("Failed to capture snapshot. errno=%d\n", result); > + > + tracker_release_snapshot(dev_id_set, dev_id_set_size); > + } > + return result; > +} > + > +void _tracker_release_snapshot(struct tracker *tracker) > +{ > + struct super_block *superblock = NULL; > + struct defer_io *defer_io = tracker->defer_io; > + > + blk_freeze_bdev(tracker->original_dev_id, tracker->target_dev, &superblock); > + blk_filter_freeze(tracker->target_dev); > + { //locked region > + atomic_set(&tracker->is_captured, false); //clear freeze flag > + > + tracker->defer_io = NULL; > + } > + blk_filter_thaw(tracker->target_dev); > + > + blk_thaw_bdev(tracker->original_dev_id, tracker->target_dev, superblock); > + > + defer_io_stop(defer_io); > + defer_io_put_resource(defer_io); > +} > + > +void tracker_release_snapshot(dev_t *dev_id_set, int dev_id_set_size) > +{ > + int inx = 0; > + > + for (; inx < dev_id_set_size; ++inx) { > + int status; > + struct tracker *p_tracker = NULL; > + dev_t dev = dev_id_set[inx]; > + > + status = tracker_find_by_dev_id(dev, &p_tracker); > + if (status == SUCCESS) > + _tracker_release_snapshot(p_tracker); > + else > + pr_err("Unable to release snapshot: cannot find tracker for device [%d:%d]\n", > + MAJOR(dev), MINOR(dev)); > + } > +} > diff --git a/drivers/block/blk-snap/tracker.h b/drivers/block/blk-snap/tracker.h > new file mode 100644 > index 000000000000..9fff7c0942c3 > --- /dev/null > +++ b/drivers/block/blk-snap/tracker.h > @@ -0,0 +1,38 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > +#include "cbt_map.h" > +#include "defer_io.h" > +#include "blk-snap-ctl.h" > +#include "snapshot.h" > + > +struct tracker { > + struct list_head link; > + dev_t original_dev_id; > + struct block_device *target_dev; > + struct cbt_map *cbt_map; > + atomic_t is_captured; > + struct defer_io *defer_io; > + unsigned long long snapshot_id; // current snapshot for this device > +}; > + > +void tracker_done(void); > + > +int tracker_find_by_bio(struct bio *bio, struct tracker **ptracker); > +int tracker_find_by_dev_id(dev_t dev_id, struct tracker **ptracker); > + > +int tracker_enum_cbt_info(int max_count, struct cbt_info_s *p_cbt_info, int *p_count); > + > +int tracker_capture_snapshot(dev_t *dev_id_set, int dev_id_set_size); > +void tracker_release_snapshot(dev_t *dev_id_set, int dev_id_set_size); > + > +int _tracker_create(struct tracker *tracker, void *filter, bool attach_filter); > +int tracker_create(dev_t dev_id, void *filter, struct tracker **ptracker); > + > +void _tracker_remove(struct tracker *tracker, bool detach_filter); > +void tracker_remove(struct tracker *tracker); > +void tracker_remove_all(void); > + > +void tracker_cbt_bitmap_set(struct tracker *tracker, sector_t sector, sector_t sector_cnt); > + > +bool tracker_cbt_bitmap_lock(struct tracker *tracker); > +void tracker_cbt_bitmap_unlock(struct tracker *tracker); > diff --git a/drivers/block/blk-snap/tracking.c b/drivers/block/blk-snap/tracking.c > new file mode 100644 > index 000000000000..55e18891bb96 > --- /dev/null > +++ b/drivers/block/blk-snap/tracking.c > @@ -0,0 +1,270 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define BLK_SNAP_SECTION "-tracking" > +#include "common.h" > +#include "tracking.h" > +#include "tracker.h" > +#include "blk_util.h" > +#include "defer_io.h" > +#include "params.h" > + > +#include <linux/blk-filter.h> > + > +/* pointer to block layer filter */ > +void *filter; > + > +/* > + * _tracking_submit_bio() - Intercept bio by block io layer filter > + */ > +static bool _tracking_submit_bio(struct bio *bio, void *filter_data) > +{ > + int res; > + bool cbt_locked = false; > + struct tracker *tracker = filter_data; > + > + if (!tracker) > + return false; > + > + //intercepting > + if (atomic_read(&tracker->is_captured)) { > + //snapshot is captured, call bio redirect algorithm > + > + res = defer_io_redirect_bio(tracker->defer_io, bio, tracker); > + if (res == SUCCESS) > + return true; > + } > + > + cbt_locked = false; > + if (tracker && bio_data_dir(bio) && bio_has_data(bio)) { > + //call CBT algorithm > + cbt_locked = tracker_cbt_bitmap_lock(tracker); > + if (cbt_locked) { > + sector_t sectStart = bio->bi_iter.bi_sector; > + sector_t sectCount = bio_sectors(bio); > + > + tracker_cbt_bitmap_set(tracker, sectStart, sectCount); > + } > + } > + if (cbt_locked) > + tracker_cbt_bitmap_unlock(tracker); > + > + return false; > +} > + > +static bool _tracking_part_add(dev_t devt, void **p_filter_data) > +{ > + int result; > + struct tracker *tracker = NULL; > + > + pr_info("new block device [%d:%d] in system\n", MAJOR(devt), MINOR(devt)); > + > + result = tracker_find_by_dev_id(devt, &tracker); > + if (result != SUCCESS) > + return false; /*do not track this device*/ > + > + if (_tracker_create(tracker, filter, false)) { > + pr_err("Failed to attach new device to tracker. errno=%d\n", result); > + return false; /*failed to attach new device to tracker*/ > + } > + > + *p_filter_data = tracker; > + return true; > +} > + > +static void _tracking_part_del(void *private_data) > +{ > + struct tracker *tracker = private_data; > + > + if (!tracker) > + return; > + > + pr_info("delete block device [%d:%d] from system\n", > + MAJOR(tracker->original_dev_id), MINOR(tracker->original_dev_id)); > + > + _tracker_remove(tracker, false); > +} > + > +struct blk_filter_ops filter_ops = { > + .filter_bio = _tracking_submit_bio, > + .part_add = _tracking_part_add, > + .part_del = _tracking_part_del }; > + > + > + > +int tracking_init(void) > +{ > + filter = blk_filter_register(&filter_ops); > + if (!filter) > + return -ENOMEM; > + return SUCCESS; > +} > + > +void tracking_done(void) > +{ > + if (filter) { > + blk_filter_unregister(filter); > + filter = NULL; > + } > +} > + > +static int _add_already_tracked(dev_t dev_id, unsigned long long snapshot_id, > + struct tracker *tracker) > +{ > + int result = SUCCESS; > + bool cbt_reset_needed = false; > + > + if ((snapshot_id != 0ull) && (tracker->snapshot_id == 0ull)) > + tracker->snapshot_id = snapshot_id; // set new snapshot id > + > + if (tracker->cbt_map == NULL) { > + unsigned int sect_in_block_degree = > + get_change_tracking_block_size_pow() - SECTOR_SHIFT; > + tracker->cbt_map = cbt_map_create(sect_in_block_degree - SECTOR_SHIFT, > + part_nr_sects_read(tracker->target_dev->bd_part)); > + if (tracker->cbt_map == NULL) > + return -ENOMEM; > + > + // skip snapshot id > + tracker->snapshot_id = snapshot_id; > + return SUCCESS; > + } > + > + if (!tracker->cbt_map->active) { > + cbt_reset_needed = true; > + pr_warn("Nonactive CBT table detected. CBT fault\n"); > + } > + > + if (tracker->cbt_map->device_capacity != part_nr_sects_read(tracker->target_dev->bd_part)) { > + cbt_reset_needed = true; > + pr_warn("Device resize detected. CBT fault\n"); > + } > + > + if (!cbt_reset_needed) > + return SUCCESS; > + > + _tracker_remove(tracker, true); > + > + result = _tracker_create(tracker, filter, true); > + if (result != SUCCESS) { > + pr_err("Failed to create tracker. errno=%d\n", result); > + return result; > + } > + > + tracker->snapshot_id = snapshot_id; > + > + return SUCCESS; > +} > + > +static int _create_new_tracker(dev_t dev_id, unsigned long long snapshot_id) > +{ > + int result; > + struct tracker *tracker = NULL; > + > + result = tracker_create(dev_id, filter, &tracker); > + if (result != SUCCESS) { > + pr_err("Failed to create tracker. errno=%d\n", result); > + return result; > + } > + > + tracker->snapshot_id = snapshot_id; > + > + return SUCCESS; > +} > + > + > +int tracking_add(dev_t dev_id, unsigned long long snapshot_id) > +{ > + int result; > + struct tracker *tracker = NULL; > + > + pr_info("Adding device [%d:%d] under tracking\n", MAJOR(dev_id), MINOR(dev_id)); > + > + result = tracker_find_by_dev_id(dev_id, &tracker); > + if (result == SUCCESS) { > + //pr_info("Device [%d:%d] is already tracked\n", MAJOR(dev_id), MINOR(dev_id)); > + result = _add_already_tracked(dev_id, snapshot_id, tracker); > + if (result == SUCCESS) > + result = -EALREADY; > + } else if (-ENODATA == result) > + result = _create_new_tracker(dev_id, snapshot_id); > + else { > + pr_err("Unable to add device [%d:%d] under tracking\n", MAJOR(dev_id), > + MINOR(dev_id)); > + pr_err("Invalid trackers container. errno=%d\n", result); > + } > + > + return result; > +} > + > +int tracking_remove(dev_t dev_id) > +{ > + int result; > + struct tracker *tracker = NULL; > + > + pr_info("Removing device [%d:%d] from tracking\n", MAJOR(dev_id), MINOR(dev_id)); > + > + result = tracker_find_by_dev_id(dev_id, &tracker); > + if (result != SUCCESS) { > + pr_err("Unable to remove device [%d:%d] from tracking: ", > + MAJOR(dev_id), MINOR(dev_id)); > + > + if (-ENODATA == result) > + pr_err("tracker not found\n"); > + else > + pr_err("tracker container failed. errno=%d\n", result); > + > + return result; > + } > + > + if (tracker->snapshot_id != 0ull) { > + pr_err("Unable to remove device [%d:%d] from tracking: ", > + MAJOR(dev_id), MINOR(dev_id)); > + pr_err("snapshot [0x%llx] already exist\n", tracker->snapshot_id); > + return -EBUSY; > + } > + > + tracker_remove(tracker); > + > + return SUCCESS; > +} > + > +int tracking_collect(int max_count, struct cbt_info_s *p_cbt_info, int *p_count) > +{ > + int res = tracker_enum_cbt_info(max_count, p_cbt_info, p_count); > + > + if (res == SUCCESS) > + pr_info("%d devices found under tracking\n", *p_count); > + else if (res == -ENODATA) { > + pr_info("There are no devices under tracking\n"); > + *p_count = 0; > + res = SUCCESS; > + } else > + pr_err("Failed to collect devices under tracking. errno=%d", res); > + > + return res; > +} > + > +int tracking_read_cbt_bitmap(dev_t dev_id, unsigned int offset, size_t length, > + void __user *user_buff) > +{ > + int result = SUCCESS; > + struct tracker *tracker = NULL; > + > + result = tracker_find_by_dev_id(dev_id, &tracker); > + if (result == SUCCESS) { > + if (atomic_read(&tracker->is_captured)) > + result = cbt_map_read_to_user(tracker->cbt_map, user_buff, offset, length); > + else { > + pr_err("Unable to read CBT bitmap for device [%d:%d]: ", MAJOR(dev_id), > + MINOR(dev_id)); > + pr_err("device is not captured by snapshot\n"); > + result = -EPERM; > + } > + } else if (-ENODATA == result) { > + pr_err("Unable to read CBT bitmap for device [%d:%d]: ", MAJOR(dev_id), > + MINOR(dev_id)); > + pr_err("device not found\n"); > + } else > + pr_err("Failed to find devices under tracking. errno=%d", result); > + > + return result; > +} > diff --git a/drivers/block/blk-snap/tracking.h b/drivers/block/blk-snap/tracking.h > new file mode 100644 > index 000000000000..22bd5ba54963 > --- /dev/null > +++ b/drivers/block/blk-snap/tracking.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > +#include "blk-snap-ctl.h" > +#include <linux/bio.h> > + > +int tracking_init(void); > +void tracking_done(void); > + > +int tracking_add(dev_t dev_id, unsigned long long snapshot_id); > +int tracking_remove(dev_t dev_id); > +int tracking_collect(int max_count, struct cbt_info_s *p_cbt_info, int *p_count); > +int tracking_read_cbt_bitmap(dev_t dev_id, unsigned int offset, size_t length, > + void __user *user_buff); > diff --git a/drivers/block/blk-snap/version.h b/drivers/block/blk-snap/version.h > new file mode 100644 > index 000000000000..a4431da73611 > --- /dev/null > +++ b/drivers/block/blk-snap/version.h > @@ -0,0 +1,7 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#pragma once > + > +#define FILEVER_MAJOR 5 > +#define FILEVER_MINOR 0 > +#define FILEVER_REVISION 0 > +#define FILEVER_STR "5.0.0" > -- Damien Le Moal Western Digital Research