xref: /dragonfly/sys/dev/disk/dm/dm_dev.c (revision d4ef6694)
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/types.h>
34 #include <sys/param.h>
35 #include <machine/thread.h>
36 #include <sys/thread2.h>
37 
38 #include <sys/disk.h>
39 #include <sys/disklabel.h>
40 #include <sys/devicestat.h>
41 #include <sys/device.h>
42 #include <sys/udev.h>
43 #include <sys/devfs.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 	/* Make sure the device are immediately available */
253 	sync_devs();
254 
255 	dmv->devt->si_drv1 = dmv;
256 	dmv->devt->si_drv2 = dmv->diskp;
257 
258 	dmv->minor = minor(dmv->devt);
259 	udev_dict_set_cstr(dmv->devt, "subsystem", "disk");
260 
261 	if ((r = dm_dev_insert(dmv)) != 0)
262 		dm_dev_destroy(dmv);
263 
264 	/* Increment device counter After creating device */
265 	++dm_dev_counter; /* XXX: was atomic 64 */
266 	*dmvp = dmv;
267 
268 	return r;
269 }
270 
271 int
272 dm_dev_destroy(dm_dev_t *dmv)
273 {
274 	int minor;
275 
276 	/* Destroy active table first.  */
277 	dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
278 
279 	/* Destroy inactive table if exits, too. */
280 	dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
281 
282 	dm_table_head_destroy(&dmv->table_head);
283 
284 	minor = dkunit(dmv->devt);
285 	disk_destroy(dmv->diskp);
286 	devstat_remove_entry(&dmv->stats);
287 
288 	release_dev(dmv->devt);
289 	devfs_clone_bitmap_put(&dm_minor_bitmap, minor);
290 
291 	lockuninit(&dmv->dev_mtx);
292 	cv_destroy(&dmv->dev_cv);
293 
294 	/* Destroy device */
295 	(void)dm_dev_free(dmv);
296 
297 	/* Decrement device counter After removing device */
298 	--dm_dev_counter; /* XXX: was atomic 64 */
299 
300 	return 0;
301 }
302 
303 /*
304  * dm_detach is called to completely destroy & remove a dm disk device.
305  */
306 int
307 dm_dev_remove(dm_dev_t *dmv)
308 {
309 	/* Remove device from list and wait for refcnt to drop to zero */
310 	dm_dev_rem(dmv, NULL, NULL, -1);
311 
312 	/* Destroy and free the device */
313 	dm_dev_destroy(dmv);
314 
315 	return 0;
316 }
317 
318 int
319 dm_dev_remove_all(int gentle)
320 {
321 	dm_dev_t *dmv, *dmv2;
322 	int r;
323 
324 	r = 0;
325 
326 	lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
327 
328 	/*
329 	 * Process in reverse order so that it can deal with inter-depentent
330 	 * devices.
331 	 */
332 	TAILQ_FOREACH_REVERSE_MUTABLE(dmv, &dm_dev_list, dm_dev_head,
333 	    next_devlist, dmv2) {
334 		if (gentle && dmv->is_open) {
335 			r = EBUSY;
336 			continue;
337 		}
338 
339 		disable_dev(dmv);
340 		dm_dev_destroy(dmv);
341 	}
342 	lockmgr(&dm_dev_mutex, LK_RELEASE);
343 
344 	return r;
345 }
346 
347 /*
348  * Allocate new device entry.
349  */
350 dm_dev_t *
351 dm_dev_alloc(void)
352 {
353 	dm_dev_t *dmv;
354 
355 	dmv = kmalloc(sizeof(dm_dev_t), M_DM, M_WAITOK | M_ZERO);
356 
357 	if (dmv != NULL)
358 		dmv->diskp = kmalloc(sizeof(struct disk), M_DM, M_WAITOK | M_ZERO);
359 
360 	return dmv;
361 }
362 /*
363  * Freed device entry.
364  */
365 int
366 dm_dev_free(dm_dev_t * dmv)
367 {
368 	KKASSERT(dmv != NULL);
369 
370 	if (dmv->diskp != NULL)
371 		(void) kfree(dmv->diskp, M_DM);
372 
373 	(void) kfree(dmv, M_DM);
374 
375 	return 0;
376 }
377 
378 void
379 dm_dev_busy(dm_dev_t * dmv)
380 {
381 	lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
382 	dmv->ref_cnt++;
383 	lockmgr(&dmv->dev_mtx, LK_RELEASE);
384 }
385 
386 void
387 dm_dev_unbusy(dm_dev_t * dmv)
388 {
389 	KKASSERT(dmv->ref_cnt != 0);
390 
391 	lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE);
392 	if (--dmv->ref_cnt == 0)
393 		cv_broadcast(&dmv->dev_cv);
394 	lockmgr(&dmv->dev_mtx, LK_RELEASE);
395 }
396 /*
397  * Return prop_array of dm_targer_list dictionaries.
398  */
399 prop_array_t
400 dm_dev_prop_list(void)
401 {
402 	dm_dev_t *dmv;
403 	prop_array_t dev_array;
404 	prop_dictionary_t dev_dict;
405 
406 	dev_array = prop_array_create();
407 
408 	lockmgr(&dm_dev_mutex, LK_EXCLUSIVE);
409 
410 	TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
411 		dev_dict = prop_dictionary_create();
412 
413 		prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name);
414 		prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor);
415 
416 		prop_array_add(dev_array, dev_dict);
417 		prop_object_release(dev_dict);
418 	}
419 
420 	lockmgr(&dm_dev_mutex, LK_RELEASE);
421 	return dev_array;
422 }
423 /*
424  * Initialize global device mutex.
425  */
426 int
427 dm_dev_init(void)
428 {
429 	TAILQ_INIT(&dm_dev_list);	/* initialize global dev list */
430 	lockinit(&dm_dev_mutex, "dmdevlist", 0, LK_CANRECURSE);
431 	devfs_clone_bitmap_init(&dm_minor_bitmap);
432 	return 0;
433 }
434 
435 /*
436  * Destroy all devices created in device-mapper. Remove all tables
437  * free all allocated memmory.
438  */
439 int
440 dm_dev_uninit(void)
441 {
442 	/* Force removal of all devices */
443 	dm_dev_remove_all(0);
444 
445 	lockuninit(&dm_dev_mutex);
446 	return 0;
447 }
448