diff options
-rw-r--r-- | sys/scsi/files.scsi | 15 | ||||
-rw-r--r-- | sys/scsi/mpath.c | 348 | ||||
-rw-r--r-- | sys/scsi/mpath_emc.c | 298 | ||||
-rw-r--r-- | sys/scsi/mpath_rdac.c | 335 | ||||
-rw-r--r-- | sys/scsi/mpath_sym.c | 179 | ||||
-rw-r--r-- | sys/scsi/mpathvar.h | 55 | ||||
-rw-r--r-- | sys/scsi/scsiconf.c | 36 | ||||
-rw-r--r-- | sys/scsi/scsiconf.h | 11 |
8 files changed, 1153 insertions, 124 deletions
diff --git a/sys/scsi/files.scsi b/sys/scsi/files.scsi index 2066b8f9579..e5e5a70ba5a 100644 --- a/sys/scsi/files.scsi +++ b/sys/scsi/files.scsi @@ -1,4 +1,4 @@ -# $OpenBSD: files.scsi,v 1.22 2010/07/03 03:59:17 krw Exp $ +# $OpenBSD: files.scsi,v 1.23 2011/04/05 14:25:42 dlg Exp $ # $NetBSD: files.scsi,v 1.4 1996/05/16 04:01:08 mycroft Exp $ # # Config.new file and device description for machine-independent SCSI code. @@ -39,3 +39,16 @@ file scsi/safte.c safte needs-flag device ses: disk attach ses at scsibus file scsi/ses.c ses needs-flag + + +device sym +attach sym at scsibus +file scsi/mpath_sym.c sym + +device rdac +attach rdac at scsibus +file scsi/mpath_rdac.c rdac + +device emc +attach emc at scsibus +file scsi/mpath_emc.c emc diff --git a/sys/scsi/mpath.c b/sys/scsi/mpath.c index 239b2bb5c09..02389cb518b 100644 --- a/sys/scsi/mpath.c +++ b/sys/scsi/mpath.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mpath.c,v 1.18 2010/07/21 21:34:12 todd Exp $ */ +/* $OpenBSD: mpath.c,v 1.19 2011/04/05 14:25:42 dlg Exp $ */ /* * Copyright (c) 2009 David Gwynne <dlg@openbsd.org> @@ -33,6 +33,7 @@ #include <scsi/scsi_all.h> #include <scsi/scsiconf.h> +#include <scsi/mpathvar.h> #define MPATH_BUSWIDTH 256 @@ -40,27 +41,37 @@ int mpath_match(struct device *, void *, void *); void mpath_attach(struct device *, struct device *, void *); void mpath_shutdown(void *); -struct mpath_path { - struct scsi_link *path_link; - TAILQ_ENTRY(mpath_path) path_entry; -}; TAILQ_HEAD(mpath_paths, mpath_path); -struct mpath_node { - struct devid *node_id; - struct mpath_paths node_paths; +struct mpath_ccb { + struct scsi_xfer *c_xs; + SIMPLEQ_ENTRY(mpath_ccb) c_entry; +}; +SIMPLEQ_HEAD(mpath_ccbs, mpath_ccb); + +struct mpath_dev { + struct mutex d_mtx; + + struct mpath_ccbs d_ccbs; + struct mpath_paths d_paths; + struct mpath_path *d_next_path; + + u_int d_path_count; + + struct devid *d_id; }; struct mpath_softc { struct device sc_dev; struct scsi_link sc_link; + struct pool sc_ccb_pool; + struct scsi_iopool sc_iopool; struct scsibus_softc *sc_scsibus; }; +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) struct mpath_softc *mpath; -struct mpath_node *mpath_nodes[MPATH_BUSWIDTH]; - -#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) +struct mpath_dev *mpath_devs[MPATH_BUSWIDTH]; struct cfattach mpath_ca = { sizeof(struct mpath_softc), @@ -78,17 +89,20 @@ void mpath_cmd(struct scsi_xfer *); void mpath_minphys(struct buf *, struct scsi_link *); int mpath_probe(struct scsi_link *); +struct mpath_path *mpath_next_path(struct mpath_dev *); void mpath_done(struct scsi_xfer *); struct scsi_adapter mpath_switch = { mpath_cmd, scsi_minphys, - mpath_probe, - NULL + mpath_probe }; void mpath_xs_stuffup(struct scsi_xfer *); +void * mpath_ccb_get(void *); +void mpath_ccb_put(void *, void *); + int mpath_match(struct device *parent, void *match, void *aux) { @@ -105,11 +119,19 @@ mpath_attach(struct device *parent, struct device *self, void *aux) printf("\n"); + pool_init(&sc->sc_ccb_pool, sizeof(struct mpath_ccb), 0, 0, 0, + "mpathccb", NULL); + pool_setipl(&sc->sc_ccb_pool, IPL_BIO); + + scsi_iopool_init(&sc->sc_iopool, sc, mpath_ccb_get, mpath_ccb_put); + sc->sc_link.adapter = &mpath_switch; sc->sc_link.adapter_softc = sc; sc->sc_link.adapter_target = MPATH_BUSWIDTH; sc->sc_link.adapter_buswidth = MPATH_BUSWIDTH; - sc->sc_link.openings = 1; + sc->sc_link.luns = 1; + sc->sc_link.openings = 1024; /* XXX magical */ + sc->sc_link.pool = &sc->sc_iopool; bzero(&saa, sizeof(saa)); saa.saa_sc_link = &sc->sc_link; @@ -128,34 +150,119 @@ mpath_xs_stuffup(struct scsi_xfer *xs) int mpath_probe(struct scsi_link *link) { - struct mpath_node *n = mpath_nodes[link->target]; + struct mpath_dev *d = mpath_devs[link->target]; - if (link->lun != 0 || n == NULL) + if (link->lun != 0 || d == NULL) return (ENXIO); - link->id = devid_copy(n->node_id); + link->id = devid_copy(d->d_id); return (0); } +struct mpath_path * +mpath_next_path(struct mpath_dev *d) +{ + struct mpath_path *p; + + if (d == NULL) + panic("%s: d is NULL", __func__); + + p = d->d_next_path; + if (p != NULL) { + d->d_next_path = TAILQ_NEXT(p, p_entry); + if (d->d_next_path == NULL) + d->d_next_path = TAILQ_FIRST(&d->d_paths); + } + + return (p); +} + void mpath_cmd(struct scsi_xfer *xs) { struct scsi_link *link = xs->sc_link; - struct mpath_node *n = mpath_nodes[link->target]; - struct mpath_path *p = TAILQ_FIRST(&n->node_paths); + struct mpath_dev *d = mpath_devs[link->target]; + struct mpath_ccb *ccb = xs->io; + struct mpath_path *p; struct scsi_xfer *mxs; - if (n == NULL || p == NULL) { - mpath_xs_stuffup(xs); +#ifdef DIAGNOSTIC + if (d == NULL) + panic("mpath_cmd issued against nonexistant device"); +#endif + + if (ISSET(xs->flags, SCSI_POLL)) { + mtx_enter(&d->d_mtx); + p = mpath_next_path(d); + mtx_leave(&d->d_mtx); + if (p == NULL) { + mpath_xs_stuffup(xs); + return; + } + + mxs = scsi_xs_get(p->p_link, xs->flags); + if (mxs == NULL) { + mpath_xs_stuffup(xs); + return; + } + + memcpy(mxs->cmd, xs->cmd, xs->cmdlen); + mxs->cmdlen = xs->cmdlen; + mxs->data = xs->data; + mxs->datalen = xs->datalen; + mxs->retries = xs->retries; + mxs->timeout = xs->timeout; + mxs->bp = xs->bp; + + scsi_xs_sync(mxs); + + xs->error = mxs->error; + xs->status = mxs->status; + xs->resid = mxs->resid; + + memcpy(&xs->sense, &mxs->sense, sizeof(xs->sense)); + + scsi_xs_put(mxs); + scsi_done(xs); return; } - mxs = scsi_xs_get(p->path_link, xs->flags); - if (mxs == NULL) { - mpath_xs_stuffup(xs); - return; + ccb->c_xs = xs; + + mtx_enter(&d->d_mtx); + SIMPLEQ_INSERT_TAIL(&d->d_ccbs, ccb, c_entry); + p = mpath_next_path(d); + mtx_leave(&d->d_mtx); + + if (p != NULL) + scsi_xsh_add(&p->p_xsh); +} + +void +mpath_start(struct mpath_path *p, struct scsi_xfer *mxs) +{ + struct mpath_dev *d = p->p_dev; + struct mpath_ccb *ccb; + struct scsi_xfer *xs; + int addxsh = 0; + + if (ISSET(p->p_link->state, SDEV_S_DYING) || d == NULL) + goto fail; + + mtx_enter(&d->d_mtx); + ccb = SIMPLEQ_FIRST(&d->d_ccbs); + if (ccb != NULL) { + SIMPLEQ_REMOVE_HEAD(&d->d_ccbs, c_entry); + if (!SIMPLEQ_EMPTY(&d->d_ccbs)) + addxsh = 1; } + mtx_leave(&d->d_mtx); + + if (ccb == NULL) + goto fail; + + xs = ccb->c_xs; memcpy(mxs->cmd, xs->cmd, xs->cmdlen); mxs->cmdlen = xs->cmdlen; @@ -164,21 +271,46 @@ mpath_cmd(struct scsi_xfer *xs) mxs->retries = xs->retries; mxs->timeout = xs->timeout; mxs->bp = xs->bp; + mxs->flags = xs->flags; mxs->cookie = xs; mxs->done = mpath_done; scsi_xs_exec(mxs); + + if (addxsh) + scsi_xsh_add(&p->p_xsh); + + return; +fail: + scsi_xs_put(mxs); } void mpath_done(struct scsi_xfer *mxs) { struct scsi_xfer *xs = mxs->cookie; + struct scsi_link *link = xs->sc_link; + struct mpath_ccb *ccb = xs->io; + struct mpath_dev *d = mpath_devs[link->target]; + struct mpath_path *p; + + if (mxs->error == XS_RESET) { + mtx_enter(&d->d_mtx); + SIMPLEQ_INSERT_HEAD(&d->d_ccbs, ccb, c_entry); + p = mpath_next_path(d); + mtx_leave(&d->d_mtx); + + scsi_xs_put(mxs); + + if (p != NULL) + scsi_xsh_add(&p->p_xsh); + + return; + } xs->error = mxs->error; xs->status = mxs->status; - xs->flags = mxs->flags; xs->resid = mxs->resid; memcpy(&xs->sense, &mxs->sense, sizeof(xs->sense)); @@ -191,56 +323,74 @@ mpath_done(struct scsi_xfer *mxs) void mpath_minphys(struct buf *bp, struct scsi_link *link) { - struct mpath_node *n = mpath_nodes[link->target]; + struct mpath_dev *d = mpath_devs[link->target]; struct mpath_path *p; - if (n == NULL) - return; +#ifdef DIAGNOSTIC + if (d == NULL) + panic("mpath_minphys against nonexistant device"); +#endif - TAILQ_FOREACH(p, &n->node_paths, path_entry) - p->path_link->adapter->scsi_minphys(bp, p->path_link); + TAILQ_FOREACH(p, &d->d_paths, p_entry) + p->p_link->adapter->scsi_minphys(bp, p->p_link); } int -mpath_path_attach(struct scsi_link *link) +mpath_path_probe(struct scsi_link *link) { - struct mpath_node *n; - struct mpath_path *p; - int probe = 0; - int target; - - if (mpath != NULL && link->adapter_softc == mpath) - return (ENODEV); + if (link->id == NULL) + return (EINVAL); - /* XXX this is dumb. should check inq shizz */ - if (ISSET(link->flags, SDEV_VIRTUAL) || link->id == NULL) + if (mpath != NULL && mpath == link->adapter_softc) return (ENXIO); + return (0); +} + +int +mpath_path_attach(struct mpath_path *p) +{ + struct scsi_link *link = p->p_link; + struct mpath_dev *d = NULL; + int newdev = 0, addxsh = 0; + int target; + +#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"); +#endif + for (target = 0; target < MPATH_BUSWIDTH; target++) { - if ((n = mpath_nodes[target]) == NULL) + if ((d = mpath_devs[target]) == NULL) continue; - if (DEVID_CMP(n->node_id, link->id)) + if (DEVID_CMP(d->d_id, link->id)) break; - n = NULL; + d = NULL; } - if (n == NULL) { + if (d == NULL) { for (target = 0; target < MPATH_BUSWIDTH; target++) { - if (mpath_nodes[target] == NULL) + if (mpath_devs[target] == NULL) break; } if (target >= MPATH_BUSWIDTH) return (ENXIO); - n = malloc(sizeof(*n), M_DEVBUF, M_WAITOK | M_ZERO); - TAILQ_INIT(&n->node_paths); + d = malloc(sizeof(*d), M_DEVBUF, M_WAITOK | M_ZERO); + if (d == NULL) + return (ENOMEM); - n->node_id = devid_copy(link->id); + mtx_init(&d->d_mtx, IPL_BIO); + TAILQ_INIT(&d->d_paths); + SIMPLEQ_INIT(&d->d_ccbs); + d->d_id = devid_copy(link->id); - mpath_nodes[target] = n; - probe = 1; + mpath_devs[target] = d; + newdev = 1; } else { /* * instead of carrying identical values in different devid @@ -248,60 +398,94 @@ mpath_path_attach(struct scsi_link *link) * the new scsi_link. */ devid_free(link->id); - link->id = devid_copy(n->node_id); + link->id = devid_copy(d->d_id); } - p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK); - - p->path_link = link; - TAILQ_INSERT_TAIL(&n->node_paths, p, path_entry); - - if (mpath != NULL && probe) + p->p_dev = d; + mtx_enter(&d->d_mtx); + if (TAILQ_EMPTY(&d->d_paths)) + d->d_next_path = p; + TAILQ_INSERT_TAIL(&d->d_paths, p, p_entry); + d->d_path_count++; + if (!SIMPLEQ_EMPTY(&d->d_ccbs)) + addxsh = 1; + mtx_leave(&d->d_mtx); + + if (newdev && mpath != NULL) scsi_probe_target(mpath->sc_scsibus, target); + else if (addxsh) + scsi_xsh_add(&p->p_xsh); return (0); } int -mpath_path_detach(struct scsi_link *link, int flags) +mpath_path_detach(struct mpath_path *p) { - struct mpath_node *n; - struct mpath_path *p; - int target; + struct mpath_dev *d = p->p_dev; + struct mpath_path *np = NULL; - for (target = 0; target < MPATH_BUSWIDTH; target++) { - if ((n = mpath_nodes[target]) == NULL) - continue; +#ifdef DIAGNOSTIC + if (d == NULL) + panic("mpath: detaching a path from a nonexistant bus"); +#endif + p->p_dev = NULL; - if (DEVID_CMP(n->node_id, link->id)) - break; + mtx_enter(&d->d_mtx); + TAILQ_REMOVE(&d->d_paths, p, p_entry); + if (d->d_next_path == p) + d->d_next_path = TAILQ_FIRST(&d->d_paths); - n = NULL; - } + d->d_path_count--; + if (!SIMPLEQ_EMPTY(&d->d_ccbs)) + np = d->d_next_path; + mtx_leave(&d->d_mtx); - if (n == NULL) - panic("mpath: detaching a path from a nonexistant bus"); + scsi_xsh_del(&p->p_xsh); - TAILQ_FOREACH(p, &n->node_paths, path_entry) { - if (p->path_link == link) { - TAILQ_REMOVE(&n->node_paths, p, path_entry); - free(p, M_DEVBUF); - return (0); - } - } + if (np != NULL) + scsi_xsh_add(&np->p_xsh); - panic("mpath: unable to locate path for detach"); + return (0); } -void -mpath_path_activate(struct scsi_link *link) +void * +mpath_ccb_get(void *cookie) { + struct mpath_softc *sc = cookie; + return (pool_get(&sc->sc_ccb_pool, PR_NOWAIT)); } void -mpath_path_deactivate(struct scsi_link *link) +mpath_ccb_put(void *cookie, void *io) { + struct mpath_softc *sc = cookie; + pool_put(&sc->sc_ccb_pool, io); } +struct device * +mpath_bootdv(struct device *dev) +{ + struct mpath_dev *d; + struct mpath_path *p; + int target; + + if (mpath == NULL) + return (dev); + + for (target = 0; target < MPATH_BUSWIDTH; target++) { + if ((d = mpath_devs[target]) == NULL) + continue; + + TAILQ_FOREACH(p, &d->d_paths, p_entry) { + if (p->p_link->device_softc == dev) { + return (scsi_get_link(mpath->sc_scsibus, + target, 0)->device_softc); + } + } + } + + return (dev); +} diff --git a/sys/scsi/mpath_emc.c b/sys/scsi/mpath_emc.c new file mode 100644 index 00000000000..cb77e0c079b --- /dev/null +++ b/sys/scsi/mpath_emc.c @@ -0,0 +1,298 @@ +/* $OpenBSD: mpath_emc.c,v 1.1 2011/04/05 14:25:42 dlg Exp $ */ + +/* + * Copyright (c) 2011 David Gwynne <dlg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* EMC CLARiiON AX/CX support for mpath(4) */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/queue.h> +#include <sys/rwlock.h> +#include <sys/pool.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/selinfo.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/mpathvar.h> + +#define EMC_VPD_SP_INFO 0xc0 + +struct emc_vpd_sp_info { + struct scsi_vpd_hdr hdr; /* EMC_VPD_SP_INFO */ + + u_int8_t lun_state; +#define EMC_SP_INFO_LUN_STATE_UNBOUND 0x00 +#define EMC_SP_INFO_LUN_STATE_BOUND 0x01 +#define EMC_SP_INFO_LUN_STATE_OWNED 0x02 + u_int8_t default_sp; + u_int8_t _reserved1[1]; + u_int8_t port; + u_int8_t current_sp; + u_int8_t _reserved2[1]; + u_int8_t unique_id[16]; + u_int8_t _reserved3[1]; + u_int8_t type; + u_int8_t failover_mode; + u_int8_t _reserved4[21]; + u_int8_t serial[16]; +} __packed; + +struct emc_softc { + struct device sc_dev; + struct mpath_path sc_path; + u_int sc_flags; + u_int8_t sc_sp; + u_int8_t sc_port; + u_int8_t sc_lun_state; + +}; +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) + +int emc_match(struct device *, void *, void *); +void emc_attach(struct device *, struct device *, void *); +int emc_detach(struct device *, int); +int emc_activate(struct device *, int); + +struct cfattach emc_ca = { + sizeof(struct emc_softc), + emc_match, + emc_attach, + emc_detach, + emc_activate +}; + +struct cfdriver emc_cd = { + NULL, + "emc", + DV_DULL +}; + +void emc_mpath_start(struct scsi_xfer *); +int emc_mpath_checksense(struct scsi_xfer *); +int emc_mpath_online(struct scsi_link *); +int emc_mpath_offline(struct scsi_link *); + +struct mpath_ops emc_mpath_ops = { + "emc", + emc_mpath_start, + emc_mpath_checksense, + emc_mpath_online, + emc_mpath_offline, +}; + +struct emc_device { + char *vendor; + char *product; +}; + +int emc_inquiry(struct emc_softc *, char *, char *); +int emc_sp_info(struct emc_softc *); + +struct emc_device emc_devices[] = { +/* " vendor " " device " */ +/* "01234567" "0123456789012345" */ + { "DGC ", "LUNZ" }, + { "DGC ", "RAID" }, + { "DGC ", "DISK" }, + { "DGC ", "VRAID" } +}; + +int +emc_match(struct device *parent, void *match, void *aux) +{ + struct scsi_attach_args *sa = aux; + struct scsi_inquiry_data *inq = sa->sa_inqbuf; + struct emc_device *s; + int i; + + if (mpath_path_probe(sa->sa_sc_link) != 0) + return (0); + + for (i = 0; i < nitems(emc_devices); i++) { + s = &emc_devices[i]; + + if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && + bcmp(s->product, inq->product, strlen(s->product)) == 0) + return (3); + } + + return (0); +} + +void +emc_attach(struct device *parent, struct device *self, void *aux) +{ + char model[256], serial[256]; + struct emc_softc *sc = (struct emc_softc *)self; + struct scsi_attach_args *sa = aux; + struct scsi_link *link = sa->sa_sc_link; + + printf("\n"); + + /* init link */ + link->device_softc = sc; + + /* init path */ + scsi_xsh_set(&sc->sc_path.p_xsh, link, emc_mpath_start); + sc->sc_path.p_link = link; + sc->sc_path.p_ops = &emc_mpath_ops; + + if (emc_sp_info(sc)) { + printf("%s: unable to get sp info\n", DEVNAME(sc)); + return; + } + + if (emc_inquiry(sc, model, serial) != 0) { + printf("%s: unable to get inquiry data\n", DEVNAME(sc)); + return; + } + + printf("%s: %s %s SP-%c port %d\n", DEVNAME(sc), model, serial, + sc->sc_sp + 'A', sc->sc_port); + + if (sc->sc_lun_state == EMC_SP_INFO_LUN_STATE_OWNED) { + if (mpath_path_attach(&sc->sc_path) != 0) + printf("%s: unable to attach path\n", DEVNAME(sc)); + } +} + +int +emc_detach(struct device *self, int flags) +{ + return (0); +} + +int +emc_activate(struct device *self, int act) +{ + struct emc_softc *sc = (struct emc_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + case DVACT_SUSPEND: + case DVACT_RESUME: + break; + case DVACT_DEACTIVATE: + if (sc->sc_path.p_dev != NULL) + mpath_path_detach(&sc->sc_path); + break; + } + return (rv); +} + +void +emc_mpath_start(struct scsi_xfer *xs) +{ + struct emc_softc *sc = xs->sc_link->device_softc; + + mpath_start(&sc->sc_path, xs); +} + +int +emc_mpath_checksense(struct scsi_xfer *xs) +{ + return (0); +} + +int +emc_mpath_online(struct scsi_link *link) +{ + return (0); +} + +int +emc_mpath_offline(struct scsi_link *link) +{ + return (0); +} + +int +emc_inquiry(struct emc_softc *sc, char *model, char *serial) +{ + u_int8_t buffer[255]; + struct scsi_inquiry *cdb; + struct scsi_xfer *xs; + size_t length; + int error; + u_int8_t slen, mlen; + + length = MIN(sc->sc_path.p_link->inqdata.additional_length + 5, + sizeof(buffer)); + if (length < 160) { + printf("%s: FC (Legacy)\n"); + return (0); + } + + xs = scsi_xs_get(sc->sc_path.p_link, scsi_autoconf); + if (xs == NULL) + return (EBUSY); + + cdb = (struct scsi_inquiry *)xs->cmd; + cdb->opcode = INQUIRY; + _lto2b(length, cdb->length); + + xs->cmdlen = sizeof(*cdb); + xs->flags |= SCSI_DATA_IN; + xs->data = buffer; + xs->datalen = length; + + error = scsi_xs_sync(xs); + scsi_xs_put(xs); + + if (error != 0) + return (error); + + slen = buffer[160]; + if (slen == 0 || slen + 161 > length) + return (EIO); + + mlen = buffer[99]; + if (mlen == 0 || slen + mlen + 161 > length) + return (EIO); + + scsi_strvis(serial, buffer + 161, slen); + scsi_strvis(model, buffer + 161 + slen, mlen); + + return (0); +} + +int +emc_sp_info(struct emc_softc *sc) +{ + struct emc_vpd_sp_info pg; + int error; + + error = scsi_inquire_vpd(sc->sc_path.p_link, &pg, sizeof(pg), + EMC_VPD_SP_INFO, scsi_autoconf); + if (error != 0) + return (error); + + sc->sc_sp = pg.current_sp; + sc->sc_port = pg.port; + sc->sc_lun_state = pg.lun_state; + + return (0); +} diff --git a/sys/scsi/mpath_rdac.c b/sys/scsi/mpath_rdac.c new file mode 100644 index 00000000000..493912ead7e --- /dev/null +++ b/sys/scsi/mpath_rdac.c @@ -0,0 +1,335 @@ +/* $OpenBSD: mpath_rdac.c,v 1.1 2011/04/05 14:25:42 dlg Exp $ */ + +/* + * Copyright (c) 2010 David Gwynne <dlg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Redundant Disk Array Controller support for mpath(4) */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/queue.h> +#include <sys/rwlock.h> +#include <sys/pool.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/selinfo.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/mpathvar.h> + +struct rdac_common_mode_page { + u_int8_t controller_serial[16]; + u_int8_t alt_controller_serial[16]; + u_int8_t mode[2]; + u_int8_t alt_mode[2]; + u_int8_t timeout; + u_int8_t options; +}; + +/* + * RDAC VPD pages + */ +#define RDAC_VPD_HDWVER 0xc0 /* Hardware Version */ +#define RDAC_VPD_SERNUM 0xc1 /* Serial Numbers */ +#define RDAC_VPD_SFWVER 0xc2 +#define RDAC_VPD_FEAPAR 0xc3 /* Feature Parameters */ +#define RDAC_VPD_SUBSYS 0xc4 +#define RDAC_VPD_HSTINT 0xc5 +#define RDAC_VPD_DGM 0xc6 +#define RDAC_VPD_HSTINT2 0xc7 +#define RDAC_VPD_EXTDEVID 0xc8 +#define RDAC_VPD_VOLACCESSCTL 0xc9 + +struct rdac_vpd_hdwver { + struct scsi_vpd_hdr hdr; /* RDAC_VPD_HDWVER */ + u_int8_t pg_id[4]; +#define RDAC_VPD_ID_HDWVER 0x68777234 /* "hwr4" */ + u_int8_t num_channels; + u_int8_t flags; + u_int8_t proc_memory_size; + u_int8_t _reserved1[5]; + u_int8_t board_name[64]; + u_int8_t board_part_number[16]; + u_int8_t schematic_number[12]; + u_int8_t schematic_revision[4]; + u_int8_t serial_number[16]; + u_int8_t _reserved2[16]; + u_int8_t date_manufactured[8]; + u_int8_t board_revision[2]; + u_int8_t board_identifier[4]; +}; + +struct rdac_vpd_subsys { + struct scsi_vpd_hdr hdr; /* RDAC_VPD_SUBSYS */ + u_int8_t pg_id[4]; +#define RDAC_VPD_ID_SUBSYS 0x73756273 /* "subs" */ + u_int8_t subsystem_id[16]; + u_int8_t subsystem_revision[4]; + u_int8_t controller_slot_id[2]; + u_int8_t _reserved[2]; +}; + +struct rdac_vpd_extdevid { + struct scsi_vpd_hdr hdr; /* RDAC_VPD_EXTDEVID */ + u_int8_t pg_id[4]; +#define RDAC_VPD_ID_EXTDEVID 0x65646964 /* "edid" */ + u_int8_t _reserved[3]; + u_int8_t vol_id_len; + u_int8_t vol_id[16]; + u_int8_t vol_label_len; + u_int8_t vol_label[60]; + u_int8_t array_id_len; + u_int8_t array_id[16]; + u_int8_t array_label_len; + u_int8_t array_label[60]; + u_int8_t lun[8]; +}; + +struct rdac_vpd_volaccessctl { + struct scsi_vpd_hdr hdr; /* RDAC_VPD_VOLACCESSCTL */ + u_int8_t pg_id[4]; +#define RDAC_VPD_ID_VOLACCESSCTL 0x76616331 /* "vac1" */ + u_int8_t avtcvp; +#define RDAC_VOLACCESSCTL_OWNER 0x01 +#define RDAC_VOLACCESSCTL_AVT 0x70 + u_int8_t path_priority; + u_int8_t _reserved[38]; +}; + +struct rdac_softc { + struct device sc_dev; + struct mpath_path sc_path; +}; +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) + +int rdac_match(struct device *, void *, void *); +void rdac_attach(struct device *, struct device *, void *); +int rdac_detach(struct device *, int); +int rdac_activate(struct device *, int); + +struct cfattach rdac_ca = { + sizeof(struct rdac_softc), + rdac_match, + rdac_attach, + rdac_detach, + rdac_activate +}; + +struct cfdriver rdac_cd = { + NULL, + "rdac", + DV_DULL +}; + +void rdac_mpath_start(struct scsi_xfer *); +int rdac_mpath_checksense(struct scsi_xfer *); +int rdac_mpath_online(struct scsi_link *); +int rdac_mpath_offline(struct scsi_link *); + +struct mpath_ops rdac_mpath_ops = { + "rdac", + rdac_mpath_start, + rdac_mpath_checksense, + rdac_mpath_online, + rdac_mpath_offline, +}; + +int rdac_c8(struct rdac_softc *); +int rdac_c9(struct rdac_softc *); + +struct rdac_device { + char *vendor; + char *product; +}; + +struct rdac_device rdac_devices[] = { +/* " vendor " " device " */ +/* "01234567" "0123456789012345" */ + { "SUN ", "CSM200_" }, + { "DELL ", "MD3000i " } +}; + +int +rdac_match(struct device *parent, void *match, void *aux) +{ + struct scsi_attach_args *sa = aux; + struct scsi_inquiry_data *inq = sa->sa_inqbuf; + struct rdac_device *s; + int i; + + if (mpath_path_probe(sa->sa_sc_link) != 0) + return (0); + + for (i = 0; i < nitems(rdac_devices); i++) { + s = &rdac_devices[i]; + + if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && + bcmp(s->product, inq->product, strlen(s->product)) == 0) + return (3); + } + + return (0); +} + +void +rdac_attach(struct device *parent, struct device *self, void *aux) +{ + struct rdac_softc *sc = (struct rdac_softc *)self; + struct scsi_attach_args *sa = aux; + struct scsi_link *link = sa->sa_sc_link; + + printf("\n"); + + /* init link */ + link->device_softc = sc; + + /* init path */ + scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start); + sc->sc_path.p_link = link; + sc->sc_path.p_ops = &rdac_mpath_ops; + + if (rdac_c8(sc) != 0) + return; + + if (rdac_c9(sc) != 0) + return; + + if (mpath_path_attach(&sc->sc_path) != 0) + printf("%s: unable to attach path\n", DEVNAME(sc)); +} + +int +rdac_detach(struct device *self, int flags) +{ + return (0); +} + +int +rdac_activate(struct device *self, int act) +{ + struct rdac_softc *sc = (struct rdac_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + case DVACT_SUSPEND: + case DVACT_RESUME: + break; + case DVACT_DEACTIVATE: + if (sc->sc_path.p_dev != NULL) + mpath_path_detach(&sc->sc_path); + break; + } + return (rv); + +} + +void +rdac_mpath_start(struct scsi_xfer *xs) +{ + struct rdac_softc *sc = xs->sc_link->device_softc; + + mpath_start(&sc->sc_path, xs); +} + +int +rdac_mpath_checksense(struct scsi_xfer *xs) +{ + return (0); +} + +int +rdac_mpath_online(struct scsi_link *link) +{ + return (0); +} + +int +rdac_mpath_offline(struct scsi_link *link) +{ + return (0); +} + +int +rdac_c8(struct rdac_softc *sc) +{ + struct rdac_vpd_extdevid pg; + char array[31]; + char vol[31]; + int i; + + if (scsi_inquire_vpd(sc->sc_path.p_link, &pg, sizeof(pg), 0xc8, + scsi_autoconf) != 0) { + printf("%s: unable to fetch vpd page c8\n", DEVNAME(sc)); + return (1); + } + + if (_4btol(pg.pg_id) != RDAC_VPD_ID_EXTDEVID) { + printf("%s: extended hardware id page is invalid\n", + DEVNAME(sc)); + return (1); + } + + memset(array, 0, sizeof(array)); + for (i = 0; i < sizeof(pg.array_label) / 2; i++) + array[i] = pg.array_label[i * 2 + 1]; + + memset(vol, 0, sizeof(vol)); + for (i = 0; i < sizeof(pg.vol_label) / 2; i++) + vol[i] = pg.vol_label[i * 2 + 1]; + + printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol); + + return (0); +} + +int +rdac_c9(struct rdac_softc *sc) +{ + struct rdac_vpd_volaccessctl pg; + + if (scsi_inquire_vpd(sc->sc_path.p_link, &pg, sizeof(pg), + RDAC_VPD_VOLACCESSCTL, scsi_autoconf) != 0) { + printf("%s: unable to fetch vpd page c9\n", DEVNAME(sc)); + return (1); + } + + if (_4btol(pg.pg_id) != RDAC_VPD_ID_VOLACCESSCTL) { + printf("%s: volume access control page id is invalid\n", + DEVNAME(sc)); + return (1); + } + + if (ISSET(pg.avtcvp, RDAC_VOLACCESSCTL_AVT)) { + printf("%s: avt\n", DEVNAME(sc)); + return (0); + } + if (ISSET(pg.avtcvp, RDAC_VOLACCESSCTL_OWNER)) { + printf("%s: owner\n", DEVNAME(sc)); + return (0); + } + + printf("%s: unowned\n", DEVNAME(sc)); + return (1); +} + diff --git a/sys/scsi/mpath_sym.c b/sys/scsi/mpath_sym.c new file mode 100644 index 00000000000..8eb5ecaefdf --- /dev/null +++ b/sys/scsi/mpath_sym.c @@ -0,0 +1,179 @@ +/* $OpenBSD: mpath_sym.c,v 1.1 2011/04/05 14:25:42 dlg Exp $ */ + +/* + * Copyright (c) 2010 David Gwynne <dlg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/queue.h> +#include <sys/rwlock.h> +#include <sys/pool.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/selinfo.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/mpathvar.h> + +struct sym_softc { + struct device sc_dev; + struct mpath_path sc_path; +}; +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) + +int sym_match(struct device *, void *, void *); +void sym_attach(struct device *, struct device *, void *); +int sym_detach(struct device *, int); +int sym_activate(struct device *, int); + +struct cfattach sym_ca = { + sizeof(struct sym_softc), + sym_match, + sym_attach, + sym_detach, + sym_activate +}; + +struct cfdriver sym_cd = { + NULL, + "sym", + DV_DULL +}; + +void sym_mpath_start(struct scsi_xfer *); +int sym_mpath_checksense(struct scsi_xfer *); +int sym_mpath_online(struct scsi_link *); +int sym_mpath_offline(struct scsi_link *); + +struct mpath_ops sym_mpath_ops = { + "sym", + sym_mpath_start, + sym_mpath_checksense, + sym_mpath_online, + sym_mpath_offline +}; + +struct sym_device { + char *vendor; + char *product; +}; + +struct sym_device sym_devices[] = { +/* " vendor " " device " */ +/* "01234567" "0123456789012345" */ + { "SEAGATE ", "ST" } +}; + +int +sym_match(struct device *parent, void *match, void *aux) +{ + struct scsi_attach_args *sa = aux; + struct scsi_inquiry_data *inq = sa->sa_inqbuf; + struct sym_device *s; + int i; + + if (mpath_path_probe(sa->sa_sc_link) != 0) + return (0); + + for (i = 0; i < nitems(sym_devices); i++) { + s = &sym_devices[i]; + + if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && + bcmp(s->product, inq->product, strlen(s->product)) == 0) + return (3); + } + + return (0); +} + +void +sym_attach(struct device *parent, struct device *self, void *aux) +{ + struct sym_softc *sc = (struct sym_softc *)self; + struct scsi_attach_args *sa = aux; + struct scsi_link *link = sa->sa_sc_link; + + printf("\n"); + + /* init link */ + link->device_softc = sc; + + /* init path */ + scsi_xsh_set(&sc->sc_path.p_xsh, link, sym_mpath_start); + sc->sc_path.p_link = link; + sc->sc_path.p_ops = &sym_mpath_ops; + + if (mpath_path_attach(&sc->sc_path) != 0) + printf("%s: unable to attach path\n", DEVNAME(sc)); +} + +int +sym_detach(struct device *self, int flags) +{ + return (0); +} + +int +sym_activate(struct device *self, int act) +{ + struct sym_softc *sc = (struct sym_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + case DVACT_SUSPEND: + case DVACT_RESUME: + break; + case DVACT_DEACTIVATE: + if (sc->sc_path.p_dev != NULL) + mpath_path_detach(&sc->sc_path); + break; + } + return (rv); +} + +void +sym_mpath_start(struct scsi_xfer *xs) +{ + struct sym_softc *sc = xs->sc_link->device_softc; + + mpath_start(&sc->sc_path, xs); +} + +int +sym_mpath_checksense(struct scsi_xfer *xs) +{ + return (0); +} + +int +sym_mpath_online(struct scsi_link *link) +{ + return (0); +} + +int +sym_mpath_offline(struct scsi_link *link) +{ + return (0); +} diff --git a/sys/scsi/mpathvar.h b/sys/scsi/mpathvar.h new file mode 100644 index 00000000000..8a1357a500b --- /dev/null +++ b/sys/scsi/mpathvar.h @@ -0,0 +1,55 @@ +/* $OpenBSD: mpathvar.h,v 1.1 2011/04/05 14:25:42 dlg Exp $ */ + +/* + * Copyright (c) 2010 David Gwynne <dlg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SYS_SCSI_MPATH_H_ +#define _SYS_SCSI_MPATH_H_ + +struct mpath_dev; +struct mpath_group; + +struct mpath_ops { + char op_name[16]; + void (*op_start)(struct scsi_xfer *); + int (*op_checksense)(struct scsi_xfer *); + int (*op_online)(struct scsi_link *); + int (*op_offline)(struct scsi_link *); +}; + +struct mpath_path { + /* the path driver must set these */ + struct scsi_xshandler p_xsh; + struct scsi_link *p_link; + struct mpath_ops *p_ops; + int p_gid; + + /* the follwoing are private to mpath.c */ + TAILQ_ENTRY(mpath_path) p_entry; + struct mpath_dev *p_dev; + int p_state; +}; + +int mpath_path_probe(struct scsi_link *); +int mpath_path_attach(struct mpath_path *); +void mpath_path_state(struct mpath_path *, int); +int mpath_path_detach(struct mpath_path *); + +void mpath_start(struct mpath_path *, struct scsi_xfer *); + +struct device *mpath_bootdv(struct device *); + +#endif /* _SYS_SCSI_MPATH_H_ */ diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c index 9fa6900e761..778c42a701b 100644 --- a/sys/scsi/scsiconf.c +++ b/sys/scsi/scsiconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scsiconf.c,v 1.169 2011/03/31 18:42:48 jasper Exp $ */ +/* $OpenBSD: scsiconf.c,v 1.170 2011/04/05 14:25:42 dlg Exp $ */ /* $NetBSD: scsiconf.c,v 1.57 1996/05/02 01:09:01 neil Exp $ */ /* @@ -61,6 +61,7 @@ #include <scsi/scsi_all.h> #include <scsi/scsiconf.h> +#include <scsi/mpathvar.h> #if NBIO > 0 #include <sys/ioctl.h> @@ -248,12 +249,7 @@ scsi_activate_lun(struct scsibus_softc *sc, int target, int lun, int act) switch (act) { case DVACT_ACTIVATE: atomic_clearbits_int(&link->state, SDEV_S_DYING); -#if NMPATH > 0 - if (dev == NULL) - mpath_path_activate(link); - else -#endif /* NMPATH */ - config_activate(dev); + config_activate(dev); break; case DVACT_QUIESCE: case DVACT_SUSPEND: @@ -262,12 +258,7 @@ scsi_activate_lun(struct scsibus_softc *sc, int target, int lun, int act) break; case DVACT_DEACTIVATE: atomic_setbits_int(&link->state, SDEV_S_DYING); -#if NMPATH > 0 - if (dev == NULL) - mpath_path_deactivate(link); - else -#endif /* NMPATH */ - config_deactivate(dev); + config_deactivate(dev); break; default: break; @@ -502,12 +493,7 @@ scsi_detach_lun(struct scsibus_softc *sc, int target, int lun, int flags) scsi_link_shutdown(link); /* 2. detach the device */ -#if NMPATH > 0 - if (link->device_softc == NULL) - rv = mpath_path_detach(link, flags); - else -#endif /* NMPATH */ - rv = config_detach(link->device_softc, flags); + rv = config_detach(link->device_softc, flags); if (rv != 0) return (rv); @@ -970,18 +956,6 @@ scsi_probedev(struct scsibus_softc *scsi, int target, int lun) goto free_devid; } -#if NMPATH > 0 - /* should multipathing steal the link? */ - if (mpath_path_attach(sc_link) == 0) { - printf("%s: path to", scsi->sc_dev.dv_xname); - scsibus_printlink(sc_link); - printf("\n"); - - scsi_add_link(scsi, sc_link); - return (0); - } -#endif /* NMPATH */ - finger = (const struct scsi_quirk_inquiry_pattern *)scsi_inqmatch( inqbuf, scsi_quirk_patterns, nitems(scsi_quirk_patterns), diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h index 73a67f13c19..e9a5f80e8b5 100644 --- a/sys/scsi/scsiconf.h +++ b/sys/scsi/scsiconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: scsiconf.h,v 1.142 2010/12/24 02:45:33 krw Exp $ */ +/* $OpenBSD: scsiconf.h,v 1.143 2011/04/05 14:25:42 dlg Exp $ */ /* $NetBSD: scsiconf.h,v 1.35 1997/04/02 02:29:38 mycroft Exp $ */ /* @@ -627,15 +627,6 @@ void scsi_xsh_add(struct scsi_xshandler *); void scsi_xsh_del(struct scsi_xshandler *); /* - * Entrypoints for multipathing - */ -int mpath_path_attach(struct scsi_link *); -int mpath_path_detach(struct scsi_link *, int); - -void mpath_path_activate(struct scsi_link *); -void mpath_path_deactivate(struct scsi_link *); - -/* * Utility functions for SCSI HBA emulation. */ void scsi_cmd_rw_decode(struct scsi_generic *, u_int64_t *, u_int32_t *); |