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 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <devfsadm.h> 29 #include <stdio.h> 30 #include <strings.h> 31 #include <stdlib.h> 32 #include <limits.h> 33 #include <sys/stat.h> 34 #include <bsm/devalloc.h> 35 36 #define DISK_SUBPATH_MAX 100 37 #define RM_STALE 0x01 38 #define DISK_LINK_RE "^r?dsk/c[0-9]+(t[0-9A-F]+)?d[0-9]+(((s|p))[0-9]+)?$" 39 #define DISK_LINK_TO_UPPER(ch)\ 40 (((ch) >= 'a' && (ch) <= 'z') ? (ch - 'a' + 'A') : ch) 41 42 #define SLICE_SMI "s7" 43 #define SLICE_EFI "" 44 45 #define MN_SMI "h" 46 #define MN_EFI "wd" 47 #define ASCIIWWNSIZE 255 48 49 extern int system_labeled; 50 51 static int disk_callback_chan(di_minor_t minor, di_node_t node); 52 static int disk_callback_nchan(di_minor_t minor, di_node_t node); 53 static int disk_callback_wwn(di_minor_t minor, di_node_t node); 54 static int disk_callback_fabric(di_minor_t minor, di_node_t node); 55 static void disk_common(di_minor_t minor, di_node_t node, char *disk, 56 int flags); 57 static char *diskctrl(di_node_t node, di_minor_t minor); 58 static int reserved_links_exist(di_node_t node, di_minor_t minor, int nflags); 59 60 61 static devfsadm_create_t disk_cbt[] = { 62 { "disk", "ddi_block", NULL, 63 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 64 }, 65 { "disk", "ddi_block:channel", NULL, 66 TYPE_EXACT, ILEVEL_0, disk_callback_chan 67 }, 68 { "disk", "ddi_block:fabric", NULL, 69 TYPE_EXACT, ILEVEL_0, disk_callback_fabric 70 }, 71 { "disk", "ddi_block:wwn", NULL, 72 TYPE_EXACT, ILEVEL_0, disk_callback_wwn 73 }, 74 { "disk", "ddi_block:cdrom", NULL, 75 TYPE_EXACT, ILEVEL_0, disk_callback_nchan 76 }, 77 { "disk", "ddi_block:cdrom:channel", NULL, 78 TYPE_EXACT, ILEVEL_0, disk_callback_chan 79 }, 80 }; 81 82 DEVFSADM_CREATE_INIT_V0(disk_cbt); 83 84 /* 85 * HOT auto cleanup of disks not desired. 86 */ 87 static devfsadm_remove_t disk_remove_cbt[] = { 88 { "disk", DISK_LINK_RE, RM_POST, 89 ILEVEL_0, devfsadm_rm_all 90 } 91 }; 92 93 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt); 94 95 static devlink_re_t disks_re_array[] = { 96 {"^r?dsk/c([0-9]+)", 1}, 97 {"^cfg/c([0-9]+)$", 1}, 98 {"^scsi/.+/c([0-9]+)", 1}, 99 {NULL} 100 }; 101 102 static char *disk_mid = "disk_mid"; 103 static char *modname = "disk_link"; 104 105 int 106 minor_init() 107 { 108 devfsadm_print(disk_mid, 109 "%s: minor_init(): Creating disks reserved ID cache\n", 110 modname); 111 return (devfsadm_reserve_id_cache(disks_re_array, NULL)); 112 } 113 114 static int 115 disk_callback_chan(di_minor_t minor, di_node_t node) 116 { 117 char *addr; 118 char disk[20]; 119 uint_t targ; 120 uint_t lun; 121 122 addr = di_bus_addr(node); 123 (void) sscanf(addr, "%X,%X", &targ, &lun); 124 (void) sprintf(disk, "t%dd%d", targ, lun); 125 disk_common(minor, node, disk, 0); 126 return (DEVFSADM_CONTINUE); 127 128 } 129 130 static int 131 disk_callback_nchan(di_minor_t minor, di_node_t node) 132 { 133 char *addr; 134 char disk[10]; 135 uint_t lun; 136 137 addr = di_bus_addr(node); 138 (void) sscanf(addr, "%X", &lun); 139 (void) sprintf(disk, "d%d", lun); 140 disk_common(minor, node, disk, 0); 141 return (DEVFSADM_CONTINUE); 142 143 } 144 145 static int 146 disk_callback_wwn(di_minor_t minor, di_node_t node) 147 { 148 char disk[10]; 149 int lun; 150 int targ; 151 int *intp; 152 153 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "target", &intp) <= 0) { 154 return (DEVFSADM_CONTINUE); 155 } 156 targ = *intp; 157 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &intp) <= 0) { 158 lun = 0; 159 } else { 160 lun = *intp; 161 } 162 (void) sprintf(disk, "t%dd%d", targ, lun); 163 164 disk_common(minor, node, disk, RM_STALE); 165 166 return (DEVFSADM_CONTINUE); 167 } 168 169 static int 170 disk_callback_fabric(di_minor_t minor, di_node_t node) 171 { 172 char disk[DISK_SUBPATH_MAX]; 173 int lun; 174 int count; 175 int *intp; 176 uchar_t *str; 177 uchar_t *wwn; 178 uchar_t ascii_wwn[ASCIIWWNSIZE]; 179 180 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 181 "client-guid", (char **)&wwn) > 0) { 182 if (strlcpy((char *)ascii_wwn, (char *)wwn, 183 sizeof (ascii_wwn)) >= sizeof (ascii_wwn)) { 184 devfsadm_errprint("SUNW_disk_link: GUID too long:%d", 185 strlen((char *)wwn)); 186 return (DEVFSADM_CONTINUE); 187 } 188 lun = 0; 189 } else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 190 "port-wwn", &wwn) > 0) { 191 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, 192 "lun", &intp) > 0) { 193 lun = *intp; 194 } else { 195 lun = 0; 196 } 197 198 for (count = 0, str = ascii_wwn; count < 8; count++, str += 2) { 199 (void) sprintf((caddr_t)str, "%02x", wwn[count]); 200 } 201 *str = '\0'; 202 } else { 203 return (DEVFSADM_CONTINUE); 204 } 205 206 for (str = ascii_wwn; *str != '\0'; str++) { 207 *str = DISK_LINK_TO_UPPER(*str); 208 } 209 210 (void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", ascii_wwn, lun); 211 212 disk_common(minor, node, disk, RM_STALE); 213 214 return (DEVFSADM_CONTINUE); 215 } 216 217 /* 218 * This function is called for every disk minor node. 219 * Calls enumerate to assign a logical controller number, and 220 * then devfsadm_mklink to make the link. 221 */ 222 static void 223 disk_common(di_minor_t minor, di_node_t node, char *disk, int flags) 224 { 225 char l_path[PATH_MAX + 1]; 226 char sec_path[PATH_MAX + 1]; 227 char stale_re[DISK_SUBPATH_MAX]; 228 char *dir; 229 char slice[4]; 230 char *mn; 231 char *ctrl; 232 char *nt = NULL; 233 int *int_prop; 234 int nflags = 0; 235 236 if (strstr(mn = di_minor_name(minor), ",raw")) { 237 dir = "rdsk"; 238 } else { 239 dir = "dsk"; 240 } 241 242 if (mn[0] < 113) { 243 (void) sprintf(slice, "s%d", mn[0] - 'a'); 244 } else if (strncmp(mn, MN_EFI, 2) != 0) { 245 (void) sprintf(slice, "p%d", mn[0] - 'q'); 246 } else { 247 /* For EFI label */ 248 (void) sprintf(slice, SLICE_EFI); 249 } 250 251 nflags = 0; 252 if (system_labeled) { 253 nt = di_minor_nodetype(minor); 254 if ((nt != NULL) && 255 ((strcmp(nt, DDI_NT_CD) == 0) || 256 (strcmp(nt, DDI_NT_CD_CHAN) == 0) || 257 (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) { 258 nflags = DA_ADD|DA_CD; 259 } 260 } 261 262 if (reserved_links_exist(node, minor, nflags) == DEVFSADM_SUCCESS) { 263 devfsadm_print(disk_mid, "Reserved link exists. Not " 264 "creating links for slice %s\n", slice); 265 return; 266 } 267 268 if (NULL == (ctrl = diskctrl(node, minor))) 269 return; 270 271 (void) strcpy(l_path, dir); 272 (void) strcat(l_path, "/c"); 273 (void) strcat(l_path, ctrl); 274 (void) strcat(l_path, disk); 275 276 /* 277 * If switching between SMI and EFI label or vice versa 278 * cleanup the previous label's devlinks. 279 */ 280 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 281 char *s, tpath[PATH_MAX + 1]; 282 struct stat sb; 283 284 s = l_path + strlen(l_path); 285 (void) strcat(l_path, (*mn == *(MN_SMI)) 286 ? SLICE_EFI : SLICE_SMI); 287 /* 288 * Attempt the remove only if the stale link exists 289 */ 290 (void) snprintf(tpath, sizeof (tpath), "%s/dev/%s", 291 devfsadm_root_path(), l_path); 292 if (lstat(tpath, &sb) != -1) 293 devfsadm_rm_all(l_path); 294 *s = '\0'; 295 } 296 (void) strcat(l_path, slice); 297 298 (void) devfsadm_mklink(l_path, node, minor, nflags); 299 300 /* secondary links for removable and hotpluggable devices */ 301 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", 302 &int_prop) >= 0) { 303 (void) strcpy(sec_path, "removable-media/"); 304 (void) strcat(sec_path, l_path); 305 (void) devfsadm_secondary_link(sec_path, l_path, 0); 306 } 307 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", 308 &int_prop) >= 0) { 309 (void) strcpy(sec_path, "hotpluggable/"); 310 (void) strcat(sec_path, l_path); 311 (void) devfsadm_secondary_link(sec_path, l_path, 0); 312 } 313 314 if ((flags & RM_STALE) == RM_STALE) { 315 (void) strcpy(stale_re, "^"); 316 (void) strcat(stale_re, dir); 317 (void) strcat(stale_re, "/c"); 318 (void) strcat(stale_re, ctrl); 319 (void) strcat(stale_re, "t[0-9A-F]+d[0-9]+(s[0-9]+)?$"); 320 /* 321 * optimizations are made inside of devfsadm_rm_stale_links 322 * instead of before calling the function, as it always 323 * needs to add the valid link to the cache. 324 */ 325 devfsadm_rm_stale_links(stale_re, l_path, node, minor); 326 } 327 328 free(ctrl); 329 } 330 331 332 /* index of enumeration rule applicable to this module */ 333 #define RULE_INDEX 0 334 335 static char * 336 diskctrl(di_node_t node, di_minor_t minor) 337 { 338 char path[PATH_MAX + 1]; 339 char *devfspath; 340 char *buf, *mn; 341 342 devfsadm_enumerate_t rules[3] = { 343 {"^r?dsk$/^c([0-9]+)", 1, MATCH_PARENT}, 344 {"^cfg$/^c([0-9]+)$", 1, MATCH_ADDR}, 345 {"^scsi$/^.+$/^c([0-9]+)", 1, MATCH_PARENT} 346 }; 347 348 mn = di_minor_name(minor); 349 350 if ((devfspath = di_devfs_path(node)) == NULL) { 351 return (NULL); 352 } 353 (void) strcpy(path, devfspath); 354 (void) strcat(path, ":"); 355 (void) strcat(path, mn); 356 di_devfs_path_free(devfspath); 357 358 /* 359 * Use controller component of disk path 360 */ 361 if (disk_enumerate_int(path, RULE_INDEX, &buf, rules, 3) == 362 DEVFSADM_MULTIPLE) { 363 364 /* 365 * We failed because there are multiple logical controller 366 * numbers for a single physical controller. If we use node 367 * name also in the match it should fix this and only find one 368 * logical controller. (See 4045879). 369 * NOTE: Rules for controllers are not changed, as there is 370 * no unique controller number for them in this case. 371 * 372 * MATCH_UNCACHED flag is private to the "disks" and "sgen" 373 * modules. NOT to be used by other modules. 374 */ 375 376 rules[0].flags = MATCH_NODE | MATCH_UNCACHED; /* disks */ 377 rules[2].flags = MATCH_NODE | MATCH_UNCACHED; /* generic scsi */ 378 if (devfsadm_enumerate_int(path, RULE_INDEX, &buf, rules, 3)) { 379 return (NULL); 380 } 381 } 382 383 return (buf); 384 } 385 386 typedef struct dvlist { 387 char *dv_link; 388 struct dvlist *dv_next; 389 } dvlist_t; 390 391 static void 392 free_dvlist(dvlist_t **pp) 393 { 394 dvlist_t *entry; 395 396 while (*pp) { 397 entry = *pp; 398 *pp = entry->dv_next; 399 assert(entry->dv_link); 400 free(entry->dv_link); 401 free(entry); 402 } 403 } 404 static int 405 dvlink_cb(di_devlink_t devlink, void *arg) 406 { 407 char *path; 408 char *can_path; 409 dvlist_t **pp = (dvlist_t **)arg; 410 dvlist_t *entry = NULL; 411 412 entry = calloc(1, sizeof (dvlist_t)); 413 if (entry == NULL) { 414 devfsadm_errprint("%s: calloc failed\n", modname); 415 goto error; 416 } 417 418 path = (char *)di_devlink_path(devlink); 419 assert(path); 420 if (path == NULL) { 421 devfsadm_errprint("%s: di_devlink_path() returned NULL\n", 422 modname); 423 goto error; 424 } 425 426 devfsadm_print(disk_mid, "%s: found link %s in reverse link cache\n", 427 modname, path); 428 429 /* 430 * Return linkname in canonical form i.e. without the 431 * "/dev/" prefix 432 */ 433 can_path = strstr(path, "/dev/"); 434 if (can_path == NULL) { 435 devfsadm_errprint("%s: devlink path %s has no /dev/\n", 436 modname, path); 437 goto error; 438 } 439 440 entry->dv_link = s_strdup(can_path + strlen("/dev/")); 441 entry->dv_next = *pp; 442 *pp = entry; 443 444 return (DI_WALK_CONTINUE); 445 446 error: 447 free(entry); 448 free_dvlist(pp); 449 *pp = NULL; 450 return (DI_WALK_TERMINATE); 451 } 452 453 /* 454 * Returns success only if all goes well. If there is no matching reserved link 455 * or if there is an error, we assume no match. It is better to err on the side 456 * of caution by creating extra links than to miss out creating a required link. 457 */ 458 static int 459 reserved_links_exist(di_node_t node, di_minor_t minor, int nflags) 460 { 461 di_devlink_handle_t dvlink_cache = devfsadm_devlink_cache(); 462 char phys_path[PATH_MAX]; 463 char *minor_path; 464 dvlist_t *head; 465 dvlist_t *entry; 466 char *s; 467 char l[PATH_MAX]; 468 int switch_link = 0; 469 char *mn = di_minor_name(minor); 470 471 if (dvlink_cache == NULL || mn == NULL) { 472 devfsadm_errprint("%s: No minor or devlink cache\n", modname); 473 return (DEVFSADM_FAILURE); 474 } 475 476 if (!devfsadm_have_reserved()) { 477 devfsadm_print(disk_mid, "%s: No reserved links\n", modname); 478 return (DEVFSADM_FAILURE); 479 } 480 481 minor_path = di_devfs_minor_path(minor); 482 if (minor_path == NULL) { 483 devfsadm_errprint("%s: di_devfs_minor_path failed\n", modname); 484 return (DEVFSADM_FAILURE); 485 } 486 487 (void) strlcpy(phys_path, minor_path, sizeof (phys_path)); 488 489 di_devfs_path_free(minor_path); 490 491 head = NULL; 492 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, phys_path, 493 DI_PRIMARY_LINK, &head, dvlink_cb); 494 495 /* 496 * We may be switching between EFI label and SMI label in which case 497 * we only have minors of the other type. 498 */ 499 if (head == NULL && (*mn == *(MN_SMI) || 500 (strncmp(mn, MN_EFI, 2) == 0))) { 501 devfsadm_print(disk_mid, "%s: No links for minor %s in /dev. " 502 "Trying another label\n", modname, mn); 503 s = strrchr(phys_path, ':'); 504 if (s == NULL) { 505 devfsadm_errprint("%s: invalid minor path: %s\n", 506 modname, phys_path); 507 return (DEVFSADM_FAILURE); 508 } 509 (void) snprintf(s+1, sizeof (phys_path) - (s + 1 - phys_path), 510 "%s%s", *mn == *(MN_SMI) ? MN_EFI : MN_SMI, 511 strstr(s, ",raw") ? ",raw" : ""); 512 (void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, 513 phys_path, DI_PRIMARY_LINK, &head, dvlink_cb); 514 } 515 516 if (head == NULL) { 517 devfsadm_print(disk_mid, "%s: minor %s has no links in /dev\n", 518 modname, phys_path); 519 /* no links on disk */ 520 return (DEVFSADM_FAILURE); 521 } 522 523 /* 524 * It suffices to use 1 link to this minor, since 525 * we are matching with reserved IDs on the basis of 526 * the controller number which will be the same for 527 * all links to this minor. 528 */ 529 if (!devfsadm_is_reserved(disks_re_array, head->dv_link)) { 530 /* not reserved links */ 531 devfsadm_print(disk_mid, "%s: devlink %s and its minor " 532 "are NOT reserved\n", modname, head->dv_link); 533 free_dvlist(&head); 534 return (DEVFSADM_FAILURE); 535 } 536 537 devfsadm_print(disk_mid, "%s: devlink %s and its minor are on " 538 "reserved list\n", modname, head->dv_link); 539 540 /* 541 * Switch between SMI and EFI labels if required 542 */ 543 switch_link = 0; 544 if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) { 545 for (entry = head; entry; entry = entry->dv_next) { 546 s = strrchr(entry->dv_link, '/'); 547 assert(s); 548 if (s == NULL) { 549 devfsadm_errprint("%s: disk link %s has no " 550 "directory\n", modname, entry->dv_link); 551 continue; 552 } 553 if (*mn == *(MN_SMI) && strchr(s, 's') == NULL) { 554 (void) snprintf(l, sizeof (l), "%s%s", 555 entry->dv_link, SLICE_SMI); 556 switch_link = 1; 557 devfsadm_print(disk_mid, "%s: switching " 558 "reserved link from EFI to SMI label. " 559 "New link is %s\n", modname, l); 560 } else if (strncmp(mn, MN_EFI, 2) == 0 && 561 (s = strchr(s, 's'))) { 562 *s = '\0'; 563 (void) snprintf(l, sizeof (l), "%s", 564 entry->dv_link); 565 *s = 's'; 566 switch_link = 1; 567 devfsadm_print(disk_mid, "%s: switching " 568 "reserved link from SMI to EFI label. " 569 "New link is %s\n", modname, l); 570 } 571 if (switch_link) { 572 devfsadm_print(disk_mid, "%s: switching " 573 "link: deleting %s and creating %s\n", 574 modname, entry->dv_link, l); 575 devfsadm_rm_link(entry->dv_link); 576 (void) devfsadm_mklink(l, node, minor, nflags); 577 } 578 } 579 } 580 free_dvlist(&head); 581 582 /* 583 * return SUCCESS to indicate that new links to this minor should not 584 * be created so that only compatibility links to this minor remain. 585 */ 586 return (DEVFSADM_SUCCESS); 587 } 588