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