xref: /illumos-gate/usr/src/lib/storage/libg_fc/common/hot.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 /*
31  *	This module is part of fibre channel interface library.
32  */
33 
34 /*
35  * I18N message number ranges
36  *  This file: 11000 - 11499
37  *  Shared common messages: 1 - 1999
38  */
39 
40 /* #define		_POSIX_SOURCE 1 */
41 
42 /*	Includes	*/
43 #include	<stdlib.h>
44 #include	<stdio.h>
45 #include	<sys/file.h>
46 #include	<sys/errno.h>
47 #include	<sys/types.h>
48 #include	<sys/param.h>
49 #include	<sys/stat.h>
50 #include	<fcntl.h>
51 #include	<unistd.h>
52 #include	<errno.h>
53 #include	<string.h>
54 #include	<strings.h>
55 #include	<sys/sunddi.h>
56 #include	<sys/scsi/scsi.h>
57 #include	<nl_types.h>
58 #include	<l_common.h>
59 #include	<stgcom.h>
60 #include	<l_error.h>
61 #include	<g_state.h>
62 
63 /* Forward declarations */
64 static int issue_lip(char *, int);
65 
66 
67 /*	Global variables	*/
68 extern	uchar_t		g_switch_to_alpa[];
69 extern	uchar_t		g_sf_alpa_to_switch[];
70 
71 /*
72  * starts a device.
73  *
74  * RETURNS:
75  *	0	 if O.K.
76  *	non-zero otherwise
77  */
78 int
79 g_dev_start(char *drv_path, int verbose)
80 {
81 int status;
82 
83 	if ((drv_path != NULL) && (*drv_path != '\0')) {
84 		if (status = g_start(drv_path)) {
85 			return (status);
86 		}
87 	}
88 	return (L_INVALID_PATH);
89 }
90 
91 
92 
93 /*
94  * stops a device. If the device was
95  * reserved by a host, it gets multiple
96  * paths to the device and try to stop the
97  * device using a different path.
98  *
99  * Returns:
100  *	0 if OK
101  *	-1 otherwise
102  */
103 
104 int
105 g_dev_stop(char *drv_path, struct wwn_list_struct *wwn_list,
106 						int verbose)
107 {
108 int		status, err;
109 char		*phys_path;
110 struct dlist	*ml = NULL;
111 
112 
113 	/* stop the device */
114 	/* Make the stop NOT immediate, so we wait. */
115 	if ((drv_path == NULL) || (*drv_path == '\0')) {
116 		return (L_INVALID_PATH);
117 	}
118 	if ((status = g_stop(drv_path, 0)) != 0) {
119 		/*
120 		 * In case of reservation conflict,
121 		 * get the multiple paths and try to
122 		 * stop the device through the path
123 		 * which held the reservations.
124 		 */
125 		if ((status & ~L_SCSI_ERROR) == STATUS_RESERVATION_CONFLICT) {
126 			if ((phys_path = g_get_physical_name(drv_path))
127 								== NULL) {
128 				return (L_INVALID_PATH);
129 			}
130 			if ((err = g_get_multipath(phys_path, &ml,
131 						wwn_list, verbose)) != 0) {
132 				return (err);
133 			}
134 			while (ml != NULL) {
135 				if (g_stop(ml->logical_path, 0) == 0) {
136 					(void) g_free_multipath(ml);
137 					goto done;
138 				}
139 				ml = ml->next;
140 			}
141 			(void) g_free_multipath(ml);
142 		}
143 		return (status);
144 	}
145 done:
146 	return (0);
147 }
148 
149 /*
150  * This function is for Leadville devices only
151  * It takes as input the actual path on which to issue the LIP and issues it
152  *
153  * INPUT :
154  * Path to the FCA devctl node.
155  *
156  * For example,
157  * /devices/pci@6,2000/pci@2/SUNW,qlc@4/fp@0,0:devctl
158  *
159  * No SCSI_VHCI paths will work. No checks are done and we'll let the ioctl
160  * handle any failures if it is passed in.
161  *
162  * RETURNS:
163  * 0 on Success
164  * non-zero otherwise
165  */
166 static int
167 issue_lip(char *fp_path, int verbose)
168 {
169 	int		fp_fd;
170 	la_wwn_t	wwn;
171 	fcio_t		fcio;
172 
173 	/*
174 	 * open fp path with exclusive path, otherwise,
175 	 * FCIO_RESET_LINK ioctl will fail with permission
176 	 * denied error.
177 	 */
178 	if ((fp_fd = g_object_open(fp_path, O_RDONLY | O_EXCL)) < 0) {
179 		return (L_OPEN_PATH_FAIL);
180 	}
181 
182 	if (verbose) {
183 		(void) fprintf(stdout, MSGSTR(11001,
184 			" Reinitializing the loop at:  %s\n"), fp_path);
185 	}
186 
187 	fcio.fcio_cmd = FCIO_RESET_LINK;
188 	fcio.fcio_xfer = FCIO_XFER_WRITE;
189 	/*
190 	 * Reset the local loop here (fcio_ibuf = 0).
191 	 * Reset a remote loop on the Fabric by
192 	 * passing its node wwn (fcio_len = sizeof(nwwn)
193 	 * and fcio_ibuf = (caddr_t)&nwwn) to the port driver.
194 	 */
195 	(void) bzero((caddr_t)&wwn, sizeof (wwn));
196 	fcio.fcio_ilen = sizeof (wwn);
197 	fcio.fcio_ibuf = (caddr_t)&wwn;
198 	if (g_issue_fcio_ioctl(fp_fd, &fcio, verbose) != 0) {
199 		I_DPRINTF(" issue_lip: FCIO_RESET_LINK"
200 			" ioctl failed: %s\n", fp_path);
201 		(void) close(fp_fd);
202 		return (L_FCIO_RESET_LINK_FAIL);
203 	}
204 	(void) close(fp_fd);
205 	return (0);
206 }
207 
208 /*
209  * Issues the LIP (Loop Intialization Protocol)
210  * on a nexus path (in case of socal) or on an
211  * fp path (in case of fabric).
212  *
213  * RETURNS:
214  *	0	 O.K.
215  *	non-zero otherwise
216  */
217 int
218 g_force_lip(char *path_phys, int verbose)
219 {
220 int		fd, err = 0, i = 0, pathcnt = 0;
221 char		nexus_path[MAXPATHLEN], *nexus_path_ptr;
222 char		*charPtr, fp_path[MAXPATHLEN];
223 struct stat	stbuf;
224 uint_t		dev_type;
225 mp_pathlist_t	pathlist;
226 mp_pathinfo_t	*pinfop;
227 
228 	/* return invalid path if path_phys NULL */
229 	if (path_phys == NULL) {
230 		return (L_INVALID_PATH);
231 	}
232 
233 	/* Make a copy of the arg passed in ... we'll need it down */
234 	(void) strcpy(fp_path, path_phys);
235 
236 	if (strstr(path_phys, SCSI_VHCI) != NULL) {
237 
238 		/*
239 		 * Its an MPXIO device path
240 		 *
241 		 * First, Get a list of all the pHCI for the given vHCI
242 		 * Then issue a LIP on all the pHCI FCAs that are in the
243 		 * MDI_PATHINFO_STATE_ONLINE or MDI_PATHINFO_STATE_STANDBY
244 		 * states.
245 		 */
246 		if (g_get_pathlist(fp_path, &pathlist)) {
247 			return (L_INVALID_PATH);
248 		}
249 		for (i = 0; i < pathlist.path_count; i++) {
250 			pinfop = &pathlist.path_info[i];
251 			if ((pinfop->path_state ==
252 				MDI_PATHINFO_STATE_ONLINE) ||
253 				    (pinfop->path_state ==
254 					MDI_PATHINFO_STATE_STANDBY)) {
255 				pathcnt++;
256 				sprintf(fp_path, "%s%s",
257 						pinfop->path_hba, FC_CTLR);
258 				if (issue_lip(fp_path, verbose) != 0) {
259 					err++;
260 				}
261 			}
262 		}
263 		free(pathlist.path_info);
264 		if (err == 0)
265 			return (0);
266 		if (err == pathcnt)
267 			return (L_FCIO_FORCE_LIP_FAIL);
268 		return (L_FCIO_FORCE_LIP_PARTIAL_FAIL);
269 	}
270 
271 	/* Non-MPXIO case */
272 
273 	if ((dev_type = g_get_path_type(fp_path)) == 0) {
274 		return (L_INVALID_PATH);
275 	}
276 
277 	if (dev_type & FC_FCA_MASK) {
278 		if (strstr(fp_path, DRV_NAME_SSD) ||
279 			strstr(fp_path, SES_NAME) ||
280 			strstr(fp_path, DRV_NAME_ST)) {
281 			if ((charPtr = strrchr(fp_path, '/')) == NULL) {
282 				return (L_INVALID_PATH);
283 			}
284 			*charPtr = '\0';
285 			/* append devctl to the path */
286 			(void) strcat(fp_path, FC_CTLR);
287 		} else {
288 			/* should have fp transport node to continue. */
289 			if (!(dev_type & FC_XPORT_MASK)) {
290 				return (L_INVALID_PATH_TYPE);
291 			}
292 			if (stat(fp_path, &stbuf) < 0) {
293 				return (L_LSTAT_ERROR);
294 			}
295 			if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
296 				/* append devctl to the path */
297 				(void) strcat(fp_path, FC_CTLR);
298 			}
299 		}
300 		return (issue_lip(fp_path, verbose));
301 
302 	} else {	/* for fc4 devices */
303 		if ((err = g_get_nexus_path(path_phys,
304 					&nexus_path_ptr)) != 0)
305 			return (err);
306 
307 		(void) strcpy(nexus_path, nexus_path_ptr);
308 		(void) g_destroy_data(nexus_path_ptr);
309 		P_DPRINTF("  g_force_lip: Force lip on:"
310 			" Path %s\n", nexus_path);
311 
312 		/* open driver */
313 		if ((fd = g_object_open(nexus_path,
314 				O_NDELAY | O_RDONLY)) == -1)
315 			return (L_OPEN_PATH_FAIL);
316 
317 		if (verbose) {
318 			(void) fprintf(stdout,
319 					MSGSTR(11000,
320 					"  Forcing lip (Loop Initialization "
321 					"Protocol)"
322 					"\n  on loop at: %s\n"), nexus_path);
323 		}
324 		if (ioctl(fd, FCIO_FORCE_LIP) != 0) {
325 			I_DPRINTF("  FCIO_FORCE_LIP ioctl failed.\n");
326 			(void) close(fd);
327 			return (L_FCIO_FORCE_LIP_FAIL);
328 		}
329 		(void) close(fd);
330 	}
331 	return (0);
332 }
333 
334 
335 
336 /*
337  * Takes one or more drives offline.
338  * If the force flag is supplied then: (1) don't pass the exclusive flag
339  * to the acquire routine and (2) allow the offline to fail
340  * If any acquire fails, print an error message and continue.
341  *
342  * RETURNS:
343  *	0		iff each offline succeeds
344  *	non-zero	otherwise
345  */
346 int
347 g_offline_drive(struct dlist *dl, int force_flag)
348 {
349 devctl_hdl_t		devhdl;
350 
351 
352 	/* for each drive attempt to take it offline */
353 	for (; dl != NULL; dl = dl->next) {
354 
355 		/* attempt to acquire the device */
356 		if ((devhdl = devctl_device_acquire(dl->dev_path,
357 				force_flag ? 0 : DC_EXCL)) == NULL) {
358 			if (errno != EBUSY) {
359 				P_DPRINTF("%s: Could not acquire"
360 					" the device: %s\n\n",
361 					strerror(errno), dl->dev_path);
362 				continue;
363 			}
364 		}
365 		/* attempt to offline the drive */
366 		if ((devctl_device_offline(devhdl) != 0) && !force_flag) {
367 			(void) devctl_release(devhdl);
368 			return (L_DEV_BUSY);
369 		}
370 
371 		/* offline succeeded -- release handle acquired above */
372 		(void) devctl_release(devhdl);
373 	}
374 
375 	return (0);
376 }
377 
378 
379 
380 /*
381  * Brings one or more drives online.
382  * If the force flag is supplied then: (1) don't pass the exclusive
383  * flag to the acquire routine and (2) allow the offline to fail
384  * If any acquire fails, continue with the next device.
385  *
386  * RETURNS:
387  *	None.
388  */
389 void
390 g_online_drive(struct dlist *dl, int force_flag)
391 {
392 devctl_hdl_t		devhdl;
393 
394 
395 	while (dl != NULL) {
396 		if ((devhdl = devctl_device_acquire(dl->dev_path,
397 					force_flag ? 0 : DC_EXCL)) != NULL) {
398 			(void) devctl_device_online(devhdl);
399 			(void) devctl_release(devhdl);
400 		}
401 		dl = dl->next;
402 	}
403 }
404 
405 
406 
407 void
408 g_ll_to_str(uchar_t *wwn_ll, char	*wwn_str)
409 {
410 int	j, k, fnib, snib;
411 uchar_t	c;
412 
413 	for (j = 0, k = 0; j < 8; j++) {
414 		c = wwn_ll[j];
415 		fnib = ((int)(c & 0xf0) >> 4);
416 		snib = (c & 0x0f);
417 		if (fnib >= 0 && fnib <= 9)
418 			wwn_str[k++] = '0' + fnib;
419 		else if (fnib >= 10 && fnib <= 15)
420 			wwn_str[k++] = 'a' + fnib - 10;
421 		if (snib >= 0 && snib <= 9)
422 			wwn_str[k++] = '0' + snib;
423 		else if (snib >= 10 && snib <= 15)
424 			wwn_str[k++] = 'a' + snib - 10;
425 	}
426 	wwn_str[k] = '\0';
427 }
428 
429 
430 
431 /*
432  * Creates a list of nexus paths for each
433  * hotpluggable device and sends the list to g_force_lip(),
434  * which forces the LIP on each nexus path in the list.
435  *
436  * RETURNS:
437  *	None.
438  */
439 int
440 g_forcelip_all(struct hotplug_disk_list *disk_list)
441 {
442 char		*p;
443 int		len, ndevs = 0, err = 0;
444 struct	dlist	*dl;
445 struct loop_list {	/* adp_name holds full dev path for MPXIO devices */
446 		char adp_name[MAXPATHLEN];
447 		struct loop_list *next;
448 		struct loop_list *prev;
449 	} *llist_head, *llist_tail, *llist, *llist1;
450 
451 	llist_head = llist_tail = NULL;
452 
453 	while (disk_list) {
454 		if (disk_list->dev_location == SENA) {
455 			dl = disk_list->seslist;
456 		} else {
457 			dl = disk_list->dlhead;
458 		}
459 		while (dl != NULL) {
460 			if (strstr(dl->dev_path, SCSI_VHCI) == NULL) {
461 				/* non-MPXIO device path */
462 				if (disk_list->dev_location == SENA) {
463 				    p = strstr(dl->dev_path, SLASH_SES);
464 				} else {
465 				    p = strstr(dl->dev_path, SLSH_DRV_NAME_SSD);
466 				    if (p == NULL) {
467 					p = strstr(dl->dev_path,
468 							SLSH_DRV_NAME_ST);
469 				    }
470 				}
471 				if (p == NULL) {
472 					P_DPRINTF(
473 					"  g_forcelip_all: Not able to do"
474 					" LIP on this path because path "
475 					"invalid.\n  Path: %s\n", dl->dev_path);
476 					dl = dl->next;
477 					continue;
478 				}
479 				len = strlen(dl->dev_path) - strlen(p);
480 			} else {
481 				/* MPXIO path */
482 				len = strlen(dl->dev_path);
483 			}
484 
485 			/*
486 			 * Avoid issuing forcelip
487 			 * on the same HA more than once
488 			 */
489 			if (llist_head != NULL) {
490 				for (llist1 = llist_head; llist1 != NULL;
491 						llist1 = llist1->next) {
492 					if (strncmp(llist1->adp_name,
493 						dl->dev_path, len) == 0) {
494 						break;
495 					}
496 				}
497 				if (llist1 != NULL) {
498 					dl = dl->next;
499 					continue;
500 				}
501 			}
502 			if ((llist = (struct loop_list *)
503 				g_zalloc(sizeof (struct loop_list))) == NULL)
504 				return (L_MALLOC_FAILED);
505 			(void) strncpy(llist->adp_name, dl->dev_path, len);
506 			llist->adp_name[len] = '\0';
507 			ndevs++;
508 
509 			if (llist_head == NULL) {
510 				llist_head = llist_tail = llist;
511 			} else {
512 				llist->prev = llist_tail;
513 				llist_tail = llist_tail->next = llist;
514 			}
515 			dl = dl->next;
516 		}
517 		disk_list = disk_list->next;
518 	}
519 
520 	while (llist_head) {
521 		if ((err = g_force_lip(llist_head->adp_name, 0)) != 0) {
522 			(void) g_destroy_data(llist);
523 			(void) g_destroy_data(llist_head);
524 			return (err);
525 		}
526 		llist = llist_head;
527 		llist_head = llist_head->next;
528 		(void) g_destroy_data((char *)llist);
529 	}
530 	(void) sleep(ndevs*10);
531 	return (0);
532 }
533