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