Module Name:    src
Committed By:   jdolecek
Date:           Thu Apr 16 16:38:43 UTC 2020

Modified Files:
        src/sys/arch/xen/xen: xbd_xenbus.c

Log Message:
if backend supports it, use indirect segment for I/O requests over 32KB

there is some overhead for backend without persistent mappings
(backend needs to map the extra indirect segment page), but this
makes it possible to queue more I/O if using 64KB block size

tested on NetBSD/amd64 DomU against Linux/amd64 Dom0


To generate a diff of this commit:
cvs rdiff -u -r1.115 -r1.116 src/sys/arch/xen/xen/xbd_xenbus.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/xen/xen/xbd_xenbus.c
diff -u src/sys/arch/xen/xen/xbd_xenbus.c:1.115 src/sys/arch/xen/xen/xbd_xenbus.c:1.116
--- src/sys/arch/xen/xen/xbd_xenbus.c:1.115	Thu Apr 16 09:51:40 2020
+++ src/sys/arch/xen/xen/xbd_xenbus.c	Thu Apr 16 16:38:43 2020
@@ -1,4 +1,4 @@
-/*      $NetBSD: xbd_xenbus.c,v 1.115 2020/04/16 09:51:40 jdolecek Exp $      */
+/*      $NetBSD: xbd_xenbus.c,v 1.116 2020/04/16 16:38:43 jdolecek Exp $      */
 
 /*
  * Copyright (c) 2006 Manuel Bouyer.
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xbd_xenbus.c,v 1.115 2020/04/16 09:51:40 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xbd_xenbus.c,v 1.116 2020/04/16 16:38:43 jdolecek Exp $");
 
 #include "opt_xen.h"
 
@@ -103,6 +103,12 @@ __KERNEL_RCSID(0, "$NetBSD: xbd_xenbus.c
 CTASSERT((MAXPHYS <= 2*XBD_MAX_CHUNK));
 CTASSERT(XEN_BSIZE == DEV_BSIZE);
 
+struct xbd_indirect {
+	SLIST_ENTRY(xbd_indirect) in_next;
+	struct blkif_request_segment *in_addr;
+	grant_ref_t in_gntref;
+};
+
 struct xbd_req {
 	SLIST_ENTRY(xbd_req) req_next;
 	uint16_t req_id; /* ID passed to backend */
@@ -114,6 +120,7 @@ struct xbd_req {
 		grant_ref_t req_gntref[XBD_XFER_LIMIT >> PAGE_SHIFT];
 		struct buf *req_bp; /* buffer associated with this request */
 		void *req_data; /* pointer to the data buffer */
+		struct xbd_indirect *req_indirect;	/* indirect page */
 	    } req_rw;
 	    struct {
 		int s_error;
@@ -124,6 +131,7 @@ struct xbd_req {
 #define req_gntref	u.req_rw.req_gntref
 #define req_bp		u.req_rw.req_bp
 #define req_data	u.req_rw.req_data
+#define req_indirect	u.req_rw.req_indirect
 #define req_sync	u.req_sync
 
 struct xbd_xenbus_softc {
@@ -144,6 +152,9 @@ struct xbd_xenbus_softc {
 	struct xbd_req sc_reqs[XBD_RING_SIZE];
 	SLIST_HEAD(,xbd_req) sc_xbdreq_head; /* list of free requests */
 
+	struct xbd_indirect sc_indirect[XBD_RING_SIZE];
+	SLIST_HEAD(,xbd_indirect) sc_indirect_head;
+
 	vmem_addr_t sc_unalign_buffer;
 	struct xbd_req *sc_unalign_used;
 
@@ -166,11 +177,13 @@ struct xbd_xenbus_softc {
 #define BLKIF_FEATURE_CACHE_FLUSH	0x1
 #define BLKIF_FEATURE_BARRIER		0x2
 #define BLKIF_FEATURE_PERSISTENT	0x4
+#define BLKIF_FEATURE_INDIRECT		0x8
 #define BLKIF_FEATURE_BITS		\
-	"\20\1CACHE-FLUSH\2BARRIER\3PERSISTENT"
+	"\20\1CACHE-FLUSH\2BARRIER\3PERSISTENT\4INDIRECT"
 	struct evcnt sc_cnt_map_unalign;
 	struct evcnt sc_cnt_unalign_busy;
 	struct evcnt sc_cnt_queue_full;
+	struct evcnt sc_cnt_indirect;
 };
 
 #if 0
@@ -191,9 +204,12 @@ static int  xbd_diskstart(device_t, stru
 static void xbd_iosize(device_t, int *);
 static void xbd_backend_changed(void *, XenbusState);
 static void xbd_connect(struct xbd_xenbus_softc *);
+static void xbd_features(struct xbd_xenbus_softc *);
 
 static void xbd_diskstart_submit(struct xbd_xenbus_softc *, int,
 	struct buf *bp, int, bus_dmamap_t, grant_ref_t *);
+static void xbd_diskstart_submit_indirect(struct xbd_xenbus_softc *,
+	struct xbd_req *, struct buf *bp);
 static int  xbd_map_align(struct xbd_xenbus_softc *, struct xbd_req *);
 static void xbd_unmap_align(struct xbd_xenbus_softc *, struct xbd_req *, bool);
 
@@ -293,6 +309,8 @@ xbd_xenbus_attach(device_t parent, devic
 	cv_init(&sc->sc_detach_cv, "xbddetach");
 	cv_init(&sc->sc_suspend_cv, "xbdsuspend");
 
+	xbd_features(sc);
+
 	/* initialize free requests list */
 	SLIST_INIT(&sc->sc_xbdreq_head);
 	for (i = 0; i < XBD_RING_SIZE; i++) {
@@ -301,6 +319,22 @@ xbd_xenbus_attach(device_t parent, devic
 		    req_next);
 	}
 
+	if (sc->sc_features & BLKIF_FEATURE_INDIRECT) {
+		/* initialize indirect page list */
+		for (i = 0; i < XBD_RING_SIZE; i++) {
+			vmem_addr_t va;
+			if (uvm_km_kmem_alloc(kmem_va_arena,
+			    PAGE_SIZE, VM_SLEEP | VM_INSTANTFIT, &va) != 0) {
+				aprint_error_dev(self,
+				    "can't alloc indirect pages\n");
+				return;
+			}
+			sc->sc_indirect[i].in_addr = (void *)va;
+			SLIST_INSERT_HEAD(&sc->sc_indirect_head,
+			    &sc->sc_indirect[i], in_next);
+		}
+	}
+
 	sc->sc_backend_status = BLKIF_STATE_DISCONNECTED;
 	sc->sc_shutdown = BLKIF_SHUTDOWN_REMOTE;
 
@@ -315,6 +349,8 @@ xbd_xenbus_attach(device_t parent, devic
 	    NULL, device_xname(self), "map unaligned");
 	evcnt_attach_dynamic(&sc->sc_cnt_queue_full, EVCNT_TYPE_MISC,
 	    NULL, device_xname(self), "queue full");
+	evcnt_attach_dynamic(&sc->sc_cnt_indirect, EVCNT_TYPE_MISC,
+	    NULL, device_xname(self), "indirect segment");
 
 	for (i = 0; i < XBD_RING_SIZE; i++) {
 		if (bus_dmamap_create(sc->sc_xbusd->xbusd_dmat,
@@ -341,7 +377,6 @@ xbd_xenbus_attach(device_t parent, devic
 
 	if (!pmf_device_register(self, xbd_xenbus_suspend, xbd_xenbus_resume))
 		aprint_error_dev(self, "couldn't establish power handler\n");
-
 }
 
 static int
@@ -441,6 +476,7 @@ xbd_xenbus_detach(device_t dev, int flag
 	evcnt_detach(&sc->sc_cnt_map_unalign);
 	evcnt_detach(&sc->sc_cnt_unalign_busy);
 	evcnt_detach(&sc->sc_cnt_queue_full);
+	evcnt_detach(&sc->sc_cnt_indirect);
 
 	pmf_device_deregister(dev);
 
@@ -509,6 +545,22 @@ xbd_xenbus_resume(device_t dev, const pm
 	if (error)
 		goto abort_resume;
 
+	if (sc->sc_features & BLKIF_FEATURE_INDIRECT) {
+		for (int i = 0; i < XBD_RING_SIZE; i++) {
+			vaddr_t va = (vaddr_t)sc->sc_indirect[i].in_addr;
+			KASSERT(va != 0);
+			KASSERT((va & PAGE_MASK) == 0);
+			(void)pmap_extract_ma(pmap_kernel(), va, &ma);
+			if (xengnt_grant_access(
+			    sc->sc_xbusd->xbusd_otherend_id,
+			    ma, true, &sc->sc_indirect[i].in_gntref)) {
+				aprint_error_dev(dev,
+				    "indirect page grant failed\n");
+				goto abort_resume;
+			}
+		}
+	}
+
 	error = xenbus_alloc_evtchn(sc->sc_xbusd, &sc->sc_evtchn);
 	if (error)
 		goto abort_resume;
@@ -581,7 +633,7 @@ xbd_backend_changed(void *arg, XenbusSta
 	struct xbd_xenbus_softc *sc = device_private((device_t)arg);
 	struct disk_geom *dg;
 
-	char buf[32];
+	char buf[64];
 	DPRINTF(("%s: new backend state %d\n",
 	    device_xname(sc->sc_dksc.sc_dev), new_state));
 
@@ -662,7 +714,6 @@ xbd_connect(struct xbd_xenbus_softc *sc)
 {
 	int err;
 	unsigned long long sectors;
-	u_long val;
 
 	err = xenbus_read_ul(NULL,
 	    sc->sc_xbusd->xbusd_path, "virtual-device", &sc->sc_handle, 10);
@@ -691,6 +742,15 @@ xbd_connect(struct xbd_xenbus_softc *sc)
 		    device_xname(sc->sc_dksc.sc_dev),
 		    sc->sc_xbusd->xbusd_otherend);
 
+	xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateConnected);
+}
+
+static void
+xbd_features(struct xbd_xenbus_softc *sc)
+{
+	int err;
+	u_long val;
+
 	err = xenbus_read_ul(NULL, sc->sc_xbusd->xbusd_otherend,
 	    "feature-flush-cache", &val, 10);
 	if (err)
@@ -712,7 +772,14 @@ xbd_connect(struct xbd_xenbus_softc *sc)
 	if (val > 0)
 		sc->sc_features |= BLKIF_FEATURE_PERSISTENT;
 
-	xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateConnected);
+	err = xenbus_read_ul(NULL, sc->sc_xbusd->xbusd_otherend,
+	    "feature-max-indirect-segments", &val, 10);
+	if (err)
+		val = 0;
+	if (val > (MAXPHYS >> PAGE_SHIFT)) {
+		/* We can use indirect segments, the limit is big enough */
+		sc->sc_features |= BLKIF_FEATURE_INDIRECT;
+	}
 }
 
 static int
@@ -723,6 +790,7 @@ xbd_handler(void *arg)
 	RING_IDX resp_prod, i;
 	int more_to_do;
 	int seg;
+	grant_ref_t gntref;
 
 	DPRINTF(("xbd_handler(%s)\n", device_xname(sc->sc_dksc.sc_dev)));
 
@@ -795,8 +863,13 @@ again:
 			 * expect the backend to release the grant
 			 * immediately.
 			 */
-			KASSERT(xengnt_status(xbdreq->req_gntref[seg]) == 0);
-			xengnt_revoke_access(xbdreq->req_gntref[seg]);
+			if (xbdreq->req_indirect) {
+				gntref =
+				    xbdreq->req_indirect->in_addr[seg].gref;
+			} else
+				gntref = xbdreq->req_gntref[seg];
+			KASSERT(xengnt_status(gntref) == 0);
+			xengnt_revoke_access(gntref);
 		}
 
 		bus_dmamap_unload(sc->sc_xbusd->xbusd_dmat, xbdreq->req_dmamap);
@@ -807,6 +880,16 @@ again:
 
 		dk_done(&sc->sc_dksc, bp);
 
+		if (xbdreq->req_indirect) {
+			/* No persistent mappings, so check that
+			 * backend unmapped the indirect segment grant too.
+			 */
+			KASSERT(xengnt_status(xbdreq->req_indirect->in_gntref)
+			    == 0);
+			SLIST_INSERT_HEAD(&sc->sc_indirect_head,
+			    xbdreq->req_indirect, in_next);
+			xbdreq->req_indirect = NULL;
+		}
 		SLIST_INSERT_HEAD(&sc->sc_xbdreq_head, xbdreq, req_next);
 	}
 
@@ -1064,7 +1147,8 @@ xbd_diskstart(device_t self, struct buf 
 		goto out;
 	}
 
-	if (bp->b_bcount > XBD_MAX_CHUNK) {
+	if ((sc->sc_features & BLKIF_FEATURE_INDIRECT) == 0
+	    && bp->b_bcount > XBD_MAX_CHUNK) {
 		if (!SLIST_NEXT(xbdreq, req_next)) {
 			DPRINTF(("%s: need extra req\n", __func__));
 			error = EAGAIN;
@@ -1124,6 +1208,13 @@ xbd_diskstart(device_t self, struct buf 
 
 	/* We are now committed to the transfer */
 	SLIST_REMOVE_HEAD(&sc->sc_xbdreq_head, req_next);
+
+	if ((sc->sc_features & BLKIF_FEATURE_INDIRECT) != 0 &&
+	    bp->b_bcount > XBD_MAX_CHUNK) {
+		xbd_diskstart_submit_indirect(sc, xbdreq, bp);
+		goto push;
+	}
+
 	xbd_diskstart_submit(sc, xbdreq->req_id,
 	    bp, 0, xbdreq->req_dmamap, xbdreq->req_gntref);
 
@@ -1141,6 +1232,7 @@ xbd_diskstart(device_t self, struct buf 
 		    xbdreq->req_gntref);
 	}
 
+push:
 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->sc_ring, notify);
 	if (notify)
 		hypervisor_notify_via_evtchn(sc->sc_evtchn);
@@ -1204,6 +1296,56 @@ xbd_diskstart_submit(struct xbd_xenbus_s
 	sc->sc_ring.req_prod_pvt++;
 }
 
+static void
+xbd_diskstart_submit_indirect(struct xbd_xenbus_softc *sc,
+    struct xbd_req *xbdreq, struct buf *bp)
+{
+	blkif_request_indirect_t *req;
+	paddr_t ma;
+	int nsects, nbytes, dmaseg, first_sect;
+	struct blkif_request_segment *reqseg;
+
+	KASSERT(mutex_owned(&sc->sc_lock));
+
+	req = (blkif_request_indirect_t *)RING_GET_REQUEST(&sc->sc_ring,
+	    sc->sc_ring.req_prod_pvt);
+	req->id = xbdreq->req_id;
+	req->operation = BLKIF_OP_INDIRECT;
+	req->indirect_op =
+	    bp->b_flags & B_READ ? BLKIF_OP_READ : BLKIF_OP_WRITE;
+	req->sector_number = bp->b_rawblkno;
+	req->handle = sc->sc_handle;
+
+	xbdreq->req_indirect = SLIST_FIRST(&sc->sc_indirect_head);
+	KASSERT(xbdreq->req_indirect != NULL);	/* always as many as reqs */
+	SLIST_REMOVE_HEAD(&sc->sc_indirect_head, in_next);
+	req->indirect_grefs[0] = xbdreq->req_indirect->in_gntref;
+
+	reqseg = xbdreq->req_indirect->in_addr;
+	for (dmaseg = 0; dmaseg < xbdreq->req_dmamap->dm_nsegs; dmaseg++) {
+		bus_dma_segment_t *ds = &xbdreq->req_dmamap->dm_segs[dmaseg];
+
+		ma = ds->ds_addr;
+		nbytes = ds->ds_len;
+
+		first_sect = (ma & PAGE_MASK) >> XEN_BSHIFT;
+		nsects = nbytes >> XEN_BSHIFT;
+
+		reqseg->first_sect = first_sect;
+		reqseg->last_sect = first_sect + nsects - 1;
+		reqseg->gref = xbdreq->req_gntref[dmaseg];
+
+		KASSERT(reqseg->first_sect <= reqseg->last_sect);
+		KASSERT(reqseg->last_sect < (PAGE_SIZE / XEN_BSIZE));
+
+		reqseg++;
+	}
+	req->nr_segments = dmaseg;
+	sc->sc_ring.req_prod_pvt++;
+
+	sc->sc_cnt_indirect.ev_count++;
+}
+
 static int
 xbd_map_align(struct xbd_xenbus_softc *sc, struct xbd_req *req)
 {

Reply via email to