Re: [PATCH 2/2] blk-snap - snapshots and change-tracking for block devices
From: Damien Le Moal
Date: Wed Oct 21 2020 - 05:24:04 EST
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