1*4d9ed068Ssf /* $OpenBSD: virtio.c,v 1.37 2025/01/09 10:55:22 sf Exp $ */ 2ff2c981aSreyk /* $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */ 3ff2c981aSreyk 4ff2c981aSreyk /* 5ff2c981aSreyk * Copyright (c) 2012 Stefan Fritsch, Alexander Fiveg. 6ff2c981aSreyk * Copyright (c) 2010 Minoura Makoto. 7ff2c981aSreyk * All rights reserved. 8ff2c981aSreyk * 9ff2c981aSreyk * Redistribution and use in source and binary forms, with or without 10ff2c981aSreyk * modification, are permitted provided that the following conditions 11ff2c981aSreyk * are met: 12ff2c981aSreyk * 1. Redistributions of source code must retain the above copyright 13ff2c981aSreyk * notice, this list of conditions and the following disclaimer. 14ff2c981aSreyk * 2. Redistributions in binary form must reproduce the above copyright 15ff2c981aSreyk * notice, this list of conditions and the following disclaimer in the 16ff2c981aSreyk * documentation and/or other materials provided with the distribution. 17ff2c981aSreyk * 18ff2c981aSreyk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19ff2c981aSreyk * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20ff2c981aSreyk * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21ff2c981aSreyk * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22ff2c981aSreyk * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23ff2c981aSreyk * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24ff2c981aSreyk * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25ff2c981aSreyk * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26ff2c981aSreyk * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27ff2c981aSreyk * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28ff2c981aSreyk */ 29ff2c981aSreyk 30ff2c981aSreyk #include <sys/param.h> 31ff2c981aSreyk #include <sys/systm.h> 32ff2c981aSreyk #include <sys/device.h> 33ff2c981aSreyk #include <sys/atomic.h> 34ff2c981aSreyk #include <sys/malloc.h> 35ff2c981aSreyk 36ff2c981aSreyk #include <dev/pv/virtioreg.h> 37ff2c981aSreyk #include <dev/pv/virtiovar.h> 38ff2c981aSreyk 39ff2c981aSreyk #if VIRTIO_DEBUG 40ff2c981aSreyk #define VIRTIO_ASSERT(x) KASSERT(x) 41ff2c981aSreyk #else 42ff2c981aSreyk #define VIRTIO_ASSERT(x) 43ff2c981aSreyk #endif 44ff2c981aSreyk 45ff2c981aSreyk void virtio_init_vq(struct virtio_softc *, 4642004ccbSsf struct virtqueue *); 47ff2c981aSreyk void vq_free_entry(struct virtqueue *, struct vq_entry *); 48ff2c981aSreyk struct vq_entry *vq_alloc_entry(struct virtqueue *); 49ff2c981aSreyk 50ff2c981aSreyk struct cfdriver virtio_cd = { 51ff2c981aSreyk NULL, "virtio", DV_DULL 52ff2c981aSreyk }; 53ff2c981aSreyk 54ff2c981aSreyk static const char * const virtio_device_name[] = { 55ff2c981aSreyk "Unknown (0)", /* 0 */ 56ff2c981aSreyk "Network", /* 1 */ 57ff2c981aSreyk "Block", /* 2 */ 58ff2c981aSreyk "Console", /* 3 */ 59ff2c981aSreyk "Entropy", /* 4 */ 60ff2c981aSreyk "Memory Balloon", /* 5 */ 61ff2c981aSreyk "IO Memory", /* 6 */ 62ff2c981aSreyk "Rpmsg", /* 7 */ 63ff2c981aSreyk "SCSI host", /* 8 */ 6479c8d24aSreyk "9P Transport", /* 9 */ 65e208b562Sjcs "mac80211 wlan", /* 10 */ 66e208b562Sjcs NULL, /* 11 */ 67e208b562Sjcs NULL, /* 12 */ 68e208b562Sjcs NULL, /* 13 */ 69e208b562Sjcs NULL, /* 14 */ 70e208b562Sjcs NULL, /* 15 */ 71e208b562Sjcs "GPU", /* 16 */ 72ff2c981aSreyk }; 73ff2c981aSreyk #define NDEVNAMES (sizeof(virtio_device_name)/sizeof(char*)) 74ff2c981aSreyk 75e578ff6aSsf const char * 76e578ff6aSsf virtio_device_string(int id) 77e578ff6aSsf { 78e578ff6aSsf return id < NDEVNAMES ? virtio_device_name[id] : "Unknown"; 79e578ff6aSsf } 80e578ff6aSsf 81e578ff6aSsf #if VIRTIO_DEBUG 82ff2c981aSreyk static const struct virtio_feature_name transport_feature_names[] = { 83ff2c981aSreyk { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty"}, 846354d80bSsf { VIRTIO_F_ANY_LAYOUT, "AnyLayout"}, 85ff2c981aSreyk { VIRTIO_F_RING_INDIRECT_DESC, "RingIndirectDesc"}, 86ff2c981aSreyk { VIRTIO_F_RING_EVENT_IDX, "RingEventIdx"}, 87ff2c981aSreyk { VIRTIO_F_BAD_FEATURE, "BadFeature"}, 88b3dbd5f2Ssf { VIRTIO_F_VERSION_1, "Version1"}, 896354d80bSsf { VIRTIO_F_ACCESS_PLATFORM, "AccessPlatf"}, 906354d80bSsf { VIRTIO_F_RING_PACKED, "RingPacked"}, 916354d80bSsf { VIRTIO_F_IN_ORDER, "InOrder"}, 926354d80bSsf { VIRTIO_F_ORDER_PLATFORM, "OrderPlatf"}, 936354d80bSsf { VIRTIO_F_SR_IOV, "SrIov"}, 946354d80bSsf { VIRTIO_F_NOTIFICATION_DATA, "NotifData"}, 956354d80bSsf { VIRTIO_F_NOTIF_CONFIG_DATA, "NotifConfData"}, 966354d80bSsf { VIRTIO_F_RING_RESET, "RingReset"}, 97ff2c981aSreyk { 0, NULL} 98ff2c981aSreyk }; 99ff2c981aSreyk 100ff2c981aSreyk void 101750a9ae1Ssf virtio_log_features(uint64_t host, uint64_t neg, 102ff2c981aSreyk const struct virtio_feature_name *guest_feature_names) 103ff2c981aSreyk { 104ff2c981aSreyk const struct virtio_feature_name *namep; 105ff2c981aSreyk int i; 106ff2c981aSreyk char c; 107c0ea3bd1Ssf uint64_t bit; 108ff2c981aSreyk 109750a9ae1Ssf for (i = 0; i < 64; i++) { 110ff2c981aSreyk if (i == 30) { 111ff2c981aSreyk /* 112ff2c981aSreyk * VIRTIO_F_BAD_FEATURE is only used for 113ff2c981aSreyk * checking correct negotiation 114ff2c981aSreyk */ 115ff2c981aSreyk continue; 116ff2c981aSreyk } 117c0ea3bd1Ssf bit = 1ULL << i; 118ff2c981aSreyk if ((host&bit) == 0) 119ff2c981aSreyk continue; 120c0ea3bd1Ssf namep = guest_feature_names; 121ff2c981aSreyk while (namep->bit && namep->bit != bit) 122ff2c981aSreyk namep++; 123c0ea3bd1Ssf if (namep->name == NULL) { 124c0ea3bd1Ssf namep = transport_feature_names; 125c0ea3bd1Ssf while (namep->bit && namep->bit != bit) 126c0ea3bd1Ssf namep++; 127c0ea3bd1Ssf } 128ff2c981aSreyk c = (neg&bit) ? '+' : '-'; 129ff2c981aSreyk if (namep->name) 130ff2c981aSreyk printf(" %c%s", c, namep->name); 131ff2c981aSreyk else 132ff2c981aSreyk printf(" %cUnknown(%d)", c, i); 133ff2c981aSreyk } 134ff2c981aSreyk } 135e578ff6aSsf #endif 136ff2c981aSreyk 137ff2c981aSreyk /* 138ff2c981aSreyk * Reset the device. 139ff2c981aSreyk */ 140ff2c981aSreyk /* 141ff2c981aSreyk * To reset the device to a known state, do following: 142ff2c981aSreyk * virtio_reset(sc); // this will stop the device activity 143ff2c981aSreyk * <dequeue finished requests>; // virtio_dequeue() still can be called 144ff2c981aSreyk * <revoke pending requests in the vqs if any>; 1454b1a56afSjsg * virtio_reinit_start(sc); // dequeue prohibited 146ff2c981aSreyk * <some other initialization>; 147ff2c981aSreyk * virtio_reinit_end(sc); // device activated; enqueue allowed 1480091658aSsf * Once attached, features are assumed to not change again. 149ff2c981aSreyk */ 150ff2c981aSreyk void 151ff2c981aSreyk virtio_reset(struct virtio_softc *sc) 152ff2c981aSreyk { 153ff2c981aSreyk virtio_device_reset(sc); 1540091658aSsf sc->sc_active_features = 0; 155ff2c981aSreyk } 156ff2c981aSreyk 15777d0f823Ssf int 15877d0f823Ssf virtio_attach_finish(struct virtio_softc *sc, struct virtio_attach_args *va) 15977d0f823Ssf { 16077d0f823Ssf int i, ret; 16177d0f823Ssf 16277d0f823Ssf ret = sc->sc_ops->attach_finish(sc, va); 16377d0f823Ssf if (ret != 0) 16477d0f823Ssf return ret; 16577d0f823Ssf 16677d0f823Ssf sc->sc_ops->setup_intrs(sc); 16777d0f823Ssf for (i = 0; i < sc->sc_nvqs; i++) { 16877d0f823Ssf struct virtqueue *vq = &sc->sc_vqs[i]; 16977d0f823Ssf 170*4d9ed068Ssf if (vq->vq_num == 0) 171*4d9ed068Ssf continue; 17277d0f823Ssf virtio_setup_queue(sc, vq, vq->vq_dmamap->dm_segs[0].ds_addr); 17377d0f823Ssf } 17477d0f823Ssf virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); 17577d0f823Ssf return 0; 17677d0f823Ssf } 17777d0f823Ssf 178ff2c981aSreyk void 179ff2c981aSreyk virtio_reinit_start(struct virtio_softc *sc) 180ff2c981aSreyk { 181ff2c981aSreyk int i; 182ff2c981aSreyk 183ff2c981aSreyk virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_ACK); 184ff2c981aSreyk virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER); 1850091658aSsf virtio_negotiate_features(sc, NULL); 18677d0f823Ssf sc->sc_ops->setup_intrs(sc); 187ff2c981aSreyk for (i = 0; i < sc->sc_nvqs; i++) { 188ff2c981aSreyk int n; 189ff2c981aSreyk struct virtqueue *vq = &sc->sc_vqs[i]; 190*4d9ed068Ssf if (vq->vq_num == 0) /* not used */ 191ff2c981aSreyk continue; 192*4d9ed068Ssf n = virtio_read_queue_size(sc, vq->vq_index); 193ff2c981aSreyk if (n != vq->vq_num) { 1944123b6a7Sderaadt panic("%s: virtqueue size changed, vq index %d", 195ff2c981aSreyk sc->sc_dev.dv_xname, vq->vq_index); 196ff2c981aSreyk } 19742004ccbSsf virtio_init_vq(sc, vq); 198ee262b2eSsf virtio_setup_queue(sc, vq, vq->vq_dmamap->dm_segs[0].ds_addr); 199ff2c981aSreyk } 200ff2c981aSreyk } 201ff2c981aSreyk 202ff2c981aSreyk void 203ff2c981aSreyk virtio_reinit_end(struct virtio_softc *sc) 204ff2c981aSreyk { 205ff2c981aSreyk virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); 206ff2c981aSreyk } 207ff2c981aSreyk 208ff2c981aSreyk /* 209ff2c981aSreyk * dmamap sync operations for a virtqueue. 210d3638ce2Ssf * 211d3638ce2Ssf * XXX These should be more fine grained. Syncing the whole ring if we 212d3638ce2Ssf * XXX only need a few bytes is inefficient if we use bounce buffers. 213ff2c981aSreyk */ 214ff2c981aSreyk static inline void 215ff2c981aSreyk vq_sync_descs(struct virtio_softc *sc, struct virtqueue *vq, int ops) 216ff2c981aSreyk { 217ff2c981aSreyk /* availoffset == sizeof(vring_desc)*vq_num */ 218ff2c981aSreyk bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, 0, vq->vq_availoffset, 219ff2c981aSreyk ops); 220ff2c981aSreyk } 221ff2c981aSreyk 222ff2c981aSreyk static inline void 223ff2c981aSreyk vq_sync_aring(struct virtio_softc *sc, struct virtqueue *vq, int ops) 224ff2c981aSreyk { 225ff2c981aSreyk bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_availoffset, 226ff2c981aSreyk offsetof(struct vring_avail, ring) + vq->vq_num * sizeof(uint16_t), 227ff2c981aSreyk ops); 228ff2c981aSreyk } 229ff2c981aSreyk 230ff2c981aSreyk static inline void 231d3638ce2Ssf vq_sync_aring_used_event(struct virtio_softc *sc, struct virtqueue *vq, int ops) 232d3638ce2Ssf { 233d3638ce2Ssf bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_availoffset + 234d3638ce2Ssf offsetof(struct vring_avail, ring) + vq->vq_num * sizeof(uint16_t), 235d3638ce2Ssf sizeof(uint16_t), ops); 236d3638ce2Ssf } 237d3638ce2Ssf 238d3638ce2Ssf 239d3638ce2Ssf static inline void 240ff2c981aSreyk vq_sync_uring(struct virtio_softc *sc, struct virtqueue *vq, int ops) 241ff2c981aSreyk { 242ff2c981aSreyk bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, vq->vq_usedoffset, 243ff2c981aSreyk offsetof(struct vring_used, ring) + vq->vq_num * 244ff2c981aSreyk sizeof(struct vring_used_elem), ops); 245ff2c981aSreyk } 246ff2c981aSreyk 247ff2c981aSreyk static inline void 248d3638ce2Ssf vq_sync_uring_avail_event(struct virtio_softc *sc, struct virtqueue *vq, int ops) 249d3638ce2Ssf { 250d3638ce2Ssf bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, 251d3638ce2Ssf vq->vq_usedoffset + offsetof(struct vring_used, ring) + 252d3638ce2Ssf vq->vq_num * sizeof(struct vring_used_elem), sizeof(uint16_t), 253d3638ce2Ssf ops); 254d3638ce2Ssf } 255d3638ce2Ssf 256d3638ce2Ssf 257d3638ce2Ssf static inline void 258ff2c981aSreyk vq_sync_indirect(struct virtio_softc *sc, struct virtqueue *vq, int slot, 259ff2c981aSreyk int ops) 260ff2c981aSreyk { 261ff2c981aSreyk int offset = vq->vq_indirectoffset + 262ff2c981aSreyk sizeof(struct vring_desc) * vq->vq_maxnsegs * slot; 263ff2c981aSreyk 264ff2c981aSreyk bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, offset, 265ff2c981aSreyk sizeof(struct vring_desc) * vq->vq_maxnsegs, ops); 266ff2c981aSreyk } 267ff2c981aSreyk 268ff2c981aSreyk /* 269ff2c981aSreyk * Scan vq, bus_dmamap_sync for the vqs (not for the payload), 270ff2c981aSreyk * and calls (*vq_done)() if some entries are consumed. 271ff2c981aSreyk * For use in transport specific irq handlers. 272ff2c981aSreyk */ 273ff2c981aSreyk int 274ff2c981aSreyk virtio_check_vqs(struct virtio_softc *sc) 275ff2c981aSreyk { 276ff2c981aSreyk int i, r = 0; 277ff2c981aSreyk 278ff2c981aSreyk /* going backwards is better for if_vio */ 279*4d9ed068Ssf for (i = sc->sc_nvqs - 1; i >= 0; i--) { 280*4d9ed068Ssf if (sc->sc_vqs[i].vq_num == 0) /* not used */ 281*4d9ed068Ssf continue; 2826c89734dSpatrick r |= virtio_check_vq(sc, &sc->sc_vqs[i]); 283*4d9ed068Ssf } 2846c89734dSpatrick 2856c89734dSpatrick return r; 2866c89734dSpatrick } 2876c89734dSpatrick 2886c89734dSpatrick int 2896c89734dSpatrick virtio_check_vq(struct virtio_softc *sc, struct virtqueue *vq) 2906c89734dSpatrick { 291ff2c981aSreyk if (vq->vq_queued) { 292ff2c981aSreyk vq->vq_queued = 0; 293ff2c981aSreyk vq_sync_aring(sc, vq, BUS_DMASYNC_POSTWRITE); 294ff2c981aSreyk } 295ff2c981aSreyk vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); 296ff2c981aSreyk if (vq->vq_used_idx != vq->vq_used->idx) { 297ff2c981aSreyk if (vq->vq_done) 2986c89734dSpatrick return (vq->vq_done)(vq); 299ff2c981aSreyk } 300ff2c981aSreyk 3016c89734dSpatrick return 0; 302ff2c981aSreyk } 303ff2c981aSreyk 304ff2c981aSreyk /* 305ff2c981aSreyk * Initialize vq structure. 306ff2c981aSreyk */ 307ff2c981aSreyk void 30842004ccbSsf virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq) 309ff2c981aSreyk { 310ff2c981aSreyk int i, j; 311ff2c981aSreyk int vq_size = vq->vq_num; 312ff2c981aSreyk 313*4d9ed068Ssf VIRTIO_ASSERT(vq_size > 0); 314ff2c981aSreyk memset(vq->vq_vaddr, 0, vq->vq_bytesize); 315ff2c981aSreyk 316ff2c981aSreyk /* build the indirect descriptor chain */ 317ff2c981aSreyk if (vq->vq_indirect != NULL) { 318ff2c981aSreyk struct vring_desc *vd; 319ff2c981aSreyk 320ff2c981aSreyk for (i = 0; i < vq_size; i++) { 321ff2c981aSreyk vd = vq->vq_indirect; 322ff2c981aSreyk vd += vq->vq_maxnsegs * i; 323ff2c981aSreyk for (j = 0; j < vq->vq_maxnsegs-1; j++) 324ff2c981aSreyk vd[j].next = j + 1; 325ff2c981aSreyk } 326ff2c981aSreyk } 327ff2c981aSreyk 328ff2c981aSreyk /* free slot management */ 329c07e6025Ssf SLIST_INIT(&vq->vq_freelist); 330109703c5Ssf /* 331109703c5Ssf * virtio_enqueue_trim needs monotonely raising entries, therefore 332109703c5Ssf * initialize in reverse order 333109703c5Ssf */ 334109703c5Ssf for (i = vq_size - 1; i >= 0; i--) { 335c07e6025Ssf SLIST_INSERT_HEAD(&vq->vq_freelist, &vq->vq_entries[i], 336c07e6025Ssf qe_list); 337ff2c981aSreyk vq->vq_entries[i].qe_index = i; 338ff2c981aSreyk } 339ff2c981aSreyk 340657921cbSsf bus_dmamap_sync(sc->sc_dmat, vq->vq_dmamap, 0, vq->vq_bytesize, 341657921cbSsf BUS_DMASYNC_PREWRITE); 342ff2c981aSreyk /* enqueue/dequeue status */ 343ff2c981aSreyk vq->vq_avail_idx = 0; 344ff2c981aSreyk vq->vq_used_idx = 0; 345ff2c981aSreyk vq_sync_uring(sc, vq, BUS_DMASYNC_PREREAD); 346ff2c981aSreyk vq->vq_queued = 1; 347ff2c981aSreyk } 348ff2c981aSreyk 349ff2c981aSreyk /* 350ff2c981aSreyk * Allocate/free a vq. 35154f891c2Ssf * 35254f891c2Ssf * maxnsegs denotes how much space should be allocated for indirect 35354f891c2Ssf * descriptors. maxnsegs == 1 can be used to disable use indirect 35454f891c2Ssf * descriptors for this queue. 355ff2c981aSreyk */ 356ff2c981aSreyk int 357ff2c981aSreyk virtio_alloc_vq(struct virtio_softc *sc, struct virtqueue *vq, int index, 358fdf28b39Ssf int maxnsegs, const char *name) 359ff2c981aSreyk { 360ff2c981aSreyk int vq_size, allocsize1, allocsize2, allocsize3, allocsize = 0; 361ff2c981aSreyk int rsegs, r, hdrlen; 362ff2c981aSreyk #define VIRTQUEUE_ALIGN(n) (((n)+(VIRTIO_PAGE_SIZE-1))& \ 363ff2c981aSreyk ~(VIRTIO_PAGE_SIZE-1)) 364ff2c981aSreyk 365ff2c981aSreyk memset(vq, 0, sizeof(*vq)); 366ff2c981aSreyk 367ff2c981aSreyk vq_size = virtio_read_queue_size(sc, index); 368ff2c981aSreyk if (vq_size == 0) { 369ff2c981aSreyk printf("virtqueue not exist, index %d for %s\n", index, name); 370ff2c981aSreyk goto err; 371ff2c981aSreyk } 372ff2c981aSreyk if (((vq_size - 1) & vq_size) != 0) 373ff2c981aSreyk panic("vq_size not power of two: %d", vq_size); 374ff2c981aSreyk 3750091658aSsf hdrlen = virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX) ? 3 : 2; 376ff2c981aSreyk 377ff2c981aSreyk /* allocsize1: descriptor table + avail ring + pad */ 378ff2c981aSreyk allocsize1 = VIRTQUEUE_ALIGN(sizeof(struct vring_desc) * vq_size 379ff2c981aSreyk + sizeof(uint16_t) * (hdrlen + vq_size)); 380ff2c981aSreyk /* allocsize2: used ring + pad */ 381ff2c981aSreyk allocsize2 = VIRTQUEUE_ALIGN(sizeof(uint16_t) * hdrlen 382ff2c981aSreyk + sizeof(struct vring_used_elem) * vq_size); 383ff2c981aSreyk /* allocsize3: indirect table */ 38454f891c2Ssf if (sc->sc_indirect && maxnsegs > 1) 385ff2c981aSreyk allocsize3 = sizeof(struct vring_desc) * maxnsegs * vq_size; 386ff2c981aSreyk else 387ff2c981aSreyk allocsize3 = 0; 388ff2c981aSreyk allocsize = allocsize1 + allocsize2 + allocsize3; 389ff2c981aSreyk 390ff2c981aSreyk /* alloc and map the memory */ 391ff2c981aSreyk r = bus_dmamem_alloc(sc->sc_dmat, allocsize, VIRTIO_PAGE_SIZE, 0, 392ff2c981aSreyk &vq->vq_segs[0], 1, &rsegs, BUS_DMA_NOWAIT); 393ff2c981aSreyk if (r != 0) { 394ff2c981aSreyk printf("virtqueue %d for %s allocation failed, error %d\n", 395ff2c981aSreyk index, name, r); 396ff2c981aSreyk goto err; 397ff2c981aSreyk } 398ff2c981aSreyk r = bus_dmamem_map(sc->sc_dmat, &vq->vq_segs[0], 1, allocsize, 399ff2c981aSreyk (caddr_t*)&vq->vq_vaddr, BUS_DMA_NOWAIT); 400ff2c981aSreyk if (r != 0) { 401ff2c981aSreyk printf("virtqueue %d for %s map failed, error %d\n", index, 402ff2c981aSreyk name, r); 403ff2c981aSreyk goto err; 404ff2c981aSreyk } 405ff2c981aSreyk r = bus_dmamap_create(sc->sc_dmat, allocsize, 1, allocsize, 0, 406ff2c981aSreyk BUS_DMA_NOWAIT, &vq->vq_dmamap); 407ff2c981aSreyk if (r != 0) { 408ff2c981aSreyk printf("virtqueue %d for %s dmamap creation failed, " 409ff2c981aSreyk "error %d\n", index, name, r); 410ff2c981aSreyk goto err; 411ff2c981aSreyk } 412ff2c981aSreyk r = bus_dmamap_load(sc->sc_dmat, vq->vq_dmamap, vq->vq_vaddr, 413ff2c981aSreyk allocsize, NULL, BUS_DMA_NOWAIT); 414ff2c981aSreyk if (r != 0) { 415ff2c981aSreyk printf("virtqueue %d for %s dmamap load failed, error %d\n", 416ff2c981aSreyk index, name, r); 417ff2c981aSreyk goto err; 418ff2c981aSreyk } 419ff2c981aSreyk 420ff2c981aSreyk /* remember addresses and offsets for later use */ 421ff2c981aSreyk vq->vq_owner = sc; 422ff2c981aSreyk vq->vq_num = vq_size; 423ff2c981aSreyk vq->vq_mask = vq_size - 1; 424ff2c981aSreyk vq->vq_index = index; 425ff2c981aSreyk vq->vq_desc = vq->vq_vaddr; 426ff2c981aSreyk vq->vq_availoffset = sizeof(struct vring_desc)*vq_size; 427ff2c981aSreyk vq->vq_avail = (struct vring_avail*)(((char*)vq->vq_desc) + 428ff2c981aSreyk vq->vq_availoffset); 429ff2c981aSreyk vq->vq_usedoffset = allocsize1; 430ff2c981aSreyk vq->vq_used = (struct vring_used*)(((char*)vq->vq_desc) + 431ff2c981aSreyk vq->vq_usedoffset); 432ff2c981aSreyk if (allocsize3 > 0) { 433ff2c981aSreyk vq->vq_indirectoffset = allocsize1 + allocsize2; 434ff2c981aSreyk vq->vq_indirect = (void*)(((char*)vq->vq_desc) 435ff2c981aSreyk + vq->vq_indirectoffset); 436ff2c981aSreyk } 437ff2c981aSreyk vq->vq_bytesize = allocsize; 438ff2c981aSreyk vq->vq_maxnsegs = maxnsegs; 439ff2c981aSreyk 440ff2c981aSreyk /* free slot management */ 441ff2c981aSreyk vq->vq_entries = mallocarray(vq_size, sizeof(struct vq_entry), 442ff2c981aSreyk M_DEVBUF, M_NOWAIT | M_ZERO); 443ff2c981aSreyk if (vq->vq_entries == NULL) { 444ff2c981aSreyk r = ENOMEM; 445ff2c981aSreyk goto err; 446ff2c981aSreyk } 447ff2c981aSreyk 44842004ccbSsf virtio_init_vq(sc, vq); 449ff2c981aSreyk 450ff2c981aSreyk #if VIRTIO_DEBUG 451ff2c981aSreyk printf("\nallocated %u byte for virtqueue %d for %s, size %d\n", 452ff2c981aSreyk allocsize, index, name, vq_size); 453ff2c981aSreyk if (allocsize3 > 0) 454ff2c981aSreyk printf("using %d byte (%d entries) indirect descriptors\n", 455ff2c981aSreyk allocsize3, maxnsegs * vq_size); 456ff2c981aSreyk #endif 457ff2c981aSreyk return 0; 458ff2c981aSreyk 459ff2c981aSreyk err: 460ff2c981aSreyk if (vq->vq_dmamap) 461ff2c981aSreyk bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap); 462ff2c981aSreyk if (vq->vq_vaddr) 463ff2c981aSreyk bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, allocsize); 464ff2c981aSreyk if (vq->vq_segs[0].ds_addr) 465ff2c981aSreyk bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1); 466ff2c981aSreyk memset(vq, 0, sizeof(*vq)); 467ff2c981aSreyk 468ff2c981aSreyk return -1; 469ff2c981aSreyk } 470ff2c981aSreyk 471ff2c981aSreyk int 472ff2c981aSreyk virtio_free_vq(struct virtio_softc *sc, struct virtqueue *vq) 473ff2c981aSreyk { 474ff2c981aSreyk struct vq_entry *qe; 475ff2c981aSreyk int i = 0; 476ff2c981aSreyk 477*4d9ed068Ssf if (vq->vq_num == 0) { 478*4d9ed068Ssf /* virtio_alloc_vq() was never called */ 479*4d9ed068Ssf return 0; 480*4d9ed068Ssf } 481*4d9ed068Ssf 482ff2c981aSreyk /* device must be already deactivated */ 483ff2c981aSreyk /* confirm the vq is empty */ 484c07e6025Ssf SLIST_FOREACH(qe, &vq->vq_freelist, qe_list) { 485ff2c981aSreyk i++; 486ff2c981aSreyk } 487ff2c981aSreyk if (i != vq->vq_num) { 488ff2c981aSreyk printf("%s: freeing non-empty vq, index %d\n", 489ff2c981aSreyk sc->sc_dev.dv_xname, vq->vq_index); 490ff2c981aSreyk return EBUSY; 491ff2c981aSreyk } 492ff2c981aSreyk 493ff2c981aSreyk /* tell device that there's no virtqueue any longer */ 494ee262b2eSsf virtio_setup_queue(sc, vq, 0); 495ff2c981aSreyk 496ff2c981aSreyk free(vq->vq_entries, M_DEVBUF, 0); 497ff2c981aSreyk bus_dmamap_unload(sc->sc_dmat, vq->vq_dmamap); 498ff2c981aSreyk bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap); 499ff2c981aSreyk bus_dmamem_unmap(sc->sc_dmat, vq->vq_vaddr, vq->vq_bytesize); 500ff2c981aSreyk bus_dmamem_free(sc->sc_dmat, &vq->vq_segs[0], 1); 501ff2c981aSreyk memset(vq, 0, sizeof(*vq)); 502ff2c981aSreyk 503ff2c981aSreyk return 0; 504ff2c981aSreyk } 505ff2c981aSreyk 506ff2c981aSreyk /* 507ff2c981aSreyk * Free descriptor management. 508ff2c981aSreyk */ 509ff2c981aSreyk struct vq_entry * 510ff2c981aSreyk vq_alloc_entry(struct virtqueue *vq) 511ff2c981aSreyk { 512ff2c981aSreyk struct vq_entry *qe; 513ff2c981aSreyk 514c07e6025Ssf if (SLIST_EMPTY(&vq->vq_freelist)) 515ff2c981aSreyk return NULL; 516c07e6025Ssf qe = SLIST_FIRST(&vq->vq_freelist); 517c07e6025Ssf SLIST_REMOVE_HEAD(&vq->vq_freelist, qe_list); 518ff2c981aSreyk 519ff2c981aSreyk return qe; 520ff2c981aSreyk } 521ff2c981aSreyk 522ff2c981aSreyk void 523ff2c981aSreyk vq_free_entry(struct virtqueue *vq, struct vq_entry *qe) 524ff2c981aSreyk { 525c07e6025Ssf SLIST_INSERT_HEAD(&vq->vq_freelist, qe, qe_list); 526ff2c981aSreyk } 527ff2c981aSreyk 528ff2c981aSreyk /* 529ff2c981aSreyk * Enqueue several dmamaps as a single request. 530ff2c981aSreyk */ 531ff2c981aSreyk /* 532ff2c981aSreyk * Typical usage: 533ff2c981aSreyk * <queue size> number of followings are stored in arrays 534ff2c981aSreyk * - command blocks (in dmamem) should be pre-allocated and mapped 535ff2c981aSreyk * - dmamaps for command blocks should be pre-allocated and loaded 536ff2c981aSreyk * - dmamaps for payload should be pre-allocated 537ff2c981aSreyk * r = virtio_enqueue_prep(sc, vq, &slot); // allocate a slot 538ff2c981aSreyk * if (r) // currently 0 or EAGAIN 539ff2c981aSreyk * return r; 540ff2c981aSreyk * r = bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..); 541ff2c981aSreyk * if (r) { 542ff2c981aSreyk * virtio_enqueue_abort(sc, vq, slot); 543ff2c981aSreyk * bus_dmamap_unload(dmat, dmamap_payload[slot]); 544ff2c981aSreyk * return r; 545ff2c981aSreyk * } 546ff2c981aSreyk * r = virtio_enqueue_reserve(sc, vq, slot, 547ff2c981aSreyk * dmamap_payload[slot]->dm_nsegs+1); 548ff2c981aSreyk * // ^ +1 for command 549ff2c981aSreyk * if (r) { // currently 0 or EAGAIN 550ff2c981aSreyk * bus_dmamap_unload(dmat, dmamap_payload[slot]); 551ff2c981aSreyk * return r; // do not call abort() 552ff2c981aSreyk * } 553ff2c981aSreyk * <setup and prepare commands> 554ff2c981aSreyk * bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE); 555ff2c981aSreyk * bus_dmamap_sync(dmat, dmamap_payload[slot],...); 556ff2c981aSreyk * virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], 0); 557ff2c981aSreyk * virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite); 558ff2c981aSreyk * virtio_enqueue_commit(sc, vq, slot, 1); 5594085a40eSsf * 5604085a40eSsf * Alternative usage with statically allocated slots: 5614085a40eSsf * <during initialization> 5624085a40eSsf * // while not out of slots, do 5634085a40eSsf * virtio_enqueue_prep(sc, vq, &slot); // allocate a slot 5644085a40eSsf * virtio_enqueue_reserve(sc, vq, slot, max_segs); // reserve all slots 5654085a40eSsf * that may ever be needed 5664085a40eSsf * 5674b1a56afSjsg * <when enqueuing a request> 5684085a40eSsf * // Don't call virtio_enqueue_prep() 5694085a40eSsf * bus_dmamap_load(dmat, dmamap_payload[slot], data, count, ..); 5704085a40eSsf * bus_dmamap_sync(dmat, dmamap_cmd[slot],... BUS_DMASYNC_PREWRITE); 5714085a40eSsf * bus_dmamap_sync(dmat, dmamap_payload[slot],...); 5724085a40eSsf * virtio_enqueue_trim(sc, vq, slot, num_segs_needed); 5734085a40eSsf * virtio_enqueue(sc, vq, slot, dmamap_cmd[slot], 0); 5744085a40eSsf * virtio_enqueue(sc, vq, slot, dmamap_payload[slot], iswrite); 5754085a40eSsf * virtio_enqueue_commit(sc, vq, slot, 1); 5764085a40eSsf * 5774085a40eSsf * <when dequeuing> 5784085a40eSsf * // don't call virtio_dequeue_commit() 579ff2c981aSreyk */ 580ff2c981aSreyk 581ff2c981aSreyk /* 582ff2c981aSreyk * enqueue_prep: allocate a slot number 583ff2c981aSreyk */ 584ff2c981aSreyk int 585ff2c981aSreyk virtio_enqueue_prep(struct virtqueue *vq, int *slotp) 586ff2c981aSreyk { 587ff2c981aSreyk struct vq_entry *qe1; 588ff2c981aSreyk 589ff2c981aSreyk VIRTIO_ASSERT(slotp != NULL); 590ff2c981aSreyk 591ff2c981aSreyk qe1 = vq_alloc_entry(vq); 592ff2c981aSreyk if (qe1 == NULL) 593ff2c981aSreyk return EAGAIN; 594ff2c981aSreyk /* next slot is not allocated yet */ 595ff2c981aSreyk qe1->qe_next = -1; 596ff2c981aSreyk *slotp = qe1->qe_index; 597ff2c981aSreyk 598ff2c981aSreyk return 0; 599ff2c981aSreyk } 600ff2c981aSreyk 601ff2c981aSreyk /* 602ff2c981aSreyk * enqueue_reserve: allocate remaining slots and build the descriptor chain. 603ff2c981aSreyk * Calls virtio_enqueue_abort() on failure. 604ff2c981aSreyk */ 605ff2c981aSreyk int 606ff2c981aSreyk virtio_enqueue_reserve(struct virtqueue *vq, int slot, int nsegs) 607ff2c981aSreyk { 608ff2c981aSreyk struct vq_entry *qe1 = &vq->vq_entries[slot]; 609ff2c981aSreyk 610ff2c981aSreyk VIRTIO_ASSERT(qe1->qe_next == -1); 611ff2c981aSreyk VIRTIO_ASSERT(1 <= nsegs && nsegs <= vq->vq_num); 612ff2c981aSreyk 61354f891c2Ssf if (vq->vq_indirect != NULL && nsegs > 1 && nsegs <= vq->vq_maxnsegs) { 614ff2c981aSreyk struct vring_desc *vd; 615ff2c981aSreyk int i; 616ff2c981aSreyk 61754f891c2Ssf qe1->qe_indirect = 1; 61854f891c2Ssf 619ff2c981aSreyk vd = &vq->vq_desc[qe1->qe_index]; 620ff2c981aSreyk vd->addr = vq->vq_dmamap->dm_segs[0].ds_addr + 621ff2c981aSreyk vq->vq_indirectoffset; 622ff2c981aSreyk vd->addr += sizeof(struct vring_desc) * vq->vq_maxnsegs * 623ff2c981aSreyk qe1->qe_index; 624ff2c981aSreyk vd->len = sizeof(struct vring_desc) * nsegs; 625ff2c981aSreyk vd->flags = VRING_DESC_F_INDIRECT; 626ff2c981aSreyk 627ff2c981aSreyk vd = vq->vq_indirect; 628ff2c981aSreyk vd += vq->vq_maxnsegs * qe1->qe_index; 629ff2c981aSreyk qe1->qe_desc_base = vd; 630ff2c981aSreyk 631ff2c981aSreyk for (i = 0; i < nsegs-1; i++) 632ff2c981aSreyk vd[i].flags = VRING_DESC_F_NEXT; 633ff2c981aSreyk vd[i].flags = 0; 634ff2c981aSreyk qe1->qe_next = 0; 635ff2c981aSreyk 636ff2c981aSreyk return 0; 637ff2c981aSreyk } else { 638ff2c981aSreyk struct vring_desc *vd; 639ff2c981aSreyk struct vq_entry *qe; 640ff2c981aSreyk int i, s; 641ff2c981aSreyk 64254f891c2Ssf qe1->qe_indirect = 0; 64354f891c2Ssf 644ff2c981aSreyk vd = &vq->vq_desc[0]; 645ff2c981aSreyk qe1->qe_desc_base = vd; 646ff2c981aSreyk qe1->qe_next = qe1->qe_index; 647ff2c981aSreyk s = slot; 648ff2c981aSreyk for (i = 0; i < nsegs - 1; i++) { 649ff2c981aSreyk qe = vq_alloc_entry(vq); 650ff2c981aSreyk if (qe == NULL) { 651ff2c981aSreyk vd[s].flags = 0; 652ff2c981aSreyk virtio_enqueue_abort(vq, slot); 653ff2c981aSreyk return EAGAIN; 654ff2c981aSreyk } 655ff2c981aSreyk vd[s].flags = VRING_DESC_F_NEXT; 656ff2c981aSreyk vd[s].next = qe->qe_index; 657ff2c981aSreyk s = qe->qe_index; 658ff2c981aSreyk } 659ff2c981aSreyk vd[s].flags = 0; 660ff2c981aSreyk 661ff2c981aSreyk return 0; 662ff2c981aSreyk } 663ff2c981aSreyk } 664ff2c981aSreyk 665ff2c981aSreyk /* 666ff2c981aSreyk * enqueue: enqueue a single dmamap. 667ff2c981aSreyk */ 668ff2c981aSreyk int 669ff2c981aSreyk virtio_enqueue(struct virtqueue *vq, int slot, bus_dmamap_t dmamap, int write) 670ff2c981aSreyk { 671ff2c981aSreyk struct vq_entry *qe1 = &vq->vq_entries[slot]; 672ff2c981aSreyk struct vring_desc *vd = qe1->qe_desc_base; 673ff2c981aSreyk int i; 674ff2c981aSreyk int s = qe1->qe_next; 675ff2c981aSreyk 676ff2c981aSreyk VIRTIO_ASSERT(s >= 0); 677ff2c981aSreyk VIRTIO_ASSERT(dmamap->dm_nsegs > 0); 678ff2c981aSreyk if (dmamap->dm_nsegs > vq->vq_maxnsegs) { 679ff2c981aSreyk #if VIRTIO_DEBUG 680ff2c981aSreyk for (i = 0; i < dmamap->dm_nsegs; i++) { 681ff2c981aSreyk printf(" %d (%d): %p %lx \n", i, write, 682ff2c981aSreyk (void *)dmamap->dm_segs[i].ds_addr, 683ff2c981aSreyk dmamap->dm_segs[i].ds_len); 684ff2c981aSreyk } 685ff2c981aSreyk #endif 6864123b6a7Sderaadt panic("dmamap->dm_nseg %d > vq->vq_maxnsegs %d", 687ff2c981aSreyk dmamap->dm_nsegs, vq->vq_maxnsegs); 688ff2c981aSreyk } 689ff2c981aSreyk 690ff2c981aSreyk for (i = 0; i < dmamap->dm_nsegs; i++) { 691ff2c981aSreyk vd[s].addr = dmamap->dm_segs[i].ds_addr; 692ff2c981aSreyk vd[s].len = dmamap->dm_segs[i].ds_len; 693ff2c981aSreyk if (!write) 694ff2c981aSreyk vd[s].flags |= VRING_DESC_F_WRITE; 695ff2c981aSreyk s = vd[s].next; 696ff2c981aSreyk } 697ff2c981aSreyk qe1->qe_next = s; 698ff2c981aSreyk 699ff2c981aSreyk return 0; 700ff2c981aSreyk } 701ff2c981aSreyk 702ff2c981aSreyk int 703ff2c981aSreyk virtio_enqueue_p(struct virtqueue *vq, int slot, bus_dmamap_t dmamap, 704ff2c981aSreyk bus_addr_t start, bus_size_t len, int write) 705ff2c981aSreyk { 706ff2c981aSreyk struct vq_entry *qe1 = &vq->vq_entries[slot]; 707ff2c981aSreyk struct vring_desc *vd = qe1->qe_desc_base; 708ff2c981aSreyk int s = qe1->qe_next; 709ff2c981aSreyk 710ff2c981aSreyk VIRTIO_ASSERT(s >= 0); 711ff2c981aSreyk /* XXX todo: handle more segments */ 712ff2c981aSreyk VIRTIO_ASSERT(dmamap->dm_nsegs == 1); 713ff2c981aSreyk VIRTIO_ASSERT((dmamap->dm_segs[0].ds_len > start) && 714ff2c981aSreyk (dmamap->dm_segs[0].ds_len >= start + len)); 715ff2c981aSreyk 716ff2c981aSreyk vd[s].addr = dmamap->dm_segs[0].ds_addr + start; 717ff2c981aSreyk vd[s].len = len; 718ff2c981aSreyk if (!write) 719ff2c981aSreyk vd[s].flags |= VRING_DESC_F_WRITE; 720ff2c981aSreyk qe1->qe_next = vd[s].next; 721ff2c981aSreyk 722ff2c981aSreyk return 0; 723ff2c981aSreyk } 724ff2c981aSreyk 725ff2c981aSreyk static void 726ff2c981aSreyk publish_avail_idx(struct virtio_softc *sc, struct virtqueue *vq) 727ff2c981aSreyk { 728d3638ce2Ssf /* first make sure the avail ring entries are visible to the device */ 729ff2c981aSreyk vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE); 730ff2c981aSreyk 731ff2c981aSreyk virtio_membar_producer(); 732ff2c981aSreyk vq->vq_avail->idx = vq->vq_avail_idx; 733d3638ce2Ssf /* make the avail idx visible to the device */ 734d3638ce2Ssf vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE); 735ff2c981aSreyk vq->vq_queued = 1; 736ff2c981aSreyk } 737ff2c981aSreyk 738ff2c981aSreyk /* 739ff2c981aSreyk * enqueue_commit: add it to the aring. 740ff2c981aSreyk */ 741ff2c981aSreyk void 742ff2c981aSreyk virtio_enqueue_commit(struct virtio_softc *sc, struct virtqueue *vq, int slot, 743ff2c981aSreyk int notifynow) 744ff2c981aSreyk { 745ff2c981aSreyk struct vq_entry *qe1; 746ff2c981aSreyk 747ff2c981aSreyk if (slot < 0) 748ff2c981aSreyk goto notify; 749ff2c981aSreyk vq_sync_descs(sc, vq, BUS_DMASYNC_PREWRITE); 750ff2c981aSreyk qe1 = &vq->vq_entries[slot]; 751ff2c981aSreyk if (qe1->qe_indirect) 752ff2c981aSreyk vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_PREWRITE); 753ff2c981aSreyk vq->vq_avail->ring[(vq->vq_avail_idx++) & vq->vq_mask] = slot; 754ff2c981aSreyk 755ff2c981aSreyk notify: 756ff2c981aSreyk if (notifynow) { 7570091658aSsf if (virtio_has_feature(vq->vq_owner, VIRTIO_F_RING_EVENT_IDX)) { 758ff2c981aSreyk uint16_t o = vq->vq_avail->idx; 759ff2c981aSreyk uint16_t n = vq->vq_avail_idx; 760ff2c981aSreyk uint16_t t; 761ff2c981aSreyk publish_avail_idx(sc, vq); 762ff2c981aSreyk 763ff2c981aSreyk virtio_membar_sync(); 764d3638ce2Ssf vq_sync_uring_avail_event(sc, vq, BUS_DMASYNC_POSTREAD); 765ff2c981aSreyk t = VQ_AVAIL_EVENT(vq) + 1; 766ff2c981aSreyk if ((uint16_t)(n - t) < (uint16_t)(n - o)) 767ff2c981aSreyk sc->sc_ops->kick(sc, vq->vq_index); 768ff2c981aSreyk } else { 769ff2c981aSreyk publish_avail_idx(sc, vq); 770ff2c981aSreyk 771ff2c981aSreyk virtio_membar_sync(); 772d3638ce2Ssf vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); 773ff2c981aSreyk if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY)) 774ff2c981aSreyk sc->sc_ops->kick(sc, vq->vq_index); 775ff2c981aSreyk } 776ff2c981aSreyk } 777ff2c981aSreyk } 778ff2c981aSreyk 779ff2c981aSreyk /* 780ff2c981aSreyk * enqueue_abort: rollback. 781ff2c981aSreyk */ 782ff2c981aSreyk int 783ff2c981aSreyk virtio_enqueue_abort(struct virtqueue *vq, int slot) 784ff2c981aSreyk { 785ff2c981aSreyk struct vq_entry *qe = &vq->vq_entries[slot]; 786ff2c981aSreyk struct vring_desc *vd; 787ff2c981aSreyk int s; 788ff2c981aSreyk 789ff2c981aSreyk if (qe->qe_next < 0) { 790ff2c981aSreyk vq_free_entry(vq, qe); 791ff2c981aSreyk return 0; 792ff2c981aSreyk } 793ff2c981aSreyk 794ff2c981aSreyk s = slot; 795ff2c981aSreyk vd = &vq->vq_desc[0]; 796ff2c981aSreyk while (vd[s].flags & VRING_DESC_F_NEXT) { 797ff2c981aSreyk s = vd[s].next; 798ff2c981aSreyk vq_free_entry(vq, qe); 799ff2c981aSreyk qe = &vq->vq_entries[s]; 800ff2c981aSreyk } 801ff2c981aSreyk vq_free_entry(vq, qe); 802ff2c981aSreyk return 0; 803ff2c981aSreyk } 804ff2c981aSreyk 805ff2c981aSreyk /* 80667f227bcSkrw * enqueue_trim: adjust buffer size to given # of segments, a.k.a. 80767f227bcSkrw * descriptors. 80867f227bcSkrw */ 80967f227bcSkrw void 81067f227bcSkrw virtio_enqueue_trim(struct virtqueue *vq, int slot, int nsegs) 81167f227bcSkrw { 812e7a13de1Skrw struct vq_entry *qe1 = &vq->vq_entries[slot]; 81367f227bcSkrw struct vring_desc *vd = &vq->vq_desc[0]; 814cf707a04Skrw int i; 81567f227bcSkrw 81667f227bcSkrw if ((vd[slot].flags & VRING_DESC_F_INDIRECT) == 0) { 817e7a13de1Skrw qe1->qe_next = qe1->qe_index; 818e7a13de1Skrw /* 819e7a13de1Skrw * N.B.: the vq_entries are ASSUMED to be a contiguous 820e7a13de1Skrw * block with slot being the index to the first one. 821e7a13de1Skrw */ 82267f227bcSkrw } else { 823cf707a04Skrw qe1->qe_next = 0; 82467f227bcSkrw vd = &vq->vq_desc[qe1->qe_index]; 82567f227bcSkrw vd->len = sizeof(struct vring_desc) * nsegs; 826ccc91726Ssf vd = qe1->qe_desc_base; 827cf707a04Skrw slot = 0; 82867f227bcSkrw } 829cf707a04Skrw 830cf707a04Skrw for (i = 0; i < nsegs -1 ; i++) { 831cf707a04Skrw vd[slot].flags = VRING_DESC_F_NEXT; 832cf707a04Skrw slot++; 83367f227bcSkrw } 834cf707a04Skrw vd[slot].flags = 0; 83567f227bcSkrw } 83667f227bcSkrw 83767f227bcSkrw /* 838ff2c981aSreyk * Dequeue a request. 839ff2c981aSreyk */ 840ff2c981aSreyk /* 841d3638ce2Ssf * dequeue: dequeue a request from uring; bus_dmamap_sync for uring must 842d3638ce2Ssf * already have been done, usually by virtio_check_vq() 843d3638ce2Ssf * in the interrupt handler. This means that polling virtio_dequeue() 844d3638ce2Ssf * repeatedly until it returns 0 does not work. 845ff2c981aSreyk */ 846ff2c981aSreyk int 847ff2c981aSreyk virtio_dequeue(struct virtio_softc *sc, struct virtqueue *vq, 848ff2c981aSreyk int *slotp, int *lenp) 849ff2c981aSreyk { 850ff2c981aSreyk uint16_t slot, usedidx; 851ff2c981aSreyk struct vq_entry *qe; 852ff2c981aSreyk 853ff2c981aSreyk if (vq->vq_used_idx == vq->vq_used->idx) 854ff2c981aSreyk return ENOENT; 855ff2c981aSreyk usedidx = vq->vq_used_idx++; 856ff2c981aSreyk usedidx &= vq->vq_mask; 857ff2c981aSreyk 858ff2c981aSreyk virtio_membar_consumer(); 859d3638ce2Ssf vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); 860ff2c981aSreyk slot = vq->vq_used->ring[usedidx].id; 861ff2c981aSreyk qe = &vq->vq_entries[slot]; 862ff2c981aSreyk 863ff2c981aSreyk if (qe->qe_indirect) 864ff2c981aSreyk vq_sync_indirect(sc, vq, slot, BUS_DMASYNC_POSTWRITE); 865ff2c981aSreyk 866ff2c981aSreyk if (slotp) 867ff2c981aSreyk *slotp = slot; 868ff2c981aSreyk if (lenp) 869ff2c981aSreyk *lenp = vq->vq_used->ring[usedidx].len; 870ff2c981aSreyk 871ff2c981aSreyk return 0; 872ff2c981aSreyk } 873ff2c981aSreyk 874ff2c981aSreyk /* 875ff2c981aSreyk * dequeue_commit: complete dequeue; the slot is recycled for future use. 876ff2c981aSreyk * if you forget to call this the slot will be leaked. 8774085a40eSsf * 8784085a40eSsf * Don't call this if you use statically allocated slots 879c36fc2a0Ssf * and virtio_enqueue_trim(). 880ff0ccef3Ssf * 881ff0ccef3Ssf * returns the number of freed slots. 882ff2c981aSreyk */ 883ff2c981aSreyk int 884ff2c981aSreyk virtio_dequeue_commit(struct virtqueue *vq, int slot) 885ff2c981aSreyk { 886ff2c981aSreyk struct vq_entry *qe = &vq->vq_entries[slot]; 887ff2c981aSreyk struct vring_desc *vd = &vq->vq_desc[0]; 888ff0ccef3Ssf int s = slot, r = 1; 889ff2c981aSreyk 890ff2c981aSreyk while (vd[s].flags & VRING_DESC_F_NEXT) { 891ff2c981aSreyk s = vd[s].next; 892ff2c981aSreyk vq_free_entry(vq, qe); 893ff2c981aSreyk qe = &vq->vq_entries[s]; 894ff0ccef3Ssf r++; 895ff2c981aSreyk } 896ff2c981aSreyk vq_free_entry(vq, qe); 897ff2c981aSreyk 898ff0ccef3Ssf return r; 899ff2c981aSreyk } 900ff2c981aSreyk 901ff2c981aSreyk /* 902ff2c981aSreyk * Increase the event index in order to delay interrupts. 903ff2c981aSreyk * Returns 0 on success; returns 1 if the used ring has already advanced 9044b1a56afSjsg * too far, and the caller must process the queue again (otherwise, no 905ff2c981aSreyk * more interrupts will happen). 906ff2c981aSreyk */ 907ff2c981aSreyk int 908ff2c981aSreyk virtio_postpone_intr(struct virtqueue *vq, uint16_t nslots) 909ff2c981aSreyk { 910ff2c981aSreyk uint16_t idx; 911ff2c981aSreyk 912ff2c981aSreyk idx = vq->vq_used_idx + nslots; 913ff2c981aSreyk 914ff2c981aSreyk /* set the new event index: avail_ring->used_event = idx */ 915ff2c981aSreyk VQ_USED_EVENT(vq) = idx; 916ff2c981aSreyk virtio_membar_sync(); 917ff2c981aSreyk 918d3638ce2Ssf vq_sync_aring_used_event(vq->vq_owner, vq, BUS_DMASYNC_PREWRITE); 919ff2c981aSreyk vq->vq_queued++; 920ff2c981aSreyk 921ff2c981aSreyk if (nslots < virtio_nused(vq)) 922ff2c981aSreyk return 1; 923ff2c981aSreyk 924ff2c981aSreyk return 0; 925ff2c981aSreyk } 926ff2c981aSreyk 927ff2c981aSreyk /* 928ff2c981aSreyk * Postpone interrupt until 3/4 of the available descriptors have been 929ff2c981aSreyk * consumed. 930ff2c981aSreyk */ 931ff2c981aSreyk int 932ff2c981aSreyk virtio_postpone_intr_smart(struct virtqueue *vq) 933ff2c981aSreyk { 934ff2c981aSreyk uint16_t nslots; 935ff2c981aSreyk 936ff2c981aSreyk nslots = (uint16_t)(vq->vq_avail->idx - vq->vq_used_idx) * 3 / 4; 937ff2c981aSreyk 938ff2c981aSreyk return virtio_postpone_intr(vq, nslots); 939ff2c981aSreyk } 940ff2c981aSreyk 941ff2c981aSreyk /* 942ff2c981aSreyk * Postpone interrupt until all of the available descriptors have been 943ff2c981aSreyk * consumed. 944ff2c981aSreyk */ 945ff2c981aSreyk int 946ff2c981aSreyk virtio_postpone_intr_far(struct virtqueue *vq) 947ff2c981aSreyk { 948ff2c981aSreyk uint16_t nslots; 949ff2c981aSreyk 950ff2c981aSreyk nslots = (uint16_t)(vq->vq_avail->idx - vq->vq_used_idx); 951ff2c981aSreyk 952ff2c981aSreyk return virtio_postpone_intr(vq, nslots); 953ff2c981aSreyk } 954ff2c981aSreyk 955ff2c981aSreyk 956ff2c981aSreyk /* 957ff2c981aSreyk * Start/stop vq interrupt. No guarantee. 958ff2c981aSreyk */ 959ff2c981aSreyk void 960ff2c981aSreyk virtio_stop_vq_intr(struct virtio_softc *sc, struct virtqueue *vq) 961ff2c981aSreyk { 9620091658aSsf if (virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX)) { 963ff2c981aSreyk /* 964ff2c981aSreyk * No way to disable the interrupt completely with 965ff2c981aSreyk * RingEventIdx. Instead advance used_event by half 966ff2c981aSreyk * the possible value. This won't happen soon and 9674b1a56afSjsg * is far enough in the past to not trigger a spurious 968ff2c981aSreyk * interrupt. 969ff2c981aSreyk */ 970ff2c981aSreyk VQ_USED_EVENT(vq) = vq->vq_used_idx + 0x8000; 971d3638ce2Ssf vq_sync_aring_used_event(sc, vq, BUS_DMASYNC_PREWRITE); 972ff2c981aSreyk } else { 973ff2c981aSreyk vq->vq_avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; 974ff2c981aSreyk } 975ff2c981aSreyk vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE); 976ff2c981aSreyk vq->vq_queued++; 977ff2c981aSreyk } 978ff2c981aSreyk 979ff2c981aSreyk int 980ff2c981aSreyk virtio_start_vq_intr(struct virtio_softc *sc, struct virtqueue *vq) 981ff2c981aSreyk { 982ff2c981aSreyk /* 983ff2c981aSreyk * If event index feature is negotiated, enabling 984ff2c981aSreyk * interrupts is done through setting the latest 985ff2c981aSreyk * consumed index in the used_event field 986ff2c981aSreyk */ 987d3638ce2Ssf if (virtio_has_feature(sc, VIRTIO_F_RING_EVENT_IDX)) { 988ff2c981aSreyk VQ_USED_EVENT(vq) = vq->vq_used_idx; 989d3638ce2Ssf vq_sync_aring_used_event(sc, vq, BUS_DMASYNC_PREWRITE); 990d3638ce2Ssf } else { 991ff2c981aSreyk vq->vq_avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; 992d3638ce2Ssf vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE); 993d3638ce2Ssf } 994ff2c981aSreyk 995ff2c981aSreyk virtio_membar_sync(); 996ff2c981aSreyk 997ff2c981aSreyk vq->vq_queued++; 998ff2c981aSreyk 999d3638ce2Ssf vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); 1000ff2c981aSreyk if (vq->vq_used_idx != vq->vq_used->idx) 1001ff2c981aSreyk return 1; 1002ff2c981aSreyk 1003ff2c981aSreyk return 0; 1004ff2c981aSreyk } 1005ff2c981aSreyk 1006ff2c981aSreyk /* 1007ff2c981aSreyk * Returns a number of slots in the used ring available to 1008ff2c981aSreyk * be supplied to the avail ring. 1009ff2c981aSreyk */ 1010ff2c981aSreyk int 1011ff2c981aSreyk virtio_nused(struct virtqueue *vq) 1012ff2c981aSreyk { 1013ff2c981aSreyk uint16_t n; 1014ff2c981aSreyk 1015d3638ce2Ssf vq_sync_uring(vq->vq_owner, vq, BUS_DMASYNC_POSTREAD); 1016ff2c981aSreyk n = (uint16_t)(vq->vq_used->idx - vq->vq_used_idx); 1017ff2c981aSreyk VIRTIO_ASSERT(n <= vq->vq_num); 1018ff2c981aSreyk 1019ff2c981aSreyk return n; 1020ff2c981aSreyk } 1021ff2c981aSreyk 1022ff2c981aSreyk #if VIRTIO_DEBUG 1023ff2c981aSreyk void 1024ff2c981aSreyk virtio_vq_dump(struct virtqueue *vq) 1025ff2c981aSreyk { 102650bdd322Ssf #if VIRTIO_DEBUG >= 2 102750bdd322Ssf int i; 102850bdd322Ssf #endif 1029ff2c981aSreyk /* Common fields */ 103050bdd322Ssf printf(" + addr: %p\n", vq); 1031*4d9ed068Ssf if (vq->vq_num == 0) { 1032*4d9ed068Ssf printf(" + vq is unused\n"); 1033*4d9ed068Ssf return; 1034*4d9ed068Ssf } 1035ff2c981aSreyk printf(" + vq num: %d\n", vq->vq_num); 1036ff2c981aSreyk printf(" + vq mask: 0x%X\n", vq->vq_mask); 1037ff2c981aSreyk printf(" + vq index: %d\n", vq->vq_index); 1038ff2c981aSreyk printf(" + vq used idx: %d\n", vq->vq_used_idx); 1039ff2c981aSreyk printf(" + vq avail idx: %d\n", vq->vq_avail_idx); 1040ff2c981aSreyk printf(" + vq queued: %d\n",vq->vq_queued); 104150bdd322Ssf #if VIRTIO_DEBUG >= 2 104250bdd322Ssf for (i = 0; i < vq->vq_num; i++) { 104350bdd322Ssf struct vring_desc *desc = &vq->vq_desc[i]; 104450bdd322Ssf printf(" D%-3d len:%d flags:%d next:%d\n", i, desc->len, 104550bdd322Ssf desc->flags, desc->next); 104650bdd322Ssf } 104750bdd322Ssf #endif 1048ff2c981aSreyk /* Avail ring fields */ 1049ff2c981aSreyk printf(" + avail flags: 0x%X\n", vq->vq_avail->flags); 1050ff2c981aSreyk printf(" + avail idx: %d\n", vq->vq_avail->idx); 1051ff2c981aSreyk printf(" + avail event: %d\n", VQ_AVAIL_EVENT(vq)); 105250bdd322Ssf #if VIRTIO_DEBUG >= 2 105350bdd322Ssf for (i = 0; i < vq->vq_num; i++) 105450bdd322Ssf printf(" A%-3d idx:%d\n", i, vq->vq_avail->ring[i]); 105550bdd322Ssf #endif 1056ff2c981aSreyk /* Used ring fields */ 1057ff2c981aSreyk printf(" + used flags: 0x%X\n",vq->vq_used->flags); 1058ff2c981aSreyk printf(" + used idx: %d\n",vq->vq_used->idx); 1059ff2c981aSreyk printf(" + used event: %d\n", VQ_USED_EVENT(vq)); 106050bdd322Ssf #if VIRTIO_DEBUG >= 2 106150bdd322Ssf for (i = 0; i < vq->vq_num; i++) { 106250bdd322Ssf printf(" U%-3d id:%d len:%d\n", i, 106350bdd322Ssf vq->vq_used->ring[i].id, 106450bdd322Ssf vq->vq_used->ring[i].len); 106550bdd322Ssf } 106650bdd322Ssf #endif 1067ff2c981aSreyk printf(" +++++++++++++++++++++++++++\n"); 1068ff2c981aSreyk } 1069ff2c981aSreyk #endif 1070