// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Red Hat, Inc. * Author: Michael S. Tsirkin <mst@redhat.com> * * Partial implementation of virtio 0.9. event index is used for signalling, * unconditionally. Design roughly follows linux kernel implementation in order * to be able to judge its performance. */ #define _GNU_SOURCE #include "main.h" #include <stdlib.h> #include <stdio.h> #include <assert.h> #include <string.h> #include <linux/virtio_ring.h> struct data { void *data; } *data; struct vring ring; /* enabling the below activates experimental ring polling code * (which skips index reads on consumer in favor of looking at * high bits of ring id ^ 0x8000). */ /* #ifdef RING_POLL */ /* enabling the below activates experimental in-order code * (which skips ring updates and reads and writes len in descriptor). */ /* #ifdef INORDER */ #if defined(RING_POLL) && defined(INORDER) #error "RING_POLL and INORDER are mutually exclusive" #endif /* how much padding is needed to avoid false cache sharing */ #define HOST_GUEST_PADDING 0x80 struct guest { unsigned short avail_idx; unsigned short last_used_idx; unsigned short num_free; unsigned short kicked_avail_idx; #ifndef INORDER unsigned short free_head; #else unsigned short reserved_free_head; #endif unsigned char reserved[HOST_GUEST_PADDING - 10]; } guest; struct host { /* we do not need to track last avail index * unless we have more than one in flight. */ unsigned short used_idx; unsigned short called_used_idx; unsigned char reserved[HOST_GUEST_PADDING - 4]; } host; /* implemented by ring */ void alloc_ring(void) { int ret; int i; void *p; ret = posix_memalign(&p, 0x1000, vring_size(ring_size, 0x1000)); if (ret) { perror("Unable to allocate ring buffer.\n"); exit(3); } memset(p, 0, vring_size(ring_size, 0x1000)); vring_init(&ring, ring_size, p, 0x1000); guest.avail_idx = 0; guest.kicked_avail_idx = -1; guest.last_used_idx = 0; #ifndef INORDER /* Put everything in free lists. */ guest.free_head = 0; #endif for (i = 0; i < ring_size - 1; i++) ring.desc[i].next = i + 1; host.used_idx = 0; host.called_used_idx = -1; guest.num_free = ring_size; data = malloc(ring_size * sizeof *data); if (!data) { perror("Unable to allocate data buffer.\n"); exit(3); } memset(data, 0, ring_size * sizeof *data); } /* guest side */ int add_inbuf(unsigned len, void *buf, void *datap) { unsigned head; #ifndef INORDER unsigned avail; #endif struct vring_desc *desc; if (!guest.num_free) return -1; #ifdef INORDER head = (ring_size - 1) & (guest.avail_idx++); #else head = guest.free_head; #endif guest.num_free--; desc = ring.desc; desc[head].flags = VRING_DESC_F_NEXT; desc[head].addr = (unsigned long)(void *)buf; desc[head].len = len; /* We do it like this to simulate the way * we'd have to flip it if we had multiple * descriptors. */ desc[head].flags &= ~VRING_DESC_F_NEXT; #ifndef INORDER guest.free_head = desc[head].next; #endif data[head].data = datap; #ifdef RING_POLL /* Barrier A (for pairing) */ smp_release(); avail = guest.avail_idx++; ring.avail->ring[avail & (ring_size - 1)] = (head | (avail & ~(ring_size - 1))) ^ 0x8000; #else #ifndef INORDER /* Barrier A (for pairing) */ smp_release(); avail = (ring_size - 1) & (guest.avail_idx++); ring.avail->ring[avail] = head; #endif /* Barrier A (for pairing) */ smp_release(); #endif ring.avail->idx = guest.avail_idx; return 0; } void *get_buf(unsigned *lenp, void **bufp) { unsigned head; unsigned index; void *datap; #ifdef RING_POLL head = (ring_size - 1) & guest.last_used_idx; index = ring.used->ring[head].id; if ((index ^ guest.last_used_idx ^ 0x8000) & ~(ring_size - 1)) return NULL; /* Barrier B (for pairing) */ smp_acquire(); index &= ring_size - 1; #else if (ring.used->idx == guest.last_used_idx) return NULL; /* Barrier B (for pairing) */ smp_acquire(); #ifdef INORDER head = (ring_size - 1) & guest.last_used_idx; index = head; #else head = (ring_size - 1) & guest.last_used_idx; index = ring.used->ring[head].id; #endif #endif #ifdef INORDER *lenp = ring.desc[index].len; #else *lenp = ring.used->ring[head].len; #endif datap = data[index].data; *bufp = (void*)(unsigned long)ring.desc[index].addr; data[index].data = NULL; #ifndef INORDER ring.desc[index].next = guest.free_head; guest.free_head = index; #endif guest.num_free++; guest.last_used_idx++; return datap; } bool used_empty() { unsigned short last_used_idx = guest.last_used_idx; #ifdef RING_POLL unsigned short head = last_used_idx & (ring_size - 1); unsigned index = ring.used->ring[head].id; return (index ^ last_used_idx ^ 0x8000) & ~(ring_size - 1); #else return ring.used->idx == last_used_idx; #endif } void disable_call() { /* Doing nothing to disable calls might cause * extra interrupts, but reduces the number of cache misses. */ } bool enable_call() { vring_used_event(&ring) = guest.last_used_idx; /* Flush call index write */ /* Barrier D (for pairing) */ smp_mb(); return used_empty(); } void kick_available(void) { bool need; /* Flush in previous flags write */ /* Barrier C (for pairing) */ smp_mb(); need = vring_need_event(vring_avail_event(&ring), guest.avail_idx, guest.kicked_avail_idx); guest.kicked_avail_idx = guest.avail_idx; if (need) kick(); } /* host side */ void disable_kick() { /* Doing nothing to disable kicks might cause * extra interrupts, but reduces the number of cache misses. */ } bool enable_kick() { vring_avail_event(&ring) = host.used_idx; /* Barrier C (for pairing) */ smp_mb(); return avail_empty(); } bool avail_empty() { unsigned head = host.used_idx; #ifdef RING_POLL unsigned index = ring.avail->ring[head & (ring_size - 1)]; return ((index ^ head ^ 0x8000) & ~(ring_size - 1)); #else return head == ring.avail->idx; #endif } bool use_buf(unsigned *lenp, void **bufp) { unsigned used_idx = host.used_idx; struct vring_desc *desc; unsigned head; #ifdef RING_POLL head = ring.avail->ring[used_idx & (ring_size - 1)]; if ((used_idx ^ head ^ 0x8000) & ~(ring_size - 1)) return false; /* Barrier A (for pairing) */ smp_acquire(); used_idx &= ring_size - 1; desc = &ring.desc[head & (ring_size - 1)]; #else if (used_idx == ring.avail->idx) return false; /* Barrier A (for pairing) */ smp_acquire(); used_idx &= ring_size - 1; #ifdef INORDER head = used_idx; #else head = ring.avail->ring[used_idx]; #endif desc = &ring.desc[head]; #endif *lenp = desc->len; *bufp = (void *)(unsigned long)desc->addr; #ifdef INORDER desc->len = desc->len - 1; #else /* now update used ring */ ring.used->ring[used_idx].id = head; ring.used->ring[used_idx].len = desc->len - 1; #endif /* Barrier B (for pairing) */ smp_release(); host.used_idx++; ring.used->idx = host.used_idx; return true; } void call_used(void) { bool need; /* Flush in previous flags write */ /* Barrier D (for pairing) */ smp_mb(); need = vring_need_event(vring_used_event(&ring), host.used_idx, host.called_used_idx); host.called_used_idx = host.used_idx; if (need) call(); }