Path: blob/master/tools/virtio/ringtest/virtio_ring_0_9.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2016 Red Hat, Inc.3* Author: Michael S. Tsirkin <[email protected]>4*5* Partial implementation of virtio 0.9. event index is used for signalling,6* unconditionally. Design roughly follows linux kernel implementation in order7* to be able to judge its performance.8*/9#define _GNU_SOURCE10#include "main.h"11#include <stdlib.h>12#include <stdio.h>13#include <assert.h>14#include <string.h>15#include <linux/virtio_ring.h>1617struct data {18void *data;19} *data;2021struct vring ring;2223/* enabling the below activates experimental ring polling code24* (which skips index reads on consumer in favor of looking at25* high bits of ring id ^ 0x8000).26*/27/* #ifdef RING_POLL */28/* enabling the below activates experimental in-order code29* (which skips ring updates and reads and writes len in descriptor).30*/31/* #ifdef INORDER */3233#if defined(RING_POLL) && defined(INORDER)34#error "RING_POLL and INORDER are mutually exclusive"35#endif3637/* how much padding is needed to avoid false cache sharing */38#define HOST_GUEST_PADDING 0x803940struct guest {41unsigned short avail_idx;42unsigned short last_used_idx;43unsigned short num_free;44unsigned short kicked_avail_idx;45#ifndef INORDER46unsigned short free_head;47#else48unsigned short reserved_free_head;49#endif50unsigned char reserved[HOST_GUEST_PADDING - 10];51} guest;5253struct host {54/* we do not need to track last avail index55* unless we have more than one in flight.56*/57unsigned short used_idx;58unsigned short called_used_idx;59unsigned char reserved[HOST_GUEST_PADDING - 4];60} host;6162/* implemented by ring */63void alloc_ring(void)64{65int ret;66int i;67void *p;6869ret = posix_memalign(&p, 0x1000, vring_size(ring_size, 0x1000));70if (ret) {71perror("Unable to allocate ring buffer.\n");72exit(3);73}74memset(p, 0, vring_size(ring_size, 0x1000));75vring_init(&ring, ring_size, p, 0x1000);7677guest.avail_idx = 0;78guest.kicked_avail_idx = -1;79guest.last_used_idx = 0;80#ifndef INORDER81/* Put everything in free lists. */82guest.free_head = 0;83#endif84for (i = 0; i < ring_size - 1; i++)85ring.desc[i].next = i + 1;86host.used_idx = 0;87host.called_used_idx = -1;88guest.num_free = ring_size;89data = malloc(ring_size * sizeof *data);90if (!data) {91perror("Unable to allocate data buffer.\n");92exit(3);93}94memset(data, 0, ring_size * sizeof *data);95}9697/* guest side */98int add_inbuf(unsigned len, void *buf, void *datap)99{100unsigned head;101#ifndef INORDER102unsigned avail;103#endif104struct vring_desc *desc;105106if (!guest.num_free)107return -1;108109#ifdef INORDER110head = (ring_size - 1) & (guest.avail_idx++);111#else112head = guest.free_head;113#endif114guest.num_free--;115116desc = ring.desc;117desc[head].flags = VRING_DESC_F_NEXT;118desc[head].addr = (unsigned long)(void *)buf;119desc[head].len = len;120/* We do it like this to simulate the way121* we'd have to flip it if we had multiple122* descriptors.123*/124desc[head].flags &= ~VRING_DESC_F_NEXT;125#ifndef INORDER126guest.free_head = desc[head].next;127#endif128129data[head].data = datap;130131#ifdef RING_POLL132/* Barrier A (for pairing) */133smp_release();134avail = guest.avail_idx++;135ring.avail->ring[avail & (ring_size - 1)] =136(head | (avail & ~(ring_size - 1))) ^ 0x8000;137#else138#ifndef INORDER139/* Barrier A (for pairing) */140smp_release();141avail = (ring_size - 1) & (guest.avail_idx++);142ring.avail->ring[avail] = head;143#endif144/* Barrier A (for pairing) */145smp_release();146#endif147ring.avail->idx = guest.avail_idx;148return 0;149}150151void *get_buf(unsigned *lenp, void **bufp)152{153unsigned head;154unsigned index;155void *datap;156157#ifdef RING_POLL158head = (ring_size - 1) & guest.last_used_idx;159index = ring.used->ring[head].id;160if ((index ^ guest.last_used_idx ^ 0x8000) & ~(ring_size - 1))161return NULL;162/* Barrier B (for pairing) */163smp_acquire();164index &= ring_size - 1;165#else166if (ring.used->idx == guest.last_used_idx)167return NULL;168/* Barrier B (for pairing) */169smp_acquire();170#ifdef INORDER171head = (ring_size - 1) & guest.last_used_idx;172index = head;173#else174head = (ring_size - 1) & guest.last_used_idx;175index = ring.used->ring[head].id;176#endif177178#endif179#ifdef INORDER180*lenp = ring.desc[index].len;181#else182*lenp = ring.used->ring[head].len;183#endif184datap = data[index].data;185*bufp = (void*)(unsigned long)ring.desc[index].addr;186data[index].data = NULL;187#ifndef INORDER188ring.desc[index].next = guest.free_head;189guest.free_head = index;190#endif191guest.num_free++;192guest.last_used_idx++;193return datap;194}195196bool used_empty()197{198unsigned short last_used_idx = guest.last_used_idx;199#ifdef RING_POLL200unsigned short head = last_used_idx & (ring_size - 1);201unsigned index = ring.used->ring[head].id;202203return (index ^ last_used_idx ^ 0x8000) & ~(ring_size - 1);204#else205return ring.used->idx == last_used_idx;206#endif207}208209void disable_call()210{211/* Doing nothing to disable calls might cause212* extra interrupts, but reduces the number of cache misses.213*/214}215216bool enable_call()217{218vring_used_event(&ring) = guest.last_used_idx;219/* Flush call index write */220/* Barrier D (for pairing) */221smp_mb();222return used_empty();223}224225void kick_available(void)226{227bool need;228229/* Flush in previous flags write */230/* Barrier C (for pairing) */231smp_mb();232need = vring_need_event(vring_avail_event(&ring),233guest.avail_idx,234guest.kicked_avail_idx);235236guest.kicked_avail_idx = guest.avail_idx;237if (need)238kick();239}240241/* host side */242void disable_kick()243{244/* Doing nothing to disable kicks might cause245* extra interrupts, but reduces the number of cache misses.246*/247}248249bool enable_kick()250{251vring_avail_event(&ring) = host.used_idx;252/* Barrier C (for pairing) */253smp_mb();254return avail_empty();255}256257bool avail_empty()258{259unsigned head = host.used_idx;260#ifdef RING_POLL261unsigned index = ring.avail->ring[head & (ring_size - 1)];262263return ((index ^ head ^ 0x8000) & ~(ring_size - 1));264#else265return head == ring.avail->idx;266#endif267}268269bool use_buf(unsigned *lenp, void **bufp)270{271unsigned used_idx = host.used_idx;272struct vring_desc *desc;273unsigned head;274275#ifdef RING_POLL276head = ring.avail->ring[used_idx & (ring_size - 1)];277if ((used_idx ^ head ^ 0x8000) & ~(ring_size - 1))278return false;279/* Barrier A (for pairing) */280smp_acquire();281282used_idx &= ring_size - 1;283desc = &ring.desc[head & (ring_size - 1)];284#else285if (used_idx == ring.avail->idx)286return false;287288/* Barrier A (for pairing) */289smp_acquire();290291used_idx &= ring_size - 1;292#ifdef INORDER293head = used_idx;294#else295head = ring.avail->ring[used_idx];296#endif297desc = &ring.desc[head];298#endif299300*lenp = desc->len;301*bufp = (void *)(unsigned long)desc->addr;302303#ifdef INORDER304desc->len = desc->len - 1;305#else306/* now update used ring */307ring.used->ring[used_idx].id = head;308ring.used->ring[used_idx].len = desc->len - 1;309#endif310/* Barrier B (for pairing) */311smp_release();312host.used_idx++;313ring.used->idx = host.used_idx;314315return true;316}317318void call_used(void)319{320bool need;321322/* Flush in previous flags write */323/* Barrier D (for pairing) */324smp_mb();325need = vring_need_event(vring_used_event(&ring),326host.used_idx,327host.called_used_idx);328329host.called_used_idx = host.used_idx;330if (need)331call();332}333334335