1 1.21 andvar /* $NetBSD: dm_table.c,v 1.21 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.8 christos #include <sys/cdefs.h> 32 1.21 andvar __KERNEL_RCSID(0, "$NetBSD: dm_table.c,v 1.21 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.2 haad #include <sys/kmem.h> 37 1.2 haad 38 1.2 haad #include "dm.h" 39 1.2 haad 40 1.2 haad /* 41 1.2 haad * There are two types of users of this interface: 42 1.5 haad * 43 1.2 haad * a) Readers such as 44 1.2 haad * dmstrategy, dmgetdisklabel, dmsize, dm_dev_status_ioctl, 45 1.2 haad * dm_table_deps_ioctl, dm_table_status_ioctl, dm_table_reload_ioctl 46 1.2 haad * 47 1.2 haad * b) Writers such as 48 1.2 haad * dm_dev_remove_ioctl, dm_dev_resume_ioctl, dm_table_clear_ioctl 49 1.2 haad * 50 1.2 haad * Writers can work with table_head only when there are no readers. I 51 1.2 haad * use reference counting on io_cnt. 52 1.2 haad * 53 1.2 haad */ 54 1.2 haad 55 1.5 haad static int dm_table_busy(dm_table_head_t *, uint8_t); 56 1.2 haad static void dm_table_unbusy(dm_table_head_t *); 57 1.17 tkusumi static void dm_table_free_deps(dm_table_entry_t *); 58 1.17 tkusumi 59 1.2 haad 60 1.2 haad /* 61 1.2 haad * Function to increment table user reference counter. Return id 62 1.2 haad * of table_id table. 63 1.2 haad * DM_TABLE_ACTIVE will return active table id. 64 1.2 haad * DM_TABLE_INACTIVE will return inactive table id. 65 1.2 haad */ 66 1.2 haad static int 67 1.12 tkusumi dm_table_busy(dm_table_head_t *head, uint8_t table_id) 68 1.2 haad { 69 1.2 haad uint8_t id; 70 1.2 haad 71 1.2 haad mutex_enter(&head->table_mtx); 72 1.2 haad 73 1.2 haad if (table_id == DM_TABLE_ACTIVE) 74 1.2 haad id = head->cur_active_table; 75 1.2 haad else 76 1.2 haad id = 1 - head->cur_active_table; 77 1.2 haad 78 1.2 haad head->io_cnt++; 79 1.2 haad 80 1.2 haad mutex_exit(&head->table_mtx); 81 1.2 haad return id; 82 1.2 haad } 83 1.8 christos 84 1.2 haad /* 85 1.2 haad * Function release table lock and eventually wakeup all waiters. 86 1.2 haad */ 87 1.2 haad static void 88 1.12 tkusumi dm_table_unbusy(dm_table_head_t *head) 89 1.2 haad { 90 1.16 tkusumi 91 1.2 haad KASSERT(head->io_cnt != 0); 92 1.2 haad 93 1.2 haad mutex_enter(&head->table_mtx); 94 1.5 haad 95 1.2 haad if (--head->io_cnt == 0) 96 1.2 haad cv_broadcast(&head->table_cv); 97 1.5 haad 98 1.2 haad mutex_exit(&head->table_mtx); 99 1.2 haad } 100 1.8 christos 101 1.2 haad /* 102 1.2 haad * Return current active table to caller, increment io_cnt reference counter. 103 1.2 haad */ 104 1.2 haad dm_table_t * 105 1.12 tkusumi dm_table_get_entry(dm_table_head_t *head, uint8_t table_id) 106 1.2 haad { 107 1.2 haad uint8_t id; 108 1.2 haad 109 1.2 haad id = dm_table_busy(head, table_id); 110 1.5 haad 111 1.2 haad return &head->tables[id]; 112 1.2 haad } 113 1.2 haad /* 114 1.2 haad * Decrement io reference counter and wake up all callers, with table_head cv. 115 1.2 haad */ 116 1.2 haad void 117 1.12 tkusumi dm_table_release(dm_table_head_t *head, uint8_t table_id) 118 1.2 haad { 119 1.16 tkusumi 120 1.2 haad dm_table_unbusy(head); 121 1.2 haad } 122 1.8 christos 123 1.2 haad /* 124 1.2 haad * Switch table from inactive to active mode. Have to wait until io_cnt is 0. 125 1.2 haad */ 126 1.2 haad void 127 1.12 tkusumi dm_table_switch_tables(dm_table_head_t *head) 128 1.2 haad { 129 1.16 tkusumi 130 1.2 haad mutex_enter(&head->table_mtx); 131 1.2 haad 132 1.2 haad while (head->io_cnt != 0) 133 1.2 haad cv_wait(&head->table_cv, &head->table_mtx); 134 1.2 haad 135 1.2 haad head->cur_active_table = 1 - head->cur_active_table; 136 1.2 haad 137 1.2 haad mutex_exit(&head->table_mtx); 138 1.2 haad } 139 1.8 christos 140 1.2 haad /* 141 1.2 haad * Destroy all table data. This function can run when there are no 142 1.2 haad * readers on table lists. 143 1.2 haad * 144 1.21 andvar * XXX Is it ok to call kmem_free and potentially VOP_CLOSE with held mutex ?xs 145 1.2 haad */ 146 1.2 haad int 147 1.12 tkusumi dm_table_destroy(dm_table_head_t *head, uint8_t table_id) 148 1.2 haad { 149 1.5 haad dm_table_t *tbl; 150 1.2 haad dm_table_entry_t *table_en; 151 1.2 haad uint8_t id; 152 1.2 haad 153 1.2 haad mutex_enter(&head->table_mtx); 154 1.2 haad 155 1.4 joerg aprint_debug("dm_Table_destroy called with %d--%d\n", table_id, head->io_cnt); 156 1.4 joerg 157 1.2 haad while (head->io_cnt != 0) 158 1.2 haad cv_wait(&head->table_cv, &head->table_mtx); 159 1.2 haad 160 1.2 haad if (table_id == DM_TABLE_ACTIVE) 161 1.2 haad id = head->cur_active_table; 162 1.2 haad else 163 1.2 haad id = 1 - head->cur_active_table; 164 1.5 haad 165 1.2 haad tbl = &head->tables[id]; 166 1.5 haad 167 1.11 tkusumi while ((table_en = SLIST_FIRST(tbl)) != NULL) { 168 1.11 tkusumi SLIST_REMOVE(tbl, table_en, dm_table_entry, next); 169 1.10 tkusumi if (table_en->target->destroy(table_en) == 0) 170 1.10 tkusumi table_en->target_config = NULL; 171 1.17 tkusumi dm_table_free_deps(table_en); 172 1.2 haad kmem_free(table_en, sizeof(*table_en)); 173 1.2 haad } 174 1.11 tkusumi KASSERT(SLIST_EMPTY(tbl)); 175 1.2 haad 176 1.2 haad mutex_exit(&head->table_mtx); 177 1.5 haad 178 1.2 haad return 0; 179 1.2 haad } 180 1.8 christos 181 1.2 haad /* 182 1.2 haad * Return length of active table in device. 183 1.2 haad */ 184 1.19 tkusumi static uint64_t 185 1.12 tkusumi dm_table_size_impl(dm_table_head_t *head, int table) 186 1.2 haad { 187 1.5 haad dm_table_t *tbl; 188 1.2 haad dm_table_entry_t *table_en; 189 1.2 haad uint64_t length; 190 1.2 haad uint8_t id; 191 1.5 haad 192 1.2 haad length = 0; 193 1.2 haad 194 1.7 ahoka id = dm_table_busy(head, table); 195 1.5 haad 196 1.2 haad /* Select active table */ 197 1.2 haad tbl = &head->tables[id]; 198 1.5 haad 199 1.2 haad /* 200 1.2 haad * Find out what tables I want to select. 201 1.2 haad * if length => rawblkno then we should used that table. 202 1.2 haad */ 203 1.2 haad SLIST_FOREACH(table_en, tbl, next) 204 1.9 tkusumi length += table_en->length; 205 1.2 haad 206 1.2 haad dm_table_unbusy(head); 207 1.5 haad 208 1.3 jakllsch return length; 209 1.2 haad } 210 1.7 ahoka 211 1.7 ahoka /* 212 1.7 ahoka * Return length of active table in device. 213 1.7 ahoka */ 214 1.7 ahoka uint64_t 215 1.12 tkusumi dm_table_size(dm_table_head_t *head) 216 1.7 ahoka { 217 1.16 tkusumi 218 1.7 ahoka return dm_table_size_impl(head, DM_TABLE_ACTIVE); 219 1.7 ahoka } 220 1.7 ahoka 221 1.7 ahoka /* 222 1.7 ahoka * Return length of active table in device. 223 1.7 ahoka */ 224 1.7 ahoka uint64_t 225 1.12 tkusumi dm_inactive_table_size(dm_table_head_t *head) 226 1.7 ahoka { 227 1.16 tkusumi 228 1.7 ahoka return dm_table_size_impl(head, DM_TABLE_INACTIVE); 229 1.7 ahoka } 230 1.7 ahoka 231 1.2 haad /* 232 1.6 mlelstv * Return combined disk geometry 233 1.6 mlelstv */ 234 1.6 mlelstv void 235 1.16 tkusumi dm_table_disksize(dm_table_head_t *head, uint64_t *numsecp, 236 1.16 tkusumi unsigned int *secsizep) 237 1.6 mlelstv { 238 1.6 mlelstv dm_table_t *tbl; 239 1.6 mlelstv dm_table_entry_t *table_en; 240 1.6 mlelstv uint64_t length; 241 1.14 tkusumi unsigned int secsize, tsecsize; 242 1.6 mlelstv uint8_t id; 243 1.6 mlelstv 244 1.6 mlelstv length = 0; 245 1.6 mlelstv 246 1.6 mlelstv id = dm_table_busy(head, DM_TABLE_ACTIVE); 247 1.6 mlelstv 248 1.6 mlelstv /* Select active table */ 249 1.6 mlelstv tbl = &head->tables[id]; 250 1.6 mlelstv 251 1.6 mlelstv /* 252 1.6 mlelstv * Find out what tables I want to select. 253 1.6 mlelstv * if length => rawblkno then we should used that table. 254 1.6 mlelstv */ 255 1.6 mlelstv secsize = 0; 256 1.6 mlelstv SLIST_FOREACH(table_en, tbl, next) { 257 1.9 tkusumi length += table_en->length; 258 1.15 tkusumi if (table_en->target->secsize) 259 1.15 tkusumi table_en->target->secsize(table_en, &tsecsize); 260 1.15 tkusumi else 261 1.15 tkusumi tsecsize = 0; 262 1.9 tkusumi if (secsize < tsecsize) 263 1.9 tkusumi secsize = tsecsize; 264 1.6 mlelstv } 265 1.18 tkusumi 266 1.18 tkusumi if (numsecp) 267 1.18 tkusumi *numsecp = secsize > 0 ? dbtob(length) / secsize : 0; 268 1.18 tkusumi if (secsizep) 269 1.18 tkusumi *secsizep = secsize; 270 1.6 mlelstv 271 1.6 mlelstv dm_table_unbusy(head); 272 1.6 mlelstv } 273 1.8 christos 274 1.6 mlelstv /* 275 1.2 haad * Return > 0 if table is at least one table entry (returns number of entries) 276 1.2 haad * and return 0 if there is not. Target count returned from this function 277 1.21 andvar * doesn't need to be true when userspace user receives it (after return 278 1.21 andvar * there can be dm_dev_resume_ioctl), therefore this is only informative. 279 1.2 haad */ 280 1.2 haad int 281 1.12 tkusumi dm_table_get_target_count(dm_table_head_t *head, uint8_t table_id) 282 1.5 haad { 283 1.2 haad dm_table_entry_t *table_en; 284 1.2 haad dm_table_t *tbl; 285 1.2 haad uint32_t target_count; 286 1.2 haad uint8_t id; 287 1.2 haad 288 1.2 haad target_count = 0; 289 1.2 haad 290 1.2 haad id = dm_table_busy(head, table_id); 291 1.2 haad tbl = &head->tables[id]; 292 1.2 haad 293 1.2 haad SLIST_FOREACH(table_en, tbl, next) 294 1.9 tkusumi target_count++; 295 1.2 haad 296 1.2 haad dm_table_unbusy(head); 297 1.5 haad 298 1.2 haad return target_count; 299 1.2 haad } 300 1.2 haad 301 1.2 haad /* 302 1.2 haad * Initialize table_head structures, I'm trying to keep this structure as 303 1.2 haad * opaque as possible. 304 1.2 haad */ 305 1.2 haad void 306 1.12 tkusumi dm_table_head_init(dm_table_head_t *head) 307 1.2 haad { 308 1.16 tkusumi 309 1.2 haad head->cur_active_table = 0; 310 1.2 haad head->io_cnt = 0; 311 1.5 haad 312 1.2 haad /* Initialize tables. */ 313 1.2 haad SLIST_INIT(&head->tables[0]); 314 1.2 haad SLIST_INIT(&head->tables[1]); 315 1.2 haad 316 1.2 haad mutex_init(&head->table_mtx, MUTEX_DEFAULT, IPL_NONE); 317 1.2 haad cv_init(&head->table_cv, "dm_io"); 318 1.2 haad } 319 1.8 christos 320 1.2 haad /* 321 1.2 haad * Destroy all variables in table_head 322 1.2 haad */ 323 1.2 haad void 324 1.12 tkusumi dm_table_head_destroy(dm_table_head_t *head) 325 1.2 haad { 326 1.16 tkusumi 327 1.2 haad KASSERT(!mutex_owned(&head->table_mtx)); 328 1.2 haad KASSERT(!cv_has_waiters(&head->table_cv)); 329 1.20 andvar /* tables don't exist when I call this routine, therefore it 330 1.5 haad * doesn't make sense to have io_cnt != 0 */ 331 1.5 haad KASSERT(head->io_cnt == 0); 332 1.5 haad 333 1.2 haad cv_destroy(&head->table_cv); 334 1.2 haad mutex_destroy(&head->table_mtx); 335 1.2 haad } 336 1.17 tkusumi 337 1.17 tkusumi int 338 1.17 tkusumi dm_table_add_deps(dm_table_entry_t *table_en, dm_pdev_t *pdev) 339 1.17 tkusumi { 340 1.17 tkusumi dm_table_head_t *head; 341 1.17 tkusumi dm_mapping_t *map; 342 1.17 tkusumi 343 1.17 tkusumi if (!pdev) 344 1.17 tkusumi return -1; 345 1.17 tkusumi 346 1.17 tkusumi head = &table_en->dm_dev->table_head; 347 1.17 tkusumi mutex_enter(&head->table_mtx); 348 1.17 tkusumi 349 1.17 tkusumi TAILQ_FOREACH(map, &table_en->pdev_maps, next) { 350 1.17 tkusumi if (map->data.pdev->pdev_vnode->v_rdev == 351 1.17 tkusumi pdev->pdev_vnode->v_rdev) { 352 1.17 tkusumi mutex_exit(&head->table_mtx); 353 1.17 tkusumi return -1; 354 1.17 tkusumi } 355 1.17 tkusumi } 356 1.17 tkusumi 357 1.17 tkusumi map = kmem_alloc(sizeof(*map), KM_SLEEP); 358 1.17 tkusumi map->data.pdev = pdev; 359 1.17 tkusumi aprint_debug("%s: %s\n", __func__, pdev->name); 360 1.17 tkusumi TAILQ_INSERT_TAIL(&table_en->pdev_maps, map, next); 361 1.17 tkusumi 362 1.17 tkusumi mutex_exit(&head->table_mtx); 363 1.17 tkusumi 364 1.17 tkusumi return 0; 365 1.17 tkusumi } 366 1.17 tkusumi 367 1.17 tkusumi /* caller must hold ->table_mtx */ 368 1.17 tkusumi static void 369 1.17 tkusumi dm_table_free_deps(dm_table_entry_t *table_en) 370 1.17 tkusumi { 371 1.17 tkusumi dm_mapping_t *map; 372 1.17 tkusumi 373 1.17 tkusumi while ((map = TAILQ_FIRST(&table_en->pdev_maps)) != NULL) { 374 1.17 tkusumi TAILQ_REMOVE(&table_en->pdev_maps, map, next); 375 1.17 tkusumi aprint_debug("%s: %s\n", __func__, map->data.pdev->name); 376 1.17 tkusumi kmem_free(map, sizeof(*map)); 377 1.17 tkusumi } 378 1.17 tkusumi KASSERT(TAILQ_EMPTY(&table_en->pdev_maps)); 379 1.17 tkusumi } 380