dm - add remove_all, refactor
[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) 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 #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/ioccom.h>
44 #include <sys/malloc.h>
45 #include <dev/disk/dm/dm.h>
46
47 #include "netbsd-dm.h"
48
49 extern struct dev_ops dm_ops;
50
51 struct devfs_bitmap dm_minor_bitmap;
52 uint64_t dm_dev_counter;
53
54 static dm_dev_t *dm_dev_lookup_name(const char *);
55 static dm_dev_t *dm_dev_lookup_uuid(const char *);
56 static dm_dev_t *dm_dev_lookup_minor(int);
57
58 static struct dm_dev_head dm_dev_list =
59 TAILQ_HEAD_INITIALIZER(dm_dev_list);
60
61 struct lock dm_dev_mutex;
62
63 /* dm_dev_mutex must be held by caller before using disable_dev. */
64 static void
65 disable_dev(dm_dev_t * dmv)
66 {
67         KKASSERT(lockstatus(&dm_dev_mutex, curthread) == LK_EXCLUSIVE);
68
69         TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist);
70
71         lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
72         while (dmv->ref_cnt != 0)
73                 cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
74         lockmgr(&dmv->dev_mtx, LK_RELEASE);
75 }
76 /*
77  * Generic function used to lookup dm_dev_t. Calling with dm_dev_name
78  * and dm_dev_uuid NULL is allowed.
79  */
80 dm_dev_t *
81 dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid,
82     int dm_dev_minor)
83 {
84         dm_dev_t *dmv;
85
86         dmv = NULL;
87         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
88
89         /* KKASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor
90          * > 0); */
91         if (dm_dev_minor > 0)
92                 if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) {
93                         dm_dev_busy(dmv);
94                         lockmgr(&dm_dev_mutex, LK_RELEASE);
95                         return dmv;
96                 }
97         if (dm_dev_name != NULL)
98                 if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) {
99                         dm_dev_busy(dmv);
100                         lockmgr(&dm_dev_mutex, LK_RELEASE);
101                         return dmv;
102                 }
103         if (dm_dev_uuid != NULL)
104                 if ((dmv = dm_dev_lookup_uuid(dm_dev_uuid)) != NULL) {
105                         dm_dev_busy(dmv);
106                         lockmgr(&dm_dev_mutex, LK_RELEASE);
107                         return dmv;
108                 }
109         lockmgr(&dm_dev_mutex, LK_RELEASE);
110         return NULL;
111 }
112
113
114 /*
115  * Lookup device with its minor number.
116  */
117 static dm_dev_t *
118 dm_dev_lookup_minor(int dm_dev_minor)
119 {
120         dm_dev_t *dmv;
121
122         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
123                 if (dm_dev_minor == dmv->minor)
124                         return dmv;
125         }
126
127         return NULL;
128 }
129 /*
130  * Lookup device with it's device name.
131  */
132 static dm_dev_t *
133 dm_dev_lookup_name(const char *dm_dev_name)
134 {
135         dm_dev_t *dmv;
136
137         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
138                 if (strcmp(dm_dev_name, dmv->name) == 0)
139                         return dmv;
140         }
141
142         return NULL;
143 }
144 /*
145  * Lookup device with it's device uuid. Used mostly by LVM2tools.
146  */
147 static dm_dev_t *
148 dm_dev_lookup_uuid(const char *dm_dev_uuid)
149 {
150         dm_dev_t *dmv;
151
152         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
153                 if (strcmp(dm_dev_uuid, dmv->uuid) == 0)
154                         return dmv;
155         }
156
157         return NULL;
158 }
159 /*
160  * Insert new device to the global list of devices.
161  */
162 int
163 dm_dev_insert(dm_dev_t * dev)
164 {
165         dm_dev_t *dmv;
166         int r;
167
168         dmv = NULL;
169         r = 0;
170
171         KKASSERT(dev != NULL);
172         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
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
177                 TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist);
178
179         } else
180                 r = EEXIST;
181
182         lockmgr(&dm_dev_mutex, LK_RELEASE);
183         return r;
184 }
185
186 /*
187  * Remove device selected with dm_dev from global list of devices.
188  */
189 dm_dev_t *
190 dm_dev_rem(dm_dev_t *dmv, const char *dm_dev_name, const char *dm_dev_uuid,
191     int dm_dev_minor)
192 {
193         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
194
195         if (dmv != NULL) {
196                 disable_dev(dmv);
197         } else if (dm_dev_minor > 0) {
198                 if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL)
199                         disable_dev(dmv);
200         } else if (dm_dev_name != NULL) {
201                 if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL)
202                         disable_dev(dmv);
203         } else if (dm_dev_uuid != NULL) {
204                 if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL)
205                         disable_dev(dmv);
206         }
207
208         lockmgr(&dm_dev_mutex, LK_RELEASE);
209
210         return dmv;
211 }
212
213 int
214 dm_dev_create(dm_dev_t **dmvp, const char *name, const char *uuid, int flags)
215 {
216         dm_dev_t *dmv;
217         char name_buf[MAXPATHLEN];
218         int r, dm_minor;
219
220         if ((dmv = dm_dev_alloc()) == NULL)
221                 return ENOMEM;
222
223         if (uuid)
224                 strncpy(dmv->uuid, uuid, DM_UUID_LEN);
225         else
226                 dmv->uuid[0] = '\0';
227
228         if (name)
229                 strlcpy(dmv->name, name, DM_NAME_LEN);
230
231         dm_minor = devfs_clone_bitmap_get(&dm_minor_bitmap, 0);
232
233         dm_table_head_init(&dmv->table_head);
234
235         lockinit(&dmv->dev_mtx, "dmdev", 0, LK_CANRECURSE);
236         cv_init(&dmv->dev_cv, "dm_dev");
237
238         if (flags & DM_READONLY_FLAG)
239                 dmv->flags |= DM_READONLY_FLAG;
240
241         aprint_debug("Creating device dm/%s\n", name);
242         ksnprintf(name_buf, sizeof(name_buf), "mapper/%s", dmv->name);
243
244         devstat_add_entry(&dmv->stats, name, 0, DEV_BSIZE,
245             DEVSTAT_NO_ORDERED_TAGS,
246             DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER,
247             DEVSTAT_PRIORITY_DISK);
248
249         dmv->devt = disk_create_named(name_buf, dm_minor, dmv->diskp, &dm_ops);
250         reference_dev(dmv->devt);
251
252         dmv->minor = minor(dmv->devt);
253         udev_dict_set_cstr(dmv->devt, "subsystem", "disk");
254
255         if ((r = dm_dev_insert(dmv)) != 0)
256                 dm_dev_destroy(dmv);
257
258         /* Increment device counter After creating device */
259         ++dm_dev_counter; /* XXX: was atomic 64 */
260         *dmvp = dmv;
261
262         return r;
263 }
264
265 int
266 dm_dev_destroy(dm_dev_t *dmv)
267 {
268         int minor;
269
270         /* Destroy active table first.  */
271         dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
272
273         /* Destroy inactive table if exits, too. */
274         dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
275
276         dm_table_head_destroy(&dmv->table_head);
277
278         minor = dkunit(dmv->devt);
279         disk_destroy(dmv->diskp);
280         devstat_remove_entry(&dmv->stats);
281
282         release_dev(dmv->devt);
283         devfs_clone_bitmap_put(&dm_minor_bitmap, minor);
284
285         lockuninit(&dmv->dev_mtx);
286         cv_destroy(&dmv->dev_cv);
287
288         /* Destroy device */
289         (void)dm_dev_free(dmv);
290
291         /* Decrement device counter After removing device */
292         --dm_dev_counter; /* XXX: was atomic 64 */
293
294         return 0;
295 }
296
297 /*
298  * dm_detach is called to completely destroy & remove a dm disk device.
299  */
300 int
301 dm_dev_remove(dm_dev_t *dmv)
302 {
303         /* Remove device from list and wait for refcnt to drop to zero */
304         dm_dev_rem(dmv, NULL, NULL, -1);
305
306         /* Destroy and free the device */
307         dm_dev_destroy(dmv);
308
309         return 0;
310 }
311
312 int
313 dm_dev_remove_all(int gentle)
314 {
315         dm_dev_t *dmv, *dmv2;
316         int r;
317
318         r = 0;
319
320         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
321
322         TAILQ_FOREACH_MUTABLE(dmv, &dm_dev_list, next_devlist, dmv2) {
323                 if (gentle && dmv->is_open) {
324                         r = EBUSY;
325                         continue;
326                 }
327
328                 disable_dev(dmv);
329                 dm_dev_destroy(dmv);
330         }
331         lockmgr(&dm_dev_mutex, LK_RELEASE);
332
333         return r;
334 }
335
336 /*
337  * Allocate new device entry.
338  */
339 dm_dev_t *
340 dm_dev_alloc(void)
341 {
342         dm_dev_t *dmv;
343
344         dmv = kmalloc(sizeof(dm_dev_t), M_DM, M_WAITOK | M_ZERO);
345
346         if (dmv != NULL)
347                 dmv->diskp = kmalloc(sizeof(struct disk), M_DM, M_WAITOK | M_ZERO);
348
349         return dmv;
350 }
351 /*
352  * Freed device entry.
353  */
354 int
355 dm_dev_free(dm_dev_t * dmv)
356 {
357         KKASSERT(dmv != NULL);
358
359         if (dmv->diskp != NULL)
360                 (void) kfree(dmv->diskp, M_DM);
361
362         (void) kfree(dmv, M_DM);
363
364         return 0;
365 }
366
367 void
368 dm_dev_busy(dm_dev_t * dmv)
369 {
370         lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
371         dmv->ref_cnt++;
372         lockmgr(&dmv->dev_mtx, LK_RELEASE);
373 }
374
375 void
376 dm_dev_unbusy(dm_dev_t * dmv)
377 {
378         KKASSERT(dmv->ref_cnt != 0);
379
380         lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
381         if (--dmv->ref_cnt == 0)
382                 cv_broadcast(&dmv->dev_cv);
383         lockmgr(&dmv->dev_mtx, LK_RELEASE);
384 }
385 /*
386  * Return prop_array of dm_targer_list dictionaries.
387  */
388 prop_array_t
389 dm_dev_prop_list(void)
390 {
391         dm_dev_t *dmv;
392         prop_array_t dev_array;
393         prop_dictionary_t dev_dict;
394
395         dev_array = prop_array_create();
396
397         lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
398
399         TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
400                 dev_dict = prop_dictionary_create();
401
402                 prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name);
403                 prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor);
404
405                 prop_array_add(dev_array, dev_dict);
406                 prop_object_release(dev_dict);
407         }
408
409         lockmgr(&dm_dev_mutex, LK_RELEASE);
410         return dev_array;
411 }
412 /*
413  * Initialize global device mutex.
414  */
415 int
416 dm_dev_init(void)
417 {
418         TAILQ_INIT(&dm_dev_list);       /* initialize global dev list */
419         lockinit(&dm_dev_mutex, "dmdevlist", 0, LK_CANRECURSE);
420         devfs_clone_bitmap_init(&dm_minor_bitmap);
421         return 0;
422 }
423
424 /*
425  * Destroy all devices created in device-mapper. Remove all tables
426  * free all allocated memmory.
427  */
428 int
429 dm_dev_uninit(void)
430 {
431         /* Force removal of all devices */
432         dm_dev_remove_all(0);
433
434         lockuninit(&dm_dev_mutex);
435         return 0;
436 }