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 2006 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 <sys/stat.h> 29 #include <sys/types.h> 30 31 /* 32 * Dependent on types.h, but not including it... 33 */ 34 #include <stdio.h> 35 #include <sys/types.h> 36 #include <sys/dkio.h> 37 #include <sys/dktp/fdisk.h> 38 #include <sys/mnttab.h> 39 #include <sys/mntent.h> 40 #include <sys/sysmacros.h> 41 #include <sys/mkdev.h> 42 #include <sys/vfs.h> 43 #include <nfs/nfs.h> 44 #include <nfs/nfs_clnt.h> 45 #include <kstat.h> 46 #include <ctype.h> 47 #include <dirent.h> 48 #include <libdevinfo.h> 49 #include <limits.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <strings.h> 53 #include <unistd.h> 54 #include <errno.h> 55 #include <devid.h> 56 #include <sys/scsi/adapters/scsi_vhci.h> 57 58 #include "dsr.h" 59 #include "statcommon.h" 60 61 /* where we get kstat name translation information from */ 62 static di_node_t di_root; /* from di_init: for devid */ 63 static di_dim_t di_dim; /* from di_dim_init: for /dev names */ 64 static int scsi_vhci_fd = -1; /* from scsi_vhci: for mpxio path */ 65 66 /* disk/tape/misc info */ 67 typedef struct { 68 char *minor_name; 69 int minor_isdisk; 70 } minor_match_t; 71 static minor_match_t mm_disk = {"a", 1}; 72 static minor_match_t mm_tape = {"", 0}; 73 static minor_match_t mm_misc = {"0", 0}; 74 static char md_minor_name[MAXPATHLEN]; 75 static minor_match_t mm_md = {md_minor_name, 0}; 76 static minor_match_t *mma_disk_tape_misc[] = 77 {&mm_disk, &mm_tape, &mm_misc, NULL}; 78 static minor_match_t *mma_md[] = {&mm_md, NULL}; 79 static char *mdsetno2name(int setno); 80 #define DISKLIST_MOD 256 /* ^2 instunit mod hash */ 81 static disk_list_t *disklist[DISKLIST_MOD]; 82 83 84 /* nfs info */ 85 extern kstat_ctl_t *kc; 86 extern mnt_t *nfs; 87 static int nfs_tried; 88 static char *get_nfs_by_minor(uint_t); 89 static char *cur_hostname(uint_t, kstat_ctl_t *); 90 static char *cur_special(char *, char *); 91 92 /* 93 * Clear the snapshot so a cache miss in lookup_ks_name() will cause a fresh 94 * snapshot in drvinstunit2dev(). 95 */ 96 void 97 cleanup_iodevs_snapshot() 98 { 99 if (di_dim) { 100 di_dim_fini(di_dim); 101 di_dim = NULL; 102 } 103 104 if (di_root) { 105 di_fini(di_root); 106 di_root = DI_NODE_NIL; 107 } 108 109 nfs_tried = 0; 110 } 111 112 /* 113 * Find information for (driver, instunit) device: return zero on failure. 114 * 115 * NOTE: Failure of drvinstunit2dev works out OK for the caller if the kstat 116 * name is the same as public name: the caller will just use kstat name. 117 */ 118 static int 119 drvinstunitpart2dev(char *driver, int instunit, char *part, 120 char **devpathp, char **adevpathp, char **devidp) 121 { 122 int instance; 123 minor_match_t **mma; 124 minor_match_t *mm; 125 char *devpath; 126 char *devid; 127 char *a, *s; 128 int mdsetno; 129 char *mdsetname = NULL; 130 char amdsetname[MAXPATHLEN]; 131 char *devicespath; 132 di_node_t node; 133 134 /* setup "no result" return values */ 135 if (devpathp) 136 *devpathp = NULL; 137 if (adevpathp) 138 *adevpathp = NULL; 139 if (devidp) 140 *devidp = NULL; 141 142 /* take <driver><instance><minor_name> snapshot if not established */ 143 if (di_dim == NULL) { 144 di_dim = di_dim_init(); 145 if (di_dim == NULL) 146 return (0); 147 } 148 149 /* 150 * Determine if 'instunit' is an 'instance' or 'unit' based on the 151 * 'driver'. The current code only detects 'md' metadevice 'units', 152 * and defaults to 'instance' for everything else. 153 * 154 * For a metadevice, 'driver' is either "md" or "<setno>/md". 155 */ 156 s = strstr(driver, "/md"); 157 if ((strcmp(driver, "md") == 0) || 158 (s && isdigit(*driver) && (strcmp(s, "/md") == 0))) { 159 /* 160 * "md" unit: Special case translation of "md" kstat names. 161 * For the local set the kstat name is "md<unit>", and for 162 * a shared set the kstat name is "<setno>/md<unit>": we map 163 * these to the minor paths "/pseudo/md@0:<unit>,blk" and 164 * "/pseudo/md@0:<set>,<unit>,blk" respectively. 165 */ 166 if (isdigit(*driver)) { 167 mdsetno = atoi(driver); 168 169 /* convert setno to setname */ 170 mdsetname = mdsetno2name(mdsetno); 171 } else 172 mdsetno = 0; 173 174 driver = "md"; 175 instance = 0; 176 mma = mma_md; /* metadevice dynamic minor */ 177 (void) snprintf(md_minor_name, sizeof (md_minor_name), 178 "%d,%d,blk", mdsetno, instunit); 179 } else { 180 instance = instunit; 181 mma = mma_disk_tape_misc; /* disk/tape/misc minors */ 182 } 183 184 if (part) { 185 devpath = di_dim_path_dev(di_dim, driver, instance, part); 186 } else { 187 /* Try to find a minor_match that works */ 188 for (mm = *mma++; mm; mm = *mma++) { 189 if ((devpath = di_dim_path_dev(di_dim, 190 driver, instance, mm->minor_name)) != NULL) 191 break; 192 } 193 } 194 if (devpath == NULL) 195 return (0); 196 197 /* 198 * At this point we have a devpath result. Return the information about 199 * the result that the caller is asking for. 200 */ 201 if (devpathp) /* devpath */ 202 *devpathp = safe_strdup(devpath); 203 204 if (adevpathp) { /* abbreviated devpath */ 205 if ((part == NULL) && mm->minor_isdisk) { 206 /* 207 * For disk kstats without a partition we return the 208 * last component with trailing "s#" or "p#" stripped 209 * off (i.e. partition/slice information is removed). 210 * For example for devpath of "/dev/dsk/c0t0d0s0" the 211 * abbreviated devpath would be "c0t0d0". 212 */ 213 a = strrchr(devpath, '/'); 214 if (a == NULL) { 215 free(devpath); 216 return (0); 217 } 218 a++; 219 s = strrchr(a, 's'); 220 if (s == NULL) { 221 s = strrchr(a, 'p'); 222 if (s == NULL) { 223 free(devpath); 224 return (0); 225 } 226 } 227 /* don't include slice information in devpath */ 228 *s = '\0'; 229 } else { 230 /* 231 * remove "/dev/", and "/dsk/", from 'devpath' (like 232 * "/dev/md/dsk/d0") to form the abbreviated devpath 233 * (like "md/d0"). 234 */ 235 if ((s = strstr(devpath, "/dev/")) != NULL) 236 (void) strcpy(s + 1, s + 5); 237 if ((s = strstr(devpath, "/dsk/")) != NULL) 238 (void) strcpy(s + 1, s + 5); 239 240 /* 241 * If we have an mdsetname, convert abbreviated setno 242 * notation (like "md/shared/1/d0" to abbreviated 243 * setname notation (like "md/red/d0"). 244 */ 245 if (mdsetname) { 246 a = strrchr(devpath, '/'); 247 (void) snprintf(amdsetname, sizeof (amdsetname), 248 "md/%s%s", mdsetname, a); 249 free(mdsetname); 250 a = amdsetname; 251 } else { 252 if (*devpath == '/') 253 a = devpath + 1; 254 else 255 a = devpath; 256 } 257 } 258 *adevpathp = safe_strdup(a); 259 } 260 261 if (devidp) { /* lookup the devid */ 262 /* take snapshot if not established */ 263 if (di_root == DI_NODE_NIL) { 264 di_root = di_init("/", DINFOCACHE); 265 } 266 if (di_root) { 267 /* get path to /devices devinfo node */ 268 devicespath = di_dim_path_devices(di_dim, 269 driver, instance, NULL); 270 if (devicespath) { 271 /* find the node in the snapshot */ 272 node = di_lookup_node(di_root, devicespath); 273 free(devicespath); 274 275 /* and lookup devid property on the node */ 276 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 277 DEVID_PROP_NAME, &devid) != -1) 278 *devidp = devid; 279 } 280 } 281 } 282 283 free(devpath); 284 return (1); /* success */ 285 } 286 287 /* 288 * Do <pid> to 'target-port' translation 289 */ 290 static int 291 drvpid2port(uint_t pid, char **target_portp) 292 { 293 sv_iocdata_t ioc; 294 char target_port[MAXNAMELEN]; 295 296 /* setup "no result" return values */ 297 *target_portp = NULL; 298 299 /* open scsi_vhci if not already done */ 300 if (scsi_vhci_fd == -1) { 301 scsi_vhci_fd = open("/devices/scsi_vhci:devctl", O_RDONLY); 302 if (scsi_vhci_fd == -1) 303 return (0); /* failure */ 304 } 305 306 /* 307 * Perform ioctl for <pid> -> 'target-port' translation. 308 * 309 * NOTE: it is legimite for this ioctl to fail for transports 310 * that use mpxio, but don't set a 'target-port' pathinfo property. 311 * On failure we return the the "<pid>" as the target port string. 312 */ 313 bzero(&ioc, sizeof (sv_iocdata_t)); 314 ioc.buf_elem = pid; 315 ioc.addr = target_port; 316 if (ioctl(scsi_vhci_fd, SCSI_VHCI_GET_TARGET_LONGNAME, &ioc) < 0) { 317 (void) snprintf(target_port, sizeof (target_port), "%d", pid); 318 } 319 320 *target_portp = safe_strdup(target_port); 321 return (1); /* success */ 322 } 323 324 /* 325 * Find/create a disk_list entry for given a kstat name. 326 * The basic format of a kstat name is 327 * 328 * "<driver><instunit>.<pid>.<phci-driver><instance>,<partition>". 329 * 330 * The <instunit> is a decimal number. The ".<pid>.<phci-driver><instance>", 331 * which describes mpxio path stat information, and ",<partition>" parts are 332 * optional. The <pid> consists of the letter 't' followed by a decimal number. 333 * When available, we use the <pid> to find the 'target-port' via ioctls to 334 * the scsi_vhci driver. 335 * 336 * NOTE: In the case of non-local metadevices, the format of "<driver>" in 337 * a kstat name is acutally "<setno>/md". 338 */ 339 disk_list_t * 340 lookup_ks_name(char *ks_name, int want_devid) 341 { 342 char *pidp; /* ".<pid>... */ 343 char *part; /* ",partition... */ 344 char *initiator; /* ".<phci-driver>... */ 345 char *p; 346 int len; 347 char driver[KSTAT_STRLEN]; 348 int instunit; 349 disk_list_t **dlhp; /* disklist head */ 350 disk_list_t *entry; 351 char *devpath = NULL; 352 char *adevpath = NULL; 353 char *devid = NULL; 354 int pid; 355 char *target_port = NULL; 356 char portform[MAXPATHLEN]; 357 358 /* Filter out illegal forms (like all digits). */ 359 if ((ks_name == NULL) || (*ks_name == 0) || 360 (strspn(ks_name, "0123456789") == strlen(ks_name))) 361 goto fail; 362 363 /* parse ks_name to create new entry */ 364 pidp = strchr(ks_name, '.'); /* start of ".<pid>" */ 365 initiator = strrchr(ks_name, '.'); /* start of ".<pHCI-driver>" */ 366 if (pidp && (pidp == initiator)) /* can't have same start */ 367 goto fail; 368 369 part = strchr(ks_name, ','); /* start of ",<partition>" */ 370 p = strchr(ks_name, ':'); /* start of ":<partition>" */ 371 if (part && p) 372 goto fail; /* can't have both */ 373 if (p) 374 part = p; 375 if (part && pidp) 376 goto fail; /* <pid> and partition: bad */ 377 378 p = part ? part : pidp; 379 if (p == NULL) 380 p = &ks_name[strlen(ks_name) - 1]; /* last char */ 381 else 382 p--; /* before ',' or '.' */ 383 384 while ((p >= ks_name) && isdigit(*p)) 385 p--; /* backwards over digits */ 386 p++; /* start of instunit */ 387 if ((*p == '\0') || (*p == ',') || (*p == '.') || (*p == ':')) 388 goto fail; /* no <instunit> */ 389 len = p - ks_name; 390 (void) strncpy(driver, ks_name, len); 391 driver[len] = '\0'; 392 instunit = atoi(p); 393 if (part) 394 part++; /* skip ',' */ 395 396 /* hash by instunit and search for existing entry */ 397 dlhp = &disklist[instunit & (DISKLIST_MOD - 1)]; 398 for (entry = *dlhp; entry; entry = entry->next) { 399 if (strcmp(entry->ks_name, ks_name) == 0) { 400 return (entry); 401 } 402 } 403 404 /* not found, translate kstat_name components and create new entry */ 405 406 /* translate kstat_name dev information */ 407 if (drvinstunitpart2dev(driver, instunit, part, 408 &devpath, &adevpath, want_devid ? &devid : NULL) == 0) { 409 goto fail; 410 } 411 412 /* parse and translate path information */ 413 if (pidp) { 414 /* parse path information: ".t#.<phci-driver><instance>" */ 415 pidp++; /* skip '.' */ 416 initiator++; /* skip '.' */ 417 if ((*pidp != 't') || !isdigit(pidp[1])) 418 goto fail; /* not ".t#" */ 419 pid = atoi(&pidp[1]); 420 421 /* translate <pid> to 'target-port' */ 422 if (drvpid2port(pid, &target_port) == 0) 423 goto fail; 424 425 /* Establish 'target-port' form. */ 426 (void) snprintf(portform, sizeof (portform), 427 "%s.t%s.%s", adevpath, target_port, initiator); 428 free(target_port); 429 free(adevpath); 430 adevpath = strdup(portform); 431 } 432 433 /* make a new entry ... */ 434 entry = safe_alloc(sizeof (disk_list_t)); 435 entry->ks_name = safe_strdup(ks_name); 436 entry->dname = devpath; 437 entry->dsk = adevpath; 438 entry->devidstr = devid; 439 440 #ifdef DEBUG 441 (void) printf("lookup_ks_name: new: %s %s\n", 442 ks_name, entry->dsk ? entry->dsk : "NULL"); 443 #endif /* DEBUG */ 444 445 /* add new entry to head of hashed list */ 446 entry->next = *dlhp; 447 *dlhp = entry; 448 return (entry); 449 450 fail: 451 free(devpath); 452 free(adevpath); 453 free(devid); 454 #ifdef DEBUG 455 (void) printf("lookup_ks_name: failed: %s\n", ks_name); 456 #endif /* DEBUG */ 457 return (NULL); 458 } 459 460 /* 461 * Convert metadevice setno to setname by looking in /dev/md for symlinks 462 * that point to "shared/setno" - the name of such a symlink is the setname. 463 * The caller is responsible for freeing the returned string. 464 */ 465 static char * 466 mdsetno2name(int setno) 467 { 468 char setlink[MAXPATHLEN + 1]; 469 char link[MAXPATHLEN + 1]; 470 char path[MAXPATHLEN + 1]; 471 char *p; 472 DIR *dirp; 473 struct dirent *dp; 474 size_t len; 475 char *mdsetname = NULL; 476 477 /* we are looking for a link to setlink */ 478 (void) snprintf(setlink, MAXPATHLEN, "shared/%d", setno); 479 480 /* in the directory /dev/md */ 481 (void) strcpy(path, "/dev/md/"); 482 p = path + strlen(path); 483 dirp = opendir(path); 484 if (dirp == NULL) 485 return (NULL); 486 487 /* loop through /dev/md directory entries */ 488 while ((dp = readdir(dirp)) != NULL) { 489 490 /* doing a readlink of entry (fails for non-symlinks) */ 491 *p = '\0'; 492 (void) strcpy(p, dp->d_name); 493 if ((len = readlink(path, link, MAXPATHLEN)) == (size_t)-1) 494 continue; 495 496 /* and looking for a link to setlink */ 497 link[len] = '\0'; 498 if (strcmp(setlink, link)) 499 continue; 500 501 /* found- name of link is the setname */ 502 mdsetname = safe_strdup(dp->d_name); 503 break; 504 } 505 506 (void) closedir(dirp); 507 return (mdsetname); 508 } 509 510 char * 511 lookup_nfs_name(char *ks, kstat_ctl_t *kc) 512 { 513 uint_t minor; 514 char *host, *path; 515 char *cp; 516 char *rstr = 0; 517 size_t len; 518 519 if (sscanf(ks, "nfs%u", &minor) == 1) { 520 retry: 521 cp = get_nfs_by_minor(minor); 522 if (cp) { 523 if (strchr(cp, ',') == NULL) { 524 rstr = safe_strdup(cp); 525 return (rstr); 526 } 527 host = cur_hostname(minor, kc); 528 if (host) { 529 if (*host) { 530 path = cur_special(host, cp); 531 if (path) { 532 len = strlen(host); 533 len += strlen(path); 534 len += 2; 535 rstr = safe_alloc(len); 536 (void) snprintf(rstr, len, 537 "%s:%s", host, path); 538 } else { 539 rstr = safe_strdup(cp); 540 } 541 } else { 542 rstr = safe_strdup(ks); 543 } 544 free(host); 545 } else { 546 rstr = safe_strdup(cp); 547 } 548 } else if (nfs_tried == 0) { 549 nfs_tried = 1; 550 do_mnttab(); 551 goto retry; 552 } 553 } 554 return (rstr); 555 } 556 557 static char * 558 get_nfs_by_minor(uint_t minor) 559 { 560 mnt_t *localnfs; 561 562 localnfs = nfs; 563 while (localnfs) { 564 if (localnfs->minor == minor) { 565 return (localnfs->device_name); 566 } 567 localnfs = localnfs->next; 568 } 569 return (0); 570 } 571 572 /* 573 * Read the cur_hostname from the mntinfo kstat 574 */ 575 static char * 576 cur_hostname(uint_t minor, kstat_ctl_t *kc) 577 { 578 kstat_t *ksp; 579 static struct mntinfo_kstat mik; 580 char *rstr; 581 582 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 583 if (ksp->ks_type != KSTAT_TYPE_RAW) 584 continue; 585 if (ksp->ks_instance != minor) 586 continue; 587 if (strcmp(ksp->ks_module, "nfs")) 588 continue; 589 if (strcmp(ksp->ks_name, "mntinfo")) 590 continue; 591 if (ksp->ks_flags & KSTAT_FLAG_INVALID) 592 return (NULL); 593 if (kstat_read(kc, ksp, &mik) == -1) 594 return (NULL); 595 rstr = safe_strdup(mik.mik_curserver); 596 return (rstr); 597 } 598 return (NULL); 599 } 600 601 /* 602 * Given the hostname of the mounted server, extract the server 603 * mount point from the mnttab string. 604 * 605 * Common forms: 606 * server1,server2,server3:/path 607 * server1:/path,server2:/path 608 * or a hybrid of the two 609 */ 610 static char * 611 cur_special(char *hostname, char *special) 612 { 613 char *cp; 614 char *path; 615 size_t hlen = strlen(hostname); 616 617 /* 618 * find hostname in string 619 */ 620 again: 621 if ((cp = strstr(special, hostname)) == NULL) 622 return (NULL); 623 624 /* 625 * hostname must be followed by ',' or ':' 626 */ 627 if (cp[hlen] != ',' && cp[hlen] != ':') { 628 special = &cp[hlen]; 629 goto again; 630 } 631 632 /* 633 * If hostname is followed by a ',' eat all characters until a ':' 634 */ 635 cp = &cp[hlen]; 636 if (*cp == ',') { 637 cp++; 638 while (*cp != ':') { 639 if (*cp == NULL) 640 return (NULL); 641 cp++; 642 } 643 } 644 path = ++cp; /* skip ':' */ 645 646 /* 647 * path is terminated by either 0, or space or ',' 648 */ 649 while (*cp) { 650 if (isspace(*cp) || *cp == ',') { 651 *cp = NULL; 652 return (path); 653 } 654 cp++; 655 } 656 return (path); 657 } 658