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