summaryrefslogtreecommitdiff
path: root/sys/scsi/mpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/scsi/mpath.c')
-rw-r--r--sys/scsi/mpath.c140
1 files changed, 112 insertions, 28 deletions
diff --git a/sys/scsi/mpath.c b/sys/scsi/mpath.c
index ca0d8546d98..c3aef7a19f3 100644
--- a/sys/scsi/mpath.c
+++ b/sys/scsi/mpath.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mpath.c,v 1.30 2013/08/26 10:13:17 dlg Exp $ */
+/* $OpenBSD: mpath.c,v 1.31 2013/08/26 12:20:12 dlg Exp $ */
/*
* Copyright (c) 2009 David Gwynne <dlg@openbsd.org>
@@ -45,6 +45,7 @@ TAILQ_HEAD(mpath_paths, mpath_path);
struct mpath_group {
TAILQ_ENTRY(mpath_group) g_entry;
struct mpath_paths g_paths;
+ struct mpath_dev *g_dev;
u_int g_id;
};
TAILQ_HEAD(mpath_groups, mpath_group);
@@ -53,9 +54,14 @@ struct mpath_dev {
struct mutex d_mtx;
struct scsi_xfer_list d_xfers;
- struct mpath_groups d_groups;
struct mpath_path *d_next_path;
+ struct mpath_groups d_groups;
+
+ struct mpath_group *d_failover_iter;
+ struct timeout d_failover_tmo;
+ u_int d_failover;
+
const struct mpath_ops *d_ops;
struct devid *d_id;
};
@@ -89,6 +95,10 @@ int mpath_probe(struct scsi_link *);
struct mpath_path *mpath_next_path(struct mpath_dev *, int);
void mpath_done(struct scsi_xfer *);
+void mpath_failover(struct mpath_dev *);
+void mpath_failover_start(void *);
+void mpath_failover_check(struct mpath_dev *);
+
struct scsi_adapter mpath_switch = {
mpath_cmd,
scsi_minphys,
@@ -231,7 +241,7 @@ mpath_cmd(struct scsi_xfer *xs)
void
mpath_start(struct mpath_path *p, struct scsi_xfer *mxs)
{
- struct mpath_dev *d = p->p_dev;
+ struct mpath_dev *d = p->p_group->g_dev;
struct scsi_xfer *xs;
int addxsh = 0;
@@ -295,8 +305,27 @@ mpath_done(struct scsi_xfer *mxs)
if (p != NULL)
scsi_xsh_add(&p->p_xsh);
-
return;
+ case XS_SENSE:
+ switch (d->d_ops->op_checksense(mxs)) {
+ case MPATH_SENSE_FAILOVER:
+ mtx_enter(&d->d_mtx);
+ SIMPLEQ_INSERT_HEAD(&d->d_xfers, xs, xfer_list);
+ p = mpath_next_path(d, next);
+ mtx_leave(&d->d_mtx);
+
+ scsi_xs_put(mxs);
+
+ mpath_failover(d);
+ return;
+ case MPATH_SENSE_DECLINED:
+ break;
+#ifdef DIAGNOSTIC
+ default:
+ panic("unexpected return from checksense");
+#endif
+ }
+ break;
}
xs->error = mxs->error;
@@ -311,6 +340,64 @@ mpath_done(struct scsi_xfer *mxs)
}
void
+mpath_failover(struct mpath_dev *d)
+{
+ if (!scsi_sem_enter(&d->d_mtx, &d->d_failover))
+ return;
+
+ mpath_failover_start(d);
+}
+
+void
+mpath_failover_start(void *xd)
+{
+ struct mpath_dev *d = xd;
+
+ mtx_enter(&d->d_mtx);
+ d->d_failover_iter = TAILQ_FIRST(&d->d_groups);
+ mtx_leave(&d->d_mtx);
+
+ mpath_failover_check(d);
+}
+
+void
+mpath_failover_check(struct mpath_dev *d)
+{
+ struct mpath_group *g = d->d_failover_iter;
+ struct mpath_path *p;
+
+ if (g == NULL)
+ timeout_add_sec(&d->d_failover_tmo, 1);
+ else {
+ p = TAILQ_FIRST(&g->g_paths);
+ d->d_ops->op_status(p->p_link);
+ }
+}
+
+void
+mpath_path_status(struct mpath_path *p, int status)
+{
+ struct mpath_group *g = p->p_group;
+ struct mpath_dev *d = g->g_dev;
+
+ mtx_enter(&d->d_mtx);
+ if (status == MPATH_S_ACTIVE) {
+ TAILQ_REMOVE(&d->d_groups, g, g_entry);
+ TAILQ_INSERT_HEAD(&d->d_groups, g, g_entry);
+ d->d_next_path = p;
+ } else
+ d->d_failover_iter = TAILQ_NEXT(d->d_failover_iter, g_entry);
+ mtx_leave(&d->d_mtx);
+
+ if (status == MPATH_S_ACTIVE) {
+ scsi_xsh_add(&p->p_xsh);
+ if (!scsi_sem_leave(&d->d_mtx, &d->d_failover))
+ mpath_failover_start(d);
+ } else
+ mpath_failover_check(d);
+}
+
+void
mpath_minphys(struct buf *bp, struct scsi_link *link)
{
struct mpath_softc *sc = link->adapter_softc;
@@ -361,8 +448,8 @@ mpath_path_attach(struct mpath_path *p, u_int g_id, const struct mpath_ops *ops)
#ifdef DIAGNOSTIC
if (p->p_link == NULL)
panic("mpath_path_attach: NULL link");
- if (p->p_dev != NULL)
- panic("mpath_path_attach: dev is not NULL");
+ if (p->p_group != NULL)
+ panic("mpath_path_attach: group is not NULL");
#endif
for (target = 0; target < MPATH_BUSWIDTH; target++) {
@@ -393,6 +480,8 @@ mpath_path_attach(struct mpath_path *p, u_int g_id, const struct mpath_ops *ops)
d->d_id = devid_copy(link->id);
d->d_ops = ops;
+ timeout_set(&d->d_failover_tmo, mpath_failover_start, d);
+
sc->sc_devs[target] = d;
newdev = 1;
} else {
@@ -423,6 +512,7 @@ mpath_path_attach(struct mpath_path *p, u_int g_id, const struct mpath_ops *ops)
}
TAILQ_INIT(&g->g_paths);
+ g->g_dev = d;
g->g_id = g_id;
mtx_enter(&d->d_mtx);
@@ -430,18 +520,15 @@ mpath_path_attach(struct mpath_path *p, u_int g_id, const struct mpath_ops *ops)
mtx_leave(&d->d_mtx);
}
- p->p_dev = d;
+ p->p_group = g;
mtx_enter(&d->d_mtx);
-
- if (d->d_next_path == NULL)
- d->d_next_path = p;
-
TAILQ_INSERT_TAIL(&g->g_paths, p, p_entry);
-
if (!SIMPLEQ_EMPTY(&d->d_xfers))
addxsh = 1;
+ if (d->d_next_path == NULL)
+ d->d_next_path = p;
mtx_leave(&d->d_mtx);
if (newdev)
@@ -455,46 +542,43 @@ mpath_path_attach(struct mpath_path *p, u_int g_id, const struct mpath_ops *ops)
int
mpath_path_detach(struct mpath_path *p)
{
- struct mpath_dev *d = p->p_dev;
- struct mpath_group *g;
+ struct mpath_group *g = p->p_group;
+ struct mpath_dev *d = g->g_dev;
struct mpath_path *np = NULL;
#ifdef DIAGNOSTIC
- if (d == NULL)
+ if (g == NULL)
panic("mpath: detaching a path from a nonexistant bus");
#endif
- p->p_dev = NULL;
+ p->p_group = NULL;
mtx_enter(&d->d_mtx);
- g = TAILQ_FIRST(&d->d_groups);
-
TAILQ_REMOVE(&g->g_paths, p, p_entry);
if (d->d_next_path == p)
d->d_next_path = TAILQ_FIRST(&g->g_paths);
- if (TAILQ_EMPTY(&g->g_paths)) {
+ if (TAILQ_EMPTY(&g->g_paths))
TAILQ_REMOVE(&d->d_groups, g, g_entry);
- free(g, M_DEVBUF);
- }
+ else
+ g = NULL;
if (!SIMPLEQ_EMPTY(&d->d_xfers))
np = d->d_next_path;
mtx_leave(&d->d_mtx);
+ if (g != NULL)
+ free(g, M_DEVBUF);
+
scsi_xsh_del(&p->p_xsh);
- if (np != NULL)
+ if (np == NULL)
+ mpath_failover(d);
+ else
scsi_xsh_add(&np->p_xsh);
return (0);
}
-void
-mpath_path_status(struct mpath_path *p, int status)
-{
-
-}
-
struct device *
mpath_bootdv(struct device *dev)
{