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