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
g_get_lun_str(char * dev_path,char lunstr[],int path_num)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
g_get_lun_number(char * path_phys)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
get_pathlist(char * dev_path,sv_iocdata_t * ioc,int * num_paths_to_copy)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
g_get_pathlist(char * dev_path,struct mp_pathlist * pathlist_p)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
g_get_pathcount(char * dev_path)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
g_failover(char * dev_path,char * path_class)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
g_free_pi_list(sv_path_info_t * pi,uint_t num_paths)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
stms_path_enable_disable(char * client_path,char * phci,int request)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
g_stms_path_disable(char * client_path,char * phci)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
g_stms_path_enable(char * client_path,char * phci)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
stms_path_enable_disable_all(char * phci,int request)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
g_stms_path_disable_all(char * phci)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
g_stms_path_enable_all(char * phci)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
g_stms_get_path_state(char * client_path,char * phci,int * state,int * ext_state)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