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