1*1525749fSvisa /* $OpenBSD: mpath_rdac.c,v 1.27 2022/07/02 08:50:42 visa Exp $ */
27e24ec3eSdlg
37e24ec3eSdlg /*
47e24ec3eSdlg * Copyright (c) 2010 David Gwynne <dlg@openbsd.org>
57e24ec3eSdlg *
67e24ec3eSdlg * Permission to use, copy, modify, and distribute this software for any
77e24ec3eSdlg * purpose with or without fee is hereby granted, provided that the above
87e24ec3eSdlg * copyright notice and this permission notice appear in all copies.
97e24ec3eSdlg *
107e24ec3eSdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117e24ec3eSdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127e24ec3eSdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137e24ec3eSdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147e24ec3eSdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157e24ec3eSdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167e24ec3eSdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177e24ec3eSdlg */
187e24ec3eSdlg
197e24ec3eSdlg /* Redundant Disk Array Controller support for mpath(4) */
207e24ec3eSdlg
217e24ec3eSdlg #include <sys/param.h>
227e24ec3eSdlg #include <sys/systm.h>
237e24ec3eSdlg #include <sys/kernel.h>
247e24ec3eSdlg #include <sys/malloc.h>
257e24ec3eSdlg #include <sys/device.h>
267e24ec3eSdlg #include <sys/conf.h>
277e24ec3eSdlg #include <sys/queue.h>
287e24ec3eSdlg #include <sys/rwlock.h>
297e24ec3eSdlg #include <sys/pool.h>
307e24ec3eSdlg #include <sys/ioctl.h>
317e24ec3eSdlg
327e24ec3eSdlg #include <scsi/scsi_all.h>
337e24ec3eSdlg #include <scsi/scsiconf.h>
347e24ec3eSdlg #include <scsi/mpathvar.h>
357e24ec3eSdlg
367e24ec3eSdlg struct rdac_common_mode_page {
377e24ec3eSdlg u_int8_t controller_serial[16];
387e24ec3eSdlg u_int8_t alt_controller_serial[16];
397e24ec3eSdlg u_int8_t mode[2];
407e24ec3eSdlg u_int8_t alt_mode[2];
417e24ec3eSdlg u_int8_t timeout;
427e24ec3eSdlg u_int8_t options;
437e24ec3eSdlg };
447e24ec3eSdlg
457e24ec3eSdlg /*
467e24ec3eSdlg * RDAC VPD pages
477e24ec3eSdlg */
487e24ec3eSdlg #define RDAC_VPD_HDWVER 0xc0 /* Hardware Version */
497e24ec3eSdlg #define RDAC_VPD_SERNUM 0xc1 /* Serial Numbers */
507e24ec3eSdlg #define RDAC_VPD_SFWVER 0xc2
517e24ec3eSdlg #define RDAC_VPD_FEAPAR 0xc3 /* Feature Parameters */
527e24ec3eSdlg #define RDAC_VPD_SUBSYS 0xc4
537e24ec3eSdlg #define RDAC_VPD_HSTINT 0xc5
547e24ec3eSdlg #define RDAC_VPD_DGM 0xc6
557e24ec3eSdlg #define RDAC_VPD_HSTINT2 0xc7
567e24ec3eSdlg #define RDAC_VPD_EXTDEVID 0xc8
577e24ec3eSdlg #define RDAC_VPD_VOLACCESSCTL 0xc9
587e24ec3eSdlg
597e24ec3eSdlg struct rdac_vpd_hdwver {
607e24ec3eSdlg struct scsi_vpd_hdr hdr; /* RDAC_VPD_HDWVER */
617e24ec3eSdlg u_int8_t pg_id[4];
627e24ec3eSdlg #define RDAC_VPD_ID_HDWVER 0x68777234 /* "hwr4" */
637e24ec3eSdlg u_int8_t num_channels;
647e24ec3eSdlg u_int8_t flags;
657e24ec3eSdlg u_int8_t proc_memory_size;
667e24ec3eSdlg u_int8_t _reserved1[5];
677e24ec3eSdlg u_int8_t board_name[64];
687e24ec3eSdlg u_int8_t board_part_number[16];
697e24ec3eSdlg u_int8_t schematic_number[12];
707e24ec3eSdlg u_int8_t schematic_revision[4];
717e24ec3eSdlg u_int8_t serial_number[16];
727e24ec3eSdlg u_int8_t _reserved2[16];
737e24ec3eSdlg u_int8_t date_manufactured[8];
747e24ec3eSdlg u_int8_t board_revision[2];
757e24ec3eSdlg u_int8_t board_identifier[4];
767e24ec3eSdlg };
777e24ec3eSdlg
787e24ec3eSdlg struct rdac_vpd_subsys {
797e24ec3eSdlg struct scsi_vpd_hdr hdr; /* RDAC_VPD_SUBSYS */
807e24ec3eSdlg u_int8_t pg_id[4];
817e24ec3eSdlg #define RDAC_VPD_ID_SUBSYS 0x73756273 /* "subs" */
827e24ec3eSdlg u_int8_t subsystem_id[16];
837e24ec3eSdlg u_int8_t subsystem_revision[4];
847e24ec3eSdlg u_int8_t controller_slot_id[2];
857e24ec3eSdlg u_int8_t _reserved[2];
867e24ec3eSdlg };
877e24ec3eSdlg
887e24ec3eSdlg struct rdac_vpd_extdevid {
897e24ec3eSdlg struct scsi_vpd_hdr hdr; /* RDAC_VPD_EXTDEVID */
907e24ec3eSdlg u_int8_t pg_id[4];
917e24ec3eSdlg #define RDAC_VPD_ID_EXTDEVID 0x65646964 /* "edid" */
927e24ec3eSdlg u_int8_t _reserved[3];
937e24ec3eSdlg u_int8_t vol_id_len;
947e24ec3eSdlg u_int8_t vol_id[16];
957e24ec3eSdlg u_int8_t vol_label_len;
967e24ec3eSdlg u_int8_t vol_label[60];
977e24ec3eSdlg u_int8_t array_id_len;
987e24ec3eSdlg u_int8_t array_id[16];
997e24ec3eSdlg u_int8_t array_label_len;
1007e24ec3eSdlg u_int8_t array_label[60];
1017e24ec3eSdlg u_int8_t lun[8];
1027e24ec3eSdlg };
1037e24ec3eSdlg
1047e24ec3eSdlg struct rdac_vpd_volaccessctl {
1057e24ec3eSdlg struct scsi_vpd_hdr hdr; /* RDAC_VPD_VOLACCESSCTL */
1067e24ec3eSdlg u_int8_t pg_id[4];
1077e24ec3eSdlg #define RDAC_VPD_ID_VOLACCESSCTL 0x76616331 /* "vac1" */
1087e24ec3eSdlg u_int8_t avtcvp;
1097e24ec3eSdlg #define RDAC_VOLACCESSCTL_OWNER 0x01
1107e24ec3eSdlg #define RDAC_VOLACCESSCTL_AVT 0x70
111e08cdcacSdlg u_int8_t _reserved1;
112e08cdcacSdlg u_int8_t asym_access_state_cur;
113e08cdcacSdlg u_int8_t vendor_specific_cur;
114e08cdcacSdlg u_int8_t _reserved[36];
1157e24ec3eSdlg };
1167e24ec3eSdlg
1177e24ec3eSdlg struct rdac_softc {
1187e24ec3eSdlg struct device sc_dev;
1197e24ec3eSdlg struct mpath_path sc_path;
120e08cdcacSdlg struct scsi_xshandler sc_xsh;
121e08cdcacSdlg struct rdac_vpd_volaccessctl *sc_pg;
1227e24ec3eSdlg };
1237e24ec3eSdlg #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
1247e24ec3eSdlg
1257e24ec3eSdlg int rdac_match(struct device *, void *, void *);
1267e24ec3eSdlg void rdac_attach(struct device *, struct device *, void *);
1277e24ec3eSdlg int rdac_detach(struct device *, int);
1287e24ec3eSdlg int rdac_activate(struct device *, int);
1297e24ec3eSdlg
1309eaf72d1Smpi const struct cfattach rdac_ca = {
1317e24ec3eSdlg sizeof(struct rdac_softc),
1327e24ec3eSdlg rdac_match,
1337e24ec3eSdlg rdac_attach,
1347e24ec3eSdlg rdac_detach,
1357e24ec3eSdlg rdac_activate
1367e24ec3eSdlg };
1377e24ec3eSdlg
1387e24ec3eSdlg struct cfdriver rdac_cd = {
1397e24ec3eSdlg NULL,
1407e24ec3eSdlg "rdac",
1417e24ec3eSdlg DV_DULL
1427e24ec3eSdlg };
1437e24ec3eSdlg
1447e24ec3eSdlg void rdac_mpath_start(struct scsi_xfer *);
1457e24ec3eSdlg int rdac_mpath_checksense(struct scsi_xfer *);
1469ce0d10eSdlg void rdac_mpath_status(struct scsi_link *);
1477e24ec3eSdlg
148c32a5b15Sdlg const struct mpath_ops rdac_mpath_ops = {
1497e24ec3eSdlg "rdac",
1507e24ec3eSdlg rdac_mpath_checksense,
1518f5a0873Sdlg rdac_mpath_status
1527e24ec3eSdlg };
1537e24ec3eSdlg
1543c83f1daSdlg int rdac_extdevid(struct rdac_softc *);
155ecfe95d9Sdlg int rdac_groupid(struct rdac_softc *);
156e08cdcacSdlg
157e08cdcacSdlg void rdac_status(struct scsi_xfer *);
158e08cdcacSdlg void rdac_status_done(struct scsi_xfer *);
1597e24ec3eSdlg
1607e24ec3eSdlg struct rdac_device {
1617e24ec3eSdlg char *vendor;
1627e24ec3eSdlg char *product;
1637e24ec3eSdlg };
1647e24ec3eSdlg
1657e24ec3eSdlg struct rdac_device rdac_devices[] = {
1667e24ec3eSdlg /* " vendor " " device " */
1677e24ec3eSdlg /* "01234567" "0123456789012345" */
1687e24ec3eSdlg { "SUN ", "CSM200_" },
1697c9637abSdlg { "DELL ", "MD3000 " },
1707c9637abSdlg { "DELL ", "MD3000i " },
1717c9637abSdlg { "DELL ", "MD32xx " },
1727c9637abSdlg { "DELL ", "MD32xxi " }
1737e24ec3eSdlg };
1747e24ec3eSdlg
1757e24ec3eSdlg int
rdac_match(struct device * parent,void * match,void * aux)1767e24ec3eSdlg rdac_match(struct device *parent, void *match, void *aux)
1777e24ec3eSdlg {
1787e24ec3eSdlg struct scsi_attach_args *sa = aux;
1790fbd355cSkrw struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
1807e24ec3eSdlg struct rdac_device *s;
1817e24ec3eSdlg int i;
1827e24ec3eSdlg
1837e24ec3eSdlg if (mpath_path_probe(sa->sa_sc_link) != 0)
1847e24ec3eSdlg return (0);
1857e24ec3eSdlg
1867e24ec3eSdlg for (i = 0; i < nitems(rdac_devices); i++) {
1877e24ec3eSdlg s = &rdac_devices[i];
1887e24ec3eSdlg
1897e24ec3eSdlg if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 &&
1907e24ec3eSdlg bcmp(s->product, inq->product, strlen(s->product)) == 0)
191b2d08956Sdlg return (8);
1927e24ec3eSdlg }
1937e24ec3eSdlg
1947e24ec3eSdlg return (0);
1957e24ec3eSdlg }
1967e24ec3eSdlg
1977e24ec3eSdlg void
rdac_attach(struct device * parent,struct device * self,void * aux)1987e24ec3eSdlg rdac_attach(struct device *parent, struct device *self, void *aux)
1997e24ec3eSdlg {
2007e24ec3eSdlg struct rdac_softc *sc = (struct rdac_softc *)self;
2017e24ec3eSdlg struct scsi_attach_args *sa = aux;
2027e24ec3eSdlg struct scsi_link *link = sa->sa_sc_link;
203bba3b8d8Sdlg int id;
2047e24ec3eSdlg
2057e24ec3eSdlg printf("\n");
2067e24ec3eSdlg
2077e24ec3eSdlg /* init link */
2087e24ec3eSdlg link->device_softc = sc;
2097e24ec3eSdlg
2107e24ec3eSdlg /* init path */
2117e24ec3eSdlg scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start);
2127e24ec3eSdlg sc->sc_path.p_link = link;
2137e24ec3eSdlg
214e08cdcacSdlg /* init status handler */
215e08cdcacSdlg scsi_xsh_set(&sc->sc_xsh, link, rdac_status);
216e08cdcacSdlg sc->sc_pg = dma_alloc(sizeof(*sc->sc_pg), PR_WAITOK);
2177e24ec3eSdlg
218e08cdcacSdlg /* let's go */
219e08cdcacSdlg if (rdac_extdevid(sc) != 0)
2207e24ec3eSdlg return;
2217e24ec3eSdlg
222bba3b8d8Sdlg id = rdac_groupid(sc);
223bba3b8d8Sdlg if (id == -1) {
224bba3b8d8Sdlg /* error printed by rdac_groupid */
225bba3b8d8Sdlg return;
226bba3b8d8Sdlg }
227bba3b8d8Sdlg
228bba3b8d8Sdlg if (mpath_path_attach(&sc->sc_path, id, &rdac_mpath_ops) != 0)
2297e24ec3eSdlg printf("%s: unable to attach path\n", DEVNAME(sc));
2307e24ec3eSdlg }
2317e24ec3eSdlg
2327e24ec3eSdlg int
rdac_detach(struct device * self,int flags)2337e24ec3eSdlg rdac_detach(struct device *self, int flags)
2347e24ec3eSdlg {
235e08cdcacSdlg struct rdac_softc *sc = (struct rdac_softc *)self;
236e08cdcacSdlg
237e08cdcacSdlg dma_free(sc->sc_pg, sizeof(*sc->sc_pg));
238e08cdcacSdlg
2397e24ec3eSdlg return (0);
2407e24ec3eSdlg }
2417e24ec3eSdlg
2427e24ec3eSdlg int
rdac_activate(struct device * self,int act)2437e24ec3eSdlg rdac_activate(struct device *self, int act)
2447e24ec3eSdlg {
2457e24ec3eSdlg struct rdac_softc *sc = (struct rdac_softc *)self;
2467e24ec3eSdlg
2477e24ec3eSdlg switch (act) {
2487e24ec3eSdlg case DVACT_DEACTIVATE:
249e08cdcacSdlg if (scsi_xsh_del(&sc->sc_xsh))
250e08cdcacSdlg mpath_path_status(&sc->sc_path, MPATH_S_UNKNOWN);
2515a08a2c9Sdlg if (sc->sc_path.p_group != NULL)
2527e24ec3eSdlg mpath_path_detach(&sc->sc_path);
2537e24ec3eSdlg break;
2547e24ec3eSdlg }
255f7a04f0fSkrw return (0);
2567e24ec3eSdlg }
2577e24ec3eSdlg
2587e24ec3eSdlg void
rdac_mpath_start(struct scsi_xfer * xs)2597e24ec3eSdlg rdac_mpath_start(struct scsi_xfer *xs)
2607e24ec3eSdlg {
2617e24ec3eSdlg struct rdac_softc *sc = xs->sc_link->device_softc;
2627e24ec3eSdlg
2637e24ec3eSdlg mpath_start(&sc->sc_path, xs);
2647e24ec3eSdlg }
2657e24ec3eSdlg
2667e24ec3eSdlg int
rdac_mpath_checksense(struct scsi_xfer * xs)2677e24ec3eSdlg rdac_mpath_checksense(struct scsi_xfer *xs)
2687e24ec3eSdlg {
269401e70b2Sdlg struct scsi_sense_data *sense = &xs->sense;
270401e70b2Sdlg u_int8_t skey;
271401e70b2Sdlg
272401e70b2Sdlg if ((sense->error_code & SSD_ERRCODE) != SSD_ERRCODE_CURRENT)
273401e70b2Sdlg return (MPATH_SENSE_DECLINED);
274401e70b2Sdlg
275401e70b2Sdlg skey = sense->flags & SSD_KEY;
276401e70b2Sdlg
277401e70b2Sdlg /* i wish i knew what the magic numbers meant */
278401e70b2Sdlg
27950e5b7a7Sdlg /* invalid request due to current lu ownership */
280401e70b2Sdlg if (skey == SKEY_ILLEGAL_REQUEST && ASC_ASCQ(sense) == 0x9401)
281401e70b2Sdlg return (MPATH_SENSE_FAILOVER);
282401e70b2Sdlg
283401e70b2Sdlg if (skey == SKEY_UNIT_ATTENTION && ASC_ASCQ(sense) == 0x8b02)
284401e70b2Sdlg return (MPATH_SENSE_FAILOVER);
285401e70b2Sdlg
286cdf96446Sdlg return (MPATH_SENSE_DECLINED);
2877e24ec3eSdlg }
2887e24ec3eSdlg
2899ce0d10eSdlg void
rdac_mpath_status(struct scsi_link * link)2909ce0d10eSdlg rdac_mpath_status(struct scsi_link *link)
2917e24ec3eSdlg {
2929ce0d10eSdlg struct rdac_softc *sc = link->device_softc;
2937e24ec3eSdlg
294e08cdcacSdlg scsi_xsh_add(&sc->sc_xsh);
295e08cdcacSdlg }
296e08cdcacSdlg
297e08cdcacSdlg void
rdac_status(struct scsi_xfer * xs)298e08cdcacSdlg rdac_status(struct scsi_xfer *xs)
299e08cdcacSdlg {
300e08cdcacSdlg struct scsi_link *link = xs->sc_link;
301e08cdcacSdlg struct rdac_softc *sc = link->device_softc;
302e08cdcacSdlg
303e08cdcacSdlg scsi_init_inquiry(xs, SI_EVPD, RDAC_VPD_VOLACCESSCTL,
304e08cdcacSdlg sc->sc_pg, sizeof(*sc->sc_pg));
305e08cdcacSdlg
306e08cdcacSdlg xs->done = rdac_status_done;
307e08cdcacSdlg
308e08cdcacSdlg scsi_xs_exec(xs);
309e08cdcacSdlg }
310e08cdcacSdlg
311e08cdcacSdlg void
rdac_status_done(struct scsi_xfer * xs)312e08cdcacSdlg rdac_status_done(struct scsi_xfer *xs)
313e08cdcacSdlg {
314e08cdcacSdlg struct scsi_link *link = xs->sc_link;
315e08cdcacSdlg struct rdac_softc *sc = link->device_softc;
316e08cdcacSdlg struct rdac_vpd_volaccessctl *pg = sc->sc_pg;
317e08cdcacSdlg int status = MPATH_S_UNKNOWN;
318e08cdcacSdlg
319e08cdcacSdlg if (xs->error == XS_NOERROR &&
320e08cdcacSdlg _4btol(pg->pg_id) == RDAC_VPD_ID_VOLACCESSCTL) {
321e08cdcacSdlg status = (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT) ||
322e08cdcacSdlg ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) ?
323e08cdcacSdlg MPATH_S_ACTIVE : MPATH_S_PASSIVE;
324e08cdcacSdlg }
325e08cdcacSdlg
326e08cdcacSdlg scsi_xs_put(xs);
327e08cdcacSdlg mpath_path_status(&sc->sc_path, status);
3287e24ec3eSdlg }
3297e24ec3eSdlg
3307e24ec3eSdlg int
rdac_groupid(struct rdac_softc * sc)331ecfe95d9Sdlg rdac_groupid(struct rdac_softc *sc)
332ecfe95d9Sdlg {
333ecfe95d9Sdlg struct rdac_vpd_subsys *pg;
334ecfe95d9Sdlg int rv = -1;
335ecfe95d9Sdlg
336ecfe95d9Sdlg pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
337ecfe95d9Sdlg
338ecfe95d9Sdlg if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
339ecfe95d9Sdlg RDAC_VPD_SUBSYS, scsi_autoconf) != 0) {
340ecfe95d9Sdlg printf("%s: unable to fetch subsys vpd page\n", DEVNAME(sc));
341ecfe95d9Sdlg goto done;
342ecfe95d9Sdlg }
343ecfe95d9Sdlg
344ecfe95d9Sdlg if (_4btol(pg->pg_id) != RDAC_VPD_ID_SUBSYS) {
345ecfe95d9Sdlg printf("%s: subsys page is invalid\n", DEVNAME(sc));
346ecfe95d9Sdlg goto done;
347ecfe95d9Sdlg }
348ecfe95d9Sdlg
349ecfe95d9Sdlg rv = _2btol(pg->controller_slot_id);
350ecfe95d9Sdlg
351ecfe95d9Sdlg done:
352ecfe95d9Sdlg dma_free(pg, sizeof(*pg));
353ecfe95d9Sdlg return (rv);
354ecfe95d9Sdlg }
355ecfe95d9Sdlg
356ecfe95d9Sdlg int
rdac_extdevid(struct rdac_softc * sc)3573c83f1daSdlg rdac_extdevid(struct rdac_softc *sc)
3587e24ec3eSdlg {
3595dc09c93Sdlg struct rdac_vpd_extdevid *pg;
3607e24ec3eSdlg char array[31];
3617e24ec3eSdlg char vol[31];
3627e24ec3eSdlg int i;
3635dc09c93Sdlg int rv = 1;
3647e24ec3eSdlg
3655dc09c93Sdlg pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
3665dc09c93Sdlg
3673c83f1daSdlg if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
3683c83f1daSdlg RDAC_VPD_EXTDEVID, scsi_autoconf) != 0) {
3693c83f1daSdlg printf("%s: unable to fetch extdevid vpd page\n", DEVNAME(sc));
3705dc09c93Sdlg goto done;
3717e24ec3eSdlg }
3727e24ec3eSdlg
3735dc09c93Sdlg if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) {
37467ad556dSdlg printf("%s: extdevid page is invalid\n", DEVNAME(sc));
3755dc09c93Sdlg goto done;
3767e24ec3eSdlg }
3777e24ec3eSdlg
3787e24ec3eSdlg memset(array, 0, sizeof(array));
3795dc09c93Sdlg for (i = 0; i < sizeof(pg->array_label) / 2; i++)
3805dc09c93Sdlg array[i] = pg->array_label[i * 2 + 1];
3817e24ec3eSdlg
3827e24ec3eSdlg memset(vol, 0, sizeof(vol));
3835dc09c93Sdlg for (i = 0; i < sizeof(pg->vol_label) / 2; i++)
3845dc09c93Sdlg vol[i] = pg->vol_label[i * 2 + 1];
3857e24ec3eSdlg
3867e24ec3eSdlg printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol);
3877e24ec3eSdlg
3885dc09c93Sdlg rv = 0;
3895dc09c93Sdlg done:
3905dc09c93Sdlg dma_free(pg, sizeof(*pg));
3915dc09c93Sdlg return (rv);
3927e24ec3eSdlg }
393