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,
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
g_get_pathlist(char * dev_path,struct mp_pathlist * pathlist_p)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
g_get_pathcount(char * dev_path)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
g_failover(char * dev_path,char * path_class)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
g_free_pi_list(sv_path_info_t * pi,uint_t num_paths)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
stms_path_enable_disable(char * client_path,char * phci,int request)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
g_stms_path_disable(char * client_path,char * phci)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
g_stms_path_enable(char * client_path,char * phci)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
stms_path_enable_disable_all(char * phci,int request)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
g_stms_path_disable_all(char * phci)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
g_stms_path_enable_all(char * phci)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
g_stms_get_path_state(char * client_path,char * phci,int * state,int * ext_state)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