2019-06-04 10:11:15 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-01-21 14:44:10 +02:00
/*
* 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 */
2016-05-08 18:43:54 +03:00
/* 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
2016-01-21 14:44:10 +02:00
/* 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 ;
2016-05-08 18:43:54 +03:00
# ifndef INORDER
2016-01-21 14:44:10 +02:00
unsigned short free_head ;
2016-05-08 18:43:54 +03:00
# else
unsigned short reserved_free_head ;
# endif
2016-01-21 14:44:10 +02:00
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 ;
2016-05-08 18:43:54 +03:00
# ifndef INORDER
2016-01-21 14:44:10 +02:00
/* Put everything in free lists. */
guest . free_head = 0 ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
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 )
{
2016-05-08 18:43:54 +03:00
unsigned head ;
# ifndef INORDER
unsigned avail ;
# endif
2016-01-21 14:44:10 +02:00
struct vring_desc * desc ;
if ( ! guest . num_free )
return - 1 ;
2016-05-08 18:43:54 +03:00
# ifdef INORDER
head = ( ring_size - 1 ) & ( guest . avail_idx + + ) ;
# else
2016-01-21 14:44:10 +02:00
head = guest . free_head ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
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 ;
2016-05-08 18:43:54 +03:00
# ifndef INORDER
2016-01-21 14:44:10 +02:00
guest . free_head = desc [ head ] . next ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
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
2016-05-08 18:43:54 +03:00
# ifndef INORDER
/* Barrier A (for pairing) */
smp_release ( ) ;
2016-01-21 14:44:10 +02:00
avail = ( ring_size - 1 ) & ( guest . avail_idx + + ) ;
ring . avail - > ring [ avail ] = head ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
/* 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 ( ) ;
2016-05-08 18:43:54 +03:00
# ifdef INORDER
head = ( ring_size - 1 ) & guest . last_used_idx ;
index = head ;
# else
2016-01-21 14:44:10 +02:00
head = ( ring_size - 1 ) & guest . last_used_idx ;
index = ring . used - > ring [ head ] . id ;
# endif
2016-05-08 18:43:54 +03:00
# endif
# ifdef INORDER
* lenp = ring . desc [ index ] . len ;
# else
2016-01-21 14:44:10 +02:00
* lenp = ring . used - > ring [ head ] . len ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
datap = data [ index ] . data ;
* bufp = ( void * ) ( unsigned long ) ring . desc [ index ] . addr ;
data [ index ] . data = NULL ;
2016-05-08 18:43:54 +03:00
# ifndef INORDER
2016-01-21 14:44:10 +02:00
ring . desc [ index ] . next = guest . free_head ;
guest . free_head = index ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
guest . num_free + + ;
guest . last_used_idx + + ;
return datap ;
}
2016-10-06 11:39:11 +02:00
bool used_empty ( )
2016-01-21 14:44:10 +02:00
{
2016-10-06 11:39:11 +02:00
unsigned short last_used_idx = guest . last_used_idx ;
2016-01-21 14:44:10 +02:00
# ifdef RING_POLL
2016-10-06 11:39:11 +02:00
unsigned short head = last_used_idx & ( ring_size - 1 ) ;
unsigned index = ring . used - > ring [ head ] . id ;
2016-01-21 14:44:10 +02:00
2016-10-06 11:39:11 +02:00
return ( index ^ last_used_idx ^ 0x8000 ) & ~ ( ring_size - 1 ) ;
2016-01-21 14:44:10 +02:00
# else
2016-10-06 11:39:11 +02:00
return ring . used - > idx = = last_used_idx ;
2016-01-21 14:44:10 +02:00
# endif
}
void disable_call ( )
{
/* Doing nothing to disable calls might cause
* extra interrupts , but reduces the number of cache misses .
*/
}
bool enable_call ( )
{
2016-10-06 11:39:11 +02:00
vring_used_event ( & ring ) = guest . last_used_idx ;
2016-01-21 14:44:10 +02:00
/* Flush call index write */
/* Barrier D (for pairing) */
smp_mb ( ) ;
2016-10-06 11:39:11 +02:00
return used_empty ( ) ;
2016-01-21 14:44:10 +02:00
}
void kick_available ( void )
{
2017-10-27 16:11:13 +03:00
bool need ;
2016-01-21 14:44:10 +02:00
/* Flush in previous flags write */
/* Barrier C (for pairing) */
smp_mb ( ) ;
2017-10-27 16:11:13 +03:00
need = vring_need_event ( vring_avail_event ( & ring ) ,
guest . avail_idx ,
guest . kicked_avail_idx ) ;
2016-01-21 14:44:10 +02:00
guest . kicked_avail_idx = guest . avail_idx ;
2017-10-27 16:11:13 +03:00
if ( need )
kick ( ) ;
2016-01-21 14:44:10 +02:00
}
/* host side */
void disable_kick ( )
{
/* Doing nothing to disable kicks might cause
* extra interrupts , but reduces the number of cache misses .
*/
}
bool enable_kick ( )
{
2016-10-06 11:39:11 +02:00
vring_avail_event ( & ring ) = host . used_idx ;
2016-01-21 14:44:10 +02:00
/* Barrier C (for pairing) */
smp_mb ( ) ;
2016-10-06 11:39:11 +02:00
return avail_empty ( ) ;
2016-01-21 14:44:10 +02:00
}
2016-10-06 11:39:11 +02:00
bool avail_empty ( )
2016-01-21 14:44:10 +02:00
{
unsigned head = host . used_idx ;
# ifdef RING_POLL
2016-10-06 11:39:11 +02:00
unsigned index = ring . avail - > ring [ head & ( ring_size - 1 ) ] ;
return ( ( index ^ head ^ 0x8000 ) & ~ ( ring_size - 1 ) ) ;
2016-01-21 14:44:10 +02:00
# else
2016-10-06 11:39:11 +02:00
return head = = ring . avail - > idx ;
2016-01-21 14:44:10 +02:00
# 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 ;
2016-05-08 18:43:54 +03:00
# ifdef INORDER
head = used_idx ;
# else
2016-01-21 14:44:10 +02:00
head = ring . avail - > ring [ used_idx ] ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
desc = & ring . desc [ head ] ;
# endif
* lenp = desc - > len ;
* bufp = ( void * ) ( unsigned long ) desc - > addr ;
2016-05-08 18:43:54 +03:00
# ifdef INORDER
desc - > len = desc - > len - 1 ;
# else
2016-01-21 14:44:10 +02:00
/* now update used ring */
ring . used - > ring [ used_idx ] . id = head ;
ring . used - > ring [ used_idx ] . len = desc - > len - 1 ;
2016-05-08 18:43:54 +03:00
# endif
2016-01-21 14:44:10 +02:00
/* Barrier B (for pairing) */
smp_release ( ) ;
host . used_idx + + ;
ring . used - > idx = host . used_idx ;
return true ;
}
void call_used ( void )
{
2017-10-27 16:11:13 +03:00
bool need ;
2016-01-21 14:44:10 +02:00
/* Flush in previous flags write */
/* Barrier D (for pairing) */
smp_mb ( ) ;
2017-10-27 16:11:13 +03:00
need = vring_need_event ( vring_used_event ( & ring ) ,
host . used_idx ,
host . called_used_idx ) ;
2016-01-21 14:44:10 +02:00
host . called_used_idx = host . used_idx ;
2017-10-27 16:11:13 +03:00
if ( need )
call ( ) ;
2016-01-21 14:44:10 +02:00
}