diff options
Diffstat (limited to 'drivers/md/dm-log.c')
-rw-r--r-- | drivers/md/dm-log.c | 254 |
1 files changed, 164 insertions, 90 deletions
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 2a74b2142f50..67a6f31b7fc3 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2003 Sistina Software + * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. * * This file is released under the LGPL. */ @@ -8,64 +9,58 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/vmalloc.h> +#include <linux/dm-io.h> +#include <linux/dm-dirty-log.h> -#include "dm-log.h" -#include "dm-io.h" +#include "dm.h" -#define DM_MSG_PREFIX "mirror log" +#define DM_MSG_PREFIX "dirty region log" -static LIST_HEAD(_log_types); -static DEFINE_SPINLOCK(_lock); +struct dm_dirty_log_internal { + struct dm_dirty_log_type *type; -int dm_register_dirty_log_type(struct dirty_log_type *type) -{ - spin_lock(&_lock); - type->use_count = 0; - list_add(&type->list, &_log_types); - spin_unlock(&_lock); + struct list_head list; + long use; +}; - return 0; -} +static LIST_HEAD(_log_types); +static DEFINE_SPINLOCK(_lock); -int dm_unregister_dirty_log_type(struct dirty_log_type *type) +static struct dm_dirty_log_internal *__find_dirty_log_type(const char *name) { - spin_lock(&_lock); - - if (type->use_count) - DMWARN("Attempt to unregister a log type that is still in use"); - else - list_del(&type->list); + struct dm_dirty_log_internal *log_type; - spin_unlock(&_lock); + list_for_each_entry(log_type, &_log_types, list) + if (!strcmp(name, log_type->type->name)) + return log_type; - return 0; + return NULL; } -static struct dirty_log_type *_get_type(const char *type_name) +static struct dm_dirty_log_internal *_get_dirty_log_type(const char *name) { - struct dirty_log_type *type; + struct dm_dirty_log_internal *log_type; spin_lock(&_lock); - list_for_each_entry (type, &_log_types, list) - if (!strcmp(type_name, type->name)) { - if (!type->use_count && !try_module_get(type->module)){ - spin_unlock(&_lock); - return NULL; - } - type->use_count++; - spin_unlock(&_lock); - return type; - } + + log_type = __find_dirty_log_type(name); + if (log_type) { + if (!log_type->use && !try_module_get(log_type->type->module)) + log_type = NULL; + else + log_type->use++; + } spin_unlock(&_lock); - return NULL; + + return log_type; } /* * get_type * @type_name * - * Attempt to retrieve the dirty_log_type by name. If not already + * Attempt to retrieve the dm_dirty_log_type by name. If not already * available, attempt to load the appropriate module. * * Log modules are named "dm-log-" followed by the 'type_name'. @@ -78,14 +73,17 @@ static struct dirty_log_type *_get_type(const char *type_name) * * Returns: dirty_log_type* on success, NULL on failure */ -static struct dirty_log_type *get_type(const char *type_name) +static struct dm_dirty_log_type *get_type(const char *type_name) { char *p, *type_name_dup; - struct dirty_log_type *type; + struct dm_dirty_log_internal *log_type; + + if (!type_name) + return NULL; - type = _get_type(type_name); - if (type) - return type; + log_type = _get_dirty_log_type(type_name); + if (log_type) + return log_type->type; type_name_dup = kstrdup(type_name, GFP_KERNEL); if (!type_name_dup) { @@ -95,34 +93,106 @@ static struct dirty_log_type *get_type(const char *type_name) } while (request_module("dm-log-%s", type_name_dup) || - !(type = _get_type(type_name))) { + !(log_type = _get_dirty_log_type(type_name))) { p = strrchr(type_name_dup, '-'); if (!p) break; p[0] = '\0'; } - if (!type) + if (!log_type) DMWARN("Module for logging type \"%s\" not found.", type_name); kfree(type_name_dup); - return type; + return log_type ? log_type->type : NULL; } -static void put_type(struct dirty_log_type *type) +static void put_type(struct dm_dirty_log_type *type) { + struct dm_dirty_log_internal *log_type; + + if (!type) + return; + spin_lock(&_lock); - if (!--type->use_count) + log_type = __find_dirty_log_type(type->name); + if (!log_type) + goto out; + + if (!--log_type->use) module_put(type->module); + + BUG_ON(log_type->use < 0); + +out: spin_unlock(&_lock); } -struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *ti, - unsigned int argc, char **argv) +static struct dm_dirty_log_internal *_alloc_dirty_log_type(struct dm_dirty_log_type *type) { - struct dirty_log_type *type; - struct dirty_log *log; + struct dm_dirty_log_internal *log_type = kzalloc(sizeof(*log_type), + GFP_KERNEL); + + if (log_type) + log_type->type = type; + + return log_type; +} + +int dm_dirty_log_type_register(struct dm_dirty_log_type *type) +{ + struct dm_dirty_log_internal *log_type = _alloc_dirty_log_type(type); + int r = 0; + + if (!log_type) + return -ENOMEM; + + spin_lock(&_lock); + if (!__find_dirty_log_type(type->name)) + list_add(&log_type->list, &_log_types); + else { + kfree(log_type); + r = -EEXIST; + } + spin_unlock(&_lock); + + return r; +} +EXPORT_SYMBOL(dm_dirty_log_type_register); + +int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type) +{ + struct dm_dirty_log_internal *log_type; + + spin_lock(&_lock); + + log_type = __find_dirty_log_type(type->name); + if (!log_type) { + spin_unlock(&_lock); + return -EINVAL; + } + + if (log_type->use) { + spin_unlock(&_lock); + return -ETXTBSY; + } + + list_del(&log_type->list); + + spin_unlock(&_lock); + kfree(log_type); + + return 0; +} +EXPORT_SYMBOL(dm_dirty_log_type_unregister); + +struct dm_dirty_log *dm_dirty_log_create(const char *type_name, + struct dm_target *ti, + unsigned int argc, char **argv) +{ + struct dm_dirty_log_type *type; + struct dm_dirty_log *log; log = kmalloc(sizeof(*log), GFP_KERNEL); if (!log) @@ -143,13 +213,15 @@ struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *t return log; } +EXPORT_SYMBOL(dm_dirty_log_create); -void dm_destroy_dirty_log(struct dirty_log *log) +void dm_dirty_log_destroy(struct dm_dirty_log *log) { log->type->dtr(log); put_type(log->type); kfree(log); } +EXPORT_SYMBOL(dm_dirty_log_destroy); /*----------------------------------------------------------------- * Persistent and core logs share a lot of their implementation. @@ -207,7 +279,7 @@ struct log_c { struct dm_dev *log_dev; struct log_header header; - struct io_region header_location; + struct dm_io_region header_location; struct log_header *disk_header; }; @@ -215,7 +287,7 @@ struct log_c { * The touched member needs to be updated every time we access * one of the bitsets. */ -static inline int log_test_bit(uint32_t *bs, unsigned bit) +static inline int log_test_bit(uint32_t *bs, unsigned bit) { return ext2_test_bit(bit, (unsigned long *) bs) ? 1 : 0; } @@ -302,7 +374,7 @@ static inline int write_header(struct log_c *log) * argv contains region_size followed optionally by [no]sync *--------------------------------------------------------------*/ #define BYTE_SHIFT 3 -static int create_log_context(struct dirty_log *log, struct dm_target *ti, +static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv, struct dm_dev *dev) { @@ -315,7 +387,7 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti, int r; if (argc < 1 || argc > 2) { - DMWARN("wrong number of arguments to mirror log"); + DMWARN("wrong number of arguments to dirty region log"); return -EINVAL; } @@ -325,8 +397,8 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti, else if (!strcmp(argv[1], "nosync")) sync = NOSYNC; else { - DMWARN("unrecognised sync argument to mirror log: %s", - argv[1]); + DMWARN("unrecognised sync argument to " + "dirty region log: %s", argv[1]); return -EINVAL; } } @@ -434,7 +506,7 @@ static int create_log_context(struct dirty_log *log, struct dm_target *ti, return 0; } -static int core_ctr(struct dirty_log *log, struct dm_target *ti, +static int core_ctr(struct dm_dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { return create_log_context(log, ti, argc, argv, NULL); @@ -447,7 +519,7 @@ static void destroy_log_context(struct log_c *lc) kfree(lc); } -static void core_dtr(struct dirty_log *log) +static void core_dtr(struct dm_dirty_log *log) { struct log_c *lc = (struct log_c *) log->context; @@ -460,14 +532,14 @@ static void core_dtr(struct dirty_log *log) * * argv contains log_device region_size followed optionally by [no]sync *--------------------------------------------------------------*/ -static int disk_ctr(struct dirty_log *log, struct dm_target *ti, +static int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { int r; struct dm_dev *dev; if (argc < 2 || argc > 3) { - DMWARN("wrong number of arguments to disk mirror log"); + DMWARN("wrong number of arguments to disk dirty region log"); return -EINVAL; } @@ -485,7 +557,7 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, return 0; } -static void disk_dtr(struct dirty_log *log) +static void disk_dtr(struct dm_dirty_log *log) { struct log_c *lc = (struct log_c *) log->context; @@ -514,7 +586,7 @@ static void fail_log_device(struct log_c *lc) dm_table_event(lc->ti->table); } -static int disk_resume(struct dirty_log *log) +static int disk_resume(struct dm_dirty_log *log) { int r; unsigned i; @@ -524,7 +596,7 @@ static int disk_resume(struct dirty_log *log) /* read the disk header */ r = read_header(lc); if (r) { - DMWARN("%s: Failed to read header on mirror log device", + DMWARN("%s: Failed to read header on dirty region log device", lc->log_dev->name); fail_log_device(lc); /* @@ -562,7 +634,7 @@ static int disk_resume(struct dirty_log *log) /* write the new header */ r = write_header(lc); if (r) { - DMWARN("%s: Failed to write header on mirror log device", + DMWARN("%s: Failed to write header on dirty region log device", lc->log_dev->name); fail_log_device(lc); } @@ -570,38 +642,38 @@ static int disk_resume(struct dirty_log *log) return r; } -static uint32_t core_get_region_size(struct dirty_log *log) +static uint32_t core_get_region_size(struct dm_dirty_log *log) { struct log_c *lc = (struct log_c *) log->context; return lc->region_size; } -static int core_resume(struct dirty_log *log) +static int core_resume(struct dm_dirty_log *log) { struct log_c *lc = (struct log_c *) log->context; lc->sync_search = 0; return 0; } -static int core_is_clean(struct dirty_log *log, region_t region) +static int core_is_clean(struct dm_dirty_log *log, region_t region) { struct log_c *lc = (struct log_c *) log->context; return log_test_bit(lc->clean_bits, region); } -static int core_in_sync(struct dirty_log *log, region_t region, int block) +static int core_in_sync(struct dm_dirty_log *log, region_t region, int block) { struct log_c *lc = (struct log_c *) log->context; return log_test_bit(lc->sync_bits, region); } -static int core_flush(struct dirty_log *log) +static int core_flush(struct dm_dirty_log *log) { /* no op */ return 0; } -static int disk_flush(struct dirty_log *log) +static int disk_flush(struct dm_dirty_log *log) { int r; struct log_c *lc = (struct log_c *) log->context; @@ -619,19 +691,19 @@ static int disk_flush(struct dirty_log *log) return r; } -static void core_mark_region(struct dirty_log *log, region_t region) +static void core_mark_region(struct dm_dirty_log *log, region_t region) { struct log_c *lc = (struct log_c *) log->context; log_clear_bit(lc, lc->clean_bits, region); } -static void core_clear_region(struct dirty_log *log, region_t region) +static void core_clear_region(struct dm_dirty_log *log, region_t region) { struct log_c *lc = (struct log_c *) log->context; log_set_bit(lc, lc->clean_bits, region); } -static int core_get_resync_work(struct dirty_log *log, region_t *region) +static int core_get_resync_work(struct dm_dirty_log *log, region_t *region) { struct log_c *lc = (struct log_c *) log->context; @@ -654,7 +726,7 @@ static int core_get_resync_work(struct dirty_log *log, region_t *region) return 1; } -static void core_set_region_sync(struct dirty_log *log, region_t region, +static void core_set_region_sync(struct dm_dirty_log *log, region_t region, int in_sync) { struct log_c *lc = (struct log_c *) log->context; @@ -669,7 +741,7 @@ static void core_set_region_sync(struct dirty_log *log, region_t region, } } -static region_t core_get_sync_count(struct dirty_log *log) +static region_t core_get_sync_count(struct dm_dirty_log *log) { struct log_c *lc = (struct log_c *) log->context; @@ -680,7 +752,7 @@ static region_t core_get_sync_count(struct dirty_log *log) if (lc->sync != DEFAULTSYNC) \ DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : "") -static int core_status(struct dirty_log *log, status_type_t status, +static int core_status(struct dm_dirty_log *log, status_type_t status, char *result, unsigned int maxlen) { int sz = 0; @@ -700,7 +772,7 @@ static int core_status(struct dirty_log *log, status_type_t status, return sz; } -static int disk_status(struct dirty_log *log, status_type_t status, +static int disk_status(struct dm_dirty_log *log, status_type_t status, char *result, unsigned int maxlen) { int sz = 0; @@ -722,7 +794,7 @@ static int disk_status(struct dirty_log *log, status_type_t status, return sz; } -static struct dirty_log_type _core_type = { +static struct dm_dirty_log_type _core_type = { .name = "core", .module = THIS_MODULE, .ctr = core_ctr, @@ -740,7 +812,7 @@ static struct dirty_log_type _core_type = { .status = core_status, }; -static struct dirty_log_type _disk_type = { +static struct dm_dirty_log_type _disk_type = { .name = "disk", .module = THIS_MODULE, .ctr = disk_ctr, @@ -763,26 +835,28 @@ int __init dm_dirty_log_init(void) { int r; - r = dm_register_dirty_log_type(&_core_type); + r = dm_dirty_log_type_register(&_core_type); if (r) DMWARN("couldn't register core log"); - r = dm_register_dirty_log_type(&_disk_type); + r = dm_dirty_log_type_register(&_disk_type); if (r) { DMWARN("couldn't register disk type"); - dm_unregister_dirty_log_type(&_core_type); + dm_dirty_log_type_unregister(&_core_type); } return r; } -void dm_dirty_log_exit(void) +void __exit dm_dirty_log_exit(void) { - dm_unregister_dirty_log_type(&_disk_type); - dm_unregister_dirty_log_type(&_core_type); + dm_dirty_log_type_unregister(&_disk_type); + dm_dirty_log_type_unregister(&_core_type); } -EXPORT_SYMBOL(dm_register_dirty_log_type); -EXPORT_SYMBOL(dm_unregister_dirty_log_type); -EXPORT_SYMBOL(dm_create_dirty_log); -EXPORT_SYMBOL(dm_destroy_dirty_log); +module_init(dm_dirty_log_init); +module_exit(dm_dirty_log_exit); + +MODULE_DESCRIPTION(DM_NAME " dirty region log"); +MODULE_AUTHOR("Joe Thornber, Heinz Mauelshagen <dm-devel@redhat.com>"); +MODULE_LICENSE("GPL"); |