Merge branch 'x86/for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip...
[linux-2.6] / drivers / md / dm-path-selector.c
1 /*
2  * Copyright (C) 2003 Sistina Software.
3  * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
4  *
5  * Module Author: Heinz Mauelshagen
6  *
7  * This file is released under the GPL.
8  *
9  * Path selector registration.
10  */
11
12 #include "dm.h"
13 #include "dm-path-selector.h"
14
15 #include <linux/slab.h>
16
17 struct ps_internal {
18         struct path_selector_type pst;
19
20         struct list_head list;
21         long use;
22 };
23
24 #define pst_to_psi(__pst) container_of((__pst), struct ps_internal, pst)
25
26 static LIST_HEAD(_path_selectors);
27 static DECLARE_RWSEM(_ps_lock);
28
29 static struct ps_internal *__find_path_selector_type(const char *name)
30 {
31         struct ps_internal *psi;
32
33         list_for_each_entry(psi, &_path_selectors, list) {
34                 if (!strcmp(name, psi->pst.name))
35                         return psi;
36         }
37
38         return NULL;
39 }
40
41 static struct ps_internal *get_path_selector(const char *name)
42 {
43         struct ps_internal *psi;
44
45         down_read(&_ps_lock);
46         psi = __find_path_selector_type(name);
47         if (psi) {
48                 if ((psi->use == 0) && !try_module_get(psi->pst.module))
49                         psi = NULL;
50                 else
51                         psi->use++;
52         }
53         up_read(&_ps_lock);
54
55         return psi;
56 }
57
58 struct path_selector_type *dm_get_path_selector(const char *name)
59 {
60         struct ps_internal *psi;
61
62         if (!name)
63                 return NULL;
64
65         psi = get_path_selector(name);
66         if (!psi) {
67                 request_module("dm-%s", name);
68                 psi = get_path_selector(name);
69         }
70
71         return psi ? &psi->pst : NULL;
72 }
73
74 void dm_put_path_selector(struct path_selector_type *pst)
75 {
76         struct ps_internal *psi;
77
78         if (!pst)
79                 return;
80
81         down_read(&_ps_lock);
82         psi = __find_path_selector_type(pst->name);
83         if (!psi)
84                 goto out;
85
86         if (--psi->use == 0)
87                 module_put(psi->pst.module);
88
89         BUG_ON(psi->use < 0);
90
91 out:
92         up_read(&_ps_lock);
93 }
94
95 static struct ps_internal *_alloc_path_selector(struct path_selector_type *pst)
96 {
97         struct ps_internal *psi = kzalloc(sizeof(*psi), GFP_KERNEL);
98
99         if (psi)
100                 psi->pst = *pst;
101
102         return psi;
103 }
104
105 int dm_register_path_selector(struct path_selector_type *pst)
106 {
107         int r = 0;
108         struct ps_internal *psi = _alloc_path_selector(pst);
109
110         if (!psi)
111                 return -ENOMEM;
112
113         down_write(&_ps_lock);
114
115         if (__find_path_selector_type(pst->name)) {
116                 kfree(psi);
117                 r = -EEXIST;
118         } else
119                 list_add(&psi->list, &_path_selectors);
120
121         up_write(&_ps_lock);
122
123         return r;
124 }
125
126 int dm_unregister_path_selector(struct path_selector_type *pst)
127 {
128         struct ps_internal *psi;
129
130         down_write(&_ps_lock);
131
132         psi = __find_path_selector_type(pst->name);
133         if (!psi) {
134                 up_write(&_ps_lock);
135                 return -EINVAL;
136         }
137
138         if (psi->use) {
139                 up_write(&_ps_lock);
140                 return -ETXTBSY;
141         }
142
143         list_del(&psi->list);
144
145         up_write(&_ps_lock);
146
147         kfree(psi);
148
149         return 0;
150 }
151
152 EXPORT_SYMBOL_GPL(dm_register_path_selector);
153 EXPORT_SYMBOL_GPL(dm_unregister_path_selector);