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 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 * 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 * 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 * 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 * 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 * 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 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 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 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 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 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 * 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 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 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 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 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 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 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