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 */
74static void g_free_pi_list(sv_path_info_t *, uint_t num_paths);
75static int get_pathlist(char *, sv_iocdata_t *, int *);
76static int stms_path_enable_disable(char *, char *, int);
77static 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 */
89int
90g_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 */
183int
184g_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 */
230static int
231get_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	}
381return (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 */
396int
397g_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);
486return (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 */
502int
503g_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 */
569int
570g_failover(char *dev_path, char *path_class)
571{
572int		fd = 0, ret = 0;
573char		client_path[MAXPATHLEN];
574char		class[MAXNAMELEN];
575sv_switch_to_cntlr_iocdata_t	iocsc;
576
577char		*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
626static void
627g_free_pi_list(sv_path_info_t *pi, uint_t num_paths)
628{
629sv_path_info_t *pi_h = pi;
630int 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 */
660static int
661stms_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
730int
731g_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
737int
738g_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
761static int
762stms_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
800int
801g_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
809int
810g_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 */
841int
842g_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