980 lines · 22.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2001-2002 Sistina Software (UK) Limited. * Copyright (C) 2006-2008 Red Hat GmbH * * This file is released under the GPL. */ #include "dm-exception-store.h" #include <linux/ctype.h>#include <linux/mm.h>#include <linux/pagemap.h>#include <linux/vmalloc.h>#include <linux/export.h>#include <linux/slab.h>#include <linux/dm-io.h>#include <linux/dm-bufio.h> #define DM_MSG_PREFIX "persistent snapshot"#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32U	/* 16KB */ #define DM_PREFETCH_CHUNKS		12 /* *--------------------------------------------------------------- * Persistent snapshots, by persistent we mean that the snapshot * will survive a reboot. *--------------------------------------------------------------- */ /* * We need to store a record of which parts of the origin have * been copied to the snapshot device.  The snapshot code * requires that we copy exception chunks to chunk aligned areas * of the COW store.  It makes sense therefore, to store the * metadata in chunk size blocks. * * There is no backward or forward compatibility implemented, * snapshots with different disk versions than the kernel will * not be usable.  It is expected that "lvcreate" will blank out * the start of a fresh COW device before calling the snapshot * constructor. * * The first chunk of the COW device just contains the header. * After this there is a chunk filled with exception metadata, * followed by as many exception chunks as can fit in the * metadata areas. * * All on disk structures are in little-endian format.  The end * of the exceptions info is indicated by an exception with a * new_chunk of 0, which is invalid since it would point to the * header chunk. */ /* * Magic for persistent snapshots: "SnAp" - Feeble isn't it. */#define SNAP_MAGIC 0x70416e53 /* * The on-disk version of the metadata. */#define SNAPSHOT_DISK_VERSION 1 #define NUM_SNAPSHOT_HDR_CHUNKS 1 struct disk_header {	__le32 magic; 	/*	 * Is this snapshot valid.  There is no way of recovering	 * an invalid snapshot.	 */	__le32 valid; 	/*	 * Simple, incrementing version. no backward	 * compatibility.	 */	__le32 version; 	/* In sectors */	__le32 chunk_size;} __packed; struct disk_exception {	__le64 old_chunk;	__le64 new_chunk;} __packed; struct core_exception {	uint64_t old_chunk;	uint64_t new_chunk;}; struct commit_callback {	void (*callback)(void *ref, int success);	void *context;}; /* * The top level structure for a persistent exception store. */struct pstore {	struct dm_exception_store *store;	int version;	int valid;	uint32_t exceptions_per_area; 	/*	 * Now that we have an asynchronous kcopyd there is no	 * need for large chunk sizes, so it wont hurt to have a	 * whole chunks worth of metadata in memory at once.	 */	void *area; 	/*	 * An area of zeros used to clear the next area.	 */	void *zero_area; 	/*	 * An area used for header. The header can be written	 * concurrently with metadata (when invalidating the snapshot),	 * so it needs a separate buffer.	 */	void *header_area; 	/*	 * Used to keep track of which metadata area the data in	 * 'chunk' refers to.	 */	chunk_t current_area; 	/*	 * The next free chunk for an exception.	 *	 * When creating exceptions, all the chunks here and above are	 * free.  It holds the next chunk to be allocated.  On rare	 * occasions (e.g. after a system crash) holes can be left in	 * the exception store because chunks can be committed out of	 * order.	 *	 * When merging exceptions, it does not necessarily mean all the	 * chunks here and above are free.  It holds the value it would	 * have held if all chunks had been committed in order of	 * allocation.  Consequently the value may occasionally be	 * slightly too low, but since it's only used for 'status' and	 * it can never reach its minimum value too early this doesn't	 * matter.	 */ 	chunk_t next_free; 	/*	 * The index of next free exception in the current	 * metadata area.	 */	uint32_t current_committed; 	atomic_t pending_count;	uint32_t callback_count;	struct commit_callback *callbacks;	struct dm_io_client *io_client; 	struct workqueue_struct *metadata_wq;}; static int alloc_area(struct pstore *ps){	int r = -ENOMEM;	size_t len; 	len = ps->store->chunk_size << SECTOR_SHIFT; 	/*	 * Allocate the chunk_size block of memory that will hold	 * a single metadata area.	 */	ps->area = vmalloc(len);	if (!ps->area)		goto err_area; 	ps->zero_area = vzalloc(len);	if (!ps->zero_area)		goto err_zero_area; 	ps->header_area = vmalloc(len);	if (!ps->header_area)		goto err_header_area; 	return 0; err_header_area:	vfree(ps->zero_area); err_zero_area:	vfree(ps->area); err_area:	return r;} static void free_area(struct pstore *ps){	vfree(ps->area);	ps->area = NULL;	vfree(ps->zero_area);	ps->zero_area = NULL;	vfree(ps->header_area);	ps->header_area = NULL;} struct mdata_req {	struct dm_io_region *where;	struct dm_io_request *io_req;	struct work_struct work;	int result;}; static void do_metadata(struct work_struct *work){	struct mdata_req *req = container_of(work, struct mdata_req, work); 	req->result = dm_io(req->io_req, 1, req->where, NULL, NULL, IOPRIO_DEFAULT);} /* * Read or write a chunk aligned and sized block of data from a device. */static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, blk_opf_t opf,		    int metadata){	struct dm_io_region where = {		.bdev = dm_snap_cow(ps->store->snap)->bdev,		.sector = ps->store->chunk_size * chunk,		.count = ps->store->chunk_size,	};	struct dm_io_request io_req = {		.bi_opf = opf,		.mem.type = DM_IO_VMA,		.mem.ptr.vma = area,		.client = ps->io_client,		.notify.fn = NULL,	};	struct mdata_req req; 	if (!metadata)		return dm_io(&io_req, 1, &where, NULL, NULL, IOPRIO_DEFAULT); 	req.where = &where;	req.io_req = &io_req; 	/*	 * Issue the synchronous I/O from a different thread	 * to avoid submit_bio_noacct recursion.	 */	INIT_WORK_ONSTACK(&req.work, do_metadata);	queue_work(ps->metadata_wq, &req.work);	flush_workqueue(ps->metadata_wq);	destroy_work_on_stack(&req.work); 	return req.result;} /* * Convert a metadata area index to a chunk index. */static chunk_t area_location(struct pstore *ps, chunk_t area){	return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);} static void skip_metadata(struct pstore *ps){	uint32_t stride = ps->exceptions_per_area + 1;	chunk_t next_free = ps->next_free; 	if (sector_div(next_free, stride) == NUM_SNAPSHOT_HDR_CHUNKS)		ps->next_free++;} /* * Read or write a metadata area.  Remembering to skip the first * chunk which holds the header. */static int area_io(struct pstore *ps, blk_opf_t opf){	chunk_t chunk = area_location(ps, ps->current_area); 	return chunk_io(ps, ps->area, chunk, opf, 0);} static void zero_memory_area(struct pstore *ps){	memset(ps->area, 0, ps->store->chunk_size << SECTOR_SHIFT);} static int zero_disk_area(struct pstore *ps, chunk_t area){	return chunk_io(ps, ps->zero_area, area_location(ps, area),			REQ_OP_WRITE, 0);} static int read_header(struct pstore *ps, int *new_snapshot){	int r;	struct disk_header *dh;	unsigned int chunk_size;	int chunk_size_supplied = 1;	char *chunk_err; 	/*	 * Use default chunk size (or logical_block_size, if larger)	 * if none supplied	 */	if (!ps->store->chunk_size) {		ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,		    bdev_logical_block_size(dm_snap_cow(ps->store->snap)->					    bdev) >> 9);		ps->store->chunk_mask = ps->store->chunk_size - 1;		ps->store->chunk_shift = __ffs(ps->store->chunk_size);		chunk_size_supplied = 0;	} 	ps->io_client = dm_io_client_create();	if (IS_ERR(ps->io_client))		return PTR_ERR(ps->io_client); 	r = alloc_area(ps);	if (r)		return r; 	r = chunk_io(ps, ps->header_area, 0, REQ_OP_READ, 1);	if (r)		goto bad; 	dh = ps->header_area; 	if (le32_to_cpu(dh->magic) == 0) {		*new_snapshot = 1;		return 0;	} 	if (le32_to_cpu(dh->magic) != SNAP_MAGIC) {		DMWARN("Invalid or corrupt snapshot");		r = -ENXIO;		goto bad;	} 	*new_snapshot = 0;	ps->valid = le32_to_cpu(dh->valid);	ps->version = le32_to_cpu(dh->version);	chunk_size = le32_to_cpu(dh->chunk_size); 	if (ps->store->chunk_size == chunk_size)		return 0; 	if (chunk_size_supplied)		DMWARN("chunk size %u in device metadata overrides table chunk size of %u.",		       chunk_size, ps->store->chunk_size); 	/* We had a bogus chunk_size. Fix stuff up. */	free_area(ps); 	r = dm_exception_store_set_chunk_size(ps->store, chunk_size,					      &chunk_err);	if (r) {		DMERR("invalid on-disk chunk size %u: %s.",		      chunk_size, chunk_err);		return r;	} 	r = alloc_area(ps);	return r; bad:	free_area(ps);	return r;} static int write_header(struct pstore *ps){	struct disk_header *dh; 	memset(ps->header_area, 0, ps->store->chunk_size << SECTOR_SHIFT); 	dh = ps->header_area;	dh->magic = cpu_to_le32(SNAP_MAGIC);	dh->valid = cpu_to_le32(ps->valid);	dh->version = cpu_to_le32(ps->version);	dh->chunk_size = cpu_to_le32(ps->store->chunk_size); 	return chunk_io(ps, ps->header_area, 0, REQ_OP_WRITE, 1);} /* * Access functions for the disk exceptions, these do the endian conversions. */static struct disk_exception *get_exception(struct pstore *ps, void *ps_area,					    uint32_t index){	BUG_ON(index >= ps->exceptions_per_area); 	return ((struct disk_exception *) ps_area) + index;} static void read_exception(struct pstore *ps, void *ps_area,			   uint32_t index, struct core_exception *result){	struct disk_exception *de = get_exception(ps, ps_area, index); 	/* copy it */	result->old_chunk = le64_to_cpu(de->old_chunk);	result->new_chunk = le64_to_cpu(de->new_chunk);} static void write_exception(struct pstore *ps,			    uint32_t index, struct core_exception *e){	struct disk_exception *de = get_exception(ps, ps->area, index); 	/* copy it */	de->old_chunk = cpu_to_le64(e->old_chunk);	de->new_chunk = cpu_to_le64(e->new_chunk);} static void clear_exception(struct pstore *ps, uint32_t index){	struct disk_exception *de = get_exception(ps, ps->area, index); 	/* clear it */	de->old_chunk = 0;	de->new_chunk = 0;} /* * Registers the exceptions that are present in the current area. * 'full' is filled in to indicate if the area has been * filled. */static int insert_exceptions(struct pstore *ps, void *ps_area,			     int (*callback)(void *callback_context,					     chunk_t old, chunk_t new),			     void *callback_context,			     int *full){	int r;	unsigned int i;	struct core_exception e; 	/* presume the area is full */	*full = 1; 	for (i = 0; i < ps->exceptions_per_area; i++) {		read_exception(ps, ps_area, i, &e); 		/*		 * If the new_chunk is pointing at the start of		 * the COW device, where the first metadata area		 * is we know that we've hit the end of the		 * exceptions.  Therefore the area is not full.		 */		if (e.new_chunk == 0LL) {			ps->current_committed = i;			*full = 0;			break;		} 		/*		 * Keep track of the start of the free chunks.		 */		if (ps->next_free <= e.new_chunk)			ps->next_free = e.new_chunk + 1; 		/*		 * Otherwise we add the exception to the snapshot.		 */		r = callback(callback_context, e.old_chunk, e.new_chunk);		if (r)			return r;	} 	return 0;} static int read_exceptions(struct pstore *ps,			   int (*callback)(void *callback_context, chunk_t old,					   chunk_t new),			   void *callback_context){	int r, full = 1;	struct dm_bufio_client *client;	chunk_t prefetch_area = 0; 	client = dm_bufio_client_create(dm_snap_cow(ps->store->snap)->bdev,					ps->store->chunk_size << SECTOR_SHIFT,					1, 0, NULL, NULL, 0); 

Lines 1–500 of 980.