forked from Minki/linux
10d3bd09a3
Change dm_unregister_target to return void and use BUG() for error reporting. dm_unregister_target can only fail because of programming bug in the target driver. It can't fail because of user's behavior or disk errors. This patch changes unregister_target to return void and use BUG if someone tries to unregister non-registered target or unregister target that is in use. This patch removes code duplication (testing of error codes in all dm targets) and reports bugs in just one place, in dm_unregister_target. In some target drivers, these return codes were ignored, which could lead to a situation where bugs could be missed. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
195 lines
3.2 KiB
C
195 lines
3.2 KiB
C
/*
|
|
* Copyright (C) 2001 Sistina Software (UK) Limited
|
|
*
|
|
* This file is released under the GPL.
|
|
*/
|
|
|
|
#include "dm.h"
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/slab.h>
|
|
|
|
#define DM_MSG_PREFIX "target"
|
|
|
|
struct tt_internal {
|
|
struct target_type tt;
|
|
|
|
struct list_head list;
|
|
long use;
|
|
};
|
|
|
|
static LIST_HEAD(_targets);
|
|
static DECLARE_RWSEM(_lock);
|
|
|
|
#define DM_MOD_NAME_SIZE 32
|
|
|
|
static inline struct tt_internal *__find_target_type(const char *name)
|
|
{
|
|
struct tt_internal *ti;
|
|
|
|
list_for_each_entry (ti, &_targets, list)
|
|
if (!strcmp(name, ti->tt.name))
|
|
return ti;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct tt_internal *get_target_type(const char *name)
|
|
{
|
|
struct tt_internal *ti;
|
|
|
|
down_read(&_lock);
|
|
|
|
ti = __find_target_type(name);
|
|
if (ti) {
|
|
if ((ti->use == 0) && !try_module_get(ti->tt.module))
|
|
ti = NULL;
|
|
else
|
|
ti->use++;
|
|
}
|
|
|
|
up_read(&_lock);
|
|
return ti;
|
|
}
|
|
|
|
static void load_module(const char *name)
|
|
{
|
|
request_module("dm-%s", name);
|
|
}
|
|
|
|
struct target_type *dm_get_target_type(const char *name)
|
|
{
|
|
struct tt_internal *ti = get_target_type(name);
|
|
|
|
if (!ti) {
|
|
load_module(name);
|
|
ti = get_target_type(name);
|
|
}
|
|
|
|
return ti ? &ti->tt : NULL;
|
|
}
|
|
|
|
void dm_put_target_type(struct target_type *t)
|
|
{
|
|
struct tt_internal *ti = (struct tt_internal *) t;
|
|
|
|
down_read(&_lock);
|
|
if (--ti->use == 0)
|
|
module_put(ti->tt.module);
|
|
|
|
BUG_ON(ti->use < 0);
|
|
up_read(&_lock);
|
|
|
|
return;
|
|
}
|
|
|
|
static struct tt_internal *alloc_target(struct target_type *t)
|
|
{
|
|
struct tt_internal *ti = kzalloc(sizeof(*ti), GFP_KERNEL);
|
|
|
|
if (ti)
|
|
ti->tt = *t;
|
|
|
|
return ti;
|
|
}
|
|
|
|
|
|
int dm_target_iterate(void (*iter_func)(struct target_type *tt,
|
|
void *param), void *param)
|
|
{
|
|
struct tt_internal *ti;
|
|
|
|
down_read(&_lock);
|
|
list_for_each_entry (ti, &_targets, list)
|
|
iter_func(&ti->tt, param);
|
|
up_read(&_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dm_register_target(struct target_type *t)
|
|
{
|
|
int rv = 0;
|
|
struct tt_internal *ti = alloc_target(t);
|
|
|
|
if (!ti)
|
|
return -ENOMEM;
|
|
|
|
down_write(&_lock);
|
|
if (__find_target_type(t->name))
|
|
rv = -EEXIST;
|
|
else
|
|
list_add(&ti->list, &_targets);
|
|
|
|
up_write(&_lock);
|
|
if (rv)
|
|
kfree(ti);
|
|
return rv;
|
|
}
|
|
|
|
void dm_unregister_target(struct target_type *t)
|
|
{
|
|
struct tt_internal *ti;
|
|
|
|
down_write(&_lock);
|
|
if (!(ti = __find_target_type(t->name))) {
|
|
DMCRIT("Unregistering unrecognised target: %s", t->name);
|
|
BUG();
|
|
}
|
|
|
|
if (ti->use) {
|
|
DMCRIT("Attempt to unregister target still in use: %s",
|
|
t->name);
|
|
BUG();
|
|
}
|
|
|
|
list_del(&ti->list);
|
|
kfree(ti);
|
|
|
|
up_write(&_lock);
|
|
}
|
|
|
|
/*
|
|
* io-err: always fails an io, useful for bringing
|
|
* up LVs that have holes in them.
|
|
*/
|
|
static int io_err_ctr(struct dm_target *ti, unsigned int argc, char **args)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void io_err_dtr(struct dm_target *ti)
|
|
{
|
|
/* empty */
|
|
}
|
|
|
|
static int io_err_map(struct dm_target *ti, struct bio *bio,
|
|
union map_info *map_context)
|
|
{
|
|
return -EIO;
|
|
}
|
|
|
|
static struct target_type error_target = {
|
|
.name = "error",
|
|
.version = {1, 0, 1},
|
|
.ctr = io_err_ctr,
|
|
.dtr = io_err_dtr,
|
|
.map = io_err_map,
|
|
};
|
|
|
|
int __init dm_target_init(void)
|
|
{
|
|
return dm_register_target(&error_target);
|
|
}
|
|
|
|
void dm_target_exit(void)
|
|
{
|
|
dm_unregister_target(&error_target);
|
|
}
|
|
|
|
EXPORT_SYMBOL(dm_register_target);
|
|
EXPORT_SYMBOL(dm_unregister_target);
|