summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/sunmdi.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/sunmdi.c')
-rw-r--r--usr/src/uts/common/os/sunmdi.c286
1 files changed, 286 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/sunmdi.c b/usr/src/uts/common/os/sunmdi.c
index 0c6b1e3055..cec7a252b6 100644
--- a/usr/src/uts/common/os/sunmdi.c
+++ b/usr/src/uts/common/os/sunmdi.c
@@ -4777,6 +4777,292 @@ i_mdi_phci_offline(dev_info_t *dip, uint_t flags)
return (rv);
}
+void
+mdi_phci_mark_retiring(dev_info_t *dip, char **cons_array)
+{
+ mdi_phci_t *ph;
+ mdi_client_t *ct;
+ mdi_pathinfo_t *pip;
+ mdi_pathinfo_t *next;
+ dev_info_t *cdip;
+
+ if (!MDI_PHCI(dip))
+ return;
+
+ ph = i_devi_get_phci(dip);
+ if (ph == NULL) {
+ return;
+ }
+
+ MDI_PHCI_LOCK(ph);
+
+ if (MDI_PHCI_IS_OFFLINE(ph)) {
+ /* has no last path */
+ MDI_PHCI_UNLOCK(ph);
+ return;
+ }
+
+ pip = ph->ph_path_head;
+ while (pip != NULL) {
+ MDI_PI_LOCK(pip);
+ next = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link;
+
+ ct = MDI_PI(pip)->pi_client;
+ i_mdi_client_lock(ct, pip);
+ MDI_PI_UNLOCK(pip);
+
+ cdip = ct->ct_dip;
+ if (cdip && (i_ddi_node_state(cdip) >= DS_INITIALIZED) &&
+ (i_mdi_client_compute_state(ct, ph) ==
+ MDI_CLIENT_STATE_FAILED)) {
+ /* Last path. Mark client dip as retiring */
+ i_mdi_client_unlock(ct);
+ MDI_PHCI_UNLOCK(ph);
+ (void) e_ddi_mark_retiring(cdip, cons_array);
+ MDI_PHCI_LOCK(ph);
+ pip = next;
+ } else {
+ i_mdi_client_unlock(ct);
+ pip = next;
+ }
+ }
+
+ MDI_PHCI_UNLOCK(ph);
+
+ return;
+}
+
+void
+mdi_phci_retire_notify(dev_info_t *dip, int *constraint)
+{
+ mdi_phci_t *ph;
+ mdi_client_t *ct;
+ mdi_pathinfo_t *pip;
+ mdi_pathinfo_t *next;
+ dev_info_t *cdip;
+
+ if (!MDI_PHCI(dip))
+ return;
+
+ ph = i_devi_get_phci(dip);
+ if (ph == NULL)
+ return;
+
+ MDI_PHCI_LOCK(ph);
+
+ if (MDI_PHCI_IS_OFFLINE(ph)) {
+ MDI_PHCI_UNLOCK(ph);
+ /* not last path */
+ return;
+ }
+
+ if (ph->ph_unstable) {
+ MDI_PHCI_UNLOCK(ph);
+ /* can't check for constraints */
+ *constraint = 0;
+ return;
+ }
+
+ pip = ph->ph_path_head;
+ while (pip != NULL) {
+ MDI_PI_LOCK(pip);
+ next = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link;
+
+ /*
+ * The mdi_pathinfo state is OK. Check the client state.
+ * If failover in progress fail the pHCI from offlining
+ */
+ ct = MDI_PI(pip)->pi_client;
+ i_mdi_client_lock(ct, pip);
+ if ((MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) ||
+ (ct->ct_unstable)) {
+ /*
+ * Failover is in progress, can't check for constraints
+ */
+ MDI_PI_UNLOCK(pip);
+ i_mdi_client_unlock(ct);
+ MDI_PHCI_UNLOCK(ph);
+ *constraint = 0;
+ return;
+ }
+ MDI_PI_UNLOCK(pip);
+
+ /*
+ * Check to see of we are retiring the last path of this
+ * client device...
+ */
+ cdip = ct->ct_dip;
+ if (cdip && (i_ddi_node_state(cdip) >= DS_INITIALIZED) &&
+ (i_mdi_client_compute_state(ct, ph) ==
+ MDI_CLIENT_STATE_FAILED)) {
+ i_mdi_client_unlock(ct);
+ MDI_PHCI_UNLOCK(ph);
+ (void) e_ddi_retire_notify(cdip, constraint);
+ MDI_PHCI_LOCK(ph);
+ pip = next;
+ } else {
+ i_mdi_client_unlock(ct);
+ pip = next;
+ }
+ }
+
+ MDI_PHCI_UNLOCK(ph);
+
+ return;
+}
+
+/*
+ * offline the path(s) hanging off the PHCI. If the
+ * last path to any client, check that constraints
+ * have been applied.
+ */
+void
+mdi_phci_retire_finalize(dev_info_t *dip, int phci_only)
+{
+ mdi_phci_t *ph;
+ mdi_client_t *ct;
+ mdi_pathinfo_t *pip;
+ mdi_pathinfo_t *next;
+ dev_info_t *cdip;
+ int unstable = 0;
+ int constraint;
+
+ if (!MDI_PHCI(dip))
+ return;
+
+ ph = i_devi_get_phci(dip);
+ if (ph == NULL) {
+ /* no last path and no pips */
+ return;
+ }
+
+ MDI_PHCI_LOCK(ph);
+
+ if (MDI_PHCI_IS_OFFLINE(ph)) {
+ MDI_PHCI_UNLOCK(ph);
+ /* no last path and no pips */
+ return;
+ }
+
+ /*
+ * Check to see if the pHCI can be offlined
+ */
+ if (ph->ph_unstable) {
+ unstable = 1;
+ }
+
+ pip = ph->ph_path_head;
+ while (pip != NULL) {
+ MDI_PI_LOCK(pip);
+ next = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link;
+
+ /*
+ * if failover in progress fail the pHCI from offlining
+ */
+ ct = MDI_PI(pip)->pi_client;
+ i_mdi_client_lock(ct, pip);
+ if ((MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) ||
+ (ct->ct_unstable)) {
+ unstable = 1;
+ }
+ MDI_PI_UNLOCK(pip);
+
+ /*
+ * Check to see of we are removing the last path of this
+ * client device...
+ */
+ cdip = ct->ct_dip;
+ if (!phci_only && cdip &&
+ (i_ddi_node_state(cdip) >= DS_INITIALIZED) &&
+ (i_mdi_client_compute_state(ct, ph) ==
+ MDI_CLIENT_STATE_FAILED)) {
+ i_mdi_client_unlock(ct);
+ MDI_PHCI_UNLOCK(ph);
+ /*
+ * We don't retire clients we just retire the
+ * path to a client. If it is the last path
+ * to a client, constraints are checked and
+ * if we pass the last path is offlined. MPXIO will
+ * then fail all I/Os to the client. Since we don't
+ * want to retire the client on a path error
+ * set constraint = 0 so that the client dip
+ * is not retired.
+ */
+ constraint = 0;
+ (void) e_ddi_retire_finalize(cdip, &constraint);
+ MDI_PHCI_LOCK(ph);
+ pip = next;
+ } else {
+ i_mdi_client_unlock(ct);
+ pip = next;
+ }
+ }
+
+ /*
+ * Cannot offline pip(s)
+ */
+ if (unstable) {
+ cmn_err(CE_WARN, "PHCI in transient state, cannot "
+ "retire, dip = %p", (void *)dip);
+ MDI_PHCI_UNLOCK(ph);
+ return;
+ }
+
+ /*
+ * Mark the pHCI as offline
+ */
+ MDI_PHCI_SET_OFFLINE(ph);
+
+ /*
+ * Mark the child mdi_pathinfo nodes as transient
+ */
+ pip = ph->ph_path_head;
+ while (pip != NULL) {
+ MDI_PI_LOCK(pip);
+ next = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link;
+ MDI_PI_SET_OFFLINING(pip);
+ MDI_PI_UNLOCK(pip);
+ pip = next;
+ }
+ MDI_PHCI_UNLOCK(ph);
+ /*
+ * Give a chance for any pending commands to execute
+ */
+ delay(1);
+ MDI_PHCI_LOCK(ph);
+ pip = ph->ph_path_head;
+ while (pip != NULL) {
+ next = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link;
+ (void) i_mdi_pi_offline(pip, 0);
+ MDI_PI_LOCK(pip);
+ ct = MDI_PI(pip)->pi_client;
+ if (!MDI_PI_IS_OFFLINE(pip)) {
+ cmn_err(CE_WARN, "PHCI busy, cannot offline path: "
+ "PHCI dip = %p", (void *)dip);
+ MDI_PI_UNLOCK(pip);
+ MDI_PHCI_SET_ONLINE(ph);
+ MDI_PHCI_UNLOCK(ph);
+ return;
+ }
+ MDI_PI_UNLOCK(pip);
+ pip = next;
+ }
+ MDI_PHCI_UNLOCK(ph);
+
+ return;
+}
+
+void
+mdi_phci_unretire(dev_info_t *dip)
+{
+ ASSERT(MDI_PHCI(dip));
+
+ /*
+ * Online the phci
+ */
+ i_mdi_phci_online(dip);
+}
+
/*ARGSUSED*/
static int
i_mdi_client_offline(dev_info_t *dip, uint_t flags)