Line data Source code
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 0 : rdac_match(struct device *parent, void *match, void *aux)
179 : {
180 0 : struct scsi_attach_args *sa = aux;
181 0 : struct scsi_inquiry_data *inq = sa->sa_inqbuf;
182 : struct rdac_device *s;
183 : int i;
184 :
185 0 : if (mpath_path_probe(sa->sa_sc_link) != 0)
186 0 : return (0);
187 :
188 0 : for (i = 0; i < nitems(rdac_devices); i++) {
189 0 : s = &rdac_devices[i];
190 :
191 0 : if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 &&
192 0 : bcmp(s->product, inq->product, strlen(s->product)) == 0)
193 0 : return (8);
194 : }
195 :
196 0 : return (0);
197 0 : }
198 :
199 : void
200 0 : rdac_attach(struct device *parent, struct device *self, void *aux)
201 : {
202 0 : struct rdac_softc *sc = (struct rdac_softc *)self;
203 0 : struct scsi_attach_args *sa = aux;
204 0 : struct scsi_link *link = sa->sa_sc_link;
205 : int id;
206 :
207 0 : printf("\n");
208 :
209 : /* init link */
210 0 : link->device_softc = sc;
211 :
212 : /* init path */
213 0 : scsi_xsh_set(&sc->sc_path.p_xsh, link, rdac_mpath_start);
214 0 : sc->sc_path.p_link = link;
215 :
216 : /* init status handler */
217 0 : scsi_xsh_set(&sc->sc_xsh, link, rdac_status);
218 0 : sc->sc_pg = dma_alloc(sizeof(*sc->sc_pg), PR_WAITOK);
219 :
220 : /* let's go */
221 0 : if (rdac_extdevid(sc) != 0)
222 0 : return;
223 :
224 0 : id = rdac_groupid(sc);
225 0 : if (id == -1) {
226 : /* error printed by rdac_groupid */
227 0 : return;
228 : }
229 :
230 0 : if (mpath_path_attach(&sc->sc_path, id, &rdac_mpath_ops) != 0)
231 0 : printf("%s: unable to attach path\n", DEVNAME(sc));
232 0 : }
233 :
234 : int
235 0 : rdac_detach(struct device *self, int flags)
236 : {
237 0 : struct rdac_softc *sc = (struct rdac_softc *)self;
238 :
239 0 : dma_free(sc->sc_pg, sizeof(*sc->sc_pg));
240 :
241 0 : return (0);
242 : }
243 :
244 : int
245 0 : rdac_activate(struct device *self, int act)
246 : {
247 0 : struct rdac_softc *sc = (struct rdac_softc *)self;
248 : int rv = 0;
249 :
250 0 : switch (act) {
251 : case DVACT_DEACTIVATE:
252 0 : if (scsi_xsh_del(&sc->sc_xsh))
253 0 : mpath_path_status(&sc->sc_path, MPATH_S_UNKNOWN);
254 0 : if (sc->sc_path.p_group != NULL)
255 0 : mpath_path_detach(&sc->sc_path);
256 : break;
257 : }
258 0 : return (rv);
259 : }
260 :
261 : void
262 0 : rdac_mpath_start(struct scsi_xfer *xs)
263 : {
264 0 : struct rdac_softc *sc = xs->sc_link->device_softc;
265 :
266 0 : mpath_start(&sc->sc_path, xs);
267 0 : }
268 :
269 : int
270 0 : rdac_mpath_checksense(struct scsi_xfer *xs)
271 : {
272 0 : struct scsi_sense_data *sense = &xs->sense;
273 : u_int8_t skey;
274 :
275 0 : if ((sense->error_code & SSD_ERRCODE) != SSD_ERRCODE_CURRENT)
276 0 : return (MPATH_SENSE_DECLINED);
277 :
278 0 : 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 0 : if (skey == SKEY_ILLEGAL_REQUEST && ASC_ASCQ(sense) == 0x9401)
284 0 : return (MPATH_SENSE_FAILOVER);
285 :
286 0 : if (skey == SKEY_UNIT_ATTENTION && ASC_ASCQ(sense) == 0x8b02)
287 0 : return (MPATH_SENSE_FAILOVER);
288 :
289 0 : return (MPATH_SENSE_DECLINED);
290 0 : }
291 :
292 : void
293 0 : rdac_mpath_status(struct scsi_link *link)
294 : {
295 0 : struct rdac_softc *sc = link->device_softc;
296 :
297 0 : scsi_xsh_add(&sc->sc_xsh);
298 0 : }
299 :
300 : void
301 0 : rdac_status(struct scsi_xfer *xs)
302 : {
303 0 : struct scsi_link *link = xs->sc_link;
304 0 : struct rdac_softc *sc = link->device_softc;
305 :
306 0 : scsi_init_inquiry(xs, SI_EVPD, RDAC_VPD_VOLACCESSCTL,
307 0 : sc->sc_pg, sizeof(*sc->sc_pg));
308 :
309 0 : xs->done = rdac_status_done;
310 :
311 0 : scsi_xs_exec(xs);
312 0 : }
313 :
314 : void
315 0 : rdac_status_done(struct scsi_xfer *xs)
316 : {
317 0 : struct scsi_link *link = xs->sc_link;
318 0 : struct rdac_softc *sc = link->device_softc;
319 0 : struct rdac_vpd_volaccessctl *pg = sc->sc_pg;
320 : int status = MPATH_S_UNKNOWN;
321 :
322 0 : if (xs->error == XS_NOERROR &&
323 0 : _4btol(pg->pg_id) == RDAC_VPD_ID_VOLACCESSCTL) {
324 0 : status = (ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_AVT) ||
325 0 : ISSET(pg->avtcvp, RDAC_VOLACCESSCTL_OWNER)) ?
326 : MPATH_S_ACTIVE : MPATH_S_PASSIVE;
327 0 : }
328 :
329 0 : scsi_xs_put(xs);
330 0 : mpath_path_status(&sc->sc_path, status);
331 0 : }
332 :
333 : int
334 0 : rdac_groupid(struct rdac_softc *sc)
335 : {
336 : struct rdac_vpd_subsys *pg;
337 : int rv = -1;
338 :
339 0 : pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
340 :
341 0 : if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
342 0 : RDAC_VPD_SUBSYS, scsi_autoconf) != 0) {
343 0 : printf("%s: unable to fetch subsys vpd page\n", DEVNAME(sc));
344 0 : goto done;
345 : }
346 :
347 0 : if (_4btol(pg->pg_id) != RDAC_VPD_ID_SUBSYS) {
348 0 : printf("%s: subsys page is invalid\n", DEVNAME(sc));
349 0 : goto done;
350 : }
351 :
352 0 : rv = _2btol(pg->controller_slot_id);
353 :
354 : done:
355 0 : dma_free(pg, sizeof(*pg));
356 0 : return (rv);
357 : }
358 :
359 : int
360 0 : rdac_extdevid(struct rdac_softc *sc)
361 : {
362 : struct rdac_vpd_extdevid *pg;
363 0 : char array[31];
364 0 : char vol[31];
365 : int i;
366 : int rv = 1;
367 :
368 0 : pg = dma_alloc(sizeof(*pg), PR_WAITOK | PR_ZERO);
369 :
370 0 : if (scsi_inquire_vpd(sc->sc_path.p_link, pg, sizeof(*pg),
371 0 : RDAC_VPD_EXTDEVID, scsi_autoconf) != 0) {
372 0 : printf("%s: unable to fetch extdevid vpd page\n", DEVNAME(sc));
373 0 : goto done;
374 : }
375 :
376 0 : if (_4btol(pg->pg_id) != RDAC_VPD_ID_EXTDEVID) {
377 0 : printf("%s: extdevid page is invalid\n", DEVNAME(sc));
378 0 : goto done;
379 : }
380 :
381 0 : memset(array, 0, sizeof(array));
382 0 : for (i = 0; i < sizeof(pg->array_label) / 2; i++)
383 0 : array[i] = pg->array_label[i * 2 + 1];
384 :
385 0 : memset(vol, 0, sizeof(vol));
386 0 : for (i = 0; i < sizeof(pg->vol_label) / 2; i++)
387 0 : vol[i] = pg->vol_label[i * 2 + 1];
388 :
389 0 : printf("%s: array %s, volume %s\n", DEVNAME(sc), array, vol);
390 :
391 0 : rv = 0;
392 : done:
393 0 : dma_free(pg, sizeof(*pg));
394 0 : return (rv);
395 0 : }
|