this is based on the solaris code. its a lot less arbitrary than
the stupid linux code.
if this works i'll add back the printing of the physical path, it can be useful
when debugging wiring issues.
cheers,
dlg
Index: mpath_hds.c
===================================================================
RCS file: /cvs/src/sys/scsi/mpath_hds.c,v
retrieving revision 1.2
diff -u -p -r1.2 mpath_hds.c
--- mpath_hds.c 28 Apr 2011 10:43:36 -0000 1.2
+++ mpath_hds.c 14 Jun 2011 05:34:00 -0000
@@ -37,6 +37,17 @@
#include <scsi/scsiconf.h>
#include <scsi/mpathvar.h>
+#define HDS_VPD 0xe0
+
+struct hds_vpd {
+ struct scsi_vpd_hdr hdr; /* HDS_VPD */
+ u_int8_t state;
+#define HDS_VPD_VALID 0x80
+#define HDS_VPD_PREFERRED 0x40
+
+ /* followed by lots of unknown stuff */
+};
+
struct hds_softc {
struct device sc_dev;
struct mpath_path sc_path;
@@ -80,7 +91,7 @@ struct hds_device {
char *product;
};
-int hds_priority(struct hds_softc *);
+int hds_preferred(struct scsi_link *, int *);
struct hds_device hds_devices[] = {
/* " vendor " " device " */
@@ -94,8 +105,9 @@ hds_match(struct device *parent, void *m
{
struct scsi_attach_args *sa = aux;
struct scsi_inquiry_data *inq = sa->sa_inqbuf;
+ struct scsi_link *link = sa->sa_sc_link;
struct hds_device *s;
- int i;
+ int i, preferred;
if (mpath_path_probe(sa->sa_sc_link) != 0)
return (0);
@@ -104,8 +116,11 @@ hds_match(struct device *parent, void *m
s = &hds_devices[i];
if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 &&
- bcmp(s->product, inq->product, strlen(s->product)) == 0)
- return (3);
+ bcmp(s->product, inq->product, strlen(s->product)) == 0 &&
+ hds_preferred(link, &preferred) == 0) {
+ /* match above sym(4) */
+ return (4);
+ }
}
return (0);
@@ -128,7 +143,7 @@ hds_attach(struct device *parent, struct
sc->sc_path.p_link = link;
sc->sc_path.p_ops = &hds_mpath_ops;
- if (hds_priority(sc) != 0)
+ if (hds_preferred(link, &sc->sc_active) != 0)
return;
if (!sc->sc_active)
@@ -190,68 +205,26 @@ hds_mpath_offline(struct scsi_link *link
}
int
-hds_priority(struct hds_softc *sc)
+hds_preferred(struct scsi_link *link, int *preferred)
{
- u_int8_t *buffer;
- struct scsi_inquiry *cdb;
- struct scsi_xfer *xs;
- size_t length;
- u_int8_t ldev[9];
- u_int8_t ctrl;
- u_int8_t port;
- int p, c;
+ struct hds_vpd *pg;
int error;
- length = MIN(sc->sc_path.p_link->inqdata.additional_length + 5, 255);
- if (length < 51)
- return (EIO);
-
- buffer = dma_alloc(length, PR_WAITOK);
-
- xs = scsi_xs_get(sc->sc_path.p_link, scsi_autoconf);
- if (xs == NULL) {
- error = EBUSY;
- goto done;
- }
-
- 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;
+ pg = dma_alloc(sizeof(*pg), PR_WAITOK);
- error = scsi_xs_sync(xs);
- scsi_xs_put(xs);
-
- if (error != 0)
+ error = scsi_inquire_vpd(link, pg, sizeof(*pg), HDS_VPD, scsi_autoconf);
+ if (error)
goto done;
- /* XXX magical */
- bzero(ldev, sizeof(ldev));
- scsi_strvis(ldev, buffer + 44, 4);
- ctrl = buffer[49];
- port = buffer[50];
-
- if (strlen(ldev) > 4 || ldev[3] < '0' || ldev[3] > 'F' ||
- ctrl < '0' || ctrl > '9' ||
- port < 'A' || port > 'B') {
- error = EIO;
+ if (_2btol(pg->hdr.page_length) < sizeof(pg->state) ||
+ !ISSET(pg->state, HDS_VPD_VALID)) {
+ error = ENXIO;
goto done;
}
- c = ctrl - '0';
- p = port - 'A';
- if ((c & 0x1) == (p & 0x1))
- sc->sc_active = 1;
-
- printf("%s: ldev %s, controller %c, port %c\n", DEVNAME(sc), ldev,
- ctrl, port);
+ *preferred = ISSET(pg->state, HDS_VPD_PREFERRED);
- error = 0;
done:
- dma_free(buffer, length);
+ dma_free(pg, sizeof(*pg));
return (error);
}