%PDF- %PDF-
| Direktori : /var/lib/dkms/blksnap/6.3.0.73/source/ |
| Current File : //var/lib/dkms/blksnap/6.3.0.73/source/snapshot.c |
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME "-snapshot: " fmt
#include <linux/slab.h>
#include <linux/sched/mm.h>
#ifdef STANDALONE_BDEVFILTER
#include "blk_snap.h"
#else
#include <linux/blk_snap.h>
#endif
#include "memory_checker.h"
#include "snapshot.h"
#include "tracker.h"
#include "diff_storage.h"
#include "diff_area.h"
#include "snapimage.h"
#include "cbt_map.h"
#ifdef STANDALONE_BDEVFILTER
#include "log.h"
#endif
#ifdef STANDALONE_BDEVFILTER
#include "bdevfilter.h"
#endif
LIST_HEAD(snapshots);
DECLARE_RWSEM(snapshots_lock);
#if defined(BLK_SNAP_SEQUENTALFREEZE)
/**
* snapshot_release_trackers - Releases snapshots trackers
*
* The sequential algorithm allows to freeze block devices one at a time.
*/
static void snapshot_release_trackers(struct snapshot *snapshot)
{
int inx;
pr_info("Sequentially release snapshots trackers\n");
for (inx = 0; inx < snapshot->count; ++inx) {
struct tracker *tracker = snapshot->tracker_array[inx];
#if defined(HAVE_SUPER_BLOCK_FREEZE)
struct super_block *sb = NULL;
#else
bool is_frozen = false;
#endif
if (!tracker || !tracker->diff_area)
continue;
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("\tfor device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
/* Flush and freeze fs */
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_freeze_bdev(tracker->diff_area->orig_bdev, &sb);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_freeze(tracker->diff_area->orig_bdev))
#else
if (freeze_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to freeze device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else {
is_frozen = true;
pr_debug("Device [%u:%u] was frozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
}
#endif
/* Set tracker as available for new snapshots. */
tracker_lock();
tracker_release_snapshot(tracker);
tracker_unlock();
/* Thaw fs */
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_thaw_bdev(tracker->diff_area->orig_bdev, sb);
#else
if (!is_frozen)
continue;
#if defined(HAVE_BDEV_FREEZE)
if (bdev_thaw(tracker->diff_area->orig_bdev))
#else
if (thaw_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to thaw device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was unfrozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
}
}
#else /* BLK_SNAP_SEQUENTALFREEZE */
/**
* snapshot_release_trackers - Releases snapshots trackers
*
* The simultaneous algorithm allows to freeze all the snapshot block devices.
*/
static void snapshot_release_trackers(struct snapshot *snapshot)
{
int inx;
unsigned int current_flag;
/* Flush and freeze fs on each original block device. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Flush and freeze fs on each original block device\n");
#endif
for (inx = 0; inx < snapshot->count; ++inx) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (!tracker || !tracker->diff_area)
continue;
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_freeze_bdev(tracker->diff_area->orig_bdev,
&snapshot->superblock_array[inx]);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_freeze(tracker->diff_area->orig_bdev))
#else
if (freeze_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to freeze device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was frozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
}
current_flag = memalloc_noio_save();
tracker_lock();
/* Set tracker as available for new snapshots. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Set tracker as available for new snapshots");
#endif
for (inx = 0; inx < snapshot->count; ++inx)
tracker_release_snapshot(snapshot->tracker_array[inx]);
tracker_unlock();
memalloc_noio_restore(current_flag);
/* Thaw fs on each original block device. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Thaw fs on each original block device");
#endif
for (inx = 0; inx < snapshot->count; ++inx) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (!tracker || !tracker->diff_area)
continue;
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_thaw_bdev(tracker->diff_area->orig_bdev,
snapshot->superblock_array[inx]);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_thaw(tracker->diff_area->orig_bdev))
#else
if (thaw_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to thaw device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was unfrozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
}
}
#endif /* BLK_SNAP_SEQUENTALFREEZE */
static void snapshot_release(struct snapshot *snapshot)
{
int inx;
pr_info("Release snapshot %pUb\n", &snapshot->id);
/* Destroy all snapshot images. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Destroy all snapshot images\n");
#endif
for (inx = 0; inx < snapshot->count; ++inx) {
struct snapimage *snapimage = snapshot->snapimage_array[inx];
if (snapimage)
snapimage_free(snapimage);
}
snapshot_release_trackers(snapshot);
/* Destroy diff area for each tracker. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Destroy diff area for each tracker");
#endif
for (inx = 0; inx < snapshot->count; ++inx) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (tracker) {
diff_area_put(tracker->diff_area);
tracker->diff_area = NULL;
tracker_lock();
uuid_copy(&tracker->owner_id, &uuid_null);
tracker_unlock();
tracker_put(tracker);
snapshot->tracker_array[inx] = NULL;
}
}
}
static void snapshot_free(struct kref *kref)
{
struct snapshot *snapshot = container_of(kref, struct snapshot, kref);
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("DEBUG! %s releasing snapshot\n", __FUNCTION__);
#endif
snapshot_release(snapshot);
kfree(snapshot->snapimage_array);
if (snapshot->snapimage_array)
memory_object_dec(memory_object_snapimage_array);
kfree(snapshot->tracker_array);
if (snapshot->tracker_array)
memory_object_dec(memory_object_tracker_array);
#if defined(HAVE_SUPER_BLOCK_FREEZE) && !defined(BLK_SNAP_SEQUENTALFREEZE)
if (snapshot->superblock_array) {
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("DEBUG! %s free superblocks\n", __FUNCTION__);
#endif
kfree(snapshot->superblock_array);
memory_object_dec(memory_object_superblock_array);
}
#endif
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("DEBUG! %s put diff storage\n", __FUNCTION__);
#endif
diff_storage_put(snapshot->diff_storage);
kfree(snapshot);
memory_object_dec(memory_object_snapshot);
}
static inline void snapshot_get(struct snapshot *snapshot)
{
kref_get(&snapshot->kref);
};
static inline void snapshot_put(struct snapshot *snapshot)
{
if (likely(snapshot))
kref_put(&snapshot->kref, snapshot_free);
};
static struct snapshot *snapshot_new(unsigned int count)
{
int ret;
struct snapshot *snapshot = NULL;
snapshot = kzalloc(sizeof(struct snapshot), GFP_KERNEL);
if (!snapshot) {
ret = -ENOMEM;
goto fail;
}
memory_object_inc(memory_object_snapshot);
snapshot->tracker_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!snapshot->tracker_array) {
ret = -ENOMEM;
goto fail_free_snapshot;
}
memory_object_inc(memory_object_tracker_array);
snapshot->snapimage_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!snapshot->snapimage_array) {
ret = -ENOMEM;
goto fail_free_trackers;
}
memory_object_inc(memory_object_snapimage_array);
#if defined(HAVE_SUPER_BLOCK_FREEZE) && !defined(BLK_SNAP_SEQUENTALFREEZE)
snapshot->superblock_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!snapshot->superblock_array) {
ret = -ENOMEM;
goto fail_free_snapimage;
}
memory_object_inc(memory_object_superblock_array);
#endif
snapshot->diff_storage = diff_storage_new();
if (!snapshot->diff_storage) {
ret = -ENOMEM;
goto fail_free_snapimage;
}
INIT_LIST_HEAD(&snapshot->link);
kref_init(&snapshot->kref);
uuid_gen(&snapshot->id);
snapshot->is_taken = false;
return snapshot;
fail_free_snapimage:
#if defined(HAVE_SUPER_BLOCK_FREEZE) && !defined(BLK_SNAP_SEQUENTALFREEZE)
kfree(snapshot->superblock_array);
if (snapshot->superblock_array)
memory_object_dec(memory_object_superblock_array);
#endif
kfree(snapshot->snapimage_array);
if (snapshot->snapimage_array)
memory_object_dec(memory_object_snapimage_array);
fail_free_trackers:
kfree(snapshot->tracker_array);
if (snapshot->tracker_array)
memory_object_dec(memory_object_tracker_array);
fail_free_snapshot:
kfree(snapshot);
if (snapshot)
memory_object_dec(memory_object_snapshot);
fail:
return ERR_PTR(ret);
}
void snapshot_done(void)
{
struct snapshot *snapshot;
pr_debug("Cleanup snapshots\n");
do {
down_write(&snapshots_lock);
snapshot = list_first_entry_or_null(&snapshots, struct snapshot,
link);
if (snapshot)
list_del(&snapshot->link);
up_write(&snapshots_lock);
snapshot_put(snapshot);
} while (snapshot);
}
static inline bool blk_snap_dev_is_equal(struct blk_snap_dev_t* first,
struct blk_snap_dev_t* second)
{
return (first->mj == second->mj) && (first->mn == second->mn);
}
static inline int check_same_devices(struct blk_snap_dev_t *devices,
unsigned int count)
{
struct blk_snap_dev_t *first;
struct blk_snap_dev_t *second;
for (first = devices; first < (devices + (count - 1)); ++first) {
for (second = first + 1; second < (devices + count); ++second) {
if (blk_snap_dev_is_equal(first, second)) {
pr_err("Unable to create snapshot: The same device [%d:%d] was added twice.\n",
first->mj, first->mn);
return -EINVAL;
}
}
}
return 0;
}
int snapshot_create(struct blk_snap_dev_t *dev_id_array, unsigned int count,
uuid_t *id)
{
struct snapshot *snapshot = NULL;
int ret;
unsigned int inx;
pr_info("Create snapshot for devices:\n");
for (inx = 0; inx < count; ++inx)
pr_info("\t%u:%u\n", dev_id_array[inx].mj,
dev_id_array[inx].mn);
ret = check_same_devices(dev_id_array, count);
if (ret)
return ret;
snapshot = snapshot_new(count);
if (IS_ERR(snapshot)) {
pr_err("Unable to create snapshot: failed to allocate snapshot structure\n");
return PTR_ERR(snapshot);
}
ret = -ENODEV;
for (inx = 0; inx < count; ++inx) {
dev_t dev_id =
MKDEV(dev_id_array[inx].mj, dev_id_array[inx].mn);
struct tracker *tracker;
tracker = tracker_create_or_get(dev_id, &snapshot->id);
if (IS_ERR(tracker)) {
pr_err("Failed to add device [%u:%u] to snapshot tracking\n",
MAJOR(dev_id), MINOR(dev_id));
ret = PTR_ERR(tracker);
goto fail;
}
snapshot->tracker_array[inx] = tracker;
snapshot->count++;
}
down_write(&snapshots_lock);
list_add_tail(&snapshots, &snapshot->link);
up_write(&snapshots_lock);
uuid_copy(id, &snapshot->id);
pr_info("Snapshot %pUb was created\n", &snapshot->id);
return 0;
fail:
pr_err("Snapshot cannot be created\n");
snapshot_put(snapshot);
return ret;
}
static struct snapshot *snapshot_get_by_id(uuid_t *id)
{
struct snapshot *snapshot = NULL;
struct snapshot *s;
down_read(&snapshots_lock);
if (list_empty(&snapshots))
goto out;
list_for_each_entry(s, &snapshots, link) {
if (uuid_equal(&s->id, id)) {
snapshot = s;
snapshot_get(snapshot);
break;
}
}
out:
up_read(&snapshots_lock);
return snapshot;
}
int snapshot_destroy(uuid_t *id)
{
struct snapshot *snapshot = NULL;
pr_info("Destroy snapshot %pUb\n", id);
down_write(&snapshots_lock);
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("DEBUG! %s try to find snapshot\n", __FUNCTION__);
#endif
if (!list_empty(&snapshots)) {
struct snapshot *s = NULL;
list_for_each_entry(s, &snapshots, link) {
if (uuid_equal(&s->id, id)) {
snapshot = s;
list_del(&snapshot->link);
break;
}
}
}
up_write(&snapshots_lock);
if (!snapshot) {
pr_err("Unable to destroy snapshot: cannot find snapshot by id %pUb\n",
id);
return -ENODEV;
}
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("DEBUG! %s snapshot was found and should be released\n",
__FUNCTION__);
#endif
snapshot_put(snapshot);
#ifdef CONFIG_BLK_SNAP_DEBUG_MEMORY_LEAK
pr_debug("blksnap memory consumption:\n");
memory_object_print(false);
memory_object_max_print();
#endif
return 0;
}
int snapshot_append_storage(uuid_t *id, struct blk_snap_dev_t dev_id,
struct big_buffer *ranges, unsigned int range_count)
{
int ret = 0;
struct snapshot *snapshot;
snapshot = snapshot_get_by_id(id);
if (!snapshot)
return -ESRCH;
ret = diff_storage_append_block(snapshot->diff_storage,
MKDEV(dev_id.mj, dev_id.mn), ranges,
range_count);
snapshot_put(snapshot);
return ret;
}
#if defined(BLK_SNAP_SEQUENTALFREEZE)
/**
* snapshot_take_trackers - Take tracker for snapshot
*
* The sequential algorithm allows to freeze block devices one at a time.
*/
static int snapshot_take_trackers(struct snapshot *snapshot)
{
int ret = 0;
int inx;
unsigned int current_flag;
/* Try to flush and freeze file system on each original block device. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Try to flush and freeze file system on each original block device\n");
#endif
for (inx = 0; inx < snapshot->count; inx++) {
struct tracker *tracker = snapshot->tracker_array[inx];
#if defined(HAVE_SUPER_BLOCK_FREEZE)
struct super_block *sb;
#else
bool is_frozen = false;
#endif
struct block_device *orig_bdev;
if (!tracker)
continue;
orig_bdev = tracker->diff_area->orig_bdev;
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_freeze_bdev(orig_bdev, &sb);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_freeze(orig_bdev))
#else
if (freeze_bdev(orig_bdev))
#endif
pr_err("Failed to freeze device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else {
is_frozen = true;
pr_debug("Device [%u:%u] was frozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
}
#endif
current_flag = memalloc_noio_save();
tracker_lock();
/*
* Take snapshot - switch CBT tables and enable COW logic
* for each tracker.
*/
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Switch CBT tables and enable COW logic for each tracker\n");
#endif
ret = tracker_take_snapshot(tracker);
if (ret) {
pr_err("Unable to take snapshot: failed to capture snapshot %pUb\n",
&snapshot->id);
break;
}
tracker_unlock();
memalloc_noio_restore(current_flag);
/* Thaw file systems on original block devices. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Thaw file systems on original block devices\n");
#endif
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_thaw_bdev(orig_bdev, sb);
#else
if (!is_frozen)
continue;
#if defined(HAVE_BDEV_FREEZE)
if (bdev_thaw(orig_bdev))
#else
if (thaw_bdev(orig_bdev))
#endif
pr_err("Failed to thaw device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was unfrozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
}
if (!ret) {
snapshot->is_taken = true;
return 0;
}
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Release taked trackers\n");
#endif
while (inx--) {
struct tracker *tracker = snapshot->tracker_array[inx];
#if defined(HAVE_SUPER_BLOCK_FREEZE)
struct super_block *sb;
#endif
if (!tracker)
continue;
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_freeze_bdev(tracker->diff_area->orig_bdev, &sb);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_freeze(tracker->diff_area->orig_bdev))
#else
if (freeze_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to freeze device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was frozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
tracker_lock();
tracker_release_snapshot(tracker);
tracker_unlock();
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_thaw_bdev(tracker->diff_area->orig_bdev, sb);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_thaw(tracker->diff_area->orig_bdev))
#else
if (thaw_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to thaw device [%u:%u]\n",
MAJOR(tracker->dev_id),
MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was unfrozen\n",
MAJOR(tracker->dev_id),
MINOR(tracker->dev_id));
#endif
}
return ret;
}
#else /* BLK_SNAP_SEQUENTALFREEZE */
/**
* snapshot_take_trackers - Take tracker for snapshot
*
* The simultaneous algorithm allows to freeze all the snapshot block devices.
*/
static int snapshot_take_trackers(struct snapshot *snapshot)
{
int ret = 0;
int inx;
unsigned int current_flag;
/* Try to flush and freeze file system on each original block device. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Try to flush and freeze file system on each original block device\n");
#endif
for (inx = 0; inx < snapshot->count; inx++) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (!tracker)
continue;
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_freeze_bdev(tracker->diff_area->orig_bdev,
&snapshot->superblock_array[inx]);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_freeze(tracker->diff_area->orig_bdev))
#else
if (freeze_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to freeze device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was frozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
}
current_flag = memalloc_noio_save();
tracker_lock();
/*
* Take snapshot - switch CBT tables and enable COW logic
* for each tracker.
*/
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Switch CBT tables and enable COW logic for each tracker\n");
#endif
for (inx = 0; inx < snapshot->count; inx++) {
if (!snapshot->tracker_array[inx])
continue;
ret = tracker_take_snapshot(snapshot->tracker_array[inx]);
if (ret) {
pr_err("Unable to take snapshot: failed to capture snapshot %pUb\n",
&snapshot->id);
break;
}
}
if (ret) {
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Release taked snapshots\n");
#endif
while (inx--) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (tracker)
tracker_release_snapshot(tracker);
}
} else
snapshot->is_taken = true;
tracker_unlock();
memalloc_noio_restore(current_flag);
/* Thaw file systems on original block devices. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("Thaw file systems on original block devices\n");
#endif
for (inx = 0; inx < snapshot->count; inx++) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (!tracker)
continue;
#if defined(HAVE_SUPER_BLOCK_FREEZE)
_thaw_bdev(tracker->diff_area->orig_bdev,
snapshot->superblock_array[inx]);
#else
#if defined(HAVE_BDEV_FREEZE)
if (bdev_thaw(tracker->diff_area->orig_bdev))
#else
if (thaw_bdev(tracker->diff_area->orig_bdev))
#endif
pr_err("Failed to thaw device [%u:%u]\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
else
pr_debug("Device [%u:%u] was unfrozen\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
#endif
}
return ret;
}
#endif /* BLK_SNAP_SEQUENTALFREEZE */
int snapshot_take(uuid_t *id)
{
int ret = 0;
struct snapshot *snapshot;
int inx;
snapshot = snapshot_get_by_id(id);
if (!snapshot)
return -ESRCH;
if (snapshot->is_taken) {
ret = -EALREADY;
goto out;
}
if (!snapshot->count) {
ret = -ENODEV;
goto out;
}
/* Allocate diff area for each device in the snapshot. */
for (inx = 0; inx < snapshot->count; inx++) {
struct tracker *tracker = snapshot->tracker_array[inx];
struct diff_area *diff_area;
if (!tracker)
continue;
diff_area =
diff_area_new(tracker->dev_id, snapshot->diff_storage);
if (IS_ERR(diff_area)) {
ret = PTR_ERR(diff_area);
goto fail;
}
tracker->diff_area = diff_area;
}
ret = snapshot_take_trackers(snapshot);
if (ret)
goto fail;
pr_info("Snapshot was taken successfully\n");
/**
* Sometimes a snapshot is in the state of corrupt immediately
* after it is taken.
*/
for (inx = 0; inx < snapshot->count; inx++) {
struct tracker *tracker = snapshot->tracker_array[inx];
if (!tracker)
continue;
if (diff_area_is_corrupted(tracker->diff_area)) {
pr_err("Unable to freeze devices [%u:%u]: diff area is corrupted\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id));
ret = -EFAULT;
goto fail;
}
}
/* Create all image block devices. */
#ifdef BLK_SNAP_DEBUG_RELEASE_SNAPSHOT
pr_debug("DEBUG! %s - create all image block device", __FUNCTION__);
#endif
for (inx = 0; inx < snapshot->count; inx++) {
struct snapimage *snapimage;
struct tracker *tracker = snapshot->tracker_array[inx];
snapimage =
snapimage_create(tracker->diff_area, tracker->cbt_map);
if (IS_ERR(snapimage)) {
ret = PTR_ERR(snapimage);
pr_err("Failed to create snapshot image for device [%u:%u] with error=%d\n",
MAJOR(tracker->dev_id), MINOR(tracker->dev_id),
ret);
break;
}
snapshot->snapimage_array[inx] = snapimage;
}
goto out;
fail:
pr_err("Unable to take snapshot: failed to capture snapshot %pUb\n",
&snapshot->id);
down_write(&snapshots_lock);
list_del(&snapshot->link);
up_write(&snapshots_lock);
snapshot_put(snapshot);
out:
snapshot_put(snapshot);
return ret;
}
struct event *snapshot_wait_event(uuid_t *id, unsigned long timeout_ms)
{
struct snapshot *snapshot;
struct event *event;
snapshot = snapshot_get_by_id(id);
if (!snapshot)
return ERR_PTR(-ESRCH);
event = event_wait(&snapshot->diff_storage->event_queue, timeout_ms);
snapshot_put(snapshot);
return event;
}
static inline int uuid_copy_to_user(uuid_t __user *dst, const uuid_t *src)
{
int len;
len = copy_to_user(dst, src, sizeof(uuid_t));
if (len)
return -ENODATA;
return 0;
}
int snapshot_collect(unsigned int *pcount, uuid_t __user *id_array)
{
int ret = 0;
int inx = 0;
struct snapshot *s;
pr_debug("Collect snapshots\n");
down_read(&snapshots_lock);
if (list_empty(&snapshots))
goto out;
if (!id_array) {
list_for_each_entry(s, &snapshots, link)
inx++;
goto out;
}
list_for_each_entry(s, &snapshots, link) {
if (inx >= *pcount) {
ret = -ENODATA;
goto out;
}
ret = uuid_copy_to_user(&id_array[inx], &s->id);
if (ret) {
pr_err("Unable to collect snapshots: failed to copy data to user buffer\n");
goto out;
}
inx++;
}
out:
up_read(&snapshots_lock);
*pcount = inx;
return ret;
}
int snapshot_collect_images(
uuid_t *id, struct blk_snap_image_info __user *user_image_info_array,
unsigned int *pcount)
{
int ret = 0;
int inx;
unsigned long len;
struct blk_snap_image_info *image_info_array = NULL;
struct snapshot *snapshot;
pr_debug("Collect images for snapshots\n");
snapshot = snapshot_get_by_id(id);
if (!snapshot)
return -ESRCH;
if (!snapshot->is_taken) {
ret = -ENODEV;
goto out;
}
pr_debug("Found snapshot with %d devices\n", snapshot->count);
if (!user_image_info_array) {
pr_debug(
"Unable to collect snapshot images: users buffer is not set\n");
goto out;
}
if (*pcount < snapshot->count) {
ret = -ENODATA;
goto out;
}
image_info_array =
kcalloc(snapshot->count, sizeof(struct blk_snap_image_info),
GFP_KERNEL);
if (!image_info_array) {
pr_err("Unable to collect snapshot images: not enough memory.\n");
ret = -ENOMEM;
goto out;
}
memory_object_inc(memory_object_blk_snap_image_info);
for (inx = 0; inx < snapshot->count; inx++) {
if (snapshot->tracker_array[inx]) {
dev_t orig_dev_id =
snapshot->tracker_array[inx]->dev_id;
pr_debug("Original [%u:%u]\n",
MAJOR(orig_dev_id),
MINOR(orig_dev_id));
image_info_array[inx].orig_dev_id.mj =
MAJOR(orig_dev_id);
image_info_array[inx].orig_dev_id.mn =
MINOR(orig_dev_id);
}
if (snapshot->snapimage_array[inx]) {
dev_t image_dev_id =
snapshot->snapimage_array[inx]->image_dev_id;
pr_debug("Image [%u:%u]\n",
MAJOR(image_dev_id),
MINOR(image_dev_id));
image_info_array[inx].image_dev_id.mj =
MAJOR(image_dev_id);
image_info_array[inx].image_dev_id.mn =
MINOR(image_dev_id);
}
}
len = copy_to_user(user_image_info_array, image_info_array,
snapshot->count *
sizeof(struct blk_snap_image_info));
if (len != 0) {
pr_err("Unable to collect snapshot images: failed to copy data to user buffer\n");
ret = -ENODATA;
}
out:
*pcount = snapshot->count;
kfree(image_info_array);
if (image_info_array)
memory_object_dec(memory_object_blk_snap_image_info);
snapshot_put(snapshot);
return ret;
}
int snapshot_mark_dirty_blocks(dev_t image_dev_id,
struct blk_snap_block_range *block_ranges,
unsigned int count)
{
int ret = 0;
int inx = 0;
struct snapshot *s;
struct cbt_map *cbt_map = NULL;
pr_debug("Marking [%d] dirty blocks for device [%u:%u]\n", count,
MAJOR(image_dev_id), MINOR(image_dev_id));
down_read(&snapshots_lock);
if (list_empty(&snapshots))
goto out;
list_for_each_entry(s, &snapshots, link) {
for (inx = 0; inx < s->count; inx++) {
if (s->snapimage_array[inx]->image_dev_id ==
image_dev_id) {
cbt_map = s->snapimage_array[inx]->cbt_map;
break;
}
}
inx++;
}
if (!cbt_map) {
pr_err("Cannot find snapshot image device [%u:%u]\n",
MAJOR(image_dev_id), MINOR(image_dev_id));
ret = -ENODEV;
goto out;
}
ret = cbt_map_mark_dirty_blocks(cbt_map, block_ranges, count);
if (ret)
pr_err("Failed to set CBT table. errno=%d\n", abs(ret));
out:
up_read(&snapshots_lock);
return ret;
}
#ifdef BLK_SNAP_DEBUG_SECTOR_STATE
int snapshot_get_chunk_state(dev_t image_dev_id, sector_t sector,
struct blk_snap_sector_state *state)
{
int ret = 0;
int inx = 0;
struct snapshot *s;
struct snapimage *image = NULL;
down_read(&snapshots_lock);
if (list_empty(&snapshots))
goto out;
list_for_each_entry (s, &snapshots, link) {
for (inx = 0; inx < s->count; inx++) {
if (s->snapimage_array[inx]->image_dev_id ==
image_dev_id) {
image = s->snapimage_array[inx];
break;
}
}
inx++;
}
if (!image) {
pr_err("Cannot find snapshot image device [%u:%u]\n",
MAJOR(image_dev_id), MINOR(image_dev_id));
ret = -ENODEV;
goto out;
}
ret = snapimage_get_chunk_state(image, sector, state);
if (ret)
pr_err("Failed to get chunk state. errno=%d\n", abs(ret));
out:
up_read(&snapshots_lock);
return ret;
}
#endif