fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / 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 = kmalloc(sizeof(*psi), GFP_KERNEL);
98
99         if (psi) {
100                 memset(psi, 0, sizeof(*psi));
101                 psi->pst = *pst;
102         }
103
104         return psi;
105 }
106
107 int dm_register_path_selector(struct path_selector_type *pst)
108 {
109         int r = 0;
110         struct ps_internal *psi = _alloc_path_selector(pst);
111
112         if (!psi)
113                 return -ENOMEM;
114
115         down_write(&_ps_lock);
116
117         if (__find_path_selector_type(pst->name)) {
118                 kfree(psi);
119                 r = -EEXIST;
120         } else
121                 list_add(&psi->list, &_path_selectors);
122
123         up_write(&_ps_lock);
124
125         return r;
126 }
127
128 int dm_unregister_path_selector(struct path_selector_type *pst)
129 {
130         struct ps_internal *psi;
131
132         down_write(&_ps_lock);
133
134         psi = __find_path_selector_type(pst->name);
135         if (!psi) {
136                 up_write(&_ps_lock);
137                 return -EINVAL;
138         }
139
140         if (psi->use) {
141                 up_write(&_ps_lock);
142                 return -ETXTBSY;
143         }
144
145         list_del(&psi->list);
146
147         up_write(&_ps_lock);
148
149         kfree(psi);
150
151         return 0;
152 }
153
154 EXPORT_SYMBOL_GPL(dm_register_path_selector);
155 EXPORT_SYMBOL_GPL(dm_unregister_path_selector);