1 /* $NetBSD: dm_table.c,v 1.8 2018/01/05 14:22:26 christos 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.8 2018/01/05 14:22:26 christos Exp $"); 33 34 #include <sys/types.h> 35 #include <sys/param.h> 36 37 #include <sys/kmem.h> 38 39 #include "dm.h" 40 41 /* 42 * There are two types of users of this interface: 43 * 44 * a) Readers such as 45 * dmstrategy, dmgetdisklabel, dmsize, dm_dev_status_ioctl, 46 * dm_table_deps_ioctl, dm_table_status_ioctl, dm_table_reload_ioctl 47 * 48 * b) Writers such as 49 * dm_dev_remove_ioctl, dm_dev_resume_ioctl, dm_table_clear_ioctl 50 * 51 * Writers can work with table_head only when there are no readers. I 52 * use reference counting on io_cnt. 53 * 54 */ 55 56 static int dm_table_busy(dm_table_head_t *, uint8_t); 57 static void dm_table_unbusy(dm_table_head_t *); 58 59 /* 60 * Function to increment table user reference counter. Return id 61 * of table_id table. 62 * DM_TABLE_ACTIVE will return active table id. 63 * DM_TABLE_INACTIVE will return inactive table id. 64 */ 65 static int 66 dm_table_busy(dm_table_head_t * head, uint8_t table_id) 67 { 68 uint8_t id; 69 70 id = 0; 71 72 mutex_enter(&head->table_mtx); 73 74 if (table_id == DM_TABLE_ACTIVE) 75 id = head->cur_active_table; 76 else 77 id = 1 - head->cur_active_table; 78 79 head->io_cnt++; 80 81 mutex_exit(&head->table_mtx); 82 return id; 83 } 84 85 /* 86 * Function release table lock and eventually wakeup all waiters. 87 */ 88 static void 89 dm_table_unbusy(dm_table_head_t * head) 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 dm_table_unbusy(head); 120 } 121 122 /* 123 * Switch table from inactive to active mode. Have to wait until io_cnt is 0. 124 */ 125 void 126 dm_table_switch_tables(dm_table_head_t * head) 127 { 128 mutex_enter(&head->table_mtx); 129 130 while (head->io_cnt != 0) 131 cv_wait(&head->table_cv, &head->table_mtx); 132 133 head->cur_active_table = 1 - head->cur_active_table; 134 135 mutex_exit(&head->table_mtx); 136 } 137 138 /* 139 * Destroy all table data. This function can run when there are no 140 * readers on table lists. 141 * 142 * XXX Is it ok to call kmem_free and potentialy VOP_CLOSE with held mutex ?xs 143 */ 144 int 145 dm_table_destroy(dm_table_head_t * head, uint8_t table_id) 146 { 147 dm_table_t *tbl; 148 dm_table_entry_t *table_en; 149 uint8_t id; 150 151 mutex_enter(&head->table_mtx); 152 153 aprint_debug("dm_Table_destroy called with %d--%d\n", table_id, head->io_cnt); 154 155 while (head->io_cnt != 0) 156 cv_wait(&head->table_cv, &head->table_mtx); 157 158 if (table_id == DM_TABLE_ACTIVE) 159 id = head->cur_active_table; 160 else 161 id = 1 - head->cur_active_table; 162 163 tbl = &head->tables[id]; 164 165 while (!SLIST_EMPTY(tbl)) { /* List Deletion. */ 166 table_en = SLIST_FIRST(tbl); 167 /* 168 * Remove target specific config data. After successfull 169 * call table_en->target_config must be set to NULL. 170 */ 171 table_en->target->destroy(table_en); 172 173 SLIST_REMOVE_HEAD(tbl, next); 174 175 kmem_free(table_en, sizeof(*table_en)); 176 } 177 178 mutex_exit(&head->table_mtx); 179 180 return 0; 181 } 182 183 /* 184 * Return length of active table in device. 185 */ 186 static inline uint64_t 187 dm_table_size_impl(dm_table_head_t * head, int table) 188 { 189 dm_table_t *tbl; 190 dm_table_entry_t *table_en; 191 uint64_t length; 192 uint8_t id; 193 194 length = 0; 195 196 id = dm_table_busy(head, table); 197 198 /* Select active table */ 199 tbl = &head->tables[id]; 200 201 /* 202 * Find out what tables I want to select. 203 * if length => rawblkno then we should used that table. 204 */ 205 SLIST_FOREACH(table_en, tbl, next) 206 length += table_en->length; 207 208 dm_table_unbusy(head); 209 210 return length; 211 } 212 213 /* 214 * Return length of active table in device. 215 */ 216 uint64_t 217 dm_table_size(dm_table_head_t * head) 218 { 219 return dm_table_size_impl(head, DM_TABLE_ACTIVE); 220 } 221 222 /* 223 * Return length of active table in device. 224 */ 225 uint64_t 226 dm_inactive_table_size(dm_table_head_t * head) 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, unsigned *secsizep) 236 { 237 dm_table_t *tbl; 238 dm_table_entry_t *table_en; 239 uint64_t length; 240 unsigned secsize, tsecsize; 241 uint8_t id; 242 243 length = 0; 244 245 id = dm_table_busy(head, DM_TABLE_ACTIVE); 246 247 /* Select active table */ 248 tbl = &head->tables[id]; 249 250 /* 251 * Find out what tables I want to select. 252 * if length => rawblkno then we should used that table. 253 */ 254 secsize = 0; 255 SLIST_FOREACH(table_en, tbl, next) { 256 length += table_en->length; 257 (void)table_en->target->secsize(table_en, &tsecsize); 258 if (secsize < tsecsize) 259 secsize = tsecsize; 260 } 261 *numsecp = secsize > 0 ? dbtob(length) / secsize : 0; 262 *secsizep = secsize; 263 264 dm_table_unbusy(head); 265 } 266 267 /* 268 * Return > 0 if table is at least one table entry (returns number of entries) 269 * and return 0 if there is not. Target count returned from this function 270 * doesn't need to be true when userspace user receive it (after return 271 * there can be dm_dev_resume_ioctl), therfore this isonly informative. 272 */ 273 int 274 dm_table_get_target_count(dm_table_head_t * head, uint8_t table_id) 275 { 276 dm_table_entry_t *table_en; 277 dm_table_t *tbl; 278 uint32_t target_count; 279 uint8_t id; 280 281 target_count = 0; 282 283 id = dm_table_busy(head, table_id); 284 285 tbl = &head->tables[id]; 286 287 SLIST_FOREACH(table_en, tbl, next) 288 target_count++; 289 290 dm_table_unbusy(head); 291 292 return target_count; 293 } 294 295 296 /* 297 * Initialize table_head structures, I'm trying to keep this structure as 298 * opaque as possible. 299 */ 300 void 301 dm_table_head_init(dm_table_head_t * head) 302 { 303 head->cur_active_table = 0; 304 head->io_cnt = 0; 305 306 /* Initialize tables. */ 307 SLIST_INIT(&head->tables[0]); 308 SLIST_INIT(&head->tables[1]); 309 310 mutex_init(&head->table_mtx, MUTEX_DEFAULT, IPL_NONE); 311 cv_init(&head->table_cv, "dm_io"); 312 } 313 314 /* 315 * Destroy all variables in table_head 316 */ 317 void 318 dm_table_head_destroy(dm_table_head_t * head) 319 { 320 KASSERT(!mutex_owned(&head->table_mtx)); 321 KASSERT(!cv_has_waiters(&head->table_cv)); 322 /* tables doens't exists when I call this routine, therefore it 323 * doesn't make sense to have io_cnt != 0 */ 324 KASSERT(head->io_cnt == 0); 325 326 cv_destroy(&head->table_cv); 327 mutex_destroy(&head->table_mtx); 328 } 329