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 2016 Toomas Soome <tsoome@me.com> 24 * Copyright 2022 Tintri by DDN, Inc. All rights reserved. 25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 * 28 * Copyright 2026 Oxide Computer Company 29 */ 30 31 #include <devfsadm.h> 32 #include <stdio.h> 33 #include <strings.h> 34 #include <stdlib.h> 35 #include <limits.h> 36 #include <ctype.h> 37 #include <unistd.h> 38 #include <sys/int_fmtio.h> 39 #include <sys/stat.h> 40 #include <bsm/devalloc.h> 41 #include <sys/scsi/scsi_address.h> 42 #include <sys/libdevid.h> 43 #include <sys/lofi.h> 44 45 #define DISK_SUBPATH_MAX 100 46 #define RM_STALE 0x01 47 #define DISK_LINK_RE "^r?dsk/c[0-9]+(t[0-9A-F]+)?d[0-9]+(((s|p))[0-9]+)?$" 48 #define DISK_LINK_TO_UPPER(ch)\ 49 (((ch) >= 'a' && (ch) <= 'z') ? (ch - 'a' + 'A') : ch) 50 51 #define SLICE_SMI "s7" 52 #define SLICE_EFI "" 53 54 #define MN_SMI "h" 55 #define MN_EFI "wd" 56 #define ASCIIWWNSIZE 255 57 #if defined(__i386) || defined(__amd64) 58 /* 59 * The number of minor nodes per LUN is defined by the disk drivers. 60 * Currently it is set to 64. Refer CMLBUNIT_SHIFT (cmlb_impl.h) 61 */ 62 #define NUM_MINORS_PER_INSTANCE 64 63 #endif 64 65 66 extern int system_labeled; 67 68 static int disk_callback_chan(di_minor_t minor, di_node_t node); 69 static int disk_callback_nchan(di_minor_t minor, di_node_t node); 70 static int disk_callback_blkdev(di_minor_t minor, di_node_t node); 71 static int disk_callback_wwn(di_minor_t minor, di_node_t node); 72 static int disk_callback_xvmd(di_minor_t minor, di_node_t node); 73 static int disk_callback_fabric(di_minor_t minor, di_node_t node); 74 static int disk_callback_sas(di_minor_t minor, di_node_t node); 75 static void disk_common(di_minor_t minor, di_node_t node, char *disk, 76 int flags); 77 static char *diskctrl(di_node_t node, di_minor_t minor); 78 static int reserved_links_exist(di_node_t node, di_minor_t minor, int nflags); 79 static void disk_rm_lofi_all(char *file); 80 81 82 static devfsadm_create_t disk_cbt[] = { 83 { "disk", DDI_NT_BLOCK, NULL, 84 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 85 }, 86 { "disk", DDI_NT_BLOCK_CHAN, NULL, 87 TYPE_EXACT, ILEVEL_0, disk_callback_chan 88 }, 89 { "disk", DDI_NT_BLOCK_BLKDEV, NULL, 90 TYPE_EXACT, ILEVEL_0, disk_callback_blkdev 91 }, 92 { "disk", DDI_NT_BLOCK_FABRIC, NULL, 93 TYPE_EXACT, ILEVEL_0, disk_callback_fabric 94 }, 95 { "disk", DDI_NT_BLOCK_WWN, NULL, 96 TYPE_EXACT, ILEVEL_0, disk_callback_wwn 97 }, 98 { "disk", DDI_NT_BLOCK_SAS, NULL, 99 TYPE_EXACT, ILEVEL_0, disk_callback_sas 100 }, 101 { "disk", DDI_NT_CD, NULL, 102 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 103 }, 104 { "disk", DDI_NT_CD_CHAN, NULL, 105 TYPE_EXACT, ILEVEL_0, disk_callback_chan 106 }, 107 { "disk", DDI_NT_BLOCK_XVMD, NULL, 108 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd 109 }, 110 { "disk", DDI_NT_CD_XVMD, NULL, 111 TYPE_EXACT, ILEVEL_0, disk_callback_xvmd 112 }, 113 }; 114 115 DEVFSADM_CREATE_INIT_V0(disk_cbt); 116 117 /* 118 * HOT auto cleanup of disks is done for lofi devices only. 119 */ 120 static devfsadm_remove_t disk_remove_cbt[] = { 121 { "disk", DISK_LINK_RE, RM_HOT | RM_POST | RM_ALWAYS, 122 ILEVEL_0, disk_rm_lofi_all 123 }, 124 { "disk", DISK_LINK_RE, RM_POST, 125 ILEVEL_0, devfsadm_rm_all 126 } 127 }; 128 129 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt); 130 131 static devlink_re_t disks_re_array[] = { 132 {"^r?dsk/c([0-9]+)", 1}, 133 {"^cfg/c([0-9]+)$", 1}, 134 {"^scsi/.+/c([0-9]+)", 1}, 135 {NULL} 136 }; 137 138 static char *disk_mid = "disk_mid"; 139 static char *modname = "disk_link"; 140 141 /* 142 * Check if link is from lofi by checking path from readlink(). 143 */ 144 static int 145 is_lofi_disk(char *file) 146 { 147 char buf[PATH_MAX + 1]; 148 char filepath[PATH_MAX]; 149 char *ptr; 150 ssize_t size; 151 152 size = snprintf(filepath, sizeof (filepath), "%s/dev/%s", 153 devfsadm_root_path(), file); 154 if (size > sizeof (filepath)) 155 return (0); 156 157 size = readlink(filepath, buf, sizeof (buf) - 1); 158 if (size == -1) 159 return (0); 160 buf[size] = '\0'; 161 ptr = strchr(buf, '@'); 162 if (ptr == NULL) 163 return (0); 164 ptr[1] = '\0'; 165 if (strcmp(buf, "../../devices/pseudo/lofi@") != 0) 166 return (0); 167 return (1); 168 } 169 170 /* 171 * Wrapper around devfsadm_rm_link() for lofi devices. 172 */ 173 static void disk_rm_lofi_all(char *file) 174 { 175 if (is_lofi_disk(file)) 176 devfsadm_rm_link(file); 177 } 178 179 int 180 minor_init() 181 { 182 devfsadm_print(disk_mid, 183 "%s: minor_init(): Creating disks reserved ID cache\n", 184 modname); 185 return (devfsadm_reserve_id_cache(disks_re_array, NULL)); 186 } 187 188 static int 189 disk_callback_chan(di_minor_t minor, di_node_t node) 190 { 191 char *addr; 192 char disk[23]; 193 char *driver; 194 uint_t targ = 0; 195 uint_t lun = 0; 196 197 driver = di_driver_name(node); 198 if (strcmp(driver, LOFI_DRIVER_NAME) != 0) { 199 addr = di_bus_addr(node); 200 (void) sscanf(addr, "%X,%X", &targ, &lun); 201 } else { 202 targ = di_instance(node); 203 } 204 205 (void) snprintf(disk, sizeof (disk), "t%dd%d", targ, lun); 206 disk_common(minor, node, disk, 0); 207 return (DEVFSADM_CONTINUE); 208 209 } 210 211 static int 212 disk_callback_nchan(di_minor_t minor, di_node_t node) 213 { 214 char *addr; 215 char disk[10]; 216 uint_t lun; 217 218 addr = di_bus_addr(node); 219 (void) sscanf(addr, "%X", &lun); 220 (void) sprintf(disk, "d%d", lun); 221 disk_common(minor, node, disk, 0); 222 return (DEVFSADM_CONTINUE); 223 224 } 225 226 static int 227 disk_callback_blkdev(di_minor_t minor, di_node_t node) 228 { 229 char *addr; 230 char disk[DISK_SUBPATH_MAX]; 231 char guid[50]; 232 uint_t lun = 0; 233 234 addr = di_bus_addr(node); 235 (void) sscanf(addr, "w%49[0-9A-F],%X", &guid, &lun); 236 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", guid, lun); 237 disk_common(minor, node, disk, RM_STALE); 238 return (DEVFSADM_CONTINUE); 239 } 240 241 static int 242 disk_callback_wwn(di_minor_t minor, di_node_t node) 243 { 244 char disk[10]; 245 int lun; 246 int targ; 247 int *intp; 248 249 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_TARGET, 250 &intp) <= 0) { 251 return (DEVFSADM_CONTINUE); 252 } 253 targ = *intp; 254 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_LUN, 255 &intp) <= 0) { 256 lun = 0; 257 } else { 258 lun = *intp; 259 } 260 (void) sprintf(disk, "t%dd%d", targ, lun); 261 262 disk_common(minor, node, disk, RM_STALE); 263 264 return (DEVFSADM_CONTINUE); 265 } 266 267 static int 268 disk_callback_fabric(di_minor_t minor, di_node_t node) 269 { 270 char disk[DISK_SUBPATH_MAX]; 271 int lun; 272 int count; 273 int *intp; 274 uchar_t *str; 275 uchar_t *wwn; 276 uchar_t ascii_wwn[ASCIIWWNSIZE]; 277 278 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 279 "client-guid", (char **)&wwn) > 0) { 280 if (strlcpy((char *)ascii_wwn, (char *)wwn, 281 sizeof (ascii_wwn)) >= sizeof (ascii_wwn)) { 282 devfsadm_errprint("SUNW_disk_link: GUID too long:%d", 283 strlen((char *)wwn)); 284 return (DEVFSADM_CONTINUE); 285 } 286 lun = 0; 287 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 288 "port-wwn", &wwn) > 0) { 289 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 290 SCSI_ADDR_PROP_LUN, &intp) > 0) { 291 lun = *intp; 292 } else { 293 lun = 0; 294 } 295 296 for (count = 0, str = ascii_wwn; count < 8; count++, str += 2) { 297 (void) sprintf((caddr_t)str, "%02x", wwn[count]); 298 } 299 *str = '\0'; 300 } else { 301 return (DEVFSADM_CONTINUE); 302 } 303 304 for (str = ascii_wwn; *str != '\0'; str++) { 305 *str = DISK_LINK_TO_UPPER(*str); 306 } 307 308 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", ascii_wwn, lun); 309 310 disk_common(minor, node, disk, RM_STALE); 311 312 return (DEVFSADM_CONTINUE); 313 } 314 315 static int 316 disk_callback_sas(di_minor_t minor, di_node_t node) 317 { 318 char disk[DISK_SUBPATH_MAX]; 319 boolean_t lun64_found = B_FALSE; 320 scsi_lun64_t lun64, sl; 321 scsi_lun_t lun; 322 int64_t *lun64p; 323 uint64_t wwn; 324 int *intp; 325 char *tgt_port; 326 uchar_t addr_method; 327 328 /* Get lun property */ 329 if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, 330 SCSI_ADDR_PROP_LUN64, &lun64p) > 0) { 331 if (*lun64p != SCSI_LUN64_ILLEGAL) { 332 lun64_found = B_TRUE; 333 lun64 = (uint64_t)*lun64p; 334 } 335 } 336 if (!lun64_found && di_prop_lookup_ints(DDI_DEV_T_ANY, node, 337 SCSI_ADDR_PROP_LUN, &intp) > 0) { 338 lun64_found = B_TRUE; 339 lun64 = (uint64_t)*intp; 340 } 341 342 if (!lun64_found) 343 return (DEVFSADM_CONTINUE); 344 345 lun = scsi_lun64_to_lun(lun64); 346 347 addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK); 348 349 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 350 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) { 351 (void) scsi_wwnstr_to_wwn(tgt_port, &wwn); 352 if ((addr_method == SCSI_LUN_AM_PDEV) && 353 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 354 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 355 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 356 (void) snprintf(disk, DISK_SUBPATH_MAX, 357 "t%"PRIX64"d%"PRId64, wwn, lun64); 358 } else if ((addr_method == SCSI_LUN_AM_FLAT) && 359 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 360 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 361 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 362 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb; 363 (void) snprintf(disk, DISK_SUBPATH_MAX, 364 "t%"PRIX64"d%"PRIX16, wwn, sl); 365 } else { 366 (void) snprintf(disk, DISK_SUBPATH_MAX, 367 "t%"PRIX64"d%"PRIX64, wwn, lun64); 368 } 369 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 370 SCSI_ADDR_PROP_SATA_PHY, &intp) > 0) { 371 /* Use phy format naming, for SATA devices without wwn */ 372 if ((addr_method == SCSI_LUN_AM_PDEV) && 373 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 374 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 375 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 376 (void) snprintf(disk, DISK_SUBPATH_MAX, 377 "t%dd%"PRId64, *intp, lun64); 378 } else if ((addr_method == SCSI_LUN_AM_FLAT) && 379 (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) && 380 (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) && 381 (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) { 382 sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb; 383 (void) snprintf(disk, DISK_SUBPATH_MAX, 384 "t%dd%"PRIX16, *intp, sl); 385 } else { 386 (void) snprintf(disk, DISK_SUBPATH_MAX, 387 "t%dd%"PRIX64, *intp, lun64); 388 } 389 } else { 390 return (DEVFSADM_CONTINUE); 391 } 392 393 disk_common(minor, node, disk, RM_STALE); 394 395 return (DEVFSADM_CONTINUE); 396 } 397 398 /* 399 * xVM virtual block device 400 * 401 * Xen passes device number in the following format: 402 * 403 * 1 << 28 | disk << 8 | partition xvd, disks or partitions 16 onwards 404 * 202 << 8 | disk << 4 | partition xvd, disks and partitions up to 15 405 * 8 << 8 | disk << 4 | partition sd, disks and partitions up to 15 406 * 3 << 8 | disk << 6 | partition hd, disks 0..1, partitions 0..63 407 * 22 << 8 | (disk-2) << 6 | partition hd, disks 2..3, partitions 0..63 408 * 2 << 28 onwards reserved for future use 409 * other values less than 1 << 28 deprecated / reserved 410 * 411 * The corresponding /dev/dsk name can be: 412 * 413 * c0tYdX 414 * 415 * where Y,X >= 0. 416 * 417 * For PV guests using the legacy naming (0, 1, 2, ...) 418 * the disk names created will be c0d[0..767]. 419 */ 420 421 #define HD_BASE (3 << 8) 422 #define XEN_EXT_SHIFT (28) 423 424 /* 425 * Return: Number of parsed and written parameters 426 */ 427 static int 428 decode_xen_device(uint_t device, uint_t *disk, uint_t *plun) 429 { 430 uint_t dsk, lun = 0; 431 int ret = 1; 432 433 if ((device >> XEN_EXT_SHIFT) > 1) 434 return (0); 435 436 if (device < HD_BASE) { 437 /* legacy device address */ 438 dsk = device; 439 goto end; 440 } 441 442 ret = 2; 443 if (device & (1 << XEN_EXT_SHIFT)) { 444 /* extended */ 445 dsk = device & (~0xff); 446 lun = device & 0xff; 447 goto end; 448 } 449 450 switch (device >> 8) { 451 case 202: /* xvd */ 452 dsk = (device >> 4) & 0xf; 453 lun = device & 0xf; 454 break; 455 case 8: /* sd */ 456 dsk = device & (~0xf); 457 lun = device & 0xf; 458 break; 459 case 3: /* hd, disk 0..1 */ 460 dsk = (device >> 6) & 0x1; 461 lun = device & 0x3f; 462 break; 463 case 22: /* hd, disk 2..3 */ 464 dsk = ((device >> 6) & 0x1) + 2; 465 lun = device & 0x3f; 466 break; 467 default: 468 return (0); 469 } 470 end: 471 *disk = dsk; 472 *plun = lun; 473 return (ret); 474 } 475 476 static int 477 disk_callback_xvmd(di_minor_t minor, di_node_t node) 478 { 479 char *addr; 480 char disk[16]; 481 uint_t targ; 482 uint_t dsk, lun; 483 int res; 484 485 addr = di_bus_addr(node); 486 targ = strtol(addr, (char **)NULL, 10); 487 488 res = decode_xen_device(targ, &dsk, &lun); 489 490 /* HVM device names are generated using the standard generator */ 491 492 if (res == 1) 493 (void) snprintf(disk, sizeof (disk), "d%d", dsk); 494 else if (res == 2) 495 (void) snprintf(disk, sizeof (disk), "t%dd%d", dsk, lun); 496 else { 497 devfsadm_errprint("%s: invalid disk device number (%s)\n", 498 modname, addr); 499 return (DEVFSADM_CONTINUE); 500 } 501 disk_common(minor, node, disk, 0); 502 return (DEVFSADM_CONTINUE); 503 } 504 505 /* 506 * This function is called for every disk minor node. 507 * Calls enumerate to assign a logical controller number, and 508 * then devfsadm_mklink to make the link. 509 */ 510 static void 511 disk_common(di_minor_t minor, di_node_t node, char *disk, int flags) 512 { 513 char l_path[PATH_MAX + 1]; 514 char sec_path[PATH_MAX + 1]; 515 char stale_re[DISK_SUBPATH_MAX]; 516 char *dir; 517 char slice[4]; 518 char *mn; 519 char *ctrl; 520 char *nt = NULL; 521 int *int_prop; 522 int nflags = 0; 523 #if defined(__i386) || defined(__amd64) 524 char mn_copy[4]; 525 char *part; 526 int part_num; 527 #endif 528 529 mn = di_minor_name(minor); 530 if (strstr(mn, ",raw")) { 531 dir = "rdsk"; 532 #if defined(__i386) || defined(__amd64) 533 (void) strncpy(mn_copy, mn, 4); 534 part = strtok(mn_copy, ","); 535 #endif 536 } else { 537 dir = "dsk"; 538 #if defined(__i386) || defined(__amd64) 539 part = mn; 540 #endif 541 } 542 543 #if defined(__i386) || defined(__amd64) 544 /* 545 * The following is a table describing the allocation of 546 * minor numbers, minor names and /dev/dsk names for partitions 547 * and slices on x86 systems. 548 * 549 * Minor Number Minor Name /dev/dsk name 550 * --------------------------------------------- 551 * 0 to 15 "a" to "p" s0 to s15 552 * 16 "q" p0 553 * 17 to 20 "r" to "u" p1 to p4 554 * 21 to 52 "p5" to "p36" p5 to p36 555 * 556 */ 557 part_num = atoi(part + 1); 558 559 if ((mn[0] == 'p') && (part_num >= 5)) { 560 /* logical drive */ 561 (void) snprintf(slice, 4, "%s", part); 562 } else { 563 #endif 564 if (mn[0] < 'q') { 565 (void) sprintf(slice, "s%d", mn[0] - 'a'); 566 } else if (strncmp(mn, MN_EFI, 2) != 0) { 567 (void) sprintf(slice, "p%d", mn[0] - 'q'); 568 } else { 569 /* For EFI label */ 570 (void) sprintf(slice, SLICE_EFI); 571 } 572 #if defined(__i386) || defined(__amd64) 573 } 574 #endif 575 576 nflags = 0; 577 if (system_labeled) { 578 nt = di_minor_nodetype(minor); 579 if ((nt != NULL) && 580 ((strcmp(nt, DDI_NT_CD) == 0) || 581 (strcmp(nt, DDI_NT_CD_CHAN) == 0) || 582 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) { 583 nflags = DA_ADD|DA_CD; 584 } 585 } 586 587 if (reserved_links_exist(node, minor, nflags) == DEVFSADM_SUCCESS) { 588 devfsadm_print(disk_mid, "Reserved link exists. Not " 589 "creating links for slice %s\n", slice); 590 return; 591 } 592 593 if (NULL == (ctrl = diskctrl(node, minor))) 594 return; 595 596 (void) strcpy(l_path, dir); 597 (void) strcat(l_path, "/c"); 598 (void) strcat(l_path, ctrl); 599 (void) strcat(l_path, disk); 600 601 /* 602 * If switching between SMI and EFI label or vice versa 603 * cleanup the previous label's devlinks. 604 */ 605 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 606 char *s, tpath[PATH_MAX + 1]; 607 struct stat sb; 608 609 s = l_path + strlen(l_path); 610 (void) strcat(l_path, (*mn == *(MN_SMI)) 611 ? SLICE_EFI : SLICE_SMI); 612 /* 613 * Attempt the remove only if the stale link exists 614 */ 615 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s", 616 devfsadm_root_path(), l_path); 617 if (lstat(tpath, &sb) != -1) 618 devfsadm_rm_all(l_path); 619 *s = '\0'; 620 } 621 (void) strcat(l_path, slice); 622 623 (void) devfsadm_mklink(l_path, node, minor, nflags); 624 625 /* secondary links for removable and hotpluggable devices */ 626 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", 627 &int_prop) >= 0) { 628 (void) strcpy(sec_path, "removable-media/"); 629 (void) strcat(sec_path, l_path); 630 (void) devfsadm_secondary_link(sec_path, l_path, 0); 631 } 632 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", 633 &int_prop) >= 0) { 634 (void) strcpy(sec_path, "hotpluggable/"); 635 (void) strcat(sec_path, l_path); 636 (void) devfsadm_secondary_link(sec_path, l_path, 0); 637 } 638 639 if ((flags & RM_STALE) == RM_STALE) { 640 (void) strcpy(stale_re, "^"); 641 (void) strcat(stale_re, dir); 642 (void) strcat(stale_re, "/c"); 643 (void) strcat(stale_re, ctrl); 644 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$"); 645 /* 646 * optimizations are made inside of devfsadm_rm_stale_links 647 * instead of before calling the function, as it always 648 * needs to add the valid link to the cache. 649 */ 650 devfsadm_rm_stale_links(stale_re, l_path, node, minor); 651 } 652 653 free(ctrl); 654 } 655 656 657 /* index of enumeration rule applicable to this module */ 658 #define RULE_INDEX 0 659 660 static char * 661 diskctrl(di_node_t node, di_minor_t minor) 662 { 663 char path[PATH_MAX + 1]; 664 char *devfspath; 665 char *buf, *mn; 666 boolean_t is_vhci; 667 668 devfsadm_enumerate_t rules[3] = { 669 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 670 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 671 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 672 }; 673 674 mn = di_minor_name(minor); 675 676 if ((devfspath = di_devfs_path(node)) == NULL) { 677 return (NULL); 678 } 679 (void) strcpy(path, devfspath); 680 (void) strcat(path, ":"); 681 (void) strcat(path, mn); 682 di_devfs_path_free(devfspath); 683 684 /* 685 * Use controller component of disk path 686 */ 687 is_vhci = (strncmp(path, "/scsi_vhci/", 11) == 0); 688 689 if (ctrl_enumerate_int(path, RULE_INDEX, &buf, rules, 3, 1, is_vhci) == 690 DEVFSADM_MULTIPLE) { 691 /* 692 * We failed because there are multiple logical controller 693 * numbers for a single physical controller. If we use node 694 * name also in the match it should fix this and only find one 695 * logical controller. (See 4045879). 696 * NOTE: Rules for controllers are not changed, as there is 697 * no unique controller number for them in this case. 698 * 699 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 700 * modules. NOT to be used by other modules. 701 */ 702 703 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 704 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 705 if (ctrl_enumerate_int(path, RULE_INDEX, &buf, rules, 3, 0, 706 is_vhci)) { 707 return (NULL); 708 } 709 } 710 711 return (buf); 712 } 713 714 typedef struct dvlist { 715 char *dv_link; 716 struct dvlist *dv_next; 717 } dvlist_t; 718 719 static void 720 free_dvlist(dvlist_t **pp) 721 { 722 dvlist_t *entry; 723 724 while (*pp) { 725 entry = *pp; 726 *pp = entry->dv_next; 727 assert(entry->dv_link); 728 free(entry->dv_link); 729 free(entry); 730 } 731 } 732 static int 733 dvlink_cb(di_devlink_t devlink, void *arg) 734 { 735 char *path; 736 char *can_path; 737 dvlist_t **pp = (dvlist_t **)arg; 738 dvlist_t *entry = NULL; 739 740 entry = calloc(1, sizeof (dvlist_t)); 741 if (entry == NULL) { 742 devfsadm_errprint("%s: calloc failed\n", modname); 743 goto error; 744 } 745 746 path = (char *)di_devlink_path(devlink); 747 assert(path); 748 if (path == NULL) { 749 devfsadm_errprint("%s: di_devlink_path() returned NULL\n", 750 modname); 751 goto error; 752 } 753 754 devfsadm_print(disk_mid, "%s: found link %s in reverse link cache\n", 755 modname, path); 756 757 /* 758 * Return linkname in canonical form i.e. without the 759 * "/dev/" prefix 760 */ 761 can_path = strstr(path, "/dev/"); 762 if (can_path == NULL) { 763 devfsadm_errprint("%s: devlink path %s has no /dev/\n", 764 modname, path); 765 goto error; 766 } 767 768 entry->dv_link = s_strdup(can_path + strlen("/dev/")); 769 entry->dv_next = *pp; 770 *pp = entry; 771 772 return (DI_WALK_CONTINUE); 773 774 error: 775 free(entry); 776 free_dvlist(pp); 777 *pp = NULL; 778 return (DI_WALK_TERMINATE); 779 } 780 781 /* 782 * Returns success only if all goes well. If there is no matching reserved link 783 * or if there is an error, we assume no match. It is better to err on the side 784 * of caution by creating extra links than to miss out creating a required link. 785 */ 786 static int 787 reserved_links_exist(di_node_t node, di_minor_t minor, int nflags) 788 { 789 di_devlink_handle_t dvlink_cache = devfsadm_devlink_cache(); 790 char phys_path[PATH_MAX]; 791 char *minor_path; 792 dvlist_t *head; 793 dvlist_t *entry; 794 char *s; 795 char l[PATH_MAX]; 796 int switch_link = 0; 797 char *mn = di_minor_name(minor); 798 799 if (dvlink_cache == NULL || mn == NULL) { 800 devfsadm_errprint("%s: No minor or devlink cache\n", modname); 801 return (DEVFSADM_FAILURE); 802 } 803 804 if (!devfsadm_have_reserved()) { 805 devfsadm_print(disk_mid, "%s: No reserved links\n", modname); 806 return (DEVFSADM_FAILURE); 807 } 808 809 minor_path = di_devfs_minor_path(minor); 810 if (minor_path == NULL) { 811 devfsadm_errprint("%s: di_devfs_minor_path failed\n", modname); 812 return (DEVFSADM_FAILURE); 813 } 814 815 (void) strlcpy(phys_path, minor_path, sizeof (phys_path)); 816 817 di_devfs_path_free(minor_path); 818 819 head = NULL; 820 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, phys_path, 821 DI_PRIMARY_LINK, &head, dvlink_cb); 822 823 /* 824 * We may be switching between EFI label and SMI label in which case 825 * we only have minors of the other type. 826 */ 827 if (head == NULL && (*mn == *(MN_SMI) || 828 (strncmp(mn, MN_EFI, 2) == 0))) { 829 devfsadm_print(disk_mid, "%s: No links for minor %s in /dev. " 830 "Trying another label\n", modname, mn); 831 s = strrchr(phys_path, ':'); 832 if (s == NULL) { 833 devfsadm_errprint("%s: invalid minor path: %s\n", 834 modname, phys_path); 835 return (DEVFSADM_FAILURE); 836 } 837 (void) snprintf(s+1, sizeof (phys_path) - (s + 1 - phys_path), 838 "%s%s", *mn == *(MN_SMI) ? MN_EFI : MN_SMI, 839 strstr(s, ",raw") ? ",raw" : ""); 840 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, 841 phys_path, DI_PRIMARY_LINK, &head, dvlink_cb); 842 } 843 844 if (head == NULL) { 845 devfsadm_print(disk_mid, "%s: minor %s has no links in /dev\n", 846 modname, phys_path); 847 /* no links on disk */ 848 return (DEVFSADM_FAILURE); 849 } 850 851 /* 852 * It suffices to use 1 link to this minor, since 853 * we are matching with reserved IDs on the basis of 854 * the controller number which will be the same for 855 * all links to this minor. 856 */ 857 if (!devfsadm_is_reserved(disks_re_array, head->dv_link)) { 858 /* not reserved links */ 859 devfsadm_print(disk_mid, "%s: devlink %s and its minor " 860 "are NOT reserved\n", modname, head->dv_link); 861 free_dvlist(&head); 862 return (DEVFSADM_FAILURE); 863 } 864 865 devfsadm_print(disk_mid, "%s: devlink %s and its minor are on " 866 "reserved list\n", modname, head->dv_link); 867 868 /* 869 * Switch between SMI and EFI labels if required 870 */ 871 switch_link = 0; 872 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 873 for (entry = head; entry; entry = entry->dv_next) { 874 s = strrchr(entry->dv_link, '/'); 875 assert(s); 876 if (s == NULL) { 877 devfsadm_errprint("%s: disk link %s has no " 878 "directory\n", modname, entry->dv_link); 879 continue; 880 } 881 if (*mn == *(MN_SMI) && strchr(s, 's') == NULL) { 882 (void) snprintf(l, sizeof (l), "%s%s", 883 entry->dv_link, SLICE_SMI); 884 switch_link = 1; 885 devfsadm_print(disk_mid, "%s: switching " 886 "reserved link from EFI to SMI label. " 887 "New link is %s\n", modname, l); 888 } else if (strncmp(mn, MN_EFI, 2) == 0 && 889 (s = strchr(s, 's'))) { 890 *s = '\0'; 891 (void) snprintf(l, sizeof (l), "%s", 892 entry->dv_link); 893 *s = 's'; 894 switch_link = 1; 895 devfsadm_print(disk_mid, "%s: switching " 896 "reserved link from SMI to EFI label. " 897 "New link is %s\n", modname, l); 898 } 899 if (switch_link) { 900 devfsadm_print(disk_mid, "%s: switching " 901 "link: deleting %s and creating %s\n", 902 modname, entry->dv_link, l); 903 devfsadm_rm_link(entry->dv_link); 904 (void) devfsadm_mklink(l, node, minor, nflags); 905 } 906 } 907 } 908 free_dvlist(&head); 909 910 /* 911 * return SUCCESS to indicate that new links to this minor should not 912 * be created so that only compatibility links to this minor remain. 913 */ 914 return (DEVFSADM_SUCCESS); 915 } 916