1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * User Objects. 28 * 29 * User objects are used to manage and protect resources that 30 * have been created for a user context. Each user object 31 * maintains a reference count and a read/write mutex to 32 * provide the appropriate access to the object depending 33 * on the operation at hand. 34 * 35 * For example when initializing or creating a PD user object, 36 * the active context would hold a write lock, but to simply 37 * reference the PD object as in a CQ create operation, a 38 * read lock is only required. 39 * 40 * Each user object also maintains a "live" flag. If this flag 41 * is not set, then lookups on this user object will fail 42 * even if it still resides in the associated user object 43 * management table. This specifically handles the case 44 * where a get operation blocks and does not acquire the lock 45 * until after the object has been destroyed (but not yet 46 * released). Destroy operations set the "live" flag to 0 47 * prior to dropping their write lock on the user object. 48 * This allows the reader to realize when it receives the 49 * lock that the object has been destroyed so it can then 50 * release it's reference to the user object, and allow it to 51 * be freed (the storage will not be freed until the last reference 52 * is released). 53 */ 54 #include <sys/debug.h> 55 #include <sys/kmem.h> 56 #include <sys/sunddi.h> 57 #include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h> 58 59 extern char *sol_ofs_dbg_str; 60 static sol_ofs_uobj_t *ofs_uobj_find(sol_ofs_uobj_table_t *, 61 uint_t, int); 62 63 /* 64 * Function: 65 * sol_ofs_uobj_tbl_init 66 * Input: 67 * uo_tbl - A pointer to the user object resource management table 68 * to initialize. 69 * Output: 70 * None 71 * Returns: 72 * None 73 * Description: 74 * Initializes the specified user object resource managment table. 75 */ 76 void 77 sol_ofs_uobj_tbl_init(sol_ofs_uobj_table_t *uo_tbl, size_t uobj_sz) 78 { 79 ASSERT(uo_tbl != NULL); 80 81 rw_init(&uo_tbl->uobj_tbl_lock, NULL, RW_DRIVER, NULL); 82 uo_tbl->uobj_tbl_used_blks = 0; 83 uo_tbl->uobj_tbl_num_blks = 0; 84 uo_tbl->uobj_tbl_uo_cnt = 0; 85 uo_tbl->uobj_tbl_uo_sz = uobj_sz; 86 uo_tbl->uobj_tbl_uo_root = NULL; 87 } 88 89 /* 90 * Function: 91 * sol_ofs_uobj_tbl_fini 92 * Input: 93 * uo_tbl - A pointer to the user object resource management table 94 * to be released. 95 * Output: 96 * None 97 * Returns: 98 * None 99 * Description: 100 * Releases any resources held by the specified user object resource 101 * managment table. The table is no longer valid upon return. NOTE: 102 * the table should be empty when this routine is called, so this 103 * really is more of just a sanity check. 104 */ 105 void 106 sol_ofs_uobj_tbl_fini(sol_ofs_uobj_table_t *uo_tbl) 107 { 108 int i, j; 109 uint32_t size; 110 sol_ofs_uobj_blk_t *blk; 111 112 ASSERT(uo_tbl != NULL); 113 114 rw_enter(&uo_tbl->uobj_tbl_lock, RW_WRITER); 115 116 if (uo_tbl->uobj_tbl_uo_cnt > 0) { 117 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 118 "UOBJ TBL FINI: object count not zero (cnt=%d)", 119 uo_tbl->uobj_tbl_uo_cnt); 120 } 121 122 /* 123 * Go through the roots looking for blocks to free. Warn if any 124 * our found (there shouldn't be any). 125 */ 126 for (i = 0; i < uo_tbl->uobj_tbl_used_blks; i++) { 127 blk = uo_tbl->uobj_tbl_uo_root[i]; 128 if (!blk) { 129 continue; 130 } 131 for (j = 0; j < SOL_OFS_UO_BLKSZ; j++) { 132 if (blk->ofs_uoblk_blks[j]) { 133 /* 134 * This is an error, we may want to free 135 * ultimately sol_ofs_uobj_free 136 * (blk->ofs_uoblk_blks[j]); 137 */ 138 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 139 "UOBJ TBL FINI: blk %p, slot %d non null", 140 blk, j); 141 } 142 } 143 kmem_free(blk, sizeof (*blk)); 144 } 145 146 if (uo_tbl->uobj_tbl_uo_root) { 147 148 size = uo_tbl->uobj_tbl_num_blks * 149 sizeof (sol_ofs_uobj_blk_t *); 150 kmem_free(uo_tbl->uobj_tbl_uo_root, size); 151 } 152 153 rw_exit(&uo_tbl->uobj_tbl_lock); 154 rw_destroy(&uo_tbl->uobj_tbl_lock); 155 } 156 157 /* 158 * Function: 159 * uverbs_uob_init 160 * Input: 161 * uobj - Pointer to the user object to initialize. 162 * user_handle - A user space handle to associates with the object. 163 * Generally used to identify object in asynchronous 164 * notifications. 165 * uob_type - The type of user object. 166 * Ouput: 167 * uobj - Initialized user object. 168 * Returns: 169 * None 170 * Description: 171 * Initialize a new user object. The object will have one reference 172 * placed on it. 173 */ 174 void 175 sol_ofs_uobj_init(sol_ofs_uobj_t *uobj, 176 uint64_t user_handle, sol_ofs_uobj_type_t uobj_type) 177 { 178 uobj->uo_user_handle = user_handle; 179 uobj->uo_refcnt = 1; 180 uobj->uo_type = uobj_type; 181 uobj->uo_id = -1; 182 uobj->uo_live = 0; 183 rw_init(&uobj->uo_lock, NULL, RW_DRIVER, NULL); 184 mutex_init(&uobj->uo_reflock, NULL, MUTEX_DRIVER, NULL); 185 } 186 187 /* 188 * Function: 189 * ofs_uobj_fini 190 * Input: 191 * uobj - Pointer to the user object to be cleaned up. 192 * Ouput: 193 * None 194 * Returns: 195 * None 196 * Description: 197 * Performs user object cleanup prior to releasing memory. 198 */ 199 static void 200 ofs_uobj_fini(sol_ofs_uobj_t *uobj) 201 { 202 rw_destroy(&uobj->uo_lock); 203 mutex_destroy(&uobj->uo_reflock); 204 } 205 206 /* 207 * Function: 208 * sol_ofs_uobj_ref 209 * Input: 210 * uobj - Pointer to the user object 211 * Ouput: 212 * None 213 * Returns: 214 * None 215 * Description: 216 * Place a reference on the specified user object. 217 */ 218 void 219 sol_ofs_uobj_ref(sol_ofs_uobj_t *uobj) 220 { 221 mutex_enter(&uobj->uo_reflock); 222 uobj->uo_refcnt++; 223 ASSERT(uobj->uo_refcnt != 0); 224 mutex_exit(&uobj->uo_reflock); 225 } 226 227 /* 228 * Function: 229 * sol_ofs_uobj_deref 230 * Input: 231 * uobj - Pointer to the user object 232 * free_func - Pointer to release function, called if the 233 * last reference is removed for the user object. 234 * Ouput: 235 * None 236 * Returns: 237 * None 238 * Description: 239 * Remove a reference to a user object. If a free function 240 * was specified and the last reference is released, then the 241 * free function is invoked to release the user object. 242 */ 243 void 244 sol_ofs_uobj_deref(sol_ofs_uobj_t *uobj, 245 void (*free_func)(sol_ofs_uobj_t *uobj)) 246 { 247 SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "UOBJ_DEREF: uobj = %p, " 248 "refcnt=%d", uobj, uobj->uo_refcnt); 249 250 mutex_enter(&uobj->uo_reflock); 251 252 ASSERT(uobj->uo_refcnt != 0); 253 uobj->uo_refcnt--; 254 if (uobj->uo_refcnt == 0) { 255 mutex_exit(&uobj->uo_reflock); 256 if (free_func) 257 free_func(uobj); 258 } else { 259 mutex_exit(&uobj->uo_reflock); 260 } 261 } 262 263 /* 264 * Function: 265 * sol_ofs_uobj_add 266 * Input: 267 * uo_tbl - A pointer to the user object resource management table 268 * to which the object should be added. 269 * uobj - A pointer ot the user object to be added; a reference 270 * should exist on this object prior to addition, and the 271 * object should be removed prior to all references being 272 * removed. 273 * Output: 274 * uobj - The user object "uo_id" is updated and should be 275 * used in subsequent lookup operations. 276 * Returns: 277 * DDI_SUCCESS on success, else error code. 278 * Description: 279 * Add a user object to the specified user object resource management 280 * table. 281 * 282 */ 283 int 284 sol_ofs_uobj_add(sol_ofs_uobj_table_t *uo_tbl, sol_ofs_uobj_t *uobj) 285 { 286 int i, j, empty = -1; 287 sol_ofs_uobj_blk_t *blk; 288 289 rw_enter(&uo_tbl->uobj_tbl_lock, RW_WRITER); 290 291 /* 292 * Try to find an empty slot for the new user object. 293 */ 294 for (i = 0; i < uo_tbl->uobj_tbl_used_blks; i++) { 295 blk = uo_tbl->uobj_tbl_uo_root[i]; 296 if (blk != NULL && blk->ofs_uo_blk_avail > 0) { 297 SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, 298 "UOBJ ADD: table:%p, available blks:%d", 299 uo_tbl, blk->ofs_uo_blk_avail); 300 for (j = 0; j < SOL_OFS_UO_BLKSZ; j++) { 301 if (blk->ofs_uoblk_blks[j] == NULL) { 302 blk->ofs_uoblk_blks[j] = uobj; 303 uobj->uo_id = j + (i * 304 SOL_OFS_UO_BLKSZ); 305 uobj->uo_uobj_sz = 306 uo_tbl->uobj_tbl_uo_sz; 307 blk->ofs_uo_blk_avail--; 308 uo_tbl->uobj_tbl_uo_cnt++; 309 goto obj_added; 310 } 311 } 312 } else if (blk == NULL && empty < 0) { 313 /* 314 * Remember the first empty blk we came across. 315 */ 316 empty = i; 317 } 318 } 319 320 /* 321 * No entries were available, we must allocate a new block. If we did 322 * not find a empty block available, then we must allocate/reallocate 323 * the root array (copying any existing blk pointers to it). 324 */ 325 if (empty < 0) { 326 if (uo_tbl->uobj_tbl_used_blks == uo_tbl->uobj_tbl_num_blks) { 327 sol_ofs_uobj_blk_t **p; 328 uint_t newsz; 329 330 newsz = uo_tbl->uobj_tbl_num_blks + SOL_OFS_UO_BLKSZ; 331 SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, 332 "UOBJ ADD: Increasing uobj table size to %d", 333 newsz); 334 335 p = kmem_zalloc(newsz * sizeof (*p), KM_NOSLEEP); 336 if (!p) { 337 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 338 "UOBJ ADD: Mem alloc fail\n"); 339 rw_exit(&uo_tbl->uobj_tbl_lock); 340 return (1); 341 } 342 343 if (uo_tbl->uobj_tbl_uo_root) { 344 uint_t oldsz; 345 346 oldsz = (uint_t)uo_tbl->uobj_tbl_num_blks * 347 (int)(sizeof (*p)); 348 bcopy(uo_tbl->uobj_tbl_uo_root, p, oldsz); 349 kmem_free(uo_tbl->uobj_tbl_uo_root, oldsz); 350 } 351 uo_tbl->uobj_tbl_uo_root = p; 352 uo_tbl->uobj_tbl_num_blks = newsz; 353 } 354 empty = uo_tbl->uobj_tbl_used_blks; 355 uo_tbl->uobj_tbl_used_blks++; 356 } 357 358 /* 359 * There are enough free block pointers in the root, allocate 360 * a new block. 361 */ 362 blk = kmem_zalloc(sizeof (*blk), KM_NOSLEEP); 363 if (!blk) { 364 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 365 "UOBJ ADD: Mem alloc fail\n"); 366 rw_exit(&uo_tbl->uobj_tbl_lock); 367 return (1); 368 } 369 ASSERT(uo_tbl->uobj_tbl_uo_root[empty] == NULL); 370 uo_tbl->uobj_tbl_uo_root[empty] = blk; 371 blk->ofs_uo_blk_avail = SOL_OFS_UO_BLKSZ - 1; 372 373 /* 374 * Use the first slot in this new block to add the new user object. 375 */ 376 uobj->uo_id = empty * SOL_OFS_UO_BLKSZ; 377 blk->ofs_uoblk_blks[0] = uobj; 378 uobj->uo_uobj_sz = uo_tbl->uobj_tbl_uo_sz; 379 uo_tbl->uobj_tbl_uo_cnt++; 380 381 obj_added: 382 rw_exit(&uo_tbl->uobj_tbl_lock); 383 return (0); 384 } 385 386 /* 387 * Function: 388 * sol_ofs_uobj_remove 389 * Input: 390 * uo_tbl - A pointer to the user object resource management table 391 * from which the object should be removed. 392 * uobj - A pointer ot the user object to be removed. 393 * Output: 394 * None 395 * Returns: 396 * A pointer to the user object that was removed on success, otherwise 397 * NULL. 398 * Description: 399 * Remove a user object from the specified user resource management 400 * table. 401 * 402 * The uobj uo_lock must be held as a writer before calling this. 403 */ 404 sol_ofs_uobj_t * 405 sol_ofs_uobj_remove(sol_ofs_uobj_table_t *uo_tbl, sol_ofs_uobj_t *uobj) 406 { 407 uint_t i, j; 408 sol_ofs_uobj_blk_t *blk; 409 sol_ofs_uobj_t *p; 410 411 ASSERT(uo_tbl != NULL); 412 ASSERT(uobj != NULL); 413 414 p = NULL; 415 rw_enter(&uo_tbl->uobj_tbl_lock, RW_WRITER); 416 417 if (!uobj->uo_live) { 418 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 419 "UOBJ REMOVE: object 0x%P, already removed", (void *)uobj); 420 goto remove_done; 421 } 422 423 if ((uo_tbl->uobj_tbl_uo_cnt == 0) || !(uo_tbl->uobj_tbl_uo_root)) { 424 /* 425 * The table is empty, just return not found 426 * Don't panic, userland app could have double free'd 427 * let them deal with it. 428 */ 429 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 430 "UOBJ REMOVE: table 0x%P empty", (void *)uo_tbl); 431 goto remove_done; 432 } 433 434 i = uobj->uo_id / SOL_OFS_UO_BLKSZ; 435 j = uobj->uo_id % SOL_OFS_UO_BLKSZ; 436 437 if (i >= uo_tbl->uobj_tbl_used_blks) { 438 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 439 "UOBJ REMOVE: object id %d exceeds table size", 440 uobj->uo_id); 441 goto remove_done; 442 } 443 444 ASSERT(i < uo_tbl->uobj_tbl_num_blks); 445 446 blk = uo_tbl->uobj_tbl_uo_root[i]; 447 if (blk == NULL) { 448 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 449 "UOBJ REMOVE: object id %d points to invalid root", 450 uobj->uo_id); 451 goto remove_done; 452 } 453 454 if (blk->ofs_uoblk_blks[j] == NULL) { 455 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 456 "UOBJ REMOVE: object id %d points to invalid block", 457 uobj->uo_id); 458 goto remove_done; 459 } 460 461 /* 462 * Mark as dead 463 */ 464 uobj->uo_live = 0; 465 466 p = blk->ofs_uoblk_blks[j]; 467 blk->ofs_uoblk_blks[j] = NULL; 468 blk->ofs_uo_blk_avail++; 469 if (blk->ofs_uo_blk_avail == SOL_OFS_UO_BLKSZ) { 470 kmem_free(blk, sizeof (*blk)); 471 uo_tbl->uobj_tbl_uo_root[i] = NULL; 472 } 473 uo_tbl->uobj_tbl_uo_cnt--; 474 475 remove_done: 476 rw_exit(&uo_tbl->uobj_tbl_lock); 477 return (p); 478 } 479 480 /* 481 * Function: 482 * ofs_uobj_find 483 * Input: 484 * uo_tbl - A pointer to the user object resource management table 485 * to be used for the lookup. 486 * uo_id - The user object ID to lookup. This ID was set when 487 * the object was added to the resource management table. 488 * add_ref - A non zero value indicates that the user objects reference 489 * count should be updated to reflect and additional 490 * reference before it is returned. 491 * Output: 492 * None 493 * Returns: 494 * A pointer to the user object associated with the uo_id if found, 495 * otherwise NULL. 496 * Description: 497 * Lookup and return a user object from the specified user resource 498 * management table. 499 */ 500 static sol_ofs_uobj_t * 501 ofs_uobj_find(sol_ofs_uobj_table_t *uo_tbl, uint32_t uo_id, int add_ref) 502 { 503 uint32_t i, j; 504 sol_ofs_uobj_blk_t *blk; 505 sol_ofs_uobj_t *uobj; 506 507 ASSERT(uo_tbl != NULL); 508 uobj = NULL; 509 510 rw_enter(&uo_tbl->uobj_tbl_lock, RW_READER); 511 512 if ((uo_tbl->uobj_tbl_uo_cnt == 0) || !(uo_tbl->uobj_tbl_uo_root)) { 513 /* 514 * The table is empty, just return not found 515 * Don't panic, userland app could have double free'd 516 * let them deal with it. 517 */ 518 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 519 "UOBJ FIND: id %d in tbl 0x%P - tbl empty", uo_id, 520 (void *)uo_tbl); 521 goto find_done; 522 } 523 524 i = uo_id / SOL_OFS_UO_BLKSZ; 525 j = uo_id % SOL_OFS_UO_BLKSZ; 526 527 if (i >= uo_tbl->uobj_tbl_used_blks) { 528 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 529 "UOBJ FIND: Index not valid, %d", uo_id); 530 goto find_done; 531 } 532 533 /* 534 * Get the user object, and if valid perform a get (ref++). 535 * The caller issuing the find, must release the reference 536 * when done. 537 */ 538 blk = uo_tbl->uobj_tbl_uo_root[i]; 539 if (blk != NULL) { 540 ASSERT(i < uo_tbl->uobj_tbl_num_blks); 541 542 uobj = blk->ofs_uoblk_blks[j]; 543 544 if (uobj == NULL) { 545 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 546 "UOBJ FIND: Index %d not found, blk = %p", 547 uo_id, blk->ofs_uoblk_blks[j]); 548 } else if (add_ref) { 549 sol_ofs_uobj_ref(uobj); 550 } 551 } else { 552 SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, 553 "UOBJ FIND: Uobject not found, %d", uo_id); 554 goto find_done; 555 } 556 557 find_done: 558 rw_exit(&uo_tbl->uobj_tbl_lock); 559 return (uobj); 560 } 561 562 /* 563 * Function: 564 * sol_ofs_uobj_get_read 565 * Input: 566 * tbl - Pointer to the user object managment table to 567 * be used in the lookup. 568 * uo_id - The ID to object mapping, assigned to the user 569 * object at addition to the table. 570 * Ouput: 571 * None 572 * Returns: 573 * A pointer to the user object associated with uo_id or NULL 574 * if the entry does not exist. 575 * Description: 576 * Lookup a user object and place a reference on it. Acquires 577 * the object with a READ lock. The reference and lock should 578 * be released using the sol_ofs_uobj_put() call. 579 */ 580 sol_ofs_uobj_t * 581 sol_ofs_uobj_get_read(sol_ofs_uobj_table_t *tbl, uint32_t uo_id) 582 { 583 sol_ofs_uobj_t *uobj; 584 585 uobj = ofs_uobj_find(tbl, uo_id, 1); 586 if (!uobj) 587 return (NULL); 588 589 rw_enter(&uobj->uo_lock, RW_READER); 590 591 /* 592 * If object was destroyed before we got the lock, just release 593 * our reference and indicate we didn't find the object. 594 */ 595 if (!uobj->uo_live) { 596 sol_ofs_uobj_put(uobj); 597 return (NULL); 598 } 599 return (uobj); 600 } 601 602 /* 603 * Function: 604 * sol_ofs_uobj_get_write 605 * Input: 606 * tbl - Pointer to the user object managment table to 607 * be used in the lookup. 608 * uo_id - The ID to object mapping, assigned to the user 609 * object at addition to the table. 610 * Ouput: 611 * None 612 * Returns: 613 * A pointer to the user object associated with uo_id or NULL 614 * if the entry does not exist. 615 * Description: 616 * Lookup a user object and place a reference on it. Acquires 617 * the object with a WRITE lock. The reference and lock should 618 * be released using the sol_ofs_uobj_put() call. 619 */ 620 sol_ofs_uobj_t * 621 sol_ofs_uobj_get_write(sol_ofs_uobj_table_t *tbl, uint32_t uo_id) 622 { 623 sol_ofs_uobj_t *uobj; 624 625 626 uobj = ofs_uobj_find(tbl, uo_id, 1); 627 if (!uobj) 628 return (NULL); 629 630 rw_enter(&uobj->uo_lock, RW_WRITER); 631 632 /* 633 * If object was destroyed before we got the lock, just release 634 * our reference and indicate we didn't find the object. 635 */ 636 if (!uobj->uo_live) { 637 sol_ofs_uobj_put(uobj); 638 return (NULL); 639 } 640 return (uobj); 641 } 642 643 /* 644 * Function: 645 * sol_ofs_uobj_free 646 * Input: 647 * uobj - A pointer to the Solaris User Verbs kernel agent user 648 * object to be freed. 649 * Output: 650 * None. 651 * Returns: 652 * None. 653 * Description: 654 * Called when the user object is no longer referenced, it will release 655 * any user object resources and free the container object memory. 656 * NOTE: Currently there is a stipulation that the user object be the 657 * first element of any user object specialization. 658 */ 659 void 660 sol_ofs_uobj_free(sol_ofs_uobj_t *uobj) 661 { 662 size_t sz; 663 664 ASSERT(uobj); 665 666 /* 667 * Cleanup common user object and then free memory using 668 * length based on associated object type. 669 */ 670 ofs_uobj_fini(uobj); 671 672 sz = uobj->uo_uobj_sz; 673 if (sz) 674 kmem_free(uobj, sz); 675 } 676 677 /* 678 * Function: 679 * sol_ofs_uobj_put 680 * Input: 681 * uobj - Pointer to the user object 682 * Ouput: 683 * None 684 * Returns: 685 * None 686 * Description: 687 * Remove a lock associated with a user object, and decrement 688 * the reference held. On the last deference the user object 689 * will be freed. 690 */ 691 void 692 sol_ofs_uobj_put(sol_ofs_uobj_t *uobj) 693 { 694 rw_exit(&uobj->uo_lock); 695 sol_ofs_uobj_deref(uobj, sol_ofs_uobj_free); 696 } 697