xref: /OK3568_Linux_fs/kernel/tools/virtio/ringtest/virtio_ring_0_9.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2016 Red Hat, Inc.
4*4882a593Smuzhiyun  * Author: Michael S. Tsirkin <mst@redhat.com>
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Partial implementation of virtio 0.9. event index is used for signalling,
7*4882a593Smuzhiyun  * unconditionally. Design roughly follows linux kernel implementation in order
8*4882a593Smuzhiyun  * to be able to judge its performance.
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun #define _GNU_SOURCE
11*4882a593Smuzhiyun #include "main.h"
12*4882a593Smuzhiyun #include <stdlib.h>
13*4882a593Smuzhiyun #include <stdio.h>
14*4882a593Smuzhiyun #include <assert.h>
15*4882a593Smuzhiyun #include <string.h>
16*4882a593Smuzhiyun #include <linux/virtio_ring.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun struct data {
19*4882a593Smuzhiyun 	void *data;
20*4882a593Smuzhiyun } *data;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun struct vring ring;
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun /* enabling the below activates experimental ring polling code
25*4882a593Smuzhiyun  * (which skips index reads on consumer in favor of looking at
26*4882a593Smuzhiyun  * high bits of ring id ^ 0x8000).
27*4882a593Smuzhiyun  */
28*4882a593Smuzhiyun /* #ifdef RING_POLL */
29*4882a593Smuzhiyun /* enabling the below activates experimental in-order code
30*4882a593Smuzhiyun  * (which skips ring updates and reads and writes len in descriptor).
31*4882a593Smuzhiyun  */
32*4882a593Smuzhiyun /* #ifdef INORDER */
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #if defined(RING_POLL) && defined(INORDER)
35*4882a593Smuzhiyun #error "RING_POLL and INORDER are mutually exclusive"
36*4882a593Smuzhiyun #endif
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun /* how much padding is needed to avoid false cache sharing */
39*4882a593Smuzhiyun #define HOST_GUEST_PADDING 0x80
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun struct guest {
42*4882a593Smuzhiyun 	unsigned short avail_idx;
43*4882a593Smuzhiyun 	unsigned short last_used_idx;
44*4882a593Smuzhiyun 	unsigned short num_free;
45*4882a593Smuzhiyun 	unsigned short kicked_avail_idx;
46*4882a593Smuzhiyun #ifndef INORDER
47*4882a593Smuzhiyun 	unsigned short free_head;
48*4882a593Smuzhiyun #else
49*4882a593Smuzhiyun 	unsigned short reserved_free_head;
50*4882a593Smuzhiyun #endif
51*4882a593Smuzhiyun 	unsigned char reserved[HOST_GUEST_PADDING - 10];
52*4882a593Smuzhiyun } guest;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun struct host {
55*4882a593Smuzhiyun 	/* we do not need to track last avail index
56*4882a593Smuzhiyun 	 * unless we have more than one in flight.
57*4882a593Smuzhiyun 	 */
58*4882a593Smuzhiyun 	unsigned short used_idx;
59*4882a593Smuzhiyun 	unsigned short called_used_idx;
60*4882a593Smuzhiyun 	unsigned char reserved[HOST_GUEST_PADDING - 4];
61*4882a593Smuzhiyun } host;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun /* implemented by ring */
alloc_ring(void)64*4882a593Smuzhiyun void alloc_ring(void)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	int ret;
67*4882a593Smuzhiyun 	int i;
68*4882a593Smuzhiyun 	void *p;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	ret = posix_memalign(&p, 0x1000, vring_size(ring_size, 0x1000));
71*4882a593Smuzhiyun 	if (ret) {
72*4882a593Smuzhiyun 		perror("Unable to allocate ring buffer.\n");
73*4882a593Smuzhiyun 		exit(3);
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun 	memset(p, 0, vring_size(ring_size, 0x1000));
76*4882a593Smuzhiyun 	vring_init(&ring, ring_size, p, 0x1000);
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	guest.avail_idx = 0;
79*4882a593Smuzhiyun 	guest.kicked_avail_idx = -1;
80*4882a593Smuzhiyun 	guest.last_used_idx = 0;
81*4882a593Smuzhiyun #ifndef INORDER
82*4882a593Smuzhiyun 	/* Put everything in free lists. */
83*4882a593Smuzhiyun 	guest.free_head = 0;
84*4882a593Smuzhiyun #endif
85*4882a593Smuzhiyun 	for (i = 0; i < ring_size - 1; i++)
86*4882a593Smuzhiyun 		ring.desc[i].next = i + 1;
87*4882a593Smuzhiyun 	host.used_idx = 0;
88*4882a593Smuzhiyun 	host.called_used_idx = -1;
89*4882a593Smuzhiyun 	guest.num_free = ring_size;
90*4882a593Smuzhiyun 	data = malloc(ring_size * sizeof *data);
91*4882a593Smuzhiyun 	if (!data) {
92*4882a593Smuzhiyun 		perror("Unable to allocate data buffer.\n");
93*4882a593Smuzhiyun 		exit(3);
94*4882a593Smuzhiyun 	}
95*4882a593Smuzhiyun 	memset(data, 0, ring_size * sizeof *data);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun /* guest side */
add_inbuf(unsigned len,void * buf,void * datap)99*4882a593Smuzhiyun int add_inbuf(unsigned len, void *buf, void *datap)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	unsigned head;
102*4882a593Smuzhiyun #ifndef INORDER
103*4882a593Smuzhiyun 	unsigned avail;
104*4882a593Smuzhiyun #endif
105*4882a593Smuzhiyun 	struct vring_desc *desc;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	if (!guest.num_free)
108*4882a593Smuzhiyun 		return -1;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun #ifdef INORDER
111*4882a593Smuzhiyun 	head = (ring_size - 1) & (guest.avail_idx++);
112*4882a593Smuzhiyun #else
113*4882a593Smuzhiyun 	head = guest.free_head;
114*4882a593Smuzhiyun #endif
115*4882a593Smuzhiyun 	guest.num_free--;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	desc = ring.desc;
118*4882a593Smuzhiyun 	desc[head].flags = VRING_DESC_F_NEXT;
119*4882a593Smuzhiyun 	desc[head].addr = (unsigned long)(void *)buf;
120*4882a593Smuzhiyun 	desc[head].len = len;
121*4882a593Smuzhiyun 	/* We do it like this to simulate the way
122*4882a593Smuzhiyun 	 * we'd have to flip it if we had multiple
123*4882a593Smuzhiyun 	 * descriptors.
124*4882a593Smuzhiyun 	 */
125*4882a593Smuzhiyun 	desc[head].flags &= ~VRING_DESC_F_NEXT;
126*4882a593Smuzhiyun #ifndef INORDER
127*4882a593Smuzhiyun 	guest.free_head = desc[head].next;
128*4882a593Smuzhiyun #endif
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	data[head].data = datap;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun #ifdef RING_POLL
133*4882a593Smuzhiyun 	/* Barrier A (for pairing) */
134*4882a593Smuzhiyun 	smp_release();
135*4882a593Smuzhiyun 	avail = guest.avail_idx++;
136*4882a593Smuzhiyun 	ring.avail->ring[avail & (ring_size - 1)] =
137*4882a593Smuzhiyun 		(head | (avail & ~(ring_size - 1))) ^ 0x8000;
138*4882a593Smuzhiyun #else
139*4882a593Smuzhiyun #ifndef INORDER
140*4882a593Smuzhiyun 	/* Barrier A (for pairing) */
141*4882a593Smuzhiyun 	smp_release();
142*4882a593Smuzhiyun 	avail = (ring_size - 1) & (guest.avail_idx++);
143*4882a593Smuzhiyun 	ring.avail->ring[avail] = head;
144*4882a593Smuzhiyun #endif
145*4882a593Smuzhiyun 	/* Barrier A (for pairing) */
146*4882a593Smuzhiyun 	smp_release();
147*4882a593Smuzhiyun #endif
148*4882a593Smuzhiyun 	ring.avail->idx = guest.avail_idx;
149*4882a593Smuzhiyun 	return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
get_buf(unsigned * lenp,void ** bufp)152*4882a593Smuzhiyun void *get_buf(unsigned *lenp, void **bufp)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	unsigned head;
155*4882a593Smuzhiyun 	unsigned index;
156*4882a593Smuzhiyun 	void *datap;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun #ifdef RING_POLL
159*4882a593Smuzhiyun 	head = (ring_size - 1) & guest.last_used_idx;
160*4882a593Smuzhiyun 	index = ring.used->ring[head].id;
161*4882a593Smuzhiyun 	if ((index ^ guest.last_used_idx ^ 0x8000) & ~(ring_size - 1))
162*4882a593Smuzhiyun 		return NULL;
163*4882a593Smuzhiyun 	/* Barrier B (for pairing) */
164*4882a593Smuzhiyun 	smp_acquire();
165*4882a593Smuzhiyun 	index &= ring_size - 1;
166*4882a593Smuzhiyun #else
167*4882a593Smuzhiyun 	if (ring.used->idx == guest.last_used_idx)
168*4882a593Smuzhiyun 		return NULL;
169*4882a593Smuzhiyun 	/* Barrier B (for pairing) */
170*4882a593Smuzhiyun 	smp_acquire();
171*4882a593Smuzhiyun #ifdef INORDER
172*4882a593Smuzhiyun 	head = (ring_size - 1) & guest.last_used_idx;
173*4882a593Smuzhiyun 	index = head;
174*4882a593Smuzhiyun #else
175*4882a593Smuzhiyun 	head = (ring_size - 1) & guest.last_used_idx;
176*4882a593Smuzhiyun 	index = ring.used->ring[head].id;
177*4882a593Smuzhiyun #endif
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun #endif
180*4882a593Smuzhiyun #ifdef INORDER
181*4882a593Smuzhiyun 	*lenp = ring.desc[index].len;
182*4882a593Smuzhiyun #else
183*4882a593Smuzhiyun 	*lenp = ring.used->ring[head].len;
184*4882a593Smuzhiyun #endif
185*4882a593Smuzhiyun 	datap = data[index].data;
186*4882a593Smuzhiyun 	*bufp = (void*)(unsigned long)ring.desc[index].addr;
187*4882a593Smuzhiyun 	data[index].data = NULL;
188*4882a593Smuzhiyun #ifndef INORDER
189*4882a593Smuzhiyun 	ring.desc[index].next = guest.free_head;
190*4882a593Smuzhiyun 	guest.free_head = index;
191*4882a593Smuzhiyun #endif
192*4882a593Smuzhiyun 	guest.num_free++;
193*4882a593Smuzhiyun 	guest.last_used_idx++;
194*4882a593Smuzhiyun 	return datap;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
used_empty()197*4882a593Smuzhiyun bool used_empty()
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	unsigned short last_used_idx = guest.last_used_idx;
200*4882a593Smuzhiyun #ifdef RING_POLL
201*4882a593Smuzhiyun 	unsigned short head = last_used_idx & (ring_size - 1);
202*4882a593Smuzhiyun 	unsigned index = ring.used->ring[head].id;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	return (index ^ last_used_idx ^ 0x8000) & ~(ring_size - 1);
205*4882a593Smuzhiyun #else
206*4882a593Smuzhiyun 	return ring.used->idx == last_used_idx;
207*4882a593Smuzhiyun #endif
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
disable_call()210*4882a593Smuzhiyun void disable_call()
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	/* Doing nothing to disable calls might cause
213*4882a593Smuzhiyun 	 * extra interrupts, but reduces the number of cache misses.
214*4882a593Smuzhiyun 	 */
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
enable_call()217*4882a593Smuzhiyun bool enable_call()
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 	vring_used_event(&ring) = guest.last_used_idx;
220*4882a593Smuzhiyun 	/* Flush call index write */
221*4882a593Smuzhiyun 	/* Barrier D (for pairing) */
222*4882a593Smuzhiyun 	smp_mb();
223*4882a593Smuzhiyun 	return used_empty();
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
kick_available(void)226*4882a593Smuzhiyun void kick_available(void)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun 	bool need;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	/* Flush in previous flags write */
231*4882a593Smuzhiyun 	/* Barrier C (for pairing) */
232*4882a593Smuzhiyun 	smp_mb();
233*4882a593Smuzhiyun 	need = vring_need_event(vring_avail_event(&ring),
234*4882a593Smuzhiyun 				guest.avail_idx,
235*4882a593Smuzhiyun 				guest.kicked_avail_idx);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	guest.kicked_avail_idx = guest.avail_idx;
238*4882a593Smuzhiyun 	if (need)
239*4882a593Smuzhiyun 		kick();
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun /* host side */
disable_kick()243*4882a593Smuzhiyun void disable_kick()
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	/* Doing nothing to disable kicks might cause
246*4882a593Smuzhiyun 	 * extra interrupts, but reduces the number of cache misses.
247*4882a593Smuzhiyun 	 */
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
enable_kick()250*4882a593Smuzhiyun bool enable_kick()
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	vring_avail_event(&ring) = host.used_idx;
253*4882a593Smuzhiyun 	/* Barrier C (for pairing) */
254*4882a593Smuzhiyun 	smp_mb();
255*4882a593Smuzhiyun 	return avail_empty();
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
avail_empty()258*4882a593Smuzhiyun bool avail_empty()
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	unsigned head = host.used_idx;
261*4882a593Smuzhiyun #ifdef RING_POLL
262*4882a593Smuzhiyun 	unsigned index = ring.avail->ring[head & (ring_size - 1)];
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	return ((index ^ head ^ 0x8000) & ~(ring_size - 1));
265*4882a593Smuzhiyun #else
266*4882a593Smuzhiyun 	return head == ring.avail->idx;
267*4882a593Smuzhiyun #endif
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun 
use_buf(unsigned * lenp,void ** bufp)270*4882a593Smuzhiyun bool use_buf(unsigned *lenp, void **bufp)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun 	unsigned used_idx = host.used_idx;
273*4882a593Smuzhiyun 	struct vring_desc *desc;
274*4882a593Smuzhiyun 	unsigned head;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun #ifdef RING_POLL
277*4882a593Smuzhiyun 	head = ring.avail->ring[used_idx & (ring_size - 1)];
278*4882a593Smuzhiyun 	if ((used_idx ^ head ^ 0x8000) & ~(ring_size - 1))
279*4882a593Smuzhiyun 		return false;
280*4882a593Smuzhiyun 	/* Barrier A (for pairing) */
281*4882a593Smuzhiyun 	smp_acquire();
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	used_idx &= ring_size - 1;
284*4882a593Smuzhiyun 	desc = &ring.desc[head & (ring_size - 1)];
285*4882a593Smuzhiyun #else
286*4882a593Smuzhiyun 	if (used_idx == ring.avail->idx)
287*4882a593Smuzhiyun 		return false;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	/* Barrier A (for pairing) */
290*4882a593Smuzhiyun 	smp_acquire();
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	used_idx &= ring_size - 1;
293*4882a593Smuzhiyun #ifdef INORDER
294*4882a593Smuzhiyun 	head = used_idx;
295*4882a593Smuzhiyun #else
296*4882a593Smuzhiyun 	head = ring.avail->ring[used_idx];
297*4882a593Smuzhiyun #endif
298*4882a593Smuzhiyun 	desc = &ring.desc[head];
299*4882a593Smuzhiyun #endif
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	*lenp = desc->len;
302*4882a593Smuzhiyun 	*bufp = (void *)(unsigned long)desc->addr;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun #ifdef INORDER
305*4882a593Smuzhiyun 	desc->len = desc->len - 1;
306*4882a593Smuzhiyun #else
307*4882a593Smuzhiyun 	/* now update used ring */
308*4882a593Smuzhiyun 	ring.used->ring[used_idx].id = head;
309*4882a593Smuzhiyun 	ring.used->ring[used_idx].len = desc->len - 1;
310*4882a593Smuzhiyun #endif
311*4882a593Smuzhiyun 	/* Barrier B (for pairing) */
312*4882a593Smuzhiyun 	smp_release();
313*4882a593Smuzhiyun 	host.used_idx++;
314*4882a593Smuzhiyun 	ring.used->idx = host.used_idx;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	return true;
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun 
call_used(void)319*4882a593Smuzhiyun void call_used(void)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun 	bool need;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	/* Flush in previous flags write */
324*4882a593Smuzhiyun 	/* Barrier D (for pairing) */
325*4882a593Smuzhiyun 	smp_mb();
326*4882a593Smuzhiyun 	need = vring_need_event(vring_used_event(&ring),
327*4882a593Smuzhiyun 				host.used_idx,
328*4882a593Smuzhiyun 				host.called_used_idx);
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	host.called_used_idx = host.used_idx;
331*4882a593Smuzhiyun 	if (need)
332*4882a593Smuzhiyun 		call();
333*4882a593Smuzhiyun }
334