1 | /* $NetBSD: ld_ataraid.c,v 1.44 2016/09/27 08:05:34 pgoyette Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2003 Wasabi Systems, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Written by Jason R. Thorpe for Wasabi Systems, Inc. |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. |
17 | * 3. All advertising materials mentioning features or use of this software |
18 | * must display the following acknowledgement: |
19 | * This product includes software developed for the NetBSD Project by |
20 | * Wasabi Systems, Inc. |
21 | * 4. The name of Wasabi Systems, Inc. may not be used to endorse |
22 | * or promote products derived from this software without specific prior |
23 | * written permission. |
24 | * |
25 | * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND |
26 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
27 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
28 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC |
29 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
33 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
35 | * POSSIBILITY OF SUCH DAMAGE. |
36 | */ |
37 | |
38 | /* |
39 | * Support for ATA RAID logical disks. |
40 | * |
41 | * Note that all the RAID happens in software here; the ATA RAID |
42 | * controllers we're dealing with (Promise, etc.) only support |
43 | * configuration data on the component disks, with the BIOS supporting |
44 | * booting from the RAID volumes. |
45 | * |
46 | * bio(4) support was written by Juan Romero Pardines <xtraeme@gmail.com>. |
47 | */ |
48 | |
49 | #include <sys/cdefs.h> |
50 | __KERNEL_RCSID(0, "$NetBSD: ld_ataraid.c,v 1.44 2016/09/27 08:05:34 pgoyette Exp $" ); |
51 | |
52 | #if defined(_KERNEL_OPT) |
53 | #include "bio.h" |
54 | #endif |
55 | |
56 | #include <sys/param.h> |
57 | #include <sys/systm.h> |
58 | #include <sys/conf.h> |
59 | #include <sys/kernel.h> |
60 | #include <sys/device.h> |
61 | #include <sys/buf.h> |
62 | #include <sys/bufq.h> |
63 | #include <sys/dkio.h> |
64 | #include <sys/disk.h> |
65 | #include <sys/disklabel.h> |
66 | #include <sys/fcntl.h> |
67 | #include <sys/malloc.h> |
68 | #include <sys/vnode.h> |
69 | #include <sys/kauth.h> |
70 | #include <sys/module.h> |
71 | #if NBIO > 0 |
72 | #include <dev/ata/atavar.h> |
73 | #include <dev/ata/atareg.h> |
74 | #include <dev/ata/wdvar.h> |
75 | #include <dev/biovar.h> |
76 | #include <dev/scsipi/scsipiconf.h> /* for scsipi_strvis() */ |
77 | #endif |
78 | |
79 | #include <miscfs/specfs/specdev.h> |
80 | |
81 | #include <dev/ldvar.h> |
82 | |
83 | #include <dev/ata/ata_raidvar.h> |
84 | |
85 | #include "ioconf.h" |
86 | |
87 | struct ld_ataraid_softc { |
88 | struct ld_softc sc_ld; |
89 | |
90 | struct ataraid_array_info *sc_aai; |
91 | struct vnode *sc_vnodes[ATA_RAID_MAX_DISKS]; |
92 | |
93 | void (*sc_iodone)(struct buf *); |
94 | |
95 | pool_cache_t sc_cbufpool; |
96 | |
97 | SIMPLEQ_HEAD(, cbuf) sc_cbufq; |
98 | |
99 | void *sc_sih_cookie; |
100 | }; |
101 | |
102 | static int ld_ataraid_match(device_t, cfdata_t, void *); |
103 | static void ld_ataraid_attach(device_t, device_t, void *); |
104 | |
105 | static int ld_ataraid_dump(struct ld_softc *, void *, int, int); |
106 | |
107 | static int cbufpool_ctor(void *, void *, int); |
108 | static void cbufpool_dtor(void *, void *); |
109 | |
110 | static void ld_ataraid_start_vstrategy(void *); |
111 | static int ld_ataraid_start_span(struct ld_softc *, struct buf *); |
112 | |
113 | static int ld_ataraid_start_raid0(struct ld_softc *, struct buf *); |
114 | static void ld_ataraid_iodone_raid0(struct buf *); |
115 | |
116 | #if NBIO > 0 |
117 | static int ld_ataraid_bioctl(device_t, u_long, void *); |
118 | static int ld_ataraid_bioinq(struct ld_ataraid_softc *, struct bioc_inq *); |
119 | static int ld_ataraid_biovol(struct ld_ataraid_softc *, struct bioc_vol *); |
120 | static int ld_ataraid_biodisk(struct ld_ataraid_softc *, |
121 | struct bioc_disk *); |
122 | #endif |
123 | |
124 | CFATTACH_DECL_NEW(ld_ataraid, sizeof(struct ld_ataraid_softc), |
125 | ld_ataraid_match, ld_ataraid_attach, NULL, NULL); |
126 | |
127 | struct cbuf { |
128 | struct buf cb_buf; /* new I/O buf */ |
129 | struct buf *cb_obp; /* ptr. to original I/O buf */ |
130 | struct ld_ataraid_softc *cb_sc; /* pointer to ld softc */ |
131 | u_int cb_comp; /* target component */ |
132 | SIMPLEQ_ENTRY(cbuf) cb_q; /* fifo of component buffers */ |
133 | struct cbuf *cb_other; /* other cbuf in case of mirror */ |
134 | int cb_flags; |
135 | #define CBUF_IODONE 0x00000001 /* I/O is already successfully done */ |
136 | }; |
137 | |
138 | #define CBUF_GET() pool_cache_get(sc->sc_cbufpool, PR_NOWAIT); |
139 | #define CBUF_PUT(cbp) pool_cache_put(sc->sc_cbufpool, (cbp)) |
140 | |
141 | static int |
142 | ld_ataraid_match(device_t parent, cfdata_t match, void *aux) |
143 | { |
144 | |
145 | return (1); |
146 | } |
147 | |
148 | static void |
149 | ld_ataraid_attach(device_t parent, device_t self, void *aux) |
150 | { |
151 | struct ld_ataraid_softc *sc = device_private(self); |
152 | struct ld_softc *ld = &sc->sc_ld; |
153 | struct ataraid_array_info *aai = aux; |
154 | struct ataraid_disk_info *adi = NULL; |
155 | const char *level; |
156 | struct vnode *vp; |
157 | char unklev[32]; |
158 | u_int i; |
159 | |
160 | ld->sc_dv = self; |
161 | |
162 | sc->sc_cbufpool = pool_cache_init(sizeof(struct cbuf), 0, |
163 | 0, 0, "ldcbuf" , NULL, IPL_BIO, cbufpool_ctor, cbufpool_dtor, sc); |
164 | sc->sc_sih_cookie = softint_establish(SOFTINT_BIO, |
165 | ld_ataraid_start_vstrategy, sc); |
166 | |
167 | sc->sc_aai = aai; /* this data persists */ |
168 | |
169 | ld->sc_maxxfer = MAXPHYS * aai->aai_width; /* XXX */ |
170 | ld->sc_secperunit = aai->aai_capacity; |
171 | ld->sc_secsize = 512; /* XXX */ |
172 | ld->sc_maxqueuecnt = 128; /* XXX */ |
173 | ld->sc_dump = ld_ataraid_dump; |
174 | |
175 | switch (aai->aai_level) { |
176 | case AAI_L_SPAN: |
177 | level = "SPAN" ; |
178 | ld->sc_start = ld_ataraid_start_span; |
179 | sc->sc_iodone = ld_ataraid_iodone_raid0; |
180 | break; |
181 | |
182 | case AAI_L_RAID0: |
183 | level = "RAID-0" ; |
184 | ld->sc_start = ld_ataraid_start_raid0; |
185 | sc->sc_iodone = ld_ataraid_iodone_raid0; |
186 | break; |
187 | |
188 | case AAI_L_RAID1: |
189 | level = "RAID-1" ; |
190 | ld->sc_start = ld_ataraid_start_raid0; |
191 | sc->sc_iodone = ld_ataraid_iodone_raid0; |
192 | break; |
193 | |
194 | case AAI_L_RAID0 | AAI_L_RAID1: |
195 | level = "RAID-10" ; |
196 | ld->sc_start = ld_ataraid_start_raid0; |
197 | sc->sc_iodone = ld_ataraid_iodone_raid0; |
198 | break; |
199 | |
200 | default: |
201 | snprintf(unklev, sizeof(unklev), "<unknown level 0x%x>" , |
202 | aai->aai_level); |
203 | level = unklev; |
204 | } |
205 | |
206 | aprint_naive(": ATA %s array\n" , level); |
207 | aprint_normal(": %s ATA %s array\n" , |
208 | ata_raid_type_name(aai->aai_type), level); |
209 | |
210 | if (ld->sc_start == NULL) { |
211 | aprint_error_dev(ld->sc_dv, "unsupported array type\n" ); |
212 | return; |
213 | } |
214 | |
215 | /* |
216 | * We get a geometry from the device; use it. |
217 | */ |
218 | ld->sc_nheads = aai->aai_heads; |
219 | ld->sc_nsectors = aai->aai_sectors; |
220 | ld->sc_ncylinders = aai->aai_cylinders; |
221 | |
222 | /* |
223 | * Configure all the component disks. |
224 | */ |
225 | for (i = 0; i < aai->aai_ndisks; i++) { |
226 | adi = &aai->aai_disks[i]; |
227 | vp = ata_raid_disk_vnode_find(adi); |
228 | if (vp == NULL) { |
229 | /* |
230 | * XXX This is bogus. We should just mark the |
231 | * XXX component as FAILED, and write-back new |
232 | * XXX config blocks. |
233 | */ |
234 | break; |
235 | } |
236 | sc->sc_vnodes[i] = vp; |
237 | } |
238 | if (i == aai->aai_ndisks) { |
239 | ld->sc_flags = LDF_ENABLED; |
240 | goto finish; |
241 | } |
242 | |
243 | for (i = 0; i < aai->aai_ndisks; i++) { |
244 | vp = sc->sc_vnodes[i]; |
245 | sc->sc_vnodes[i] = NULL; |
246 | if (vp != NULL) |
247 | (void) vn_close(vp, FREAD|FWRITE, NOCRED); |
248 | } |
249 | |
250 | finish: |
251 | #if NBIO > 0 |
252 | if (bio_register(self, ld_ataraid_bioctl) != 0) |
253 | panic("%s: bioctl registration failed\n" , |
254 | device_xname(ld->sc_dv)); |
255 | #endif |
256 | SIMPLEQ_INIT(&sc->sc_cbufq); |
257 | ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); |
258 | } |
259 | |
260 | static int |
261 | cbufpool_ctor(void *arg, void *obj, int flags) |
262 | { |
263 | struct ld_ataraid_softc *sc = arg; |
264 | struct ld_softc *ld = &sc->sc_ld; |
265 | struct cbuf *cbp = obj; |
266 | |
267 | /* We release/reacquire the spinlock before calling buf_init() */ |
268 | mutex_exit(&ld->sc_mutex); |
269 | buf_init(&cbp->cb_buf); |
270 | mutex_enter(&ld->sc_mutex); |
271 | |
272 | return 0; |
273 | } |
274 | |
275 | static void |
276 | cbufpool_dtor(void *arg, void *obj) |
277 | { |
278 | struct cbuf *cbp = obj; |
279 | |
280 | buf_destroy(&cbp->cb_buf); |
281 | } |
282 | |
283 | static struct cbuf * |
284 | ld_ataraid_make_cbuf(struct ld_ataraid_softc *sc, struct buf *bp, |
285 | u_int comp, daddr_t bn, void *addr, long bcount) |
286 | { |
287 | struct cbuf *cbp; |
288 | |
289 | cbp = CBUF_GET(); |
290 | if (cbp == NULL) |
291 | return NULL; |
292 | cbp->cb_buf.b_flags = bp->b_flags; |
293 | cbp->cb_buf.b_oflags = bp->b_oflags; |
294 | cbp->cb_buf.b_cflags = bp->b_cflags; |
295 | cbp->cb_buf.b_iodone = sc->sc_iodone; |
296 | cbp->cb_buf.b_proc = bp->b_proc; |
297 | cbp->cb_buf.b_vp = sc->sc_vnodes[comp]; |
298 | cbp->cb_buf.b_objlock = sc->sc_vnodes[comp]->v_interlock; |
299 | cbp->cb_buf.b_blkno = bn + sc->sc_aai->aai_offset; |
300 | cbp->cb_buf.b_data = addr; |
301 | cbp->cb_buf.b_bcount = bcount; |
302 | |
303 | /* Context for iodone */ |
304 | cbp->cb_obp = bp; |
305 | cbp->cb_sc = sc; |
306 | cbp->cb_comp = comp; |
307 | cbp->cb_other = NULL; |
308 | cbp->cb_flags = 0; |
309 | |
310 | return cbp; |
311 | } |
312 | |
313 | static void |
314 | ld_ataraid_start_vstrategy(void *arg) |
315 | { |
316 | struct ld_ataraid_softc *sc = arg; |
317 | struct cbuf *cbp; |
318 | |
319 | while ((cbp = SIMPLEQ_FIRST(&sc->sc_cbufq)) != NULL) { |
320 | SIMPLEQ_REMOVE_HEAD(&sc->sc_cbufq, cb_q); |
321 | if ((cbp->cb_buf.b_flags & B_READ) == 0) { |
322 | mutex_enter(cbp->cb_buf.b_vp->v_interlock); |
323 | cbp->cb_buf.b_vp->v_numoutput++; |
324 | mutex_exit(cbp->cb_buf.b_vp->v_interlock); |
325 | } |
326 | VOP_STRATEGY(cbp->cb_buf.b_vp, &cbp->cb_buf); |
327 | } |
328 | } |
329 | |
330 | static int |
331 | ld_ataraid_start_span(struct ld_softc *ld, struct buf *bp) |
332 | { |
333 | struct ld_ataraid_softc *sc = (void *) ld; |
334 | struct ataraid_array_info *aai = sc->sc_aai; |
335 | struct ataraid_disk_info *adi; |
336 | struct cbuf *cbp; |
337 | char *addr; |
338 | daddr_t bn; |
339 | long bcount, rcount; |
340 | u_int comp; |
341 | |
342 | /* Allocate component buffers. */ |
343 | addr = bp->b_data; |
344 | |
345 | /* Find the first component. */ |
346 | comp = 0; |
347 | adi = &aai->aai_disks[comp]; |
348 | bn = bp->b_rawblkno; |
349 | while (bn >= adi->adi_compsize) { |
350 | bn -= adi->adi_compsize; |
351 | adi = &aai->aai_disks[++comp]; |
352 | } |
353 | |
354 | bp->b_resid = bp->b_bcount; |
355 | |
356 | for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { |
357 | rcount = bp->b_bcount; |
358 | if ((adi->adi_compsize - bn) < btodb(rcount)) |
359 | rcount = dbtob(adi->adi_compsize - bn); |
360 | |
361 | cbp = ld_ataraid_make_cbuf(sc, bp, comp, bn, addr, rcount); |
362 | if (cbp == NULL) { |
363 | /* Free the already allocated component buffers. */ |
364 | while ((cbp = SIMPLEQ_FIRST(&sc->sc_cbufq)) != NULL) { |
365 | SIMPLEQ_REMOVE_HEAD(&sc->sc_cbufq, cb_q); |
366 | CBUF_PUT(cbp); |
367 | } |
368 | return EAGAIN; |
369 | } |
370 | |
371 | /* |
372 | * For a span, we always know we advance to the next disk, |
373 | * and always start at offset 0 on that disk. |
374 | */ |
375 | adi = &aai->aai_disks[++comp]; |
376 | bn = 0; |
377 | |
378 | SIMPLEQ_INSERT_TAIL(&sc->sc_cbufq, cbp, cb_q); |
379 | addr += rcount; |
380 | } |
381 | |
382 | /* Now fire off the requests. */ |
383 | softint_schedule(sc->sc_sih_cookie); |
384 | |
385 | return 0; |
386 | } |
387 | |
388 | static int |
389 | ld_ataraid_start_raid0(struct ld_softc *ld, struct buf *bp) |
390 | { |
391 | struct ld_ataraid_softc *sc = (void *)ld; |
392 | struct ataraid_array_info *aai = sc->sc_aai; |
393 | struct ataraid_disk_info *adi; |
394 | struct cbuf *cbp, *other_cbp; |
395 | char *addr; |
396 | daddr_t bn, cbn, tbn, off; |
397 | long bcount, rcount; |
398 | u_int comp; |
399 | const int read = bp->b_flags & B_READ; |
400 | const int mirror = aai->aai_level & AAI_L_RAID1; |
401 | int error = 0; |
402 | |
403 | /* Allocate component buffers. */ |
404 | addr = bp->b_data; |
405 | bn = bp->b_rawblkno; |
406 | |
407 | bp->b_resid = bp->b_bcount; |
408 | |
409 | for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { |
410 | tbn = bn / aai->aai_interleave; |
411 | off = bn % aai->aai_interleave; |
412 | |
413 | if (__predict_false(tbn == aai->aai_capacity / |
414 | aai->aai_interleave)) { |
415 | /* Last stripe. */ |
416 | daddr_t sz = (aai->aai_capacity - |
417 | (tbn * aai->aai_interleave)) / |
418 | aai->aai_width; |
419 | comp = off / sz; |
420 | cbn = ((tbn / aai->aai_width) * aai->aai_interleave) + |
421 | (off % sz); |
422 | rcount = min(bcount, dbtob(sz)); |
423 | } else { |
424 | comp = tbn % aai->aai_width; |
425 | cbn = ((tbn / aai->aai_width) * aai->aai_interleave) + |
426 | off; |
427 | rcount = min(bcount, dbtob(aai->aai_interleave - off)); |
428 | } |
429 | |
430 | /* |
431 | * See if a component is valid. |
432 | */ |
433 | try_mirror: |
434 | adi = &aai->aai_disks[comp]; |
435 | if ((adi->adi_status & ADI_S_ONLINE) == 0) { |
436 | if (mirror && comp < aai->aai_width) { |
437 | comp += aai->aai_width; |
438 | goto try_mirror; |
439 | } |
440 | |
441 | /* |
442 | * No component available. |
443 | */ |
444 | error = EIO; |
445 | goto free_and_exit; |
446 | } |
447 | |
448 | cbp = ld_ataraid_make_cbuf(sc, bp, comp, cbn, addr, rcount); |
449 | if (cbp == NULL) { |
450 | resource_shortage: |
451 | error = EAGAIN; |
452 | free_and_exit: |
453 | /* Free the already allocated component buffers. */ |
454 | while ((cbp = SIMPLEQ_FIRST(&sc->sc_cbufq)) != NULL) { |
455 | SIMPLEQ_REMOVE_HEAD(&sc->sc_cbufq, cb_q); |
456 | CBUF_PUT(cbp); |
457 | } |
458 | return error; |
459 | } |
460 | SIMPLEQ_INSERT_TAIL(&sc->sc_cbufq, cbp, cb_q); |
461 | if (mirror && !read && comp < aai->aai_width) { |
462 | comp += aai->aai_width; |
463 | adi = &aai->aai_disks[comp]; |
464 | if (adi->adi_status & ADI_S_ONLINE) { |
465 | other_cbp = ld_ataraid_make_cbuf(sc, bp, |
466 | comp, cbn, addr, rcount); |
467 | if (other_cbp == NULL) |
468 | goto resource_shortage; |
469 | SIMPLEQ_INSERT_TAIL(&sc->sc_cbufq, |
470 | other_cbp, cb_q); |
471 | other_cbp->cb_other = cbp; |
472 | cbp->cb_other = other_cbp; |
473 | } |
474 | } |
475 | bn += btodb(rcount); |
476 | addr += rcount; |
477 | } |
478 | |
479 | /* Now fire off the requests. */ |
480 | softint_schedule(sc->sc_sih_cookie); |
481 | |
482 | return error; |
483 | } |
484 | |
485 | /* |
486 | * Called at interrupt time. Mark the component as done and if all |
487 | * components are done, take an "interrupt". |
488 | */ |
489 | static void |
490 | ld_ataraid_iodone_raid0(struct buf *vbp) |
491 | { |
492 | struct cbuf *cbp = (struct cbuf *) vbp, *other_cbp; |
493 | struct buf *bp = cbp->cb_obp; |
494 | struct ld_ataraid_softc *sc = cbp->cb_sc; |
495 | struct ataraid_array_info *aai = sc->sc_aai; |
496 | struct ataraid_disk_info *adi; |
497 | long count; |
498 | int s, iodone; |
499 | |
500 | s = splbio(); |
501 | |
502 | iodone = cbp->cb_flags & CBUF_IODONE; |
503 | other_cbp = cbp->cb_other; |
504 | if (other_cbp != NULL) |
505 | /* You are alone */ |
506 | other_cbp->cb_other = NULL; |
507 | |
508 | if (cbp->cb_buf.b_error != 0) { |
509 | /* |
510 | * Mark this component broken. |
511 | */ |
512 | adi = &aai->aai_disks[cbp->cb_comp]; |
513 | adi->adi_status &= ~ADI_S_ONLINE; |
514 | |
515 | printf("%s: error %d on component %d (%s)\n" , |
516 | device_xname(sc->sc_ld.sc_dv), bp->b_error, cbp->cb_comp, |
517 | device_xname(adi->adi_dev)); |
518 | |
519 | /* |
520 | * If we didn't see an error yet and we are reading |
521 | * RAID1 disk, try another component. |
522 | */ |
523 | if (bp->b_error == 0 && |
524 | (cbp->cb_buf.b_flags & B_READ) != 0 && |
525 | (aai->aai_level & AAI_L_RAID1) != 0 && |
526 | cbp->cb_comp < aai->aai_width) { |
527 | cbp->cb_comp += aai->aai_width; |
528 | adi = &aai->aai_disks[cbp->cb_comp]; |
529 | if (adi->adi_status & ADI_S_ONLINE) { |
530 | cbp->cb_buf.b_error = 0; |
531 | VOP_STRATEGY(cbp->cb_buf.b_vp, &cbp->cb_buf); |
532 | goto out; |
533 | } |
534 | } |
535 | |
536 | if (iodone || other_cbp != NULL) |
537 | /* |
538 | * If I/O on other component successfully done |
539 | * or the I/O is still in progress, no need |
540 | * to tell an error to upper layer. |
541 | */ |
542 | ; |
543 | else { |
544 | bp->b_error = cbp->cb_buf.b_error ? |
545 | cbp->cb_buf.b_error : EIO; |
546 | } |
547 | |
548 | /* XXX Update component config blocks. */ |
549 | |
550 | } else { |
551 | /* |
552 | * If other I/O is still in progress, tell it that |
553 | * our I/O is successfully done. |
554 | */ |
555 | if (other_cbp != NULL) |
556 | other_cbp->cb_flags |= CBUF_IODONE; |
557 | } |
558 | count = cbp->cb_buf.b_bcount; |
559 | CBUF_PUT(cbp); |
560 | |
561 | if (other_cbp != NULL) |
562 | goto out; |
563 | |
564 | /* If all done, "interrupt". */ |
565 | bp->b_resid -= count; |
566 | if (bp->b_resid < 0) |
567 | panic("ld_ataraid_iodone_raid0: count" ); |
568 | if (bp->b_resid == 0) |
569 | lddone(&sc->sc_ld, bp); |
570 | |
571 | out: |
572 | splx(s); |
573 | } |
574 | |
575 | static int |
576 | ld_ataraid_dump(struct ld_softc *sc, void *data, |
577 | int blkno, int blkcnt) |
578 | { |
579 | |
580 | return (EIO); |
581 | } |
582 | |
583 | #if NBIO > 0 |
584 | static int |
585 | ld_ataraid_bioctl(device_t self, u_long cmd, void *addr) |
586 | { |
587 | struct ld_ataraid_softc *sc = device_private(self); |
588 | int error = 0; |
589 | |
590 | switch (cmd) { |
591 | case BIOCINQ: |
592 | error = ld_ataraid_bioinq(sc, (struct bioc_inq *)addr); |
593 | break; |
594 | case BIOCVOL: |
595 | error = ld_ataraid_biovol(sc, (struct bioc_vol *)addr); |
596 | break; |
597 | case BIOCDISK: |
598 | error = ld_ataraid_biodisk(sc, (struct bioc_disk *)addr); |
599 | break; |
600 | default: |
601 | error = ENOTTY; |
602 | break; |
603 | } |
604 | |
605 | return error; |
606 | } |
607 | |
608 | static int |
609 | ld_ataraid_bioinq(struct ld_ataraid_softc *sc, struct bioc_inq *bi) |
610 | { |
611 | struct ataraid_array_info *aai = sc->sc_aai; |
612 | |
613 | /* there's always one volume per ld device */ |
614 | bi->bi_novol = 1; |
615 | bi->bi_nodisk = aai->aai_ndisks; |
616 | |
617 | return 0; |
618 | } |
619 | |
620 | static int |
621 | ld_ataraid_biovol(struct ld_ataraid_softc *sc, struct bioc_vol *bv) |
622 | { |
623 | struct ataraid_array_info *aai = sc->sc_aai; |
624 | struct ld_softc *ld = &sc->sc_ld; |
625 | #define to_kibytes(ld,s) (ld->sc_secsize*(s)/1024) |
626 | |
627 | /* Fill in data for _this_ volume */ |
628 | bv->bv_percent = -1; |
629 | bv->bv_seconds = 0; |
630 | |
631 | switch (aai->aai_status) { |
632 | case AAI_S_READY: |
633 | bv->bv_status = BIOC_SVONLINE; |
634 | break; |
635 | case AAI_S_DEGRADED: |
636 | bv->bv_status = BIOC_SVDEGRADED; |
637 | break; |
638 | } |
639 | |
640 | bv->bv_size = ld->sc_secsize * ld->sc_secperunit; |
641 | |
642 | switch (aai->aai_level) { |
643 | case AAI_L_SPAN: |
644 | case AAI_L_RAID0: |
645 | bv->bv_stripe_size = to_kibytes(ld, aai->aai_interleave); |
646 | bv->bv_level = 0; |
647 | break; |
648 | case AAI_L_RAID1: |
649 | bv->bv_stripe_size = 0; |
650 | bv->bv_level = 1; |
651 | break; |
652 | case AAI_L_RAID5: |
653 | bv->bv_stripe_size = to_kibytes(ld, aai->aai_interleave); |
654 | bv->bv_level = 5; |
655 | break; |
656 | } |
657 | |
658 | bv->bv_nodisk = aai->aai_ndisks; |
659 | strlcpy(bv->bv_dev, device_xname(ld->sc_dv), sizeof(bv->bv_dev)); |
660 | if (aai->aai_name[0] != '\0') |
661 | strlcpy(bv->bv_vendor, aai->aai_name, sizeof(bv->bv_vendor)); |
662 | |
663 | return 0; |
664 | } |
665 | |
666 | static int |
667 | ld_ataraid_biodisk(struct ld_ataraid_softc *sc, struct bioc_disk *bd) |
668 | { |
669 | struct ataraid_array_info *aai = sc->sc_aai; |
670 | struct ataraid_disk_info *adi; |
671 | struct ld_softc *ld = &sc->sc_ld; |
672 | struct atabus_softc *atabus; |
673 | struct wd_softc *wd; |
674 | char model[81], serial[41], rev[17]; |
675 | |
676 | /* sanity check */ |
677 | if (bd->bd_diskid > aai->aai_ndisks) |
678 | return EINVAL; |
679 | |
680 | adi = &aai->aai_disks[bd->bd_diskid]; |
681 | atabus = device_private(device_parent(adi->adi_dev)); |
682 | wd = device_private(adi->adi_dev); |
683 | |
684 | /* fill in data for _this_ disk */ |
685 | switch (adi->adi_status) { |
686 | case ADI_S_ONLINE | ADI_S_ASSIGNED: |
687 | bd->bd_status = BIOC_SDONLINE; |
688 | break; |
689 | case ADI_S_SPARE: |
690 | bd->bd_status = BIOC_SDHOTSPARE; |
691 | break; |
692 | default: |
693 | bd->bd_status = BIOC_SDOFFLINE; |
694 | break; |
695 | } |
696 | |
697 | bd->bd_channel = 0; |
698 | bd->bd_target = atabus->sc_chan->ch_channel; |
699 | bd->bd_lun = 0; |
700 | bd->bd_size = (wd->sc_capacity * ld->sc_secsize) - aai->aai_reserved; |
701 | |
702 | strlcpy(bd->bd_procdev, device_xname(adi->adi_dev), |
703 | sizeof(bd->bd_procdev)); |
704 | |
705 | strnvisx(serial, sizeof(serial), wd->sc_params.atap_serial, |
706 | sizeof(wd->sc_params.atap_serial), VIS_TRIM|VIS_SAFE|VIS_OCTAL); |
707 | strnvisx(model, sizeof(model), wd->sc_params.atap_model, |
708 | sizeof(wd->sc_params.atap_model), VIS_TRIM|VIS_SAFE|VIS_OCTAL); |
709 | strnvisx(rev, sizeof(rev), wd->sc_params.atap_revision, |
710 | sizeof(wd->sc_params.atap_revision), VIS_TRIM|VIS_SAFE|VIS_OCTAL); |
711 | |
712 | snprintf(bd->bd_vendor, sizeof(bd->bd_vendor), "%s %s" , model, rev); |
713 | strlcpy(bd->bd_serial, serial, sizeof(bd->bd_serial)); |
714 | |
715 | return 0; |
716 | } |
717 | #endif /* NBIO > 0 */ |
718 | |
719 | MODULE(MODULE_CLASS_DRIVER, ld_ataraid, "ld,ataraid" ); |
720 | |
721 | #ifdef _MODULE |
722 | /* |
723 | * XXX Don't allow ioconf.c to redefine the "struct cfdriver ld_ataraid" |
724 | * XXX it will be defined in the common-code module |
725 | */ |
726 | #undef CFDRIVER_DECL |
727 | #define CFDRIVER_DECL(name, class, attr) |
728 | #include "ioconf.c" |
729 | #endif |
730 | |
731 | static int |
732 | ld_ataraid_modcmd(modcmd_t cmd, void *opaque) |
733 | { |
734 | #ifdef _MODULE |
735 | /* |
736 | * We ignore the cfdriver_vec[] that ioconf provides, since |
737 | * the cfdrivers are attached already. |
738 | */ |
739 | static struct cfdriver * const no_cfdriver_vec[] = { NULL }; |
740 | #endif |
741 | int error = 0; |
742 | |
743 | #ifdef _MODULE |
744 | switch (cmd) { |
745 | case MODULE_CMD_INIT: |
746 | error = config_init_component(no_cfdriver_vec, |
747 | cfattach_ioconf_ld_ataraid, cfdata_ioconf_ld_ataraid); |
748 | break; |
749 | case MODULE_CMD_FINI: |
750 | error = config_fini_component(no_cfdriver_vec, |
751 | cfattach_ioconf_ld_ataraid, cfdata_ioconf_ld_ataraid); |
752 | break; |
753 | default: |
754 | error = ENOTTY; |
755 | break; |
756 | } |
757 | #endif |
758 | |
759 | return error; |
760 | } |
761 | |