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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Main door handler functions used by dlmgmtd to process the different door 31 * call requests. Door call requests can come from the user-land applications, 32 * which will be handled by dlmgmt_call_handler(); or they can come from the 33 * kernel, which will be handled by dlmgmt_upcall_handler(). 34 */ 35 36 #include <assert.h> 37 #include <stdlib.h> 38 #include <alloca.h> 39 #include <strings.h> 40 #include <libdlmgmt.h> 41 #include "dlmgmt_impl.h" 42 43 static dlmgmt_link_t * 44 dlmgmt_getlink_by_dev(char *devname) 45 { 46 dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl); 47 48 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 49 if ((linkp->ll_class == DATALINK_CLASS_PHYS) && 50 linkattr_equal(&(linkp->ll_head), FDEVNAME, devname, 51 strlen(devname) + 1)) { 52 return (linkp); 53 } 54 } 55 return (NULL); 56 } 57 58 static void 59 dlmgmt_upcall_create(dlmgmt_upcall_arg_create_t *create, 60 dlmgmt_create_retval_t *retvalp) 61 { 62 datalink_class_t class; 63 uint32_t media; 64 dlmgmt_link_t *linkp; 65 char link[MAXLINKNAMELEN]; 66 uint32_t flags; 67 int err; 68 boolean_t created = B_FALSE; 69 70 /* 71 * Determine whether this link is persistent. Note that this request 72 * is coming from kernel so this link must be active. 73 */ 74 flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0); 75 76 class = create->ld_class; 77 media = create->ld_media; 78 79 /* 80 * Hold the writer lock to update the link table. 81 */ 82 dlmgmt_table_lock(B_TRUE); 83 84 /* 85 * Check to see whether this is the reattachment of an existing 86 * physical link. If so, return its linkid. 87 */ 88 if ((class == DATALINK_CLASS_PHYS) && 89 (linkp = dlmgmt_getlink_by_dev(create->ld_devname)) != NULL) { 90 err = linkattr_set(&(linkp->ll_head), FPHYMAJ, 91 &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64); 92 if (err != 0) 93 goto done; 94 95 err = linkattr_set(&(linkp->ll_head), FPHYINST, 96 &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64); 97 if (err != 0) 98 goto done; 99 100 linkp->ll_flags |= flags; 101 linkp->ll_gen++; 102 goto done; 103 } 104 105 if ((err = dlmgmt_create_common(create->ld_devname, class, media, 106 flags, &linkp)) == EEXIST) { 107 /* 108 * The link name already exists. Return error if this is a 109 * non-physical link (in that case, the link name must be 110 * the same as the given name). 111 */ 112 if (class != DATALINK_CLASS_PHYS) 113 goto done; 114 115 /* 116 * The physical link's name already exists, request 117 * a suggested link name: net<nextppa> 118 */ 119 err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN); 120 if (err != 0) 121 goto done; 122 123 err = dlmgmt_create_common(link, class, media, flags, &linkp); 124 } 125 126 if (err != 0) 127 goto done; 128 129 created = B_TRUE; 130 131 /* 132 * This is a new link. Only need to persist link attributes for 133 * physical links. 134 */ 135 if (class == DATALINK_CLASS_PHYS && 136 (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname, 137 strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) || 138 ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj, 139 sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) || 140 ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst, 141 sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) { 142 (void) dlmgmt_destroy_common(linkp, flags); 143 goto done; 144 } 145 146 done: 147 if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_linkid, 148 linkp->ll_flags)) != 0) && created) { 149 (void) dlmgmt_destroy_common(linkp, flags); 150 } 151 152 if (err == 0) 153 retvalp->lr_linkid = linkp->ll_linkid; 154 155 retvalp->lr_err = err; 156 dlmgmt_table_unlock(); 157 } 158 159 static void 160 dlmgmt_upcall_update(dlmgmt_upcall_arg_update_t *update, 161 dlmgmt_update_retval_t *retvalp) 162 { 163 uint32_t media = update->ld_media; 164 dlmgmt_link_t *linkp; 165 int err = 0; 166 167 /* 168 * Hold the writer lock to update the link table. 169 */ 170 dlmgmt_table_lock(B_TRUE); 171 172 /* 173 * Check to see whether this is the reattachment of an existing 174 * physical link. If so, return its linkid. 175 */ 176 if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname)) == NULL) { 177 err = ENOENT; 178 goto done; 179 } 180 181 retvalp->lr_linkid = linkp->ll_linkid; 182 retvalp->lr_media = media; 183 if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) { 184 /* 185 * Assume a DL_ETHER link ce0, a DL_WIFI link ath0 186 * 1. # dladm rename-link ce0 net0 187 * 2. DR out ce0. net0 is down. 188 * 3. use rename-link to have the ath0 device inherit 189 * the configuration from net0 190 * # dladm rename-link ath0 net0 191 * 4. DR in ath0. 192 * As ath0 and ce0 do not have the same media type, ath0 193 * cannot inherit the configuration of net0. 194 */ 195 err = EEXIST; 196 197 /* 198 * Return the media type of the existing link to indicate the 199 * reason for the name conflict. 200 */ 201 retvalp->lr_media = linkp->ll_media; 202 goto done; 203 } 204 205 if (update->ld_novanity && 206 (strcmp(update->ld_devname, linkp->ll_link) != 0)) { 207 /* 208 * Return an error if this is a physical link that does not 209 * support vanity naming, but the link name is not the same 210 * as the given device name. 211 */ 212 err = EEXIST; 213 goto done; 214 } 215 216 linkp->ll_media = media; 217 linkp->ll_gen++; 218 219 (void) dlmgmt_write_db_entry(linkp->ll_linkid, linkp->ll_flags); 220 221 done: 222 dlmgmt_table_unlock(); 223 retvalp->lr_err = err; 224 } 225 226 static void 227 dlmgmt_upcall_destroy(dlmgmt_upcall_arg_destroy_t *destroy, 228 dlmgmt_destroy_retval_t *retvalp) 229 { 230 datalink_id_t linkid = destroy->ld_linkid; 231 dlmgmt_link_t *linkp = NULL; 232 uint32_t flags, dflags = 0; 233 int err = 0; 234 235 flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0); 236 237 /* 238 * Hold the writer lock to update the link table. 239 */ 240 dlmgmt_table_lock(B_TRUE); 241 242 if ((linkp = link_by_id(linkid)) == NULL) { 243 err = ENOENT; 244 goto done; 245 } 246 247 if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) && 248 ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE)) != 0)) { 249 dflags = DLMGMT_ACTIVE; 250 goto done; 251 } 252 253 if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) && 254 ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_PERSIST)) != 0)) { 255 if (dflags != 0) 256 (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); 257 dflags |= DLMGMT_PERSIST; 258 goto done; 259 } 260 261 if ((err = dlmgmt_destroy_common(linkp, flags)) != 0 && dflags != 0) 262 (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); 263 264 done: 265 dlmgmt_table_unlock(); 266 retvalp->lr_err = err; 267 } 268 269 static void 270 dlmgmt_getname(dlmgmt_door_getname_t *getname, dlmgmt_getname_retval_t *retvalp) 271 { 272 dlmgmt_link_t *linkp; 273 int err = 0; 274 275 /* 276 * Hold the reader lock to access the link 277 */ 278 dlmgmt_table_lock(B_FALSE); 279 if ((linkp = link_by_id(getname->ld_linkid)) == NULL) { 280 /* 281 * The link does not exists. 282 */ 283 err = ENOENT; 284 goto done; 285 } 286 287 if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= 288 MAXLINKNAMELEN) { 289 err = ENOSPC; 290 goto done; 291 } 292 retvalp->lr_flags = linkp->ll_flags; 293 retvalp->lr_class = linkp->ll_class; 294 retvalp->lr_media = linkp->ll_media; 295 296 done: 297 dlmgmt_table_unlock(); 298 retvalp->lr_err = err; 299 } 300 301 static void 302 dlmgmt_getlinkid(dlmgmt_door_getlinkid_t *getlinkid, 303 dlmgmt_getlinkid_retval_t *retvalp) 304 { 305 dlmgmt_link_t *linkp; 306 int err = 0; 307 308 /* 309 * Hold the reader lock to access the link 310 */ 311 dlmgmt_table_lock(B_FALSE); 312 if ((linkp = link_by_name(getlinkid->ld_link)) == NULL) { 313 /* 314 * The link does not exists. 315 */ 316 err = ENOENT; 317 goto done; 318 } 319 320 retvalp->lr_linkid = linkp->ll_linkid; 321 retvalp->lr_flags = linkp->ll_flags; 322 retvalp->lr_class = linkp->ll_class; 323 retvalp->lr_media = linkp->ll_media; 324 325 done: 326 dlmgmt_table_unlock(); 327 retvalp->lr_err = err; 328 } 329 330 static void 331 dlmgmt_getnext(dlmgmt_door_getnext_t *getnext, dlmgmt_getnext_retval_t *retvalp) 332 { 333 dlmgmt_link_t link, *linkp; 334 datalink_id_t linkid = getnext->ld_linkid; 335 avl_index_t where; 336 int err = 0; 337 338 /* 339 * Hold the reader lock to access the link 340 */ 341 dlmgmt_table_lock(B_FALSE); 342 343 link.ll_linkid = (linkid + 1); 344 linkp = avl_find(&dlmgmt_id_avl, &link, &where); 345 if (linkp == NULL) 346 linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER); 347 348 for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 349 if ((linkp->ll_class & getnext->ld_class) && 350 (linkp->ll_flags & getnext->ld_flags) && 351 DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia, 352 linkp->ll_media)) 353 break; 354 } 355 356 if (linkp == NULL) { 357 err = ENOENT; 358 } else { 359 retvalp->lr_linkid = linkp->ll_linkid; 360 retvalp->lr_class = linkp->ll_class; 361 retvalp->lr_media = linkp->ll_media; 362 retvalp->lr_flags = linkp->ll_flags; 363 } 364 365 dlmgmt_table_unlock(); 366 retvalp->lr_err = err; 367 } 368 369 /* 370 * Note that the caller needs to free the memory of *retvalp, when it returns 371 * success. 372 */ 373 static int 374 dlmgmt_upcall_getattr(dlmgmt_upcall_arg_getattr_t *getattr, 375 dlmgmt_getattr_retval_t **retvalpp, size_t *retszp) 376 { 377 dlmgmt_link_t *linkp; 378 int err = 0; 379 380 /* 381 * Hold the reader lock to access the link 382 */ 383 dlmgmt_table_lock(B_FALSE); 384 if ((linkp = link_by_id(getattr->ld_linkid)) == NULL) { 385 /* 386 * The link does not exist. 387 */ 388 err = ENOENT; 389 goto done; 390 } 391 392 err = dlmgmt_getattr_common(&linkp->ll_head, getattr->ld_attr, 393 retvalpp, retszp); 394 395 done: 396 dlmgmt_table_unlock(); 397 return (err); 398 } 399 400 static void 401 dlmgmt_upcall_handler(void *arg, int cmd) 402 { 403 switch (cmd) { 404 case DLMGMT_CMD_DLS_CREATE: { 405 dlmgmt_create_retval_t retval; 406 407 dlmgmt_upcall_create(arg, &retval); 408 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 409 break; 410 } 411 case DLMGMT_CMD_DLS_UPDATE: { 412 dlmgmt_update_retval_t retval; 413 414 dlmgmt_upcall_update(arg, &retval); 415 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 416 break; 417 } 418 case DLMGMT_CMD_DLS_GETATTR: { 419 dlmgmt_getattr_retval_t retval; 420 dlmgmt_getattr_retval_t *retvalp = NULL; 421 dlmgmt_getattr_retval_t *tmp; 422 size_t retsz = 0; 423 int err; 424 425 if ((err = dlmgmt_upcall_getattr(arg, &retvalp, &retsz)) != 0) { 426 retval.lr_err = err; 427 retvalp = &retval; 428 retsz = sizeof (retval); 429 } else { 430 /* 431 * For the successful case, retvalp points to 432 * memory that was allocated with malloc. But, since 433 * door_return never returns, that memory gets leaked. 434 * Use alloca and free retvalp. 435 */ 436 tmp = alloca(retsz); 437 bcopy(retvalp, tmp, retsz); 438 free(retvalp); 439 retvalp = tmp; 440 } 441 (void) door_return((char *)retvalp, retsz, NULL, 0); 442 break; 443 } 444 case DLMGMT_CMD_DLS_DESTROY: { 445 dlmgmt_destroy_retval_t retval; 446 447 dlmgmt_upcall_destroy(arg, &retval); 448 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 449 break; 450 } 451 case DLMGMT_CMD_GETNAME: { 452 dlmgmt_getname_retval_t retval; 453 454 dlmgmt_getname(arg, &retval); 455 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 456 break; 457 } 458 case DLMGMT_CMD_GETLINKID: { 459 dlmgmt_getlinkid_retval_t retval; 460 461 dlmgmt_getlinkid(arg, &retval); 462 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 463 break; 464 } 465 case DLMGMT_CMD_GETNEXT: { 466 dlmgmt_getnext_retval_t retval; 467 468 dlmgmt_getnext(arg, &retval); 469 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 470 break; 471 } 472 default: { 473 struct dlmgmt_null_retval_s retval; 474 475 retval.lr_err = EINVAL; 476 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 477 break; 478 } 479 } 480 } 481 482 static void 483 dlmgmt_createid(dlmgmt_door_createid_t *createid, 484 dlmgmt_createid_retval_t *retvalp) 485 { 486 dlmgmt_link_t *linkp; 487 datalink_id_t linkid = DATALINK_INVALID_LINKID; 488 char link[MAXLINKNAMELEN]; 489 int err; 490 491 /* 492 * Hold the writer lock to update the dlconf table. 493 */ 494 dlmgmt_table_lock(B_TRUE); 495 496 if (createid->ld_prefix) { 497 err = dlmgmt_generate_name(createid->ld_link, link, 498 MAXLINKNAMELEN); 499 if (err != 0) 500 goto done; 501 502 err = dlmgmt_create_common(link, createid->ld_class, 503 createid->ld_media, createid->ld_flags, &linkp); 504 } else { 505 err = dlmgmt_create_common(createid->ld_link, 506 createid->ld_class, createid->ld_media, createid->ld_flags, 507 &linkp); 508 } 509 510 if (err == 0) { 511 /* 512 * Keep the active mapping. 513 */ 514 linkid = linkp->ll_linkid; 515 if (createid->ld_flags & DLMGMT_ACTIVE) 516 (void) dlmgmt_write_db_entry(linkid, DLMGMT_ACTIVE); 517 } 518 519 done: 520 dlmgmt_table_unlock(); 521 retvalp->lr_linkid = linkid; 522 retvalp->lr_err = err; 523 } 524 525 static void 526 dlmgmt_destroyid(dlmgmt_door_destroyid_t *destroyid, 527 dlmgmt_destroyid_retval_t *retvalp) 528 { 529 datalink_id_t linkid = destroyid->ld_linkid; 530 uint32_t flags = destroyid->ld_flags; 531 dlmgmt_link_t *linkp = NULL; 532 int err = 0; 533 534 /* 535 * Hold the writer lock to update the link table. 536 */ 537 dlmgmt_table_lock(B_TRUE); 538 if ((linkp = link_by_id(linkid)) == NULL) { 539 err = ENOENT; 540 goto done; 541 } 542 543 if ((err = dlmgmt_destroy_common(linkp, flags)) != 0) 544 goto done; 545 546 /* 547 * Delete the active mapping. 548 */ 549 if (flags & DLMGMT_ACTIVE) 550 (void) dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE); 551 552 done: 553 dlmgmt_table_unlock(); 554 retvalp->lr_err = err; 555 } 556 557 /* 558 * Remap a linkid to a given link name, i.e., rename an existing link1 559 * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to 560 * the given link name. 561 */ 562 static void 563 dlmgmt_remapid(dlmgmt_door_remapid_t *remapid, 564 dlmgmt_remapid_retval_t *retvalp) 565 { 566 datalink_id_t linkid1 = remapid->ld_linkid; 567 dlmgmt_link_t link, *linkp1, *tmp; 568 avl_index_t where; 569 int err = 0; 570 571 if (!dladm_valid_linkname(remapid->ld_link)) { 572 retvalp->lr_err = EINVAL; 573 return; 574 } 575 576 /* 577 * Hold the writer lock to update the link table. 578 */ 579 dlmgmt_table_lock(B_TRUE); 580 if ((linkp1 = link_by_id(linkid1)) == NULL) { 581 err = ENOENT; 582 goto done; 583 } 584 585 if (link_by_name(remapid->ld_link) != NULL) { 586 err = EEXIST; 587 goto done; 588 } 589 590 avl_remove(&dlmgmt_name_avl, linkp1); 591 (void) strlcpy(link.ll_link, remapid->ld_link, MAXLINKNAMELEN); 592 tmp = avl_find(&dlmgmt_name_avl, &link, &where); 593 assert(tmp == NULL); 594 (void) strlcpy(linkp1->ll_link, remapid->ld_link, MAXLINKNAMELEN); 595 avl_insert(&dlmgmt_name_avl, linkp1, where); 596 dlmgmt_advance(linkp1); 597 598 /* 599 * If we renamed a temporary link, update the temporary repository. 600 */ 601 if (linkp1->ll_flags & DLMGMT_ACTIVE) 602 (void) dlmgmt_write_db_entry(linkid1, DLMGMT_ACTIVE); 603 done: 604 dlmgmt_table_unlock(); 605 retvalp->lr_err = err; 606 } 607 608 static void 609 dlmgmt_upid(dlmgmt_door_upid_t *upid, dlmgmt_upid_retval_t *retvalp) 610 { 611 dlmgmt_link_t *linkp; 612 int err = 0; 613 614 /* 615 * Hold the writer lock to update the link table. 616 */ 617 dlmgmt_table_lock(B_TRUE); 618 if ((linkp = link_by_id(upid->ld_linkid)) == NULL) { 619 err = ENOENT; 620 goto done; 621 } 622 623 if (linkp->ll_flags & DLMGMT_ACTIVE) { 624 err = EINVAL; 625 goto done; 626 } 627 628 linkp->ll_flags |= DLMGMT_ACTIVE; 629 (void) dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_ACTIVE); 630 done: 631 dlmgmt_table_unlock(); 632 retvalp->lr_err = err; 633 } 634 635 static void 636 dlmgmt_createconf(dlmgmt_door_createconf_t *createconf, 637 dlmgmt_createconf_retval_t *retvalp) 638 { 639 dlmgmt_dlconf_t dlconf, *dlconfp, *tmp; 640 avl_index_t where; 641 int err; 642 643 /* 644 * Hold the writer lock to update the dlconf table. 645 */ 646 dlmgmt_dlconf_table_lock(B_TRUE); 647 648 if ((err = dlconf_create(createconf->ld_link, createconf->ld_linkid, 649 createconf->ld_class, createconf->ld_media, &dlconfp)) != 0) { 650 goto done; 651 } 652 653 dlconf.ld_id = dlconfp->ld_id; 654 tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); 655 assert(tmp == NULL); 656 avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); 657 dlmgmt_advance_dlconfid(dlconfp); 658 659 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 660 done: 661 dlmgmt_dlconf_table_unlock(); 662 retvalp->lr_err = err; 663 } 664 665 static void 666 dlmgmt_setattr(dlmgmt_door_setattr_t *setattr, size_t argsz, 667 dlmgmt_setattr_retval_t *retvalp) 668 { 669 dlmgmt_dlconf_t dlconf, *dlconfp; 670 int err = 0; 671 672 if (argsz < sizeof (dlmgmt_door_setattr_t) || 673 argsz != sizeof (dlmgmt_door_setattr_t) + setattr->ld_attrsz - 1) { 674 retvalp->lr_err = EINVAL; 675 return; 676 } 677 678 /* 679 * Hold the writer lock to update the dlconf table. 680 */ 681 dlmgmt_dlconf_table_lock(B_TRUE); 682 683 dlconf.ld_id = (int)setattr->ld_conf; 684 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 685 if (dlconfp == NULL) { 686 err = ENOENT; 687 goto done; 688 } 689 690 err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr, 691 &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type); 692 693 done: 694 dlmgmt_dlconf_table_unlock(); 695 retvalp->lr_err = err; 696 } 697 698 static void 699 dlmgmt_unsetattr(dlmgmt_door_unsetattr_t *unsetattr, 700 dlmgmt_unsetattr_retval_t *retvalp) 701 { 702 dlmgmt_dlconf_t dlconf, *dlconfp; 703 int err = 0; 704 705 /* 706 * Hold the writer lock to update the dlconf table. 707 */ 708 dlmgmt_dlconf_table_lock(B_TRUE); 709 710 dlconf.ld_id = (int)unsetattr->ld_conf; 711 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 712 if (dlconfp == NULL) { 713 err = ENOENT; 714 goto done; 715 } 716 717 err = linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); 718 719 done: 720 dlmgmt_dlconf_table_unlock(); 721 retvalp->lr_err = err; 722 } 723 724 /* 725 * Note that dlmgmt_readconf() returns a conf ID of a conf AVL tree entry, 726 * which is managed by dlmgmtd. The ID is used to find the conf entry when 727 * dlmgmt_write_conf() is called. The conf entry contains an ld_gen value 728 * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time 729 * of dlmgmt_readconf(), and ll_gen changes every time the dlmgmt_link_t 730 * changes its attributes. Therefore, dlmgmt_write_conf() can compare ld_gen 731 * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if 732 * anything has changed between the dlmgmt_read_conf() and dlmgmt_writeconf() 733 * calls. If so, EAGAIN is returned. This mechanism can ensures atomicity 734 * across the pair of dladm_read_conf() and dladm_write_conf() calls. 735 */ 736 static void 737 dlmgmt_writeconf(dlmgmt_door_writeconf_t *writeconf, 738 dlmgmt_writeconf_retval_t *retvalp) 739 { 740 dlmgmt_dlconf_t dlconf, *dlconfp; 741 dlmgmt_link_t *linkp; 742 dlmgmt_linkattr_t *attrp, *next; 743 int err = 0; 744 745 /* 746 * Hold the read lock to access the dlconf table. 747 */ 748 dlmgmt_dlconf_table_lock(B_TRUE); 749 750 dlconf.ld_id = (int)writeconf->ld_conf; 751 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 752 if (dlconfp == NULL) { 753 err = ENOENT; 754 goto done; 755 } 756 757 /* 758 * Hold the writer lock to update the link table. 759 */ 760 dlmgmt_table_lock(B_TRUE); 761 linkp = link_by_id(dlconfp->ld_linkid); 762 if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) || 763 (linkp->ll_media != dlconfp->ld_media) || 764 (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) { 765 /* 766 * The link does not exist. 767 */ 768 dlmgmt_table_unlock(); 769 err = ENOENT; 770 goto done; 771 } 772 773 if (linkp->ll_gen != dlconfp->ld_gen) { 774 /* 775 * Something has changed the link configuration; try again. 776 */ 777 dlmgmt_table_unlock(); 778 err = EAGAIN; 779 goto done; 780 } 781 782 /* 783 * Delete the old attribute list. 784 */ 785 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { 786 next = attrp->lp_next; 787 free(attrp->lp_val); 788 free(attrp); 789 } 790 linkp->ll_head = NULL; 791 792 /* 793 * Set the new attribute. 794 */ 795 for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) { 796 if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name, 797 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 798 dlmgmt_table_unlock(); 799 goto done; 800 } 801 } 802 803 linkp->ll_gen++; 804 err = dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_PERSIST); 805 dlmgmt_table_unlock(); 806 done: 807 dlmgmt_dlconf_table_unlock(); 808 retvalp->lr_err = err; 809 } 810 811 static void 812 dlmgmt_removeconf(dlmgmt_door_removeconf_t *removeconf, 813 dlmgmt_removeconf_retval_t *retvalp) 814 { 815 int err; 816 817 dlmgmt_table_lock(B_TRUE); 818 err = dlmgmt_delete_db_entry(removeconf->ld_linkid, DLMGMT_PERSIST); 819 dlmgmt_table_unlock(); 820 retvalp->lr_err = err; 821 } 822 823 static void 824 dlmgmt_destroyconf(dlmgmt_door_destroyconf_t *destroyconf, 825 dlmgmt_destroyconf_retval_t *retvalp) 826 { 827 dlmgmt_dlconf_t dlconf, *dlconfp; 828 int err = 0; 829 830 /* 831 * Hold the writer lock to update the dlconf table. 832 */ 833 dlmgmt_dlconf_table_lock(B_TRUE); 834 835 dlconf.ld_id = (int)destroyconf->ld_conf; 836 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 837 if (dlconfp == NULL) { 838 err = ENOENT; 839 goto done; 840 } 841 842 avl_remove(&dlmgmt_dlconf_avl, dlconfp); 843 dlconf_destroy(dlconfp); 844 845 done: 846 dlmgmt_dlconf_table_unlock(); 847 retvalp->lr_err = err; 848 } 849 850 /* 851 * See the comments above dladm_write_conf() to see how ld_gen is used to 852 * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair. 853 */ 854 static void 855 dlmgmt_readconf(dlmgmt_door_readconf_t *readconf, 856 dlmgmt_readconf_retval_t *retvalp) 857 { 858 dlmgmt_link_t *linkp; 859 datalink_id_t linkid = readconf->ld_linkid; 860 dlmgmt_dlconf_t *dlconfp, *tmp, dlconf; 861 dlmgmt_linkattr_t *attrp; 862 avl_index_t where; 863 int err = 0; 864 865 /* 866 * Hold the writer lock to update the dlconf table. 867 */ 868 dlmgmt_dlconf_table_lock(B_TRUE); 869 870 /* 871 * Hold the reader lock to access the link 872 */ 873 dlmgmt_table_lock(B_FALSE); 874 linkp = link_by_id(linkid); 875 if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) { 876 /* 877 * The persistent link configuration does not exists. 878 */ 879 err = ENOENT; 880 goto done; 881 } 882 883 if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid, 884 linkp->ll_class, linkp->ll_media, &dlconfp)) != 0) { 885 goto done; 886 } 887 888 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) { 889 if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name, 890 attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { 891 dlconf_destroy(dlconfp); 892 goto done; 893 } 894 } 895 dlconfp->ld_gen = linkp->ll_gen; 896 897 dlconf.ld_id = dlconfp->ld_id; 898 tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); 899 assert(tmp == NULL); 900 avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); 901 dlmgmt_advance_dlconfid(dlconfp); 902 903 retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; 904 done: 905 dlmgmt_table_unlock(); 906 dlmgmt_dlconf_table_unlock(); 907 retvalp->lr_err = err; 908 } 909 910 /* 911 * Note: the caller must free *retvalpp in case of success. 912 */ 913 static int 914 dlmgmt_getattr(dlmgmt_door_getattr_t *getattr, 915 dlmgmt_getattr_retval_t **retvalpp, size_t *retszp) 916 { 917 dlmgmt_dlconf_t dlconf, *dlconfp; 918 int err = 0; 919 920 /* 921 * Hold the writer lock to update the dlconf table. 922 */ 923 dlmgmt_dlconf_table_lock(B_FALSE); 924 925 dlconf.ld_id = (int)getattr->ld_conf; 926 dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); 927 if (dlconfp == NULL) { 928 err = ENOENT; 929 goto done; 930 } 931 932 err = dlmgmt_getattr_common(&dlconfp->ld_head, getattr->ld_attr, 933 retvalpp, retszp); 934 935 done: 936 dlmgmt_dlconf_table_unlock(); 937 return (err); 938 } 939 940 static void 941 dlmgmt_call_handler(void *arg, size_t argsz, int cmd) 942 { 943 switch (cmd) { 944 case DLMGMT_CMD_CREATE_LINKID: { 945 dlmgmt_createid_retval_t retval; 946 947 dlmgmt_createid(arg, &retval); 948 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 949 break; 950 } 951 case DLMGMT_CMD_DESTROY_LINKID: { 952 dlmgmt_destroyid_retval_t retval; 953 954 dlmgmt_destroyid(arg, &retval); 955 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 956 break; 957 } 958 case DLMGMT_CMD_REMAP_LINKID: { 959 dlmgmt_remapid_retval_t retval; 960 961 dlmgmt_remapid(arg, &retval); 962 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 963 break; 964 } 965 case DLMGMT_CMD_UP_LINKID: { 966 dlmgmt_upid_retval_t retval; 967 968 dlmgmt_upid(arg, &retval); 969 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 970 break; 971 } 972 case DLMGMT_CMD_CREATECONF: { 973 dlmgmt_createconf_retval_t retval; 974 975 dlmgmt_createconf(arg, &retval); 976 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 977 break; 978 } 979 case DLMGMT_CMD_SETATTR: { 980 dlmgmt_setattr_retval_t retval; 981 982 dlmgmt_setattr(arg, argsz, &retval); 983 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 984 break; 985 } 986 case DLMGMT_CMD_UNSETATTR: { 987 dlmgmt_unsetattr_retval_t retval; 988 989 dlmgmt_unsetattr(arg, &retval); 990 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 991 break; 992 } 993 case DLMGMT_CMD_WRITECONF: { 994 dlmgmt_writeconf_retval_t retval; 995 996 dlmgmt_writeconf(arg, &retval); 997 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 998 break; 999 } 1000 case DLMGMT_CMD_REMOVECONF: { 1001 dlmgmt_removeconf_retval_t retval; 1002 1003 dlmgmt_removeconf(arg, &retval); 1004 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1005 break; 1006 } 1007 case DLMGMT_CMD_DESTROYCONF: { 1008 dlmgmt_destroyconf_retval_t retval; 1009 1010 dlmgmt_destroyconf(arg, &retval); 1011 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1012 break; 1013 } 1014 case DLMGMT_CMD_READCONF: { 1015 dlmgmt_readconf_retval_t retval; 1016 1017 dlmgmt_readconf(arg, &retval); 1018 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1019 break; 1020 } 1021 case DLMGMT_CMD_GETATTR: { 1022 dlmgmt_getattr_retval_t retval; 1023 dlmgmt_getattr_retval_t *retvalp = NULL; 1024 dlmgmt_getattr_retval_t *tmp; 1025 int err; 1026 size_t retsz = 0; 1027 1028 if ((err = dlmgmt_getattr(arg, &retvalp, &retsz)) != 0) { 1029 retval.lr_err = err; 1030 retvalp = &retval; 1031 retsz = sizeof (retval); 1032 } else { 1033 /* 1034 * For the successful case, retvalp points to memory 1035 * that was allocated in dlmgmt_getattr(). Since 1036 * door_return never returns, that memory would get 1037 * leaked. So we use alloca instead, and free retvalp. 1038 */ 1039 tmp = alloca(retsz); 1040 bcopy(retvalp, tmp, retsz); 1041 free(retvalp); 1042 retvalp = tmp; 1043 } 1044 (void) door_return((char *)retvalp, retsz, NULL, 0); 1045 break; 1046 } 1047 default: { 1048 struct dlmgmt_null_retval_s retval; 1049 1050 retval.lr_err = EINVAL; 1051 (void) door_return((char *)&retval, sizeof (retval), NULL, 0); 1052 break; 1053 } 1054 } 1055 } 1056 1057 /* ARGSUSED */ 1058 void 1059 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, 1060 uint_t n_desc) 1061 { 1062 int cmd = ((dlmgmt_door_arg_t *)(void *)argp)->ld_cmd; 1063 1064 if (cmd < DLMGMT_CMD_BASE) { 1065 /* 1066 * Upcall request from the dls module. 1067 */ 1068 dlmgmt_upcall_handler(argp, cmd); 1069 } else { 1070 /* 1071 * Door call request from libdladm. 1072 */ 1073 dlmgmt_call_handler(argp, argsz, cmd); 1074 } 1075 } 1076