xref: /illumos-gate/usr/src/lib/storage/libg_fc/common/mpath.c (revision c21bd51d7acbaf77116c4cc3a23dfc6d16c637c2)
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, strlen(SCSI_VHCI)) != 0) {
245 		if ((physical_path = g_get_physical_name(dev_path)) == NULL) {
246 			return (L_INVALID_PATH);
247 		}
248 		if (strncmp(physical_path, SCSI_VHCI, strlen(SCSI_VHCI)) != 0) {
249 			free(physical_path);
250 			return (L_INVALID_PATH);
251 		}
252 	} else {
253 		if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
254 			return (L_MALLOC_FAILED);
255 		}
256 		(void) strcpy(physical_path, dev_path);
257 	}
258 	physical_path_s = physical_path;
259 
260 	/* move beyond "/devices" prefix */
261 	physical_path += DEV_PREFIX_LEN-1;
262 	/* remove  :c,raw suffix */
263 	delimiter = strrchr(physical_path, ':');
264 	/* if we didn't find the ':' fine, else truncate */
265 	if (delimiter != NULL) {
266 		*delimiter = '\0';
267 	}
268 
269 	/*
270 	 * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
271 	 * at least twice.  The first time will get the path count
272 	 * and the size of the ioctl propoerty buffer.  The second
273 	 * time will get the path_info for each path.
274 	 *
275 	 * It's possible that additional paths are added while this
276 	 * code is running.  If the path count increases between the
277 	 * 2 ioctl's above, then we'll retry (and assume all is well).
278 	 */
279 	(void) strcpy(ioc->client, physical_path);
280 	ioc->buf_elem = 1;
281 	ioc->ret_elem = (uint_t *)&(initial_path_count);
282 	ioc->ret_buf = NULL;
283 
284 	/* free physical path */
285 	free(physical_path_s);
286 
287 	/* 0 buf_size asks driver to return actual size needed */
288 	/* open the ioctl file descriptor */
289 	if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) {
290 		return (L_OPEN_PATH_FAIL);
291 	}
292 
293 	retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
294 	if (retval != 0) {
295 		close(fd);
296 		return (L_SCSI_VHCI_ERROR);
297 	}
298 	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
299 
300 
301 	while (pathlist_retry_count <= RETRY_PATHLIST) {
302 		ioc->buf_elem = initial_path_count;
303 		/* Make driver put actual # paths in variable */
304 		ioc->ret_elem = (uint_t *)&(current_path_count);
305 
306 		/*
307 		 * Allocate space for array of path_info structures.
308 		 * Allocate enough space for # paths from get_pathcount
309 		 */
310 		ioc->ret_buf = (sv_path_info_t *)
311 				calloc(initial_path_count,
312 					sizeof (sv_path_info_t));
313 		if (ioc->ret_buf == NULL) {
314 			close(fd);
315 			return (L_MALLOC_FAILED);
316 		}
317 
318 		/*
319 		 * Allocate space for path properties returned by driver
320 		 */
321 		malloc_error = 0;
322 		for (i = 0; i < initial_path_count; i++) {
323 			ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
324 			if ((ioc->ret_buf[i].ret_prop.buf =
325 			    (caddr_t)malloc(prop_buf_size)) == NULL) {
326 				malloc_error = 1;
327 				break;
328 			}
329 			if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
330 				(uint_t *)malloc(sizeof (uint_t))) == NULL) {
331 				malloc_error = 1;
332 				break;
333 			}
334 		}
335 		if (malloc_error == 1) {
336 			for (i = 0; i < initial_path_count; i++) {
337 				free(ioc->ret_buf[i].ret_prop.buf);
338 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
339 			}
340 			free(ioc->ret_buf);
341 			close(fd);
342 			return (L_MALLOC_FAILED);
343 		}
344 
345 		retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
346 		if (retval != 0) {
347 			for (i = 0; i < initial_path_count; i++) {
348 				free(ioc->ret_buf[i].ret_prop.buf);
349 				free(ioc->ret_buf[i].ret_prop.ret_buf_size);
350 			}
351 			free(ioc->ret_buf);
352 			close(fd);
353 			return (L_SCSI_VHCI_ERROR);
354 		}
355 		if (initial_path_count < current_path_count) {
356 			/* then a new path was added */
357 			pathlist_retry_count++;
358 			initial_path_count = current_path_count;
359 		} else {
360 			break;
361 		}
362 	}
363 	/* we are done with ioctl's, lose the fd */
364 	close(fd);
365 
366 	/*
367 	 * Compare the length num elements from the ioctl response
368 	 *   and the caller's request - use smaller value.
369 	 *
370 	 * pathlist_p->path_count now has count returned from ioctl.
371 	 * ioc.buf_elem has the value the caller provided.
372 	 */
373 	if (initial_path_count < current_path_count) {
374 		/* More paths exist than we allocated space for */
375 		*num_paths_to_copy = initial_path_count;
376 	} else {
377 		*num_paths_to_copy = current_path_count;
378 	}
379 return (0);
380 }
381 
382 /*
383  * To obtain pathlist of a given target device
384  *
385  * inputs:
386  *	dev_path client device path
387  *	example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw
388  * outputs:
389  *	pathlist_p pathlist structure containing pathinfo node data
390  * returns:
391  *   0 - success
392  *  !0 - failure
393  */
394 int
395 g_get_pathlist(char *dev_path, struct mp_pathlist *pathlist_p)
396 {
397 
398 	sv_iocdata_t	ioc;
399 	int	retval, caller_ret = 0;
400 	int	num_paths_to_copy;
401 	int	i;
402 	int	prop_buf_size;
403 	char	*path_class_val = NULL;
404 	char	*temp_addr;
405 	char	phci_path[MAXPATHLEN];
406 	char	client_path[MAXPATHLEN];
407 	char	paddr[MAXNAMELEN];
408 
409 
410 	ioc.client = client_path;
411 	ioc.phci = phci_path;
412 	ioc.addr = paddr;
413 
414 	if ((caller_ret = get_pathlist(dev_path, &ioc, &num_paths_to_copy))
415 		!= 0) {
416 		return (caller_ret);
417 	}
418 
419 	pathlist_p->path_count = num_paths_to_copy;
420 	pathlist_p->path_info = calloc(num_paths_to_copy,
421 					sizeof (mp_pathinfo_t));
422 
423 	prop_buf_size = SV_PROP_MAX_BUF_SIZE;
424 
425 	if (pathlist_p->path_info == NULL) {
426 		caller_ret = L_MALLOC_FAILED;
427 		/* force the loop to not run so we free buffers and exit */
428 		num_paths_to_copy = 0;
429 	}
430 
431 	/* get ioctl reponse fields and copy them to caller's buffer */
432 	for (i = 0; i < num_paths_to_copy; i++) {
433 		nvlist_t *nvl;
434 
435 		pathlist_p->path_info[i].path_state =
436 			ioc.ret_buf[i].ret_state;
437 		(void) strncpy(pathlist_p->path_info[i].path_hba, DEV_PREFIX,
438 			DEV_PREFIX_LEN - 1);
439 		(void) strcat(pathlist_p->path_info[i].path_hba,
440 			ioc.ret_buf[i].device.ret_phci);
441 		(void) strcpy(pathlist_p->path_info[i].path_dev,
442 			ioc.client);
443 
444 		/*
445 		 * Check for leading 'w'. The mpxio framework was
446 		 * incorrectly implemented to skip 'w' in mdi_pi_get_addr().
447 		 * Since the leading 'w' is fibre-channel specific, we
448 		 * do it here to remove fibre-channel specific behavior
449 		 * from the mpxio framework.
450 		 */
451 		temp_addr = ioc.ret_buf[i].ret_addr;
452 		if (*temp_addr == 'w') {
453 			temp_addr++;
454 		}
455 		(void) strcpy(pathlist_p->path_info[i].path_addr, temp_addr);
456 
457 		/* use nvlist_ calls to extract properties from retbuf */
458 		retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf,
459 					prop_buf_size, &nvl, 0);
460 		if (retval != 0) { /* ??? same retcode */
461 			(void) strcpy(pathlist_p->path_info[i].path_class,
462 				"UNKNOWN PROB");
463 		} else {
464 			retval = nvlist_lookup_string(nvl, "path-class",
465 				&path_class_val);
466 			if (retval != 0) {
467 			(void) strcpy(pathlist_p->path_info[i].path_class,
468 				"UNKNOWN");
469 			} else {
470 				(void) strcpy(pathlist_p->path_info[i].
471 					path_class,
472 					path_class_val);
473 			}
474 			nvlist_free(nvl);
475 		}
476 	}
477 
478 	/* free everything we alloced */
479 	for (i = 0; i < ioc.buf_elem; i++) {
480 		free(ioc.ret_buf[i].ret_prop.buf);
481 		free(ioc.ret_buf[i].ret_prop.ret_buf_size);
482 	}
483 	free(ioc.ret_buf);
484 return (caller_ret);
485 }
486 
487 /*
488  * To get the number of paths to a given device pathname using
489  * driver ioctl.
490  *
491  * inputs:
492  *   dev path you would like to recieve mp count on
493  * outputs:
494  * returns:
495  *   0  - success
496  *   -1 - bad device path
497  *   -2 - open failure
498  *   -3 - ioctl failure
499  */
500 int
501 g_get_pathcount(char *dev_path)
502 {
503 	char		*char_ptr;
504 	int		fd = -1;
505 	sv_iocdata_t	ioc;
506 	char		phci_path[MAXPATHLEN];
507 	char		client_path[MAXPATHLEN];
508 	char		paddr[MAXNAMELEN];
509 	uint_t		num_elem = 0;
510 	int		retval = 0;
511 	char		*physical_path;
512 
513 	/* translate device path to physical path */
514 	physical_path = g_get_physical_name(dev_path);
515 	/* ensure physical path is not NULL, or strcpy will core */
516 	if (physical_path == NULL) {
517 		return (-1);
518 	}
519 	/* copy physical path without /devices/ prefix */
520 	(void) strcpy(client_path, physical_path + DEV_PREFIX_LEN-1);
521 	free(physical_path);
522 
523 	if ((char_ptr = strrchr(client_path, ':')) != NULL) {
524 		*char_ptr = '\0';
525 	}
526 
527 	/* prepare sv_iocdata_t structure */
528 	ioc.client	= client_path;
529 	ioc.phci	= phci_path;
530 	ioc.addr	= paddr;
531 	ioc.buf_elem	= 0;
532 	ioc.ret_buf	= NULL;
533 	ioc.ret_elem	= &num_elem;
534 
535 	strcpy(ioc.phci, client_path);
536 
537 	/* Get file descr. for "/devices/scsi_vhci:devctl" */
538 	if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) {
539 		return (-2);
540 	}
541 
542 	/* Issue open to device to get multipath_info (ie. count) */
543 	retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
544 	close(fd);
545 
546 	/* Check icotl status */
547 	if (retval == 0) {
548 		/* success */
549 		return (*ioc.ret_elem);
550 	} else {
551 		/* failure */
552 		return (-3);
553 	}
554 
555 }
556 
557 
558 /*
559  * Call driver to effect failover for a given pathclass
560  *
561  * inputs:
562  * outputs:
563  * returns:
564  *   0  - success
565  *   !0 - failure
566  */
567 int
568 g_failover(char *dev_path, char *path_class)
569 {
570 int		fd = 0, ret = 0;
571 char		client_path[MAXPATHLEN];
572 char		class[MAXNAMELEN];
573 sv_switch_to_cntlr_iocdata_t	iocsc;
574 
575 char		*char_ptr_start, *char_ptr_end;
576 
577 
578 	if (strstr(dev_path, SCSI_VHCI) == NULL) {
579 		return (L_INVALID_PATH);
580 	}
581 
582 	char_ptr_start = dev_path + strlen("/devices");
583 	if ((char_ptr_end = strrchr(char_ptr_start, ':')) != NULL) {
584 		*char_ptr_end = '\0';
585 	}
586 
587 	if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) {
588 		return (L_OPEN_PATH_FAIL);
589 	}
590 
591 	iocsc.client = client_path;
592 	iocsc.class = class;
593 
594 	strcpy(iocsc.client, char_ptr_start);
595 	strcpy(iocsc.class, path_class);
596 
597 	if (ioctl(fd, SCSI_VHCI_SWITCH_TO_CNTLR, &iocsc) != 0) {
598 		switch (errno) {
599 			case EALREADY:
600 				ret = L_SCSI_VHCI_ALREADY_ACTIVE;
601 				break;
602 			case ENXIO:
603 				ret = L_INVALID_PATH;
604 				break;
605 			case EIO:
606 				ret = L_SCSI_VHCI_NO_STANDBY;
607 				break;
608 			case ENOTSUP:
609 				ret = L_SCSI_VHCI_FAILOVER_NOTSUP;
610 				break;
611 			case EBUSY:
612 				ret = L_SCSI_VHCI_FAILOVER_BUSY;
613 				break;
614 			case EFAULT:
615 			default:
616 				ret = L_SCSI_VHCI_ERROR;
617 		}
618 	}
619 
620 	close(fd);
621 	return (ret);
622 }
623 
624 static void
625 g_free_pi_list(sv_path_info_t *pi, uint_t num_paths)
626 {
627 sv_path_info_t *pi_h = pi;
628 int i = 0;
629 
630 	while (i++ < num_paths && pi != NULL) {
631 		free(pi->ret_prop.buf);
632 		free(pi->ret_prop.ret_buf_size);
633 		pi++;
634 	}
635 	free(pi_h);
636 }
637 
638 
639 /*
640  * Name: stms_path_enable_disable
641  *
642  * inputs:
643  *
644  * client_path	client device path
645  *	example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw
646  *
647  * phci		Controller device path
648  *	example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0
649  *
650  * request should be set to one of the following:
651  *	SCSI_VHCI_PATH_DISABLE
652  *	SCSI_VHCI_PATH_ENABLE
653  *
654  * returns:
655  *	0 for success
656  *	non-zero otherwise
657  */
658 static int
659 stms_path_enable_disable(char *client_path, char *phci, int request)
660 {
661 	char *ioc_phci;
662 	char *char_ptr_end;
663 	char *client_physical_path, *client_path_ptr;
664 	int fd;
665 	sv_iocdata_t	ioc;
666 
667 	if (!client_path || !phci) {
668 		return (EINVAL);
669 	}
670 
671 	if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) {
672 		return (L_OPEN_PATH_FAIL);
673 	}
674 
675 	/*
676 	 * translate device path to physical path
677 	 * Save off the ptr for use by free
678 	 */
679 	client_path_ptr = client_physical_path =
680 		g_get_physical_name(client_path);
681 
682 	/* ensure physical path is not NULL, or strcpy will core */
683 	if (client_physical_path == NULL) {
684 		return (EINVAL);
685 	}
686 
687 	/*
688 	 * Must be a scsi_vhci path
689 	 */
690 	if (strstr(client_physical_path, SCSI_VHCI) == NULL) {
691 		free(client_path_ptr);
692 		return (L_INVALID_PATH);
693 	}
694 
695 	/* physical path without /devices/ prefix */
696 	client_physical_path += DEV_PREFIX_LEN - 1;
697 
698 	if ((char_ptr_end = strrchr(client_physical_path, ':')) != NULL) {
699 		*char_ptr_end = '\0';
700 	}
701 
702 	/*
703 	 * If there is a '/devices', strip it, if not
704 	 * assume it is complete and correct
705 	 */
706 	if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) {
707 		ioc_phci = phci + DEV_PREFIX_LEN - 1;
708 	} else {
709 		ioc_phci = phci;
710 	}
711 
712 	memset(&ioc, 0, sizeof (ioc));
713 
714 	ioc.client = client_physical_path;
715 	ioc.phci = ioc_phci;
716 
717 	/*
718 	 * Issue requested operation
719 	 */
720 	if (ioctl(fd, request, &ioc) != 0) {
721 		free(client_path_ptr);
722 		return (errno);
723 	}
724 	free(client_path_ptr);
725 	return (0);
726 }
727 
728 int
729 g_stms_path_disable(char *client_path, char *phci)
730 {
731 	return (stms_path_enable_disable(client_path, phci,
732 		SCSI_VHCI_PATH_DISABLE));
733 }
734 
735 int
736 g_stms_path_enable(char *client_path, char *phci)
737 {
738 	return (stms_path_enable_disable(client_path, phci,
739 		SCSI_VHCI_PATH_ENABLE));
740 }
741 
742 /*
743  * Name: stms_path_enable_disable_all
744  *
745  * inputs:
746  *
747  * phci		Controller device path
748  *	example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0
749  *
750  * request should be set to one of the following:
751  *	SCSI_VHCI_PATH_DISABLE
752  *	SCSI_VHCI_PATH_ENABLE
753  *
754  * returns:
755  *	0 for success
756  *	non-zero otherwise
757  */
758 
759 static int
760 stms_path_enable_disable_all(char *phci, int request)
761 {
762 	int fd;
763 	char *ioc_phci;
764 	sv_iocdata_t ioc;
765 
766 	if (!phci) {
767 		return (EINVAL);
768 	}
769 
770 	if ((fd = g_object_open(VHCI_NODE, O_RDWR)) < 0) {
771 		return (L_OPEN_PATH_FAIL);
772 	}
773 
774 	memset(&ioc, 0, sizeof (ioc));
775 
776 	/*
777 	 * If there is a '/devices', strip it, if not
778 	 * assume it is complete and correct
779 	 */
780 	if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) {
781 		ioc_phci = phci + DEV_PREFIX_LEN - 1;
782 	} else {
783 		ioc_phci = phci;
784 	}
785 
786 	ioc.client = "/scsi_vhci";
787 	ioc.phci = ioc_phci;
788 
789 	/*
790 	 * Issue requested operation
791 	 */
792 	if (ioctl(fd, request, &ioc) != 0) {
793 		return (errno);
794 	}
795 	return (0);
796 }
797 
798 int
799 g_stms_path_disable_all(char *phci)
800 {
801 	/*
802 	 * issue disable on all clients for a phci
803 	 */
804 	return (stms_path_enable_disable_all(phci, SCSI_VHCI_PATH_DISABLE));
805 }
806 
807 int
808 g_stms_path_enable_all(char *phci)
809 {
810 	/*
811 	 * issue enable on all clients for a phci
812 	 */
813 	return (stms_path_enable_disable_all(phci, SCSI_VHCI_PATH_ENABLE));
814 }
815 
816 /*
817  * Name: stms_get_path_state
818  *
819  * inputs:
820  *
821  * client_path	client device path
822  *	example: /devices/scsi_vhci/ssd@g280000602200416d6257333030303261:c,raw
823  *
824  * phci		Controller device path
825  *	example: /devices/pci@4,4000/SUNW,qlc@4/fp@0,0
826  *
827  * outputs:
828  * state set to one of enum mdi_pathinfo_state_t in sunmdi.h
829  *	MDI_PATHINFO_STATE_*
830  *
831  * ext_state set to one or more of the bits defined in mdi_impldefs.h
832  *	MDI_PATHINFO_STATE_*
833  *
834  *
835  * returns:
836  *	0 for success
837  *	non-zero otherwise
838  */
839 int
840 g_stms_get_path_state(char *client_path, char *phci, int *state, int *ext_state)
841 {
842 	sv_iocdata_t ioc;
843 	int num_paths;
844 	char *ioc_phci;
845 	int i;
846 	int found = 0;
847 	int err;
848 	char	phci_path[MAXPATHLEN];
849 	char	cpath[MAXPATHLEN];
850 	char	paddr[MAXNAMELEN];
851 
852 
853 	if (!client_path || !phci) {
854 		return (EINVAL);
855 	}
856 
857 	ioc.client = cpath;
858 	ioc.phci = phci_path;
859 	ioc.addr = paddr;
860 
861 	/*
862 	 * Get all the paths for this client
863 	 */
864 	if ((err = get_pathlist(client_path, &ioc, &num_paths))
865 		!= 0) {
866 		return (err);
867 	}
868 
869 	/*
870 	 * If there is a '/devices', strip it, if not
871 	 * assume it is complete and correct
872 	 */
873 	if (strncmp(phci, DEV_PREFIX, DEV_PREFIX_LEN) == 0) {
874 		ioc_phci = phci + DEV_PREFIX_LEN - 1;
875 	} else {
876 		ioc_phci = phci;
877 	}
878 
879 	/*
880 	 * get ioctl response states
881 	 * for the requested client and phci
882 	 * and copy them to caller's buffers
883 	 */
884 	for (i = 0; i < num_paths; i++) {
885 		if (strncmp(ioc_phci, ioc.ret_buf[i].device.ret_phci,
886 			strlen(ioc_phci)) == 0) {
887 			found++;
888 			*state = ioc.ret_buf[i].ret_state;
889 			*ext_state = ioc.ret_buf[i].ret_ext_state;
890 			break;
891 		}
892 	}
893 
894 	/* free everything we alloced */
895 	for (i = 0; i < ioc.buf_elem; i++) {
896 		free(ioc.ret_buf[i].ret_prop.buf);
897 		free(ioc.ret_buf[i].ret_prop.ret_buf_size);
898 	}
899 	free(ioc.ret_buf);
900 
901 	if (found) {
902 		return (0);
903 	} else {
904 		/* Requested path not found */
905 		return (ENXIO);
906 	}
907 }
908