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