dm exception store: introduce registry
Move exception stores into a registry. Signed-off-by: Jonathan Brassow <jbrassow@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
This commit is contained in:
parent
7513c2a761
commit
493df71c64
6 changed files with 307 additions and 49 deletions
|
@ -14,6 +14,168 @@
|
|||
|
||||
#define DM_MSG_PREFIX "snapshot exception stores"
|
||||
|
||||
static LIST_HEAD(_exception_store_types);
|
||||
static DEFINE_SPINLOCK(_lock);
|
||||
|
||||
static struct dm_exception_store_type *__find_exception_store_type(const char *name)
|
||||
{
|
||||
struct dm_exception_store_type *type;
|
||||
|
||||
list_for_each_entry(type, &_exception_store_types, list)
|
||||
if (!strcmp(name, type->name))
|
||||
return type;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct dm_exception_store_type *_get_exception_store_type(const char *name)
|
||||
{
|
||||
struct dm_exception_store_type *type;
|
||||
|
||||
spin_lock(&_lock);
|
||||
|
||||
type = __find_exception_store_type(name);
|
||||
|
||||
if (type && !try_module_get(type->module))
|
||||
type = NULL;
|
||||
|
||||
spin_unlock(&_lock);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_type
|
||||
* @type_name
|
||||
*
|
||||
* Attempt to retrieve the dm_exception_store_type by name. If not already
|
||||
* available, attempt to load the appropriate module.
|
||||
*
|
||||
* Exstore modules are named "dm-exstore-" followed by the 'type_name'.
|
||||
* Modules may contain multiple types.
|
||||
* This function will first try the module "dm-exstore-<type_name>",
|
||||
* then truncate 'type_name' on the last '-' and try again.
|
||||
*
|
||||
* For example, if type_name was "clustered-shared", it would search
|
||||
* 'dm-exstore-clustered-shared' then 'dm-exstore-clustered'.
|
||||
*
|
||||
* 'dm-exception-store-<type_name>' is too long of a name in my
|
||||
* opinion, which is why I've chosen to have the files
|
||||
* containing exception store implementations be 'dm-exstore-<type_name>'.
|
||||
* If you want your module to be autoloaded, you will follow this
|
||||
* naming convention.
|
||||
*
|
||||
* Returns: dm_exception_store_type* on success, NULL on failure
|
||||
*/
|
||||
static struct dm_exception_store_type *get_type(const char *type_name)
|
||||
{
|
||||
char *p, *type_name_dup;
|
||||
struct dm_exception_store_type *type;
|
||||
|
||||
type = _get_exception_store_type(type_name);
|
||||
if (type)
|
||||
return type;
|
||||
|
||||
type_name_dup = kstrdup(type_name, GFP_KERNEL);
|
||||
if (!type_name_dup) {
|
||||
DMERR("No memory left to attempt load for \"%s\"", type_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (request_module("dm-exstore-%s", type_name_dup) ||
|
||||
!(type = _get_exception_store_type(type_name))) {
|
||||
p = strrchr(type_name_dup, '-');
|
||||
if (!p)
|
||||
break;
|
||||
p[0] = '\0';
|
||||
}
|
||||
|
||||
if (!type)
|
||||
DMWARN("Module for exstore type \"%s\" not found.", type_name);
|
||||
|
||||
kfree(type_name_dup);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static void put_type(struct dm_exception_store_type *type)
|
||||
{
|
||||
spin_lock(&_lock);
|
||||
module_put(type->module);
|
||||
spin_unlock(&_lock);
|
||||
}
|
||||
|
||||
int dm_exception_store_type_register(struct dm_exception_store_type *type)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
spin_lock(&_lock);
|
||||
if (!__find_exception_store_type(type->name))
|
||||
list_add(&type->list, &_exception_store_types);
|
||||
else
|
||||
r = -EEXIST;
|
||||
spin_unlock(&_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(dm_exception_store_type_register);
|
||||
|
||||
int dm_exception_store_type_unregister(struct dm_exception_store_type *type)
|
||||
{
|
||||
spin_lock(&_lock);
|
||||
|
||||
if (!__find_exception_store_type(type->name)) {
|
||||
spin_unlock(&_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_del(&type->list);
|
||||
|
||||
spin_unlock(&_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dm_exception_store_type_unregister);
|
||||
|
||||
int dm_exception_store_create(const char *type_name,
|
||||
struct dm_exception_store **store)
|
||||
{
|
||||
int r = 0;
|
||||
struct dm_exception_store_type *type;
|
||||
struct dm_exception_store *tmp_store;
|
||||
|
||||
tmp_store = kmalloc(sizeof(*tmp_store), GFP_KERNEL);
|
||||
if (!tmp_store)
|
||||
return -ENOMEM;
|
||||
|
||||
type = get_type(type_name);
|
||||
if (!type) {
|
||||
kfree(tmp_store);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tmp_store->type = type;
|
||||
|
||||
r = type->ctr(tmp_store, 0, NULL);
|
||||
if (r) {
|
||||
put_type(type);
|
||||
kfree(tmp_store);
|
||||
return r;
|
||||
}
|
||||
|
||||
*store = tmp_store;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dm_exception_store_create);
|
||||
|
||||
void dm_exception_store_destroy(struct dm_exception_store *store)
|
||||
{
|
||||
store->type->dtr(store);
|
||||
put_type(store->type);
|
||||
kfree(store);
|
||||
}
|
||||
EXPORT_SYMBOL(dm_exception_store_destroy);
|
||||
|
||||
int dm_exception_store_init(void)
|
||||
{
|
||||
int r;
|
||||
|
|
|
@ -39,6 +39,9 @@ struct dm_snap_exception {
|
|||
*/
|
||||
struct dm_exception_store;
|
||||
struct dm_exception_store_type {
|
||||
const char *name;
|
||||
struct module *module;
|
||||
|
||||
int (*ctr) (struct dm_exception_store *store,
|
||||
unsigned argc, char **argv);
|
||||
|
||||
|
@ -85,10 +88,13 @@ struct dm_exception_store_type {
|
|||
void (*fraction_full) (struct dm_exception_store *store,
|
||||
sector_t *numerator,
|
||||
sector_t *denominator);
|
||||
|
||||
/* For internal device-mapper use only. */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct dm_exception_store {
|
||||
struct dm_exception_store_type type;
|
||||
struct dm_exception_store_type *type;
|
||||
|
||||
struct dm_snapshot *snap;
|
||||
|
||||
|
@ -138,6 +144,13 @@ static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
|
|||
|
||||
# endif
|
||||
|
||||
int dm_exception_store_type_register(struct dm_exception_store_type *type);
|
||||
int dm_exception_store_type_unregister(struct dm_exception_store_type *type);
|
||||
|
||||
int dm_exception_store_create(const char *type_name,
|
||||
struct dm_exception_store **store);
|
||||
void dm_exception_store_destroy(struct dm_exception_store *store);
|
||||
|
||||
int dm_exception_store_init(void);
|
||||
void dm_exception_store_exit(void);
|
||||
|
||||
|
@ -150,8 +163,4 @@ void dm_persistent_snapshot_exit(void);
|
|||
int dm_transient_snapshot_init(void);
|
||||
void dm_transient_snapshot_exit(void);
|
||||
|
||||
int dm_create_persistent(struct dm_exception_store *store);
|
||||
|
||||
int dm_create_transient(struct dm_exception_store *store);
|
||||
|
||||
#endif /* _LINUX_DM_EXCEPTION_STORE */
|
||||
|
|
|
@ -478,7 +478,7 @@ static void persistent_fraction_full(struct dm_exception_store *store,
|
|||
*denominator = get_dev_size(store->snap->cow->bdev);
|
||||
}
|
||||
|
||||
static void persistent_destroy(struct dm_exception_store *store)
|
||||
static void persistent_dtr(struct dm_exception_store *store)
|
||||
{
|
||||
struct pstore *ps = get_info(store);
|
||||
|
||||
|
@ -656,7 +656,8 @@ static void persistent_drop_snapshot(struct dm_exception_store *store)
|
|||
DMWARN("write header failed");
|
||||
}
|
||||
|
||||
int dm_create_persistent(struct dm_exception_store *store)
|
||||
static int persistent_ctr(struct dm_exception_store *store,
|
||||
unsigned argc, char **argv)
|
||||
{
|
||||
struct pstore *ps;
|
||||
|
||||
|
@ -683,23 +684,69 @@ int dm_create_persistent(struct dm_exception_store *store)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
store->type.dtr = persistent_destroy;
|
||||
store->type.read_metadata = persistent_read_metadata;
|
||||
store->type.prepare_exception = persistent_prepare_exception;
|
||||
store->type.commit_exception = persistent_commit_exception;
|
||||
store->type.drop_snapshot = persistent_drop_snapshot;
|
||||
store->type.fraction_full = persistent_fraction_full;
|
||||
|
||||
store->context = ps;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int persistent_status(struct dm_exception_store *store,
|
||||
status_type_t status, char *result,
|
||||
unsigned int maxlen)
|
||||
{
|
||||
int sz = 0;
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
static struct dm_exception_store_type _persistent_type = {
|
||||
.name = "persistent",
|
||||
.module = THIS_MODULE,
|
||||
.ctr = persistent_ctr,
|
||||
.dtr = persistent_dtr,
|
||||
.read_metadata = persistent_read_metadata,
|
||||
.prepare_exception = persistent_prepare_exception,
|
||||
.commit_exception = persistent_commit_exception,
|
||||
.drop_snapshot = persistent_drop_snapshot,
|
||||
.fraction_full = persistent_fraction_full,
|
||||
.status = persistent_status,
|
||||
};
|
||||
|
||||
static struct dm_exception_store_type _persistent_compat_type = {
|
||||
.name = "P",
|
||||
.module = THIS_MODULE,
|
||||
.ctr = persistent_ctr,
|
||||
.dtr = persistent_dtr,
|
||||
.read_metadata = persistent_read_metadata,
|
||||
.prepare_exception = persistent_prepare_exception,
|
||||
.commit_exception = persistent_commit_exception,
|
||||
.drop_snapshot = persistent_drop_snapshot,
|
||||
.fraction_full = persistent_fraction_full,
|
||||
.status = persistent_status,
|
||||
};
|
||||
|
||||
int dm_persistent_snapshot_init(void)
|
||||
{
|
||||
return 0;
|
||||
int r;
|
||||
|
||||
r = dm_exception_store_type_register(&_persistent_type);
|
||||
if (r) {
|
||||
DMERR("Unable to register persistent exception store type");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dm_exception_store_type_register(&_persistent_compat_type);
|
||||
if (r) {
|
||||
DMERR("Unable to register old-style persistent exception "
|
||||
"store type");
|
||||
dm_exception_store_type_unregister(&_persistent_type);
|
||||
return r;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void dm_persistent_snapshot_exit(void)
|
||||
{
|
||||
dm_exception_store_type_unregister(&_persistent_type);
|
||||
dm_exception_store_type_unregister(&_persistent_compat_type);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ struct transient_c {
|
|||
sector_t next_free;
|
||||
};
|
||||
|
||||
static void transient_destroy(struct dm_exception_store *store)
|
||||
static void transient_dtr(struct dm_exception_store *store)
|
||||
{
|
||||
kfree(store->context);
|
||||
}
|
||||
|
@ -67,17 +67,11 @@ static void transient_fraction_full(struct dm_exception_store *store,
|
|||
*denominator = get_dev_size(store->snap->cow->bdev);
|
||||
}
|
||||
|
||||
int dm_create_transient(struct dm_exception_store *store)
|
||||
static int transient_ctr(struct dm_exception_store *store,
|
||||
unsigned argc, char **argv)
|
||||
{
|
||||
struct transient_c *tc;
|
||||
|
||||
store->type.dtr = transient_destroy;
|
||||
store->type.read_metadata = transient_read_metadata;
|
||||
store->type.prepare_exception = transient_prepare_exception;
|
||||
store->type.commit_exception = transient_commit_exception;
|
||||
store->type.drop_snapshot = NULL;
|
||||
store->type.fraction_full = transient_fraction_full;
|
||||
|
||||
tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
|
||||
if (!tc)
|
||||
return -ENOMEM;
|
||||
|
@ -88,11 +82,62 @@ int dm_create_transient(struct dm_exception_store *store)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int transient_status(struct dm_exception_store *store,
|
||||
status_type_t status, char *result,
|
||||
unsigned maxlen)
|
||||
{
|
||||
int sz = 0;
|
||||
|
||||
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,
|
||||
.fraction_full = transient_fraction_full,
|
||||
.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,
|
||||
.fraction_full = transient_fraction_full,
|
||||
.status = transient_status,
|
||||
};
|
||||
|
||||
int dm_transient_snapshot_init(void)
|
||||
{
|
||||
return 0;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -610,8 +610,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|||
if (r)
|
||||
goto bad3;
|
||||
|
||||
s->type = persistent;
|
||||
|
||||
s->valid = 1;
|
||||
s->active = 0;
|
||||
atomic_set(&s->pending_exceptions_count, 0);
|
||||
|
@ -626,19 +624,15 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|||
goto bad3;
|
||||
}
|
||||
|
||||
s->store.snap = s;
|
||||
|
||||
if (persistent == 'P')
|
||||
r = dm_create_persistent(&s->store);
|
||||
else
|
||||
r = dm_create_transient(&s->store);
|
||||
|
||||
r = dm_exception_store_create(argv[2], &s->store);
|
||||
if (r) {
|
||||
ti->error = "Couldn't create exception store";
|
||||
r = -EINVAL;
|
||||
goto bad4;
|
||||
}
|
||||
|
||||
s->store->snap = s;
|
||||
|
||||
r = dm_kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client);
|
||||
if (r) {
|
||||
ti->error = "Could not create kcopyd client";
|
||||
|
@ -665,7 +659,8 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|||
spin_lock_init(&s->tracked_chunk_lock);
|
||||
|
||||
/* Metadata must only be loaded into one table at once */
|
||||
r = s->store.type.read_metadata(&s->store, dm_add_exception, (void *)s);
|
||||
r = s->store->type->read_metadata(s->store, dm_add_exception,
|
||||
(void *)s);
|
||||
if (r < 0) {
|
||||
ti->error = "Failed to read snapshot metadata";
|
||||
goto bad_load_and_register;
|
||||
|
@ -700,7 +695,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|||
dm_kcopyd_client_destroy(s->kcopyd_client);
|
||||
|
||||
bad5:
|
||||
s->store.type.dtr(&s->store);
|
||||
s->store->type->dtr(s->store);
|
||||
|
||||
bad4:
|
||||
exit_exception_table(&s->pending, pending_cache);
|
||||
|
@ -725,7 +720,7 @@ static void __free_exceptions(struct dm_snapshot *s)
|
|||
exit_exception_table(&s->pending, pending_cache);
|
||||
exit_exception_table(&s->complete, exception_cache);
|
||||
|
||||
s->store.type.dtr(&s->store);
|
||||
s->store->type->dtr(s->store);
|
||||
}
|
||||
|
||||
static void snapshot_dtr(struct dm_target *ti)
|
||||
|
@ -820,8 +815,8 @@ static void __invalidate_snapshot(struct dm_snapshot *s, int err)
|
|||
else if (err == -ENOMEM)
|
||||
DMERR("Invalidating snapshot: Unable to allocate exception.");
|
||||
|
||||
if (s->store.type.drop_snapshot)
|
||||
s->store.type.drop_snapshot(&s->store);
|
||||
if (s->store->type->drop_snapshot)
|
||||
s->store->type->drop_snapshot(s->store);
|
||||
|
||||
s->valid = 0;
|
||||
|
||||
|
@ -943,8 +938,8 @@ static void copy_callback(int read_err, unsigned long write_err, void *context)
|
|||
|
||||
else
|
||||
/* Update the metadata if we are persistent */
|
||||
s->store.type.commit_exception(&s->store, &pe->e,
|
||||
commit_callback, pe);
|
||||
s->store->type->commit_exception(s->store, &pe->e,
|
||||
commit_callback, pe);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1010,7 +1005,7 @@ __find_pending_exception(struct dm_snapshot *s,
|
|||
atomic_set(&pe->ref_count, 0);
|
||||
pe->started = 0;
|
||||
|
||||
if (s->store.type.prepare_exception(&s->store, &pe->e)) {
|
||||
if (s->store->type->prepare_exception(s->store, &pe->e)) {
|
||||
free_pending_exception(pe);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1149,11 +1144,11 @@ static int snapshot_status(struct dm_target *ti, status_type_t type,
|
|||
if (!snap->valid)
|
||||
snprintf(result, maxlen, "Invalid");
|
||||
else {
|
||||
if (snap->store.type.fraction_full) {
|
||||
if (snap->store->type->fraction_full) {
|
||||
sector_t numerator, denominator;
|
||||
snap->store.type.fraction_full(&snap->store,
|
||||
&numerator,
|
||||
&denominator);
|
||||
snap->store->type->fraction_full(snap->store,
|
||||
&numerator,
|
||||
&denominator);
|
||||
snprintf(result, maxlen, "%llu/%llu",
|
||||
(unsigned long long)numerator,
|
||||
(unsigned long long)denominator);
|
||||
|
@ -1169,9 +1164,9 @@ static int snapshot_status(struct dm_target *ti, status_type_t type,
|
|||
* to make private copies if the output is to
|
||||
* make sense.
|
||||
*/
|
||||
snprintf(result, maxlen, "%s %s %c %llu",
|
||||
snprintf(result, maxlen, "%s %s %s %llu",
|
||||
snap->origin->name, snap->cow->name,
|
||||
snap->type,
|
||||
snap->store->type->name,
|
||||
(unsigned long long)snap->chunk_size);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ struct dm_snapshot {
|
|||
spinlock_t pe_lock;
|
||||
|
||||
/* The on disk metadata handler */
|
||||
struct dm_exception_store store;
|
||||
struct dm_exception_store *store;
|
||||
|
||||
struct dm_kcopyd_client *kcopyd_client;
|
||||
|
||||
|
|
Loading…
Reference in a new issue