xref: /openbsd/sys/scsi/mpath_rdac.c (revision 4cfece93)
1 /*	$OpenBSD: mpath_rdac.c,v 1.25 2020/06/30 18:43:37 krw Exp $ */
2 
3 /*
4  * Copyright (c) 2010 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 /* Redundant Disk Array Controller 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 #include <sys/poll.h>
32 #include <sys/selinfo.h>
33 
34 #include <scsi/scsi_all.h>
35 #include <scsi/scsiconf.h>
36 #include <scsi/mpathvar.h>
37 
38 struct rdac_common_mode_page {
39 	u_int8_t	controller_serial[16];
40 	u_int8_t	alt_controller_serial[16];
41 	u_int8_t	mode[2];
42 	u_int8_t	alt_mode[2];
43 	u_int8_t	timeout;
44 	u_int8_t	options;
45 };
46 
47 /*
48  * RDAC VPD pages
49  */
50 #define RDAC_VPD_HDWVER		0xc0	/* Hardware Version */
51 #define RDAC_VPD_SERNUM		0xc1	/* Serial Numbers */
52 #define RDAC_VPD_SFWVER		0xc2
53 #define RDAC_VPD_FEAPAR		0xc3	/* Feature Parameters */
54 #define RDAC_VPD_SUBSYS		0xc4
55 #define RDAC_VPD_HSTINT		0xc5
56 #define RDAC_VPD_DGM		0xc6
57 #define RDAC_VPD_HSTINT2	0xc7
58 #define RDAC_VPD_EXTDEVID	0xc8
59 #define RDAC_VPD_VOLACCESSCTL	0xc9
60 
61 struct rdac_vpd_hdwver {
62 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_HDWVER */
63 	u_int8_t		pg_id[4];
64 #define RDAC_VPD_ID_HDWVER		0x68777234 /* "hwr4" */
65 	u_int8_t		num_channels;
66 	u_int8_t		flags;
67 	u_int8_t		proc_memory_size;
68 	u_int8_t		_reserved1[5];
69 	u_int8_t		board_name[64];
70 	u_int8_t		board_part_number[16];
71 	u_int8_t		schematic_number[12];
72 	u_int8_t		schematic_revision[4];
73 	u_int8_t		serial_number[16];
74 	u_int8_t		_reserved2[16];
75 	u_int8_t		date_manufactured[8];
76 	u_int8_t		board_revision[2];
77 	u_int8_t		board_identifier[4];
78 };
79 
80 struct rdac_vpd_subsys {
81 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_SUBSYS */
82 	u_int8_t		pg_id[4];
83 #define RDAC_VPD_ID_SUBSYS		0x73756273 /* "subs" */
84 	u_int8_t		subsystem_id[16];
85 	u_int8_t		subsystem_revision[4];
86 	u_int8_t		controller_slot_id[2];
87 	u_int8_t		_reserved[2];
88 };
89 
90 struct rdac_vpd_extdevid {
91 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_EXTDEVID */
92 	u_int8_t		pg_id[4];
93 #define RDAC_VPD_ID_EXTDEVID		0x65646964 /* "edid" */
94 	u_int8_t		_reserved[3];
95 	u_int8_t		vol_id_len;
96 	u_int8_t		vol_id[16];
97 	u_int8_t		vol_label_len;
98 	u_int8_t		vol_label[60];
99 	u_int8_t		array_id_len;
100 	u_int8_t		array_id[16];
101 	u_int8_t		array_label_len;
102 	u_int8_t		array_label[60];
103 	u_int8_t		lun[8];
104 };
105 
106 struct rdac_vpd_volaccessctl {
107 	struct scsi_vpd_hdr	hdr; /* RDAC_VPD_VOLACCESSCTL */
108 	u_int8_t		pg_id[4];
109 #define RDAC_VPD_ID_VOLACCESSCTL	0x76616331 /* "vac1" */
110 	u_int8_t		avtcvp;
111 #define RDAC_VOLACCESSCTL_OWNER		0x01
112 #define RDAC_VOLACCESSCTL_AVT		0x70
113 	u_int8_t		_reserved1;
114 	u_int8_t		asym_access_state_cur;
115 	u_int8_t		vendor_specific_cur;
116 	u_int8_t		_reserved[36];
117 };
118 
119 struct rdac_softc {
120 	struct device		sc_dev;
121 	struct mpath_path	sc_path;
122 	struct scsi_xshandler	sc_xsh;
123 	struct rdac_vpd_volaccessctl *sc_pg;
124 };
125 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
126 
127 int		rdac_match(struct device *, void *, void *);
128 void		rdac_attach(struct device *, struct device *, void *);
129 int		rdac_detach(struct device *, int);
130 int		rdac_activate(struct device *, int);
131 
132 struct cfattach rdac_ca = {
133 	sizeof(struct rdac_softc),
134 	rdac_match,
135 	rdac_attach,
136 	rdac_detach,
137 	rdac_activate
138 };
139 
140 struct cfdriver rdac_cd = {
141 	NULL,
142 	"rdac",
143 	DV_DULL
144 };
145 
146 void		rdac_mpath_start(struct scsi_xfer *);
147 int		rdac_mpath_checksense(struct scsi_xfer *);
148 void		rdac_mpath_status(struct scsi_link *);
149 
150 const struct mpath_ops rdac_mpath_ops = {
151 	"rdac",
152 	rdac_mpath_checksense,
153 	rdac_mpath_status
154 };
155 
156 int		rdac_extdevid(struct rdac_softc *);
157 int		rdac_groupid(struct rdac_softc *);
158 
159 void		rdac_status(struct scsi_xfer *);
160 void		rdac_status_done(struct scsi_xfer *);
161 
162 struct rdac_device {
163 	char *vendor;
164 	char *product;
165 };
166 
167 struct rdac_device rdac_devices[] = {
168 /*	  " vendor "  "     device     " */
169 /*	  "01234567"  "0123456789012345" */
170 	{ "SUN     ", "CSM200_" },
171 	{ "DELL    ", "MD3000          " },
172 	{ "DELL    ", "MD3000i         " },
173 	{ "DELL    ", "MD32xx          " },
174 	{ "DELL    ", "MD32xxi         " }
175 };
176 
177 int
178 rdac_match(struct device *parent, void *match, void *aux)
179 {
180 	struct scsi_attach_args *sa = aux;
181 	struct scsi_inquiry_data *inq = &sa->sa_sc_link->inqdata;
182 	struct rdac_device *s;
183 	int i;
184 
185 	if (mpath_path_probe(sa->sa_sc_link) != 0)
186 		return (0);
187 
188 	for (i = 0; i < nitems(rdac_devices); i++) {
189 		s = &rdac_devices[i];
190 
191 		if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 &&
192 		    bcmp(s->product, inq->product, strlen(s->product)) == 0)
193 			return (8);
194 	}
195 
196 	return (0);
197 }
198 
199 void
200 rdac_attach(struct device *parent, struct device *self, void *aux)
201 {
202 	struct rdac_softc *sc = (struct rdac_softc *)self;
203 	struct scsi_attach_args *sa = aux;
204 	struct scsi_link *link = sa->sa_sc_link;
205 	int id;
206 
207 	printf("\n");
208 
209 	/* init link */
210 	link->device_softc = sc;
211 
212 	/* init path */
213 	scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start);
214 	sc->sc_path.p_link = link;
215 
216 	/* init status handler */
217 	scsi_xsh_set(&sc->sc_xsh, link, rdac_status);
218 	sc->sc_pg = dma_alloc(sizeof(*sc->sc_pg), PR_WAITOK);
219 
220 	/* let's go */
221 	if (rdac_extdevid(sc) != 0)
222 		return;
223 
224 	id = rdac_groupid(sc);
225 	if (id == -1) {
226 		/* error printed by rdac_groupid */
227 		return;
228 	}
229 
230 	if (mpath_path_attach(&sc->sc_path, id, &rdac_mpath_ops) != 0)
231 		printf("%s: unable to attach path\n", DEVNAME(sc));
232 }
233 
234 int
235 rdac_detach(struct device *self, int flags)
236 {
237 	struct rdac_softc *sc = (struct rdac_softc *)self;
238 
239 	dma_free(sc->sc_pg, sizeof(*sc->sc_pg));
240 
241 	return (0);
242 }
243 
244 int
245 rdac_activate(struct device *self, int act)
246 {
247 	struct rdac_softc *sc = (struct rdac_softc *)self;
248 
249 	switch (act) {
250 	case DVACT_DEACTIVATE:
251 		if (scsi_xsh_del(&sc->sc_xsh))
252 			mpath_path_status(&sc->sc_path, MPATH_S_UNKNOWN);
253 		if (sc->sc_path.p_group != NULL)
254 			mpath_path_detach(&sc->sc_path);
255 		break;
256 	}
257 	return (0);
258 }
259 
260 void
261 rdac_mpath_start(struct scsi_xfer *xs)
262 {
263 	struct rdac_softc *sc = xs->sc_link->device_softc;
264 
265 	mpath_start(&sc->sc_path, xs);
266 }
267 
268 int
269 rdac_mpath_checksense(struct scsi_xfer *xs)
270 {
271 	struct scsi_sense_data *sense = &xs->sense;
272 	u_int8_t skey;
273 
274 	if ((sense->error_code & SSD_ERRCODE) != SSD_ERRCODE_CURRENT)
275 		return (MPATH_SENSE_DECLINED);
276 
277 	skey = sense->flags & SSD_KEY;
278 
279 	/* i wish i knew what the magic numbers meant */
280 
281 	/* invalid request due to current lu ownership */
282 	if (skey == SKEY_ILLEGAL_REQUEST && ASC_ASCQ(sense) == 0x9401)
283 		return (MPATH_SENSE_FAILOVER);
284 
285 	if (skey == SKEY_UNIT_ATTENTION && ASC_ASCQ(sense) == 0x8b02)
286 		return (MPATH_SENSE_FAILOVER);
287 
288 	return (MPATH_SENSE_DECLINED);
289 }
290 
291 void
292 rdac_mpath_status(struct scsi_link *link)
293 {
294 	struct rdac_softc *sc = link->device_softc;
295 
296 	scsi_xsh_add(&sc->sc_xsh);
297 }
298 
299 void
300 rdac_status(struct scsi_xfer *xs)
301 {
302 	struct scsi_link *link = xs->sc_link;
303 	struct rdac_softc *sc = link->device_softc;
304 
305 	scsi_init_inquiry(xs, SI_EVPD, RDAC_VPD_VOLACCESSCTL,
306 	    sc->sc_pg, sizeof(*sc->sc_pg));
307 
308 	xs->done = rdac_status_done;
309 
310 	scsi_xs_exec(xs);
311 }
312 
313 void
314 rdac_status_done(struct scsi_xfer *xs)
315 {
316 	struct scsi_link *link = xs->sc_link;
317 	struct rdac_softc *sc = link->device_softc;
318 	struct rdac_vpd_volaccessctl *pg = sc->sc_pg;
319 	int status = MPATH_S_UNKNOWN;
320 
321 	if (xs->error == XS_NOERROR &&
322 	    _4btol(pg->pg_id) == RDAC_VPD_ID_VOLACCESSCTL) {
323 		status = (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT) ||
324 		    ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) ?
325 		    MPATH_S_ACTIVE : MPATH_S_PASSIVE;
326 	}
327 
328 	scsi_xs_put(xs);
329 	mpath_path_status(&sc->sc_path, status);
330 }
331 
332 int
333 rdac_groupid(struct rdac_softc *sc)
334 {
335 	struct rdac_vpd_subsys *pg;
336 	int rv = -1;
337 
338 	pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
339 
340 	if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
341 	    RDAC_VPD_SUBSYS, scsi_autoconf) != 0) {
342 		printf("%s: unable to fetch subsys vpd page\n", DEVNAME(sc));
343 		goto done;
344 	}
345 
346 	if (_4btol(pg->pg_id) != RDAC_VPD_ID_SUBSYS) {
347 		printf("%s: subsys page is invalid\n", DEVNAME(sc));
348 		goto done;
349 	}
350 
351 	rv = _2btol(pg->controller_slot_id);
352 
353 done:
354 	dma_free(pg, sizeof(*pg));
355 	return (rv);
356 }
357 
358 int
359 rdac_extdevid(struct rdac_softc *sc)
360 {
361 	struct rdac_vpd_extdevid *pg;
362 	char array[31];
363 	char vol[31];
364 	int i;
365 	int rv = 1;
366 
367 	pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
368 
369 	if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
370 	    RDAC_VPD_EXTDEVID, scsi_autoconf) != 0) {
371 		printf("%s: unable to fetch extdevid vpd page\n", DEVNAME(sc));
372 		goto done;
373 	}
374 
375 	if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) {
376 		printf("%s: extdevid page is invalid\n", DEVNAME(sc));
377 		goto done;
378 	}
379 
380 	memset(array, 0, sizeof(array));
381 	for (i = 0; i < sizeof(pg->array_label) / 2; i++)
382 		array[i] = pg->array_label[i * 2 + 1];
383 
384 	memset(vol, 0, sizeof(vol));
385 	for (i = 0; i < sizeof(pg->vol_label) / 2; i++)
386 		vol[i] = pg->vol_label[i * 2 + 1];
387 
388 	printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol);
389 
390 	rv = 0;
391 done:
392 	dma_free(pg, sizeof(*pg));
393 	return (rv);
394 }
395