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
45 struct 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
57 struct 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
67 int hds_match(struct device *, void *, void *);
68 void hds_attach(struct device *, struct device *, void *);
69 int hds_detach(struct device *, int);
70 int hds_activate(struct device *, int);
71
72 const struct cfattach hds_ca = {
73 sizeof(struct hds_softc),
74 hds_match,
75 hds_attach,
76 hds_detach,
77 hds_activate
78 };
79
80 struct cfdriver hds_cd = {
81 NULL,
82 "hds",
83 DV_DULL
84 };
85
86 void hds_mpath_start(struct scsi_xfer *);
87 int hds_mpath_checksense(struct scsi_xfer *);
88 void hds_mpath_status(struct scsi_link *);
89
90 const struct mpath_ops hds_mpath_ops = {
91 "hds",
92 hds_mpath_checksense,
93 hds_mpath_status
94 };
95
96 struct hds_device {
97 char *vendor;
98 char *product;
99 };
100
101 int hds_inquiry(struct scsi_link *, int *);
102 int hds_info(struct hds_softc *);
103
104 void hds_status(struct scsi_xfer *);
105 void hds_status_done(struct scsi_xfer *);
106
107 struct hds_device hds_devices[] = {
108 /* " vendor " " device " */
109 /* "01234567" "0123456789012345" */
110 { "HITACHI ", "DF600F " },
111 { "HITACHI ", "DF600F-CM " }
112 };
113
114 int
hds_match(struct device * parent,void * match,void * aux)115 hds_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
138 void
hds_attach(struct device * parent,struct device * self,void * aux)139 hds_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
174 int
hds_detach(struct device * self,int flags)175 hds_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
184 int
hds_activate(struct device * self,int act)185 hds_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
198 void
hds_mpath_start(struct scsi_xfer * xs)199 hds_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
206 int
hds_mpath_checksense(struct scsi_xfer * xs)207 hds_mpath_checksense(struct scsi_xfer *xs)
208 {
209 return (MPATH_SENSE_DECLINED);
210 }
211
212 void
hds_mpath_status(struct scsi_link * link)213 hds_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
223 void
hds_status(struct scsi_xfer * xs)224 hds_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
237 void
hds_status_done(struct scsi_xfer * xs)238 hds_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
257 int
hds_inquiry(struct scsi_link * link,int * mode)258 hds_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
286 done:
287 dma_free(buf, len);
288 return (error);
289 }
290
291 int
hds_info(struct hds_softc * sc)292 hds_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
326 done:
327 dma_free(buf, len);
328 return (error);
329 }
330