implement the fetching of a scsi devices "devid". recent hardware provides
authordlg <dlg@openbsd.org>
Tue, 22 Jul 2008 01:01:31 +0000 (01:01 +0000)
committerdlg <dlg@openbsd.org>
Tue, 22 Jul 2008 01:01:31 +0000 (01:01 +0000)
a vpd page that uniquely identifies a device no matter what bus topology or
addressing was used to find it.

we have a workaround for old school scsi devices that do not differentiate
between luns. if the inq data for high luns is the same as the inq data
for lun 0, we assume it is one of these buggy devices.

the problem with this is that things like SANs present multiple
volumes as luns and they all have the same inq data. if you wanted
to present more than one volume to openbsd you would only ever see
the first one.

devices give us a mechanism to differentiate between luns, so now
i do get all my volumes attached in openbsde.

review and feedback by krw@ marco@ testing by todd@

sys/scsi/scsiconf.c
sys/scsi/scsiconf.h

index 4ed8783..969cb16 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: scsiconf.c,v 1.134 2008/07/22 00:40:37 dlg Exp $      */
+/*     $OpenBSD: scsiconf.c,v 1.135 2008/07/22 01:01:31 dlg Exp $      */
 /*     $NetBSD: scsiconf.c,v 1.57 1996/05/02 01:09:01 neil Exp $       */
 
 /*
@@ -69,6 +69,9 @@
  */
 int    scsi_probedev(struct scsibus_softc *, int, int);
 
