/* * 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/mm.h> #include <linux/pagemap.h> #include <linux/vmalloc.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/dm-io.h> #define DM_MSG_PREFIX "transient snapshot" /*----------------------------------------------------------------- * Implementation of the store for non-persistent snapshots. *---------------------------------------------------------------*/ struct transient_c { sector_t next_free; }; static void transient_dtr(struct dm_exception_store *store) { kfree(store->context); } static int transient_read_metadata(struct dm_exception_store *store, int (*callback)(void *callback_context, chunk_t old, chunk_t new), void *callback_context) { return 0; } static int transient_prepare_exception(struct dm_exception_store *store, struct dm_exception *e) { struct transient_c *tc = store->context; sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev); if (size < (tc->next_free + store->chunk_size)) return -1; e->new_chunk = sector_to_chunk(store, tc->next_free); tc->next_free += store->chunk_size; return 0; } static void transient_commit_exception(struct dm_exception_store *store, struct dm_exception *e, void (*callback) (void *, int success), void *callback_context) { /* Just succeed */ callback(callback_context, 1); } static void transient_usage(struct dm_exception_store *store, sector_t *total_sectors, sector_t *sectors_allocated, sector_t *metadata_sectors) { *sectors_allocated = ((struct transient_c *) store->context)->next_free; *total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev); *metadata_sectors = 0; } static int transient_ctr(struct dm_exception_store *store, unsigned argc, char **argv) { struct transient_c *tc; tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); if (!tc) return -ENOMEM; tc->next_free = 0; store->context = tc; return 0; } static unsigned transient_status(struct dm_exception_store *store, status_type_t status, char *result, unsigned maxlen) { unsigned sz = 0; switch (status) { case STATUSTYPE_INFO: break; case STATUSTYPE_TABLE: DMEMIT(" N %llu", (unsigned long long)store->chunk_size); } return sz; } static struct dm_exception_store_type _transient_type = { .name = "transient", .module = THIS_MODULE, .ctr = transient_ctr, .dtr = transient_dtr, .read_metadata = transient_read_metadata, .prepare_exception = transient_prepare_exception, .commit_exception = transient_commit_exception, .usage = transient_usage, .status = transient_status, }; static struct dm_exception_store_type _transient_compat_type = { .name = "N", .module = THIS_MODULE, .ctr = transient_ctr, .dtr = transient_dtr, .read_metadata = transient_read_metadata, .prepare_exception = transient_prepare_exception, .commit_exception = transient_commit_exception, .usage = transient_usage, .status = transient_status, }; int dm_transient_snapshot_init(void) { int r; r = dm_exception_store_type_register(&_transient_type); if (r) { DMWARN("Unable to register transient exception store type"); return r; } r = dm_exception_store_type_register(&_transient_compat_type); if (r) { DMWARN("Unable to register old-style transient " "exception store type"); dm_exception_store_type_unregister(&_transient_type); return r; } return r; } void dm_transient_snapshot_exit(void) { dm_exception_store_type_unregister(&_transient_type); dm_exception_store_type_unregister(&_transient_compat_type); }