Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102954592
D36667.id110841.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D36667.id110841.diff
View Options
Index: sys/dev/virtio/block/virtio_blk.c
===================================================================
--- sys/dev/virtio/block/virtio_blk.c
+++ sys/dev/virtio/block/virtio_blk.c
@@ -60,12 +60,14 @@
struct vtblk_request {
struct vtblk_softc *vbr_sc;
+ bus_dmamap_t vbr_mapp;
/* Fields after this point are zeroed for each request. */
struct virtio_blk_outhdr vbr_hdr;
struct bio *vbr_bp;
uint8_t vbr_ack;
uint8_t vbr_requeue_on_error;
+ uint8_t vbr_busdma_wait;
int vbr_error;
TAILQ_ENTRY(vtblk_request) vbr_link;
};
@@ -86,9 +88,12 @@
#define VTBLK_FLAG_SUSPEND 0x0004
#define VTBLK_FLAG_BARRIER 0x0008
#define VTBLK_FLAG_WCE_CONFIG 0x0010
+#define VTBLK_FLAG_BUSDMA_WAIT 0x0020
+#define VTBLK_FLAG_BUSDMA_ALIGN 0x0040
struct virtqueue *vtblk_vq;
struct sglist *vtblk_sglist;
+ bus_dma_tag_t vtblk_dmat;
struct disk *vtblk_disk;
struct bio_queue_head vtblk_bioq;
@@ -166,7 +171,9 @@
vtblk_request_next(struct vtblk_softc *);
static struct vtblk_request *
vtblk_request_bio(struct vtblk_softc *);
-static void vtblk_request_execute(struct vtblk_request *);
+static int vtblk_request_execute(struct vtblk_request *, int);
+static void vtblk_request_execute_cb(void *,
+ bus_dma_segment_t *, int, int);
static int vtblk_request_error(struct vtblk_request *);
static void vtblk_queue_completed(struct vtblk_softc *,
@@ -363,6 +370,33 @@
goto fail;
}
+ /*
+ * If vtblk_max_nsegs == VTBLK_MIN_SEGMENTS + 1, the device only
+ * supports a single data segment; in that case we need busdma to
+ * align to a page boundary so we can send a *contiguous* page size
+ * request to the host.
+ */
+ if (sc->vtblk_max_nsegs == VTBLK_MIN_SEGMENTS + 1)
+ sc->vtblk_flags |= VTBLK_FLAG_BUSDMA_ALIGN;
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(dev), /* parent */
+ (sc->vtblk_flags & VTBLK_FLAG_BUSDMA_ALIGN) ? PAGE_SIZE : 1,
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ maxphys, /* max request size */
+ sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS, /* max # segments */
+ maxphys, /* maxsegsize */
+ 0, /* flags */
+ busdma_lock_mutex, /* lockfunc */
+ &sc->vtblk_mtx, /* lockarg */
+ &sc->vtblk_dmat);
+ if (error) {
+ device_printf(dev, "cannot create bus dma tag\n");
+ goto fail;
+ }
+
error = vtblk_alloc_virtqueue(sc);
if (error) {
device_printf(dev, "cannot allocate virtqueue\n");
@@ -412,6 +446,11 @@
sc->vtblk_disk = NULL;
}
+ if (sc->vtblk_dmat != NULL) {
+ bus_dma_tag_destroy(sc->vtblk_dmat);
+ sc->vtblk_dmat = NULL;
+ }
+
if (sc->vtblk_sglist != NULL) {
sglist_free(sc->vtblk_sglist);
sc->vtblk_sglist = NULL;
@@ -737,13 +776,14 @@
* which is typically greater than maxphys. Eventually we should
* just advertise maxphys and split buffers that are too big.
*
- * Note we must subtract one additional segment in case of non
- * page aligned buffers.
+ * If we're not asking busdma to align data to page boundaries, the
+ * maximum I/O size is reduced by PAGE_SIZE in order to accommodate
+ * unaligned I/Os.
*/
- dp->d_maxsize = (sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS - 1) *
+ dp->d_maxsize = (sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS) *
PAGE_SIZE;
- if (dp->d_maxsize < PAGE_SIZE)
- dp->d_maxsize = PAGE_SIZE; /* XXX */
+ if ((sc->vtblk_flags & VTBLK_FLAG_BUSDMA_ALIGN) == 0)
+ dp->d_maxsize -= PAGE_SIZE;
if (virtio_with_feature(dev, VIRTIO_BLK_F_GEOMETRY)) {
dp->d_fwsectors = blkcfg->geometry.sectors;
@@ -809,6 +849,10 @@
return (ENOMEM);
req->vbr_sc = sc;
+ if (bus_dmamap_create(sc->vtblk_dmat, 0, &req->vbr_mapp)) {
+ free(req, M_DEVBUF);
+ return (ENOMEM);
+ }
MPASS(sglist_count(&req->vbr_hdr, sizeof(req->vbr_hdr)) == 1);
MPASS(sglist_count(&req->vbr_ack, sizeof(req->vbr_ack)) == 1);
@@ -829,6 +873,7 @@
while ((req = vtblk_request_dequeue(sc)) != NULL) {
sc->vtblk_request_count--;
+ bus_dmamap_destroy(sc->vtblk_dmat, req->vbr_mapp);
free(req, M_DEVBUF);
}
@@ -938,15 +983,43 @@
return (req);
}
+static int
+vtblk_request_execute(struct vtblk_request *req, int flags)
+{
+ struct vtblk_softc *sc = req->vbr_sc;
+ struct bio *bp = req->vbr_bp;
+ int error = 0;
+
+ /*
+ * Call via bus_dmamap_load_bio or directly depending on whether we
+ * have a buffer we need to map.
+ */
+ if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
+ error = bus_dmamap_load_bio(sc->vtblk_dmat, req->vbr_mapp,
+ req->vbr_bp, vtblk_request_execute_cb, req, flags);
+ if (error == EINPROGRESS) {
+ req->vbr_busdma_wait = 1;
+ sc->vtblk_flags |= VTBLK_FLAG_BUSDMA_WAIT;
+ }
+ } else {
+ vtblk_request_execute_cb(req, NULL, 0, 0);
+ }
+
+ return (error ? error : req->vbr_error);
+}
+
static void
-vtblk_request_execute(struct vtblk_request *req)
+vtblk_request_execute_cb(void * callback_arg, bus_dma_segment_t * segs,
+ int nseg, int error)
{
+ struct vtblk_request *req;
struct vtblk_softc *sc;
struct virtqueue *vq;
struct sglist *sg;
struct bio *bp;
- int ordered, readable, writable, error;
+ int ordered, readable, writable, i;
+ req = (struct vtblk_request *)callback_arg;
sc = req->vbr_sc;
vq = sc->vtblk_vq;
sg = sc->vtblk_sglist;
@@ -954,6 +1027,20 @@
ordered = 0;
writable = 0;
+ /*
+ * If we paused request queueing while we waited for busdma to call us
+ * asynchronously, unpause it now; this request made it through so we
+ * don't need to worry about others getting ahead of us. (Note that we
+ * hold the device mutex so nothing will happen until after we return
+ * anyway.)
+ */
+ if (req->vbr_busdma_wait)
+ sc->vtblk_flags &= ~VTBLK_FLAG_BUSDMA_WAIT;
+
+ /* Fail on errors from busdma. */
+ if (error)
+ goto out1;
+
/*
* Some hosts (such as bhyve) do not implement the barrier feature,
* so we emulate it in the driver by allowing the barrier request
@@ -979,10 +1066,18 @@
sglist_append(sg, &req->vbr_hdr, sizeof(struct virtio_blk_outhdr));
if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
- error = sglist_append_bio(sg, bp);
- if (error || sg->sg_nseg == sg->sg_maxseg) {
- panic("%s: bio %p data buffer too big %d",
- __func__, bp, error);
+ /*
+ * We cast bus_addr_t to vm_paddr_t here; this isn't valid on
+ * all platforms, but we believe it works on all platforms
+ * which are supported by virtio.
+ */
+ for (i = 0; i < nseg; i++) {
+ error = sglist_append_phys(sg,
+ (vm_paddr_t)segs[i].ds_addr, segs[i].ds_len);
+ if (error || sg->sg_nseg == sg->sg_maxseg) {
+ panic("%s: bio %p data buffer too big %d",
+ __func__, bp, error);
+ }
}
/* BIO_READ means the host writes into our buffer. */
@@ -1011,11 +1106,33 @@
sglist_append(sg, &req->vbr_ack, sizeof(uint8_t));
readable = sg->sg_nseg - writable;
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ bus_dmamap_sync(sc->vtblk_dmat, req->vbr_mapp,
+ BUS_DMASYNC_PREREAD);
+ break;
+ case BIO_WRITE:
+ bus_dmamap_sync(sc->vtblk_dmat, req->vbr_mapp,
+ BUS_DMASYNC_PREWRITE);
+ break;
+ }
+
error = virtqueue_enqueue(vq, req, sg, readable, writable);
if (error == 0 && ordered)
sc->vtblk_req_ordered = req;
+ /*
+ * If we were called asynchronously, we need to notify the queue that
+ * we've added a new request, since the notification from startio was
+ * performed already.
+ */
+ if (error == 0 && req->vbr_busdma_wait)
+ virtqueue_notify(vq);
+
out:
+ if (error)
+ bus_dmamap_unload(sc->vtblk_dmat, req->vbr_mapp);
+out1:
if (error && req->vbr_requeue_on_error)
vtblk_request_requeue_ready(sc, req);
req->vbr_error = error;
@@ -1054,6 +1171,18 @@
}
bp = req->vbr_bp;
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ bus_dmamap_sync(sc->vtblk_dmat, req->vbr_mapp,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->vtblk_dmat, req->vbr_mapp);
+ break;
+ case BIO_WRITE:
+ bus_dmamap_sync(sc->vtblk_dmat, req->vbr_mapp,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->vtblk_dmat, req->vbr_mapp);
+ break;
+ }
bp->bio_error = vtblk_request_error(req);
TAILQ_INSERT_TAIL(queue, bp, bio_queue);
@@ -1135,7 +1264,7 @@
vq = sc->vtblk_vq;
enq = 0;
- if (sc->vtblk_flags & VTBLK_FLAG_SUSPEND)
+ if (sc->vtblk_flags & (VTBLK_FLAG_SUSPEND | VTBLK_FLAG_BUSDMA_WAIT))
return;
while (!virtqueue_full(vq)) {
@@ -1144,8 +1273,7 @@
break;
req->vbr_requeue_on_error = 1;
- vtblk_request_execute(req);
- if (req->vbr_error != 0)
+ if (vtblk_request_execute(req, 0))
break;
enq++;
@@ -1280,8 +1408,7 @@
if (!virtqueue_empty(vq))
return (EBUSY);
- vtblk_request_execute(req);
- error = req->vbr_error;
+ error = vtblk_request_execute(req, BUS_DMA_NOWAIT);
if (error)
return (error);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 20, 4:07 AM (21 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14729945
Default Alt Text
D36667.id110841.diff (8 KB)
Attached To
Mode
D36667: vtblk: Use busdma
Attached
Detach File
Event Timeline
Log In to Comment