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