Merge branch 'to_linus' of master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[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         if (psi->use < 0)
90                 BUG();
91
92 out:
93         up_read(&_ps_lock);
94 }
95
96 static struct ps_internal *_alloc_path_selector(struct path_selector_type *pst)
97 {
98         struct ps_internal *psi = kmalloc(sizeof(*psi), GFP_KERNEL);
99
100         if (psi) {
101                 memset(psi, 0, sizeof(*psi));
102                 psi->pst = *pst;
103         }
104
105         return psi;
106 }
107
108 int dm_register_path_selector(struct path_selector_type *pst)
109 {
110         int r = 0;
111         struct ps_internal *psi = _alloc_path_selector(pst);
112
113         if (!psi)
114                 return -ENOMEM;
115
116         down_write(&_ps_lock);
117
118         if (__find_path_selector_type(pst->name)) {
119                 kfree(psi);
120                 r = -EEXIST;
121         } else
122                 list_add(&psi->list, &_path_selectors);
123
124         up_write(&_ps_lock);
125
126         return r;
127 }
128
129 int dm_unregister_path_selector(struct path_selector_type *pst)
130 {
131         struct ps_internal *psi;
132
133         down_write(&_ps_lock);
134
135         psi = __find_path_selector_type(pst->name);
136         if (!psi) {
137                 up_write(&_ps_lock);
138                 return -EINVAL;
139         }
140
141         if (psi->use) {
142                 up_write(&_ps_lock);
143                 return -ETXTBSY;
144         }
145
146         list_del(&psi->list);
147
148         up_write(&_ps_lock);
149
150         kfree(psi);
151
152         return 0;
153 }
154
155 EXPORT_SYMBOL_GPL(dm_register_path_selector);
156 EXPORT_SYMBOL_GPL(dm_unregister_path_selector);