%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /var/lib/dkms/blksnap/6.3.0.73/source/
Upload File :
Create Path :
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

Zerion Mini Shell 1.0