1 | /* $NetBSD: dm_dev.c,v 1.9 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/disk.h> |
36 | #include <sys/disklabel.h> |
37 | #include <sys/ioctl.h> |
38 | #include <sys/ioccom.h> |
39 | #include <sys/kmem.h> |
40 | |
41 | #include "netbsd-dm.h" |
42 | #include "dm.h" |
43 | |
44 | static dm_dev_t *dm_dev_lookup_name(const char *); |
45 | static dm_dev_t *dm_dev_lookup_uuid(const char *); |
46 | static dm_dev_t *dm_dev_lookup_minor(int); |
47 | |
48 | static struct dm_dev_head dm_dev_list = |
49 | TAILQ_HEAD_INITIALIZER(dm_dev_list); |
50 | |
51 | kmutex_t dm_dev_mutex; |
52 | |
53 | /* dm_dev_mutex must be holdby caller before using disable_dev. */ |
54 | __inline static void |
55 | disable_dev(dm_dev_t * dmv) |
56 | { |
57 | TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist); |
58 | mutex_enter(&dmv->dev_mtx); |
59 | mutex_exit(&dm_dev_mutex); |
60 | while (dmv->ref_cnt != 0) |
61 | cv_wait(&dmv->dev_cv, &dmv->dev_mtx); |
62 | mutex_exit(&dmv->dev_mtx); |
63 | } |
64 | /* |
65 | * Generic function used to lookup dm_dev_t. Calling with dm_dev_name |
66 | * and dm_dev_uuid NULL is allowed. |
67 | */ |
68 | dm_dev_t * |
69 | dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid, |
70 | int dm_dev_minor) |
71 | { |
72 | dm_dev_t *dmv; |
73 | |
74 | dmv = NULL; |
75 | mutex_enter(&dm_dev_mutex); |
76 | |
77 | /* KASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor |
78 | * > 0); */ |
79 | if (dm_dev_minor > 0) |
80 | if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) { |
81 | dm_dev_busy(dmv); |
82 | mutex_exit(&dm_dev_mutex); |
83 | return dmv; |
84 | } |
85 | if (dm_dev_name != NULL) |
86 | if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) { |
87 | dm_dev_busy(dmv); |
88 | mutex_exit(&dm_dev_mutex); |
89 | return dmv; |
90 | } |
91 | if (dm_dev_uuid != NULL) |
92 | if ((dmv = dm_dev_lookup_uuid(dm_dev_uuid)) != NULL) { |
93 | dm_dev_busy(dmv); |
94 | mutex_exit(&dm_dev_mutex); |
95 | return dmv; |
96 | } |
97 | mutex_exit(&dm_dev_mutex); |
98 | return NULL; |
99 | } |
100 | |
101 | |
102 | /* |
103 | * Lookup device with its minor number. |
104 | */ |
105 | static dm_dev_t * |
106 | dm_dev_lookup_minor(int dm_dev_minor) |
107 | { |
108 | dm_dev_t *dmv; |
109 | |
110 | TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { |
111 | if (dm_dev_minor == dmv->minor) |
112 | return dmv; |
113 | } |
114 | |
115 | return NULL; |
116 | } |
117 | /* |
118 | * Lookup device with its device name. |
119 | */ |
120 | static dm_dev_t * |
121 | dm_dev_lookup_name(const char *dm_dev_name) |
122 | { |
123 | dm_dev_t *dmv; |
124 | int dlen; |
125 | int slen; |
126 | |
127 | slen = strlen(dm_dev_name); |
128 | |
129 | if (slen == 0) |
130 | return NULL; |
131 | |
132 | TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { |
133 | |
134 | dlen = strlen(dmv->name); |
135 | |
136 | if (slen != dlen) |
137 | continue; |
138 | |
139 | if (strncmp(dm_dev_name, dmv->name, slen) == 0) |
140 | return dmv; |
141 | } |
142 | |
143 | return NULL; |
144 | } |
145 | /* |
146 | * Lookup device with its device uuid. Used mostly by LVM2tools. |
147 | */ |
148 | static dm_dev_t * |
149 | dm_dev_lookup_uuid(const char *dm_dev_uuid) |
150 | { |
151 | dm_dev_t *dmv; |
152 | size_t len; |
153 | |
154 | len = 0; |
155 | len = strlen(dm_dev_uuid); |
156 | |
157 | if (len == 0) |
158 | return NULL; |
159 | |
160 | TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { |
161 | |
162 | if (strlen(dmv->uuid) != len) |
163 | continue; |
164 | |
165 | if (strncmp(dm_dev_uuid, dmv->uuid, strlen(dmv->uuid)) == 0) |
166 | return dmv; |
167 | } |
168 | |
169 | return NULL; |
170 | } |
171 | /* |
172 | * Insert new device to the global list of devices. |
173 | */ |
174 | int |
175 | dm_dev_insert(dm_dev_t * dev) |
176 | { |
177 | dm_dev_t *dmv; |
178 | int r; |
179 | |
180 | dmv = NULL; |
181 | r = 0; |
182 | |
183 | KASSERT(dev != NULL); |
184 | mutex_enter(&dm_dev_mutex); |
185 | if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) && |
186 | ((dmv = dm_dev_lookup_name(dev->name)) == NULL) && |
187 | ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)) { |
188 | |
189 | TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist); |
190 | |
191 | } else |
192 | r = EEXIST; |
193 | |
194 | mutex_exit(&dm_dev_mutex); |
195 | return r; |
196 | } |
197 | #ifdef notyet |
198 | /* |
199 | * Lookup device with its minor number. |
200 | */ |
201 | int |
202 | dm_dev_test_minor(int dm_dev_minor) |
203 | { |
204 | dm_dev_t *dmv; |
205 | |
206 | mutex_enter(&dm_dev_mutex); |
207 | TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { |
208 | if (dm_dev_minor == dmv->minor) { |
209 | mutex_exit(&dm_dev_mutex); |
210 | return 1; |
211 | } |
212 | } |
213 | mutex_exit(&dm_dev_mutex); |
214 | |
215 | return 0; |
216 | } |
217 | #endif |
218 | |
219 | /* |
220 | * dm_dev_lookup_devt look for selected device_t. We keep this routine |
221 | * outside of dm_dev_lookup because it is a temporally solution. |
222 | * |
223 | * TODO: This is a hack autoconf should be more flexible. |
224 | */ |
225 | dm_dev_t * |
226 | dm_dev_detach(device_t devt) |
227 | { |
228 | dm_dev_t *dmv; |
229 | |
230 | mutex_enter(&dm_dev_mutex); |
231 | TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { |
232 | if (devt == dmv->devt) { |
233 | disable_dev(dmv); |
234 | return dmv; |
235 | } |
236 | } |
237 | mutex_exit(&dm_dev_mutex); |
238 | |
239 | return NULL; |
240 | } |
241 | /* |
242 | * Remove device selected with dm_dev from global list of devices. |
243 | */ |
244 | dm_dev_t * |
245 | dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid, |
246 | int dm_dev_minor) |
247 | { |
248 | dm_dev_t *dmv; |
249 | dmv = NULL; |
250 | |
251 | mutex_enter(&dm_dev_mutex); |
252 | |
253 | if (dm_dev_minor > 0) |
254 | if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) { |
255 | disable_dev(dmv); |
256 | return dmv; |
257 | } |
258 | if (dm_dev_name != NULL) |
259 | if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) { |
260 | disable_dev(dmv); |
261 | return dmv; |
262 | } |
263 | if (dm_dev_uuid != NULL) |
264 | if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL) { |
265 | disable_dev(dmv); |
266 | return dmv; |
267 | } |
268 | mutex_exit(&dm_dev_mutex); |
269 | |
270 | return NULL; |
271 | } |
272 | /* |
273 | * Destroy all devices created in device-mapper. Remove all tables |
274 | * free all allocated memmory. |
275 | */ |
276 | int |
277 | dm_dev_destroy(void) |
278 | { |
279 | dm_dev_t *dmv; |
280 | mutex_enter(&dm_dev_mutex); |
281 | |
282 | while (TAILQ_FIRST(&dm_dev_list) != NULL) { |
283 | |
284 | dmv = TAILQ_FIRST(&dm_dev_list); |
285 | |
286 | TAILQ_REMOVE(&dm_dev_list, TAILQ_FIRST(&dm_dev_list), |
287 | next_devlist); |
288 | |
289 | mutex_enter(&dmv->dev_mtx); |
290 | |
291 | while (dmv->ref_cnt != 0) |
292 | cv_wait(&dmv->dev_cv, &dmv->dev_mtx); |
293 | |
294 | /* Destroy active table first. */ |
295 | dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); |
296 | |
297 | /* Destroy inactive table if exits, too. */ |
298 | dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); |
299 | |
300 | dm_table_head_destroy(&dmv->table_head); |
301 | |
302 | mutex_exit(&dmv->dev_mtx); |
303 | mutex_destroy(&dmv->dev_mtx); |
304 | cv_destroy(&dmv->dev_cv); |
305 | |
306 | (void) kmem_free(dmv, sizeof(dm_dev_t)); |
307 | } |
308 | mutex_exit(&dm_dev_mutex); |
309 | |
310 | mutex_destroy(&dm_dev_mutex); |
311 | return 0; |
312 | } |
313 | /* |
314 | * Allocate new device entry. |
315 | */ |
316 | dm_dev_t * |
317 | dm_dev_alloc(void) |
318 | { |
319 | dm_dev_t *dmv; |
320 | |
321 | dmv = kmem_zalloc(sizeof(dm_dev_t), KM_SLEEP); |
322 | |
323 | if (dmv != NULL) |
324 | dmv->diskp = kmem_zalloc(sizeof(struct disk), KM_SLEEP); |
325 | |
326 | return dmv; |
327 | } |
328 | /* |
329 | * Freed device entry. |
330 | */ |
331 | int |
332 | dm_dev_free(dm_dev_t * dmv) |
333 | { |
334 | KASSERT(dmv != NULL); |
335 | |
336 | mutex_destroy(&dmv->dev_mtx); |
337 | mutex_destroy(&dmv->diskp_mtx); |
338 | cv_destroy(&dmv->dev_cv); |
339 | |
340 | if (dmv->diskp != NULL) |
341 | (void) kmem_free(dmv->diskp, sizeof(struct disk)); |
342 | |
343 | (void) kmem_free(dmv, sizeof(dm_dev_t)); |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | void |
349 | dm_dev_busy(dm_dev_t * dmv) |
350 | { |
351 | mutex_enter(&dmv->dev_mtx); |
352 | dmv->ref_cnt++; |
353 | mutex_exit(&dmv->dev_mtx); |
354 | } |
355 | |
356 | void |
357 | dm_dev_unbusy(dm_dev_t * dmv) |
358 | { |
359 | KASSERT(dmv->ref_cnt != 0); |
360 | |
361 | mutex_enter(&dmv->dev_mtx); |
362 | if (--dmv->ref_cnt == 0) |
363 | cv_broadcast(&dmv->dev_cv); |
364 | mutex_exit(&dmv->dev_mtx); |
365 | } |
366 | /* |
367 | * Return prop_array of dm_targer_list dictionaries. |
368 | */ |
369 | prop_array_t |
370 | dm_dev_prop_list(void) |
371 | { |
372 | dm_dev_t *dmv; |
373 | prop_array_t dev_array; |
374 | prop_dictionary_t dev_dict; |
375 | |
376 | dev_array = prop_array_create(); |
377 | |
378 | mutex_enter(&dm_dev_mutex); |
379 | |
380 | TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { |
381 | dev_dict = prop_dictionary_create(); |
382 | |
383 | prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name); |
384 | prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor); |
385 | |
386 | prop_array_add(dev_array, dev_dict); |
387 | prop_object_release(dev_dict); |
388 | } |
389 | |
390 | mutex_exit(&dm_dev_mutex); |
391 | return dev_array; |
392 | } |
393 | /* |
394 | * Initialize global device mutex. |
395 | */ |
396 | int |
397 | dm_dev_init(void) |
398 | { |
399 | TAILQ_INIT(&dm_dev_list); /* initialize global dev list */ |
400 | mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE); |
401 | return 0; |
402 | } |
403 | |