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