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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <devfsadm.h> 27 #include <stdio.h> 28 #include <strings.h> 29 #include <stdlib.h> 30 #include <limits.h> 31 #include <ctype.h> 32 #include <sys/stat.h> 33 #include <bsm/devalloc.h> 34 35 #define DISK_SUBPATH_MAX 100 36 #define RM_STALE 0x01 37 #define DISK_LINK_RE "^r?dsk/c[0-9]+(t[0-9A-F]+)?d[0-9]+(((s|p))[0-9]+)?$" 38 #define DISK_LINK_TO_UPPER(ch)\ 39 (((ch) >= 'a' && (ch) <= 'z') ? (ch - 'a' + 'A') : ch) 40 41 #define SLICE_SMI "s7" 42 #define SLICE_EFI "" 43 44 #define MN_SMI "h" 45 #define MN_EFI "wd" 46 #define ASCIIWWNSIZE 255 47 #if defined(__i386) || defined(__amd64) 48 /* 49 * The number of minor nodes per LUN is defined by the disk drivers. 50 * Currently it is set to 64. Refer CMLBUNIT_SHIFT (cmlb_impl.h) 51 */ 52 #define NUM_MINORS_PER_INSTANCE 64 53 #endif 54 55 56 extern int system_labeled; 57 58 static int disk_callback_chan(di_minor_t minor, di_node_t node); 59 static int disk_callback_nchan(di_minor_t minor, di_node_t node); 60 static int disk_callback_wwn(di_minor_t minor, di_node_t node); 61 static int disk_callback_xvmd(di_minor_t minor, di_node_t node); 62 static int disk_callback_fabric(di_minor_t minor, di_node_t node); 63 static void disk_common(di_minor_t minor, di_node_t node, char *disk, 64 int flags); 65 static char *diskctrl(di_node_t node, di_minor_t minor); 66 static int reserved_links_exist(di_node_t node, di_minor_t minor, int nflags); 67 68 69 static devfsadm_create_t disk_cbt[] = { 70 { "disk", "ddi_block", NULL, 71 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 72 }, 73 { "disk", "ddi_block:channel", NULL, 74 TYPE_EXACT, ILEVEL_0, disk_callback_chan 75 }, 76 { "disk", "ddi_block:fabric", NULL, 77 TYPE_EXACT, ILEVEL_0, disk_callback_fabric 78 }, 79 { "disk", "ddi_block:wwn", NULL, 80 TYPE_EXACT, ILEVEL_0, disk_callback_wwn 81 }, 82 { "disk", "ddi_block:cdrom", NULL, 83 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 84 }, 85 { "disk", "ddi_block:cdrom:channel", NULL, 86 TYPE_EXACT, ILEVEL_0, disk_callback_chan 87 }, 88 { "disk", "ddi_block:xvmd", NULL, 89 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd 90 }, 91 { "disk", "ddi_block:cdrom:xvmd", NULL, 92 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd 93 }, 94 }; 95 96 DEVFSADM_CREATE_INIT_V0(disk_cbt); 97 98 /* 99 * HOT auto cleanup of disks not desired. 100 */ 101 static devfsadm_remove_t disk_remove_cbt[] = { 102 { "disk", DISK_LINK_RE, RM_POST, 103 ILEVEL_0, devfsadm_rm_all 104 } 105 }; 106 107 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt); 108 109 static devlink_re_t disks_re_array[] = { 110 {"^r?dsk/c([0-9]+)", 1}, 111 {"^cfg/c([0-9]+)$", 1}, 112 {"^scsi/.+/c([0-9]+)", 1}, 113 {NULL} 114 }; 115 116 static char *disk_mid = "disk_mid"; 117 static char *modname = "disk_link"; 118 119 int 120 minor_init() 121 { 122 devfsadm_print(disk_mid, 123 "%s: minor_init(): Creating disks reserved ID cache\n", 124 modname); 125 return (devfsadm_reserve_id_cache(disks_re_array, NULL)); 126 } 127 128 static int 129 disk_callback_chan(di_minor_t minor, di_node_t node) 130 { 131 char *addr; 132 char disk[20]; 133 uint_t targ; 134 uint_t lun; 135 136 addr = di_bus_addr(node); 137 (void) sscanf(addr, "%X,%X", &targ, &lun); 138 (void) sprintf(disk, "t%dd%d", targ, lun); 139 disk_common(minor, node, disk, 0); 140 return (DEVFSADM_CONTINUE); 141 142 } 143 144 static int 145 disk_callback_nchan(di_minor_t minor, di_node_t node) 146 { 147 char *addr; 148 char disk[10]; 149 uint_t lun; 150 151 addr = di_bus_addr(node); 152 (void) sscanf(addr, "%X", &lun); 153 (void) sprintf(disk, "d%d", lun); 154 disk_common(minor, node, disk, 0); 155 return (DEVFSADM_CONTINUE); 156 157 } 158 159 static int 160 disk_callback_wwn(di_minor_t minor, di_node_t node) 161 { 162 char disk[10]; 163 int lun; 164 int targ; 165 int *intp; 166 167 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "target", &intp) <= 0) { 168 return (DEVFSADM_CONTINUE); 169 } 170 targ = *intp; 171 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &intp) <= 0) { 172 lun = 0; 173 } else { 174 lun = *intp; 175 } 176 (void) sprintf(disk, "t%dd%d", targ, lun); 177 178 disk_common(minor, node, disk, RM_STALE); 179 180 return (DEVFSADM_CONTINUE); 181 } 182 183 static int 184 disk_callback_fabric(di_minor_t minor, di_node_t node) 185 { 186 char disk[DISK_SUBPATH_MAX]; 187 int lun; 188 int count; 189 int *intp; 190 uchar_t *str; 191 uchar_t *wwn; 192 uchar_t ascii_wwn[ASCIIWWNSIZE]; 193 194 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 195 "client-guid", (char **)&wwn) > 0) { 196 if (strlcpy((char *)ascii_wwn, (char *)wwn, 197 sizeof (ascii_wwn)) >= sizeof (ascii_wwn)) { 198 devfsadm_errprint("SUNW_disk_link: GUID too long:%d", 199 strlen((char *)wwn)); 200 return (DEVFSADM_CONTINUE); 201 } 202 lun = 0; 203 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 204 "port-wwn", &wwn) > 0) { 205 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 206 "lun", &intp) > 0) { 207 lun = *intp; 208 } else { 209 lun = 0; 210 } 211 212 for (count = 0, str = ascii_wwn; count < 8; count++, str += 2) { 213 (void) sprintf((caddr_t)str, "%02x", wwn[count]); 214 } 215 *str = '\0'; 216 } else { 217 return (DEVFSADM_CONTINUE); 218 } 219 220 for (str = ascii_wwn; *str != '\0'; str++) { 221 *str = DISK_LINK_TO_UPPER(*str); 222 } 223 224 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", ascii_wwn, lun); 225 226 disk_common(minor, node, disk, RM_STALE); 227 228 return (DEVFSADM_CONTINUE); 229 } 230 231 /* 232 * xVM virtual block device 233 * 234 * VBDs are enumerated into xenstore by xend and named using 235 * the linux dev_t values for 'hd' and 'xvd' devices. Linux 236 * dev_t's are 16-bit values. The upper 8 bits identify the major # 237 * of the device (hd, xvd) and the lower 8 bits the instance and partition 238 * 239 * For PV guests, VBDs are named by the virt-tools using 240 * the form xvd[a-p][1-15]. The corresponding Solaris /dev/dsk name 241 * created by this generator will be c0t[0-15]d[0-15]sN, 242 * were the target (t) value represents [a-p] and the 243 * disk (d) value is either 0 (e.g. xvda) or contains the partition 244 * information if it has been specified [1-15] (e.g. xvda1) 245 * 246 * For PV guests using the legacy naming (0, 1, 2, ...) 247 * the Solaris disk names created will be c0d[0..767]sN 248 * The Solaris version of virt-install based on virtinst.101 249 * named PV disks as sequential integers. With virtinst.300_1 and 250 * beyond, the virt-* tools will no longer create legacy disk 251 * names. 252 */ 253 static int 254 disk_callback_xvmd(di_minor_t minor, di_node_t node) 255 { 256 #define HD_BASE (3 << 8) 257 #define XVBDMAJ 202 258 259 char *addr; 260 char disk[16]; 261 uint_t targ; 262 uint_t lun = 0; 263 uint_t fmaj; 264 265 addr = di_bus_addr(node); 266 targ = strtol(addr, (char **)NULL, 10); 267 fmaj = targ >> 8; 268 269 /* legacy device address */ 270 if (targ < HD_BASE) 271 (void) snprintf(disk, sizeof (disk), "d%d", targ); 272 /* PV VBD */ 273 else if (fmaj == XVBDMAJ) { 274 lun = targ & 0xf; 275 targ = (targ & 0xff) >> 4; 276 (void) snprintf(disk, sizeof (disk), "t%dd%d", targ, lun); 277 /* HVM device names are generated using the standard generator */ 278 } else { 279 devfsadm_errprint("%s: invalid disk device number (%s)\n", 280 modname, addr); 281 return (DEVFSADM_CONTINUE); 282 } 283 disk_common(minor, node, disk, 0); 284 return (DEVFSADM_CONTINUE); 285 286 } 287 288 /* 289 * This function is called for every disk minor node. 290 * Calls enumerate to assign a logical controller number, and 291 * then devfsadm_mklink to make the link. 292 */ 293 static void 294 disk_common(di_minor_t minor, di_node_t node, char *disk, int flags) 295 { 296 char l_path[PATH_MAX + 1]; 297 char sec_path[PATH_MAX + 1]; 298 char stale_re[DISK_SUBPATH_MAX]; 299 char *dir; 300 char slice[4]; 301 char *mn; 302 char *ctrl; 303 char *nt = NULL; 304 int *int_prop; 305 int nflags = 0; 306 #if defined(__i386) || defined(__amd64) 307 char mn_copy[4]; 308 char *part; 309 int part_num; 310 #endif 311 312 mn = di_minor_name(minor); 313 if (strstr(mn, ",raw")) { 314 dir = "rdsk"; 315 #if defined(__i386) || defined(__amd64) 316 (void) strncpy(mn_copy, mn, 4); 317 part = strtok(mn_copy, ","); 318 #endif 319 } else { 320 dir = "dsk"; 321 #if defined(__i386) || defined(__amd64) 322 part = mn; 323 #endif 324 } 325 326 #if defined(__i386) || defined(__amd64) 327 /* 328 * The following is a table describing the allocation of 329 * minor numbers, minor names and /dev/dsk names for partitions 330 * and slices on x86 systems. 331 * 332 * Minor Number Minor Name /dev/dsk name 333 * --------------------------------------------- 334 * 0 to 15 "a" to "p" s0 to s15 335 * 16 "q" p0 336 * 17 to 20 "r" to "u" p1 to p4 337 * 21 to 52 "p5" to "p36" p5 to p36 338 * 339 */ 340 part_num = atoi(part + 1); 341 342 if ((mn[0] == 'p') && (part_num >= 5)) { 343 /* logical drive */ 344 (void) snprintf(slice, 4, "%s", part); 345 } else { 346 #endif 347 if (mn[0] < 'q') { 348 (void) sprintf(slice, "s%d", mn[0] - 'a'); 349 } else if (strncmp(mn, MN_EFI, 2) != 0) { 350 (void) sprintf(slice, "p%d", mn[0] - 'q'); 351 } else { 352 /* For EFI label */ 353 (void) sprintf(slice, SLICE_EFI); 354 } 355 #if defined(__i386) || defined(__amd64) 356 } 357 #endif 358 359 nflags = 0; 360 if (system_labeled) { 361 nt = di_minor_nodetype(minor); 362 if ((nt != NULL) && 363 ((strcmp(nt, DDI_NT_CD) == 0) || 364 (strcmp(nt, DDI_NT_CD_CHAN) == 0) || 365 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) { 366 nflags = DA_ADD|DA_CD; 367 } 368 } 369 370 if (reserved_links_exist(node, minor, nflags) == DEVFSADM_SUCCESS) { 371 devfsadm_print(disk_mid, "Reserved link exists. Not " 372 "creating links for slice %s\n", slice); 373 return; 374 } 375 376 if (NULL == (ctrl = diskctrl(node, minor))) 377 return; 378 379 (void) strcpy(l_path, dir); 380 (void) strcat(l_path, "/c"); 381 (void) strcat(l_path, ctrl); 382 (void) strcat(l_path, disk); 383 384 /* 385 * If switching between SMI and EFI label or vice versa 386 * cleanup the previous label's devlinks. 387 */ 388 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 389 char *s, tpath[PATH_MAX + 1]; 390 struct stat sb; 391 392 s = l_path + strlen(l_path); 393 (void) strcat(l_path, (*mn == *(MN_SMI)) 394 ? SLICE_EFI : SLICE_SMI); 395 /* 396 * Attempt the remove only if the stale link exists 397 */ 398 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s", 399 devfsadm_root_path(), l_path); 400 if (lstat(tpath, &sb) != -1) 401 devfsadm_rm_all(l_path); 402 *s = '\0'; 403 } 404 (void) strcat(l_path, slice); 405 406 (void) devfsadm_mklink(l_path, node, minor, nflags); 407 408 /* secondary links for removable and hotpluggable devices */ 409 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", 410 &int_prop) >= 0) { 411 (void) strcpy(sec_path, "removable-media/"); 412 (void) strcat(sec_path, l_path); 413 (void) devfsadm_secondary_link(sec_path, l_path, 0); 414 } 415 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", 416 &int_prop) >= 0) { 417 (void) strcpy(sec_path, "hotpluggable/"); 418 (void) strcat(sec_path, l_path); 419 (void) devfsadm_secondary_link(sec_path, l_path, 0); 420 } 421 422 if ((flags & RM_STALE) == RM_STALE) { 423 (void) strcpy(stale_re, "^"); 424 (void) strcat(stale_re, dir); 425 (void) strcat(stale_re, "/c"); 426 (void) strcat(stale_re, ctrl); 427 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$"); 428 /* 429 * optimizations are made inside of devfsadm_rm_stale_links 430 * instead of before calling the function, as it always 431 * needs to add the valid link to the cache. 432 */ 433 devfsadm_rm_stale_links(stale_re, l_path, node, minor); 434 } 435 436 free(ctrl); 437 } 438 439 440 /* index of enumeration rule applicable to this module */ 441 #define RULE_INDEX 0 442 443 static char * 444 diskctrl(di_node_t node, di_minor_t minor) 445 { 446 char path[PATH_MAX + 1]; 447 char *devfspath; 448 char *buf, *mn; 449 450 devfsadm_enumerate_t rules[3] = { 451 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 452 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 453 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 454 }; 455 456 mn = di_minor_name(minor); 457 458 if ((devfspath = di_devfs_path(node)) == NULL) { 459 return (NULL); 460 } 461 (void) strcpy(path, devfspath); 462 (void) strcat(path, ":"); 463 (void) strcat(path, mn); 464 di_devfs_path_free(devfspath); 465 466 /* 467 * Use controller component of disk path 468 */ 469 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) == 470 DEVFSADM_MULTIPLE) { 471 472 /* 473 * We failed because there are multiple logical controller 474 * numbers for a single physical controller. If we use node 475 * name also in the match it should fix this and only find one 476 * logical controller. (See 4045879). 477 * NOTE: Rules for controllers are not changed, as there is 478 * no unique controller number for them in this case. 479 * 480 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 481 * modules. NOT to be used by other modules. 482 */ 483 484 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 485 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 486 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) { 487 return (NULL); 488 } 489 } 490 491 return (buf); 492 } 493 494 typedef struct dvlist { 495 char *dv_link; 496 struct dvlist *dv_next; 497 } dvlist_t; 498 499 static void 500 free_dvlist(dvlist_t **pp) 501 { 502 dvlist_t *entry; 503 504 while (*pp) { 505 entry = *pp; 506 *pp = entry->dv_next; 507 assert(entry->dv_link); 508 free(entry->dv_link); 509 free(entry); 510 } 511 } 512 static int 513 dvlink_cb(di_devlink_t devlink, void *arg) 514 { 515 char *path; 516 char *can_path; 517 dvlist_t **pp = (dvlist_t **)arg; 518 dvlist_t *entry = NULL; 519 520 entry = calloc(1, sizeof (dvlist_t)); 521 if (entry == NULL) { 522 devfsadm_errprint("%s: calloc failed\n", modname); 523 goto error; 524 } 525 526 path = (char *)di_devlink_path(devlink); 527 assert(path); 528 if (path == NULL) { 529 devfsadm_errprint("%s: di_devlink_path() returned NULL\n", 530 modname); 531 goto error; 532 } 533 534 devfsadm_print(disk_mid, "%s: found link %s in reverse link cache\n", 535 modname, path); 536 537 /* 538 * Return linkname in canonical form i.e. without the 539 * "/dev/" prefix 540 */ 541 can_path = strstr(path, "/dev/"); 542 if (can_path == NULL) { 543 devfsadm_errprint("%s: devlink path %s has no /dev/\n", 544 modname, path); 545 goto error; 546 } 547 548 entry->dv_link = s_strdup(can_path + strlen("/dev/")); 549 entry->dv_next = *pp; 550 *pp = entry; 551 552 return (DI_WALK_CONTINUE); 553 554 error: 555 free(entry); 556 free_dvlist(pp); 557 *pp = NULL; 558 return (DI_WALK_TERMINATE); 559 } 560 561 /* 562 * Returns success only if all goes well. If there is no matching reserved link 563 * or if there is an error, we assume no match. It is better to err on the side 564 * of caution by creating extra links than to miss out creating a required link. 565 */ 566 static int 567 reserved_links_exist(di_node_t node, di_minor_t minor, int nflags) 568 { 569 di_devlink_handle_t dvlink_cache = devfsadm_devlink_cache(); 570 char phys_path[PATH_MAX]; 571 char *minor_path; 572 dvlist_t *head; 573 dvlist_t *entry; 574 char *s; 575 char l[PATH_MAX]; 576 int switch_link = 0; 577 char *mn = di_minor_name(minor); 578 579 if (dvlink_cache == NULL || mn == NULL) { 580 devfsadm_errprint("%s: No minor or devlink cache\n", modname); 581 return (DEVFSADM_FAILURE); 582 } 583 584 if (!devfsadm_have_reserved()) { 585 devfsadm_print(disk_mid, "%s: No reserved links\n", modname); 586 return (DEVFSADM_FAILURE); 587 } 588 589 minor_path = di_devfs_minor_path(minor); 590 if (minor_path == NULL) { 591 devfsadm_errprint("%s: di_devfs_minor_path failed\n", modname); 592 return (DEVFSADM_FAILURE); 593 } 594 595 (void) strlcpy(phys_path, minor_path, sizeof (phys_path)); 596 597 di_devfs_path_free(minor_path); 598 599 head = NULL; 600 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, phys_path, 601 DI_PRIMARY_LINK, &head, dvlink_cb); 602 603 /* 604 * We may be switching between EFI label and SMI label in which case 605 * we only have minors of the other type. 606 */ 607 if (head == NULL && (*mn == *(MN_SMI) || 608 (strncmp(mn, MN_EFI, 2) == 0))) { 609 devfsadm_print(disk_mid, "%s: No links for minor %s in /dev. " 610 "Trying another label\n", modname, mn); 611 s = strrchr(phys_path, ':'); 612 if (s == NULL) { 613 devfsadm_errprint("%s: invalid minor path: %s\n", 614 modname, phys_path); 615 return (DEVFSADM_FAILURE); 616 } 617 (void) snprintf(s+1, sizeof (phys_path) - (s + 1 - phys_path), 618 "%s%s", *mn == *(MN_SMI) ? MN_EFI : MN_SMI, 619 strstr(s, ",raw") ? ",raw" : ""); 620 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, 621 phys_path, DI_PRIMARY_LINK, &head, dvlink_cb); 622 } 623 624 if (head == NULL) { 625 devfsadm_print(disk_mid, "%s: minor %s has no links in /dev\n", 626 modname, phys_path); 627 /* no links on disk */ 628 return (DEVFSADM_FAILURE); 629 } 630 631 /* 632 * It suffices to use 1 link to this minor, since 633 * we are matching with reserved IDs on the basis of 634 * the controller number which will be the same for 635 * all links to this minor. 636 */ 637 if (!devfsadm_is_reserved(disks_re_array, head->dv_link)) { 638 /* not reserved links */ 639 devfsadm_print(disk_mid, "%s: devlink %s and its minor " 640 "are NOT reserved\n", modname, head->dv_link); 641 free_dvlist(&head); 642 return (DEVFSADM_FAILURE); 643 } 644 645 devfsadm_print(disk_mid, "%s: devlink %s and its minor are on " 646 "reserved list\n", modname, head->dv_link); 647 648 /* 649 * Switch between SMI and EFI labels if required 650 */ 651 switch_link = 0; 652 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 653 for (entry = head; entry; entry = entry->dv_next) { 654 s = strrchr(entry->dv_link, '/'); 655 assert(s); 656 if (s == NULL) { 657 devfsadm_errprint("%s: disk link %s has no " 658 "directory\n", modname, entry->dv_link); 659 continue; 660 } 661 if (*mn == *(MN_SMI) && strchr(s, 's') == NULL) { 662 (void) snprintf(l, sizeof (l), "%s%s", 663 entry->dv_link, SLICE_SMI); 664 switch_link = 1; 665 devfsadm_print(disk_mid, "%s: switching " 666 "reserved link from EFI to SMI label. " 667 "New link is %s\n", modname, l); 668 } else if (strncmp(mn, MN_EFI, 2) == 0 && 669 (s = strchr(s, 's'))) { 670 *s = '\0'; 671 (void) snprintf(l, sizeof (l), "%s", 672 entry->dv_link); 673 *s = 's'; 674 switch_link = 1; 675 devfsadm_print(disk_mid, "%s: switching " 676 "reserved link from SMI to EFI label. " 677 "New link is %s\n", modname, l); 678 } 679 if (switch_link) { 680 devfsadm_print(disk_mid, "%s: switching " 681 "link: deleting %s and creating %s\n", 682 modname, entry->dv_link, l); 683 devfsadm_rm_link(entry->dv_link); 684 (void) devfsadm_mklink(l, node, minor, nflags); 685 } 686 } 687 } 688 free_dvlist(&head); 689 690 /* 691 * return SUCCESS to indicate that new links to this minor should not 692 * be created so that only compatibility links to this minor remain. 693 */ 694 return (DEVFSADM_SUCCESS); 695 } 696