+void   scsi_devid(struct scsi_link *);
+int    scsi_devid_pg83(struct scsi_link *);
+
 struct scsi_device probe_switch = {
        NULL,
        NULL,
@@ -700,7 +703,7 @@ int
 scsi_probedev(struct scsibus_softc *scsi, int target, int lun)
 {
        const struct scsi_quirk_inquiry_pattern *finger;
-       struct scsi_inquiry_data        *inqbuf;
+       struct scsi_inquiry_data *inqbuf;
        struct scsi_attach_args sa;
        struct scsi_link *sc_link;
        struct cfdata *cf;
@@ -784,10 +787,15 @@ scsi_probedev(struct scsibus_softc *scsi, int target, int lun)
                break;
        }
 
+       scsi_devid(sc_link);
+
        if (lun == 0 || scsi->sc_link[target][0] == NULL)
                ;
        else if (sc_link->flags & SDEV_UMASS)
                ;
+       else if (sc_link->id.d_type != DEVID_NONE &&
+           !DEVID_CMP(&scsi->sc_link[target][0]->id, &sc_link->id))
+               ;
        else if (memcmp(inqbuf, &scsi->sc_link[target][0]->inqdata,
            sizeof(*inqbuf)) == 0) {
                /* The device doesn't distinguish between LUNs. */
@@ -932,3 +940,119 @@ scsi_inqmatch(struct scsi_inquiry_data *inqbuf, const void *_base,
 
        return (bestmatch);
 }
+
+void
+scsi_devid(struct scsi_link *link)
+{
+       struct {
+               struct scsi_vpd_hdr hdr;
+               u_int8_t list[32];
+       } __packed pg;
+       int pg80 = 0, pg83 = 0, i;
+
+       if (SCSISPC(link->inqdata.version) >= 2) {
+               if (scsi_inquire_vpd(link, &pg, sizeof(pg), SI_PG_SUPPORTED,
+                   scsi_autoconf) != 0)
+                       return;
+
+               for (i = 0; i < MIN(sizeof(pg.list), pg.hdr.page_length); i++) {
+                       switch (pg.list[i]) {
+                       case SI_PG_SERIAL:
+                               pg80 = 1;
+                               break;
+                       case SI_PG_DEVID:
+                               pg83 = 1;
+                               break;
+                       }
+               }
+
+               if (pg83 && scsi_devid_pg83(link) == 0)
+                       return;
+#ifdef notyet
+               if (pg80 && scsi_devid_pg80(link) == 0)
+                       return;
+#endif
+       }
+}
+
+int
+scsi_devid_pg83(struct scsi_link *link)
+{
+       struct scsi_vpd_hdr hdr;
+       struct scsi_vpd_devid_hdr dhdr;
+       u_int8_t *pg, *id;
+       int type, idtype = 0, idlen;
+       int len, pos;
+       int rv;
+
+       rv = scsi_inquire_vpd(link, &hdr, sizeof(hdr), SI_PG_DEVID,
+           scsi_autoconf);
+       if (rv != 0)
+               return (rv);
+
+       len = sizeof(hdr) + hdr.page_length;
+       pg = malloc(len, M_TEMP, M_WAITOK);
+
+       rv = scsi_inquire_vpd(link, pg, len, SI_PG_DEVID, scsi_autoconf);
+       if (rv != 0)
+               goto err;
+
+       pos = sizeof(hdr);
+
+       do {
+               if (len - pos < sizeof(dhdr)) {
+                       rv = EIO;
+                       goto err;
+               }
+               memcpy(&dhdr, &pg[pos], sizeof(dhdr));
+               pos += sizeof(dhdr);
+               if (len - pos < dhdr.len) {
+                       rv = EIO;
+                       goto err;
+               }
+
+               if (VPD_DEVID_ASSOC(dhdr.flags) == VPD_DEVID_ASSOC_LU) {
+                       type = VPD_DEVID_TYPE(dhdr.flags);
+                       switch (type) {
+                       case VPD_DEVID_TYPE_NAA:
+                       case VPD_DEVID_TYPE_EUI64:
+                       case VPD_DEVID_TYPE_T10:
+                               if (type >= idtype) {
+                                       idtype = type;
+                                       idlen = dhdr.len;
+                                       id = &pg[pos];
+                               }
+                               break;
+
+                       default:
+                               /* skip */
+                               break;
+                       }
+               }
+
+               pos += dhdr.len;
+       } while (idtype != VPD_DEVID_TYPE_NAA && len != pos);
+
+       if (idtype > 0) {
+               link->id.d_id = malloc(idlen, M_DEVBUF, M_WAITOK);
+
+               switch (idtype) {
+               case VPD_DEVID_TYPE_NAA:
+                       link->id.d_type = DEVID_NAA;
+                       break;
+               case VPD_DEVID_TYPE_EUI64:
+                       link->id.d_type = DEVID_EUI;
+                       break;
+               case VPD_DEVID_TYPE_T10:
+                       link->id.d_type = DEVID_T10;
+                       break;
+               }
+               link->id.d_len = idlen;
+               memcpy(link->id.d_id, id, idlen);
+       } else
+               rv = ENODEV;
+
+err:
+       free(pg, M_TEMP);
+       return (rv);
+}
index d7e252f..7ab4378 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: scsiconf.h,v 1.93 2008/06/21 21:11:34 krw Exp $       */
+/*     $OpenBSD: scsiconf.h,v 1.94 2008/07/22 01:01:31 dlg Exp $       */
 /*     $NetBSD: scsiconf.h,v 1.35 1997/04/02 02:29:38 mycroft Exp $    */
 
 /*
 #include <machine/cpu.h>
 #include <scsi/scsi_debug.h>
 
+#define DEVID_NONE     0
+#define DEVID_NAA      1
+#define DEVID_EUI      2
+#define DEVID_T10      3
+
+struct devid {
+       int              d_type;
+       u_int            d_len;
+       u_int8_t        *d_id;
+};
+
+#define DEVID_CMP(_a, _b) (                            \
+       (_a) != NULL &&                                 \
+       (_b) != NULL &&                                 \
+       (_a)->d_type != DEVID_NONE &&                   \
+       (_a)->d_type == (_b)->d_type &&                 \
+       (_a)->d_len == (_b)->d_len &&                   \
+       bcmp((_a)->d_id, (_b)->d_id, (_a)->d_len) == 0  \
+)
+
 /*
  * The following documentation tries to describe the relationship between the
  * various structures defined in this file:
@@ -177,6 +197,7 @@ struct scsi_link {
        void    *adapter_softc;         /* needed for call to foo_scsi_cmd */
        struct  scsibus_softc *bus;     /* link to the scsibus we're on */
        struct  scsi_inquiry_data inqdata; /* copy of INQUIRY data from probe */
+       struct  devid id;
 };
 
 int    scsiprint(void *, const char *);