Hi all.
I suggest a modified version of the blksnap kernel module for consideration.
It allows to create non-persistent snapshots of any block devices. The main
purpose of such snapshots is to create a backup of block devices.
A snapshot is created simultaneously for several block devices, ensuring
their mutual consistency in the backup.
A change tracker is implemented in the module. It allows to determine
which blocks were changed during the time between the last snapshot
created and any of the previous snapshots of the same generation.
This allows to implement incremental and differential backups.
An arbitrary range of sectors on any block device can be used to store
snapshot changes. The size of the storage for changes can be increased after the
snapshot is created by adding new sector ranges. This allows to create a
storage of differences in individual files on a file system that can occupy
the entire space of a block device and increase the storage of differences
as needed.
To create images of snapshots of block devices, the module stores blocks
of the original block device that have been changed since the snapshot was
taken. To do this, the module intercepts write requests and reads blocks
that need to be overwritten. This algorithm guarantees the safety of the
data of the original block device in case of overflow of the snapshot and
even in case of unpredictable critical errors.
To connect and disconnect the module to the block layer, the concept of a
block device filter is introduced. Functions for connecting filters are
added to the block layer and the ability to intercept I/O requests is
provided.
The blksnap module was created specifically for upstream based on the
experience of operating the out-of-tree veeamsnap module, which is part of
the Veeam Agent for Linux product. I am sure that the module will be in
demand by other creators of backup tools and will save them from having to
use their out-of-tree kernel modules.
A tool, a library for working with blksnap, tests and some documentations
can be found at http://www.github.com/veeam/blksnap.
The first version was suggested at 13.06.2022. Many thanks to Christoph Hellwig
and Randy Dunlap for the review of that version. In this version, I tried to
eliminate all the defects.
Changes:
- Forgotten "static" declarations have been added.
- The text of the comments has been corrected.
- It is possible to connect only one filter, since there are no others in
upstream.
- Do not have additional locks for attach/detach filter.
- blksnap.h moved to include/uapi/.
- #pragma once and commented code removed.
- uuid_t removed from user API.
- Removed default values for module parameters from the configuration file.
- The debugging code for tracking memory leaks has been removed.
- Simplified Makefile.
- Optimized work with large memory buffers, CBT tables are now in virtual
memory.
- The allocation code of minor numbers has been optimized.
- The implementation of the snapshot image block device has been simplified,
now it is a bio-based block device.
- Removed initialization of global variables with null values.
- Only one bio is used to copy one chunk.
- Checked on ppc64le.
Sergei Shtepa (17):
block, bdev_filter: enable block device filters
block, blksnap: header file of the module interface
block, blksnap: module management interface functions
block, blksnap: init() and exit() functions
block, blksnap: interaction with sysfs
block, blksnap: attaching and detaching the filter and handling a bios
block, blksnap: map of change block tracking
block, blksnap: minimum data storage unit of the original block device
lock, blksnap: buffer in memory for the minimum data storage unit
block, blksnap: functions and structures for performing block I/O
operations
block, blksnap: storage for storing difference blocks
lock, blksnap: event queue from the difference storage
block, blksnap: owner of information about overwritten blocks of the
original block device
block, blksnap: snapshot image block device
block, blksnap: snapshot
block, blksnap: Kconfig and Makefile
block, blksnap: adds a blksnap to the kernel tree
block/bdev.c | 73 +++
block/blk-core.c | 19 +-
drivers/block/Kconfig | 2 +
drivers/block/Makefile | 2 +
drivers/block/blksnap/Kconfig | 12 +
drivers/block/blksnap/Makefile | 18 +
drivers/block/blksnap/cbt_map.c | 268 +++++++++++
drivers/block/blksnap/cbt_map.h | 114 +++++
drivers/block/blksnap/chunk.c | 349 ++++++++++++++
drivers/block/blksnap/chunk.h | 139 ++++++
drivers/block/blksnap/ctrl.c | 408 ++++++++++++++++
drivers/block/blksnap/ctrl.h | 9 +
drivers/block/blksnap/diff_area.c | 656 ++++++++++++++++++++++++++
drivers/block/blksnap/diff_area.h | 177 +++++++
drivers/block/blksnap/diff_buffer.c | 132 ++++++
drivers/block/blksnap/diff_buffer.h | 75 +++
drivers/block/blksnap/diff_io.c | 168 +++++++
drivers/block/blksnap/diff_io.h | 118 +++++
drivers/block/blksnap/diff_storage.c | 292 ++++++++++++
drivers/block/blksnap/diff_storage.h | 93 ++++
drivers/block/blksnap/event_queue.c | 86 ++++
drivers/block/blksnap/event_queue.h | 63 +++
drivers/block/blksnap/main.c | 164 +++++++
drivers/block/blksnap/params.h | 12 +
drivers/block/blksnap/snapimage.c | 319 +++++++++++++
drivers/block/blksnap/snapimage.h | 73 +++
drivers/block/blksnap/snapshot.c | 654 ++++++++++++++++++++++++++
drivers/block/blksnap/snapshot.h | 78 ++++
drivers/block/blksnap/sysfs.c | 79 ++++
drivers/block/blksnap/sysfs.h | 7 +
drivers/block/blksnap/tracker.c | 672 +++++++++++++++++++++++++++
drivers/block/blksnap/tracker.h | 74 +++
drivers/block/blksnap/version.h | 10 +
include/linux/blk_types.h | 2 +
include/linux/blkdev.h | 64 +++
include/uapi/linux/blksnap.h | 467 +++++++++++++++++++
36 files changed, 5946 insertions(+), 2 deletions(-)
create mode 100644 drivers/block/blksnap/Kconfig
create mode 100644 drivers/block/blksnap/Makefile
create mode 100644 drivers/block/blksnap/cbt_map.c
create mode 100644 drivers/block/blksnap/cbt_map.h
create mode 100644 drivers/block/blksnap/chunk.c
create mode 100644 drivers/block/blksnap/chunk.h
create mode 100644 drivers/block/blksnap/ctrl.c
create mode 100644 drivers/block/blksnap/ctrl.h
create mode 100644 drivers/block/blksnap/diff_area.c
create mode 100644 drivers/block/blksnap/diff_area.h
create mode 100644 drivers/block/blksnap/diff_buffer.c
create mode 100644 drivers/block/blksnap/diff_buffer.h
create mode 100644 drivers/block/blksnap/diff_io.c
create mode 100644 drivers/block/blksnap/diff_io.h
create mode 100644 drivers/block/blksnap/diff_storage.c
create mode 100644 drivers/block/blksnap/diff_storage.h
create mode 100644 drivers/block/blksnap/event_queue.c
create mode 100644 drivers/block/blksnap/event_queue.h
create mode 100644 drivers/block/blksnap/main.c
create mode 100644 drivers/block/blksnap/params.h
create mode 100644 drivers/block/blksnap/snapimage.c
create mode 100644 drivers/block/blksnap/snapimage.h
create mode 100644 drivers/block/blksnap/snapshot.c
create mode 100644 drivers/block/blksnap/snapshot.h
create mode 100644 drivers/block/blksnap/sysfs.c
create mode 100644 drivers/block/blksnap/sysfs.h
create mode 100644 drivers/block/blksnap/tracker.c
create mode 100644 drivers/block/blksnap/tracker.h
create mode 100644 drivers/block/blksnap/version.h
create mode 100644 include/uapi/linux/blksnap.h
--
2.20.1
Provides creation of a class file /sys/class/blksnap and a device file
/dev/blksnap for module management.
Signed-off-by: Sergei Shtepa <[email protected]>
---
drivers/block/blksnap/sysfs.c | 79 +++++++++++++++++++++++++++++++++++
drivers/block/blksnap/sysfs.h | 7 ++++
2 files changed, 86 insertions(+)
create mode 100644 drivers/block/blksnap/sysfs.c
create mode 100644 drivers/block/blksnap/sysfs.h
diff --git a/drivers/block/blksnap/sysfs.c b/drivers/block/blksnap/sysfs.c
new file mode 100644
index 000000000000..fd20336a14c7
--- /dev/null
+++ b/drivers/block/blksnap/sysfs.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME "-sysfs: " fmt
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <uapi/linux/blksnap.h>
+#include "sysfs.h"
+#include "ctrl.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);
+}
+
+/* Declare class_attr_major */
+CLASS_ATTR_RO(major);
+
+static struct class *blk_snap_class;
+
+static struct device *blk_snap_device;
+
+int sysfs_init(void)
+{
+ struct device *dev;
+ int res;
+
+ blk_snap_class = class_create(THIS_MODULE, THIS_MODULE->name);
+ if (IS_ERR(blk_snap_class)) {
+ res = PTR_ERR(blk_snap_class);
+
+ pr_err("Bad class create. errno=%d\n", abs(res));
+ return res;
+ }
+
+ pr_info("Create 'major' sysfs attribute\n");
+ res = class_create_file(blk_snap_class, &class_attr_major);
+ if (res) {
+ 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,
+ THIS_MODULE->name);
+ if (IS_ERR(dev)) {
+ res = PTR_ERR(dev);
+ pr_err("Failed to create device, errno=%d\n", abs(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 sysfs_done(void)
+{
+ pr_info("Cleanup sysfs\n");
+
+ 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/blksnap/sysfs.h b/drivers/block/blksnap/sysfs.h
new file mode 100644
index 000000000000..66ce9d1509af
--- /dev/null
+++ b/drivers/block/blksnap/sysfs.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BLK_SNAP_SYSFS_H
+#define __BLK_SNAP_SYSFS_H
+
+int sysfs_init(void);
+void sysfs_done(void);
+#endif /* __BLK_SNAP_SYSFS_H */
--
2.20.1
Allows to build a module.
Signed-off-by: Sergei Shtepa <[email protected]>
---
drivers/block/blksnap/Kconfig | 12 ++++++++++++
drivers/block/blksnap/Makefile | 18 ++++++++++++++++++
2 files changed, 30 insertions(+)
create mode 100644 drivers/block/blksnap/Kconfig
create mode 100644 drivers/block/blksnap/Makefile
diff --git a/drivers/block/blksnap/Kconfig b/drivers/block/blksnap/Kconfig
new file mode 100644
index 000000000000..3a6ecb5fc13d
--- /dev/null
+++ b/drivers/block/blksnap/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Block device snapshot module configuration
+#
+
+config BLK_SNAP
+ tristate "Module for snapshots of block devices."
+ help
+ Allow to create snapshots and track block changes for block devices.
+ Designed for creating backups for a simple block devices. Snapshots
+ are temporary and are released then backup is completed. Change block
+ tracking allows to create incremental or differential backups.
diff --git a/drivers/block/blksnap/Makefile b/drivers/block/blksnap/Makefile
new file mode 100644
index 000000000000..b196b17f9d9d
--- /dev/null
+++ b/drivers/block/blksnap/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+blksnap-y := \
+ cbt_map.o \
+ chunk.o \
+ ctrl.o \
+ diff_io.o \
+ diff_area.o \
+ diff_buffer.o \
+ diff_storage.o \
+ event_queue.o \
+ main.o \
+ snapimage.o \
+ snapshot.o \
+ sysfs.o \
+ tracker.o
+
+obj-$(CONFIG_BLK_SNAP) += blksnap.o
--
2.20.1
Provides transmission of events from the difference storage to the user
process. Only two events are currently defined. The first is that there
are few free regions in the difference storage. The second is that the
request for a free region for storing differences failed with an error,
since there are no more free regions left in the difference storage
(the snapshot overflow state).
Signed-off-by: Sergei Shtepa <[email protected]>
---
drivers/block/blksnap/event_queue.c | 86 +++++++++++++++++++++++++++++
drivers/block/blksnap/event_queue.h | 63 +++++++++++++++++++++
2 files changed, 149 insertions(+)
create mode 100644 drivers/block/blksnap/event_queue.c
create mode 100644 drivers/block/blksnap/event_queue.h
diff --git a/drivers/block/blksnap/event_queue.c b/drivers/block/blksnap/event_queue.c
new file mode 100644
index 000000000000..c91a81b3e3a8
--- /dev/null
+++ b/drivers/block/blksnap/event_queue.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME "-event_queue: " fmt
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include "event_queue.h"
+
+void event_queue_init(struct event_queue *event_queue)
+{
+ INIT_LIST_HEAD(&event_queue->list);
+ spin_lock_init(&event_queue->lock);
+ init_waitqueue_head(&event_queue->wq_head);
+}
+
+void event_queue_done(struct event_queue *event_queue)
+{
+ struct event *event;
+
+ spin_lock(&event_queue->lock);
+ while (!list_empty(&event_queue->list)) {
+ event = list_first_entry(&event_queue->list, struct event,
+ link);
+ list_del(&event->link);
+ event_free(event);
+ }
+ spin_unlock(&event_queue->lock);
+}
+
+int event_gen(struct event_queue *event_queue, gfp_t flags, int code,
+ const void *data, int data_size)
+{
+ struct event *event;
+
+ event = kzalloc(sizeof(struct event) + data_size, flags);
+ if (!event)
+ return -ENOMEM;
+
+ event->time = ktime_get();
+ event->code = code;
+ event->data_size = data_size;
+ memcpy(event->data, data, data_size);
+
+ pr_debug("Generate event: time=%lld code=%d data_size=%d\n",
+ event->time, event->code, event->data_size);
+
+ spin_lock(&event_queue->lock);
+ list_add_tail(&event->link, &event_queue->list);
+ spin_unlock(&event_queue->lock);
+
+ wake_up(&event_queue->wq_head);
+ return 0;
+}
+
+struct event *event_wait(struct event_queue *event_queue,
+ unsigned long timeout_ms)
+{
+ int ret;
+
+ ret = wait_event_interruptible_timeout(event_queue->wq_head,
+ !list_empty(&event_queue->list),
+ timeout_ms);
+
+ if (ret > 0) {
+ struct event *event;
+
+ spin_lock(&event_queue->lock);
+ event = list_first_entry(&event_queue->list, struct event,
+ link);
+ list_del(&event->link);
+ spin_unlock(&event_queue->lock);
+
+ pr_debug("Event received: time=%lld code=%d\n", event->time,
+ event->code);
+ return event;
+ }
+ if (ret == 0)
+ return ERR_PTR(-ENOENT);
+
+ if (ret == -ERESTARTSYS) {
+ pr_debug("event waiting interrupted\n");
+ return ERR_PTR(-EINTR);
+ }
+
+ pr_err("Failed to wait event. errno=%d\n", abs(ret));
+ return ERR_PTR(ret);
+}
diff --git a/drivers/block/blksnap/event_queue.h b/drivers/block/blksnap/event_queue.h
new file mode 100644
index 000000000000..d9aee081ab51
--- /dev/null
+++ b/drivers/block/blksnap/event_queue.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BLK_SNAP_EVENT_QUEUE_H
+#define __BLK_SNAP_EVENT_QUEUE_H
+
+#include <linux/types.h>
+#include <linux/ktime.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+/**
+ * struct event - An event to be passed to the user space.
+ * @link:
+ * The list header allows to combine events from the queue.
+ * @time:
+ * A timestamp indicates when an event occurred.
+ * @code:
+ * Event code.
+ * @data_size:
+ * The number of bytes in the event data array.
+ * @data:
+ * An array of event data.
+ *
+ * Events can be different, so they contain different data. The size of the
+ * data array is not defined exactly, but it has limitations. The size of
+ * the event structure may exceed the PAGE_SIZE.
+ */
+struct event {
+ struct list_head link;
+ ktime_t time;
+ int code;
+ int data_size;
+ char data[1]; /* up to PAGE_SIZE - sizeof(struct blk_snap_snapshot_event) */
+};
+
+/**
+ * struct event_queue - A queue of &struct event.
+ * @list:
+ * Linked list for storing events.
+ * @lock:
+ * Spinlock allows to guarantee safety of the linked list.
+ * @wq_head:
+ * A wait queue allows to put a user thread in a waiting state until
+ * an event appears in the linked list.
+ */
+struct event_queue {
+ struct list_head list;
+ spinlock_t lock;
+ struct wait_queue_head wq_head;
+};
+
+void event_queue_init(struct event_queue *event_queue);
+void event_queue_done(struct event_queue *event_queue);
+
+int event_gen(struct event_queue *event_queue, gfp_t flags, int code,
+ const void *data, int data_size);
+struct event *event_wait(struct event_queue *event_queue,
+ unsigned long timeout_ms);
+static inline void event_free(struct event *event)
+{
+ kfree(event);
+};
+#endif /* __BLK_SNAP_EVENT_QUEUE_H */
--
2.20.1
The header file contains a set of declarations, structures and control
requests (ioctl) that allows to manage the module from the user space.
Signed-off-by: Sergei Shtepa <[email protected]>
---
include/uapi/linux/blksnap.h | 467 +++++++++++++++++++++++++++++++++++
1 file changed, 467 insertions(+)
create mode 100644 include/uapi/linux/blksnap.h
diff --git a/include/uapi/linux/blksnap.h b/include/uapi/linux/blksnap.h
new file mode 100644
index 000000000000..56102c22fef8
--- /dev/null
+++ b/include/uapi/linux/blksnap.h
@@ -0,0 +1,467 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_BLK_SNAP_H
+#define _UAPI_LINUX_BLK_SNAP_H
+
+#include <linux/types.h>
+
+#define BLK_SNAP_CTL "/dev/blksnap"
+#define BLK_SNAP_IMAGE_NAME "blksnap-image"
+#define BLK_SNAP 'V'
+
+enum blk_snap_ioctl {
+ /*
+ * Service controls
+ */
+ blk_snap_ioctl_version,
+ /*
+ * Contols for tracking
+ */
+ blk_snap_ioctl_tracker_remove,
+ blk_snap_ioctl_tracker_collect,
+ blk_snap_ioctl_tracker_read_cbt_map,
+ blk_snap_ioctl_tracker_mark_dirty_blocks,
+ /*
+ * Snapshot contols
+ */
+ blk_snap_ioctl_snapshot_create,
+ blk_snap_ioctl_snapshot_destroy,
+ blk_snap_ioctl_snapshot_append_storage,
+ blk_snap_ioctl_snapshot_take,
+ blk_snap_ioctl_snapshot_collect,
+ blk_snap_ioctl_snapshot_collect_images,
+ blk_snap_ioctl_snapshot_wait_event,
+};
+
+/**
+ * struct blk_snap_version - Result for the &IOCTL_BLK_SNAP_VERSION control.
+ * @major:
+ * Version major part.
+ * @minor:
+ * Version minor part.
+ * @revision:
+ * Revision number.
+ * @build:
+ * Build number. Should be zero.
+ */
+struct blk_snap_version {
+ __u16 major;
+ __u16 minor;
+ __u16 revision;
+ __u16 build;
+};
+
+/**
+ * IOCTL_BLK_SNAP_VERSION - Get module version.
+ *
+ * Linking the product behavior to the version code does not seem to be a very
+ * good idea. Version is only for logs.
+ */
+#define IOCTL_BLK_SNAP_VERSION \
+ _IOW(BLK_SNAP, blk_snap_ioctl_version, struct blk_snap_version)
+
+/*
+ * The main functionality of the module is change block tracking (CBT).
+ * Next, a number of ioctls will describe the interface for the CBT mechanism.
+ */
+
+/**
+ * struct blk_snap_dev - Block device ID.
+ * @mj:
+ * Device ID major part.
+ * @mn:
+ * Device ID minor part.
+ *
+ * In user space and in kernel space, block devices are encoded differently.
+ * We need to enter our own type to guarantee the correct transmission of the
+ * major and minor parts.
+ */
+struct blk_snap_dev {
+ __u32 mj;
+ __u32 mn;
+};
+
+/**
+ * struct blk_snap_tracker_remove - Input argument for the
+ * &IOCTL_BLK_SNAP_TRACKER_REMOVE control.
+ * @dev_id:
+ * Device ID.
+ */
+struct blk_snap_tracker_remove {
+ struct blk_snap_dev dev_id;
+};
+/**
+ * IOCTL_BLK_SNAP_TRACKER_REMOVE - Remove a device from tracking.
+ *
+ * Removes the device from tracking changes.
+ * Adding a device for tracking is performed when creating a snapshot
+ * that includes this device.
+ */
+#define IOCTL_BLK_SNAP_TRACKER_REMOVE \
+ _IOW(BLK_SNAP, blk_snap_ioctl_tracker_remove, \
+ struct blk_snap_tracker_remove)
+
+struct blk_snap_uuid {
+ __u8 b[16];
+};
+
+/**
+ * struct blk_snap_cbt_info - Information about change tracking for a block
+ * device.
+ * @dev_id:
+ * Device ID.
+ * @blk_size:
+ * Block size in bytes.
+ * @device_capacity:
+ * Device capacity in bytes.
+ * @blk_count:
+ * Number of blocks.
+ * @generation_id:
+ * Unique identification number of change tracking generation.
+ * @snap_number:
+ * Current changes number.
+ */
+struct blk_snap_cbt_info {
+ struct blk_snap_dev dev_id;
+ __u32 blk_size;
+ __u64 device_capacity;
+ __u32 blk_count;
+ struct blk_snap_uuid generation_id;
+ __u8 snap_number;
+};
+/**
+ * struct blk_snap_tracker_collect - Argument for the
+ * &IOCTL_BLK_SNAP_TRACKER_COLLECT control.
+ * @count:
+ * Size of @cbt_info_array in the number of &struct blk_snap_cbt_info.
+ * If @cbt_info_array has not enough space, it will contain the required
+ * size of the array.
+ * @cbt_info_array:
+ * Pointer to the array for output.
+ */
+struct blk_snap_tracker_collect {
+ __u32 count;
+ struct blk_snap_cbt_info *cbt_info_array;
+};
+/**
+ * IOCTL_BLK_SNAP_TRACKER_COLLECT - Collect all tracked devices.
+ *
+ * Getting information about all devices under tracking.
+ * This ioctl returns the same information that the module outputs
+ * to sysfs for each device under tracking.
+ */
+#define IOCTL_BLK_SNAP_TRACKER_COLLECT \
+ _IOW(BLK_SNAP, blk_snap_ioctl_tracker_collect, \
+ struct blk_snap_tracker_collect)
+
+/**
+ * struct blk_snap_tracker_read_cbt_bitmap - Argument for the
+ * &IOCTL_BLK_SNAP_TRACKER_READ_CBT_MAP control.
+ * @dev_id:
+ * Device ID.
+ * @offset:
+ * Offset from the beginning of the CBT bitmap in bytes.
+ * @length:
+ * Size of @buff in bytes.
+ * @buff:
+ * Pointer to the buffer for output.
+ */
+struct blk_snap_tracker_read_cbt_bitmap {
+ struct blk_snap_dev dev_id;
+ __u32 offset;
+ __u32 length;
+ __u8 *buff;
+};
+/**
+ * IOCTL_BLK_SNAP_TRACKER_READ_CBT_MAP - Read the CBT map.
+ *
+ * This ioctl allows to read the table of changes. Sysfs also has a file that
+ * allows to read this table.
+ */
+#define IOCTL_BLK_SNAP_TRACKER_READ_CBT_MAP \
+ _IOR(BLK_SNAP, blk_snap_ioctl_tracker_read_cbt_map, \
+ struct blk_snap_tracker_read_cbt_bitmap)
+
+/**
+ * struct blk_snap_block_range - Element of array for
+ * &struct blk_snap_tracker_mark_dirty_blocks.
+ * @sector_offset:
+ * Offset from the beginning of the disk in sectors.
+ * @sector_count:
+ * Number of sectors.
+ */
+struct blk_snap_block_range {
+ __u64 sector_offset;
+ __u64 sector_count;
+};
+/**
+ * struct blk_snap_tracker_mark_dirty_blocks - Argument for the
+ * &IOCTL_BLK_SNAP_TRACKER_MARK_DIRTY_BLOCKS control.
+ * @dev_id:
+ * Device ID.
+ * @count:
+ * Size of @dirty_blocks_array in the number of
+ * &struct blk_snap_block_range.
+ * @dirty_blocks_array:
+ * Pointer to the array of &struct blk_snap_block_range.
+ */
+struct blk_snap_tracker_mark_dirty_blocks {
+ struct blk_snap_dev dev_id;
+ __u32 count;
+ struct blk_snap_block_range *dirty_blocks_array;
+};
+/**
+ * IOCTL_BLK_SNAP_TRACKER_MARK_DIRTY_BLOCKS - Set dirty blocks in the CBT map.
+ *
+ * There are cases when some blocks need to be marked as changed.
+ * This ioctl allows to do this.
+ */
+#define IOCTL_BLK_SNAP_TRACKER_MARK_DIRTY_BLOCKS \
+ _IOR(BLK_SNAP, blk_snap_ioctl_tracker_mark_dirty_blocks, \
+ struct blk_snap_tracker_mark_dirty_blocks)
+
+/*
+ * Next, there will be a description of the interface for working with
+ * snapshots.
+ */
+
+/**
+ * struct blk_snap_snapshot_create - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_CREATE control.
+ * @count:
+ * Size of @dev_id_array in the number of &struct blk_snap_dev.
+ * @dev_id_array:
+ * Pointer to the array of &struct blk_snap_dev.
+ * @id:
+ * Return ID of the created snapshot.
+ */
+struct blk_snap_snapshot_create {
+ __u32 count;
+ struct blk_snap_dev *dev_id_array;
+ struct blk_snap_uuid id;
+};
+/**
+ * This ioctl creates a snapshot structure in the memory and allocates an
+ * identifier for it. Further interaction with the snapshot is possible by
+ * this identifier.
+ * Several snapshots can be created at the same time, but with the condition
+ * that one block device can only be included in one snapshot.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_CREATE \
+ _IOW(BLK_SNAP, blk_snap_ioctl_snapshot_create, \
+ struct blk_snap_snapshot_create)
+
+/**
+ * struct blk_snap_snapshot_destroy - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_DESTROY control.
+ * @id:
+ * Snapshot ID.
+ */
+struct blk_snap_snapshot_destroy {
+ struct blk_snap_uuid id;
+};
+/**
+ * IOCTL_BLK_SNAP_SNAPSHOT_DESTROY - Release and destroy the snapshot.
+ *
+ * Destroys all snapshot structures and releases all its allocated resources.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_DESTROY \
+ _IOR(BLK_SNAP, blk_snap_ioctl_snapshot_destroy, \
+ struct blk_snap_snapshot_destroy)
+
+/**
+ * struct blk_snap_snapshot_append_storage - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_APPEND_STORAGE control.
+ * @id:
+ * Snapshot ID.
+ * @dev_id:
+ * Device ID.
+ * @count:
+ * Size of @ranges in the number of &struct blk_snap_block_range.
+ * @ranges:
+ * Pointer to the array of &struct blk_snap_block_range.
+ */
+struct blk_snap_snapshot_append_storage {
+ struct blk_snap_uuid id;
+ struct blk_snap_dev dev_id;
+ __u32 count;
+ struct blk_snap_block_range *ranges;
+};
+/**
+ * IOCTL_BLK_SNAP_SNAPSHOT_APPEND_STORAGE - Append storage to the difference
+ * storage of the snapshot.
+ *
+ * The snapshot difference storage can be set either before or after creating
+ * the snapshot images. This allows to dynamically expand the difference
+ * storage while holding the snapshot.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_APPEND_STORAGE \
+ _IOW(BLK_SNAP, blk_snap_ioctl_snapshot_append_storage, \
+ struct blk_snap_snapshot_append_storage)
+
+/**
+ * struct blk_snap_snapshot_take - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_TAKE control.
+ * @id:
+ * Snapshot ID.
+ */
+struct blk_snap_snapshot_take {
+ struct blk_snap_uuid id;
+};
+/**
+ * IOCTL_BLK_SNAP_SNAPSHOT_TAKE - Take snapshot.
+ *
+ * This ioctl creates snapshot images of block devices and switches CBT tables.
+ * The snapshot must be created before this call, and the areas of block
+ * devices should be added to the difference storage.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_TAKE \
+ _IOR(BLK_SNAP, blk_snap_ioctl_snapshot_take, \
+ struct blk_snap_snapshot_take)
+
+/**
+ * struct blk_snap_snapshot_collect - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_COLLECT control.
+ * @count:
+ * Size of @ids in the number of 16-byte UUID.
+ * If @ids has not enough space, it will contain the required
+ * size of the array.
+ * @ids:
+ * Pointer to the array with the snapshot ID for output. If the pointer is
+ * zero, the ioctl returns the number of active snapshots in &count.
+ *
+ */
+struct blk_snap_snapshot_collect {
+ __u32 count;
+ struct blk_snap_uuid *ids;
+};
+/**
+ * IOCTL_BLK_SNAP_SNAPSHOT_COLLECT - Get collection of created snapshots.
+ *
+ * This information can also be obtained from files from sysfs.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_COLLECT \
+ _IOW(BLK_SNAP, blk_snap_ioctl_snapshot_collect, \
+ struct blk_snap_snapshot_collect)
+/**
+ * struct blk_snap_image_info - Associates the original device in the snapshot
+ * and the corresponding snapshot image.
+ * @orig_dev_id:
+ * Device ID.
+ * @image_dev_id:
+ * Image ID.
+ */
+struct blk_snap_image_info {
+ struct blk_snap_dev orig_dev_id;
+ struct blk_snap_dev image_dev_id;
+};
+/**
+ * struct blk_snap_snapshot_collect_images - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_COLLECT_IMAGES control.
+ * @id:
+ * Snapshot ID.
+ * @count:
+ * Size of @image_info_array in the number of &struct blk_snap_image_info.
+ * If @image_info_array has not enough space, it will contain the required
+ * size of the array.
+ * @image_info_array:
+ * Pointer to the array for output.
+ */
+struct blk_snap_snapshot_collect_images {
+ struct blk_snap_uuid id;
+ __u32 count;
+ struct blk_snap_image_info *image_info_array;
+};
+/**
+ * IOCTL_BLK_SNAP_SNAPSHOT_COLLECT_IMAGES - Get a collection of devices and
+ * their snapshot images.
+ *
+ * While holding the snapshot, this ioctl allows you to get a table of
+ * correspondences of the original devices and their snapshot images.
+ * This information can also be obtained from files from sysfs.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_COLLECT_IMAGES \
+ _IOW(BLK_SNAP, blk_snap_ioctl_snapshot_collect_images, \
+ struct blk_snap_snapshot_collect_images)
+
+enum blk_snap_event_codes {
+ /**
+ * Low free space in difference storage event.
+ *
+ * If the free space in the difference storage is reduced to the
+ * specified limit, the module generates this event.
+ */
+ blk_snap_event_code_low_free_space,
+ /**
+ * Snapshot image is corrupted event.
+ *
+ * If a chunk could not be allocated when trying to save data to the
+ * difference storage, this event is generated.
+ * However, this does not mean that the backup process was interrupted
+ * with an error. If the snapshot image has been read to the end by
+ * this time, the backup process is considered successful.
+ */
+ blk_snap_event_code_corrupted,
+};
+
+/**
+ * struct blk_snap_snapshot_event - Argument for the
+ * &IOCTL_BLK_SNAP_SNAPSHOT_WAIT_EVENT control.
+ * @id:
+ * Snapshot ID.
+ * @timeout_ms:
+ * Timeout for waiting in milliseconds.
+ * @time_label:
+ * Timestamp of the received event.
+ * @code:
+ * Code of the received event.
+ * @data:
+ * The received event body.
+ */
+struct blk_snap_snapshot_event {
+ struct blk_snap_uuid id;
+ __u32 timeout_ms;
+ __u32 code;
+ __s64 time_label;
+ __u8 data[4096 - 32];
+};
+static_assert(
+ sizeof(struct blk_snap_snapshot_event) == 4096,
+ "The size struct blk_snap_snapshot_event should be equal to the size of the page.");
+
+/**
+ * IOCTL_BLK_SNAP_SNAPSHOT_WAIT_EVENT - Wait and get the event from the
+ * snapshot.
+ *
+ * While holding the snapshot, the kernel module can transmit information about
+ * changes in its state in the form of events to the user level.
+ * It is very important to receive these events as quickly as possible, so the
+ * user's thread is in the state of interruptable sleep.
+ */
+#define IOCTL_BLK_SNAP_SNAPSHOT_WAIT_EVENT \
+ _IOW(BLK_SNAP, blk_snap_ioctl_snapshot_wait_event, \
+ struct blk_snap_snapshot_event)
+
+/**
+ * struct blk_snap_event_low_free_space - Data for the
+ * &blk_snap_event_code_low_free_space event.
+ * @requested_nr_sect:
+ * The required number of sectors.
+ */
+struct blk_snap_event_low_free_space {
+ __u64 requested_nr_sect;
+};
+
+/**
+ * struct blk_snap_event_corrupted - Data for the
+ * &blk_snap_event_code_corrupted event.
+ * @orig_dev_id:
+ * Device ID.
+ * @err_code:
+ * Error code.
+ */
+struct blk_snap_event_corrupted {
+ struct blk_snap_dev orig_dev_id;
+ __s32 err_code;
+};
+
+#endif /* _UAPI_LINUX_BLK_SNAP_H */
--
2.20.1
Provides management of regions of block devices available for storing
difference blocks of a snapshot. Contains lists of free and already
occupied regions.
Signed-off-by: Sergei Shtepa <[email protected]>
---
drivers/block/blksnap/diff_storage.c | 292 +++++++++++++++++++++++++++
drivers/block/blksnap/diff_storage.h | 93 +++++++++
2 files changed, 385 insertions(+)
create mode 100644 drivers/block/blksnap/diff_storage.c
create mode 100644 drivers/block/blksnap/diff_storage.h
diff --git a/drivers/block/blksnap/diff_storage.c b/drivers/block/blksnap/diff_storage.c
new file mode 100644
index 000000000000..d30b7089afdc
--- /dev/null
+++ b/drivers/block/blksnap/diff_storage.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME "-diff-storage: " fmt
+#include <linux/slab.h>
+#include <linux/sched/mm.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <uapi/linux/blksnap.h>
+#include "params.h"
+#include "chunk.h"
+#include "diff_io.h"
+#include "diff_buffer.h"
+#include "diff_storage.h"
+
+/**
+ * struct storage_bdev - Information about the opened block device.
+ */
+struct storage_bdev {
+ struct list_head link;
+ dev_t dev_id;
+ struct block_device *bdev;
+};
+
+/**
+ * struct storage_block - A storage unit reserved for storing differences.
+ *
+ */
+struct storage_block {
+ struct list_head link;
+ struct block_device *bdev;
+ sector_t sector;
+ sector_t count;
+ sector_t used;
+};
+
+static inline void diff_storage_event_low(struct diff_storage *diff_storage)
+{
+ struct blk_snap_event_low_free_space data = {
+ .requested_nr_sect = diff_storage_minimum,
+ };
+
+ diff_storage->requested += data.requested_nr_sect;
+ pr_debug(
+ "Diff storage low free space. Portion: %llu sectors, requested: %llu\n",
+ data.requested_nr_sect, diff_storage->requested);
+ event_gen(&diff_storage->event_queue, GFP_NOIO,
+ blk_snap_event_code_low_free_space, &data, sizeof(data));
+}
+
+struct diff_storage *diff_storage_new(void)
+{
+ struct diff_storage *diff_storage;
+
+ diff_storage = kzalloc(sizeof(struct diff_storage), GFP_KERNEL);
+ if (!diff_storage)
+ return NULL;
+
+ kref_init(&diff_storage->kref);
+ spin_lock_init(&diff_storage->lock);
+ INIT_LIST_HEAD(&diff_storage->storage_bdevs);
+ INIT_LIST_HEAD(&diff_storage->empty_blocks);
+ INIT_LIST_HEAD(&diff_storage->filled_blocks);
+
+ event_queue_init(&diff_storage->event_queue);
+ diff_storage_event_low(diff_storage);
+
+ return diff_storage;
+}
+
+static inline struct storage_block *
+first_empty_storage_block(struct diff_storage *diff_storage)
+{
+ return list_first_entry_or_null(&diff_storage->empty_blocks,
+ struct storage_block, link);
+};
+
+static inline struct storage_block *
+first_filled_storage_block(struct diff_storage *diff_storage)
+{
+ return list_first_entry_or_null(&diff_storage->filled_blocks,
+ struct storage_block, link);
+};
+
+static inline struct storage_bdev *
+first_storage_bdev(struct diff_storage *diff_storage)
+{
+ return list_first_entry_or_null(&diff_storage->storage_bdevs,
+ struct storage_bdev, link);
+};
+
+void diff_storage_free(struct kref *kref)
+{
+ struct diff_storage *diff_storage =
+ container_of(kref, struct diff_storage, kref);
+ struct storage_block *blk;
+ struct storage_bdev *storage_bdev;
+
+ while ((blk = first_empty_storage_block(diff_storage))) {
+ list_del(&blk->link);
+ kfree(blk);
+ }
+
+ while ((blk = first_filled_storage_block(diff_storage))) {
+ list_del(&blk->link);
+ kfree(blk);
+ }
+
+ while ((storage_bdev = first_storage_bdev(diff_storage))) {
+ blkdev_put(storage_bdev->bdev, FMODE_READ | FMODE_WRITE);
+ list_del(&storage_bdev->link);
+ kfree(storage_bdev);
+ }
+ event_queue_done(&diff_storage->event_queue);
+
+ kfree(diff_storage);
+}
+
+static struct block_device *
+diff_storage_bdev_by_id(struct diff_storage *diff_storage, dev_t dev_id)
+{
+ struct block_device *bdev = NULL;
+ struct storage_bdev *storage_bdev;
+
+ spin_lock(&diff_storage->lock);
+ list_for_each_entry(storage_bdev, &diff_storage->storage_bdevs, link) {
+ if (storage_bdev->dev_id == dev_id) {
+ bdev = storage_bdev->bdev;
+ break;
+ }
+ }
+ spin_unlock(&diff_storage->lock);
+
+ return bdev;
+}
+
+static inline struct block_device *
+diff_storage_add_storage_bdev(struct diff_storage *diff_storage, dev_t dev_id)
+{
+ struct block_device *bdev;
+ struct storage_bdev *storage_bdev;
+
+ bdev = blkdev_get_by_dev(dev_id, FMODE_READ | FMODE_WRITE, NULL);
+ if (IS_ERR(bdev)) {
+ pr_err("Failed to open device. errno=%d\n",
+ abs((int)PTR_ERR(bdev)));
+ return bdev;
+ }
+
+ storage_bdev = kzalloc(sizeof(struct storage_bdev), GFP_KERNEL);
+ if (!storage_bdev) {
+ blkdev_put(bdev, FMODE_READ | FMODE_WRITE);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ storage_bdev->bdev = bdev;
+ storage_bdev->dev_id = dev_id;
+ INIT_LIST_HEAD(&storage_bdev->link);
+
+ spin_lock(&diff_storage->lock);
+ list_add_tail(&storage_bdev->link, &diff_storage->storage_bdevs);
+ spin_unlock(&diff_storage->lock);
+
+ return bdev;
+}
+
+static inline int diff_storage_add_range(struct diff_storage *diff_storage,
+ struct block_device *bdev,
+ sector_t sector, sector_t count)
+{
+ struct storage_block *storage_block;
+
+ pr_debug("Add range to diff storage: [%u:%u] %llu:%llu\n",
+ MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev), sector, count);
+
+ storage_block = kzalloc(sizeof(struct storage_block), GFP_KERNEL);
+ if (!storage_block)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&storage_block->link);
+ storage_block->bdev = bdev;
+ storage_block->sector = sector;
+ storage_block->count = count;
+
+ spin_lock(&diff_storage->lock);
+ list_add_tail(&storage_block->link, &diff_storage->empty_blocks);
+ diff_storage->capacity += count;
+ spin_unlock(&diff_storage->lock);
+
+ return 0;
+}
+
+int diff_storage_append_block(struct diff_storage *diff_storage, dev_t dev_id,
+ struct blk_snap_block_range __user *ranges,
+ unsigned int range_count)
+{
+ int ret;
+ int inx;
+ struct block_device *bdev;
+ struct blk_snap_block_range range;
+ const unsigned long range_size = sizeof(struct blk_snap_block_range);
+
+ pr_debug("Append %u blocks\n", range_count);
+
+ bdev = diff_storage_bdev_by_id(diff_storage, dev_id);
+ if (!bdev) {
+ bdev = diff_storage_add_storage_bdev(diff_storage, dev_id);
+ if (IS_ERR(bdev))
+ return PTR_ERR(bdev);
+ }
+
+ for (inx = 0; inx < range_count; inx++) {
+ if (unlikely(copy_from_user(&range, ranges+inx, range_size)))
+ return -EINVAL;
+
+ ret = diff_storage_add_range(diff_storage, bdev,
+ range.sector_offset,
+ range.sector_count);
+ if (unlikely(ret))
+ return ret;
+ }
+
+ if (atomic_read(&diff_storage->low_space_flag) &&
+ (diff_storage->capacity >= diff_storage->requested))
+ atomic_set(&diff_storage->low_space_flag, 0);
+
+ return 0;
+}
+
+struct diff_region *diff_storage_new_region(struct diff_storage *diff_storage,
+ sector_t count)
+{
+ int ret = 0;
+ struct diff_region *diff_region;
+ sector_t sectors_left;
+
+ if (atomic_read(&diff_storage->overflow_flag))
+ return ERR_PTR(-ENOSPC);
+
+ diff_region = kzalloc(sizeof(struct diff_region), GFP_NOIO);
+ if (!diff_region)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock(&diff_storage->lock);
+ do {
+ struct storage_block *storage_block;
+ sector_t available;
+
+ storage_block = first_empty_storage_block(diff_storage);
+ if (unlikely(!storage_block)) {
+ atomic_inc(&diff_storage->overflow_flag);
+ ret = -ENOSPC;
+ break;
+ }
+
+ available = storage_block->count - storage_block->used;
+ if (likely(available >= count)) {
+ diff_region->bdev = storage_block->bdev;
+ diff_region->sector =
+ storage_block->sector + storage_block->used;
+ diff_region->count = count;
+
+ storage_block->used += count;
+ diff_storage->filled += count;
+ break;
+ }
+
+ list_del(&storage_block->link);
+ list_add_tail(&storage_block->link,
+ &diff_storage->filled_blocks);
+ /*
+ * If there is still free space in the storage block, but
+ * it is not enough to store a piece, then such a block is
+ * considered used.
+ * We believe that the storage blocks are large enough
+ * to accommodate several pieces entirely.
+ */
+ diff_storage->filled += available;
+ } while (1);
+ sectors_left = diff_storage->requested - diff_storage->filled;
+ spin_unlock(&diff_storage->lock);
+
+ if (ret) {
+ pr_err("Cannot get empty storage block\n");
+ diff_storage_free_region(diff_region);
+ return ERR_PTR(ret);
+ }
+
+ if ((sectors_left <= diff_storage_minimum) &&
+ (atomic_inc_return(&diff_storage->low_space_flag) == 1))
+ diff_storage_event_low(diff_storage);
+
+ return diff_region;
+}
diff --git a/drivers/block/blksnap/diff_storage.h b/drivers/block/blksnap/diff_storage.h
new file mode 100644
index 000000000000..efd0525afd01
--- /dev/null
+++ b/drivers/block/blksnap/diff_storage.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BLK_SNAP_DIFF_STORAGE_H
+#define __BLK_SNAP_DIFF_STORAGE_H
+
+#include "event_queue.h"
+
+struct blk_snap_block_range;
+struct diff_region;
+
+/**
+ * struct diff_storage - Difference storage.
+ *
+ * @kref:
+ * The reference counter.
+ * @lock:
+ * Spinlock allows to guarantee the safety of linked lists.
+ * @storage_bdevs:
+ * List of opened block devices. Blocks for storing snapshot data can be
+ * located on different block devices. So, all opened block devices are
+ * located in this list. Blocks on opened block devices are allocated for
+ * storing the chunks data.
+ * @empty_blocks:
+ * List of empty blocks on storage. This list can be updated while
+ * holding a snapshot. This allows us to dynamically increase the
+ * storage size for these snapshots.
+ * @filled_blocks:
+ * List of filled blocks. When the blocks from the list of empty blocks are filled,
+ * we move them to the list of filled blocks.
+ * @capacity:
+ * Total amount of available storage space.
+ * @filled:
+ * The number of sectors already filled in.
+ * @requested:
+ * The number of sectors already requested from user space.
+ * @low_space_flag:
+ * The flag is set if the number of free regions available in the
+ * difference storage is less than the allowed minimum.
+ * @overflow_flag:
+ * The request for a free region failed due to the absence of free
+ * regions in the difference storage.
+ * @event_queue:
+ * A queue of events to pass events to user space. Diff storage and its
+ * owner can notify its snapshot about events like snapshot overflow,
+ * low free space and snapshot terminated.
+ *
+ * The difference storage manages the regions of block devices that are used
+ * to store the data of the original block devices in the snapshot.
+ * The difference storage is created one per snapshot and is used to store
+ * data from all the original snapshot block devices. At the same time, the
+ * difference storage itself can contain regions on various block devices.
+ */
+struct diff_storage {
+ struct kref kref;
+ spinlock_t lock;
+
+ struct list_head storage_bdevs;
+ struct list_head empty_blocks;
+ struct list_head filled_blocks;
+
+ sector_t capacity;
+ sector_t filled;
+ sector_t requested;
+
+ atomic_t low_space_flag;
+ atomic_t overflow_flag;
+
+ struct event_queue event_queue;
+};
+
+struct diff_storage *diff_storage_new(void);
+void diff_storage_free(struct kref *kref);
+
+static inline void diff_storage_get(struct diff_storage *diff_storage)
+{
+ kref_get(&diff_storage->kref);
+};
+static inline void diff_storage_put(struct diff_storage *diff_storage)
+{
+ if (likely(diff_storage))
+ kref_put(&diff_storage->kref, diff_storage_free);
+};
+
+int diff_storage_append_block(struct diff_storage *diff_storage, dev_t dev_id,
+ struct blk_snap_block_range __user *ranges,
+ unsigned int range_count);
+struct diff_region *diff_storage_new_region(struct diff_storage *diff_storage,
+ sector_t count);
+
+static inline void diff_storage_free_region(struct diff_region *region)
+{
+ kfree(region);
+}
+#endif /* __BLK_SNAP_DIFF_STORAGE_H */
--
2.20.1
Implementation of module management interface functions. At this level,
the input and output parameters are converted and the corresponding
subsystems of the module are called.
Signed-off-by: Sergei Shtepa <[email protected]>
---
drivers/block/blksnap/ctrl.c | 408 +++++++++++++++++++++++++++++++++++
drivers/block/blksnap/ctrl.h | 9 +
2 files changed, 417 insertions(+)
create mode 100644 drivers/block/blksnap/ctrl.c
create mode 100644 drivers/block/blksnap/ctrl.h
diff --git a/drivers/block/blksnap/ctrl.c b/drivers/block/blksnap/ctrl.c
new file mode 100644
index 000000000000..2bb1eaafe569
--- /dev/null
+++ b/drivers/block/blksnap/ctrl.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME "-ctrl: " fmt
+
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <uapi/linux/blksnap.h>
+#include "ctrl.h"
+#include "params.h"
+#include "version.h"
+#include "snapshot.h"
+#include "snapimage.h"
+#include "tracker.h"
+
+static_assert(sizeof(uuid_t) == sizeof(struct blk_snap_uuid),
+ "Invalid size of struct blk_snap_uuid or uuid_t.");
+
+static int blk_snap_major;
+
+static long ctrl_unlocked_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations ctrl_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ctrl_unlocked_ioctl,
+};
+
+static const struct blk_snap_version version = {
+ .major = VERSION_MAJOR,
+ .minor = VERSION_MINOR,
+ .revision = VERSION_REVISION,
+ .build = VERSION_BUILD,
+};
+
+int get_blk_snap_major(void)
+{
+ return blk_snap_major;
+}
+
+int ctrl_init(void)
+{
+ int ret;
+
+ ret = register_chrdev(0, THIS_MODULE->name, &ctrl_fops);
+ if (ret < 0) {
+ pr_err("Failed to register a character device. errno=%d\n",
+ abs(blk_snap_major));
+ return ret;
+ }
+
+ blk_snap_major = ret;
+ pr_info("Register control device [%d:0].\n", blk_snap_major);
+ return 0;
+}
+
+void ctrl_done(void)
+{
+ pr_info("Unregister control device\n");
+
+ unregister_chrdev(blk_snap_major, THIS_MODULE->name);
+}
+
+static int ioctl_version(unsigned long arg)
+{
+ if (copy_to_user((void *)arg, &version, sizeof(version))) {
+ pr_err("Unable to get version: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int ioctl_tracker_remove(unsigned long arg)
+{
+ struct blk_snap_tracker_remove karg;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg)) != 0) {
+ pr_err("Unable to remove device from tracking: invalid user buffer\n");
+ return -ENODATA;
+ }
+ return tracker_remove(MKDEV(karg.dev_id.mj, karg.dev_id.mn));
+}
+
+static int ioctl_tracker_collect(unsigned long arg)
+{
+ int res;
+ struct blk_snap_tracker_collect karg;
+ struct blk_snap_cbt_info *cbt_info = NULL;
+
+ pr_debug("Collecting tracking devices\n");
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to collect tracking devices: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ if (!karg.cbt_info_array) {
+ /*
+ * If the buffer is empty, this is a request to determine
+ * the number of trackers.
+ */
+ res = tracker_collect(0, NULL, &karg.count);
+ if (res) {
+ pr_err("Failed to execute tracker_collect. errno=%d\n",
+ abs(res));
+ return res;
+ }
+ if (copy_to_user((void *)arg, (void *)&karg, sizeof(karg))) {
+ pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n");
+ return -ENODATA;
+ }
+ return 0;
+ }
+
+ cbt_info = kcalloc(karg.count, sizeof(struct blk_snap_cbt_info),
+ GFP_KERNEL);
+ if (cbt_info == NULL)
+ return -ENOMEM;
+
+ res = tracker_collect(karg.count, cbt_info, &karg.count);
+ if (res) {
+ pr_err("Failed to execute tracker_collect. errno=%d\n",
+ abs(res));
+ goto fail;
+ }
+
+ if (copy_to_user(karg.cbt_info_array, cbt_info,
+ karg.count * sizeof(struct blk_snap_cbt_info))) {
+ pr_err("Unable to collect tracking devices: invalid user buffer for CBT info\n");
+ res = -ENODATA;
+ goto fail;
+ }
+
+ if (copy_to_user((void *)arg, (void *)&karg, sizeof(karg))) {
+ pr_err("Unable to collect tracking devices: invalid user buffer for arguments\n");
+ res = -ENODATA;
+ goto fail;
+ }
+fail:
+ kfree(cbt_info);
+
+ return res;
+}
+
+static int ioctl_tracker_read_cbt_map(unsigned long arg)
+{
+ struct blk_snap_tracker_read_cbt_bitmap karg;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to read CBT map: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ return tracker_read_cbt_bitmap(MKDEV(karg.dev_id.mj, karg.dev_id.mn),
+ karg.offset, karg.length,
+ (char __user *)karg.buff);
+}
+
+static int ioctl_tracker_mark_dirty_blocks(unsigned long arg)
+{
+ int ret = 0;
+ struct blk_snap_tracker_mark_dirty_blocks karg;
+ struct blk_snap_block_range *dirty_blocks_array;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to mark dirty blocks: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ dirty_blocks_array = kcalloc(
+ karg.count, sizeof(struct blk_snap_block_range), GFP_KERNEL);
+ if (!dirty_blocks_array)
+ return -ENOMEM;
+
+ if (copy_from_user(dirty_blocks_array, (void *)karg.dirty_blocks_array,
+ karg.count * sizeof(struct blk_snap_block_range))) {
+ pr_err("Unable to mark dirty blocks: invalid user buffer\n");
+ ret = -ENODATA;
+ } else {
+ if (karg.dev_id.mj == snapimage_major())
+ ret = snapshot_mark_dirty_blocks(
+ MKDEV(karg.dev_id.mj, karg.dev_id.mn),
+ dirty_blocks_array, karg.count);
+ else
+ ret = tracker_mark_dirty_blocks(
+ MKDEV(karg.dev_id.mj, karg.dev_id.mn),
+ dirty_blocks_array, karg.count);
+ }
+
+ kfree(dirty_blocks_array);
+
+ return ret;
+}
+
+static int ioctl_snapshot_create(unsigned long arg)
+{
+ int ret;
+ struct blk_snap_snapshot_create karg;
+ struct blk_snap_dev *dev_id_array = NULL;
+ uuid_t new_id;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to create snapshot: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ dev_id_array =
+ kcalloc(karg.count, sizeof(struct blk_snap_dev), GFP_KERNEL);
+ if (dev_id_array == NULL) {
+ pr_err("Unable to create snapshot: too many devices %d\n",
+ karg.count);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(dev_id_array, (void *)karg.dev_id_array,
+ karg.count * sizeof(struct blk_snap_dev))) {
+ pr_err("Unable to create snapshot: invalid user buffer\n");
+ ret = -ENODATA;
+ goto out;
+ }
+
+ ret = snapshot_create(dev_id_array, karg.count, &new_id);
+ if (ret)
+ goto out;
+
+ export_uuid(karg.id.b, &new_id);
+ if (copy_to_user((void *)arg, &karg, sizeof(karg))) {
+ pr_err("Unable to create snapshot: invalid user buffer\n");
+ ret = -ENODATA;
+ }
+out:
+ kfree(dev_id_array);
+
+ return ret;
+}
+
+static int ioctl_snapshot_destroy(unsigned long arg)
+{
+ struct blk_snap_snapshot_destroy karg;
+ uuid_t id;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to destroy snapshot: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ import_uuid(&id, karg.id.b);
+ return snapshot_destroy(&id);
+}
+
+static int ioctl_snapshot_append_storage(unsigned long arg)
+{
+ struct blk_snap_snapshot_append_storage karg;
+ uuid_t id;
+
+ pr_debug("Append difference storage\n");
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to append difference storage: invalid user buffer\n");
+ return -EINVAL;
+ }
+
+ import_uuid(&id, karg.id.b);
+ return snapshot_append_storage(&id, karg.dev_id, karg.ranges,
+ karg.count);
+}
+
+static int ioctl_snapshot_take(unsigned long arg)
+{
+ struct blk_snap_snapshot_take karg;
+ uuid_t id;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to take snapshot: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ import_uuid(&id, karg.id.b);
+ return snapshot_take(&id);
+}
+
+static int ioctl_snapshot_wait_event(unsigned long arg)
+{
+ int ret = 0;
+ struct blk_snap_snapshot_event *karg;
+ uuid_t id;
+ struct event *event;
+
+ karg = kzalloc(sizeof(struct blk_snap_snapshot_event), GFP_KERNEL);
+ if (!karg)
+ return -ENOMEM;
+
+ if (copy_from_user(karg, (void *)arg,
+ sizeof(struct blk_snap_snapshot_event))) {
+ pr_err("Unable failed to get snapstore error code: invalid user buffer\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ import_uuid(&id, karg->id.b);
+ event = snapshot_wait_event(&id, karg->timeout_ms);
+ if (IS_ERR(event)) {
+ ret = PTR_ERR(event);
+ goto out;
+ }
+
+ pr_debug("Received event=%lld code=%d data_size=%d\n", event->time,
+ event->code, event->data_size);
+ karg->code = event->code;
+ karg->time_label = event->time;
+
+ if (event->data_size > sizeof(karg->data)) {
+ pr_err("Event size %d is too big\n", event->data_size);
+ ret = -ENOSPC;
+ /* If we can't copy all the data, we copy only part of it. */
+ }
+ memcpy(karg->data, event->data, event->data_size);
+ event_free(event);
+
+ if (copy_to_user((void *)arg, karg,
+ sizeof(struct blk_snap_snapshot_event))) {
+ pr_err("Unable to get snapstore error code: invalid user buffer\n");
+ ret = -EINVAL;
+ }
+out:
+ kfree(karg);
+
+ return ret;
+}
+
+static int ioctl_snapshot_collect(unsigned long arg)
+{
+ int ret;
+ struct blk_snap_snapshot_collect karg;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to collect available snapshots: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ ret = snapshot_collect(&karg.count, karg.ids);
+
+ if (copy_to_user((void *)arg, &karg, sizeof(karg))) {
+ pr_err("Unable to collect available snapshots: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ return ret;
+}
+
+static int ioctl_snapshot_collect_images(unsigned long arg)
+{
+ int ret;
+ struct blk_snap_snapshot_collect_images karg;
+ uuid_t id;
+
+ if (copy_from_user(&karg, (void *)arg, sizeof(karg))) {
+ pr_err("Unable to collect snapshot images: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ import_uuid(&id, karg.id.b);
+ ret = snapshot_collect_images(&id, karg.image_info_array,
+ &karg.count);
+
+ if (copy_to_user((void *)arg, &karg, sizeof(karg))) {
+ pr_err("Unable to collect snapshot images: invalid user buffer\n");
+ return -ENODATA;
+ }
+
+ return ret;
+}
+
+static int (*const blk_snap_ioctl_table[])(unsigned long arg) = {
+ ioctl_version,
+ ioctl_tracker_remove,
+ ioctl_tracker_collect,
+ ioctl_tracker_read_cbt_map,
+ ioctl_tracker_mark_dirty_blocks,
+ ioctl_snapshot_create,
+ ioctl_snapshot_destroy,
+ ioctl_snapshot_append_storage,
+ ioctl_snapshot_take,
+ ioctl_snapshot_collect,
+ ioctl_snapshot_collect_images,
+ ioctl_snapshot_wait_event,
+};
+
+static_assert(
+ sizeof(blk_snap_ioctl_table) ==
+ ((blk_snap_ioctl_snapshot_wait_event + 1) * sizeof(void *)),
+ "The size of table blk_snap_ioctl_table does not match the enum blk_snap_ioctl.");
+
+
+static long ctrl_unlocked_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int nr = _IOC_NR(cmd);
+
+ if (nr > (sizeof(blk_snap_ioctl_table) / sizeof(void *)))
+ return -ENOTTY;
+
+ if (!blk_snap_ioctl_table[nr])
+ return -ENOTTY;
+
+ return blk_snap_ioctl_table[nr](arg);
+}
diff --git a/drivers/block/blksnap/ctrl.h b/drivers/block/blksnap/ctrl.h
new file mode 100644
index 000000000000..ade3f1cf57e9
--- /dev/null
+++ b/drivers/block/blksnap/ctrl.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BLK_SNAP_CTRL_H
+#define __BLK_SNAP_CTRL_H
+
+int get_blk_snap_major(void);
+
+int ctrl_init(void);
+void ctrl_done(void);
+#endif /* __BLK_SNAP_CTRL_H */
--
2.20.1
Allows to attach block device filters to the block devices. Kernel
modules can use this functionality to extend the capabilities of the
block layer.
Signed-off-by: Sergei Shtepa <[email protected]>
---
block/bdev.c | 73 +++++++++++++++++++++++++++++++++++++++
block/blk-core.c | 19 ++++++++--
include/linux/blk_types.h | 2 ++
include/linux/blkdev.h | 64 ++++++++++++++++++++++++++++++++++
4 files changed, 156 insertions(+), 2 deletions(-)
diff --git a/block/bdev.c b/block/bdev.c
index d699ecdb3260..8c2899267569 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -427,6 +427,7 @@ static void init_once(void *data)
static void bdev_evict_inode(struct inode *inode)
{
+ bdev_filter_detach(I_BDEV(inode));
truncate_inode_pages_final(&inode->i_data);
invalidate_inode_buffers(inode); /* is it needed here? */
clear_inode(inode);
@@ -502,6 +503,7 @@ struct block_device *bdev_alloc(struct gendisk *disk, u8 partno)
return NULL;
}
bdev->bd_disk = disk;
+ bdev->bd_filter = NULL;
return bdev;
}
@@ -1092,3 +1094,74 @@ void bdev_statx_dioalign(struct inode *inode, struct kstat *stat)
blkdev_put_no_open(bdev);
}
+
+/**
+ * bdev_filter_attach - Attach a filter to the original block device.
+ * @bdev:
+ * Block device.
+ * @flt:
+ * Pointer to the filter structure.
+ *
+ * Before adding a filter, it is necessary to initialize &struct bdev_filter.
+ *
+ * The bdev_filter_detach() function allows to detach the filter from the block
+ * device.
+ *
+ * Return:
+ * 0 - OK
+ * -EALREADY - a filter with this name already exists
+ */
+int bdev_filter_attach(struct block_device *bdev,
+ struct bdev_filter *flt)
+{
+ int ret = 0;
+
+ blk_mq_freeze_queue(bdev->bd_queue);
+ blk_mq_quiesce_queue(bdev->bd_queue);
+
+ if (bdev->bd_filter)
+ ret = -EALREADY;
+ else
+ bdev->bd_filter = flt;
+
+ blk_mq_unquiesce_queue(bdev->bd_queue);
+ blk_mq_unfreeze_queue(bdev->bd_queue);
+
+ return ret;
+}
+EXPORT_SYMBOL(bdev_filter_attach);
+
+/**
+ * bdev_filter_detach - Detach a filter from the block device.
+ * @bdev:
+ * Block device.
+ *
+ * The filter should be added using the bdev_filter_attach() function.
+ *
+ * Return:
+ * 0 - OK
+ * -ENOENT - the filter was not found in the linked list
+ */
+int bdev_filter_detach(struct block_device *bdev)
+{
+ int ret = 0;
+ struct bdev_filter *flt = NULL;
+
+ blk_mq_freeze_queue(bdev->bd_queue);
+ blk_mq_quiesce_queue(bdev->bd_queue);
+
+ flt = bdev->bd_filter;
+ if (flt)
+ bdev->bd_filter = NULL;
+ else
+ ret = -ENOENT;
+
+ blk_mq_unquiesce_queue(bdev->bd_queue);
+ blk_mq_unfreeze_queue(bdev->bd_queue);
+
+ if (flt)
+ bdev_filter_put(flt);
+
+ return ret;
+}
+EXPORT_SYMBOL(bdev_filter_detach);
diff --git a/block/blk-core.c b/block/blk-core.c
index 17667159482e..497c635eb794 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -679,9 +679,24 @@ void submit_bio_noacct_nocheck(struct bio *bio)
* to collect a list of requests submited by a ->submit_bio method while
* it is active, and then process them after it returned.
*/
- if (current->bio_list)
+ if (current->bio_list) {
bio_list_add(¤t->bio_list[0], bio);
- else if (!bio->bi_bdev->bd_disk->fops->submit_bio)
+ return;
+ }
+
+ if (bio->bi_bdev->bd_filter && !bio_flagged(bio, BIO_FILTERED)) {
+ bool pass;
+
+ pass = bio->bi_bdev->bd_filter->fops->submit_bio_cb(bio);
+ bio_set_flag(bio, BIO_FILTERED);
+ if (!pass) {
+ bio->bi_status = BLK_STS_OK;
+ bio_endio(bio);
+ return;
+ }
+ }
+
+ if (!bio->bi_bdev->bd_disk->fops->submit_bio)
__submit_bio_noacct_mq(bio);
else
__submit_bio_noacct(bio);
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index e0b098089ef2..3b58c69cbf9d 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -68,6 +68,7 @@ struct block_device {
#ifdef CONFIG_FAIL_MAKE_REQUEST
bool bd_make_it_fail;
#endif
+ struct bdev_filter *bd_filter;
} __randomize_layout;
#define bdev_whole(_bdev) \
@@ -333,6 +334,7 @@ enum {
BIO_QOS_MERGED, /* but went through rq_qos merge path */
BIO_REMAPPED,
BIO_ZONE_WRITE_LOCKED, /* Owns a zoned device zone write lock */
+ BIO_FILTERED, /* bio has already been filtered */
BIO_FLAG_LAST
};
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 50e358a19d98..91d1b4ee38d4 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1549,4 +1549,68 @@ struct io_comp_batch {
#define DEFINE_IO_COMP_BATCH(name) struct io_comp_batch name = { }
+/**
+ * struct bdev_filter_operations - List of callback functions for the filter.
+ *
+ * @submit_bio_cb:
+ * A callback function for bio processing.
+ * @detach_cb:
+ * A callback function to disable the filter when removing a block
+ * device from the system.
+ */
+struct bdev_filter_operations {
+ bool (*submit_bio_cb)(struct bio *bio);
+ void (*detach_cb)(struct kref *kref);
+};
+/**
+ * struct bdev_filter - Block device filter.
+ *
+ * @kref:
+ * Kernel reference counter.
+ * @fops:
+ * The pointer to &struct bdev_filter_operations with callback
+ * functions for the filter.
+ */
+struct bdev_filter {
+ struct kref kref;
+ const struct bdev_filter_operations *fops;
+};
+/**
+ * bdev_filter_init - Initialization of the filter structure.
+ * @flt:
+ * Pointer to the &struct bdev_filter to be initialized.
+ * @fops:
+ * The callback functions for the filter.
+ */
+static inline void bdev_filter_init(struct bdev_filter *flt,
+ const struct bdev_filter_operations *fops)
+{
+ kref_init(&flt->kref);
+ flt->fops = fops;
+};
+
+/**
+ * bdev_filter_get - Incremnent reference counter.
+ * @flt:
+ * Pointer to the &struct bdev_filter.
+ */
+static inline void bdev_filter_get(struct bdev_filter *flt)
+{
+ kref_get(&flt->kref);
+}
+
+/**
+ * bdev_filter_put - Decrement reference counter and detach filter.
+ * @flt:
+ * Pointer to the &struct bdev_filter.
+ */
+static inline void bdev_filter_put(struct bdev_filter *flt)
+{
+ kref_put(&flt->kref, flt->fops->detach_cb);
+};
+
+int bdev_filter_attach(struct block_device *bdev, struct bdev_filter *flt);
+int bdev_filter_detach(struct block_device *bdev);
+
+
#endif /* _LINUX_BLKDEV_H */
--
2.20.1
Signed-off-by: Sergei Shtepa <[email protected]>
---
drivers/block/Kconfig | 2 ++
drivers/block/Makefile | 2 ++
2 files changed, 4 insertions(+)
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index db1b4b202646..882b3dd0264d 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -410,4 +410,6 @@ config BLK_DEV_UBLK
source "drivers/block/rnbd/Kconfig"
+source "drivers/block/blksnap/Kconfig"
+
endif # BLK_DEV
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 101612cba303..8414c47960c2 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -40,3 +40,5 @@ obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_blk/
obj-$(CONFIG_BLK_DEV_UBLK) += ublk_drv.o
swim_mod-y := swim.o swim_asm.o
+
+obj-$(CONFIG_BLK_SNAP) += blksnap/
--
2.20.1
Hi--
On 11/2/22 08:51, Sergei Shtepa wrote:
> Allows to build a module.
>
> Signed-off-by: Sergei Shtepa <[email protected]>
> ---
> drivers/block/blksnap/Kconfig | 12 ++++++++++++
> drivers/block/blksnap/Makefile | 18 ++++++++++++++++++
> 2 files changed, 30 insertions(+)
> create mode 100644 drivers/block/blksnap/Kconfig
> create mode 100644 drivers/block/blksnap/Makefile
>
> diff --git a/drivers/block/blksnap/Kconfig b/drivers/block/blksnap/Kconfig
> new file mode 100644
> index 000000000000..3a6ecb5fc13d
> --- /dev/null
> +++ b/drivers/block/blksnap/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Block device snapshot module configuration
> +#
> +
> +config BLK_SNAP
> + tristate "Module for snapshots of block devices."
> + help
> + Allow to create snapshots and track block changes for block devices.
> + Designed for creating backups for a simple block devices. Snapshots
Drop the "a".
> + are temporary and are released then backup is completed. Change block
> + tracking allows to create incremental or differential backups.
--
~Randy
Hi Sergei,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on axboe-block/for-next]
[also build test ERROR on linus/master v6.1-rc3 next-20221102]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sergei-Shtepa/blksnap-creating-non-persistent-snapshots-for-backup/20221103-004434
base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
patch link: https://lore.kernel.org/r/20221102155101.4550-18-sergei.shtepa%40veeam.com
patch subject: [PATCH v1 17/17] block, blksnap: adds a blksnap to the kernel tree
config: csky-randconfig-c033-20221103
compiler: csky-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/b9fa151bb0035ff8a24621ba486011660510dc45
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sergei-Shtepa/blksnap-creating-non-persistent-snapshots-for-backup/20221103-004434
git checkout b9fa151bb0035ff8a24621ba486011660510dc45
# save the config file
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 ARCH=csky
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
csky-linux-ld: csky-linux-ld: DWARF error: could not find abbrev number 3562
drivers/block/blksnap/sysfs.o: in function `sysfs_init':
>> sysfs.c:(.text+0x78): multiple definition of `sysfs_init'; csky-linux-ld: DWARF error: could not find abbrev number 49
fs/sysfs/mount.o:mount.c:(.init.text+0x0): first defined here
cocci warnings: (new ones prefixed by >>)
>> drivers/block/blksnap/diff_area.c:223:9-16: WARNING: ERR_CAST can be used with bdev
--
>> drivers/block/blksnap/tracker.c:422:9-16: WARNING: ERR_CAST can be used with bdev
--
>> drivers/block/blksnap/ctrl.c:290:8-15: WARNING opportunity for memdup_user
--
>> drivers/block/blksnap/snapimage.c:115:2-3: Unneeded semicolon
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Sergei,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on axboe-block/for-next]
[also build test ERROR on linus/master v6.1-rc3 next-20221102]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sergei-Shtepa/blksnap-creating-non-persistent-snapshots-for-backup/20221103-004434
base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
patch link: https://lore.kernel.org/r/20221102155101.4550-3-sergei.shtepa%40veeam.com
patch subject: [PATCH v1 02/17] block, blksnap: header file of the module interface
config: x86_64-randconfig-a015
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/19bc51d2360933a4f1712270c90d956fabec5297
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sergei-Shtepa/blksnap-creating-non-persistent-snapshots-for-backup/20221103-004434
git checkout 19bc51d2360933a4f1712270c90d956fabec5297
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
In file included from <command-line>:
>> ./usr/include/linux/blksnap.h:428:9: error: expected declaration specifiers or '...' before 'sizeof'
428 | sizeof(struct blk_snap_snapshot_event) == 4096,
| ^~~~~~
>> ./usr/include/linux/blksnap.h:429:9: error: expected declaration specifiers or '...' before string constant
429 | "The size struct blk_snap_snapshot_event should be equal to the size of the page.");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Hi Sergei,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on axboe-block/for-next]
[also build test ERROR on linus/master v6.1-rc3 next-20221102]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sergei-Shtepa/blksnap-creating-non-persistent-snapshots-for-backup/20221103-004434
base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
patch link: https://lore.kernel.org/r/20221102155101.4550-3-sergei.shtepa%40veeam.com
patch subject: [PATCH v1 02/17] block, blksnap: header file of the module interface
config: x86_64-randconfig-a004
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/19bc51d2360933a4f1712270c90d956fabec5297
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sergei-Shtepa/blksnap-creating-non-persistent-snapshots-for-backup/20221103-004434
git checkout 19bc51d2360933a4f1712270c90d956fabec5297
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
In file included from <command-line>:
>> ./usr/include/linux/blksnap.h:428:9: error: expected declaration specifiers or '...' before 'sizeof'
428 | sizeof(struct blk_snap_snapshot_event) == 4096,
| ^~~~~~
>> ./usr/include/linux/blksnap.h:429:9: error: expected declaration specifiers or '...' before string constant
429 | "The size struct blk_snap_snapshot_event should be equal to the size of the page.");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
0-DAY CI Kernel Test Service
https://01.org/lkp