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 27 /*LINTLIBRARY*/ 28 29 /* 30 * I18N message number ranges 31 * This file: (not defined yet) 32 * Shared common messages: 1 - 1999 33 */ 34 35 /* 36 * This module is part of the Fibre Channel Interface library. 37 */ 38 39 /* #define _POSIX_SOURCE 1 */ 40 41 42 /* Includes */ 43 #include <stdlib.h> 44 #include <stdio.h> 45 #include <sys/file.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <sys/mkdev.h> 49 #include <sys/param.h> 50 #include <fcntl.h> 51 #include <unistd.h> 52 #include <string.h> 53 #include <sys/scsi/scsi.h> 54 #include <dirent.h> /* for DIR */ 55 #include <sys/vtoc.h> 56 #include <nl_types.h> 57 #include <strings.h> 58 #include <sys/ddi.h> /* for max */ 59 #include <fnmatch.h> 60 #include <l_common.h> 61 #include <stgcom.h> 62 #include <l_error.h> 63 #include <g_state.h> 64 #include <sys/fibre-channel/ulp/fcp_util.h> 65 #include <sys/fibre-channel/impl/fc_error.h> 66 #include <sys/fibre-channel/impl/fcph.h> 67 #include <sys/socalio.h> 68 #include <libdevinfo.h> 69 #include <libnvpair.h> 70 #include <sys/scsi/adapters/scsi_vhci.h> 71 #include <errno.h> 72 73 /* Some forward declarations of static functions */ 74 static void g_free_pi_list(sv_path_info_t *, uint_t num_paths); 75 static int get_pathlist(char *, sv_iocdata_t *, int *); 76 static int stms_path_enable_disable(char *, char *, int); 77 static int stms_path_enable_disable_all(char *, int); 78 79 /* 80 * To get lun number of a given device pathname using driver ioctl. 81 * This interface is called directly by g_get_lun_number 82 * 83 * inputs; 84 * outputs: 85 * returns: 86 * 0 - success 87 * !0 - failure 88 */ 89 int 90 g_get_lun_str(char *dev_path, char lunstr[], int path_num) 91 { 92 char *char_ptr, *charptr1; 93 int fd = 0; 94 sv_iocdata_t ioc; 95 char phci_path[MAXPATHLEN]; 96 char client_path[MAXPATHLEN]; 97 char paddr[MAXNAMELEN]; 98 uint_t num_elem = 0, i; 99 sv_path_info_t *pi = NULL; 100 int retval = 0; 101 uint_t num_paths; 102 103 if (strstr(dev_path, "/devices") == NULL) { 104 return (-1); 105 } 106 107 num_paths = path_num + 1; 108 (void) strcpy(client_path, dev_path + DEV_PREFIX_LEN-1); 109 if ((char_ptr = strrchr(client_path, ':')) != NULL) { 110 *char_ptr = '\0'; 111 } 112 113 ioc.client = client_path; 114 ioc.phci = phci_path; 115 ioc.addr = paddr; 116 ioc.buf_elem = 0; 117 ioc.ret_buf = NULL; 118 ioc.ret_elem = &num_elem; 119 120 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 121 return (L_OPEN_PATH_FAIL); 122 } 123 124 /* Allocate memory for path info structs */ 125 pi = (sv_path_info_t *)calloc((size_t)num_paths, 126 sizeof (sv_path_info_t)); 127 ioc.buf_elem = num_paths; 128 ioc.ret_buf = pi; 129 130 /* Allocate memory for getting per path info properties */ 131 132 for (i = 0; i < num_paths; i++) { 133 pi[i].ret_prop.buf_size = SV_PROP_MAX_BUF_SIZE; 134 if (((pi[i].ret_prop.buf = 135 malloc(SV_PROP_MAX_BUF_SIZE)) == NULL) || 136 ((pi[i].ret_prop.ret_buf_size = 137 malloc(sizeof (*pi[i].ret_prop.ret_buf_size))) 138 == NULL)) { 139 /* Free memory for per path info properties */ 140 g_free_pi_list(pi, num_paths); 141 (void) close(fd); 142 return (-1); 143 } 144 } 145 146 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 147 if (retval != 0) { 148 /* Free memory for per path info properties */ 149 g_free_pi_list(pi, num_paths); 150 (void) close(fd); 151 return (retval); 152 } 153 154 if (path_num < ioc.buf_elem) { 155 charptr1 = strchr(pi[path_num].ret_addr, ','); 156 retval = 0; 157 } else { 158 charptr1 = strchr(pi[0].ret_addr, ','); 159 retval = -1; 160 } 161 162 if (charptr1 != NULL) { 163 charptr1++; 164 if (charptr1 != NULL) { 165 (void) strcpy(lunstr, charptr1); 166 } 167 } 168 169 /* Free memory for per path info properties */ 170 g_free_pi_list(pi, num_paths); 171 (void) close(fd); 172 return (retval); 173 } 174 175 /* 176 * To give the lun number of a given device pathname 177 * 178 * inputs: physical pathname beginning with /devices 179 * outputs: none 180 * returns: lun number (if available) or -1 (if not available or 181 * failure) 182 */ 183 int 184 g_get_lun_number(char *path_phys) 185 { 186 char path0[MAXPATHLEN], lunarr[MAXPATHLEN]; 187 char *charptr1, *charptr2, *charptr3; 188 int lunval = 0; 189 190 if ((strstr(path_phys, "/devices")) == NULL) { 191 return (-1); 192 } 193 194 if (((charptr3 = strstr(path_phys, SLSH_DRV_NAME_SSD)) == NULL) && 195 ((charptr3 = strstr(path_phys, SLSH_DRV_NAME_ST)) == NULL)) { 196 return (-1); 197 } 198 199 (void) strcpy(path0, charptr3); 200 201 if ((charptr2 = strrchr(path0, ':')) != NULL) { 202 *charptr2 = '\0'; 203 } 204 205 if ((charptr1 = strchr(path0, ',')) != NULL) { 206 charptr1++; 207 if (*charptr1 != '0') { 208 (void) strcpy(lunarr, charptr1); 209 } else { 210 return (0); 211 } 212 } else if (strstr(path_phys, SCSI_VHCI) != NULL) { 213 /* for the time being */ 214 if (g_get_lun_str(path_phys, lunarr, 0) != 0) { 215 return (-1); 216 } 217 } else { 218 return (-1); 219 } 220 221 lunval = (int)strtol(lunarr, NULL, 16); 222 223 return (lunval); 224 } 225 226 /* 227 * Input - Space for client_path, phci_path and paddr fields of ioc structure 228 * need to be allocated by the caller of this routine. 229 */ 230 static int 231 get_pathlist(char *dev_path, sv_iocdata_t *ioc, int *num_paths_to_copy) 232 { 233 char *physical_path, *physical_path_s; 234 int retval; 235 int fd; 236 int initial_path_count; 237 int current_path_count; 238 int i; 239 char *delimiter; 240 int malloc_error = 0; 241 int prop_buf_size; 242 int pathlist_retry_count = 0; 243 244 if (strncmp(dev_path, SCSI_VHCI, strlen(SCSI_VHCI)) != 0) { 245 if ((physical_path = g_get_physical_name(dev_path)) == NULL) { 246 return (L_INVALID_PATH); 247 } 248 if (strncmp(physical_path, SCSI_VHCI, strlen(SCSI_VHCI)) != 0) { 249 free(physical_path); 250 return (L_INVALID_PATH); 251 } 252 } else { 253 if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) { 254 return (L_MALLOC_FAILED); 255 } 256 (void) strcpy(physical_path, dev_path); 257 } 258 physical_path_s = physical_path; 259 260 /* move beyond "/devices" prefix */ 261 physical_path += DEV_PREFIX_LEN-1; 262 /* remove :c,raw suffix */ 263 delimiter = strrchr(physical_path, ':'); 264 /* if we didn't find the ':' fine, else truncate */ 265 if (delimiter != NULL) { 266 *delimiter = '\0'; 267 } 268 269 /* 270 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO 271 * at least twice. The first time will get the path count 272 * and the size of the ioctl propoerty buffer. The second 273 * time will get the path_info for each path. 274 * 275 * It's possible that additional paths are added while this 276 * code is running. If the path count increases between the 277 * 2 ioctl's above, then we'll retry (and assume all is well). 278 */ 279 (void) strcpy(ioc->client, physical_path); 280 ioc->buf_elem = 1; 281 ioc->ret_elem = (uint_t *)&(initial_path_count); 282 ioc->ret_buf = NULL; 283 284 /* free physical path */ 285 free(physical_path_s); 286 287 /* 0 buf_size asks driver to return actual size needed */ 288 /* open the ioctl file descriptor */ 289 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 290 return (L_OPEN_PATH_FAIL); 291 } 292 293 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc); 294 if (retval != 0) { 295 close(fd); 296 return (L_SCSI_VHCI_ERROR); 297 } 298 prop_buf_size = SV_PROP_MAX_BUF_SIZE; 299 300 301 while (pathlist_retry_count <= RETRY_PATHLIST) { 302 ioc->buf_elem = initial_path_count; 303 /* Make driver put actual # paths in variable */ 304 ioc->ret_elem = (uint_t *)&(current_path_count); 305 306 /* 307 * Allocate space for array of path_info structures. 308 * Allocate enough space for # paths from get_pathcount 309 */ 310 ioc->ret_buf = (sv_path_info_t *) 311 calloc(initial_path_count, 312 sizeof (sv_path_info_t)); 313 if (ioc->ret_buf == NULL) { 314 close(fd); 315 return (L_MALLOC_FAILED); 316 } 317 318 /* 319 * Allocate space for path properties returned by driver 320 */ 321 malloc_error = 0; 322 for (i = 0; i < initial_path_count; i++) { 323 ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size; 324 if ((ioc->ret_buf[i].ret_prop.buf = 325 (caddr_t)malloc(prop_buf_size)) == NULL) { 326 malloc_error = 1; 327 break; 328 } 329 if ((ioc->ret_buf[i].ret_prop.ret_buf_size = 330 (uint_t *)malloc(sizeof (uint_t))) == NULL) { 331 malloc_error = 1; 332 break; 333 } 334 } 335 if (malloc_error == 1) { 336 for (i = 0; i < initial_path_count; i++) { 337 free(ioc->ret_buf[i].ret_prop.buf); 338 free(ioc->ret_buf[i].ret_prop.ret_buf_size); 339 } 340 free(ioc->ret_buf); 341 close(fd); 342 return (L_MALLOC_FAILED); 343 } 344 345 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc); 346 if (retval != 0) { 347 for (i = 0; i < initial_path_count; i++) { 348 free(ioc->ret_buf[i].ret_prop.buf); 349 free(ioc->ret_buf[i].ret_prop.ret_buf_size); 350 } 351 free(ioc->ret_buf); 352 close(fd); 353 return (L_SCSI_VHCI_ERROR); 354 } 355 if (initial_path_count < current_path_count) { 356 /* then a new path was added */ 357 pathlist_retry_count++; 358 initial_path_count = current_path_count; 359 } else { 360 break; 361 } 362 } 363 /* we are done with ioctl's, lose the fd */ 364 close(fd); 365 366 /* 367 * Compare the length num elements from the ioctl response 368 * and the caller's request - use smaller value. 369 * 370 * pathlist_p->path_count now has count returned from ioctl. 371 * ioc.buf_elem has the value the caller provided. 372 */ 373 if (initial_path_count < current_path_count) { 374 /* More paths exist than we allocated space for */ 375 *num_paths_to_copy = initial_path_count; 376 } else { 377 *num_paths_to_copy = current_path_count; 378 } 379 return (0); 380 } 381 382 /* 383 * To obtain pathlist of a given target device 384 * 385 * inputs: 386 * dev_path client device path 387 * example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw 388 * outputs: 389 * pathlist_p pathlist structure containing pathinfo node data 390 * returns: 391 * 0 - success 392 * !0 - failure 393 */ 394 int 395 g_get_pathlist(char *dev_path, struct mp_pathlist *pathlist_p) 396 { 397 398 sv_iocdata_t ioc; 399 int retval, caller_ret = 0; 400 int num_paths_to_copy; 401 int i; 402 int prop_buf_size; 403 char *path_class_val = NULL; 404 char *temp_addr; 405 char phci_path[MAXPATHLEN]; 406 char client_path[MAXPATHLEN]; 407 char paddr[MAXNAMELEN]; 408 409 410 ioc.client = client_path; 411 ioc.phci = phci_path; 412 ioc.addr = paddr; 413 414 if ((caller_ret = get_pathlist(dev_path, &ioc, &num_paths_to_copy)) 415 != 0) { 416 return (caller_ret); 417 } 418 419 pathlist_p->path_count = num_paths_to_copy; 420 pathlist_p->path_info = calloc(num_paths_to_copy, 421 sizeof (mp_pathinfo_t)); 422 423 prop_buf_size = SV_PROP_MAX_BUF_SIZE; 424 425 if (pathlist_p->path_info == NULL) { 426 caller_ret = L_MALLOC_FAILED; 427 /* force the loop to not run so we free buffers and exit */ 428 num_paths_to_copy = 0; 429 } 430 431 /* get ioctl reponse fields and copy them to caller's buffer */ 432 for (i = 0; i < num_paths_to_copy; i++) { 433 nvlist_t *nvl; 434 435 pathlist_p->path_info[i].path_state = 436 ioc.ret_buf[i].ret_state; 437 (void) strncpy(pathlist_p->path_info[i].path_hba, DEV_PREFIX, 438 DEV_PREFIX_LEN - 1); 439 (void) strcat(pathlist_p->path_info[i].path_hba, 440 ioc.ret_buf[i].device.ret_phci); 441 (void) strcpy(pathlist_p->path_info[i].path_dev, 442 ioc.client); 443 444 /* 445 * Check for leading 'w'. The mpxio framework was 446 * incorrectly implemented to skip 'w' in mdi_pi_get_addr(). 447 * Since the leading 'w' is fibre-channel specific, we 448 * do it here to remove fibre-channel specific behavior 449 * from the mpxio framework. 450 */ 451 temp_addr = ioc.ret_buf[i].ret_addr; 452 if (*temp_addr == 'w') { 453 temp_addr++; 454 } 455 (void) strcpy(pathlist_p->path_info[i].path_addr, temp_addr); 456 457 /* use nvlist_ calls to extract properties from retbuf */ 458 retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf, 459 prop_buf_size, &nvl, 0); 460 if (retval != 0) { /* ??? same retcode */ 461 (void) strcpy(pathlist_p->path_info[i].path_class, 462 "UNKNOWN PROB"); 463 } else { 464 retval = nvlist_lookup_string(nvl, "path-class", 465 &path_class_val); 466 if (retval != 0) { 467 (void) strcpy(pathlist_p->path_info[i].path_class, 468 "UNKNOWN"); 469 } else { 470 (void) strcpy(pathlist_p->path_info[i]. 471 path_class, 472 path_class_val); 473 } 474 nvlist_free(nvl); 475 } 476 } 477 478 /* free everything we alloced */ 479 for (i = 0; i < ioc.buf_elem; i++) { 480 free(ioc.ret_buf[i].ret_prop.buf); 481 free(ioc.ret_buf[i].ret_prop.ret_buf_size); 482 } 483 free(ioc.ret_buf); 484 return (caller_ret); 485 } 486 487 /* 488 * To get the number of paths to a given device pathname using 489 * driver ioctl. 490 * 491 * inputs: 492 * dev path you would like to recieve mp count on 493 * outputs: 494 * returns: 495 * 0 - success 496 * -1 - bad device path 497 * -2 - open failure 498 * -3 - ioctl failure 499 */ 500 int 501 g_get_pathcount(char *dev_path) 502 { 503 char *char_ptr; 504 int fd = -1; 505 sv_iocdata_t ioc; 506 char phci_path[MAXPATHLEN]; 507 char client_path[MAXPATHLEN]; 508 char paddr[MAXNAMELEN]; 509 uint_t num_elem = 0; 510 int retval = 0; 511 char *physical_path; 512 513 /* translate device path to physical path */ 514 physical_path = g_get_physical_name(dev_path); 515 /* ensure physical path is not NULL, or strcpy will core */ 516 if (physical_path == NULL) { 517 return (-1); 518 } 519 /* copy physical path without /devices/ prefix */ 520 (void) strcpy(client_path, physical_path + DEV_PREFIX_LEN-1); 521 free(physical_path); 522 523 if ((char_ptr = strrchr(client_path, ':')) != NULL) { 524 *char_ptr = '\0'; 525 } 526 527 /* prepare sv_iocdata_t structure */ 528 ioc.client = client_path; 529 ioc.phci = phci_path; 530 ioc.addr = paddr; 531 ioc.buf_elem = 0; 532 ioc.ret_buf = NULL; 533 ioc.ret_elem = &num_elem; 534 535 strcpy(ioc.phci, client_path); 536 537 /* Get file descr. for "/devices/scsi_vhci:devctl" */ 538 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 539 return (-2); 540 } 541 542 /* Issue open to device to get multipath_info (ie. count) */ 543 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 544 close(fd); 545 546 /* Check icotl status */ 547 if (retval == 0) { 548 /* success */ 549 return (*ioc.ret_elem); 550 } else { 551 /* failure */ 552 return (-3); 553 } 554 555 } 556 557 558 /* 559 * Call driver to effect failover for a given pathclass 560 * 561 * inputs: 562 * outputs: 563 * returns: 564 * 0 - success 565 * !0 - failure 566 */ 567 int 568 g_failover(char *dev_path, char *path_class) 569 { 570 int fd = 0, ret = 0; 571 char client_path[MAXPATHLEN]; 572 char class[MAXNAMELEN]; 573 sv_switch_to_cntlr_iocdata_t iocsc; 574 575 char *char_ptr_start, *char_ptr_end; 576 577 578 if (strstr(dev_path, SCSI_VHCI) == NULL) { 579 return (L_INVALID_PATH); 580 } 581 582 char_ptr_start = dev_path + strlen("/devices"); 583 if ((char_ptr_end = strrchr(char_ptr_start, ':')) != NULL) { 584 *char_ptr_end = '\0'; 585 } 586 587 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 588 return (L_OPEN_PATH_FAIL); 589 } 590 591 iocsc.client = client_path; 592 iocsc.class = class; 593 594 strcpy(iocsc.client, char_ptr_start); 595 strcpy(iocsc.class, path_class); 596 597 if (ioctl(fd, SCSI_VHCI_SWITCH_TO_CNTLR, &iocsc) != 0) { 598 switch (errno) { 599 case EALREADY: 600 ret = L_SCSI_VHCI_ALREADY_ACTIVE; 601 break; 602 case ENXIO: 603 ret = L_INVALID_PATH; 604 break; 605 case EIO: 606 ret = L_SCSI_VHCI_NO_STANDBY; 607 break; 608 case ENOTSUP: 609 ret = L_SCSI_VHCI_FAILOVER_NOTSUP; 610 break; 611 case EBUSY: 612 ret = L_SCSI_VHCI_FAILOVER_BUSY; 613 break; 614 case EFAULT: 615 default: 616 ret = L_SCSI_VHCI_ERROR; 617 } 618 } 619 620 close(fd); 621 return (ret); 622 } 623 624 static void 625 g_free_pi_list(sv_path_info_t *pi, uint_t num_paths) 626 { 627 sv_path_info_t *pi_h = pi; 628 int i = 0; 629 630 while (i++ < num_paths && pi != NULL) { 631 free(pi->ret_prop.buf); 632 free(pi->ret_prop.ret_buf_size); 633 pi++; 634 } 635 free(pi_h); 636 } 637 638 639 /* 640 * Name: stms_path_enable_disable 641 * 642 * inputs: 643 * 644 * client_path client device path 645 * example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw 646 * 647 * phci Controller device path 648 * example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0 649 * 650 * request should be set to one of the following: 651 * SCSI_VHCI_PATH_DISABLE 652 * SCSI_VHCI_PATH_ENABLE 653 * 654 * returns: 655 * 0 for success 656 * non-zero otherwise 657 */ 658 static int 659 stms_path_enable_disable(char *client_path, char *phci, int request) 660 { 661 char *ioc_phci; 662 char *char_ptr_end; 663 char *client_physical_path, *client_path_ptr; 664 int fd; 665 sv_iocdata_t ioc; 666 667 if (!client_path || !phci) { 668 return (EINVAL); 669 } 670 671 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 672 return (L_OPEN_PATH_FAIL); 673 } 674 675 /* 676 * translate device path to physical path 677 * Save off the ptr for use by free 678 */ 679 client_path_ptr = client_physical_path = 680 g_get_physical_name(client_path); 681 682 /* ensure physical path is not NULL, or strcpy will core */ 683 if (client_physical_path == NULL) { 684 return (EINVAL); 685 } 686 687 /* 688 * Must be a scsi_vhci path 689 */ 690 if (strstr(client_physical_path, SCSI_VHCI) == NULL) { 691 free(client_path_ptr); 692 return (L_INVALID_PATH); 693 } 694 695 /* physical path without /devices/ prefix */ 696 client_physical_path += DEV_PREFIX_LEN - 1; 697 698 if ((char_ptr_end = strrchr(client_physical_path, ':')) != NULL) { 699 *char_ptr_end = '\0'; 700 } 701 702 /* 703 * If there is a '/devices', strip it, if not 704 * assume it is complete and correct 705 */ 706 if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) { 707 ioc_phci = phci + DEV_PREFIX_LEN - 1; 708 } else { 709 ioc_phci = phci; 710 } 711 712 memset(&ioc, 0, sizeof (ioc)); 713 714 ioc.client = client_physical_path; 715 ioc.phci = ioc_phci; 716 717 /* 718 * Issue requested operation 719 */ 720 if (ioctl(fd, request, &ioc) != 0) { 721 free(client_path_ptr); 722 return (errno); 723 } 724 free(client_path_ptr); 725 return (0); 726 } 727 728 int 729 g_stms_path_disable(char *client_path, char *phci) 730 { 731 return (stms_path_enable_disable(client_path, phci, 732 SCSI_VHCI_PATH_DISABLE)); 733 } 734 735 int 736 g_stms_path_enable(char *client_path, char *phci) 737 { 738 return (stms_path_enable_disable(client_path, phci, 739 SCSI_VHCI_PATH_ENABLE)); 740 } 741 742 /* 743 * Name: stms_path_enable_disable_all 744 * 745 * inputs: 746 * 747 * phci Controller device path 748 * example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0 749 * 750 * request should be set to one of the following: 751 * SCSI_VHCI_PATH_DISABLE 752 * SCSI_VHCI_PATH_ENABLE 753 * 754 * returns: 755 * 0 for success 756 * non-zero otherwise 757 */ 758 759 static int 760 stms_path_enable_disable_all(char *phci, int request) 761 { 762 int fd; 763 char *ioc_phci; 764 sv_iocdata_t ioc; 765 766 if (!phci) { 767 return (EINVAL); 768 } 769 770 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 771 return (L_OPEN_PATH_FAIL); 772 } 773 774 memset(&ioc, 0, sizeof (ioc)); 775 776 /* 777 * If there is a '/devices', strip it, if not 778 * assume it is complete and correct 779 */ 780 if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) { 781 ioc_phci = phci + DEV_PREFIX_LEN - 1; 782 } else { 783 ioc_phci = phci; 784 } 785 786 ioc.client = "/scsi_vhci"; 787 ioc.phci = ioc_phci; 788 789 /* 790 * Issue requested operation 791 */ 792 if (ioctl(fd, request, &ioc) != 0) { 793 return (errno); 794 } 795 return (0); 796 } 797 798 int 799 g_stms_path_disable_all(char *phci) 800 { 801 /* 802 * issue disable on all clients for a phci 803 */ 804 return (stms_path_enable_disable_all(phci, SCSI_VHCI_PATH_DISABLE)); 805 } 806 807 int 808 g_stms_path_enable_all(char *phci) 809 { 810 /* 811 * issue enable on all clients for a phci 812 */ 813 return (stms_path_enable_disable_all(phci, SCSI_VHCI_PATH_ENABLE)); 814 } 815 816 /* 817 * Name: stms_get_path_state 818 * 819 * inputs: 820 * 821 * client_path client device path 822 * example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw 823 * 824 * phci Controller device path 825 * example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0 826 * 827 * outputs: 828 * state set to one of enum mdi_pathinfo_state_t in sunmdi.h 829 * MDI_PATHINFO_STATE_* 830 * 831 * ext_state set to one or more of the bits defined in mdi_impldefs.h 832 * MDI_PATHINFO_STATE_* 833 * 834 * 835 * returns: 836 * 0 for success 837 * non-zero otherwise 838 */ 839 int 840 g_stms_get_path_state(char *client_path, char *phci, int *state, int *ext_state) 841 { 842 sv_iocdata_t ioc; 843 int num_paths; 844 char *ioc_phci; 845 int i; 846 int found = 0; 847 int err; 848 char phci_path[MAXPATHLEN]; 849 char cpath[MAXPATHLEN]; 850 char paddr[MAXNAMELEN]; 851 852 853 if (!client_path || !phci) { 854 return (EINVAL); 855 } 856 857 ioc.client = cpath; 858 ioc.phci = phci_path; 859 ioc.addr = paddr; 860 861 /* 862 * Get all the paths for this client 863 */ 864 if ((err = get_pathlist(client_path, &ioc, &num_paths)) 865 != 0) { 866 return (err); 867 } 868 869 /* 870 * If there is a '/devices', strip it, if not 871 * assume it is complete and correct 872 */ 873 if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) { 874 ioc_phci = phci + DEV_PREFIX_LEN - 1; 875 } else { 876 ioc_phci = phci; 877 } 878 879 /* 880 * get ioctl response states 881 * for the requested client and phci 882 * and copy them to caller's buffers 883 */ 884 for (i = 0; i < num_paths; i++) { 885 if (strncmp(ioc_phci, ioc.ret_buf[i].device.ret_phci, 886 strlen(ioc_phci)) == 0) { 887 found++; 888 *state = ioc.ret_buf[i].ret_state; 889 *ext_state = ioc.ret_buf[i].ret_ext_state; 890 break; 891 } 892 } 893 894 /* free everything we alloced */ 895 for (i = 0; i < ioc.buf_elem; i++) { 896 free(ioc.ret_buf[i].ret_prop.buf); 897 free(ioc.ret_buf[i].ret_prop.ret_buf_size); 898 } 899 free(ioc.ret_buf); 900 901 if (found) { 902 return (0); 903 } else { 904 /* Requested path not found */ 905 return (ENXIO); 906 } 907 } 908