Index: sys/arch/xen/x86/xen_shm_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/xen/x86/xen_shm_machdep.c,v
retrieving revision 1.18
diff -p -u -r1.18 xen_shm_machdep.c
--- sys/arch/xen/x86/xen_shm_machdep.c	1 Sep 2022 12:29:00 -0000	1.18
+++ sys/arch/xen/x86/xen_shm_machdep.c	19 Sep 2026 09:16:50 -0000
@@ -30,6 +30,7 @@ __KERNEL_RCSID(0, "$NetBSD: xen_shm_mach
 #include "opt_xen.h"
 
 #include <sys/types.h>
+#include <sys/kmem.h>
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/queue.h>
@@ -59,22 +60,27 @@ int
 xen_shm_map(int nentries, int domid, grant_ref_t *grefp, vaddr_t va,
     grant_handle_t *handlep, int flags)
 {
-	gnttab_map_grant_ref_t op[XENSHM_MAX_PAGES_PER_REQUEST];
+	gnttab_map_grant_ref_t *op;
 	int ret, i;
 #ifndef XENPV
 	paddr_t base_paddr;
 #endif
 	
-
 #ifdef DIAGNOSTIC
 	if (nentries > XENSHM_MAX_PAGES_PER_REQUEST) {
 		panic("xen_shm_map: %d entries", nentries);
 	}
 #endif
+	op = kmem_alloc(nentries * sizeof(*op), KM_NOSLEEP);
+	if (op == NULL)
+		return ENOMEM;
+
 #ifndef XENPV
 	base_paddr = xenmem_alloc_pa(nentries * PAGE_SIZE, PAGE_SIZE, false);
-	if (base_paddr == 0)
+	if (base_paddr == 0) {
+		kmem_free(op, nentries * sizeof(*op));
 		return ENOMEM;
+	}
 #endif
 
 	for (i = 0; i < nentries; i++) {
@@ -166,12 +172,13 @@ xen_shm_map(int nentries, int domid, gra
 		    VM_PROT_READ | VM_PROT_WRITE, 0);
 	}
 #endif
-
+	kmem_free(op, nentries * sizeof(*op));
 	return 0;
 err1:
 #ifndef XENPV
 	xenmem_free_pa(base_paddr, nentries * PAGE_SIZE);
 #endif
+	kmem_free(op, nentries * sizeof(*op));
 	return ret;
 }
 
Index: sys/arch/xen/xen/xbd_xenbus.c
===================================================================
RCS file: /cvsroot/src/sys/arch/xen/xen/xbd_xenbus.c,v
retrieving revision 1.136
diff -p -u -r1.136 xbd_xenbus.c
--- sys/arch/xen/xen/xbd_xenbus.c	15 Jul 2026 15:38:21 -0000	1.136
+++ sys/arch/xen/xen/xbd_xenbus.c	19 Sep 2026 09:16:50 -0000
@@ -94,14 +94,17 @@ __KERNEL_RCSID(0, "$NetBSD: xbd_xenbus.c
 #define GRANT_INVALID_REF -1
 
 #define XBD_RING_SIZE __CONST_RING_SIZE(blkif, PAGE_SIZE)
+
+/* max I/O size for direct request */
 #define XBD_MAX_XFER (PAGE_SIZE * BLKIF_MAX_SEGMENTS_PER_REQUEST)
-#define XBD_MAX_CHUNK	32*1024		/* max I/O size we process in 1 req */
-#define XBD_XFER_LIMIT	(2*XBD_MAX_XFER)
+#define XBD_XFER_LIMIT	MAXPHYS
+
+/* max number of pages touched by I/O (+1 if not page aligned) */
+#define XBD_XFER_PAGES ((MAXPHYS >> PAGE_SHIFT) + 1)
 
 #define XEN_BSHIFT      9               /* log2(XEN_BSIZE) */
 #define XEN_BSIZE       (1 << XEN_BSHIFT)
 
-CTASSERT((MAXPHYS <= 2*XBD_MAX_CHUNK));
 CTASSERT(XEN_BSIZE == DEV_BSIZE);
 
 struct xbd_indirect {
@@ -114,11 +117,10 @@ struct xbd_req {
 	SLIST_ENTRY(xbd_req) req_next;
 	uint16_t req_id; /* ID passed to backend */
 	bus_dmamap_t req_dmamap;
-	struct xbd_req *req_parent, *req_child;
-	bool req_parent_done;
+	struct xbd_req *req_parent, *req_child, *req_first;
 	union {
 	    struct {
-		grant_ref_t req_gntref[XBD_XFER_LIMIT >> PAGE_SHIFT];
+		grant_ref_t req_gntref[XBD_XFER_PAGES];
 		struct buf *req_bp; /* buffer associated with this request */
 		void *req_data; /* pointer to the data buffer */
 		struct xbd_indirect *req_indirect;	/* indirect page */
@@ -159,6 +161,8 @@ struct xbd_xenbus_softc {
 	vmem_addr_t sc_unalign_buffer;
 	void *sc_unalign_used;
 
+	u_long sc_xfer_bytes;
+
 	int sc_backend_status; /* our status with backend */
 #define BLKIF_STATE_DISCONNECTED 0
 #define BLKIF_STATE_CONNECTED    1
@@ -169,9 +173,8 @@ struct xbd_xenbus_softc {
 #define BLKIF_SHUTDOWN_REMOTE 1 /* backend-initiated shutdown in progress */
 #define BLKIF_SHUTDOWN_LOCAL  2 /* locally-initiated shutdown in progress */
 
-	uint64_t sc_sectors; /* number of sc_secsize sectors for this device */
+	uint64_t sc_sectors; /* number of sectors for this device */
 	u_long sc_secsize; /* sector size */
-	uint64_t sc_xbdsize; /* size of disk in DEV_BSIZE */
 	u_long sc_info; /* VDISK_* */
 	u_long sc_handle; /* from backend */
 	int sc_features;
@@ -204,7 +207,7 @@ static void xbd_features(struct xbd_xenb
 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);
+	struct xbd_req *, struct buf *bp, int, bus_dmamap_t, grant_ref_t *);
 static int  xbd_map_align(struct xbd_xenbus_softc *, struct xbd_req *);
 static void xbd_unmap_align(struct xbd_xenbus_softc *, struct xbd_req *,
 	struct buf *);
@@ -299,6 +302,12 @@ xbd_xenbus_attach(device_t parent, devic
 	cv_init(&sc->sc_detach_cv, "xbddetach");
 	cv_init(&sc->sc_suspend_cv, "xbdsuspend");
 
+	/*
+	 * maximum bytes per transfer (updated when indirect
+	 * transfers are available)
+	 */
+	sc->sc_xfer_bytes = XBD_MAX_XFER;
+
 	xbd_features(sc);
 
 	/* initialize free requests list */
@@ -344,7 +353,7 @@ xbd_xenbus_attach(device_t parent, devic
 
 	for (i = 0; i < XBD_RING_SIZE; i++) {
 		if (bus_dmamap_create(sc->sc_xbusd->xbusd_dmat,
-		    MAXPHYS, XBD_XFER_LIMIT >> PAGE_SHIFT,
+		    MAXPHYS, XBD_XFER_PAGES,
 		    PAGE_SIZE, PAGE_SIZE, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
 		    &sc->sc_reqs[i].req_dmamap) != 0) {
 			aprint_error_dev(self, "can't alloc dma maps\n");
@@ -675,16 +684,15 @@ xbd_backend_changed(void *arg, XenbusSta
 
 		xbd_connect(sc);
 		sc->sc_shutdown = BLKIF_SHUTDOWN_RUN;
-		sc->sc_xbdsize =
-		    sc->sc_sectors * (uint64_t)sc->sc_secsize / DEV_BSIZE;
 		dg = &sc->sc_dksc.sc_dkdev.dk_geom;
 		memset(dg, 0, sizeof(*dg));
 
 		dg->dg_secperunit = sc->sc_sectors;
 		dg->dg_secsize = sc->sc_secsize;
+		/* Fake geometry with 1MByte cylinders */
 		dg->dg_ntracks = 1;
 		dg->dg_nsectors = (1024 * 1024) / dg->dg_secsize;
-		dg->dg_ncylinders = dg->dg_secperunit / dg->dg_nsectors;
+		dg->dg_ncylinders = dg->dg_secperunit / (dg->dg_nsectors * dg->dg_ntracks);
 
 		bufq_alloc(&sc->sc_dksc.sc_bufq, "fcfs", 0);
 		dk_attach(&sc->sc_dksc);
@@ -694,10 +702,10 @@ xbd_backend_changed(void *arg, XenbusSta
 		hypervisor_unmask_event(sc->sc_evtchn);
 
 		format_bytes(buf, uimin(9, sizeof(buf)),
-		    sc->sc_sectors * dg->dg_secsize);
+		    sc->sc_sectors * sc->sc_secsize);
 		aprint_normal_dev(sc->sc_dksc.sc_dev,
-				"%s, %d bytes/sect x %" PRIu64 " sectors\n",
-				buf, (int)dg->dg_secsize, sc->sc_sectors);
+				"%s, %u bytes/sect x %" PRIu64 " sectors\n",
+				buf, (unsigned)sc->sc_secsize, sc->sc_sectors);
 		snprintb(buf, sizeof(buf), BLKIF_FEATURE_BITS,
 		    sc->sc_features);
 		aprint_normal_dev(sc->sc_dksc.sc_dev,
@@ -788,9 +796,29 @@ xbd_features(struct xbd_xenbus_softc *sc
 	    "feature-max-indirect-segments", &val, 10);
 	if (err)
 		val = 0;
-	if (val >= (MAXPHYS >> PAGE_SHIFT) + 1) {
-		/* We can use indirect segments, the limit is big enough */
+	if (val > BLKIF_MAX_SEGMENTS_PER_REQUEST) {
+		/*
+		 * Indirect limit is larger than direct limit.
+		 * Update max transfer size.
+		 */
 		sc->sc_features |= BLKIF_FEATURE_INDIRECT;
+		val = ulmin(val, MAXPHYS >> PAGE_SHIFT);
+		sc->sc_xfer_bytes = val << PAGE_SHIFT;
+	}
+}
+
+static void
+xbd_release_indirect(struct xbd_xenbus_softc *sc, struct xbd_req *xbdreq)
+{
+	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;
 	}
 }
 
@@ -828,7 +856,7 @@ again:
 
 		if (rep->operation != BLKIF_OP_READ &&
 		    rep->operation != BLKIF_OP_WRITE) {
-			aprint_error_dev(sc->sc_dksc.sc_dev,
+			device_printf(sc->sc_dksc.sc_dev,
 			    "bad operation %d from backend\n", rep->operation);
 			continue;
 		}
@@ -847,28 +875,38 @@ again:
 		}
 
 		if (xbdreq->req_parent) {
-			struct xbd_req *req_parent = xbdreq->req_parent;
+			/* Unhook request with busy parent */
+			xbdreq->req_parent->req_child = xbdreq->req_child;
+		}
 
-			/* Unhook and recycle child */
+		if (xbdreq->req_child) {
+			/* Unhook request with busy child */
+			xbdreq->req_child->req_parent = xbdreq->req_parent;
+		}
+
+		if (xbdreq->req_parent != NULL || xbdreq->req_child != NULL) {
+			/* Finished before other requests */
 			xbdreq->req_parent = NULL;
-			req_parent->req_child = NULL;
-			SLIST_INSERT_HEAD(&sc->sc_xbdreq_head, xbdreq,
-				    req_next);
+			xbdreq->req_child = NULL;
 
-			if (!req_parent->req_parent_done) {
-				/* Finished before parent, nothing else to do */
-				continue;
+			/* Recycle unless first request */
+			if (xbdreq != xbdreq->req_first) {
+				xbd_release_indirect(sc, xbdreq);
+				SLIST_INSERT_HEAD(&sc->sc_xbdreq_head, xbdreq,
+					    req_next);
 			}
 
-			/* Must do the cleanup now */
-			xbdreq = req_parent;
-		}
-		if (xbdreq->req_child) {
-			/* Finished before child, child will cleanup */
-			xbdreq->req_parent_done = true;
 			continue;
 		}
 
+		if (xbdreq != xbdreq->req_first) {
+			struct xbd_req *req_first = xbdreq->req_first;
+			xbd_release_indirect(sc, xbdreq);
+			SLIST_INSERT_HEAD(&sc->sc_xbdreq_head, xbdreq,
+				    req_next);
+			xbdreq = req_first;
+		}
+
 		if (bp->b_error == 0)
 			bp->b_resid = 0;
 
@@ -879,11 +917,7 @@ again:
 			 * expect the backend to release the grant
 			 * immediately.
 			 */
-			if (xbdreq->req_indirect) {
-				gntref =
-				    xbdreq->req_indirect->in_addr[seg].gref;
-			} else
-				gntref = xbdreq->req_gntref[seg];
+			gntref = xbdreq->req_gntref[seg];
 			KASSERT(xengnt_status(gntref) == 0);
 			xengnt_revoke_access(gntref);
 		}
@@ -896,16 +930,7 @@ 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;
-		}
+		xbd_release_indirect(sc, xbdreq);
 		SLIST_INSERT_HEAD(&sc->sc_xbdreq_head, xbdreq, req_next);
 	}
 	sc->sc_ring.rsp_cons = i;
@@ -1124,9 +1149,11 @@ static int
 xbd_diskstart(device_t self, struct buf *bp)
 {
 	struct xbd_xenbus_softc *sc = device_private(self);
-	struct xbd_req *xbdreq;
+	struct xbd_req *xbdreq, *req_parent, *req_next;
 	int error = 0;
 	int notify;
+	bool indirect;
+	int chunk;
 
 	KASSERT(bp->b_bcount <= MAXPHYS);
 
@@ -1140,7 +1167,7 @@ xbd_diskstart(device_t self, struct buf 
 		goto out;
 	}
 
-	if (bp->b_rawblkno < 0 || bp->b_rawblkno > sc->sc_sectors) {
+	if (bp->b_rawblkno < 0 || bp->b_rawblkno >= sc->sc_sectors) {
 		/* invalid block number */
 		error = EINVAL;
 		goto out;
@@ -1164,19 +1191,40 @@ xbd_diskstart(device_t self, struct buf 
 	}
 	KASSERT(!RING_FULL(&sc->sc_ring));
 
-	if ((sc->sc_features & BLKIF_FEATURE_INDIRECT) == 0
-	    && bp->b_bcount > XBD_MAX_CHUNK) {
-		if (!SLIST_NEXT(xbdreq, req_next)) {
+	/* Is indirect mode available and needed ? */
+	indirect = sc->sc_xfer_bytes > XBD_MAX_XFER && bp->b_bcount > XBD_MAX_XFER;
+
+	/* Max bytes per request */
+	chunk = indirect ? sc->sc_xfer_bytes : XBD_MAX_XFER;
+
+	/* Check if we have enough requests in free list */
+	req_next = xbdreq;
+	for (int bytes = bp->b_bcount; bytes > chunk; bytes -= chunk) {
+		req_next = SLIST_NEXT(req_next, req_next);
+		if (!req_next) {
 			DPRINTF(("%s: need extra req\n", __func__));
 			error = EAGAIN;
 			goto out;
 		}
 	}
 
+	if (indirect) {
+		/* Check if we have enough indirect requests in free list */
+		struct xbd_indirect *req_ind = SLIST_FIRST(&sc->sc_indirect_head);
+		for (int bytes = bp->b_bcount; bytes > 0; bytes -= chunk) {
+			if (!req_ind) {
+				DPRINTF(("%s: need extra indirect headers\n", __func__));
+				error = EAGAIN;
+				goto out;
+			}
+			req_ind = SLIST_NEXT(req_ind, in_next);
+		}
+	}
+
 	bp->b_resid = bp->b_bcount;
 	xbdreq->req_bp = bp;
 	xbdreq->req_data = bp->b_data;
-	if (__predict_false((vaddr_t)bp->b_data & (sc->sc_secsize - 1))) {
+	if (__predict_false((vaddr_t)bp->b_data & PAGE_MASK)) {
 		if (__predict_false(xbd_map_align(sc, xbdreq) != 0)) {
 			DPRINTF(("xbd_diskstart: no align\n"));
 			error = EAGAIN;
@@ -1184,11 +1232,13 @@ xbd_diskstart(device_t self, struct buf 
 		}
 	}
 
-	if (__predict_false(bus_dmamap_load(sc->sc_xbusd->xbusd_dmat,
+	error = bus_dmamap_load(sc->sc_xbusd->xbusd_dmat,
 	    xbdreq->req_dmamap, xbdreq->req_data, bp->b_bcount, NULL,
-	    BUS_DMA_NOWAIT) != 0)) {
-		printf("%s: %s: bus_dmamap_load failed\n",
-		    device_xname(sc->sc_dksc.sc_dev), __func__);
+	    BUS_DMA_NOWAIT);
+	if (__predict_false(error != 0)) {
+		printf("%s: %s: bus_dmamap_load failed (error=%d, bcount=%d)\n",
+		    device_xname(sc->sc_dksc.sc_dev), __func__,
+		    error, bp->b_bcount);
 		if (__predict_false(bp->b_data != xbdreq->req_data))
 			xbd_unmap_align(sc, xbdreq, NULL);
 		error = EINVAL;
@@ -1228,31 +1278,56 @@ 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;
-	}
+	req_parent = NULL;
+	for (int bytes = bp->b_bcount, start = 0; bytes > 0;
+	    bytes -= chunk, start += chunk) {
 
-	xbd_diskstart_submit(sc, xbdreq->req_id,
-	    bp, 0, xbdreq->req_dmamap, xbdreq->req_gntref);
+		struct xbd_req *xbdreq2;
 
-	if (bp->b_bcount > XBD_MAX_CHUNK) {
 		KASSERT(!RING_FULL(&sc->sc_ring));
-		struct xbd_req *xbdreq2 = SLIST_FIRST(&sc->sc_xbdreq_head);
-		KASSERT(xbdreq2 != NULL); /* Checked earlier */
-		SLIST_REMOVE_HEAD(&sc->sc_xbdreq_head, req_next);
-		xbdreq->req_child = xbdreq2;
-		xbdreq->req_parent_done = false;
-		xbdreq2->req_parent = xbdreq;
-		xbdreq2->req_bp = bp;
-		xbdreq2->req_data = xbdreq->req_data;
-		xbd_diskstart_submit(sc, xbdreq2->req_id,
-		    bp, XBD_MAX_CHUNK, xbdreq->req_dmamap,
-		    xbdreq->req_gntref);
+
+		if (start == 0) {
+			/*
+			 * First chunk uses the already allocated
+			 * transfer.
+			 */
+			KASSERT(req_parent == NULL);
+			xbdreq2 = xbdreq;
+		} else {
+			/*
+			 * Any further chunk is taken from free list.
+			 * We already checked that there are enough
+			 * requests available
+			 */
+			KASSERT(req_parent != NULL);
+			xbdreq2 = SLIST_FIRST(&sc->sc_xbdreq_head);
+			KASSERT(xbdreq2 != NULL);
+			SLIST_REMOVE_HEAD(&sc->sc_xbdreq_head, req_next);
+
+			/*
+			 * Extend chain
+			 */
+			req_parent->req_child = xbdreq2;
+			xbdreq2->req_parent = req_parent;
+			xbdreq2->req_child = NULL;
+			xbdreq2->req_bp = xbdreq->req_bp;
+			xbdreq2->req_data = xbdreq->req_data;
+		}
+
+		xbdreq2->req_first = xbdreq;
+		req_parent = xbdreq2;
+
+		if (indirect) {
+			xbd_diskstart_submit_indirect(sc, xbdreq2,
+			    bp, start, xbdreq->req_dmamap,
+			    xbdreq->req_gntref);
+		} else {
+			xbd_diskstart_submit(sc, xbdreq2->req_id,
+			    bp, start, xbdreq->req_dmamap,
+			    xbdreq->req_gntref);
+		}
 	}
 
-push:
 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&sc->sc_ring, notify);
 	if (notify)
 		hypervisor_notify_via_evtchn(sc->sc_evtchn);
@@ -1284,7 +1359,7 @@ xbd_diskstart_submit(struct xbd_xenbus_s
 	    __func__, req->id, req->operation, req->sector_number,
 	    req->handle));
 
-	size = uimin(bp->b_bcount - start, XBD_MAX_CHUNK);
+	size = uimin(bp->b_bcount - start, XBD_MAX_XFER);
 	for (dmaseg = 0; dmaseg < dmamap->dm_nsegs && size > 0; dmaseg++) {
 		bus_dma_segment_t *ds = &dmamap->dm_segs[dmaseg];
 
@@ -1325,11 +1400,12 @@ xbd_diskstart_submit(struct xbd_xenbus_s
 
 static void
 xbd_diskstart_submit_indirect(struct xbd_xenbus_softc *sc,
-    struct xbd_req *xbdreq, struct buf *bp)
+    struct xbd_req *xbdreq, struct buf *bp, int start,
+    bus_dmamap_t dmamap, grant_ref_t *gntref)
 {
 	blkif_request_indirect_t *req;
 	paddr_t ma;
-	int nsects, nbytes, dmaseg, first_sect;
+	int nsects, nbytes, dmaseg, first_sect, size;
 	struct blkif_request_segment *reqseg;
 
 	KASSERT(mutex_owned(&sc->sc_lock));
@@ -1340,7 +1416,8 @@ xbd_diskstart_submit_indirect(struct xbd
 	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 * sc->sc_secsize / XEN_BSIZE;
+	req->sector_number = (bp->b_rawblkno * sc->sc_secsize / XEN_BSIZE) +
+	    (start >> XEN_BSHIFT);
 	req->handle = sc->sc_handle;
 	DPRINTF(("%s: id %" PRIu64 " op %d sn %" PRIu64 " handle %d\n",
 	    __func__, req->id, req->indirect_op, req->sector_number,
@@ -1352,30 +1429,42 @@ xbd_diskstart_submit_indirect(struct xbd
 	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];
+	size = uimin(bp->b_bcount - start, sc->sc_xfer_bytes);
+	for (dmaseg = 0; dmaseg < dmamap->dm_nsegs && size > 0; dmaseg++) {
+		bus_dma_segment_t *ds = &dmamap->dm_segs[dmaseg];
 
 		ma = ds->ds_addr;
 		nbytes = ds->ds_len;
 
+		if (start > 0) {
+			if (start >= nbytes) {
+				start -= nbytes;
+				continue;
+			}
+			ma += start;
+			nbytes -= start;
+			start = 0;
+		}
+		size -= nbytes;
+
 		KASSERT(((ma & PAGE_MASK) & (sc->sc_secsize - 1)) == 0);
 		KASSERT((nbytes & (sc->sc_secsize - 1)) == 0);
-
+		KASSERT((size & (sc->sc_secsize - 1)) == 0);
 		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];
-		DPRINTF(("%s: seg %d fs %d ls %d\n", __func__, dmaseg,
-		    reqseg->first_sect, reqseg->last_sect));
-
 		KASSERT(reqseg->first_sect <= reqseg->last_sect);
 		KASSERT(reqseg->last_sect < (PAGE_SIZE / XEN_BSIZE));
 
-		reqseg++;
+		reqseg->gref = gntref[dmaseg];
+		DPRINTF(("%s: seg %d fs %d ls %d\n", __func__, dmaseg,
+		    reqseg->first_sect, reqseg->last_sect));
+
+		++reqseg;
 	}
-	req->nr_segments = dmaseg;
+	req->nr_segments = reqseg - xbdreq->req_indirect->in_addr;
 	sc->sc_ring.req_prod_pvt++;
 
 	sc->sc_cnt_indirect.ev_count++;
Index: sys/arch/xen/xen/xbdback_xenbus.c
===================================================================
RCS file: /cvsroot/src/sys/arch/xen/xen/xbdback_xenbus.c,v
retrieving revision 1.109
diff -p -u -r1.109 xbdback_xenbus.c
--- sys/arch/xen/xen/xbdback_xenbus.c	12 Sep 2026 09:43:42 -0000	1.109
+++ sys/arch/xen/xen/xbdback_xenbus.c	19 Sep 2026 09:16:50 -0000
@@ -208,6 +208,7 @@ struct xbdback_instance {
 	const struct bdevsw *xbdi_bdevsw; /* pointer to the device's bdevsw */
 	struct vnode *xbdi_vp;
 	uint64_t xbdi_size;
+	unsigned xbdi_secsize;
 	bool xbdi_ro; /* is device read-only ? */
 	/* parameters for the communication */
 	unsigned int xbdi_evtchn;
@@ -397,7 +398,10 @@ xbdback_xenbus_create(struct xenbus_devi
 	 * is 128 so this helps us avoiding a page boundary withing a
 	 * block of VBD_MAX_INDIRECT_SEGMENTS segments.
 	 */
-	CTASSERT(sizeof(struct blkif_request_segment) * VBD_MAX_INDIRECT_SEGMENTS == 128);
+
+	CTASSERT(PAGE_SIZE % (sizeof(struct blkif_request_segment) * VBD_MAX_INDIRECT_SEGMENTS) == 0);
+	CTASSERT(PAGE_SIZE / (sizeof(struct blkif_request_segment) * VBD_MAX_INDIRECT_SEGMENTS) > 1);
+
 	xbdi->xbdi_segs = (void *)uvm_km_alloc(kernel_map, round_page(
 	    sizeof(struct blkif_request_segment) * VBD_MAX_INDIRECT_SEGMENTS * BLKIF_RING_SIZE),
 	    PAGE_SIZE, UVM_KMF_WIRED | UVM_KMF_WAITVA);
@@ -572,7 +576,7 @@ xbdback_connect(struct xbdback_instance 
 			xbdi->xbdi_proto = XBDIP_64;
 			proto = XEN_IO_PROTO_ABI_X86_64;
 		} else {
-			aprint_error("xbd domain %d: unknown proto %s\n",
+			printf("xbd domain %d: unknown proto %s\n",
 			    xbdi->xbdi_domid, xsproto);
 			return -1;
 		}
@@ -591,7 +595,7 @@ xbdback_connect(struct xbdback_instance 
 	gring_ref = ring_ref;
 	if (xen_shm_map(1, xbdi->xbdi_domid, &gring_ref, xbdi->xbdi_ring_va,
 	    &xbdi->xbdi_ring_handle, 0) != 0) {
-		aprint_error("xbdback %s: can't map grant ref\n",
+		printf("xbdback %s: can't map grant ref\n",
 		    xbusd->xbusd_path);
 		xenbus_dev_fatal(xbusd, EINVAL,
 		    "can't map ring", xbusd->xbusd_otherend);
@@ -625,7 +629,7 @@ xbdback_connect(struct xbdback_instance 
 	evop.u.bind_interdomain.remote_port = revtchn;
 	err = HYPERVISOR_event_channel_op(&evop);
 	if (err) {
-		aprint_error("blkback %s: "
+		printf("blkback %s: "
 		    "can't get event channel: %d\n",
 		    xbusd->xbusd_otherend, err);
 		xenbus_dev_fatal(xbusd, err,
@@ -639,7 +643,7 @@ xbdback_connect(struct xbdback_instance 
 	    xbdi->xbdi_evtchn, IST_LEVEL, IPL_BIO, xbdback_evthandler, xbdi,
 	    true, xbdi->xbdi_name);
 	KASSERT(xbdi->xbdi_ih != NULL);
-	aprint_verbose("xbd backend domain %d handle %#x (%d) "
+	printf("xbd backend domain %d handle %#x (%d) "
 	    "using event channel %d, protocol %s\n", xbdi->xbdi_domid,
 	    xbdi->xbdi_handle, xbdi->xbdi_handle, xbdi->xbdi_evtchn, proto);
 
@@ -713,7 +717,7 @@ xbdback_frontend_changed(void *arg, Xenb
 	case XenbusStateUnknown:
 	case XenbusStateInitWait:
 	default:
-		aprint_error("xbdback %s: invalid frontend state %d\n",
+		printf("xbdback %s: invalid frontend state %d\n",
 		    xbusd->xbusd_path, new_state);
 	}
 	return;
@@ -800,16 +804,19 @@ xbdback_backend_changed(struct xenbus_wa
 	}
 	VOP_UNLOCK(xbdi->xbdi_vp);
 
-	/* dk device; get wedge data */
 	struct dkwedge_info wi;
-	if ((err = getdiskinfo(xbdi->xbdi_vp, &wi)) == 0) {
-		xbdi->xbdi_size = wi.dkw_size;
-		printf("xbd backend: attach device %s (size %" PRIu64 ") "
-		    "for domain %d\n", wi.dkw_devname, xbdi->xbdi_size,
-		    xbdi->xbdi_domid);
+	if (getdiskinfo(xbdi->xbdi_vp, &wi) == 0)
+		devname = wi.dkw_devname;
+
+	err = getdisksize(xbdi->xbdi_vp, &xbdi->xbdi_size, &xbdi->xbdi_secsize);
+	if (err == 0) {
+		printf("xbd backend: attach device %s"
+		    " (size %" PRIu64 " x %u) for domain %d\n",
+		    devname, xbdi->xbdi_size,
+		    xbdi->xbdi_secsize, xbdi->xbdi_domid);
 	} else {
-		/* If both Ioctls failed set device size to 0 and return */
-		printf("xbdback %s: can't DIOCGWEDGEINFO device "
+		/* If Ioctls failed set device size to 0 and return */
+		printf("xbdback %s: can't get size of device "
 		    "0x%"PRIx64": %d\n", xbusd->xbusd_path,
 		    xbdi->xbdi_dev, err);
 		xbdi->xbdi_size = xbdi->xbdi_dev = 0;
@@ -825,7 +832,7 @@ again:
 		    return;
 	}
 	err = xenbus_printf(xbt, xbusd->xbusd_path, "sectors", "%" PRIu64 ,
-	    xbdi->xbdi_size);
+	    xbdi->xbdi_size * xbdi->xbdi_secsize / VBD_BSIZE);
 	if (err) {
 		printf("xbdback: failed to write %s/sectors: %d\n",
 		    xbusd->xbusd_path, err);
@@ -838,8 +845,17 @@ again:
 		    xbusd->xbusd_path, err);
 		goto abort;
 	}
+
+	/*
+	 * don't pass true sector size as defined by spec but
+	 * constant 512 bytes.
+	 * 1. doesn't confuse guests that cannot handle large sectors
+	 * 2. matches the disk-size above
+	 * 3. what Linux does
+	 * 4. guest needs to take care about alignment on its own
+	 */
 	err = xenbus_printf(xbt, xbusd->xbusd_path, "sector-size", "%lu",
-	    (u_long)DEV_BSIZE);
+	    (u_long)VBD_BSIZE);
 	if (err) {
 		printf("xbdback: failed to write %s/sector-size: %d\n",
 		    xbusd->xbusd_path, err);
@@ -1461,7 +1477,7 @@ xbdback_co_do_io(struct xbdback_instance
 		    kauth_cred_get());
 		mutex_enter(&xbdi->xbdi_lock);
 		if (error) {
-			aprint_error("xbdback %s: DIOCCACHESYNC returned %d\n",
+			printf("xbdback %s: DIOCCACHESYNC returned %d\n",
 			    xbdi->xbdi_xbusd->xbusd_path, error);
 			 if (error == EOPNOTSUPP || error == ENOTTY)
 				error = BLKIF_RSP_EOPNOTSUPP;
