1 | /* $NetBSD: ld_virtio.c,v 1.12 2016/09/27 03:33:32 pgoyette Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2010 Minoura Makoto. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <sys/cdefs.h> |
29 | __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.12 2016/09/27 03:33:32 pgoyette Exp $" ); |
30 | |
31 | #include <sys/param.h> |
32 | #include <sys/systm.h> |
33 | #include <sys/kernel.h> |
34 | #include <sys/buf.h> |
35 | #include <sys/bufq.h> |
36 | #include <sys/bus.h> |
37 | #include <sys/device.h> |
38 | #include <sys/disk.h> |
39 | #include <sys/mutex.h> |
40 | #include <sys/module.h> |
41 | |
42 | #include <dev/pci/pcidevs.h> |
43 | #include <dev/pci/pcireg.h> |
44 | #include <dev/pci/pcivar.h> |
45 | |
46 | #include <dev/ldvar.h> |
47 | #include <dev/pci/virtioreg.h> |
48 | #include <dev/pci/virtiovar.h> |
49 | |
50 | #include "ioconf.h" |
51 | |
52 | /* |
53 | * ld_virtioreg: |
54 | */ |
55 | /* Configuration registers */ |
56 | #define VIRTIO_BLK_CONFIG_CAPACITY 0 /* 64bit */ |
57 | #define VIRTIO_BLK_CONFIG_SIZE_MAX 8 /* 32bit */ |
58 | #define VIRTIO_BLK_CONFIG_SEG_MAX 12 /* 32bit */ |
59 | #define VIRTIO_BLK_CONFIG_GEOMETRY_C 16 /* 16bit */ |
60 | #define VIRTIO_BLK_CONFIG_GEOMETRY_H 18 /* 8bit */ |
61 | #define VIRTIO_BLK_CONFIG_GEOMETRY_S 19 /* 8bit */ |
62 | #define VIRTIO_BLK_CONFIG_BLK_SIZE 20 /* 32bit */ |
63 | |
64 | /* Feature bits */ |
65 | #define VIRTIO_BLK_F_BARRIER (1<<0) |
66 | #define VIRTIO_BLK_F_SIZE_MAX (1<<1) |
67 | #define VIRTIO_BLK_F_SEG_MAX (1<<2) |
68 | #define VIRTIO_BLK_F_GEOMETRY (1<<4) |
69 | #define VIRTIO_BLK_F_RO (1<<5) |
70 | #define VIRTIO_BLK_F_BLK_SIZE (1<<6) |
71 | #define VIRTIO_BLK_F_SCSI (1<<7) |
72 | #define VIRTIO_BLK_F_FLUSH (1<<9) |
73 | |
74 | /* |
75 | * Each block request uses at least two segments - one for the header |
76 | * and one for the status. |
77 | */ |
78 | #define VIRTIO_BLK_MIN_SEGMENTS 2 |
79 | |
80 | #define VIRTIO_BLK_FLAG_BITS \ |
81 | VIRTIO_COMMON_FLAG_BITS \ |
82 | "\x0a""FLUSH" \ |
83 | "\x08""SCSI" \ |
84 | "\x07""BLK_SIZE" \ |
85 | "\x06""RO" \ |
86 | "\x05""GEOMETRY" \ |
87 | "\x03""SEG_MAX" \ |
88 | "\x02""SIZE_MAX" \ |
89 | "\x01""BARRIER" |
90 | |
91 | /* Command */ |
92 | #define VIRTIO_BLK_T_IN 0 |
93 | #define VIRTIO_BLK_T_OUT 1 |
94 | #define VIRTIO_BLK_T_BARRIER 0x80000000 |
95 | |
96 | /* Status */ |
97 | #define VIRTIO_BLK_S_OK 0 |
98 | #define VIRTIO_BLK_S_IOERR 1 |
99 | |
100 | /* Request header structure */ |
101 | struct virtio_blk_req_hdr { |
102 | uint32_t type; /* VIRTIO_BLK_T_* */ |
103 | uint32_t ioprio; |
104 | uint64_t sector; |
105 | } __packed; |
106 | /* 512*virtio_blk_req_hdr.sector byte payload and 1 byte status follows */ |
107 | |
108 | |
109 | /* |
110 | * ld_virtiovar: |
111 | */ |
112 | struct virtio_blk_req { |
113 | struct virtio_blk_req_hdr vr_hdr; |
114 | uint8_t vr_status; |
115 | struct buf *vr_bp; |
116 | bus_dmamap_t vr_cmdsts; |
117 | bus_dmamap_t vr_payload; |
118 | }; |
119 | |
120 | struct ld_virtio_softc { |
121 | struct ld_softc sc_ld; |
122 | device_t sc_dev; |
123 | |
124 | struct virtio_softc *sc_virtio; |
125 | struct virtqueue sc_vq; |
126 | |
127 | struct virtio_blk_req *sc_reqs; |
128 | bus_dma_segment_t sc_reqs_seg; |
129 | |
130 | int sc_readonly; |
131 | }; |
132 | |
133 | static int ld_virtio_match(device_t, cfdata_t, void *); |
134 | static void ld_virtio_attach(device_t, device_t, void *); |
135 | static int ld_virtio_detach(device_t, int); |
136 | |
137 | CFATTACH_DECL_NEW(ld_virtio, sizeof(struct ld_virtio_softc), |
138 | ld_virtio_match, ld_virtio_attach, ld_virtio_detach, NULL); |
139 | |
140 | static int |
141 | ld_virtio_match(device_t parent, cfdata_t match, void *aux) |
142 | { |
143 | struct virtio_softc *va = aux; |
144 | |
145 | if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_BLOCK) |
146 | return 1; |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | static int ld_virtio_vq_done(struct virtqueue *); |
152 | static int ld_virtio_dump(struct ld_softc *, void *, int, int); |
153 | static int ld_virtio_start(struct ld_softc *, struct buf *); |
154 | |
155 | static int |
156 | ld_virtio_alloc_reqs(struct ld_virtio_softc *sc, int qsize) |
157 | { |
158 | int allocsize, r, rsegs, i; |
159 | struct ld_softc *ld = &sc->sc_ld; |
160 | void *vaddr; |
161 | |
162 | allocsize = sizeof(struct virtio_blk_req) * qsize; |
163 | r = bus_dmamem_alloc(sc->sc_virtio->sc_dmat, allocsize, 0, 0, |
164 | &sc->sc_reqs_seg, 1, &rsegs, BUS_DMA_NOWAIT); |
165 | if (r != 0) { |
166 | aprint_error_dev(sc->sc_dev, |
167 | "DMA memory allocation failed, size %d, " |
168 | "error code %d\n" , allocsize, r); |
169 | goto err_none; |
170 | } |
171 | r = bus_dmamem_map(sc->sc_virtio->sc_dmat, |
172 | &sc->sc_reqs_seg, 1, allocsize, |
173 | &vaddr, BUS_DMA_NOWAIT); |
174 | if (r != 0) { |
175 | aprint_error_dev(sc->sc_dev, |
176 | "DMA memory map failed, " |
177 | "error code %d\n" , r); |
178 | goto err_dmamem_alloc; |
179 | } |
180 | sc->sc_reqs = vaddr; |
181 | memset(vaddr, 0, allocsize); |
182 | for (i = 0; i < qsize; i++) { |
183 | struct virtio_blk_req *vr = &sc->sc_reqs[i]; |
184 | r = bus_dmamap_create(sc->sc_virtio->sc_dmat, |
185 | offsetof(struct virtio_blk_req, vr_bp), |
186 | 1, |
187 | offsetof(struct virtio_blk_req, vr_bp), |
188 | 0, |
189 | BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, |
190 | &vr->vr_cmdsts); |
191 | if (r != 0) { |
192 | aprint_error_dev(sc->sc_dev, |
193 | "command dmamap creation failed, " |
194 | "error code %d\n" , r); |
195 | goto err_reqs; |
196 | } |
197 | r = bus_dmamap_load(sc->sc_virtio->sc_dmat, vr->vr_cmdsts, |
198 | &vr->vr_hdr, |
199 | offsetof(struct virtio_blk_req, vr_bp), |
200 | NULL, BUS_DMA_NOWAIT); |
201 | if (r != 0) { |
202 | aprint_error_dev(sc->sc_dev, |
203 | "command dmamap load failed, " |
204 | "error code %d\n" , r); |
205 | goto err_reqs; |
206 | } |
207 | r = bus_dmamap_create(sc->sc_virtio->sc_dmat, |
208 | ld->sc_maxxfer, |
209 | (ld->sc_maxxfer / NBPG) + |
210 | VIRTIO_BLK_MIN_SEGMENTS, |
211 | ld->sc_maxxfer, |
212 | 0, |
213 | BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, |
214 | &vr->vr_payload); |
215 | if (r != 0) { |
216 | aprint_error_dev(sc->sc_dev, |
217 | "payload dmamap creation failed, " |
218 | "error code %d\n" , r); |
219 | goto err_reqs; |
220 | } |
221 | } |
222 | return 0; |
223 | |
224 | err_reqs: |
225 | for (i = 0; i < qsize; i++) { |
226 | struct virtio_blk_req *vr = &sc->sc_reqs[i]; |
227 | if (vr->vr_cmdsts) { |
228 | bus_dmamap_destroy(sc->sc_virtio->sc_dmat, |
229 | vr->vr_cmdsts); |
230 | vr->vr_cmdsts = 0; |
231 | } |
232 | if (vr->vr_payload) { |
233 | bus_dmamap_destroy(sc->sc_virtio->sc_dmat, |
234 | vr->vr_payload); |
235 | vr->vr_payload = 0; |
236 | } |
237 | } |
238 | bus_dmamem_unmap(sc->sc_virtio->sc_dmat, sc->sc_reqs, allocsize); |
239 | err_dmamem_alloc: |
240 | bus_dmamem_free(sc->sc_virtio->sc_dmat, &sc->sc_reqs_seg, 1); |
241 | err_none: |
242 | return -1; |
243 | } |
244 | |
245 | static void |
246 | ld_virtio_attach(device_t parent, device_t self, void *aux) |
247 | { |
248 | struct ld_virtio_softc *sc = device_private(self); |
249 | struct ld_softc *ld = &sc->sc_ld; |
250 | struct virtio_softc *vsc = device_private(parent); |
251 | uint32_t features; |
252 | char buf[256]; |
253 | int qsize, maxxfersize, maxnsegs; |
254 | |
255 | if (vsc->sc_child != NULL) { |
256 | aprint_normal(": child already attached for %s; " |
257 | "something wrong...\n" , device_xname(parent)); |
258 | return; |
259 | } |
260 | |
261 | sc->sc_dev = self; |
262 | sc->sc_virtio = vsc; |
263 | |
264 | vsc->sc_child = self; |
265 | vsc->sc_ipl = IPL_BIO; |
266 | vsc->sc_vqs = &sc->sc_vq; |
267 | vsc->sc_nvqs = 1; |
268 | vsc->sc_config_change = NULL; |
269 | vsc->sc_intrhand = virtio_vq_intr; |
270 | vsc->sc_flags = 0; |
271 | |
272 | features = virtio_negotiate_features(vsc, |
273 | (VIRTIO_BLK_F_SIZE_MAX | |
274 | VIRTIO_BLK_F_SEG_MAX | |
275 | VIRTIO_BLK_F_GEOMETRY | |
276 | VIRTIO_BLK_F_RO | |
277 | VIRTIO_BLK_F_BLK_SIZE)); |
278 | if (features & VIRTIO_BLK_F_RO) |
279 | sc->sc_readonly = 1; |
280 | else |
281 | sc->sc_readonly = 0; |
282 | |
283 | snprintb(buf, sizeof(buf), VIRTIO_BLK_FLAG_BITS, features); |
284 | aprint_normal(": Features: %s\n" , buf); |
285 | aprint_naive("\n" ); |
286 | if (features & VIRTIO_BLK_F_BLK_SIZE) { |
287 | ld->sc_secsize = virtio_read_device_config_4(vsc, |
288 | VIRTIO_BLK_CONFIG_BLK_SIZE); |
289 | } else |
290 | ld->sc_secsize = 512; |
291 | |
292 | /* At least genfs_io assumes maxxfer == MAXPHYS. */ |
293 | if (features & VIRTIO_BLK_F_SIZE_MAX) { |
294 | maxxfersize = virtio_read_device_config_4(vsc, |
295 | VIRTIO_BLK_CONFIG_SIZE_MAX); |
296 | if (maxxfersize < MAXPHYS) { |
297 | aprint_error_dev(sc->sc_dev, |
298 | "Too small SIZE_MAX %dK minimum is %dK\n" , |
299 | maxxfersize / 1024, MAXPHYS / 1024); |
300 | // goto err; |
301 | maxxfersize = MAXPHYS; |
302 | } else if (maxxfersize > MAXPHYS) { |
303 | aprint_normal_dev(sc->sc_dev, |
304 | "Clip SEG_MAX from %dK to %dK\n" , |
305 | maxxfersize / 1024, |
306 | MAXPHYS / 1024); |
307 | maxxfersize = MAXPHYS; |
308 | } |
309 | } else |
310 | maxxfersize = MAXPHYS; |
311 | |
312 | if (features & VIRTIO_BLK_F_SEG_MAX) { |
313 | maxnsegs = virtio_read_device_config_4(vsc, |
314 | VIRTIO_BLK_CONFIG_SEG_MAX); |
315 | if (maxnsegs < VIRTIO_BLK_MIN_SEGMENTS) { |
316 | aprint_error_dev(sc->sc_dev, |
317 | "Too small SEG_MAX %d minimum is %d\n" , |
318 | maxnsegs, VIRTIO_BLK_MIN_SEGMENTS); |
319 | maxnsegs = maxxfersize / NBPG; |
320 | // goto err; |
321 | } |
322 | } else |
323 | maxnsegs = maxxfersize / NBPG; |
324 | |
325 | /* 2 for the minimum size */ |
326 | maxnsegs += VIRTIO_BLK_MIN_SEGMENTS; |
327 | |
328 | if (virtio_alloc_vq(vsc, &sc->sc_vq, 0, maxxfersize, maxnsegs, |
329 | "I/O request" ) != 0) { |
330 | goto err; |
331 | } |
332 | qsize = sc->sc_vq.vq_num; |
333 | sc->sc_vq.vq_done = ld_virtio_vq_done; |
334 | |
335 | ld->sc_dv = self; |
336 | ld->sc_secperunit = virtio_read_device_config_8(vsc, |
337 | VIRTIO_BLK_CONFIG_CAPACITY); |
338 | ld->sc_maxxfer = maxxfersize; |
339 | if (features & VIRTIO_BLK_F_GEOMETRY) { |
340 | ld->sc_ncylinders = virtio_read_device_config_2(vsc, |
341 | VIRTIO_BLK_CONFIG_GEOMETRY_C); |
342 | ld->sc_nheads = virtio_read_device_config_1(vsc, |
343 | VIRTIO_BLK_CONFIG_GEOMETRY_H); |
344 | ld->sc_nsectors = virtio_read_device_config_1(vsc, |
345 | VIRTIO_BLK_CONFIG_GEOMETRY_S); |
346 | } |
347 | ld->sc_maxqueuecnt = qsize; |
348 | |
349 | if (ld_virtio_alloc_reqs(sc, qsize) < 0) |
350 | goto err; |
351 | |
352 | ld->sc_dump = ld_virtio_dump; |
353 | ld->sc_flush = NULL; |
354 | ld->sc_start = ld_virtio_start; |
355 | |
356 | ld->sc_flags = LDF_ENABLED; |
357 | ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); |
358 | |
359 | return; |
360 | |
361 | err: |
362 | vsc->sc_child = (void*)1; |
363 | return; |
364 | } |
365 | |
366 | static int |
367 | ld_virtio_start(struct ld_softc *ld, struct buf *bp) |
368 | { |
369 | /* splbio */ |
370 | struct ld_virtio_softc *sc = device_private(ld->sc_dv); |
371 | struct virtio_softc *vsc = sc->sc_virtio; |
372 | struct virtqueue *vq = &sc->sc_vq; |
373 | struct virtio_blk_req *vr; |
374 | int r; |
375 | int isread = (bp->b_flags & B_READ); |
376 | int slot; |
377 | |
378 | if (sc->sc_readonly && !isread) |
379 | return EIO; |
380 | |
381 | r = virtio_enqueue_prep(vsc, vq, &slot); |
382 | if (r != 0) |
383 | return r; |
384 | |
385 | vr = &sc->sc_reqs[slot]; |
386 | KASSERT(vr->vr_bp == NULL); |
387 | |
388 | r = bus_dmamap_load(vsc->sc_dmat, vr->vr_payload, |
389 | bp->b_data, bp->b_bcount, NULL, |
390 | ((isread?BUS_DMA_READ:BUS_DMA_WRITE) |
391 | |BUS_DMA_NOWAIT)); |
392 | if (r != 0) { |
393 | aprint_error_dev(sc->sc_dev, |
394 | "payload dmamap failed, error code %d\n" , r); |
395 | virtio_enqueue_abort(vsc, vq, slot); |
396 | return r; |
397 | } |
398 | |
399 | r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs + |
400 | VIRTIO_BLK_MIN_SEGMENTS); |
401 | if (r != 0) { |
402 | virtio_enqueue_abort(vsc, vq, slot); |
403 | bus_dmamap_unload(vsc->sc_dmat, vr->vr_payload); |
404 | return r; |
405 | } |
406 | |
407 | vr->vr_bp = bp; |
408 | vr->vr_hdr.type = isread?VIRTIO_BLK_T_IN:VIRTIO_BLK_T_OUT; |
409 | vr->vr_hdr.ioprio = 0; |
410 | vr->vr_hdr.sector = bp->b_rawblkno * sc->sc_ld.sc_secsize / 512; |
411 | |
412 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
413 | 0, sizeof(struct virtio_blk_req_hdr), |
414 | BUS_DMASYNC_PREWRITE); |
415 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload, |
416 | 0, bp->b_bcount, |
417 | isread?BUS_DMASYNC_PREREAD:BUS_DMASYNC_PREWRITE); |
418 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
419 | offsetof(struct virtio_blk_req, vr_status), |
420 | sizeof(uint8_t), |
421 | BUS_DMASYNC_PREREAD); |
422 | |
423 | virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts, |
424 | 0, sizeof(struct virtio_blk_req_hdr), |
425 | true); |
426 | virtio_enqueue(vsc, vq, slot, vr->vr_payload, !isread); |
427 | virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts, |
428 | offsetof(struct virtio_blk_req, vr_status), |
429 | sizeof(uint8_t), |
430 | false); |
431 | virtio_enqueue_commit(vsc, vq, slot, true); |
432 | |
433 | return 0; |
434 | } |
435 | |
436 | static void |
437 | ld_virtio_vq_done1(struct ld_virtio_softc *sc, struct virtio_softc *vsc, |
438 | struct virtqueue *vq, int slot) |
439 | { |
440 | struct virtio_blk_req *vr = &sc->sc_reqs[slot]; |
441 | struct buf *bp = vr->vr_bp; |
442 | |
443 | vr->vr_bp = NULL; |
444 | |
445 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
446 | 0, sizeof(struct virtio_blk_req_hdr), |
447 | BUS_DMASYNC_POSTWRITE); |
448 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload, |
449 | 0, bp->b_bcount, |
450 | (bp->b_flags & B_READ)?BUS_DMASYNC_POSTREAD |
451 | :BUS_DMASYNC_POSTWRITE); |
452 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
453 | sizeof(struct virtio_blk_req_hdr), sizeof(uint8_t), |
454 | BUS_DMASYNC_POSTREAD); |
455 | |
456 | if (vr->vr_status != VIRTIO_BLK_S_OK) { |
457 | bp->b_error = EIO; |
458 | bp->b_resid = bp->b_bcount; |
459 | } else { |
460 | bp->b_error = 0; |
461 | bp->b_resid = 0; |
462 | } |
463 | |
464 | virtio_dequeue_commit(vsc, vq, slot); |
465 | |
466 | lddone(&sc->sc_ld, bp); |
467 | } |
468 | |
469 | static int |
470 | ld_virtio_vq_done(struct virtqueue *vq) |
471 | { |
472 | struct virtio_softc *vsc = vq->vq_owner; |
473 | struct ld_virtio_softc *sc = device_private(vsc->sc_child); |
474 | int r = 0; |
475 | int slot; |
476 | |
477 | again: |
478 | if (virtio_dequeue(vsc, vq, &slot, NULL)) |
479 | return r; |
480 | r = 1; |
481 | |
482 | ld_virtio_vq_done1(sc, vsc, vq, slot); |
483 | goto again; |
484 | } |
485 | |
486 | static int |
487 | ld_virtio_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt) |
488 | { |
489 | struct ld_virtio_softc *sc = device_private(ld->sc_dv); |
490 | struct virtio_softc *vsc = sc->sc_virtio; |
491 | struct virtqueue *vq = &sc->sc_vq; |
492 | struct virtio_blk_req *vr; |
493 | int slot, r; |
494 | |
495 | if (sc->sc_readonly) |
496 | return EIO; |
497 | |
498 | r = virtio_enqueue_prep(vsc, vq, &slot); |
499 | if (r != 0) { |
500 | if (r == EAGAIN) { /* no free slot; dequeue first */ |
501 | delay(100); |
502 | ld_virtio_vq_done(vq); |
503 | r = virtio_enqueue_prep(vsc, vq, &slot); |
504 | if (r != 0) |
505 | return r; |
506 | } |
507 | return r; |
508 | } |
509 | vr = &sc->sc_reqs[slot]; |
510 | r = bus_dmamap_load(vsc->sc_dmat, vr->vr_payload, |
511 | data, blkcnt*ld->sc_secsize, NULL, |
512 | BUS_DMA_WRITE|BUS_DMA_NOWAIT); |
513 | if (r != 0) |
514 | return r; |
515 | |
516 | r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs + |
517 | VIRTIO_BLK_MIN_SEGMENTS); |
518 | if (r != 0) { |
519 | bus_dmamap_unload(vsc->sc_dmat, vr->vr_payload); |
520 | return r; |
521 | } |
522 | |
523 | vr->vr_bp = (void*)0xdeadbeef; |
524 | vr->vr_hdr.type = VIRTIO_BLK_T_OUT; |
525 | vr->vr_hdr.ioprio = 0; |
526 | vr->vr_hdr.sector = (daddr_t) blkno * ld->sc_secsize / 512; |
527 | |
528 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
529 | 0, sizeof(struct virtio_blk_req_hdr), |
530 | BUS_DMASYNC_PREWRITE); |
531 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload, |
532 | 0, blkcnt*ld->sc_secsize, |
533 | BUS_DMASYNC_PREWRITE); |
534 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
535 | offsetof(struct virtio_blk_req, vr_status), |
536 | sizeof(uint8_t), |
537 | BUS_DMASYNC_PREREAD); |
538 | |
539 | virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts, |
540 | 0, sizeof(struct virtio_blk_req_hdr), |
541 | true); |
542 | virtio_enqueue(vsc, vq, slot, vr->vr_payload, true); |
543 | virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts, |
544 | offsetof(struct virtio_blk_req, vr_status), |
545 | sizeof(uint8_t), |
546 | false); |
547 | virtio_enqueue_commit(vsc, vq, slot, true); |
548 | |
549 | for ( ; ; ) { |
550 | int dslot; |
551 | |
552 | r = virtio_dequeue(vsc, vq, &dslot, NULL); |
553 | if (r != 0) |
554 | continue; |
555 | if (dslot != slot) { |
556 | ld_virtio_vq_done1(sc, vsc, vq, dslot); |
557 | continue; |
558 | } else |
559 | break; |
560 | } |
561 | |
562 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
563 | 0, sizeof(struct virtio_blk_req_hdr), |
564 | BUS_DMASYNC_POSTWRITE); |
565 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_payload, |
566 | 0, blkcnt*ld->sc_secsize, |
567 | BUS_DMASYNC_POSTWRITE); |
568 | bus_dmamap_sync(vsc->sc_dmat, vr->vr_cmdsts, |
569 | offsetof(struct virtio_blk_req, vr_status), |
570 | sizeof(uint8_t), |
571 | BUS_DMASYNC_POSTREAD); |
572 | if (vr->vr_status == VIRTIO_BLK_S_OK) |
573 | r = 0; |
574 | else |
575 | r = EIO; |
576 | virtio_dequeue_commit(vsc, vq, slot); |
577 | |
578 | return r; |
579 | } |
580 | |
581 | static int |
582 | ld_virtio_detach(device_t self, int flags) |
583 | { |
584 | struct ld_virtio_softc *sc = device_private(self); |
585 | struct ld_softc *ld = &sc->sc_ld; |
586 | bus_dma_tag_t dmat = sc->sc_virtio->sc_dmat; |
587 | int r, i, qsize; |
588 | |
589 | qsize = sc->sc_vq.vq_num; |
590 | r = ldbegindetach(ld, flags); |
591 | if (r != 0) |
592 | return r; |
593 | virtio_reset(sc->sc_virtio); |
594 | virtio_free_vq(sc->sc_virtio, &sc->sc_vq); |
595 | |
596 | for (i = 0; i < qsize; i++) { |
597 | bus_dmamap_destroy(dmat, |
598 | sc->sc_reqs[i].vr_cmdsts); |
599 | bus_dmamap_destroy(dmat, |
600 | sc->sc_reqs[i].vr_payload); |
601 | } |
602 | bus_dmamem_unmap(dmat, sc->sc_reqs, |
603 | sizeof(struct virtio_blk_req) * qsize); |
604 | bus_dmamem_free(dmat, &sc->sc_reqs_seg, 1); |
605 | |
606 | ldenddetach(ld); |
607 | |
608 | return 0; |
609 | } |
610 | |
611 | MODULE(MODULE_CLASS_DRIVER, ld_virtio, "ld,virtio" ); |
612 | |
613 | #ifdef _MODULE |
614 | /* |
615 | * XXX Don't allow ioconf.c to redefine the "struct cfdriver ld_cd" |
616 | * XXX it will be defined in the common-code module |
617 | */ |
618 | #undef CFDRIVER_DECL |
619 | #define CFDRIVER_DECL(name, class, attr) |
620 | #include "ioconf.c" |
621 | #endif |
622 | |
623 | static int |
624 | ld_virtio_modcmd(modcmd_t cmd, void *opaque) |
625 | { |
626 | #ifdef _MODULE |
627 | /* |
628 | * We ignore the cfdriver_vec[] that ioconf provides, since |
629 | * the cfdrivers are attached already. |
630 | */ |
631 | static struct cfdriver * const no_cfdriver_vec[] = { NULL }; |
632 | #endif |
633 | int error = 0; |
634 | |
635 | #ifdef _MODULE |
636 | switch (cmd) { |
637 | case MODULE_CMD_INIT: |
638 | error = config_init_component(no_cfdriver_vec, |
639 | cfattach_ioconf_ld_virtio, cfdata_ioconf_ld_virtio); |
640 | break; |
641 | case MODULE_CMD_FINI: |
642 | error = config_fini_component(no_cfdriver_vec, |
643 | cfattach_ioconf_ld_virtio, cfdata_ioconf_ld_virtio); |
644 | break; |
645 | default: |
646 | error = ENOTTY; |
647 | break; |
648 | } |
649 | #endif |
650 | |
651 | return error; |
652 | } |
653 | |