1 /* $NetBSD: dm_dev.c,v 1.8 2010/01/04 00:19:08 haad Exp $ */ 2 3 /* 4 * Copyright (c) 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Hamsik. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 35 #include <sys/disk.h> 36 #include <sys/disklabel.h> 37 #include <sys/ioctl.h> 38 #include <sys/ioccom.h> 39 #include <sys/kmem.h> 40 41 #include "netbsd-dm.h" 42 #include "dm.h" 43 44 static dm_dev_t *dm_dev_lookup_name(const char *); 45 static dm_dev_t *dm_dev_lookup_uuid(const char *); 46 static dm_dev_t *dm_dev_lookup_minor(int); 47 48 static struct dm_dev_head dm_dev_list = 49 TAILQ_HEAD_INITIALIZER(dm_dev_list); 50 51 kmutex_t dm_dev_mutex; 52 53 /* dm_dev_mutex must be holdby caller before using disable_dev. */ 54 __inline static void 55 disable_dev(dm_dev_t * dmv) 56 { 57 TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist); 58 mutex_enter(&dmv->dev_mtx); 59 mutex_exit(&dm_dev_mutex); 60 while (dmv->ref_cnt != 0) 61 cv_wait(&dmv->dev_cv, &dmv->dev_mtx); 62 mutex_exit(&dmv->dev_mtx); 63 } 64 /* 65 * Generic function used to lookup dm_dev_t. Calling with dm_dev_name 66 * and dm_dev_uuid NULL is allowed. 67 */ 68 dm_dev_t * 69 dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid, 70 int dm_dev_minor) 71 { 72 dm_dev_t *dmv; 73 74 dmv = NULL; 75 mutex_enter(&dm_dev_mutex); 76 77 /* KASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor 78 * > 0); */ 79 if (dm_dev_minor > 0) 80 if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) { 81 dm_dev_busy(dmv); 82 mutex_exit(&dm_dev_mutex); 83 return dmv; 84 } 85 if (dm_dev_name != NULL) 86 if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) { 87 dm_dev_busy(dmv); 88 mutex_exit(&dm_dev_mutex); 89 return dmv; 90 } 91 if (dm_dev_uuid != NULL) 92 if ((dmv = dm_dev_lookup_uuid(dm_dev_uuid)) != NULL) { 93 dm_dev_busy(dmv); 94 mutex_exit(&dm_dev_mutex); 95 return dmv; 96 } 97 mutex_exit(&dm_dev_mutex); 98 return NULL; 99 } 100 101 102 /* 103 * Lookup device with its minor number. 104 */ 105 static dm_dev_t * 106 dm_dev_lookup_minor(int dm_dev_minor) 107 { 108 dm_dev_t *dmv; 109 110 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 111 if (dm_dev_minor == dmv->minor) 112 return dmv; 113 } 114 115 return NULL; 116 } 117 /* 118 * Lookup device with it's device name. 119 */ 120 static dm_dev_t * 121 dm_dev_lookup_name(const char *dm_dev_name) 122 { 123 dm_dev_t *dmv; 124 int dlen; 125 int slen; 126 127 slen = strlen(dm_dev_name); 128 129 if (slen == 0) 130 return NULL; 131 132 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 133 134 dlen = strlen(dmv->name); 135 136 if (slen != dlen) 137 continue; 138 139 if (strncmp(dm_dev_name, dmv->name, slen) == 0) 140 return dmv; 141 } 142 143 return NULL; 144 } 145 /* 146 * Lookup device with it's device uuid. Used mostly by LVM2tools. 147 */ 148 static dm_dev_t * 149 dm_dev_lookup_uuid(const char *dm_dev_uuid) 150 { 151 dm_dev_t *dmv; 152 size_t len; 153 154 len = 0; 155 len = strlen(dm_dev_uuid); 156 157 if (len == 0) 158 return NULL; 159 160 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 161 162 if (strlen(dmv->uuid) != len) 163 continue; 164 165 if (strncmp(dm_dev_uuid, dmv->uuid, strlen(dmv->uuid)) == 0) 166 return dmv; 167 } 168 169 return NULL; 170 } 171 /* 172 * Insert new device to the global list of devices. 173 */ 174 int 175 dm_dev_insert(dm_dev_t * dev) 176 { 177 dm_dev_t *dmv; 178 int r; 179 180 dmv = NULL; 181 r = 0; 182 183 KASSERT(dev != NULL); 184 mutex_enter(&dm_dev_mutex); 185 if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) && 186 ((dmv = dm_dev_lookup_name(dev->name)) == NULL) && 187 ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)) { 188 189 TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist); 190 191 } else 192 r = EEXIST; 193 194 mutex_exit(&dm_dev_mutex); 195 return r; 196 } 197 #ifdef notyet 198 /* 199 * Lookup device with its minor number. 200 */ 201 int 202 dm_dev_test_minor(int dm_dev_minor) 203 { 204 dm_dev_t *dmv; 205 206 mutex_enter(&dm_dev_mutex); 207 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 208 if (dm_dev_minor == dmv->minor) { 209 mutex_exit(&dm_dev_mutex); 210 return 1; 211 } 212 } 213 mutex_exit(&dm_dev_mutex); 214 215 return 0; 216 } 217 #endif 218 219 /* 220 * dm_dev_lookup_devt look for selected device_t. We keep this routine 221 * outside of dm_dev_lookup because it is a temporally solution. 222 * 223 * TODO: This is a hack autoconf should be more flexible. 224 */ 225 dm_dev_t * 226 dm_dev_detach(device_t devt) 227 { 228 dm_dev_t *dmv; 229 230 mutex_enter(&dm_dev_mutex); 231 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 232 if (devt == dmv->devt) { 233 disable_dev(dmv); 234 return dmv; 235 } 236 } 237 mutex_exit(&dm_dev_mutex); 238 239 return NULL; 240 } 241 /* 242 * Remove device selected with dm_dev from global list of devices. 243 */ 244 dm_dev_t * 245 dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid, 246 int dm_dev_minor) 247 { 248 dm_dev_t *dmv; 249 dmv = NULL; 250 251 mutex_enter(&dm_dev_mutex); 252 253 if (dm_dev_minor > 0) 254 if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) { 255 disable_dev(dmv); 256 return dmv; 257 } 258 if (dm_dev_name != NULL) 259 if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) { 260 disable_dev(dmv); 261 return dmv; 262 } 263 if (dm_dev_uuid != NULL) 264 if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL) { 265 disable_dev(dmv); 266 return dmv; 267 } 268 mutex_exit(&dm_dev_mutex); 269 270 return NULL; 271 } 272 /* 273 * Destroy all devices created in device-mapper. Remove all tables 274 * free all allocated memmory. 275 */ 276 int 277 dm_dev_destroy(void) 278 { 279 dm_dev_t *dmv; 280 mutex_enter(&dm_dev_mutex); 281 282 while (TAILQ_FIRST(&dm_dev_list) != NULL) { 283 284 dmv = TAILQ_FIRST(&dm_dev_list); 285 286 TAILQ_REMOVE(&dm_dev_list, TAILQ_FIRST(&dm_dev_list), 287 next_devlist); 288 289 mutex_enter(&dmv->dev_mtx); 290 291 while (dmv->ref_cnt != 0) 292 cv_wait(&dmv->dev_cv, &dmv->dev_mtx); 293 294 /* Destroy active table first. */ 295 dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); 296 297 /* Destroy inactive table if exits, too. */ 298 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 299 300 dm_table_head_destroy(&dmv->table_head); 301 302 mutex_exit(&dmv->dev_mtx); 303 mutex_destroy(&dmv->dev_mtx); 304 cv_destroy(&dmv->dev_cv); 305 306 (void) kmem_free(dmv, sizeof(dm_dev_t)); 307 } 308 mutex_exit(&dm_dev_mutex); 309 310 mutex_destroy(&dm_dev_mutex); 311 return 0; 312 } 313 /* 314 * Allocate new device entry. 315 */ 316 dm_dev_t * 317 dm_dev_alloc(void) 318 { 319 dm_dev_t *dmv; 320 321 dmv = kmem_zalloc(sizeof(dm_dev_t), KM_SLEEP); 322 323 if (dmv != NULL) 324 dmv->diskp = kmem_zalloc(sizeof(struct disk), KM_SLEEP); 325 326 return dmv; 327 } 328 /* 329 * Freed device entry. 330 */ 331 int 332 dm_dev_free(dm_dev_t * dmv) 333 { 334 KASSERT(dmv != NULL); 335 336 mutex_destroy(&dmv->dev_mtx); 337 mutex_destroy(&dmv->diskp_mtx); 338 cv_destroy(&dmv->dev_cv); 339 340 if (dmv->diskp != NULL) 341 (void) kmem_free(dmv->diskp, sizeof(struct disk)); 342 343 (void) kmem_free(dmv, sizeof(dm_dev_t)); 344 345 return 0; 346 } 347 348 void 349 dm_dev_busy(dm_dev_t * dmv) 350 { 351 mutex_enter(&dmv->dev_mtx); 352 dmv->ref_cnt++; 353 mutex_exit(&dmv->dev_mtx); 354 } 355 356 void 357 dm_dev_unbusy(dm_dev_t * dmv) 358 { 359 KASSERT(dmv->ref_cnt != 0); 360 361 mutex_enter(&dmv->dev_mtx); 362 if (--dmv->ref_cnt == 0) 363 cv_broadcast(&dmv->dev_cv); 364 mutex_exit(&dmv->dev_mtx); 365 } 366 /* 367 * Return prop_array of dm_targer_list dictionaries. 368 */ 369 prop_array_t 370 dm_dev_prop_list(void) 371 { 372 dm_dev_t *dmv; 373 prop_array_t dev_array; 374 prop_dictionary_t dev_dict; 375 376 dev_array = prop_array_create(); 377 378 mutex_enter(&dm_dev_mutex); 379 380 TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { 381 dev_dict = prop_dictionary_create(); 382 383 prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name); 384 prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor); 385 386 prop_array_add(dev_array, dev_dict); 387 prop_object_release(dev_dict); 388 } 389 390 mutex_exit(&dm_dev_mutex); 391 return dev_array; 392 } 393 /* 394 * Initialize global device mutex. 395 */ 396 int 397 dm_dev_init(void) 398 { 399 TAILQ_INIT(&dm_dev_list); /* initialize global dev list */ 400 mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE); 401 return 0; 402 } 403