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/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/malloc.h> 44 #include <dev/disk/dm/dm.h> 45 46 #include "netbsd-dm.h" 47 48 extern struct dev_ops dm_ops; 49 50 static struct devfs_bitmap dm_minor_bitmap; 51 uint64_t dm_dev_counter; 52 53 static dm_dev_t *dm_dev_lookup_name(const char *); 54 static dm_dev_t *dm_dev_lookup_uuid(const char *); 55 static dm_dev_t *dm_dev_lookup_minor(int); 56 static int dm_dev_destroy(dm_dev_t *); 57 static dm_dev_t *dm_dev_alloc(const char *, const char *); 58 static int dm_dev_free(dm_dev_t *); 59 60 static TAILQ_HEAD(dm_dev_head, dm_dev) dm_dev_list; 61 62 static struct lock dm_dev_mutex; 63 64 static char dummy_uuid[DM_UUID_LEN]; 65 66 /* dm_dev_mutex must be held by caller before using disable_dev. */ 67 static void 68 disable_dev(dm_dev_t *dmv) 69 { 70 KKASSERT(lockstatus(&dm_dev_mutex, curthread) == LK_EXCLUSIVE); 71 72 TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist); 73 dm_dev_counter--; 74 75 lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE); 76 while (dmv->ref_cnt != 0) 77 cv_wait(&dmv->dev_cv, &dmv->dev_mtx); 78 lockmgr(&dmv->dev_mtx, LK_RELEASE); 79 } 80 81 static dm_dev_t * 82 _dm_dev_lookup(const char *name, const char *uuid, int minor) 83 { 84 dm_dev_t *dmv; 85 86 if (minor > 0) { 87 if ((dmv = dm_dev_lookup_minor(minor))) 88 return dmv; 89 } 90 if (name != NULL) { 91 if ((dmv = dm_dev_lookup_name(name))) 92 return dmv; 93 } 94 if (uuid != NULL) { 95 if ((dmv = dm_dev_lookup_uuid(uuid))) 96 return dmv; 97 } 98 99 return NULL; 100 } 101 102 /* 103 * Generic function used to lookup dm_dev_t. Calling with dm_dev_name 104 * and dm_dev_uuid NULL is allowed. 105 */ 106 dm_dev_t * 107 dm_dev_lookup(const char *name, const char *uuid, int minor) 108 { 109 dm_dev_t *dmv; 110 111 lockmgr(&dm_dev_mutex, LK_EXCLUSIVE); 112 113 dmv = _dm_dev_lookup(name, uuid, minor); 114 if (dmv) 115 dm_dev_busy(dmv); 116 117 lockmgr(&dm_dev_mutex, LK_RELEASE); 118 119 return dmv; 120 } 121 122 123 /* 124 * Lookup device with its minor number. 125 */ 126 static dm_dev_t * 127 dm_dev_lookup_minor(int dm_dev_minor) 128 { 129 dm_dev_t *dmv; 130 131 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 132 if (dm_dev_minor == dmv->minor) 133 return dmv; 134 } 135 136 return NULL; 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 * 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 * Insert new device to the global list of devices. 170 */ 171 int 172 dm_dev_insert(dm_dev_t *dev) 173 { 174 dm_dev_t *dmv; 175 int r; 176 177 dmv = NULL; 178 r = 0; 179 180 KKASSERT(dev != NULL); 181 lockmgr(&dm_dev_mutex, LK_EXCLUSIVE); 182 183 /* 184 * Ignore uuid lookup if dev->uuid is zero-filled. 185 */ 186 if (memcmp(dev->uuid, dummy_uuid, DM_UUID_LEN)) 187 dmv = dm_dev_lookup_uuid(dev->uuid); 188 189 if ((dmv == NULL) && 190 (_dm_dev_lookup(dev->name, NULL, dev->minor) == NULL)) { 191 TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist); 192 dm_dev_counter++; 193 } else { 194 KKASSERT(dmv != NULL); 195 r = EEXIST; 196 } 197 198 lockmgr(&dm_dev_mutex, LK_RELEASE); 199 return r; 200 } 201 202 #if 0 203 /* 204 * Remove device selected with dm_dev from global list of devices. 205 */ 206 dm_dev_t * 207 dm_dev_lookup_evict(const char *name, const char *uuid, int minor) 208 { 209 dm_dev_t *dmv; 210 211 lockmgr(&dm_dev_mutex, LK_EXCLUSIVE); 212 213 dmv = _dm_dev_lookup(name, uuid, minor); 214 if (dmv) 215 disable_dev(dmv); 216 217 lockmgr(&dm_dev_mutex, LK_RELEASE); 218 219 return dmv; 220 } 221 #endif 222 223 int 224 dm_dev_create(dm_dev_t **dmvp, const char *name, const char *uuid, int flags) 225 { 226 dm_dev_t *dmv; 227 char name_buf[MAXPATHLEN]; 228 int r, dm_minor; 229 230 if ((dmv = dm_dev_alloc(name, uuid)) == NULL) 231 return ENOMEM; 232 233 dm_minor = devfs_clone_bitmap_get(&dm_minor_bitmap, 0); 234 235 dm_table_head_init(&dmv->table_head); 236 237 lockinit(&dmv->dev_mtx, "dmdev", 0, LK_CANRECURSE); 238 cv_init(&dmv->dev_cv, "dm_dev"); 239 240 if (flags & DM_READONLY_FLAG) 241 dmv->flags |= DM_READONLY_FLAG; 242 243 dmdebug("Creating device dm/%s\n", name); 244 ksnprintf(name_buf, sizeof(name_buf), "mapper/%s", dmv->name); 245 246 devstat_add_entry(&dmv->stats, name, 0, DEV_BSIZE, 247 DEVSTAT_NO_ORDERED_TAGS, 248 DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 249 DEVSTAT_PRIORITY_DISK); 250 251 dmv->devt = disk_create_named(name_buf, dm_minor, dmv->diskp, &dm_ops); 252 reference_dev(dmv->devt); 253 254 /* Make sure the device are immediately available */ 255 sync_devs(); 256 257 dmv->devt->si_drv1 = dmv; 258 dmv->devt->si_drv2 = dmv->diskp; 259 260 dmv->minor = minor(dmv->devt); 261 udev_dict_set_cstr(dmv->devt, "subsystem", "disk"); 262 263 if ((r = dm_dev_insert(dmv)) != 0) 264 dm_dev_destroy(dmv); 265 266 *dmvp = dmv; 267 268 return r; 269 } 270 271 static 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 dm_dev_free(dmv); 296 297 return 0; 298 } 299 300 /* 301 * dm_dev_remove is called to completely destroy & remove a dm disk device. 302 */ 303 int 304 dm_dev_remove(dm_dev_t *dmv) 305 { 306 /* Remove device from list and wait for refcnt to drop to zero */ 307 lockmgr(&dm_dev_mutex, LK_EXCLUSIVE); 308 disable_dev(dmv); 309 lockmgr(&dm_dev_mutex, LK_RELEASE); 310 311 /* Destroy and free the device */ 312 dm_dev_destroy(dmv); 313 314 return 0; 315 } 316 317 int 318 dm_dev_remove_all(int gentle) 319 { 320 dm_dev_t *dmv, *dmv2; 321 int r; 322 323 r = 0; 324 325 lockmgr(&dm_dev_mutex, LK_EXCLUSIVE); 326 327 /* 328 * Process in reverse order so that it can deal with inter-depentent 329 * devices. 330 */ 331 TAILQ_FOREACH_REVERSE_MUTABLE(dmv, &dm_dev_list, dm_dev_head, 332 next_devlist, dmv2) { 333 if (gentle && dmv->is_open) { 334 r = EBUSY; 335 continue; 336 } 337 338 disable_dev(dmv); 339 dm_dev_destroy(dmv); 340 } 341 lockmgr(&dm_dev_mutex, LK_RELEASE); 342 343 return r; 344 } 345 346 /* 347 * Allocate new device entry. 348 */ 349 static dm_dev_t * 350 dm_dev_alloc(const char *name, const char*uuid) 351 { 352 dm_dev_t *dmv; 353 354 dmv = kmalloc(sizeof(*dmv), M_DM, M_WAITOK | M_ZERO); 355 if (dmv == NULL) 356 return NULL; 357 358 dmv->diskp = kmalloc(sizeof(*dmv->diskp), M_DM, M_WAITOK | M_ZERO); 359 if (dmv->diskp == NULL) { 360 kfree(dmv, M_DM); 361 return NULL; 362 } 363 364 if (name) 365 strlcpy(dmv->name, name, sizeof(dmv->name)); 366 if (uuid) 367 strncpy(dmv->uuid, uuid, sizeof(dmv->uuid)); 368 369 return dmv; 370 } 371 /* 372 * Freed device entry. 373 */ 374 static int 375 dm_dev_free(dm_dev_t *dmv) 376 { 377 KKASSERT(dmv != NULL); 378 379 if (dmv->diskp != NULL) 380 kfree(dmv->diskp, M_DM); 381 382 kfree(dmv, M_DM); 383 384 return 0; 385 } 386 387 void 388 dm_dev_busy(dm_dev_t *dmv) 389 { 390 lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE); 391 dmv->ref_cnt++; 392 lockmgr(&dmv->dev_mtx, LK_RELEASE); 393 } 394 395 void 396 dm_dev_unbusy(dm_dev_t *dmv) 397 { 398 KKASSERT(dmv->ref_cnt != 0); 399 400 lockmgr(&dmv->dev_mtx, LK_EXCLUSIVE); 401 if (--dmv->ref_cnt == 0) 402 cv_broadcast(&dmv->dev_cv); 403 lockmgr(&dmv->dev_mtx, LK_RELEASE); 404 } 405 /* 406 * Return prop_array of dm_targer_list dictionaries. 407 */ 408 prop_array_t 409 dm_dev_prop_list(void) 410 { 411 dm_dev_t *dmv; 412 prop_array_t dev_array; 413 prop_dictionary_t dev_dict; 414 415 dev_array = prop_array_create(); 416 417 lockmgr(&dm_dev_mutex, LK_EXCLUSIVE); 418 419 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 420 dev_dict = prop_dictionary_create(); 421 422 prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name); 423 prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor); 424 425 prop_array_add(dev_array, dev_dict); 426 prop_object_release(dev_dict); 427 } 428 429 lockmgr(&dm_dev_mutex, LK_RELEASE); 430 return dev_array; 431 } 432 /* 433 * Initialize global device mutex. 434 */ 435 int 436 dm_dev_init(void) 437 { 438 TAILQ_INIT(&dm_dev_list); /* initialize global dev list */ 439 lockinit(&dm_dev_mutex, "dmdevlist", 0, LK_CANRECURSE); 440 devfs_clone_bitmap_init(&dm_minor_bitmap); 441 442 memset(dummy_uuid, 0, sizeof(dummy_uuid)); 443 return 0; 444 } 445 446 /* 447 * Destroy all devices created in device-mapper. Remove all tables 448 * free all allocated memmory. 449 */ 450 int 451 dm_dev_uninit(void) 452 { 453 /* Force removal of all devices */ 454 dm_dev_remove_all(0); 455 456 lockuninit(&dm_dev_mutex); 457 return 0; 458 } 459