123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * Copyright (C) 2016 Red Hat, Inc.
- * Author: Michael S. Tsirkin <mst@redhat.com>
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
- * 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();
- }
|