1831dba47SStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause
2831dba47SStephen Hemminger * Copyright (c) 2009-2012,2016 Microsoft Corp.
3831dba47SStephen Hemminger * Copyright (c) 2012 NetApp Inc.
4831dba47SStephen Hemminger * Copyright (c) 2012 Citrix Inc.
5831dba47SStephen Hemminger * All rights reserved.
6831dba47SStephen Hemminger */
7831dba47SStephen Hemminger
8831dba47SStephen Hemminger #include <unistd.h>
9831dba47SStephen Hemminger #include <stdint.h>
10831dba47SStephen Hemminger #include <stdbool.h>
11831dba47SStephen Hemminger #include <string.h>
12831dba47SStephen Hemminger #include <sys/uio.h>
13831dba47SStephen Hemminger
14831dba47SStephen Hemminger #include <rte_eal.h>
15831dba47SStephen Hemminger #include <rte_tailq.h>
16831dba47SStephen Hemminger #include <rte_log.h>
17831dba47SStephen Hemminger #include <rte_malloc.h>
18831dba47SStephen Hemminger #include <rte_atomic.h>
19831dba47SStephen Hemminger #include <rte_memory.h>
20831dba47SStephen Hemminger #include <rte_pause.h>
21831dba47SStephen Hemminger #include <rte_bus_vmbus.h>
22831dba47SStephen Hemminger
23831dba47SStephen Hemminger #include "private.h"
24831dba47SStephen Hemminger
25831dba47SStephen Hemminger /* Increase bufring index by inc with wraparound */
vmbus_br_idxinc(uint32_t idx,uint32_t inc,uint32_t sz)26831dba47SStephen Hemminger static inline uint32_t vmbus_br_idxinc(uint32_t idx, uint32_t inc, uint32_t sz)
27831dba47SStephen Hemminger {
28831dba47SStephen Hemminger idx += inc;
29831dba47SStephen Hemminger if (idx >= sz)
30831dba47SStephen Hemminger idx -= sz;
31831dba47SStephen Hemminger
32831dba47SStephen Hemminger return idx;
33831dba47SStephen Hemminger }
34831dba47SStephen Hemminger
vmbus_br_setup(struct vmbus_br * br,void * buf,unsigned int blen)35831dba47SStephen Hemminger void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen)
36831dba47SStephen Hemminger {
37831dba47SStephen Hemminger br->vbr = buf;
38831dba47SStephen Hemminger br->windex = br->vbr->windex;
39831dba47SStephen Hemminger br->dsize = blen - sizeof(struct vmbus_bufring);
40831dba47SStephen Hemminger }
41831dba47SStephen Hemminger
42831dba47SStephen Hemminger /*
43831dba47SStephen Hemminger * When we write to the ring buffer, check if the host needs to be
44831dba47SStephen Hemminger * signaled.
45831dba47SStephen Hemminger *
46831dba47SStephen Hemminger * The contract:
47831dba47SStephen Hemminger * - The host guarantees that while it is draining the TX bufring,
48831dba47SStephen Hemminger * it will set the br_imask to indicate it does not need to be
49831dba47SStephen Hemminger * interrupted when new data are added.
50831dba47SStephen Hemminger * - The host guarantees that it will completely drain the TX bufring
51831dba47SStephen Hemminger * before exiting the read loop. Further, once the TX bufring is
52831dba47SStephen Hemminger * empty, it will clear the br_imask and re-check to see if new
53831dba47SStephen Hemminger * data have arrived.
54831dba47SStephen Hemminger */
55831dba47SStephen Hemminger static inline bool
vmbus_txbr_need_signal(const struct vmbus_bufring * vbr,uint32_t old_windex)56*99c67a0aSStephen Hemminger vmbus_txbr_need_signal(const struct vmbus_bufring *vbr, uint32_t old_windex)
57831dba47SStephen Hemminger {
58831dba47SStephen Hemminger rte_smp_mb();
59*99c67a0aSStephen Hemminger if (vbr->imask)
60831dba47SStephen Hemminger return false;
61831dba47SStephen Hemminger
62831dba47SStephen Hemminger rte_smp_rmb();
63831dba47SStephen Hemminger
64831dba47SStephen Hemminger /*
65831dba47SStephen Hemminger * This is the only case we need to signal when the
66831dba47SStephen Hemminger * ring transitions from being empty to non-empty.
67831dba47SStephen Hemminger */
68*99c67a0aSStephen Hemminger return old_windex == vbr->rindex;
69831dba47SStephen Hemminger }
70831dba47SStephen Hemminger
71831dba47SStephen Hemminger static inline uint32_t
vmbus_txbr_copyto(const struct vmbus_br * tbr,uint32_t windex,const void * src0,uint32_t cplen)72831dba47SStephen Hemminger vmbus_txbr_copyto(const struct vmbus_br *tbr, uint32_t windex,
73831dba47SStephen Hemminger const void *src0, uint32_t cplen)
74831dba47SStephen Hemminger {
75831dba47SStephen Hemminger uint8_t *br_data = tbr->vbr->data;
76831dba47SStephen Hemminger uint32_t br_dsize = tbr->dsize;
77831dba47SStephen Hemminger const uint8_t *src = src0;
78831dba47SStephen Hemminger
79831dba47SStephen Hemminger /* XXX use double mapping like Linux kernel? */
80831dba47SStephen Hemminger if (cplen > br_dsize - windex) {
81831dba47SStephen Hemminger uint32_t fraglen = br_dsize - windex;
82831dba47SStephen Hemminger
83831dba47SStephen Hemminger /* Wrap-around detected */
84831dba47SStephen Hemminger memcpy(br_data + windex, src, fraglen);
85831dba47SStephen Hemminger memcpy(br_data, src + fraglen, cplen - fraglen);
86831dba47SStephen Hemminger } else {
87831dba47SStephen Hemminger memcpy(br_data + windex, src, cplen);
88831dba47SStephen Hemminger }
89831dba47SStephen Hemminger
90831dba47SStephen Hemminger return vmbus_br_idxinc(windex, cplen, br_dsize);
91831dba47SStephen Hemminger }
92831dba47SStephen Hemminger
93831dba47SStephen Hemminger /*
94831dba47SStephen Hemminger * Write scattered channel packet to TX bufring.
95831dba47SStephen Hemminger *
96831dba47SStephen Hemminger * The offset of this channel packet is written as a 64bits value
97831dba47SStephen Hemminger * immediately after this channel packet.
98831dba47SStephen Hemminger *
99831dba47SStephen Hemminger * The write goes through three stages:
100831dba47SStephen Hemminger * 1. Reserve space in ring buffer for the new data.
101831dba47SStephen Hemminger * Writer atomically moves priv_write_index.
102831dba47SStephen Hemminger * 2. Copy the new data into the ring.
103831dba47SStephen Hemminger * 3. Update the tail of the ring (visible to host) that indicates
104831dba47SStephen Hemminger * next read location. Writer updates write_index
105831dba47SStephen Hemminger */
106831dba47SStephen Hemminger int
vmbus_txbr_write(struct vmbus_br * tbr,const struct iovec iov[],int iovlen,bool * need_sig)107831dba47SStephen Hemminger vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen,
108831dba47SStephen Hemminger bool *need_sig)
109831dba47SStephen Hemminger {
110831dba47SStephen Hemminger struct vmbus_bufring *vbr = tbr->vbr;
111831dba47SStephen Hemminger uint32_t ring_size = tbr->dsize;
112831dba47SStephen Hemminger uint32_t old_windex, next_windex, windex, total;
113831dba47SStephen Hemminger uint64_t save_windex;
114831dba47SStephen Hemminger int i;
115831dba47SStephen Hemminger
116831dba47SStephen Hemminger total = 0;
117831dba47SStephen Hemminger for (i = 0; i < iovlen; i++)
118831dba47SStephen Hemminger total += iov[i].iov_len;
119831dba47SStephen Hemminger total += sizeof(save_windex);
120831dba47SStephen Hemminger
121831dba47SStephen Hemminger /* Reserve space in ring */
122831dba47SStephen Hemminger do {
123831dba47SStephen Hemminger uint32_t avail;
124831dba47SStephen Hemminger
125831dba47SStephen Hemminger /* Get current free location */
126831dba47SStephen Hemminger old_windex = tbr->windex;
127831dba47SStephen Hemminger
128831dba47SStephen Hemminger /* Prevent compiler reordering this with calculation */
129831dba47SStephen Hemminger rte_compiler_barrier();
130831dba47SStephen Hemminger
131831dba47SStephen Hemminger avail = vmbus_br_availwrite(tbr, old_windex);
132831dba47SStephen Hemminger
133831dba47SStephen Hemminger /* If not enough space in ring, then tell caller. */
134831dba47SStephen Hemminger if (avail <= total)
135831dba47SStephen Hemminger return -EAGAIN;
136831dba47SStephen Hemminger
137831dba47SStephen Hemminger next_windex = vmbus_br_idxinc(old_windex, total, ring_size);
138831dba47SStephen Hemminger
139831dba47SStephen Hemminger /* Atomic update of next write_index for other threads */
140831dba47SStephen Hemminger } while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex));
141831dba47SStephen Hemminger
142831dba47SStephen Hemminger /* Space from old..new is now reserved */
143831dba47SStephen Hemminger windex = old_windex;
144831dba47SStephen Hemminger for (i = 0; i < iovlen; i++) {
145831dba47SStephen Hemminger windex = vmbus_txbr_copyto(tbr, windex,
146831dba47SStephen Hemminger iov[i].iov_base, iov[i].iov_len);
147831dba47SStephen Hemminger }
148831dba47SStephen Hemminger
149831dba47SStephen Hemminger /* Set the offset of the current channel packet. */
150831dba47SStephen Hemminger save_windex = ((uint64_t)old_windex) << 32;
151831dba47SStephen Hemminger windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
152831dba47SStephen Hemminger sizeof(save_windex));
153831dba47SStephen Hemminger
154831dba47SStephen Hemminger /* The region reserved should match region used */
155831dba47SStephen Hemminger RTE_ASSERT(windex == next_windex);
156831dba47SStephen Hemminger
157831dba47SStephen Hemminger /* Ensure that data is available before updating host index */
158831dba47SStephen Hemminger rte_smp_wmb();
159831dba47SStephen Hemminger
160831dba47SStephen Hemminger /* Checkin for our reservation. wait for our turn to update host */
161831dba47SStephen Hemminger while (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex))
162831dba47SStephen Hemminger rte_pause();
163831dba47SStephen Hemminger
164831dba47SStephen Hemminger /* If host had read all data before this, then need to signal */
165*99c67a0aSStephen Hemminger *need_sig |= vmbus_txbr_need_signal(vbr, old_windex);
166831dba47SStephen Hemminger return 0;
167831dba47SStephen Hemminger }
168831dba47SStephen Hemminger
169831dba47SStephen Hemminger static inline uint32_t
vmbus_rxbr_copyfrom(const struct vmbus_br * rbr,uint32_t rindex,void * dst0,size_t cplen)170831dba47SStephen Hemminger vmbus_rxbr_copyfrom(const struct vmbus_br *rbr, uint32_t rindex,
171831dba47SStephen Hemminger void *dst0, size_t cplen)
172831dba47SStephen Hemminger {
173831dba47SStephen Hemminger const uint8_t *br_data = rbr->vbr->data;
174831dba47SStephen Hemminger uint32_t br_dsize = rbr->dsize;
175831dba47SStephen Hemminger uint8_t *dst = dst0;
176831dba47SStephen Hemminger
177831dba47SStephen Hemminger if (cplen > br_dsize - rindex) {
178831dba47SStephen Hemminger uint32_t fraglen = br_dsize - rindex;
179831dba47SStephen Hemminger
180831dba47SStephen Hemminger /* Wrap-around detected. */
181831dba47SStephen Hemminger memcpy(dst, br_data + rindex, fraglen);
182831dba47SStephen Hemminger memcpy(dst + fraglen, br_data, cplen - fraglen);
183831dba47SStephen Hemminger } else {
184831dba47SStephen Hemminger memcpy(dst, br_data + rindex, cplen);
185831dba47SStephen Hemminger }
186831dba47SStephen Hemminger
187831dba47SStephen Hemminger return vmbus_br_idxinc(rindex, cplen, br_dsize);
188831dba47SStephen Hemminger }
189831dba47SStephen Hemminger
190831dba47SStephen Hemminger /* Copy data from receive ring but don't change index */
191831dba47SStephen Hemminger int
vmbus_rxbr_peek(const struct vmbus_br * rbr,void * data,size_t dlen)192831dba47SStephen Hemminger vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen)
193831dba47SStephen Hemminger {
194831dba47SStephen Hemminger uint32_t avail;
195831dba47SStephen Hemminger
196831dba47SStephen Hemminger /*
197831dba47SStephen Hemminger * The requested data and the 64bits channel packet
198831dba47SStephen Hemminger * offset should be there at least.
199831dba47SStephen Hemminger */
200831dba47SStephen Hemminger avail = vmbus_br_availread(rbr);
201831dba47SStephen Hemminger if (avail < dlen + sizeof(uint64_t))
202831dba47SStephen Hemminger return -EAGAIN;
203831dba47SStephen Hemminger
204831dba47SStephen Hemminger vmbus_rxbr_copyfrom(rbr, rbr->vbr->rindex, data, dlen);
205831dba47SStephen Hemminger return 0;
206831dba47SStephen Hemminger }
207831dba47SStephen Hemminger
208831dba47SStephen Hemminger /*
209831dba47SStephen Hemminger * Copy data from receive ring and change index
210831dba47SStephen Hemminger * NOTE:
211831dba47SStephen Hemminger * We assume (dlen + skip) == sizeof(channel packet).
212831dba47SStephen Hemminger */
213831dba47SStephen Hemminger int
vmbus_rxbr_read(struct vmbus_br * rbr,void * data,size_t dlen,size_t skip)214831dba47SStephen Hemminger vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip)
215831dba47SStephen Hemminger {
216831dba47SStephen Hemminger struct vmbus_bufring *vbr = rbr->vbr;
217831dba47SStephen Hemminger uint32_t br_dsize = rbr->dsize;
218831dba47SStephen Hemminger uint32_t rindex;
219831dba47SStephen Hemminger
220831dba47SStephen Hemminger if (vmbus_br_availread(rbr) < dlen + skip + sizeof(uint64_t))
221831dba47SStephen Hemminger return -EAGAIN;
222831dba47SStephen Hemminger
223530af95aSStephen Hemminger /* Record where host was when we started read (for debug) */
224530af95aSStephen Hemminger rbr->windex = rbr->vbr->windex;
225530af95aSStephen Hemminger
226831dba47SStephen Hemminger /*
227831dba47SStephen Hemminger * Copy channel packet from RX bufring.
228831dba47SStephen Hemminger */
229831dba47SStephen Hemminger rindex = vmbus_br_idxinc(rbr->vbr->rindex, skip, br_dsize);
230831dba47SStephen Hemminger rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);
231831dba47SStephen Hemminger
232831dba47SStephen Hemminger /*
233831dba47SStephen Hemminger * Discard this channel packet's 64bits offset, which is useless to us.
234831dba47SStephen Hemminger */
235831dba47SStephen Hemminger rindex = vmbus_br_idxinc(rindex, sizeof(uint64_t), br_dsize);
236831dba47SStephen Hemminger
237831dba47SStephen Hemminger /* Update the read index _after_ the channel packet is fetched. */
238831dba47SStephen Hemminger rte_compiler_barrier();
239831dba47SStephen Hemminger
240831dba47SStephen Hemminger vbr->rindex = rindex;
241831dba47SStephen Hemminger
242831dba47SStephen Hemminger return 0;
243831dba47SStephen Hemminger }
244