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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include "cfga_scsi.h"
28
29 struct larg {
30 int ndevs;
31 int nelem;
32 char *dev;
33 char **dev_list;
34 };
35
36 #define ETC_VFSTAB "/etc/vfstab"
37 #define SCFGA_LOCK "/var/run/cfgadm_scsi"
38
39 /* Function prototypes */
40
41 static scfga_ret_t quiesce_confirm(apid_t *apidp,
42 msgid_t cmd_msg, prompt_t *pt, int *okp, int *quiesce, int *l_errnop);
43 static scfga_ret_t dev_hotplug(apid_t *apidp,
44 prompt_t *pt, cfga_flags_t flags, int quiesce, char **errstring);
45 static int disconnect(struct cfga_confirm *confp);
46 static int critical_ctrlr(const char *hba_phys);
47 static cfga_stat_t bus_devctl_to_recep_state(uint_t bus_dc_state);
48 static int get_hba_children(char *bus_path, char *dev_excl, char ***dev_list);
49 static char *get_node_path(char *minor_path);
50 static void free_dev_list_elements(char **dev_list);
51 static void free_dev_list(char **dev_list);
52 static int alloc_dev_list(struct larg *largp);
53
54 /*
55 * Single thread all implicit quiesce operations
56 */
57 static mutex_t quiesce_mutex = DEFAULTMUTEX;
58
59 /*ARGSUSED*/
60 scfga_ret_t
bus_change_state(cfga_cmd_t state_change_cmd,apid_t * apidp,struct cfga_confirm * confp,cfga_flags_t flags,char ** errstring)61 bus_change_state(
62 cfga_cmd_t state_change_cmd,
63 apid_t *apidp,
64 struct cfga_confirm *confp,
65 cfga_flags_t flags,
66 char **errstring)
67 {
68 int l_errno = 0, force;
69 uint_t state = 0;
70 cfga_stat_t bus_state;
71 scfga_cmd_t cmd;
72 msgid_t errid;
73 cfga_stat_t prereq;
74 scfga_ret_t ret;
75 char **dev_list = NULL;
76
77 assert(apidp->path != NULL);
78 assert(apidp->hba_phys != NULL);
79
80 /*
81 * No dynamic components allowed
82 */
83 if (apidp->dyncomp != NULL) {
84 cfga_err(errstring, 0, ERR_NOT_BUSAPID, 0);
85 return (SCFGA_ERR);
86 }
87
88 /* Get bus state */
89 if (devctl_cmd(apidp->path, SCFGA_BUS_GETSTATE, &state,
90 &l_errno) != SCFGA_OK) {
91 cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
92 return (SCFGA_ERR);
93 }
94
95 bus_state = bus_devctl_to_recep_state(state);
96 force = ((flags & CFGA_FLAG_FORCE) == CFGA_FLAG_FORCE) ? 1 : 0;
97 assert(confp->confirm != NULL);
98
99 switch (state_change_cmd) {
100 case CFGA_CMD_DISCONNECT: /* quiesce bus */
101 /*
102 * If force flag not specified, check if controller is
103 * critical.
104 */
105 if (!force) {
106 /*
107 * This check is not foolproof, get user confirmation
108 * if test passes.
109 */
110 if (critical_ctrlr(apidp->path)) {
111 cfga_err(errstring, 0, ERR_CTRLR_CRIT, 0);
112 ret = SCFGA_ERR;
113 break;
114 } else if (!disconnect(confp)) {
115 ret = SCFGA_NACK;
116 break;
117 }
118 }
119
120 cmd = SCFGA_BUS_QUIESCE;
121 errid = ERR_BUS_QUIESCE;
122 prereq = CFGA_STAT_CONNECTED;
123
124 goto common;
125
126 case CFGA_CMD_CONNECT: /* unquiesce bus */
127 cmd = SCFGA_BUS_UNQUIESCE;
128 errid = ERR_BUS_UNQUIESCE;
129 prereq = CFGA_STAT_DISCONNECTED;
130
131 goto common;
132
133 case CFGA_CMD_CONFIGURE:
134 cmd = SCFGA_BUS_CONFIGURE;
135 errid = ERR_BUS_CONFIGURE;
136 prereq = CFGA_STAT_CONNECTED;
137
138 goto common;
139
140 case CFGA_CMD_UNCONFIGURE:
141 cmd = SCFGA_BUS_UNCONFIGURE;
142 errid = ERR_BUS_UNCONFIGURE;
143 prereq = CFGA_STAT_CONNECTED;
144
145 /* FALLTHROUGH */
146 common:
147 if (bus_state != prereq) {
148 cfga_err(errstring, 0,
149 (prereq == CFGA_STAT_CONNECTED)
150 ? ERR_BUS_NOTCONNECTED
151 : ERR_BUS_CONNECTED, 0);
152 ret = SCFGA_ERR;
153 break;
154 }
155
156 /*
157 * When quiescing or unconfiguring a bus, first suspend or
158 * offline it through RCM.
159 * For unquiescing, we simple build the dev_list for
160 * resume notification.
161 */
162 if (((apidp->flags & FLAG_DISABLE_RCM) == 0) &&
163 ((cmd == SCFGA_BUS_QUIESCE) ||
164 (cmd == SCFGA_BUS_UNQUIESCE) ||
165 (cmd == SCFGA_BUS_UNCONFIGURE))) {
166 ret = get_hba_children(apidp->path, NULL, &dev_list);
167 if (ret != SCFGA_OK) {
168 break;
169 }
170 if (cmd == SCFGA_BUS_QUIESCE) {
171 if ((ret = scsi_rcm_suspend(dev_list,
172 errstring, flags, 1)) != SCFGA_OK) {
173 break;
174 }
175 } else if (cmd == SCFGA_BUS_UNCONFIGURE) {
176 if ((ret = scsi_rcm_offline(dev_list,
177 errstring, flags)) != SCFGA_OK) {
178 break;
179 }
180 }
181 }
182
183 ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
184 if (ret != SCFGA_OK) {
185 /*
186 * EIO when child devices are busy may confuse user.
187 * So explain it.
188 */
189 if (cmd == SCFGA_BUS_UNCONFIGURE && l_errno == EIO)
190 errid = ERR_MAYBE_BUSY;
191
192 cfga_err(errstring, l_errno, errid, 0);
193
194 /*
195 * If the bus was suspended in RCM, then cancel the RCM
196 * operation. Discard RCM failures here because the
197 * devctl's failure is what is most relevant.
198 */
199 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
200 if (cmd == SCFGA_BUS_QUIESCE)
201 (void) scsi_rcm_resume(dev_list,
202 errstring,
203 (flags & (~CFGA_FLAG_FORCE)), 1);
204 else if (cmd == SCFGA_BUS_UNCONFIGURE) {
205 (void) devctl_cmd(apidp->path,
206 SCFGA_BUS_CONFIGURE, NULL,
207 &l_errno);
208 (void) scsi_rcm_online(dev_list,
209 errstring,
210 (flags & (~CFGA_FLAG_FORCE)));
211 }
212 }
213
214 break;
215 }
216
217 /*
218 * When unquiescing or configuring a bus, resume or online it
219 * in RCM when the devctl command is complete.
220 * When unconfiguring a bus, notify removal of devices.
221 */
222 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
223 if (cmd == SCFGA_BUS_UNQUIESCE) {
224 ret = scsi_rcm_resume(dev_list, errstring,
225 (flags & (~CFGA_FLAG_FORCE)), 1);
226 } else if (cmd == SCFGA_BUS_UNCONFIGURE) {
227 ret = scsi_rcm_remove(dev_list, errstring,
228 (flags & (~CFGA_FLAG_FORCE)));
229 }
230 }
231 break;
232
233 case CFGA_CMD_LOAD:
234 case CFGA_CMD_UNLOAD:
235 ret = SCFGA_OPNOTSUPP;
236 break;
237
238 default:
239 cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
240 ret = SCFGA_ERR;
241 break;
242 }
243
244 free_dev_list(dev_list);
245 return (ret);
246 }
247
248 scfga_ret_t
dev_change_state(cfga_cmd_t state_change_cmd,apid_t * apidp,cfga_flags_t flags,char ** errstring)249 dev_change_state(
250 cfga_cmd_t state_change_cmd,
251 apid_t *apidp,
252 cfga_flags_t flags,
253 char **errstring)
254 {
255 uint_t state = 0;
256 int l_errno = 0;
257 cfga_stat_t bus_state;
258 scfga_cmd_t cmd;
259 msgid_t errid;
260 scfga_ret_t ret;
261 char *dev_list[2] = {NULL};
262
263 assert(apidp->path != NULL);
264 assert(apidp->hba_phys != NULL);
265
266 /*
267 * For a device, dynamic component must be present
268 */
269 if (apidp->dyncomp == NULL) {
270 cfga_err(errstring, 0, ERR_APID_INVAL, 0);
271 return (SCFGA_ERR);
272 }
273
274 /* Get bus state */
275 if (devctl_cmd(apidp->hba_phys, SCFGA_BUS_GETSTATE, &state,
276 &l_errno) != SCFGA_OK) {
277 cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
278 return (SCFGA_ERR);
279 }
280
281 bus_state = bus_devctl_to_recep_state(state);
282
283 switch (state_change_cmd) {
284 case CFGA_CMD_CONFIGURE: /* online device */
285 cmd = SCFGA_DEV_CONFIGURE;
286 errid = ERR_DEV_CONFIGURE;
287 goto common;
288
289 case CFGA_CMD_UNCONFIGURE: /* offline device */
290 cmd = SCFGA_DEV_UNCONFIGURE;
291 errid = ERR_DEV_UNCONFIGURE;
292 /* FALLTHROUGH */
293 common:
294 if (bus_state != CFGA_STAT_CONNECTED) {
295 cfga_err(errstring, 0, ERR_BUS_NOTCONNECTED, 0);
296 ret = SCFGA_ERR;
297 break;
298 }
299
300 if (apidp->dyntype == PATH_APID) {
301 /* call a scsi_vhci ioctl to do online/offline path. */
302 ret = path_apid_state_change(apidp, cmd,
303 flags, errstring, &l_errno, errid);
304 } else {
305 /*
306 * When unconfiguring a device, first offline it
307 * through RCM.
308 */
309 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
310 if (cmd == SCFGA_DEV_UNCONFIGURE) {
311 dev_list[0] =
312 get_node_path(apidp->path);
313 if (dev_list[0] == NULL) {
314 ret = SCFGA_ERR;
315 break;
316 }
317 if ((ret = scsi_rcm_offline(dev_list,
318 errstring, flags)) != SCFGA_OK) {
319 break;
320 }
321 }
322 }
323
324 ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
325 if (ret != SCFGA_OK) {
326 cfga_err(errstring, l_errno, errid, 0);
327
328 /*
329 * If an unconfigure fails, cancel the RCM offline.
330 * Discard any RCM failures so that the devctl
331 * failure will still be reported.
332 */
333 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
334 if (cmd == SCFGA_DEV_UNCONFIGURE)
335 (void) scsi_rcm_online(dev_list,
336 errstring, flags);
337 }
338 break;
339 }
340 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
341 /*
342 * Unconfigure succeeded, call the RCM notify_remove.
343 */
344 if (cmd == SCFGA_DEV_UNCONFIGURE)
345 (void) scsi_rcm_remove(dev_list,
346 errstring, flags);
347 }
348 }
349 break;
350
351 /*
352 * Cannot disconnect/connect individual devices without affecting
353 * other devices on the bus. So we don't support these ops.
354 */
355 case CFGA_CMD_DISCONNECT:
356 case CFGA_CMD_CONNECT:
357 cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
358 ret = SCFGA_ERR;
359 break;
360 case CFGA_CMD_LOAD:
361 case CFGA_CMD_UNLOAD:
362 ret = SCFGA_OPNOTSUPP;
363 break;
364 default:
365 cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
366 ret = SCFGA_ERR;
367 break;
368 }
369
370 free_dev_list_elements(dev_list);
371 return (ret);
372 }
373
374 /*ARGSUSED*/
375 scfga_ret_t
dev_remove(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)376 dev_remove(
377 const char *func,
378 scfga_cmd_t cmd,
379 apid_t *apidp,
380 prompt_t *prp,
381 cfga_flags_t flags,
382 char **errstring)
383 {
384 int proceed, l_errno = 0;
385 scfga_ret_t ret;
386 int do_quiesce;
387 char *dev_list[2] = {NULL};
388
389 assert(apidp->hba_phys != NULL);
390 assert(apidp->path != NULL);
391
392 /* device operation only */
393 if (apidp->dyncomp == NULL) {
394 cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
395 return (SCFGA_ERR);
396 }
397
398 proceed = 1;
399 ret = quiesce_confirm(apidp, MSG_RMDEV, prp, &proceed, &do_quiesce,
400 &l_errno);
401 if (ret != SCFGA_OK) {
402 cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
403 return (ret);
404 }
405
406 if (!proceed) {
407 return (SCFGA_NACK);
408 }
409
410 /*
411 * Offline the device in RCM
412 */
413 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
414 dev_list[0] = get_node_path(apidp->path);
415 if (dev_list[0] == NULL)
416 return (SCFGA_ERR);
417 if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
418 != SCFGA_OK) {
419 free_dev_list_elements(dev_list);
420 return (ret);
421 }
422 }
423
424 /*
425 * Offline the device
426 */
427 ret = devctl_cmd(apidp->path, SCFGA_DEV_UNCONFIGURE, NULL, &l_errno);
428 if (ret != SCFGA_OK) {
429
430 cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
431
432 /*
433 * Cancel the RCM offline. Discard the RCM failures so that
434 * the above devctl failure is still reported.
435 */
436 if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
437 (void) scsi_rcm_online(dev_list, errstring, flags);
438 free_dev_list_elements(dev_list);
439 return (ret);
440 }
441
442 /* Do the physical removal */
443 ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
444
445 if (ret == SCFGA_OK) {
446 /*
447 * Complete the remove.
448 * Since the device is already offlined, remove shouldn't
449 * fail. Even if remove fails, there is no side effect.
450 */
451 (void) devctl_cmd(apidp->path, SCFGA_DEV_REMOVE,
452 NULL, &l_errno);
453 if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
454 ret = scsi_rcm_remove(dev_list, errstring, flags);
455 } else {
456 /*
457 * Reconfigure the device and restore the device's RCM state.
458 * If reconfigure succeeds, restore the state to online.
459 * If reconfigure fails (e.g. a typo from user), we treat
460 * the device as removed.
461 */
462 if (devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno)
463 == SCFGA_OK) {
464 if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
465 (void) scsi_rcm_online(dev_list, errstring,
466 flags);
467 } else {
468 char *cp = strrchr(apidp->path, ':');
469 if (cp)
470 *cp = '\0';
471 cfga_err(errstring, l_errno, ERR_DEV_RECONFIGURE,
472 apidp->path, 0);
473 if (cp)
474 *cp = ':';
475 if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
476 (void) scsi_rcm_remove(dev_list, errstring,
477 flags);
478 }
479 }
480
481 free_dev_list_elements(dev_list);
482 return (ret);
483 }
484
485 /*ARGSUSED*/
486 scfga_ret_t
dev_insert(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)487 dev_insert(
488 const char *func,
489 scfga_cmd_t cmd,
490 apid_t *apidp,
491 prompt_t *prp,
492 cfga_flags_t flags,
493 char **errstring)
494 {
495 int proceed, l_errno = 0;
496 scfga_ret_t ret;
497 int do_quiesce;
498
499 assert(apidp->hba_phys != NULL);
500 assert(apidp->path != NULL);
501
502 /* Currently, insert operation only allowed for bus */
503 if (apidp->dyncomp != NULL) {
504 cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
505 return (SCFGA_ERR);
506 }
507
508 proceed = 1;
509 ret = quiesce_confirm(apidp, MSG_INSDEV, prp, &proceed, &do_quiesce,
510 &l_errno);
511 if (ret != SCFGA_OK) {
512 cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
513 return (ret);
514 }
515
516 if (!proceed) {
517 return (SCFGA_NACK);
518 }
519
520 /* Do the physical addition */
521 ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
522 if (ret != SCFGA_OK) {
523 return (ret);
524 }
525
526 /*
527 * Configure bus to online new device(s).
528 * Previously offlined devices will not be onlined.
529 */
530 ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_CONFIGURE, NULL, &l_errno);
531 if (ret != SCFGA_OK) {
532 cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
533 return (SCFGA_ERR);
534 }
535
536 return (SCFGA_OK);
537 }
538
539 /*ARGSUSED*/
540 scfga_ret_t
dev_replace(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)541 dev_replace(
542 const char *func,
543 scfga_cmd_t cmd,
544 apid_t *apidp,
545 prompt_t *prp,
546 cfga_flags_t flags,
547 char **errstring)
548 {
549 int proceed, l_errno = 0;
550 scfga_ret_t ret, ret2;
551 int do_quiesce;
552 char *dev_list[2] = {NULL};
553
554 assert(apidp->hba_phys != NULL);
555 assert(apidp->path != NULL);
556
557 /* device operation only */
558 if (apidp->dyncomp == NULL) {
559 cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
560 return (SCFGA_ERR);
561 }
562
563 proceed = 1;
564 ret = quiesce_confirm(apidp, MSG_REPLDEV, prp, &proceed, &do_quiesce,
565 &l_errno);
566 if (ret != SCFGA_OK) {
567 cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
568 return (ret);
569 }
570
571 if (!proceed) {
572 return (SCFGA_NACK);
573 }
574
575 /* Offline the device in RCM */
576 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
577 dev_list[0] = get_node_path(apidp->path);
578 if (dev_list[0] == NULL)
579 return (SCFGA_ERR);
580 if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
581 != SCFGA_OK) {
582 free_dev_list_elements(dev_list);
583 return (ret);
584 }
585 }
586
587 ret = devctl_cmd(apidp->path, SCFGA_DEV_REMOVE, NULL, &l_errno);
588 if (ret != SCFGA_OK) {
589
590 /*
591 * Cancel the RCM offline. Discard any RCM failures so that
592 * the devctl failure can still be reported.
593 */
594 if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
595 (void) scsi_rcm_online(dev_list, errstring, flags);
596
597 cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
598 free_dev_list_elements(dev_list);
599 return (ret);
600 }
601
602 /* do the physical replace */
603 ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
604
605 /* Online the replacement, or restore state on error */
606 ret2 = devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno);
607
608 if (ret2 != SCFGA_OK) {
609 cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
610 }
611
612 /*
613 * Remove the replaced device in RCM, or online the device in RCM
614 * to recover.
615 */
616 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
617 if (ret == SCFGA_OK)
618 ret = scsi_rcm_remove(dev_list, errstring, flags);
619 else if (ret2 == SCFGA_OK)
620 ret2 = scsi_rcm_online(dev_list, errstring, flags);
621 }
622 free_dev_list_elements(dev_list);
623
624 return (ret == SCFGA_OK ? ret2 : ret);
625 }
626
627 #pragma weak plat_dev_led
628 /*ARGSUSED*/
629 scfga_ret_t
dev_led(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)630 dev_led(
631 const char *func,
632 scfga_cmd_t cmd,
633 apid_t *apidp,
634 prompt_t *prp,
635 cfga_flags_t flags,
636 char **errstring)
637 {
638
639 /*
640 * The implementation of the led command is platform-specific, so
641 * the default behavior is to say that the functionality is not
642 * available for this device.
643 */
644 if (plat_dev_led) {
645 return (plat_dev_led(func, cmd, apidp, prp, flags, errstring));
646 }
647 cfga_err(errstring, 0, ERR_UNAVAILABLE, 0);
648 return (SCFGA_ERR);
649 }
650
651 /*ARGSUSED*/
652 scfga_ret_t
reset_common(const char * func,scfga_cmd_t cmd,apid_t * apidp,prompt_t * prp,cfga_flags_t flags,char ** errstring)653 reset_common(
654 const char *func,
655 scfga_cmd_t cmd,
656 apid_t *apidp,
657 prompt_t *prp,
658 cfga_flags_t flags,
659 char **errstring)
660 {
661 int l_errno = 0;
662 scfga_ret_t ret;
663
664
665 assert(apidp->path != NULL);
666 assert(apidp->hba_phys != NULL);
667
668 switch (cmd) {
669 case SCFGA_RESET_DEV:
670 if (apidp->dyncomp == NULL) {
671 cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
672 return (SCFGA_ERR);
673 }
674 break;
675
676 case SCFGA_RESET_BUS:
677 case SCFGA_RESET_ALL:
678 if (apidp->dyncomp != NULL) {
679 cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
680 return (SCFGA_ERR);
681 }
682 break;
683 default:
684 cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
685 return (SCFGA_ERR);
686 }
687
688 ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
689 if (ret != SCFGA_OK) {
690 cfga_err(errstring, l_errno, ERR_RESET, 0);
691 }
692
693 return (ret);
694 }
695
696 static int
disconnect(struct cfga_confirm * confp)697 disconnect(struct cfga_confirm *confp)
698 {
699 int ans, append_newline;
700 char *cq;
701
702 append_newline = 0;
703 cq = cfga_str(append_newline, WARN_DISCONNECT, 0);
704
705 ans = confp->confirm(confp->appdata_ptr, cq);
706
707 S_FREE(cq);
708
709 return (ans == 1);
710 }
711
712 /*
713 * Check for "scsi-no-quiesce" property
714 * Return code: -1 error, 0 quiesce not required, 1 quiesce required
715 */
716 static int
quiesce_required(apid_t * apidp,int * l_errnop)717 quiesce_required(apid_t *apidp, int *l_errnop)
718 {
719 di_node_t bus_node, dev_node;
720 char *bus_path, *bus_end;
721 char *dev_path, *dev_end;
722 int *propval;
723
724 /* take libdevinfo snapshot of subtree at hba */
725 bus_path = apidp->hba_phys + strlen(DEVICES_DIR);
726 bus_end = strrchr(bus_path, ':');
727 if (bus_end)
728 *bus_end = '\0';
729
730 bus_node = di_init(bus_path, DINFOSUBTREE|DINFOPROP);
731 if (bus_end)
732 *bus_end = ':';
733 if (bus_node == DI_NODE_NIL) {
734 *l_errnop = errno;
735 return (-1); /* error */
736 }
737
738 /* check bus node for property */
739 if (di_prop_lookup_ints(DDI_DEV_T_ANY, bus_node, SCSI_NO_QUIESCE,
740 &propval) == 1) {
741 di_fini(bus_node);
742 return (0); /* quiesce not required */
743 }
744
745 /* if this ap is HBA, return with quiesce required */
746 if (apidp->dyncomp == NULL) {
747 di_fini(bus_node);
748 return (1);
749 }
750
751 /* check device node for property */
752 dev_path = apidp->path + strlen(DEVICES_DIR);
753 dev_end = strrchr(dev_path, ':');
754 if (dev_end)
755 *dev_end = '\0';
756
757 dev_node = di_child_node(bus_node);
758 while (dev_node != DI_NODE_NIL) {
759 char *child_path;
760 child_path = di_devfs_path(dev_node);
761 if (strcmp(child_path, dev_path) == 0) {
762 di_devfs_path_free(child_path);
763 break;
764 }
765 di_devfs_path_free(child_path);
766 dev_node = di_sibling_node(dev_node);
767 }
768
769 if (dev_end)
770 *dev_end = ':';
771 if (dev_node == DI_NODE_NIL) {
772 di_fini(bus_node);
773 return (1); /* dev not found (insert case) */
774 }
775
776 /* check child node for property */
777 if (di_prop_lookup_ints(DDI_DEV_T_ANY, dev_node, "scsi-no-quiesce",
778 &propval) == 1) {
779 di_fini(bus_node);
780 return (0); /* quiesce not required */
781 }
782 return (1); /* quiesce required */
783 }
784
785 static scfga_ret_t
quiesce_confirm(apid_t * apidp,msgid_t cmd_msg,prompt_t * prp,int * okp,int * quiesce,int * l_errnop)786 quiesce_confirm(
787 apid_t *apidp,
788 msgid_t cmd_msg,
789 prompt_t *prp,
790 int *okp,
791 int *quiesce,
792 int *l_errnop)
793 {
794 char *buf = NULL, *hbap = NULL, *cq1 = NULL, *cq2 = NULL;
795 char *cp;
796 size_t len = 0;
797 int i = 0, append_newline;
798 scfga_ret_t ret;
799
800 assert(apidp->path != NULL);
801 assert(apidp->hba_phys != NULL);
802
803 *quiesce = quiesce_required(apidp, l_errnop);
804 if (*quiesce == -1)
805 return (SCFGA_ERR);
806 else if (*quiesce == 0)
807 return (SCFGA_OK);
808
809 /*
810 * Try to create HBA logical ap_id.
811 * If that fails use physical path
812 */
813 ret = make_hba_logid(apidp->hba_phys, &hbap, &i);
814 if (ret != SCFGA_OK) {
815 if ((hbap = get_node_path(apidp->hba_phys)) == NULL) {
816 *l_errnop = errno;
817 return (SCFGA_LIB_ERR);
818 }
819 }
820
821 assert(hbap != NULL);
822
823 append_newline = 0;
824 cq1 = cfga_str(append_newline, CONF_QUIESCE_1, hbap, 0);
825 cq2 = cfga_str(append_newline, CONF_QUIESCE_2, 0);
826 len = strlen(cq1) + strlen(cq2) + 1; /* Includes term. NULL */
827
828 if ((buf = calloc(1, len)) == NULL) {
829 *l_errnop = errno;
830 ret = SCFGA_LIB_ERR;
831 S_FREE(cq1);
832 S_FREE(cq2);
833 goto out;
834 }
835 (void) strcpy(buf, cq1);
836 (void) strcat(buf, cq2);
837
838 S_FREE(cq1);
839 S_FREE(cq2);
840
841
842 /* Remove minor name (if any) from phys path */
843 if ((cp = strrchr(apidp->path, ':')) != NULL) {
844 *cp = '\0';
845 }
846
847 /* describe operation being attempted */
848 cfga_msg(prp->msgp, cmd_msg, apidp->path, 0);
849
850 /* Restore minor name */
851 if (cp != NULL) {
852 *cp = ':';
853 }
854
855 /* request permission to quiesce */
856 assert(prp->confp != NULL && prp->confp->confirm != NULL);
857 *okp = prp->confp->confirm(prp->confp->appdata_ptr, buf);
858
859 ret = SCFGA_OK;
860 /*FALLTHRU*/
861 out:
862 S_FREE(buf);
863 S_FREE(hbap);
864 return (ret);
865 }
866
867 static scfga_ret_t
suspend_in_rcm(apid_t * apidp,char *** suspend_list_ptr,char ** errstring,cfga_flags_t flags)868 suspend_in_rcm(
869 apid_t *apidp,
870 char ***suspend_list_ptr,
871 char **errstring,
872 cfga_flags_t flags)
873 {
874 scfga_ret_t ret;
875 char *bus_path = NULL;
876 char *dev_path = NULL;
877 char **suspend_list = NULL;
878
879 *suspend_list_ptr = NULL;
880
881 /* Suspend the bus through RCM */
882 if (apidp->flags & FLAG_DISABLE_RCM)
883 return (SCFGA_OK);
884
885 /* The bus_path is the HBA path without its minor */
886 if ((bus_path = get_node_path(apidp->hba_phys)) == NULL)
887 return (SCFGA_ERR);
888
889 /*
890 * The dev_path is already initialized to NULL. If the AP Id
891 * path differs from the HBA path, then the dev_path should
892 * instead be set to the AP Id path without its minor.
893 */
894 if (strcmp(apidp->hba_phys, apidp->path) != 0) {
895 if ((dev_path = get_node_path(apidp->path)) == NULL) {
896 ret = SCFGA_ERR;
897 goto out;
898 }
899 }
900
901 if ((ret = get_hba_children(bus_path, dev_path, &suspend_list))
902 != SCFGA_OK) {
903 free_dev_list(suspend_list);
904 goto out;
905 }
906
907 if (scsi_rcm_suspend(suspend_list, errstring, flags, 0) != SCFGA_OK) {
908 ret = SCFGA_ERR;
909 free_dev_list(suspend_list);
910 } else {
911 ret = SCFGA_OK;
912 *suspend_list_ptr = suspend_list;
913 }
914 /*FALLTHROUGH*/
915 out:
916 S_FREE(bus_path);
917 S_FREE(dev_path);
918 return (ret);
919 }
920
921 /*
922 * Resume the bus through RCM if it successfully
923 * unquiesced.
924 */
925 static void
resume_in_rcm(apid_t * apidp,char ** suspend_list,char ** errstring,cfga_flags_t flags)926 resume_in_rcm(
927 apid_t *apidp,
928 char **suspend_list,
929 char **errstring,
930 cfga_flags_t flags)
931 {
932 if (apidp->flags & FLAG_DISABLE_RCM)
933 return;
934
935 (void) scsi_rcm_resume(suspend_list, errstring, flags, 0);
936
937 free_dev_list(suspend_list);
938 }
939
940 static scfga_ret_t
wait_for_hotplug(prompt_t * pt,int msg)941 wait_for_hotplug(prompt_t *pt, int msg)
942 {
943 char *cu = NULL;
944 int append_newline = 0;
945 scfga_ret_t ret;
946
947 cu = cfga_str(append_newline, msg, 0);
948 if (pt->confp->confirm(pt->confp->appdata_ptr, cu) != 1) {
949 ret = SCFGA_NACK;
950 } else {
951 ret = SCFGA_OK;
952 }
953 S_FREE(cu);
954 return (ret);
955 }
956
957 static scfga_ret_t
bus_quiesce(apid_t * apidp,prompt_t * pt,char ** errstring,cfga_flags_t flags)958 bus_quiesce(apid_t *apidp, prompt_t *pt, char **errstring, cfga_flags_t flags)
959 {
960 int l_errno;
961 scfga_ret_t ret;
962 scfga_ret_t hpret;
963 char **suspend_list = NULL;
964
965 ret = suspend_in_rcm(apidp, &suspend_list, errstring, flags);
966 if (ret != SCFGA_OK) {
967 return (ret);
968 }
969
970 /*
971 * If the quiesce fails, then cancel the RCM suspend.
972 * Discard any RCM failures so that the devctl failure
973 * can still be reported.
974 */
975 l_errno = 0;
976 ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_QUIESCE, NULL, &l_errno);
977 if (ret != SCFGA_OK) {
978 resume_in_rcm(apidp, suspend_list, errstring, flags);
979 cfga_err(errstring, l_errno, ERR_BUS_QUIESCE, 0);
980 return (ret);
981 }
982
983 /*
984 * Prompt user to proceed with physical hotplug
985 * and wait until they are done.
986 */
987 hpret = wait_for_hotplug(pt, CONF_UNQUIESCE);
988
989 /*
990 * The unquiesce may fail with EALREADY (which is ok)
991 * or some other error (which is not ok).
992 */
993 l_errno = 0;
994 ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
995 if (ret != SCFGA_OK && l_errno != EALREADY) {
996 free_dev_list(suspend_list);
997 cfga_err(errstring, l_errno, ERR_BUS_UNQUIESCE, 0);
998 return (SCFGA_ERR);
999 }
1000
1001 resume_in_rcm(apidp, suspend_list, errstring, flags);
1002
1003 return (hpret);
1004 }
1005
1006 #define MAX_LOCK_TRIES 20
1007 #define MAX_UNLINK_TRIES 60
1008 #define s_getpid (int)getpid /* else lint causes problems */
1009
1010 static void
s_unlink(char * file)1011 s_unlink(char *file)
1012 {
1013 int count = 0;
1014
1015 retry:
1016 if (unlink(file) == -1) {
1017 if (errno != EINTR && errno != EAGAIN) {
1018 CFGA_TRACE1((stdout, "s_unlink[%d]: unlink failed: "
1019 "%s: %s\n", s_getpid(), file, strerror(errno)));
1020 return;
1021 }
1022
1023 if (++count < MAX_UNLINK_TRIES) {
1024 (void) sleep(1);
1025 goto retry;
1026 }
1027 CFGA_TRACE1((stdout, "s_unlink[%d]: retry limit: %s\n",
1028 s_getpid(), file));
1029 } else {
1030 CFGA_TRACE3((stdout, "s_unlink[%d]: unlinked: %s\n",
1031 s_getpid(), file));
1032 }
1033 }
1034
1035 static scfga_ret_t
create_lock(int * fdp,struct cfga_msg * msgp,char ** errstring)1036 create_lock(int *fdp, struct cfga_msg *msgp, char **errstring)
1037 {
1038 FILE *fp;
1039 int count;
1040 struct extmnttab ent;
1041 int mnted;
1042
1043
1044 *fdp = -1;
1045
1046 /*
1047 * Check that /var/run is mounted. In the unlikely event
1048 * that the lock file is left behind, we want it
1049 * cleared on the next reboot.
1050 */
1051 errno = 0;
1052 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1053 cfga_err(errstring, errno, ERRARG_OPEN, MNTTAB, 0);
1054 return (SCFGA_LIB_ERR);
1055 }
1056
1057 resetmnttab(fp);
1058
1059 mnted = 0;
1060 while (getextmntent(fp, &ent, sizeof (ent)) == 0) {
1061 if (strcmp(ent.mnt_mountp, "/var/run") == 0) {
1062 mnted = 1;
1063 break;
1064 }
1065 }
1066
1067 (void) fclose(fp);
1068
1069 if (!mnted) {
1070 cfga_err(errstring, 0, ERR_VAR_RUN, 0);
1071 return (SCFGA_LIB_ERR);
1072 }
1073
1074 /*
1075 * Wait for a short period of time if we cannot O_EXCL create
1076 * lock file. If some other cfgadm process is finishing up, we
1077 * can get in. If the wait required is long however, just
1078 * return SYSTEM_BUSY to the user - a hotplug operation is
1079 * probably in progress.
1080 */
1081 count = 0;
1082 retry:
1083 *fdp = open(SCFGA_LOCK, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
1084 if (*fdp == -1 && errno == EEXIST) {
1085 if (++count < MAX_LOCK_TRIES) {
1086 if (count == 1)
1087 cfga_msg(msgp, MSG_WAIT_LOCK, 0);
1088 (void) sleep(1);
1089 goto retry;
1090 }
1091 }
1092
1093 if (*fdp == -1 && errno == EEXIST) {
1094 cfga_err(errstring, 0, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
1095 return (SCFGA_SYSTEM_BUSY);
1096 } else if (*fdp == -1) {
1097 cfga_err(errstring, errno, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
1098 return (SCFGA_LIB_ERR);
1099 }
1100
1101 CFGA_TRACE3((stdout, "create_lock[%d]: created lockfile: %s\n",
1102 s_getpid(), SCFGA_LOCK));
1103
1104 return (SCFGA_OK);
1105 }
1106
1107 static scfga_ret_t
syslock(int fd,char ** errstring)1108 syslock(int fd, char **errstring)
1109 {
1110 struct flock lock;
1111 int count;
1112 int rval;
1113
1114 assert(fd != -1);
1115
1116 CFGA_TRACE3((stdout, "syslock[%d]: trying lock: %s\n",
1117 s_getpid(), SCFGA_LOCK));
1118
1119 lock.l_type = F_WRLCK;
1120 lock.l_whence = SEEK_SET;
1121 lock.l_start = 0;
1122 lock.l_len = 0;
1123
1124 count = 0;
1125 while ((rval = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) {
1126 if (++count >= MAX_LOCK_TRIES) {
1127 CFGA_TRACE1((stdout, "syslock[%d]: retry limit: %s\n",
1128 s_getpid(), SCFGA_LOCK));
1129 goto badlock;
1130 }
1131 (void) sleep(1);
1132 }
1133
1134 if (rval != -1) {
1135 CFGA_TRACE3((stdout, "syslock[%d]: locked file: %s\n",
1136 s_getpid(), SCFGA_LOCK));
1137 return (SCFGA_OK);
1138 }
1139
1140 /*FALLTHROUGH*/
1141 badlock:
1142 cfga_err(errstring, errno, ERRARG_LOCK, SCFGA_LOCK, 0);
1143 /* trace message to display pid */
1144 CFGA_TRACE1((stdout, "syslock[%d]: cannot lock %s\n",
1145 s_getpid(), SCFGA_LOCK));
1146 return (SCFGA_LIB_ERR);
1147 }
1148
1149 static void
wait_for_child(pid_t cpid)1150 wait_for_child(pid_t cpid)
1151 {
1152 int status;
1153 pid_t rval;
1154
1155 CFGA_TRACE2((stdout, "wait_for_child[%d]: child[%d]\n",
1156 s_getpid(), (int)cpid));
1157
1158 for (;;) {
1159 while ((rval = waitpid(cpid, &status, 0)) != cpid) {
1160 if (errno == ECHILD) {
1161 CFGA_TRACE1((stdout, "waitpid[%d]: child[%d] "
1162 "doesn't exist\n", s_getpid(), (int)cpid));
1163 return;
1164 }
1165
1166 CFGA_TRACE3((stdout, "waitpid[%d]: returned: %d"
1167 ": errno: %s\n", s_getpid(), (int)rval,
1168 strerror(errno)));
1169 }
1170
1171 if (WIFEXITED(status)) {
1172 CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
1173 "normal exit\n", s_getpid(), (int)cpid));
1174 return;
1175 }
1176
1177 if (WIFSIGNALED(status)) {
1178 CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
1179 "signal exit\n", s_getpid(), (int)cpid));
1180 return;
1181 }
1182
1183 /*
1184 * The child has not terminated. We received status
1185 * because the child was either stopped or continued.
1186 * Wait for child termination by calling waitpid() again.
1187 */
1188 }
1189 }
1190
1191 static void
wait_and_cleanup(int fd,apid_t * apidp)1192 wait_and_cleanup(int fd, apid_t *apidp)
1193 {
1194 int l_errno;
1195 scfga_ret_t ret;
1196
1197 /* This is the child */
1198 CFGA_TRACE2((stdout, "child[%d]: Entering wait_cleanup\n", s_getpid()));
1199
1200 if (syslock(fd, NULL) != SCFGA_OK) {
1201 CFGA_TRACE1((stdout, "child[%d]: lock failure "
1202 " - _exit(1)\n", s_getpid()));
1203 /*
1204 * As a last resort, unlink the lock file. This is relatively
1205 * safe as the child doesn't unquiesce the bus in this case.
1206 */
1207 s_unlink(SCFGA_LOCK);
1208 _exit(1);
1209 }
1210
1211 l_errno = 0;
1212 ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
1213 if (ret != SCFGA_OK) {
1214 if (l_errno == EALREADY)
1215 CFGA_TRACE3((stdout, "child[%d]: bus already "
1216 "unquiesced: %s\n", s_getpid(), apidp->hba_phys));
1217 else
1218 CFGA_TRACE1((stdout, "child[%d]: unquiesce failed: "
1219 "%s\n", s_getpid(), strerror(l_errno)));
1220 } else {
1221 CFGA_TRACE1((stdout, "child[%d]: unquiesced bus: %s\n",
1222 s_getpid(), apidp->hba_phys));
1223 }
1224
1225 s_unlink(SCFGA_LOCK);
1226
1227 CFGA_TRACE2((stdout, "child[%d]: _exit(0)\n", s_getpid()));
1228
1229 _exit(0);
1230 }
1231
1232 static void
sigblk(sigset_t * osp)1233 sigblk(sigset_t *osp)
1234 {
1235 sigset_t set;
1236
1237 (void) sigemptyset(&set);
1238 (void) sigemptyset(osp);
1239 (void) sigaddset(&set, SIGHUP);
1240 (void) sigaddset(&set, SIGINT);
1241 (void) sigaddset(&set, SIGQUIT);
1242 (void) sigaddset(&set, SIGTERM);
1243 (void) sigaddset(&set, SIGUSR1);
1244 (void) sigaddset(&set, SIGUSR2);
1245 (void) sigprocmask(SIG_BLOCK, &set, osp);
1246 }
1247
1248 static void
sigunblk(sigset_t * osp)1249 sigunblk(sigset_t *osp)
1250 {
1251 (void) sigprocmask(SIG_SETMASK, osp, NULL);
1252 }
1253
1254 /*
1255 * Here is the algorithm used to ensure that a SCSI bus is not
1256 * left in the quiesced state:
1257 *
1258 * lock quiesce mutex // single threads this code
1259 * open(O_CREAT|O_EXCL) lock file // only 1 process at a time
1260 * exclusive record lock on lock file
1261 * fork1()
1262 * quiesce bus
1263 * do the physical hotplug operation
1264 * unquiesce bus
1265 * unlock record lock
1266 * -> *child*
1267 * -> wait for record lock
1268 * -> unconditionally unquiesce bus
1269 * -> unlink lock file
1270 * -> exit
1271 * wait for child to exit
1272 * unlock quiesce mutex
1273 *
1274 * NOTE1: since record locks are per-process and a close() can
1275 * release a lock, to keep things MT-safe we need a quiesce mutex.
1276 *
1277 * NOTE2: To ensure that the child does not unquiesce a bus quiesced
1278 * by an unrelated cfgadm_scsi operation, exactly 1 process in the
1279 * system can be doing an implicit quiesce operation The exclusive
1280 * creation of the lock file guarantees this.
1281 *
1282 * NOTE3: This works even if the parent process dumps core and/or is
1283 * abnormally terminated. If the parent dies before the child is
1284 * forked, the bus is not quiesced. If the parent dies after the
1285 * bus is quiesced, the child process will ensure that the bus is
1286 * unquiesced.
1287 */
1288 static scfga_ret_t
dev_hotplug(apid_t * apidp,prompt_t * pt,cfga_flags_t flags,int do_quiesce,char ** errstring)1289 dev_hotplug(
1290 apid_t *apidp,
1291 prompt_t *pt,
1292 cfga_flags_t flags,
1293 int do_quiesce,
1294 char **errstring)
1295 {
1296 scfga_ret_t ret;
1297 pid_t cpid;
1298 int fd;
1299 sigset_t oset;
1300
1301 assert(apidp->hba_phys != NULL);
1302 assert(apidp->path != NULL);
1303
1304 /* If no quiesce required, prompt the user to do the operation */
1305 if (!do_quiesce)
1306 return (wait_for_hotplug(pt, CONF_NO_QUIESCE));
1307
1308 (void) mutex_lock(&quiesce_mutex);
1309
1310 ret = create_lock(&fd, pt->msgp, errstring);
1311 if (ret != SCFGA_OK) {
1312 (void) mutex_unlock(&quiesce_mutex);
1313 return (ret);
1314 }
1315
1316 ret = syslock(fd, errstring);
1317 if (ret != SCFGA_OK) {
1318 goto bad;
1319 }
1320
1321 /*
1322 * block signals in the child. Parent may
1323 * exit, causing signal to be sent to child.
1324 */
1325 sigblk(&oset);
1326
1327 switch (cpid = fork1()) {
1328 case 0:
1329 /* child */
1330 wait_and_cleanup(fd, apidp);
1331 _exit(0); /* paranoia */
1332 /*NOTREACHED*/
1333 case -1:
1334 cfga_err(errstring, errno, ERR_FORK, 0);
1335 sigunblk(&oset);
1336 ret = SCFGA_LIB_ERR;
1337 goto bad;
1338 default:
1339 /* parent */
1340 break;
1341 }
1342
1343 sigunblk(&oset);
1344
1345 /* We have forked successfully - this is the parent */
1346 ret = bus_quiesce(apidp, pt, errstring, flags);
1347
1348 (void) close(fd); /* also unlocks */
1349
1350 wait_for_child(cpid);
1351
1352 (void) mutex_unlock(&quiesce_mutex);
1353
1354 return (ret);
1355 bad:
1356 (void) close(fd);
1357 s_unlink(SCFGA_LOCK);
1358 (void) mutex_unlock(&quiesce_mutex);
1359 return (ret);
1360 }
1361
1362 /*
1363 * Checks if HBA controls a critical file-system (/, /usr or swap)
1364 * This routine reads /etc/vfstab and is NOT foolproof.
1365 * If an error occurs, assumes that controller is NOT critical.
1366 */
1367 static int
critical_ctrlr(const char * hba_phys)1368 critical_ctrlr(const char *hba_phys)
1369 {
1370 FILE *fp;
1371 struct vfstab vfst;
1372 int vfsret = 1, rv = -1;
1373 char *bufp;
1374 const size_t buflen = PATH_MAX;
1375 char mount[MAXPATHLEN], fstype[MAXPATHLEN], spec[MAXPATHLEN];
1376
1377
1378 if ((bufp = calloc(1, buflen)) == NULL) {
1379 return (0);
1380 }
1381
1382 fp = NULL;
1383 if ((fp = fopen(ETC_VFSTAB, "r")) == NULL) {
1384 rv = 0;
1385 goto out;
1386 }
1387
1388 while ((vfsret = getvfsent(fp, &vfst)) == 0) {
1389
1390 (void) strcpy(mount, S_STR(vfst.vfs_mountp));
1391 (void) strcpy(fstype, S_STR(vfst.vfs_fstype));
1392 (void) strcpy(spec, S_STR(vfst.vfs_special));
1393
1394 /* Ignore non-critical entries */
1395 if (strcmp(mount, "/") && strcmp(mount, "/usr") &&
1396 strcmp(fstype, "swap")) {
1397 continue;
1398 }
1399
1400 /* get physical path */
1401 if (realpath(spec, bufp) == NULL) {
1402 continue;
1403 }
1404
1405 /* Check if critical partition is on the HBA */
1406 if (!(rv = hba_dev_cmp(hba_phys, bufp))) {
1407 break;
1408 }
1409 }
1410
1411 rv = !vfsret;
1412
1413 /*FALLTHRU*/
1414 out:
1415 S_FREE(bufp);
1416 if (fp != NULL) {
1417 (void) fclose(fp);
1418 }
1419 return (rv);
1420 }
1421
1422 /*
1423 * Convert bus state to receptacle state
1424 */
1425 static cfga_stat_t
bus_devctl_to_recep_state(uint_t bus_dc_state)1426 bus_devctl_to_recep_state(uint_t bus_dc_state)
1427 {
1428 cfga_stat_t rs;
1429
1430 switch (bus_dc_state) {
1431 case BUS_ACTIVE:
1432 rs = CFGA_STAT_CONNECTED;
1433 break;
1434 case BUS_QUIESCED:
1435 case BUS_SHUTDOWN:
1436 rs = CFGA_STAT_DISCONNECTED;
1437 break;
1438 default:
1439 rs = CFGA_STAT_NONE;
1440 break;
1441 }
1442
1443 return (rs);
1444 }
1445
1446 static int
add_dev(di_node_t node,void * arg)1447 add_dev(di_node_t node, void *arg)
1448 {
1449 int ndevs, len;
1450 char *path, *p;
1451 struct larg *largp = (struct larg *)arg;
1452
1453 /* ignore hba itself and all detached nodes */
1454 if (di_parent_node(node) == DI_NODE_NIL ||
1455 di_node_state(node) < DS_ATTACHED)
1456 return (DI_WALK_CONTINUE);
1457
1458 if ((path = di_devfs_path(node)) == NULL) {
1459 largp->ndevs = -1;
1460 return (DI_WALK_TERMINATE);
1461 }
1462
1463 /* sizeof (DEVICES_DIR) includes the null terminator */
1464 len = strlen(path) + sizeof (DEVICES_DIR);
1465 if ((p = malloc(len)) == NULL) {
1466 di_devfs_path_free(path);
1467 largp->ndevs = -1;
1468 return (DI_WALK_TERMINATE);
1469 }
1470 (void) snprintf(p, len, "%s%s", DEVICES_DIR, path);
1471 di_devfs_path_free(path);
1472
1473 /* ignore device to be excluded */
1474 if (largp->dev && strcmp(largp->dev, p) == 0) {
1475 free(p);
1476 return (DI_WALK_CONTINUE);
1477 }
1478
1479 /* grow dev_list to allow room for one more device */
1480 if (alloc_dev_list(largp) != 0) {
1481 free(p);
1482 return (DI_WALK_TERMINATE);
1483 }
1484 ndevs = largp->ndevs;
1485 largp->ndevs++;
1486 largp->dev_list[ndevs] = p;
1487 largp->dev_list[ndevs + 1] = NULL;
1488 return (DI_WALK_CONTINUE);
1489 }
1490
1491 /*
1492 * Get list of children excluding dev_excl (if not null).
1493 */
1494 static int
get_hba_children(char * bus_path,char * dev_excl,char *** dev_listp)1495 get_hba_children(char *bus_path, char *dev_excl, char ***dev_listp)
1496 {
1497 int err, ret;
1498 walkarg_t u;
1499 struct larg larg;
1500
1501 *dev_listp = NULL;
1502
1503 u.node_args.flags = DI_WALK_CLDFIRST;
1504 u.node_args.fcn = add_dev;
1505
1506 larg.ndevs = 0;
1507 larg.nelem = 0;
1508 larg.dev = dev_excl;
1509 larg.dev_list = NULL;
1510
1511 ret = walk_tree(bus_path, &larg, DINFOSUBTREE, &u, SCFGA_WALK_NODE,
1512 &err);
1513 if (larg.ndevs == -1) {
1514 free_dev_list(larg.dev_list);
1515 return (SCFGA_ERR);
1516 }
1517 *dev_listp = larg.dev_list;
1518 return (ret);
1519 }
1520
1521 static char *
get_node_path(char * minor_path)1522 get_node_path(char *minor_path)
1523 {
1524 char *path, *cp;
1525
1526 if ((path = strdup(minor_path)) == NULL)
1527 return (NULL);
1528 if ((cp = strrchr(path, ':')) != NULL)
1529 *cp = '\0';
1530 return (path);
1531 }
1532
1533 /*
1534 * Ensure largp->dev_list has room for one more device.
1535 * Returns 0 on success, -1 on failure.
1536 */
1537 static int
alloc_dev_list(struct larg * largp)1538 alloc_dev_list(struct larg *largp)
1539 {
1540 int nelem;
1541 char **p;
1542
1543 if (largp->nelem > largp->ndevs + 2) /* +1 for NULL termination */
1544 return (0);
1545
1546 nelem = largp->nelem + 16;
1547 p = realloc(largp->dev_list, nelem * sizeof (char *));
1548 if (p == NULL)
1549 return (-1);
1550
1551 largp->dev_list = p;
1552 largp->nelem = nelem;
1553 return (0);
1554 }
1555
1556 static void
free_dev_list_elements(char ** dev_list)1557 free_dev_list_elements(char **dev_list)
1558 {
1559 while (*dev_list) {
1560 free(*dev_list);
1561 dev_list++;
1562 }
1563 }
1564
1565 static void
free_dev_list(char ** dev_list)1566 free_dev_list(char **dev_list)
1567 {
1568 if (dev_list == NULL)
1569 return;
1570
1571 free_dev_list_elements(dev_list);
1572 free(dev_list);
1573 }
1574