1/*
2 * Copyright 2009, 2010 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 */
23/** \file qxl_ring.c
24 * \author Søren Sandmann <sandmann@redhat.com>
25 */
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include <string.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <sched.h>
34#include "qxl.h"
35
36struct ring
37{
38    struct qxl_ring_header	header;
39    uint8_t			elements[0];
40};
41
42struct qxl_ring
43{
44    volatile struct ring *ring;
45    int			element_size;
46    int			n_elements;
47    int			io_port_prod_notify;
48    qxl_screen_t    *qxl;
49};
50
51struct qxl_ring *
52qxl_ring_create (struct qxl_ring_header *header,
53		 int                     element_size,
54		 int                     n_elements,
55		 int			 io_port_prod_notify,
56		 qxl_screen_t           *qxl)
57{
58    struct qxl_ring *ring;
59
60    ring = malloc (sizeof *ring);
61    if (!ring)
62	return NULL;
63
64    ring->ring = (volatile struct ring *)header;
65    ring->element_size = element_size;
66    ring->n_elements = n_elements;
67    ring->io_port_prod_notify = io_port_prod_notify;
68    ring->qxl = qxl;
69    return ring;
70}
71
72void
73qxl_ring_push (struct qxl_ring *ring,
74	       const void      *new_elt)
75{
76    volatile struct qxl_ring_header *header = &(ring->ring->header);
77    volatile uint8_t *elt;
78    int idx;
79
80    while (header->prod - header->cons == header->num_items)
81    {
82	header->notify_on_cons = header->cons + 1;
83#ifdef XSPICE
84	/* in gtkperf, circles, this is a major bottleneck. Can't be that good in a vm either
85	 * Adding the yield reduces cpu usage, but doesn't improve throughput. */
86	sched_yield();
87#endif
88	mem_barrier();
89    }
90
91    idx = header->prod & (ring->n_elements - 1);
92    elt = ring->ring->elements + idx * ring->element_size;
93
94    /* TODO:  We should use proper MMIO accessors; the use of
95             volatile leads to a gcc warning.  See commit f7ba4bae */
96    memcpy((void *)elt, new_elt, ring->element_size);
97
98    header->prod++;
99
100    mem_barrier();
101
102    if (header->prod == header->notify_on_prod) {
103        ioport_write (ring->qxl, ring->io_port_prod_notify, 0);
104    }
105}
106
107Bool
108qxl_ring_pop (struct qxl_ring *ring,
109	      void            *element)
110{
111    volatile struct qxl_ring_header *header = &(ring->ring->header);
112    volatile uint8_t *ring_elt;
113    int idx;
114
115    if (header->cons == header->prod)
116	return FALSE;
117
118    idx = header->cons & (ring->n_elements - 1);
119    ring_elt = ring->ring->elements + idx * ring->element_size;
120
121    memcpy (element, (void *)ring_elt, ring->element_size);
122
123    header->cons++;
124
125    return TRUE;
126}
127
128void
129qxl_ring_wait_idle (struct qxl_ring *ring)
130{
131    while (ring->ring->header.cons != ring->ring->header.prod)
132    {
133	usleep (1000);
134	mem_barrier();
135    }
136}
137
138void
139qxl_ring_request_notify (struct qxl_ring *ring)
140{
141    ring->ring->header.notify_on_prod = ring->ring->header.prod + 1;
142    ErrorF("%s: requesting notify on prod %d\n", __func__,
143           ring->ring->header.notify_on_prod);
144}
145
146int
147qxl_ring_cons (struct qxl_ring *ring)
148{
149    return ring->ring->header.cons;
150}
151
152int
153qxl_ring_prod (struct qxl_ring *ring)
154{
155    return ring->ring->header.prod;
156}
157