1/* $OpenBSD: mpath_hds.c,v 1.26 2022/07/02 08:50:42 visa Exp $ */ 2 3/* 4 * Copyright (c) 2011 David Gwynne <dlg@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/* Hitachi Modular Storage support for mpath(4) */ 20 21#include <sys/param.h> 22#include <sys/systm.h> 23#include <sys/kernel.h> 24#include <sys/malloc.h> 25#include <sys/device.h> 26#include <sys/conf.h> 27#include <sys/queue.h> 28#include <sys/rwlock.h> 29#include <sys/pool.h> 30#include <sys/ioctl.h> 31 32#include <scsi/scsi_all.h> 33#include <scsi/scsiconf.h> 34#include <scsi/mpathvar.h> 35 36#define HDS_INQ_LDEV_OFFSET 44 37#define HDS_INQ_LDEV_LEN 4 38#define HDS_INQ_CTRL_OFFSET 49 39#define HDS_INQ_PORT_OFFSET 50 40#define HDS_INQ_TYPE_OFFSET 128 41#define HDS_INQ_TYPE 0x44463030 /* "DF00" */ 42 43#define HDS_VPD 0xe0 44 45struct hds_vpd { 46 struct scsi_vpd_hdr hdr; /* HDS_VPD */ 47 u_int8_t state; 48#define HDS_VPD_VALID 0x80 49#define HDS_VPD_PREFERRED 0x40 50 51 /* followed by lots of unknown stuff */ 52}; 53 54#define HDS_SYMMETRIC 0 55#define HDS_ASYMMETRIC 1 56 57struct hds_softc { 58 struct device sc_dev; 59 struct mpath_path sc_path; 60 struct scsi_xshandler sc_xsh; 61 struct hds_vpd *sc_vpd; 62 int sc_mode; 63 int sc_ctrl; 64}; 65#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 66 67int hds_match(struct device *, void *, void *); 68void hds_attach(struct device *, struct device *, void *); 69int hds_detach(struct device *, int); 70int hds_activate(struct device *, int); 71 72const struct cfattach hds_ca = { 73 sizeof(struct hds_softc), 74 hds_match, 75 hds_attach, 76 hds_detach, 77 hds_activate 78}; 79 80struct cfdriver hds_cd = { 81 NULL, 82 "hds", 83 DV_DULL 84}; 85 86void hds_mpath_start(struct scsi_xfer *); 87int hds_mpath_checksense(struct scsi_xfer *); 88void hds_mpath_status(struct scsi_link *); 89 90const struct mpath_ops hds_mpath_ops = { 91 "hds", 92 hds_mpath_checksense, 93 hds_mpath_status 94}; 95 96struct hds_device { 97 char *vendor; 98 char *product; 99}; 100 101int hds_inquiry(struct scsi_link *, int *); 102int hds_info(struct hds_softc *); 103 104void hds_status(struct scsi_xfer *); 105void hds_status_done(struct scsi_xfer *); 106 107struct hds_device hds_devices[] = { 108/* " vendor " " device " */ 109/* "01234567" "0123456789012345" */ 110 { "HITACHI ", "DF600F " }, 111 { "HITACHI ", "DF600F-CM " } 112}; 113 114int 115hds_match(struct device *parent, void *match, void *aux) 116{ 117 struct scsi_attach_args *sa = aux; 118 struct scsi_link *link = sa->sa_sc_link; 119 struct scsi_inquiry_data *inq = &link->inqdata; 120 struct hds_device *s; 121 int i, mode; 122 123 if (mpath_path_probe(sa->sa_sc_link) != 0) 124 return (0); 125 126 for (i = 0; i < nitems(hds_devices); i++) { 127 s = &hds_devices[i]; 128 129 if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && 130 bcmp(s->product, inq->product, strlen(s->product)) == 0 && 131 hds_inquiry(link, &mode) == 0) 132 return (8); 133 } 134 135 return (0); 136} 137 138void 139hds_attach(struct device *parent, struct device *self, void *aux) 140{ 141 struct hds_softc *sc = (struct hds_softc *)self; 142 struct scsi_attach_args *sa = aux; 143 struct scsi_link *link = sa->sa_sc_link; 144 145 printf("\n"); 146 147 /* init link */ 148 link->device_softc = sc; 149 150 /* init path */ 151 scsi_xsh_set(&sc->sc_path.p_xsh, link, hds_mpath_start); 152 sc->sc_path.p_link = link; 153 154 /* init status handler */ 155 scsi_xsh_set(&sc->sc_xsh, link, hds_status); 156 sc->sc_vpd = dma_alloc(sizeof(*sc->sc_vpd), PR_WAITOK); 157 158 if (hds_inquiry(link, &sc->sc_mode) != 0) { 159 printf("%s: unable to query controller mode\n", DEVNAME(sc)); 160 return; 161 } 162 163 if (hds_info(sc) != 0) { 164 printf("%s: unable to query path info\n", DEVNAME(sc)); 165 return; 166 } 167 168 if (mpath_path_attach(&sc->sc_path, 169 (sc->sc_mode == HDS_SYMMETRIC) ? 0 : sc->sc_ctrl, 170 &hds_mpath_ops) != 0) 171 printf("%s: unable to attach path\n", DEVNAME(sc)); 172} 173 174int 175hds_detach(struct device *self, int flags) 176{ 177 struct hds_softc *sc = (struct hds_softc *)self; 178 179 dma_free(sc->sc_vpd, sizeof(*sc->sc_vpd)); 180 181 return (0); 182} 183 184int 185hds_activate(struct device *self, int act) 186{ 187 struct hds_softc *sc = (struct hds_softc *)self; 188 189 switch (act) { 190 case DVACT_DEACTIVATE: 191 if (sc->sc_path.p_group != NULL) 192 mpath_path_detach(&sc->sc_path); 193 break; 194 } 195 return (0); 196} 197 198void 199hds_mpath_start(struct scsi_xfer *xs) 200{ 201 struct hds_softc *sc = xs->sc_link->device_softc; 202 203 mpath_start(&sc->sc_path, xs); 204} 205 206int 207hds_mpath_checksense(struct scsi_xfer *xs) 208{ 209 return (MPATH_SENSE_DECLINED); 210} 211 212void 213hds_mpath_status(struct scsi_link *link) 214{ 215 struct hds_softc *sc = link->device_softc; 216 217 if (sc->sc_mode == HDS_SYMMETRIC) 218 mpath_path_status(&sc->sc_path, MPATH_S_ACTIVE); 219 else 220 scsi_xsh_add(&sc->sc_xsh); 221} 222 223void 224hds_status(struct scsi_xfer *xs) 225{ 226 struct scsi_link *link = xs->sc_link; 227 struct hds_softc *sc = link->device_softc; 228 229 scsi_init_inquiry(xs, SI_EVPD, HDS_VPD, 230 sc->sc_vpd, sizeof(*sc->sc_vpd)); 231 232 xs->done = hds_status_done; 233 234 scsi_xs_exec(xs); 235} 236 237void 238hds_status_done(struct scsi_xfer *xs) 239{ 240 struct scsi_link *link = xs->sc_link; 241 struct hds_softc *sc = link->device_softc; 242 struct hds_vpd *vpd = sc->sc_vpd; 243 int status = MPATH_S_UNKNOWN; 244 245 if (xs->error == XS_NOERROR && 246 _2btol(vpd->hdr.page_length) >= sizeof(vpd->state) && 247 ISSET(vpd->state, HDS_VPD_VALID)) { 248 status = ISSET(vpd->state, HDS_VPD_PREFERRED) ? 249 MPATH_S_ACTIVE : MPATH_S_PASSIVE; 250 } 251 252 scsi_xs_put(xs); 253 254 mpath_path_status(&sc->sc_path, status); 255} 256 257int 258hds_inquiry(struct scsi_link *link, int *mode) 259{ 260 struct scsi_xfer *xs; 261 u_int8_t *buf; 262 size_t len = SID_SCSI2_HDRLEN + link->inqdata.additional_length; 263 int error; 264 265 if (len < HDS_INQ_TYPE_OFFSET + sizeof(int)) 266 return (ENXIO); 267 268 xs = scsi_xs_get(link, scsi_autoconf); 269 if (xs == NULL) 270 return (ENOMEM); 271 272 buf = dma_alloc(len, PR_WAITOK); 273 scsi_init_inquiry(xs, 0, 0, buf, len); 274 error = scsi_xs_sync(xs); 275 scsi_xs_put(xs); 276 if (error) 277 goto done; 278 279 if (buf[128] == '\0') 280 *mode = HDS_ASYMMETRIC; 281 else if (_4btol(&buf[HDS_INQ_TYPE_OFFSET]) == HDS_INQ_TYPE) 282 *mode = HDS_SYMMETRIC; 283 else 284 error = ENXIO; 285 286done: 287 dma_free(buf, len); 288 return (error); 289} 290 291int 292hds_info(struct hds_softc *sc) 293{ 294 struct scsi_link *link = sc->sc_path.p_link; 295 struct scsi_xfer *xs; 296 u_int8_t *buf; 297 size_t len = SID_SCSI2_HDRLEN + link->inqdata.additional_length; 298 char ldev[9], ctrl, port; 299 int error; 300 301 xs = scsi_xs_get(link, scsi_autoconf); 302 if (xs == NULL) 303 return (ENOMEM); 304 305 buf = dma_alloc(len, PR_WAITOK); 306 scsi_init_inquiry(xs, 0, 0, buf, len); 307 error = scsi_xs_sync(xs); 308 scsi_xs_put(xs); 309 if (error) 310 goto done; 311 312 bzero(ldev, sizeof(ldev)); 313 scsi_strvis(ldev, &buf[HDS_INQ_LDEV_OFFSET], HDS_INQ_LDEV_LEN); 314 ctrl = buf[HDS_INQ_CTRL_OFFSET]; 315 port = buf[HDS_INQ_PORT_OFFSET]; 316 317 if (ctrl >= '0' && ctrl <= '9') { 318 printf("%s: ldev %s, controller %c, port %c, %s\n", 319 DEVNAME(sc), ldev, ctrl, port, 320 sc->sc_mode == HDS_SYMMETRIC ? "symmetric" : "asymmetric"); 321 322 sc->sc_ctrl = ctrl; 323 } else 324 error = ENXIO; 325 326done: 327 dma_free(buf, len); 328 return (error); 329} 330