1 /* $OpenBSD: mpath_hds.c,v 1.6 2011/07/11 01:02:48 dlg 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/buf.h> 24 #include <sys/kernel.h> 25 #include <sys/malloc.h> 26 #include <sys/device.h> 27 #include <sys/proc.h> 28 #include <sys/conf.h> 29 #include <sys/queue.h> 30 #include <sys/rwlock.h> 31 #include <sys/pool.h> 32 #include <sys/ioctl.h> 33 #include <sys/poll.h> 34 #include <sys/selinfo.h> 35 36 #include <scsi/scsi_all.h> 37 #include <scsi/scsiconf.h> 38 #include <scsi/mpathvar.h> 39 40 #define HDS_INQ_LDEV_OFFSET 44 41 #define HDS_INQ_LDEV_LEN 4 42 #define HDS_INQ_CTRL_OFFSET 49 43 #define HDS_INQ_PORT_OFFSET 50 44 #define HDS_INQ_TYPE_OFFSET 128 45 #define HDS_INQ_TYPE 0x44463030 /* "DF00" */ 46 47 #define HDS_VPD 0xe0 48 49 struct hds_vpd { 50 struct scsi_vpd_hdr hdr; /* HDS_VPD */ 51 u_int8_t state; 52 #define HDS_VPD_VALID 0x80 53 #define HDS_VPD_PREFERRED 0x40 54 55 /* followed by lots of unknown stuff */ 56 }; 57 58 #define HDS_SYMMETRIC 0 59 #define HDS_ASYMMETRIC 1 60 61 struct hds_softc { 62 struct device sc_dev; 63 struct mpath_path sc_path; 64 int sc_mode; 65 }; 66 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 67 68 int hds_match(struct device *, void *, void *); 69 void hds_attach(struct device *, struct device *, void *); 70 int hds_detach(struct device *, int); 71 int hds_activate(struct device *, int); 72 73 struct cfattach hds_ca = { 74 sizeof(struct hds_softc), 75 hds_match, 76 hds_attach, 77 hds_detach, 78 hds_activate 79 }; 80 81 struct cfdriver hds_cd = { 82 NULL, 83 "hds", 84 DV_DULL 85 }; 86 87 void hds_mpath_start(struct scsi_xfer *); 88 int hds_mpath_checksense(struct scsi_xfer *); 89 int hds_mpath_online(struct scsi_link *); 90 int hds_mpath_offline(struct scsi_link *); 91 92 const struct mpath_ops hds_mpath_ops = { 93 "hds", 94 hds_mpath_checksense, 95 hds_mpath_online, 96 hds_mpath_offline, 97 MPATH_ROUNDROBIN 98 }; 99 100 struct hds_device { 101 char *vendor; 102 char *product; 103 }; 104 105 int hds_inquiry(struct scsi_link *, int *); 106 int hds_info(struct hds_softc *); 107 int hds_preferred(struct hds_softc *, int *); 108 109 struct hds_device hds_devices[] = { 110 /* " vendor " " device " */ 111 /* "01234567" "0123456789012345" */ 112 { "HITACHI ", "DF600F " }, 113 { "HITACHI ", "DF600F-CM " } 114 }; 115 116 int 117 hds_match(struct device *parent, void *match, void *aux) 118 { 119 struct scsi_attach_args *sa = aux; 120 struct scsi_inquiry_data *inq = sa->sa_inqbuf; 121 struct scsi_link *link = sa->sa_sc_link; 122 struct hds_device *s; 123 int i, mode; 124 125 if (mpath_path_probe(sa->sa_sc_link) != 0) 126 return (0); 127 128 for (i = 0; i < nitems(hds_devices); i++) { 129 s = &hds_devices[i]; 130 131 if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 && 132 bcmp(s->product, inq->product, strlen(s->product)) == 0 && 133 hds_inquiry(link, &mode) == 0) 134 return (3); 135 } 136 137 return (0); 138 } 139 140 void 141 hds_attach(struct device *parent, struct device *self, void *aux) 142 { 143 struct hds_softc *sc = (struct hds_softc *)self; 144 struct scsi_attach_args *sa = aux; 145 struct scsi_link *link = sa->sa_sc_link; 146 int preferred = 1; 147 148 printf("\n"); 149 150 /* init link */ 151 link->device_softc = sc; 152 153 /* init path */ 154 scsi_xsh_set(&sc->sc_path.p_xsh, link, hds_mpath_start); 155 sc->sc_path.p_link = link; 156 157 if (hds_inquiry(link, &sc->sc_mode) != 0) { 158 printf("%s: unable to query controller mode\n"); 159 return; 160 } 161 162 if (hds_info(sc) != 0) { 163 printf("%s: unable to query path info\n"); 164 return; 165 } 166 167 if (hds_preferred(sc, &preferred) != 0) { 168 printf("%s: unable to query preferred path\n", DEVNAME(sc)); 169 return; 170 } 171 172 if (!preferred) 173 return; 174 175 if (mpath_path_attach(&sc->sc_path, &hds_mpath_ops) != 0) 176 printf("%s: unable to attach path\n", DEVNAME(sc)); 177 } 178 179 int 180 hds_detach(struct device *self, int flags) 181 { 182 return (0); 183 } 184 185 int 186 hds_activate(struct device *self, int act) 187 { 188 struct hds_softc *sc = (struct hds_softc *)self; 189 int rv = 0; 190 191 switch (act) { 192 case DVACT_SUSPEND: 193 case DVACT_RESUME: 194 break; 195 case DVACT_DEACTIVATE: 196 if (sc->sc_path.p_dev != NULL) 197 mpath_path_detach(&sc->sc_path); 198 break; 199 } 200 return (rv); 201 } 202 203 void 204 hds_mpath_start(struct scsi_xfer *xs) 205 { 206 struct hds_softc *sc = xs->sc_link->device_softc; 207 208 mpath_start(&sc->sc_path, xs); 209 } 210 211 int 212 hds_mpath_checksense(struct scsi_xfer *xs) 213 { 214 return (0); 215 } 216 217 int 218 hds_mpath_online(struct scsi_link *link) 219 { 220 return (0); 221 } 222 223 int 224 hds_mpath_offline(struct scsi_link *link) 225 { 226 return (0); 227 } 228 229 int 230 hds_inquiry(struct scsi_link *link, int *mode) 231 { 232 struct scsi_xfer *xs; 233 u_int8_t *buf; 234 size_t len = link->inqdata.additional_length + 5; 235 int error; 236 237 if (len < HDS_INQ_TYPE_OFFSET + sizeof(int)) 238 return (ENXIO); 239 240 xs = scsi_xs_get(link, scsi_autoconf); 241 if (xs == NULL) 242 return (ENOMEM); 243 244 buf = dma_alloc(len, PR_WAITOK); 245 scsi_init_inquiry(xs, 0, 0, buf, len); 246 error = scsi_xs_sync(xs); 247 scsi_xs_put(xs); 248 if (error) 249 goto done; 250 251 if (buf[128] == '\0') 252 *mode = HDS_ASYMMETRIC; 253 else if (_4btol(&buf[HDS_INQ_TYPE_OFFSET]) == HDS_INQ_TYPE) 254 *mode = HDS_SYMMETRIC; 255 else 256 error = ENXIO; 257 258 done: 259 dma_free(buf, len); 260 return (error); 261 } 262 263 int 264 hds_info(struct hds_softc *sc) 265 { 266 struct scsi_link *link = sc->sc_path.p_link; 267 struct scsi_xfer *xs; 268 u_int8_t *buf; 269 size_t len = link->inqdata.additional_length + 5; 270 char ldev[9], ctrl, port; 271 int error; 272 273 xs = scsi_xs_get(link, scsi_autoconf); 274 if (xs == NULL) 275 return (ENOMEM); 276 277 buf = dma_alloc(len, PR_WAITOK); 278 scsi_init_inquiry(xs, 0, 0, buf, len); 279 error = scsi_xs_sync(xs); 280 scsi_xs_put(xs); 281 if (error) 282 goto done; 283 284 bzero(ldev, sizeof(ldev)); 285 scsi_strvis(ldev, &buf[HDS_INQ_LDEV_OFFSET], HDS_INQ_LDEV_LEN); 286 ctrl = buf[HDS_INQ_CTRL_OFFSET]; 287 port = buf[HDS_INQ_PORT_OFFSET]; 288 289 if (ctrl >= '0' && ctrl <= '9' && port >= 'A' && port <= 'B') { 290 printf("%s: ldev %s, controller %c, port %c, %s\n", 291 DEVNAME(sc), ldev, ctrl, port, 292 sc->sc_mode == HDS_SYMMETRIC ? "symmetric" : "asymmetric"); 293 } else 294 error = ENXIO; 295 296 done: 297 dma_free(buf, len); 298 return (error); 299 } 300 301 int 302 hds_preferred(struct hds_softc *sc, int *preferred) 303 { 304 struct scsi_link *link = sc->sc_path.p_link; 305 struct hds_vpd *pg; 306 int error; 307 308 if (sc->sc_mode == HDS_SYMMETRIC) { 309 *preferred = 1; 310 return (0); 311 } 312 313 pg = dma_alloc(sizeof(*pg), PR_WAITOK); 314 315 error = scsi_inquire_vpd(link, pg, sizeof(*pg), HDS_VPD, scsi_autoconf); 316 if (error) 317 goto done; 318 319 if (_2btol(pg->hdr.page_length) < sizeof(pg->state) || 320 !ISSET(pg->state, HDS_VPD_VALID)) { 321 error = ENXIO; 322 goto done; 323 } 324 325 *preferred = ISSET(pg->state, HDS_VPD_PREFERRED); 326 327 done: 328 dma_free(pg, sizeof(*pg)); 329 return (error); 330 } 331