xref: /openbsd-src/sys/scsi/mpath_rdac.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: mpath_rdac.c,v 1.23 2015/03/14 03:38:52 jsg 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_inqbuf;
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 	int rv = 0;
249 
250 	switch (act) {
251 	case DVACT_DEACTIVATE:
252 		if (scsi_xsh_del(&sc->sc_xsh))
253 			mpath_path_status(&sc->sc_path, MPATH_S_UNKNOWN);
254 		if (sc->sc_path.p_group != NULL)
255 			mpath_path_detach(&sc->sc_path);
256 		break;
257 	}
258 	return (rv);
259 }
260 
261 void
262 rdac_mpath_start(struct scsi_xfer *xs)
263 {
264 	struct rdac_softc *sc = xs->sc_link->device_softc;
265 
266 	mpath_start(&sc->sc_path, xs);
267 }
268 
269 int
270 rdac_mpath_checksense(struct scsi_xfer *xs)
271 {
272 	struct scsi_sense_data *sense = &xs->sense;
273 	u_int8_t skey;
274 
275 	if ((sense->error_code & SSD_ERRCODE) != SSD_ERRCODE_CURRENT)
276 		return (MPATH_SENSE_DECLINED);
277 
278 	skey = sense->flags & SSD_KEY;
279 
280 	/* i wish i knew what the magic numbers meant */
281 
282 	/* invalid request due to current lu ownership */
283 	if (skey == SKEY_ILLEGAL_REQUEST && ASC_ASCQ(sense) == 0x9401)
284 		return (MPATH_SENSE_FAILOVER);
285 
286 	if (skey == SKEY_UNIT_ATTENTION && ASC_ASCQ(sense) == 0x8b02)
287 		return (MPATH_SENSE_FAILOVER);
288 
289 	return (MPATH_SENSE_DECLINED);
290 }
291 
292 void
293 rdac_mpath_status(struct scsi_link *link)
294 {
295 	struct rdac_softc *sc = link->device_softc;
296 
297 	scsi_xsh_add(&sc->sc_xsh);
298 }
299 
300 void
301 rdac_status(struct scsi_xfer *xs)
302 {
303 	struct scsi_link *link = xs->sc_link;
304 	struct rdac_softc *sc = link->device_softc;
305 
306 	scsi_init_inquiry(xs, SI_EVPD, RDAC_VPD_VOLACCESSCTL,
307 	    sc->sc_pg, sizeof(*sc->sc_pg));
308 
309 	xs->done = rdac_status_done;
310 
311 	scsi_xs_exec(xs);
312 }
313 
314 void
315 rdac_status_done(struct scsi_xfer *xs)
316 {
317 	struct scsi_link *link = xs->sc_link;
318 	struct rdac_softc *sc = link->device_softc;
319 	struct rdac_vpd_volaccessctl *pg = sc->sc_pg;
320 	int status = MPATH_S_UNKNOWN;
321 
322 	if (xs->error == XS_NOERROR &&
323 	    _4btol(pg->pg_id) == RDAC_VPD_ID_VOLACCESSCTL) {
324 		status = (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT) ||
325 		    ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) ?
326 		    MPATH_S_ACTIVE : MPATH_S_PASSIVE;
327 	}
328 
329 	scsi_xs_put(xs);
330 	mpath_path_status(&sc->sc_path, status);
331 }
332 
333 int
334 rdac_groupid(struct rdac_softc *sc)
335 {
336 	struct rdac_vpd_subsys *pg;
337 	int rv = -1;
338 
339 	pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
340 
341 	if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
342 	    RDAC_VPD_SUBSYS, scsi_autoconf) != 0) {
343 		printf("%s: unable to fetch subsys vpd page\n", DEVNAME(sc));
344 		goto done;
345 	}
346 
347 	if (_4btol(pg->pg_id) != RDAC_VPD_ID_SUBSYS) {
348 		printf("%s: subsys page is invalid\n", DEVNAME(sc));
349 		goto done;
350 	}
351 
352 	rv = _2btol(pg->controller_slot_id);
353 
354 done:
355 	dma_free(pg, sizeof(*pg));
356 	return (rv);
357 }
358 
359 int
360 rdac_extdevid(struct rdac_softc *sc)
361 {
362 	struct rdac_vpd_extdevid *pg;
363 	char array[31];
364 	char vol[31];
365 	int i;
366 	int rv = 1;
367 
368 	pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
369 
370 	if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
371 	    RDAC_VPD_EXTDEVID, scsi_autoconf) != 0) {
372 		printf("%s: unable to fetch extdevid vpd page\n", DEVNAME(sc));
373 		goto done;
374 	}
375 
376 	if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) {
377 		printf("%s: extdevid page is invalid\n", DEVNAME(sc));
378 		goto done;
379 	}
380 
381 	memset(array, 0, sizeof(array));
382 	for (i = 0; i < sizeof(pg->array_label) / 2; i++)
383 		array[i] = pg->array_label[i * 2 + 1];
384 
385 	memset(vol, 0, sizeof(vol));
386 	for (i = 0; i < sizeof(pg->vol_label) / 2; i++)
387 		vol[i] = pg->vol_label[i * 2 + 1];
388 
389 	printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol);
390 
391 	rv = 0;
392 done:
393 	dma_free(pg, sizeof(*pg));
394 	return (rv);
395 }
396