1 | /* $NetBSD: dm_target.c,v 1.19 2014/10/18 08:33:27 snj Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Adam Hamsik. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code Must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/types.h> |
33 | #include <sys/param.h> |
34 | |
35 | #include <sys/kmem.h> |
36 | #include <sys/module.h> |
37 | |
38 | |
39 | #include "netbsd-dm.h" |
40 | #include "dm.h" |
41 | |
42 | static dm_target_t *dm_target_lookup_name(const char *); |
43 | |
44 | TAILQ_HEAD(dm_target_head, dm_target); |
45 | |
46 | static struct dm_target_head dm_target_list = |
47 | TAILQ_HEAD_INITIALIZER(dm_target_list); |
48 | |
49 | kmutex_t dm_target_mutex; |
50 | |
51 | /* |
52 | * Called indirectly from dm_table_load_ioctl to mark target as used. |
53 | */ |
54 | void |
55 | dm_target_busy(dm_target_t * target) |
56 | { |
57 | atomic_inc_32(&target->ref_cnt); |
58 | } |
59 | /* |
60 | * Release reference counter on target. |
61 | */ |
62 | void |
63 | dm_target_unbusy(dm_target_t * target) |
64 | { |
65 | KASSERT(target->ref_cnt > 0); |
66 | atomic_dec_32(&target->ref_cnt); |
67 | } |
68 | /* |
69 | * Try to autoload target module if it was not found in current |
70 | * target list. |
71 | */ |
72 | dm_target_t * |
73 | dm_target_autoload(const char *dm_target_name) |
74 | { |
75 | char name[30]; |
76 | u_int gen; |
77 | dm_target_t *dmt; |
78 | |
79 | snprintf(name, sizeof(name), "dm_target_%s" , dm_target_name); |
80 | name[29] = '\0'; |
81 | |
82 | do { |
83 | gen = module_gen; |
84 | |
85 | /* Try to autoload target module */ |
86 | (void) module_autoload(name, MODULE_CLASS_MISC); |
87 | } while (gen != module_gen); |
88 | |
89 | mutex_enter(&dm_target_mutex); |
90 | dmt = dm_target_lookup_name(dm_target_name); |
91 | if (dmt != NULL) |
92 | dm_target_busy(dmt); |
93 | mutex_exit(&dm_target_mutex); |
94 | |
95 | return dmt; |
96 | } |
97 | /* |
98 | * Lookup for target in global target list. |
99 | */ |
100 | dm_target_t * |
101 | dm_target_lookup(const char *dm_target_name) |
102 | { |
103 | dm_target_t *dmt; |
104 | |
105 | dmt = NULL; |
106 | |
107 | if (dm_target_name == NULL) |
108 | return NULL; |
109 | |
110 | mutex_enter(&dm_target_mutex); |
111 | |
112 | dmt = dm_target_lookup_name(dm_target_name); |
113 | if (dmt != NULL) |
114 | dm_target_busy(dmt); |
115 | |
116 | mutex_exit(&dm_target_mutex); |
117 | |
118 | return dmt; |
119 | } |
120 | /* |
121 | * Search for name in TAIL and return apropriate pointer. |
122 | */ |
123 | static dm_target_t * |
124 | dm_target_lookup_name(const char *dm_target_name) |
125 | { |
126 | dm_target_t *dm_target; |
127 | int dlen; |
128 | int slen; |
129 | |
130 | slen = strlen(dm_target_name) + 1; |
131 | |
132 | TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) { |
133 | dlen = strlen(dm_target->name) + 1; |
134 | if (dlen != slen) |
135 | continue; |
136 | |
137 | if (strncmp(dm_target_name, dm_target->name, slen) == 0) |
138 | return dm_target; |
139 | } |
140 | |
141 | return NULL; |
142 | } |
143 | /* |
144 | * Insert new target struct into the TAIL. |
145 | * dm_target |
146 | * contains name, version, function pointer to specifif target functions. |
147 | */ |
148 | int |
149 | dm_target_insert(dm_target_t * dm_target) |
150 | { |
151 | dm_target_t *dmt; |
152 | |
153 | /* Sanity check for any missing function */ |
154 | KASSERT(dm_target->init != NULL); |
155 | KASSERT(dm_target->status != NULL); |
156 | KASSERT(dm_target->strategy != NULL); |
157 | KASSERT(dm_target->deps != NULL); |
158 | KASSERT(dm_target->destroy != NULL); |
159 | KASSERT(dm_target->upcall != NULL); |
160 | KASSERT(dm_target->sync != NULL); |
161 | KASSERT(dm_target->secsize != NULL); |
162 | |
163 | mutex_enter(&dm_target_mutex); |
164 | |
165 | dmt = dm_target_lookup_name(dm_target->name); |
166 | if (dmt != NULL) { |
167 | mutex_exit(&dm_target_mutex); |
168 | return EEXIST; |
169 | } |
170 | TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next); |
171 | |
172 | mutex_exit(&dm_target_mutex); |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | |
178 | /* |
179 | * Remove target from TAIL, target is selected with its name. |
180 | */ |
181 | int |
182 | dm_target_rem(char *dm_target_name) |
183 | { |
184 | dm_target_t *dmt; |
185 | |
186 | KASSERT(dm_target_name != NULL); |
187 | |
188 | mutex_enter(&dm_target_mutex); |
189 | |
190 | dmt = dm_target_lookup_name(dm_target_name); |
191 | if (dmt == NULL) { |
192 | mutex_exit(&dm_target_mutex); |
193 | return ENOENT; |
194 | } |
195 | if (dmt->ref_cnt > 0) { |
196 | mutex_exit(&dm_target_mutex); |
197 | return EBUSY; |
198 | } |
199 | TAILQ_REMOVE(&dm_target_list, |
200 | dmt, dm_target_next); |
201 | |
202 | mutex_exit(&dm_target_mutex); |
203 | |
204 | (void) kmem_free(dmt, sizeof(dm_target_t)); |
205 | |
206 | return 0; |
207 | } |
208 | /* |
209 | * Destroy all targets and remove them from queue. |
210 | * This routine is called from dm_detach, before module |
211 | * is unloaded. |
212 | */ |
213 | int |
214 | dm_target_destroy(void) |
215 | { |
216 | dm_target_t *dm_target; |
217 | |
218 | mutex_enter(&dm_target_mutex); |
219 | while (TAILQ_FIRST(&dm_target_list) != NULL) { |
220 | |
221 | dm_target = TAILQ_FIRST(&dm_target_list); |
222 | |
223 | TAILQ_REMOVE(&dm_target_list, TAILQ_FIRST(&dm_target_list), |
224 | dm_target_next); |
225 | |
226 | (void) kmem_free(dm_target, sizeof(dm_target_t)); |
227 | } |
228 | mutex_exit(&dm_target_mutex); |
229 | |
230 | mutex_destroy(&dm_target_mutex); |
231 | |
232 | return 0; |
233 | } |
234 | /* |
235 | * Allocate new target entry. |
236 | */ |
237 | dm_target_t * |
238 | dm_target_alloc(const char *name) |
239 | { |
240 | return kmem_zalloc(sizeof(dm_target_t), KM_SLEEP); |
241 | } |
242 | /* |
243 | * Return prop_array of dm_target dictionaries. |
244 | */ |
245 | prop_array_t |
246 | dm_target_prop_list(void) |
247 | { |
248 | prop_array_t target_array, ver; |
249 | prop_dictionary_t target_dict; |
250 | dm_target_t *dm_target; |
251 | |
252 | size_t i; |
253 | |
254 | target_array = prop_array_create(); |
255 | |
256 | mutex_enter(&dm_target_mutex); |
257 | |
258 | TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) { |
259 | |
260 | target_dict = prop_dictionary_create(); |
261 | ver = prop_array_create(); |
262 | prop_dictionary_set_cstring(target_dict, DM_TARGETS_NAME, |
263 | dm_target->name); |
264 | |
265 | for (i = 0; i < 3; i++) |
266 | prop_array_add_uint32(ver, dm_target->version[i]); |
267 | |
268 | prop_dictionary_set(target_dict, DM_TARGETS_VERSION, ver); |
269 | prop_array_add(target_array, target_dict); |
270 | |
271 | prop_object_release(ver); |
272 | prop_object_release(target_dict); |
273 | } |
274 | |
275 | mutex_exit(&dm_target_mutex); |
276 | |
277 | return target_array; |
278 | } |
279 | /* Initialize dm_target subsystem. */ |
280 | int |
281 | dm_target_init(void) |
282 | { |
283 | dm_target_t *dmt, *dmt3; |
284 | int r; |
285 | |
286 | r = 0; |
287 | |
288 | mutex_init(&dm_target_mutex, MUTEX_DEFAULT, IPL_NONE); |
289 | |
290 | dmt = dm_target_alloc("linear" ); |
291 | dmt3 = dm_target_alloc("striped" ); |
292 | |
293 | dmt->version[0] = 1; |
294 | dmt->version[1] = 0; |
295 | dmt->version[2] = 2; |
296 | strlcpy(dmt->name, "linear" , DM_MAX_TYPE_NAME); |
297 | dmt->init = &dm_target_linear_init; |
298 | dmt->status = &dm_target_linear_status; |
299 | dmt->strategy = &dm_target_linear_strategy; |
300 | dmt->sync = &dm_target_linear_sync; |
301 | dmt->deps = &dm_target_linear_deps; |
302 | dmt->destroy = &dm_target_linear_destroy; |
303 | dmt->upcall = &dm_target_linear_upcall; |
304 | dmt->secsize = &dm_target_linear_secsize; |
305 | |
306 | r = dm_target_insert(dmt); |
307 | |
308 | dmt3->version[0] = 1; |
309 | dmt3->version[1] = 0; |
310 | dmt3->version[2] = 3; |
311 | strlcpy(dmt3->name, "striped" , DM_MAX_TYPE_NAME); |
312 | dmt3->init = &dm_target_stripe_init; |
313 | dmt3->status = &dm_target_stripe_status; |
314 | dmt3->strategy = &dm_target_stripe_strategy; |
315 | dmt3->sync = &dm_target_stripe_sync; |
316 | dmt3->deps = &dm_target_stripe_deps; |
317 | dmt3->destroy = &dm_target_stripe_destroy; |
318 | dmt3->upcall = &dm_target_stripe_upcall; |
319 | dmt3->secsize = &dm_target_stripe_secsize; |
320 | |
321 | r = dm_target_insert(dmt3); |
322 | |
323 | return r; |
324 | } |
325 | |