Module Name: src
Committed By: jakllsch
Date: Thu Feb 6 20:19:32 UTC 2025
Modified Files:
src/sys/dev/pci: ld_virtio.c
Log Message:
Add discard support to ld@virtio.
To generate a diff of this commit:
cvs rdiff -u -r1.35 -r1.36 src/sys/dev/pci/ld_virtio.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/dev/pci/ld_virtio.c
diff -u src/sys/dev/pci/ld_virtio.c:1.35 src/sys/dev/pci/ld_virtio.c:1.36
--- src/sys/dev/pci/ld_virtio.c:1.35 Wed Jun 12 16:51:53 2024
+++ src/sys/dev/pci/ld_virtio.c Thu Feb 6 20:19:32 2025
@@ -1,4 +1,4 @@
-/* $NetBSD: ld_virtio.c,v 1.35 2024/06/12 16:51:53 riastradh Exp $ */
+/* $NetBSD: ld_virtio.c,v 1.36 2025/02/06 20:19:32 jakllsch Exp $ */
/*
* Copyright (c) 2010 Minoura Makoto.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.35 2024/06/12 16:51:53 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.36 2025/02/06 20:19:32 jakllsch Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -38,6 +38,7 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,
#include <sys/disk.h>
#include <sys/mutex.h>
#include <sys/module.h>
+#include <sys/kmem.h>
#include <dev/ldvar.h>
#include <dev/pci/virtioreg.h>
@@ -57,6 +58,10 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,
#define VIRTIO_BLK_CONFIG_GEOMETRY_S 19 /* 8bit */
#define VIRTIO_BLK_CONFIG_BLK_SIZE 20 /* 32bit */
#define VIRTIO_BLK_CONFIG_WRITEBACK 32 /* 8bit */
+#define VIRTIO_BLK_CONFIG_NUM_QUEUES 34 /* 16bit */
+#define VIRTIO_BLK_CONFIG_MAX_DISCARD_SECTORS 36 /* 32bit */
+#define VIRTIO_BLK_CONFIG_MAX_DISCARD_SEG 40 /* 32bit */
+#define VIRTIO_BLK_CONFIG_DISCARD_SECTOR_ALIGNMENT 44 /* 32bit */
/* Feature bits */
#define VIRTIO_BLK_F_BARRIER (1<<0)
@@ -69,6 +74,11 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,
#define VIRTIO_BLK_F_FLUSH (1<<9)
#define VIRTIO_BLK_F_TOPOLOGY (1<<10)
#define VIRTIO_BLK_F_CONFIG_WCE (1<<11)
+#define VIRTIO_BLK_F_MQ (1<<12)
+#define VIRTIO_BLK_F_DISCARD (1<<13)
+#define VIRTIO_BLK_F_WRITE_ZEROES (1<<14)
+#define VIRTIO_BLK_F_LIFETIME (1<<15)
+#define VIRTIO_BLK_F_SECURE_ERASE (1<<16)
/*
* Each block request uses at least two segments - one for the header
@@ -78,6 +88,11 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,
#define VIRTIO_BLK_FLAG_BITS \
VIRTIO_COMMON_FLAG_BITS \
+ "b\x10" "SECURE_ERASE\0" \
+ "b\x0f" "LIFETIME\0" \
+ "b\x0e" "WRITE_ZEROES\0" \
+ "b\x0d" "DISCARD\0" \
+ "b\x0c" "MQ\0" \
"b\x0b" "CONFIG_WCE\0" \
"b\x0a" "TOPOLOGY\0" \
"b\x09" "FLUSH\0" \
@@ -93,6 +108,11 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,
#define VIRTIO_BLK_T_IN 0
#define VIRTIO_BLK_T_OUT 1
#define VIRTIO_BLK_T_FLUSH 4
+#define VIRTIO_BLK_T_GET_ID 8
+#define VIRTIO_BLK_T_GET_LIFETIME 10
+#define VIRTIO_BLK_T_DISCARD 11
+#define VIRTIO_BLK_T_WRITE_ZEROES 13
+#define VIRTIO_BLK_T_SECURE_ERASE 14
#define VIRTIO_BLK_T_BARRIER 0x80000000
/* Sector */
@@ -111,6 +131,17 @@ struct virtio_blk_req_hdr {
} __packed;
/* payload and 1 byte status follows */
+struct virtio_blk_discard_write_zeroes {
+ uint64_t sector;
+ uint32_t num_sectors;
+ union {
+ uint32_t flags;
+ struct {
+ uint32_t unmap:1;
+ uint32_t reserved:31;
+ };
+ };
+} __packed;
/*
* ld_virtiovar:
@@ -122,6 +153,8 @@ struct virtio_blk_req {
#define DUMMY_VR_BP ((void *)1)
bus_dmamap_t vr_cmdsts;
bus_dmamap_t vr_payload;
+ void * vr_datap;
+ size_t vr_datas;
};
struct ld_virtio_softc {
@@ -145,6 +178,12 @@ struct ld_virtio_softc {
kcondvar_t sc_sync_wait;
kmutex_t sc_sync_wait_lock;
uint8_t sc_sync_status;
+
+ uint32_t sc_max_discard_sectors;
+ uint32_t sc_max_discard_seg;
+#if 0
+ uint32_t sc_discard_sector_alignment;
+#endif
};
static int ld_virtio_match(device_t, cfdata_t, void *);
@@ -169,6 +208,7 @@ static int ld_virtio_vq_done(struct virt
static int ld_virtio_dump(struct ld_softc *, void *, int, int);
static int ld_virtio_start(struct ld_softc *, struct buf *);
static int ld_virtio_ioctl(struct ld_softc *, u_long, void *, int32_t, bool);
+static int ld_virtio_discard(struct ld_softc *, struct buf *);
static int
ld_virtio_alloc_reqs(struct ld_virtio_softc *sc, int qsize)
@@ -235,6 +275,8 @@ ld_virtio_alloc_reqs(struct ld_virtio_so
"error code %d\n", r);
goto err_reqs;
}
+ vr->vr_datap = NULL;
+ vr->vr_datas = 0;
}
return 0;
@@ -280,7 +322,8 @@ ld_virtio_attach(device_t parent, device
virtio_child_attach_start(vsc, self, IPL_BIO,
(VIRTIO_BLK_F_SIZE_MAX | VIRTIO_BLK_F_SEG_MAX |
VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE |
- VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_CONFIG_WCE),
+ VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_CONFIG_WCE |
+ VIRTIO_BLK_F_DISCARD),
VIRTIO_BLK_FLAG_BITS);
features = virtio_features(vsc);
@@ -384,6 +427,19 @@ ld_virtio_attach(device_t parent, device
ld->sc_start = ld_virtio_start;
ld->sc_ioctl = ld_virtio_ioctl;
+ if (features & VIRTIO_BLK_F_DISCARD) {
+ ld->sc_discard = ld_virtio_discard;
+ sc->sc_max_discard_sectors = virtio_read_device_config_4(vsc,
+ VIRTIO_BLK_CONFIG_MAX_DISCARD_SECTORS);
+ sc->sc_max_discard_seg = virtio_read_device_config_4(vsc,
+ VIRTIO_BLK_CONFIG_MAX_DISCARD_SEG);
+#if 0
+ sc->sc_discard_sector_alignment =
+ virtio_read_device_config_4(vsc,
+ VIRTIO_BLK_CONFIG_DISCARD_SECTOR_ALIGNMENT);
+#endif
+ }
+
ld->sc_flags = LDF_ENABLED | LDF_MPSAFE;
ldattach(ld, BUFQ_DISK_DEFAULT_STRAT);
@@ -473,6 +529,7 @@ ld_virtio_vq_done1(struct ld_virtio_soft
{
struct virtio_blk_req *vr = &sc->sc_reqs[slot];
struct buf *bp = vr->vr_bp;
+ const uint32_t rt = virtio_rw32(vsc, vr->vr_hdr.type);
vr->vr_bp = NULL;
@@ -491,10 +548,22 @@ ld_virtio_vq_done1(struct ld_virtio_soft
virtio_dequeue_commit(vsc, vq, slot);
return;
}
- bus_dmamap_sync(virtio_dmat(vsc), vr->vr_payload,
- 0, bp->b_bcount,
- (bp->b_flags & B_READ)?BUS_DMASYNC_POSTREAD
- :BUS_DMASYNC_POSTWRITE);
+ switch (rt) {
+ case VIRTIO_BLK_T_OUT:
+ case VIRTIO_BLK_T_IN:
+ bus_dmamap_sync(virtio_dmat(vsc), vr->vr_payload,
+ 0, bp->b_bcount,
+ (bp->b_flags & B_READ)?BUS_DMASYNC_POSTREAD
+ :BUS_DMASYNC_POSTWRITE);
+ break;
+ default:
+ if (vr->vr_datap == NULL)
+ break;
+ bus_dmamap_sync(virtio_dmat(vsc), vr->vr_payload,
+ 0, vr->vr_datas, BUS_DMASYNC_POSTREAD |
+ BUS_DMASYNC_POSTWRITE);
+ break;
+ }
bus_dmamap_unload(virtio_dmat(vsc), vr->vr_payload);
if (vr->vr_status != VIRTIO_BLK_S_OK) {
@@ -505,9 +574,23 @@ ld_virtio_vq_done1(struct ld_virtio_soft
bp->b_resid = 0;
}
+ if (vr->vr_datap != NULL) {
+ kmem_free(vr->vr_datap, vr->vr_datas);
+ vr->vr_datap = NULL;
+ vr->vr_datas = 0;
+ }
+
virtio_dequeue_commit(vsc, vq, slot);
- lddone(&sc->sc_ld, bp);
+ switch (rt) {
+ case VIRTIO_BLK_T_OUT:
+ case VIRTIO_BLK_T_IN:
+ lddone(&sc->sc_ld, bp);
+ break;
+ case VIRTIO_BLK_T_DISCARD:
+ lddiscardend(&sc->sc_ld, bp);
+ break;
+ }
}
static int
@@ -803,6 +886,98 @@ ld_virtio_ioctl(struct ld_softc *ld, u_l
return error;
}
+static int
+ld_virtio_discard(struct ld_softc *ld, struct buf *bp)
+{
+ struct ld_virtio_softc * const sc = device_private(ld->sc_dv);
+ struct virtio_softc * const vsc = sc->sc_virtio;
+ struct virtqueue * const vq = &sc->sc_vq;
+ struct virtio_blk_req *vr;
+ const uint64_t features = virtio_features(vsc);
+ int r;
+ int slot;
+ uint64_t blkno;
+ uint32_t nblks;
+ struct virtio_blk_discard_write_zeroes * dwz;
+
+ if ((features & VIRTIO_BLK_F_DISCARD) == 0 ||
+ sc->sc_max_discard_seg < 1)
+ return EINVAL;
+
+ if (sc->sc_readonly)
+ return EIO;
+
+ blkno = bp->b_rawblkno * sc->sc_ld.sc_secsize / VIRTIO_BLK_BSIZE;
+ nblks = bp->b_bcount / VIRTIO_BLK_BSIZE;
+
+ if (nblks > sc->sc_max_discard_sectors)
+ return ERANGE;
+
+ r = virtio_enqueue_prep(vsc, vq, &slot);
+ if (r != 0) {
+ return r;
+ }
+
+ vr = &sc->sc_reqs[slot];
+ KASSERT(vr->vr_bp == NULL);
+
+ dwz = kmem_alloc(sizeof(*dwz), KM_SLEEP);
+
+ r = bus_dmamap_load(virtio_dmat(vsc), vr->vr_payload,
+ dwz, sizeof(*dwz), NULL, BUS_DMA_WRITE | BUS_DMA_NOWAIT);
+ if (r != 0) {
+ device_printf(sc->sc_dev,
+ "discard payload dmamap failed, error code %d\n", r);
+ virtio_enqueue_abort(vsc, vq, slot);
+ kmem_free(dwz, sizeof(*dwz));
+ return r;
+ }
+
+ KASSERT(vr->vr_payload->dm_nsegs <= sc->sc_seg_max);
+ r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs +
+ VIRTIO_BLK_CTRL_SEGMENTS);
+ if (r != 0) {
+ bus_dmamap_unload(virtio_dmat(vsc), vr->vr_payload);
+ kmem_free(dwz, sizeof(*dwz));
+ return r;
+ }
+
+ vr->vr_hdr.type = virtio_rw32(vsc, VIRTIO_BLK_T_DISCARD);
+ vr->vr_hdr.ioprio = virtio_rw32(vsc, 0);
+ vr->vr_hdr.sector = virtio_rw64(vsc, 0);
+ vr->vr_bp = bp;
+
+ KASSERT(vr->vr_datap == NULL);
+ vr->vr_datap = dwz;
+ vr->vr_datas = sizeof(*dwz);
+
+ dwz->sector = virtio_rw64(vsc, blkno);
+ dwz->num_sectors = virtio_rw32(vsc, nblks);
+ dwz->flags = virtio_rw32(vsc, 0);
+
+ bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts,
+ 0, sizeof(struct virtio_blk_req_hdr),
+ BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(virtio_dmat(vsc), vr->vr_payload,
+ 0, vr->vr_datas, BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts,
+ offsetof(struct virtio_blk_req, vr_status),
+ sizeof(uint8_t),
+ BUS_DMASYNC_PREREAD);
+
+ virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts,
+ 0, sizeof(struct virtio_blk_req_hdr),
+ true);
+ virtio_enqueue(vsc, vq, slot, vr->vr_payload, true);
+ virtio_enqueue_p(vsc, vq, slot, vr->vr_cmdsts,
+ offsetof(struct virtio_blk_req, vr_status),
+ sizeof(uint8_t),
+ false);
+ virtio_enqueue_commit(vsc, vq, slot, true);
+
+ return 0;
+}
+
MODULE(MODULE_CLASS_DRIVER, ld_virtio, "ld,virtio");
#ifdef _MODULE