Module Name: src Committed By: martin Date: Fri Jun 22 10:15:18 UTC 2018
Modified Files: src/sys/dev/pci [netbsd-8]: ld_virtio.c Log Message: Pull up following revision(s) (requested by jakllsch in ticket #888): sys/dev/pci/ld_virtio.c: revision 1.19-1.21,1.23 remove trailing whitespace add feature/register definitions from virtio-v1.0-cs04 unload payload dma map upon command completion Make ld_virtio aware of a possible device-side write cache. The virtio block device capacity config item is expressed always in request protocol sector units of 512 bytes. Also, add and use a symbolic constant to refer to request protocol sector units. destroy 'sc_sync_wait' condvar and mutex upon detach To generate a diff of this commit: cvs rdiff -u -r1.15.6.1 -r1.15.6.2 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.15.6.1 src/sys/dev/pci/ld_virtio.c:1.15.6.2 --- src/sys/dev/pci/ld_virtio.c:1.15.6.1 Fri Sep 1 09:59:11 2017 +++ src/sys/dev/pci/ld_virtio.c Fri Jun 22 10:15:18 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: ld_virtio.c,v 1.15.6.1 2017/09/01 09:59:11 martin Exp $ */ +/* $NetBSD: ld_virtio.c,v 1.15.6.2 2018/06/22 10:15:18 martin Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -26,7 +26,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.15.6.1 2017/09/01 09:59:11 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ld_virtio.c,v 1.15.6.2 2018/06/22 10:15:18 martin Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -60,6 +60,7 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c, #define VIRTIO_BLK_CONFIG_GEOMETRY_H 18 /* 8bit */ #define VIRTIO_BLK_CONFIG_GEOMETRY_S 19 /* 8bit */ #define VIRTIO_BLK_CONFIG_BLK_SIZE 20 /* 32bit */ +#define VIRTIO_BLK_CONFIG_WRITEBACK 32 /* 8bit */ /* Feature bits */ #define VIRTIO_BLK_F_BARRIER (1<<0) @@ -70,15 +71,19 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c, #define VIRTIO_BLK_F_BLK_SIZE (1<<6) #define VIRTIO_BLK_F_SCSI (1<<7) #define VIRTIO_BLK_F_FLUSH (1<<9) +#define VIRTIO_BLK_F_TOPOLOGY (1<<10) +#define VIRTIO_BLK_F_CONFIG_WCE (1<<11) -/* +/* * Each block request uses at least two segments - one for the header * and one for the status. -*/ +*/ #define VIRTIO_BLK_MIN_SEGMENTS 2 #define VIRTIO_BLK_FLAG_BITS \ VIRTIO_COMMON_FLAG_BITS \ + "\x0c""CONFIG_WCE" \ + "\x0b""TOPOLOGY" \ "\x0a""FLUSH" \ "\x08""SCSI" \ "\x07""BLK_SIZE" \ @@ -91,11 +96,16 @@ __KERNEL_RCSID(0, "$NetBSD: ld_virtio.c, /* Command */ #define VIRTIO_BLK_T_IN 0 #define VIRTIO_BLK_T_OUT 1 +#define VIRTIO_BLK_T_FLUSH 4 #define VIRTIO_BLK_T_BARRIER 0x80000000 +/* Sector */ +#define VIRTIO_BLK_BSIZE 512 + /* Status */ #define VIRTIO_BLK_S_OK 0 #define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 /* Request header structure */ struct virtio_blk_req_hdr { @@ -103,7 +113,7 @@ struct virtio_blk_req_hdr { uint32_t ioprio; uint64_t sector; } __packed; -/* 512*virtio_blk_req_hdr.sector byte payload and 1 byte status follows */ +/* payload and 1 byte status follows */ /* @@ -113,6 +123,7 @@ struct virtio_blk_req { struct virtio_blk_req_hdr vr_hdr; uint8_t vr_status; struct buf *vr_bp; +#define DUMMY_VR_BP ((void *)1) bus_dmamap_t vr_cmdsts; bus_dmamap_t vr_payload; }; @@ -128,6 +139,13 @@ struct ld_virtio_softc { bus_dma_segment_t sc_reqs_seg; int sc_readonly; + + enum { + SYNC_FREE, SYNC_BUSY, SYNC_DONE + } sc_sync_use; + kcondvar_t sc_sync_wait; + kmutex_t sc_sync_wait_lock; + uint8_t sc_sync_status; }; static int ld_virtio_match(device_t, cfdata_t, void *); @@ -151,6 +169,7 @@ ld_virtio_match(device_t parent, cfdata_ static int ld_virtio_vq_done(struct virtqueue *); 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_alloc_reqs(struct ld_virtio_softc *sc, int qsize) @@ -263,7 +282,8 @@ ld_virtio_attach(device_t parent, device virtio_child_attach_start(vsc, self, IPL_BIO, &sc->sc_vq, NULL, virtio_vq_intr, 0, (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_GEOMETRY | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE | + VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_CONFIG_WCE), VIRTIO_BLK_FLAG_BITS); features = virtio_features(vsc); @@ -277,7 +297,7 @@ ld_virtio_attach(device_t parent, device ld->sc_secsize = virtio_read_device_config_4(vsc, VIRTIO_BLK_CONFIG_BLK_SIZE); } else - ld->sc_secsize = 512; + ld->sc_secsize = VIRTIO_BLK_BSIZE; /* At least genfs_io assumes maxxfer == MAXPHYS. */ if (features & VIRTIO_BLK_F_SIZE_MAX) { @@ -327,7 +347,7 @@ ld_virtio_attach(device_t parent, device ld->sc_dv = self; ld->sc_secperunit = virtio_read_device_config_8(vsc, - VIRTIO_BLK_CONFIG_CAPACITY); + VIRTIO_BLK_CONFIG_CAPACITY) / (ld->sc_secsize / VIRTIO_BLK_BSIZE); ld->sc_maxxfer = maxxfersize; if (features & VIRTIO_BLK_F_GEOMETRY) { ld->sc_ncylinders = virtio_read_device_config_2(vsc, @@ -337,13 +357,18 @@ ld_virtio_attach(device_t parent, device ld->sc_nsectors = virtio_read_device_config_1(vsc, VIRTIO_BLK_CONFIG_GEOMETRY_S); } - ld->sc_maxqueuecnt = qsize; + ld->sc_maxqueuecnt = qsize - 1; /* reserve slot for dumps, flushes */ if (ld_virtio_alloc_reqs(sc, qsize) < 0) goto err; + cv_init(&sc->sc_sync_wait, "vblksync"); + mutex_init(&sc->sc_sync_wait_lock, MUTEX_DEFAULT, IPL_BIO); + sc->sc_sync_use = SYNC_FREE; + ld->sc_dump = ld_virtio_dump; ld->sc_start = ld_virtio_start; + ld->sc_ioctl = ld_virtio_ioctl; ld->sc_flags = LDF_ENABLED | LDF_MPSAFE; ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); @@ -398,7 +423,8 @@ ld_virtio_start(struct ld_softc *ld, str vr->vr_bp = bp; vr->vr_hdr.type = isread?VIRTIO_BLK_T_IN:VIRTIO_BLK_T_OUT; vr->vr_hdr.ioprio = 0; - vr->vr_hdr.sector = bp->b_rawblkno * sc->sc_ld.sc_secsize / 512; + vr->vr_hdr.sector = bp->b_rawblkno * sc->sc_ld.sc_secsize / + VIRTIO_BLK_BSIZE; bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts, 0, sizeof(struct virtio_blk_req_hdr), @@ -436,13 +462,23 @@ ld_virtio_vq_done1(struct ld_virtio_soft bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts, 0, sizeof(struct virtio_blk_req_hdr), BUS_DMASYNC_POSTWRITE); + bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts, + sizeof(struct virtio_blk_req_hdr), sizeof(uint8_t), + BUS_DMASYNC_POSTREAD); + if (bp == DUMMY_VR_BP) { + mutex_enter(&sc->sc_sync_wait_lock); + sc->sc_sync_status = vr->vr_status; + sc->sc_sync_use = SYNC_DONE; + cv_signal(&sc->sc_sync_wait); + mutex_exit(&sc->sc_sync_wait_lock); + 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); - bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts, - sizeof(struct virtio_blk_req_hdr), sizeof(uint8_t), - BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(virtio_dmat(vsc), vr->vr_payload); if (vr->vr_status != VIRTIO_BLK_S_OK) { bp->b_error = EIO; @@ -504,7 +540,7 @@ ld_virtio_dump(struct ld_softc *ld, void if (r != 0) return r; - r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs + + r = virtio_enqueue_reserve(vsc, vq, slot, vr->vr_payload->dm_nsegs + VIRTIO_BLK_MIN_SEGMENTS); if (r != 0) { bus_dmamap_unload(virtio_dmat(vsc), vr->vr_payload); @@ -514,7 +550,8 @@ ld_virtio_dump(struct ld_softc *ld, void vr->vr_bp = (void*)0xdeadbeef; vr->vr_hdr.type = VIRTIO_BLK_T_OUT; vr->vr_hdr.ioprio = 0; - vr->vr_hdr.sector = (daddr_t) blkno * ld->sc_secsize / 512; + vr->vr_hdr.sector = (daddr_t) blkno * ld->sc_secsize / + VIRTIO_BLK_BSIZE; bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts, 0, sizeof(struct virtio_blk_req_hdr), @@ -549,7 +586,7 @@ ld_virtio_dump(struct ld_softc *ld, void } else break; } - + bus_dmamap_sync(virtio_dmat(vsc), vr->vr_cmdsts, 0, sizeof(struct virtio_blk_req_hdr), BUS_DMASYNC_POSTWRITE); @@ -596,11 +633,158 @@ ld_virtio_detach(device_t self, int flag ldenddetach(ld); + cv_destroy(&sc->sc_sync_wait); + mutex_destroy(&sc->sc_sync_wait_lock); + virtio_child_detach(sc->sc_virtio); return 0; } +static int +ld_virtio_flush(struct ld_softc *ld, bool poll) +{ + struct ld_virtio_softc * const sc = device_private(ld->sc_dv); + struct virtio_softc * const vsc = sc->sc_virtio; + const uint32_t features = virtio_features(vsc); + struct virtqueue *vq = &sc->sc_vq; + struct virtio_blk_req *vr; + int slot; + int r; + + if ((features & VIRTIO_BLK_F_FLUSH) == 0) + return 0; + + mutex_enter(&sc->sc_sync_wait_lock); + while (sc->sc_sync_use != SYNC_FREE) { + if (poll) { + mutex_exit(&sc->sc_sync_wait_lock); + ld_virtio_vq_done(vq); + mutex_enter(&sc->sc_sync_wait_lock); + continue; + } + cv_wait(&sc->sc_sync_wait, &sc->sc_sync_wait_lock); + } + sc->sc_sync_use = SYNC_BUSY; + mutex_exit(&sc->sc_sync_wait_lock); + + r = virtio_enqueue_prep(vsc, vq, &slot); + if (r != 0) { + return r; + } + + vr = &sc->sc_reqs[slot]; + KASSERT(vr->vr_bp == NULL); + + r = virtio_enqueue_reserve(vsc, vq, slot, VIRTIO_BLK_MIN_SEGMENTS); + if (r != 0) { + return r; + } + + vr->vr_bp = DUMMY_VR_BP; + vr->vr_hdr.type = VIRTIO_BLK_T_FLUSH; + vr->vr_hdr.ioprio = 0; + vr->vr_hdr.sector = 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_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_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); + + mutex_enter(&sc->sc_sync_wait_lock); + while (sc->sc_sync_use != SYNC_DONE) { + if (poll) { + mutex_exit(&sc->sc_sync_wait_lock); + ld_virtio_vq_done(vq); + mutex_enter(&sc->sc_sync_wait_lock); + continue; + } + cv_wait(&sc->sc_sync_wait, &sc->sc_sync_wait_lock); + } + + if (sc->sc_sync_status == VIRTIO_BLK_S_OK) + r = 0; + else + r = EIO; + + sc->sc_sync_use = SYNC_FREE; + cv_signal(&sc->sc_sync_wait); + mutex_exit(&sc->sc_sync_wait_lock); + + return r; +} + +static int +ld_virtio_getcache(struct ld_softc *ld, int *bitsp) +{ + struct ld_virtio_softc * const sc = device_private(ld->sc_dv); + struct virtio_softc * const vsc = sc->sc_virtio; + const uint32_t features = virtio_features(vsc); + + *bitsp = DKCACHE_READ; + if ((features & VIRTIO_BLK_F_CONFIG_WCE) != 0) + *bitsp |= DKCACHE_WCHANGE; + if (virtio_read_device_config_1(vsc, + VIRTIO_BLK_CONFIG_WRITEBACK) != 0x00) + *bitsp |= DKCACHE_WRITE; + + return 0; +} + +static int +ld_virtio_setcache(struct ld_softc *ld, int bits) +{ + struct ld_virtio_softc * const sc = device_private(ld->sc_dv); + struct virtio_softc * const vsc = sc->sc_virtio; + const uint8_t wce = (bits & DKCACHE_WRITE) ? 0x01 : 0x00; + + virtio_write_device_config_1(vsc, + VIRTIO_BLK_CONFIG_WRITEBACK, wce); + if (virtio_read_device_config_1(vsc, + VIRTIO_BLK_CONFIG_WRITEBACK) != wce) + return EIO; + + return 0; +} + +static int +ld_virtio_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, bool poll) +{ + int error; + + switch (cmd) { + case DIOCCACHESYNC: + error = ld_virtio_flush(ld, poll); + break; + + case DIOCGCACHE: + error = ld_virtio_getcache(ld, (int *)addr); + break; + + case DIOCSCACHE: + error = ld_virtio_setcache(ld, *(int *)addr); + break; + + default: + error = EPASSTHROUGH; + break; + } + + return error; +} + MODULE(MODULE_CLASS_DRIVER, ld_virtio, "ld,virtio"); #ifdef _MODULE @@ -612,7 +796,7 @@ MODULE(MODULE_CLASS_DRIVER, ld_virtio, " #define CFDRIVER_DECL(name, class, attr) #include "ioconf.c" #endif - + static int ld_virtio_modcmd(modcmd_t cmd, void *opaque) { @@ -624,7 +808,7 @@ ld_virtio_modcmd(modcmd_t cmd, void *opa static struct cfdriver * const no_cfdriver_vec[] = { NULL }; #endif int error = 0; - + #ifdef _MODULE switch (cmd) { case MODULE_CMD_INIT: