| /* |
| * 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, int valid, |
| void (*callback) (void *, int success), |
| void *callback_context) |
| { |
| /* Just succeed */ |
| callback(callback_context, valid); |
| } |
| |
| 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, char *options) |
| { |
| 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); |
| } |