| /* |
| * 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(); |
| } |