162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
462306a36Sopenharmony_ci * Copyright (C) 2006-2008 Red Hat GmbH
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is released under the GPL.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "dm-exception-store.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/pagemap.h>
1362306a36Sopenharmony_ci#include <linux/vmalloc.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/dm-io.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define DM_MSG_PREFIX "transient snapshot"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci *---------------------------------------------------------------
2262306a36Sopenharmony_ci * Implementation of the store for non-persistent snapshots.
2362306a36Sopenharmony_ci *---------------------------------------------------------------
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistruct transient_c {
2662306a36Sopenharmony_ci	sector_t next_free;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void transient_dtr(struct dm_exception_store *store)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	kfree(store->context);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int transient_read_metadata(struct dm_exception_store *store,
3562306a36Sopenharmony_ci				   int (*callback)(void *callback_context,
3662306a36Sopenharmony_ci						   chunk_t old, chunk_t new),
3762306a36Sopenharmony_ci				   void *callback_context)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int transient_prepare_exception(struct dm_exception_store *store,
4362306a36Sopenharmony_ci				       struct dm_exception *e)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct transient_c *tc = store->context;
4662306a36Sopenharmony_ci	sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (size < (tc->next_free + store->chunk_size))
4962306a36Sopenharmony_ci		return -1;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	e->new_chunk = sector_to_chunk(store, tc->next_free);
5262306a36Sopenharmony_ci	tc->next_free += store->chunk_size;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void transient_commit_exception(struct dm_exception_store *store,
5862306a36Sopenharmony_ci				       struct dm_exception *e, int valid,
5962306a36Sopenharmony_ci				       void (*callback)(void *, int success),
6062306a36Sopenharmony_ci				       void *callback_context)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	/* Just succeed */
6362306a36Sopenharmony_ci	callback(callback_context, valid);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void transient_usage(struct dm_exception_store *store,
6762306a36Sopenharmony_ci			    sector_t *total_sectors,
6862306a36Sopenharmony_ci			    sector_t *sectors_allocated,
6962306a36Sopenharmony_ci			    sector_t *metadata_sectors)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	*sectors_allocated = ((struct transient_c *) store->context)->next_free;
7262306a36Sopenharmony_ci	*total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
7362306a36Sopenharmony_ci	*metadata_sectors = 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int transient_ctr(struct dm_exception_store *store, char *options)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct transient_c *tc;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
8162306a36Sopenharmony_ci	if (!tc)
8262306a36Sopenharmony_ci		return -ENOMEM;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	tc->next_free = 0;
8562306a36Sopenharmony_ci	store->context = tc;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic unsigned int transient_status(struct dm_exception_store *store,
9162306a36Sopenharmony_ci				 status_type_t status, char *result,
9262306a36Sopenharmony_ci				 unsigned int maxlen)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	unsigned int sz = 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	switch (status) {
9762306a36Sopenharmony_ci	case STATUSTYPE_INFO:
9862306a36Sopenharmony_ci		break;
9962306a36Sopenharmony_ci	case STATUSTYPE_TABLE:
10062306a36Sopenharmony_ci		DMEMIT(" N %llu", (unsigned long long)store->chunk_size);
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	case STATUSTYPE_IMA:
10362306a36Sopenharmony_ci		*result = '\0';
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return sz;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic struct dm_exception_store_type _transient_type = {
11162306a36Sopenharmony_ci	.name = "transient",
11262306a36Sopenharmony_ci	.module = THIS_MODULE,
11362306a36Sopenharmony_ci	.ctr = transient_ctr,
11462306a36Sopenharmony_ci	.dtr = transient_dtr,
11562306a36Sopenharmony_ci	.read_metadata = transient_read_metadata,
11662306a36Sopenharmony_ci	.prepare_exception = transient_prepare_exception,
11762306a36Sopenharmony_ci	.commit_exception = transient_commit_exception,
11862306a36Sopenharmony_ci	.usage = transient_usage,
11962306a36Sopenharmony_ci	.status = transient_status,
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct dm_exception_store_type _transient_compat_type = {
12362306a36Sopenharmony_ci	.name = "N",
12462306a36Sopenharmony_ci	.module = THIS_MODULE,
12562306a36Sopenharmony_ci	.ctr = transient_ctr,
12662306a36Sopenharmony_ci	.dtr = transient_dtr,
12762306a36Sopenharmony_ci	.read_metadata = transient_read_metadata,
12862306a36Sopenharmony_ci	.prepare_exception = transient_prepare_exception,
12962306a36Sopenharmony_ci	.commit_exception = transient_commit_exception,
13062306a36Sopenharmony_ci	.usage = transient_usage,
13162306a36Sopenharmony_ci	.status = transient_status,
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciint dm_transient_snapshot_init(void)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	int r;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	r = dm_exception_store_type_register(&_transient_type);
13962306a36Sopenharmony_ci	if (r) {
14062306a36Sopenharmony_ci		DMWARN("Unable to register transient exception store type");
14162306a36Sopenharmony_ci		return r;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	r = dm_exception_store_type_register(&_transient_compat_type);
14562306a36Sopenharmony_ci	if (r) {
14662306a36Sopenharmony_ci		DMWARN("Unable to register old-style transient exception store type");
14762306a36Sopenharmony_ci		dm_exception_store_type_unregister(&_transient_type);
14862306a36Sopenharmony_ci		return r;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return r;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_civoid dm_transient_snapshot_exit(void)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	dm_exception_store_type_unregister(&_transient_type);
15762306a36Sopenharmony_ci	dm_exception_store_type_unregister(&_transient_compat_type);
15862306a36Sopenharmony_ci}
159