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, 245 strlen(SCSI_VHCI)) != NULL) { 246 if ((physical_path = g_get_physical_name(dev_path)) == NULL) { 247 return (L_INVALID_PATH); 248 } 249 if (strncmp(physical_path, SCSI_VHCI, 250 strlen(SCSI_VHCI)) != NULL) { 251 free(physical_path); 252 return (L_INVALID_PATH); 253 } 254 } else { 255 if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) { 256 return (L_MALLOC_FAILED); 257 } 258 (void) strcpy(physical_path, dev_path); 259 } 260 physical_path_s = physical_path; 261 262 /* move beyond "/devices" prefix */ 263 physical_path += DEV_PREFIX_LEN-1; 264 /* remove :c,raw suffix */ 265 delimiter = strrchr(physical_path, ':'); 266 /* if we didn't find the ':' fine, else truncate */ 267 if (delimiter != NULL) { 268 *delimiter = NULL; 269 } 270 271 /* 272 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO 273 * at least twice. The first time will get the path count 274 * and the size of the ioctl propoerty buffer. The second 275 * time will get the path_info for each path. 276 * 277 * It's possible that additional paths are added while this 278 * code is running. If the path count increases between the 279 * 2 ioctl's above, then we'll retry (and assume all is well). 280 */ 281 (void) strcpy(ioc->client, physical_path); 282 ioc->buf_elem = 1; 283 ioc->ret_elem = (uint_t *)&(initial_path_count); 284 ioc->ret_buf = NULL; 285 286 /* free physical path */ 287 free(physical_path_s); 288 289 /* 0 buf_size asks driver to return actual size needed */ 290 /* open the ioctl file descriptor */ 291 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 292 return (L_OPEN_PATH_FAIL); 293 } 294 295 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc); 296 if (retval != 0) { 297 close(fd); 298 return (L_SCSI_VHCI_ERROR); 299 } 300 prop_buf_size = SV_PROP_MAX_BUF_SIZE; 301 302 303 while (pathlist_retry_count <= RETRY_PATHLIST) { 304 ioc->buf_elem = initial_path_count; 305 /* Make driver put actual # paths in variable */ 306 ioc->ret_elem = (uint_t *)&(current_path_count); 307 308 /* 309 * Allocate space for array of path_info structures. 310 * Allocate enough space for # paths from get_pathcount 311 */ 312 ioc->ret_buf = (sv_path_info_t *) 313 calloc(initial_path_count, 314 sizeof (sv_path_info_t)); 315 if (ioc->ret_buf == NULL) { 316 close(fd); 317 return (L_MALLOC_FAILED); 318 } 319 320 /* 321 * Allocate space for path properties returned by driver 322 */ 323 malloc_error = 0; 324 for (i = 0; i < initial_path_count; i++) { 325 ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size; 326 if ((ioc->ret_buf[i].ret_prop.buf = 327 (caddr_t)malloc(prop_buf_size)) == NULL) { 328 malloc_error = 1; 329 break; 330 } 331 if ((ioc->ret_buf[i].ret_prop.ret_buf_size = 332 (uint_t *)malloc(sizeof (uint_t))) == NULL) { 333 malloc_error = 1; 334 break; 335 } 336 } 337 if (malloc_error == 1) { 338 for (i = 0; i < initial_path_count; i++) { 339 free(ioc->ret_buf[i].ret_prop.buf); 340 free(ioc->ret_buf[i].ret_prop.ret_buf_size); 341 } 342 free(ioc->ret_buf); 343 close(fd); 344 return (L_MALLOC_FAILED); 345 } 346 347 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc); 348 if (retval != 0) { 349 for (i = 0; i < initial_path_count; i++) { 350 free(ioc->ret_buf[i].ret_prop.buf); 351 free(ioc->ret_buf[i].ret_prop.ret_buf_size); 352 } 353 free(ioc->ret_buf); 354 close(fd); 355 return (L_SCSI_VHCI_ERROR); 356 } 357 if (initial_path_count < current_path_count) { 358 /* then a new path was added */ 359 pathlist_retry_count++; 360 initial_path_count = current_path_count; 361 } else { 362 break; 363 } 364 } 365 /* we are done with ioctl's, lose the fd */ 366 close(fd); 367 368 /* 369 * Compare the length num elements from the ioctl response 370 * and the caller's request - use smaller value. 371 * 372 * pathlist_p->path_count now has count returned from ioctl. 373 * ioc.buf_elem has the value the caller provided. 374 */ 375 if (initial_path_count < current_path_count) { 376 /* More paths exist than we allocated space for */ 377 *num_paths_to_copy = initial_path_count; 378 } else { 379 *num_paths_to_copy = current_path_count; 380 } 381 return (0); 382 } 383 384 /* 385 * To obtain pathlist of a given target device 386 * 387 * inputs: 388 * dev_path client device path 389 * example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw 390 * outputs: 391 * pathlist_p pathlist structure containing pathinfo node data 392 * returns: 393 * 0 - success 394 * !0 - failure 395 */ 396 int 397 g_get_pathlist(char *dev_path, struct mp_pathlist *pathlist_p) 398 { 399 400 sv_iocdata_t ioc; 401 int retval, caller_ret = 0; 402 int num_paths_to_copy; 403 int i; 404 int prop_buf_size; 405 char *path_class_val = NULL; 406 char *temp_addr; 407 char phci_path[MAXPATHLEN]; 408 char client_path[MAXPATHLEN]; 409 char paddr[MAXNAMELEN]; 410 411 412 ioc.client = client_path; 413 ioc.phci = phci_path; 414 ioc.addr = paddr; 415 416 if ((caller_ret = get_pathlist(dev_path, &ioc, &num_paths_to_copy)) 417 != 0) { 418 return (caller_ret); 419 } 420 421 pathlist_p->path_count = num_paths_to_copy; 422 pathlist_p->path_info = calloc(num_paths_to_copy, 423 sizeof (mp_pathinfo_t)); 424 425 prop_buf_size = SV_PROP_MAX_BUF_SIZE; 426 427 if (pathlist_p->path_info == NULL) { 428 caller_ret = L_MALLOC_FAILED; 429 /* force the loop to not run so we free buffers and exit */ 430 num_paths_to_copy = 0; 431 } 432 433 /* get ioctl reponse fields and copy them to caller's buffer */ 434 for (i = 0; i < num_paths_to_copy; i++) { 435 nvlist_t *nvl; 436 437 pathlist_p->path_info[i].path_state = 438 ioc.ret_buf[i].ret_state; 439 (void) strncpy(pathlist_p->path_info[i].path_hba, DEV_PREFIX, 440 DEV_PREFIX_LEN - 1); 441 (void) strcat(pathlist_p->path_info[i].path_hba, 442 ioc.ret_buf[i].device.ret_phci); 443 (void) strcpy(pathlist_p->path_info[i].path_dev, 444 ioc.client); 445 446 /* 447 * Check for leading 'w'. The mpxio framework was 448 * incorrectly implemented to skip 'w' in mdi_pi_get_addr(). 449 * Since the leading 'w' is fibre-channel specific, we 450 * do it here to remove fibre-channel specific behavior 451 * from the mpxio framework. 452 */ 453 temp_addr = ioc.ret_buf[i].ret_addr; 454 if (*temp_addr == 'w') { 455 temp_addr++; 456 } 457 (void) strcpy(pathlist_p->path_info[i].path_addr, temp_addr); 458 459 /* use nvlist_ calls to extract properties from retbuf */ 460 retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf, 461 prop_buf_size, &nvl, 0); 462 if (retval != 0) { /* ??? same retcode */ 463 (void) strcpy(pathlist_p->path_info[i].path_class, 464 "UNKNOWN PROB"); 465 } else { 466 retval = nvlist_lookup_string(nvl, "path-class", 467 &path_class_val); 468 if (retval != 0) { 469 (void) strcpy(pathlist_p->path_info[i].path_class, 470 "UNKNOWN"); 471 } else { 472 (void) strcpy(pathlist_p->path_info[i]. 473 path_class, 474 path_class_val); 475 } 476 nvlist_free(nvl); 477 } 478 } 479 480 /* free everything we alloced */ 481 for (i = 0; i < ioc.buf_elem; i++) { 482 free(ioc.ret_buf[i].ret_prop.buf); 483 free(ioc.ret_buf[i].ret_prop.ret_buf_size); 484 } 485 free(ioc.ret_buf); 486 return (caller_ret); 487 } 488 489 /* 490 * To get the number of paths to a given device pathname using 491 * driver ioctl. 492 * 493 * inputs: 494 * dev path you would like to recieve mp count on 495 * outputs: 496 * returns: 497 * 0 - success 498 * -1 - bad device path 499 * -2 - open failure 500 * -3 - ioctl failure 501 */ 502 int 503 g_get_pathcount(char *dev_path) 504 { 505 char *char_ptr; 506 int fd = -1; 507 sv_iocdata_t ioc; 508 char phci_path[MAXPATHLEN]; 509 char client_path[MAXPATHLEN]; 510 char paddr[MAXNAMELEN]; 511 uint_t num_elem = 0; 512 int retval = 0; 513 char *physical_path; 514 515 /* translate device path to physical path */ 516 physical_path = g_get_physical_name(dev_path); 517 /* ensure physical path is not NULL, or strcpy will core */ 518 if (physical_path == NULL) { 519 return (-1); 520 } 521 /* copy physical path without /devices/ prefix */ 522 (void) strcpy(client_path, physical_path + DEV_PREFIX_LEN-1); 523 free(physical_path); 524 525 if ((char_ptr = strrchr(client_path, ':')) != NULL) { 526 *char_ptr = '\0'; 527 } 528 529 /* prepare sv_iocdata_t structure */ 530 ioc.client = client_path; 531 ioc.phci = phci_path; 532 ioc.addr = paddr; 533 ioc.buf_elem = 0; 534 ioc.ret_buf = NULL; 535 ioc.ret_elem = &num_elem; 536 537 strcpy(ioc.phci, client_path); 538 539 /* Get file descr. for "/devices/scsi_vhci:devctl" */ 540 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 541 return (-2); 542 } 543 544 /* Issue open to device to get multipath_info (ie. count) */ 545 retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 546 close(fd); 547 548 /* Check icotl status */ 549 if (retval == 0) { 550 /* success */ 551 return (*ioc.ret_elem); 552 } else { 553 /* failure */ 554 return (-3); 555 } 556 557 } 558 559 560 /* 561 * Call driver to effect failover for a given pathclass 562 * 563 * inputs: 564 * outputs: 565 * returns: 566 * 0 - success 567 * !0 - failure 568 */ 569 int 570 g_failover(char *dev_path, char *path_class) 571 { 572 int fd = 0, ret = 0; 573 char client_path[MAXPATHLEN]; 574 char class[MAXNAMELEN]; 575 sv_switch_to_cntlr_iocdata_t iocsc; 576 577 char *char_ptr_start, *char_ptr_end; 578 579 580 if (strstr(dev_path, SCSI_VHCI) == NULL) { 581 return (L_INVALID_PATH); 582 } 583 584 char_ptr_start = dev_path + strlen("/devices"); 585 if ((char_ptr_end = strrchr(char_ptr_start, ':')) != NULL) { 586 *char_ptr_end = '\0'; 587 } 588 589 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 590 return (L_OPEN_PATH_FAIL); 591 } 592 593 iocsc.client = client_path; 594 iocsc.class = class; 595 596 strcpy(iocsc.client, char_ptr_start); 597 strcpy(iocsc.class, path_class); 598 599 if (ioctl(fd, SCSI_VHCI_SWITCH_TO_CNTLR, &iocsc) != 0) { 600 switch (errno) { 601 case EALREADY: 602 ret = L_SCSI_VHCI_ALREADY_ACTIVE; 603 break; 604 case ENXIO: 605 ret = L_INVALID_PATH; 606 break; 607 case EIO: 608 ret = L_SCSI_VHCI_NO_STANDBY; 609 break; 610 case ENOTSUP: 611 ret = L_SCSI_VHCI_FAILOVER_NOTSUP; 612 break; 613 case EBUSY: 614 ret = L_SCSI_VHCI_FAILOVER_BUSY; 615 break; 616 case EFAULT: 617 default: 618 ret = L_SCSI_VHCI_ERROR; 619 } 620 } 621 622 close(fd); 623 return (ret); 624 } 625 626 static void 627 g_free_pi_list(sv_path_info_t *pi, uint_t num_paths) 628 { 629 sv_path_info_t *pi_h = pi; 630 int i = 0; 631 632 while (i++ < num_paths && pi != NULL) { 633 free(pi->ret_prop.buf); 634 free(pi->ret_prop.ret_buf_size); 635 pi++; 636 } 637 free(pi_h); 638 } 639 640 641 /* 642 * Name: stms_path_enable_disable 643 * 644 * inputs: 645 * 646 * client_path client device path 647 * example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw 648 * 649 * phci Controller device path 650 * example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0 651 * 652 * request should be set to one of the following: 653 * SCSI_VHCI_PATH_DISABLE 654 * SCSI_VHCI_PATH_ENABLE 655 * 656 * returns: 657 * 0 for success 658 * non-zero otherwise 659 */ 660 static int 661 stms_path_enable_disable(char *client_path, char *phci, int request) 662 { 663 char *ioc_phci; 664 char *char_ptr_end; 665 char *client_physical_path, *client_path_ptr; 666 int fd; 667 sv_iocdata_t ioc; 668 669 if (!client_path || !phci) { 670 return (EINVAL); 671 } 672 673 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 674 return (L_OPEN_PATH_FAIL); 675 } 676 677 /* 678 * translate device path to physical path 679 * Save off the ptr for use by free 680 */ 681 client_path_ptr = client_physical_path = 682 g_get_physical_name(client_path); 683 684 /* ensure physical path is not NULL, or strcpy will core */ 685 if (client_physical_path == NULL) { 686 return (EINVAL); 687 } 688 689 /* 690 * Must be a scsi_vhci path 691 */ 692 if (strstr(client_physical_path, SCSI_VHCI) == NULL) { 693 free(client_path_ptr); 694 return (L_INVALID_PATH); 695 } 696 697 /* physical path without /devices/ prefix */ 698 client_physical_path += DEV_PREFIX_LEN - 1; 699 700 if ((char_ptr_end = strrchr(client_physical_path, ':')) != NULL) { 701 *char_ptr_end = '\0'; 702 } 703 704 /* 705 * If there is a '/devices', strip it, if not 706 * assume it is complete and correct 707 */ 708 if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) { 709 ioc_phci = phci + DEV_PREFIX_LEN - 1; 710 } else { 711 ioc_phci = phci; 712 } 713 714 memset(&ioc, 0, sizeof (ioc)); 715 716 ioc.client = client_physical_path; 717 ioc.phci = ioc_phci; 718 719 /* 720 * Issue requested operation 721 */ 722 if (ioctl(fd, request, &ioc) != 0) { 723 free(client_path_ptr); 724 return (errno); 725 } 726 free(client_path_ptr); 727 return (0); 728 } 729 730 int 731 g_stms_path_disable(char *client_path, char *phci) 732 { 733 return (stms_path_enable_disable(client_path, phci, 734 SCSI_VHCI_PATH_DISABLE)); 735 } 736 737 int 738 g_stms_path_enable(char *client_path, char *phci) 739 { 740 return (stms_path_enable_disable(client_path, phci, 741 SCSI_VHCI_PATH_ENABLE)); 742 } 743 744 /* 745 * Name: stms_path_enable_disable_all 746 * 747 * inputs: 748 * 749 * phci Controller device path 750 * example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0 751 * 752 * request should be set to one of the following: 753 * SCSI_VHCI_PATH_DISABLE 754 * SCSI_VHCI_PATH_ENABLE 755 * 756 * returns: 757 * 0 for success 758 * non-zero otherwise 759 */ 760 761 static int 762 stms_path_enable_disable_all(char *phci, int request) 763 { 764 int fd; 765 char *ioc_phci; 766 sv_iocdata_t ioc; 767 768 if (!phci) { 769 return (EINVAL); 770 } 771 772 if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) { 773 return (L_OPEN_PATH_FAIL); 774 } 775 776 memset(&ioc, 0, sizeof (ioc)); 777 778 /* 779 * If there is a '/devices', strip it, if not 780 * assume it is complete and correct 781 */ 782 if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) { 783 ioc_phci = phci + DEV_PREFIX_LEN - 1; 784 } else { 785 ioc_phci = phci; 786 } 787 788 ioc.client = "/scsi_vhci"; 789 ioc.phci = ioc_phci; 790 791 /* 792 * Issue requested operation 793 */ 794 if (ioctl(fd, request, &ioc) != 0) { 795 return (errno); 796 } 797 return (0); 798 } 799 800 int 801 g_stms_path_disable_all(char *phci) 802 { 803 /* 804 * issue disable on all clients for a phci 805 */ 806 return (stms_path_enable_disable_all(phci, SCSI_VHCI_PATH_DISABLE)); 807 } 808 809 int 810 g_stms_path_enable_all(char *phci) 811 { 812 /* 813 * issue enable on all clients for a phci 814 */ 815 return (stms_path_enable_disable_all(phci, SCSI_VHCI_PATH_ENABLE)); 816 } 817 818 /* 819 * Name: stms_get_path_state 820 * 821 * inputs: 822 * 823 * client_path client device path 824 * example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw 825 * 826 * phci Controller device path 827 * example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0 828 * 829 * outputs: 830 * state set to one of enum mdi_pathinfo_state_t in sunmdi.h 831 * MDI_PATHINFO_STATE_* 832 * 833 * ext_state set to one or more of the bits defined in mdi_impldefs.h 834 * MDI_PATHINFO_STATE_* 835 * 836 * 837 * returns: 838 * 0 for success 839 * non-zero otherwise 840 */ 841 int 842 g_stms_get_path_state(char *client_path, char *phci, int *state, int *ext_state) 843 { 844 sv_iocdata_t ioc; 845 int num_paths; 846 char *ioc_phci; 847 int i; 848 int found = 0; 849 int err; 850 char phci_path[MAXPATHLEN]; 851 char cpath[MAXPATHLEN]; 852 char paddr[MAXNAMELEN]; 853 854 855 if (!client_path || !phci) { 856 return (EINVAL); 857 } 858 859 ioc.client = cpath; 860 ioc.phci = phci_path; 861 ioc.addr = paddr; 862 863 /* 864 * Get all the paths for this client 865 */ 866 if ((err = get_pathlist(client_path, &ioc, &num_paths)) 867 != 0) { 868 return (err); 869 } 870 871 /* 872 * If there is a '/devices', strip it, if not 873 * assume it is complete and correct 874 */ 875 if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) { 876 ioc_phci = phci + DEV_PREFIX_LEN - 1; 877 } else { 878 ioc_phci = phci; 879 } 880 881 /* 882 * get ioctl response states 883 * for the requested client and phci 884 * and copy them to caller's buffers 885 */ 886 for (i = 0; i < num_paths; i++) { 887 if (strncmp(ioc_phci, ioc.ret_buf[i].device.ret_phci, 888 strlen(ioc_phci)) == 0) { 889 found++; 890 *state = ioc.ret_buf[i].ret_state; 891 *ext_state = ioc.ret_buf[i].ret_ext_state; 892 break; 893 } 894 } 895 896 /* free everything we alloced */ 897 for (i = 0; i < ioc.buf_elem; i++) { 898 free(ioc.ret_buf[i].ret_prop.buf); 899 free(ioc.ret_buf[i].ret_prop.ret_buf_size); 900 } 901 free(ioc.ret_buf); 902 903 if (found) { 904 return (0); 905 } else { 906 /* Requested path not found */ 907 return (ENXIO); 908 } 909 } 910