sys/dev/disk/dm: Cleanups
[dragonfly.git] / sys / dev / disk / dm / dm_dev.c
1 /*        $NetBSD: dm_dev.c,v 1.8 2010/01/04 00:19:08 haad Exp $      */
2
3 /*
4  * Copyright (c) 2010-2011 Alex Hornung <alex@alexhornung.com>
5  * Copyright (c) 2008 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Adam Hamsik.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <sys/param.h>
34 #include <machine/thread.h>
35 #include <sys/thread2.h>
36
37 #include <sys/disk.h>
38 #include <sys/disklabel.h>
39 #include <sys/devicestat.h>
40 #include <sys/device.h>
41 #include <sys/udev.h>
42 #include <sys/devfs.h>
43 #include <sys/malloc.h>
44 #include <dev/disk/dm/dm.h>
45
46 #include "netbsd-dm.h"
47
48 extern struct dev_ops dm_ops;
49
50 static struct devfs_bitmap dm_minor_bitmap;
51 uint64_t dm_dev_counter;
52
53 static dm_dev_t *dm_dev_lookup_name(const char *);
54 static dm_dev_t *dm_dev_lookup_uuid(const char *);
55 static dm_dev_t *dm_dev_lookup_minor(int);
56
57 static struct dm_dev_head dm_dev_list =
58 TAILQ_HEAD_INITIALIZER(dm_dev_list);
59
60 static struct lock dm_dev_mutex;
61
62 /* dm_dev_mutex must be held by caller before using disable_dev. */
63 static void
64 disable_dev(dm_dev_t *dmv)
65 {
66         KKASSERT(lockstatus(&dm_dev_mutex, curthread) == LK_EXCLUSIVE);
67
68         TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist);
69
70         lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
71         while (dmv->ref_cnt != 0)
72                 cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
73         lockmgr(&dmv->dev_mtx, LK_RELEASE);
74 }
75 /*
76  * Generic function used to lookup dm_dev_t. Calling with dm_dev_name
77  * and dm_dev_uuid NULL is allowed.
78  */
79 dm_dev_t *
80 dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid,
81     int dm_dev_minor)
82 {
83         dm_dev_t *dmv;
84
85         dmv = NULL;
86         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
87
88         /* KKASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor
89          * > 0); */
90         if (dm_dev_minor > 0)
91                 if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) {
92                         dm_dev_busy(dmv);
93                         lockmgr(&dm_dev_mutex, LK_RELEASE);
94                         return dmv;
95                 }
96         if (dm_dev_name != NULL)
97                 if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) {
98                         dm_dev_busy(dmv);
99                         lockmgr(&dm_dev_mutex, LK_RELEASE);
100                         return dmv;
101                 }
102         if (dm_dev_uuid != NULL)
103                 if ((dmv = dm_dev_lookup_uuid(dm_dev_uuid)) != NULL) {
104                         dm_dev_busy(dmv);
105                         lockmgr(&dm_dev_mutex, LK_RELEASE);
106                         return dmv;
107                 }
108         lockmgr(&dm_dev_mutex, LK_RELEASE);
109         return NULL;
110 }
111
112
113 /*
114  * Lookup device with its minor number.
115  */
116 static dm_dev_t *
117 dm_dev_lookup_minor(int dm_dev_minor)
118 {
119         dm_dev_t *dmv;
120
121         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
122                 if (dm_dev_minor == dmv->minor)
123                         return dmv;
124         }
125
126         return NULL;
127 }
128 /*
129  * Lookup device with it's device name.
130  */
131 static dm_dev_t *
132 dm_dev_lookup_name(const char *dm_dev_name)
133 {
134         dm_dev_t *dmv;
135
136         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
137                 if (strcmp(dm_dev_name, dmv->name) == 0)
138                         return dmv;
139         }
140
141         return NULL;
142 }
143 /*
144  * Lookup device with it's device uuid. Used mostly by LVM2tools.
145  */
146 static dm_dev_t *
147 dm_dev_lookup_uuid(const char *dm_dev_uuid)
148 {
149         dm_dev_t *dmv;
150
151         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
152                 if (strcmp(dm_dev_uuid, dmv->uuid) == 0)
153                         return dmv;
154         }
155
156         return NULL;
157 }
158 /*
159  * Insert new device to the global list of devices.
160  */
161 int
162 dm_dev_insert(dm_dev_t *dev)
163 {
164         dm_dev_t *dmv;
165         int r;
166
167         dmv = NULL;
168         r = 0;
169
170         KKASSERT(dev != NULL);
171         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
172
173         if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) &&
174             ((dmv = dm_dev_lookup_name(dev->name)) == NULL) &&
175             ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)) {
176                 TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist);
177         } else {
178                 KKASSERT(dmv != NULL);
179                 r = EEXIST;
180         }
181
182         lockmgr(&dm_dev_mutex, LK_RELEASE);
183         return r;
184 }
185
186 dm_dev_t *
187 dm_dev_rem_dev(dm_dev_t *dmv)
188 {
189         if (dmv == NULL)
190                 return NULL;
191
192         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
193         disable_dev(dmv);
194         lockmgr(&dm_dev_mutex, LK_RELEASE);
195
196         KKASSERT(dmv != NULL);
197         return dmv;
198 }
199
200 /*
201  * Remove device selected with dm_dev from global list of devices.
202  */
203 dm_dev_t *
204 dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid, int dm_dev_minor)
205 {
206         dm_dev_t *dmv = NULL;
207
208         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
209
210         if (dm_dev_minor > 0) {
211                 if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL)
212                         disable_dev(dmv);
213         } else if (dm_dev_name != NULL) {
214                 if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL)
215                         disable_dev(dmv);
216         } else if (dm_dev_uuid != NULL) {
217                 if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL)
218                         disable_dev(dmv);
219         }
220
221         lockmgr(&dm_dev_mutex, LK_RELEASE);
222
223         return dmv;
224 }
225
226 int
227 dm_dev_create(dm_dev_t **dmvp, const char *name, const char *uuid, int flags)
228 {
229         dm_dev_t *dmv;
230         char name_buf[MAXPATHLEN];
231         int r, dm_minor;
232
233         if ((dmv = dm_dev_alloc()) == NULL)
234                 return ENOMEM;
235
236         if (uuid)
237                 strncpy(dmv->uuid, uuid, DM_UUID_LEN);
238         else
239                 dmv->uuid[0] = '\0';
240
241         if (name)
242                 strlcpy(dmv->name, name, DM_NAME_LEN);
243
244         dm_minor = devfs_clone_bitmap_get(&dm_minor_bitmap, 0);
245
246         dm_table_head_init(&dmv->table_head);
247
248         lockinit(&dmv->dev_mtx, "dmdev", 0, LK_CANRECURSE);
249         cv_init(&dmv->dev_cv, "dm_dev");
250
251         if (flags & DM_READONLY_FLAG)
252                 dmv->flags |= DM_READONLY_FLAG;
253
254         aprint_debug("Creating device dm/%s\n", name);
255         ksnprintf(name_buf, sizeof(name_buf), "mapper/%s", dmv->name);
256
257         devstat_add_entry(&dmv->stats, name, 0, DEV_BSIZE,
258             DEVSTAT_NO_ORDERED_TAGS,
259             DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER,
260             DEVSTAT_PRIORITY_DISK);
261
262         dmv->devt = disk_create_named(name_buf, dm_minor, dmv->diskp, &dm_ops);
263         reference_dev(dmv->devt);
264
265         /* Make sure the device are immediately available */
266         sync_devs();
267
268         dmv->devt->si_drv1 = dmv;
269         dmv->devt->si_drv2 = dmv->diskp;
270
271         dmv->minor = minor(dmv->devt);
272         udev_dict_set_cstr(dmv->devt, "subsystem", "disk");
273
274         if ((r = dm_dev_insert(dmv)) != 0)
275                 dm_dev_destroy(dmv);
276
277         /* Increment device counter After creating device */
278         ++dm_dev_counter; /* XXX: was atomic 64 */
279         *dmvp = dmv;
280
281         return r;
282 }
283
284 int
285 dm_dev_destroy(dm_dev_t *dmv)
286 {
287         int minor;
288
289         /* Destroy active table first.  */
290         dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
291
292         /* Destroy inactive table if exits, too. */
293         dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
294
295         dm_table_head_destroy(&dmv->table_head);
296
297         minor = dkunit(dmv->devt);
298         disk_destroy(dmv->diskp);
299         devstat_remove_entry(&dmv->stats);
300
301         release_dev(dmv->devt);
302         devfs_clone_bitmap_put(&dm_minor_bitmap, minor);
303
304         lockuninit(&dmv->dev_mtx);
305         cv_destroy(&dmv->dev_cv);
306
307         /* Destroy device */
308         dm_dev_free(dmv);
309
310         /* Decrement device counter After removing device */
311         --dm_dev_counter; /* XXX: was atomic 64 */
312
313         return 0;
314 }
315
316 /*
317  * dm_dev_remove is called to completely destroy & remove a dm disk device.
318  */
319 int
320 dm_dev_remove(dm_dev_t *dmv)
321 {
322         /* Remove device from list and wait for refcnt to drop to zero */
323         dm_dev_rem_dev(dmv);
324
325         /* Destroy and free the device */
326         dm_dev_destroy(dmv);
327
328         return 0;
329 }
330
331 int
332 dm_dev_remove_all(int gentle)
333 {
334         dm_dev_t *dmv, *dmv2;
335         int r;
336
337         r = 0;
338
339         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
340
341         /*
342          * Process in reverse order so that it can deal with inter-depentent
343          * devices.
344          */
345         TAILQ_FOREACH_REVERSE_MUTABLE(dmv, &dm_dev_list, dm_dev_head,
346             next_devlist, dmv2) {
347                 if (gentle && dmv->is_open) {
348                         r = EBUSY;
349                         continue;
350                 }
351
352                 disable_dev(dmv);
353                 dm_dev_destroy(dmv);
354         }
355         lockmgr(&dm_dev_mutex, LK_RELEASE);
356
357         return r;
358 }
359
360 /*
361  * Allocate new device entry.
362  */
363 dm_dev_t *
364 dm_dev_alloc(void)
365 {
366         dm_dev_t *dmv;
367
368         dmv = kmalloc(sizeof(dm_dev_t), M_DM, M_WAITOK | M_ZERO);
369
370         if (dmv != NULL)
371                 dmv->diskp = kmalloc(sizeof(struct disk), M_DM, M_WAITOK | M_ZERO);
372
373         return dmv;
374 }
375 /*
376  * Freed device entry.
377  */
378 int
379 dm_dev_free(dm_dev_t *dmv)
380 {
381         KKASSERT(dmv != NULL);
382
383         if (dmv->diskp != NULL)
384                 kfree(dmv->diskp, M_DM);
385
386         kfree(dmv, M_DM);
387
388         return 0;
389 }
390
391 void
392 dm_dev_busy(dm_dev_t *dmv)
393 {
394         lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
395         dmv->ref_cnt++;
396         lockmgr(&dmv->dev_mtx, LK_RELEASE);
397 }
398
399 void
400 dm_dev_unbusy(dm_dev_t *dmv)
401 {
402         KKASSERT(dmv->ref_cnt != 0);
403
404         lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
405         if (--dmv->ref_cnt == 0)
406                 cv_broadcast(&dmv->dev_cv);
407         lockmgr(&dmv->dev_mtx, LK_RELEASE);
408 }
409 /*
410  * Return prop_array of dm_targer_list dictionaries.
411  */
412 prop_array_t
413 dm_dev_prop_list(void)
414 {
415         dm_dev_t *dmv;
416         prop_array_t dev_array;
417         prop_dictionary_t dev_dict;
418
419         dev_array = prop_array_create();
420
421         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
422
423         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
424                 dev_dict = prop_dictionary_create();
425
426                 prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name);
427                 prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor);
428
429                 prop_array_add(dev_array, dev_dict);
430                 prop_object_release(dev_dict);
431         }
432
433         lockmgr(&dm_dev_mutex, LK_RELEASE);
434         return dev_array;
435 }
436 /*
437  * Initialize global device mutex.
438  */
439 int
440 dm_dev_init(void)
441 {
442         TAILQ_INIT(&dm_dev_list);       /* initialize global dev list */
443         lockinit(&dm_dev_mutex, "dmdevlist", 0, LK_CANRECURSE);
444         devfs_clone_bitmap_init(&dm_minor_bitmap);
445         return 0;
446 }
447
448 /*
449  * Destroy all devices created in device-mapper. Remove all tables
450  * free all allocated memmory.
451  */
452 int
453 dm_dev_uninit(void)
454 {
455         /* Force removal of all devices */
456         dm_dev_remove_all(0);
457
458         lockuninit(&dm_dev_mutex);
459         return 0;
460 }