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