Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 : : * Copyright (c) 2012 NetApp Inc.
4 : : * Copyright (c) 2012 Citrix Inc.
5 : : * All rights reserved.
6 : : */
7 : :
8 : : #include <unistd.h>
9 : : #include <stdint.h>
10 : : #include <stdbool.h>
11 : : #include <string.h>
12 : : #include <sys/uio.h>
13 : :
14 : : #include <rte_eal.h>
15 : : #include <rte_tailq.h>
16 : : #include <rte_log.h>
17 : : #include <rte_malloc.h>
18 : : #include <rte_atomic.h>
19 : : #include <rte_memory.h>
20 : : #include <rte_pause.h>
21 : : #include <rte_bus_vmbus.h>
22 : :
23 : : #include "private.h"
24 : :
25 : : /* Increase bufring index by inc with wraparound */
26 : : static inline uint32_t vmbus_br_idxinc(uint32_t idx, uint32_t inc, uint32_t sz)
27 : : {
28 : 0 : idx += inc;
29 [ # # # # : 0 : if (idx >= sz)
# # ]
30 : 0 : idx -= sz;
31 : :
32 : : return idx;
33 : : }
34 : :
35 : 0 : void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen)
36 : : {
37 : 0 : br->vbr = buf;
38 : 0 : br->windex = br->vbr->windex;
39 : :
40 : : /* The ring buffer data starts at the 2nd page of the ring buffer */
41 [ # # ]: 0 : RTE_VERIFY(blen > rte_mem_page_size());
42 : 0 : br->dsize = blen - rte_mem_page_size();
43 : 0 : }
44 : :
45 : : /*
46 : : * When we write to the ring buffer, check if the host needs to be
47 : : * signaled.
48 : : *
49 : : * The contract:
50 : : * - The host guarantees that while it is draining the TX bufring,
51 : : * it will set the br_imask to indicate it does not need to be
52 : : * interrupted when new data are added.
53 : : * - The host guarantees that it will completely drain the TX bufring
54 : : * before exiting the read loop. Further, once the TX bufring is
55 : : * empty, it will clear the br_imask and re-check to see if new
56 : : * data have arrived.
57 : : */
58 : : static inline bool
59 : : vmbus_txbr_need_signal(const struct vmbus_bufring *vbr, uint32_t old_windex)
60 : : {
61 : : rte_smp_mb();
62 [ # # ]: 0 : if (vbr->imask)
63 : : return false;
64 : :
65 : 0 : rte_smp_rmb();
66 : :
67 : : /*
68 : : * This is the only case we need to signal when the
69 : : * ring transitions from being empty to non-empty.
70 : : */
71 : 0 : return old_windex == vbr->rindex;
72 : : }
73 : :
74 : : static inline uint32_t
75 : 0 : vmbus_txbr_copyto(const struct vmbus_br *tbr, uint32_t windex,
76 : : const void *src0, uint32_t cplen)
77 : : {
78 : 0 : uint8_t *br_data = (uint8_t *)tbr->vbr + rte_mem_page_size();
79 : 0 : uint32_t br_dsize = tbr->dsize;
80 : : const uint8_t *src = src0;
81 : :
82 : : /* XXX use double mapping like Linux kernel? */
83 [ # # ]: 0 : if (cplen > br_dsize - windex) {
84 : : uint32_t fraglen = br_dsize - windex;
85 : :
86 : : /* Wrap-around detected */
87 : 0 : memcpy(br_data + windex, src, fraglen);
88 : 0 : memcpy(br_data, src + fraglen, cplen - fraglen);
89 : : } else {
90 : 0 : memcpy(br_data + windex, src, cplen);
91 : : }
92 : :
93 : 0 : return vmbus_br_idxinc(windex, cplen, br_dsize);
94 : : }
95 : :
96 : : /*
97 : : * Write scattered channel packet to TX bufring.
98 : : *
99 : : * The offset of this channel packet is written as a 64bits value
100 : : * immediately after this channel packet.
101 : : *
102 : : * The write goes through three stages:
103 : : * 1. Reserve space in ring buffer for the new data.
104 : : * Writer atomically moves priv_write_index.
105 : : * 2. Copy the new data into the ring.
106 : : * 3. Update the tail of the ring (visible to host) that indicates
107 : : * next read location. Writer updates write_index
108 : : */
109 : : int
110 : 0 : vmbus_txbr_write(struct vmbus_br *tbr, const struct iovec iov[], int iovlen,
111 : : bool *need_sig)
112 : : {
113 : 0 : struct vmbus_bufring *vbr = tbr->vbr;
114 : 0 : uint32_t ring_size = tbr->dsize;
115 : : uint32_t old_windex, next_windex, windex, total;
116 : : uint64_t save_windex;
117 : : int i;
118 : :
119 : : total = 0;
120 [ # # ]: 0 : for (i = 0; i < iovlen; i++)
121 : 0 : total += iov[i].iov_len;
122 : 0 : total += sizeof(save_windex);
123 : :
124 : : /* Reserve space in ring */
125 : : do {
126 : : uint32_t avail;
127 : :
128 : : /* Get current free location */
129 : 0 : old_windex = tbr->windex;
130 : :
131 : : /* Prevent compiler reordering this with calculation */
132 : 0 : rte_compiler_barrier();
133 : :
134 : : avail = vmbus_br_availwrite(tbr, old_windex);
135 : :
136 : : /* If not enough space in ring, then tell caller. */
137 [ # # ]: 0 : if (avail <= total)
138 : : return -EAGAIN;
139 : :
140 : : next_windex = vmbus_br_idxinc(old_windex, total, ring_size);
141 : :
142 : : /* Atomic update of next write_index for other threads */
143 [ # # ]: 0 : } while (!rte_atomic32_cmpset(&tbr->windex, old_windex, next_windex));
144 : :
145 : : /* Space from old..new is now reserved */
146 : : windex = old_windex;
147 [ # # ]: 0 : for (i = 0; i < iovlen; i++) {
148 : 0 : windex = vmbus_txbr_copyto(tbr, windex,
149 : 0 : iov[i].iov_base, iov[i].iov_len);
150 : : }
151 : :
152 : : /* Set the offset of the current channel packet. */
153 : 0 : save_windex = ((uint64_t)old_windex) << 32;
154 : 0 : windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
155 : : sizeof(save_windex));
156 : :
157 : : /* The region reserved should match region used */
158 : : RTE_ASSERT(windex == next_windex);
159 : :
160 : : /* Ensure that data is available before updating host index */
161 : 0 : rte_smp_wmb();
162 : :
163 : : /* Checkin for our reservation. wait for our turn to update host */
164 [ # # ]: 0 : while (!rte_atomic32_cmpset(&vbr->windex, old_windex, next_windex))
165 : : rte_pause();
166 : :
167 : : /* If host had read all data before this, then need to signal */
168 : 0 : *need_sig |= vmbus_txbr_need_signal(vbr, old_windex);
169 : 0 : return 0;
170 : : }
171 : :
172 : : static inline uint32_t
173 : 0 : vmbus_rxbr_copyfrom(const struct vmbus_br *rbr, uint32_t rindex,
174 : : void *dst0, size_t cplen)
175 : : {
176 : 0 : const uint8_t *br_data = (uint8_t *)rbr->vbr + rte_mem_page_size();
177 : 0 : uint32_t br_dsize = rbr->dsize;
178 : : uint8_t *dst = dst0;
179 : :
180 [ # # ]: 0 : if (cplen > br_dsize - rindex) {
181 : : uint32_t fraglen = br_dsize - rindex;
182 : :
183 : : /* Wrap-around detected. */
184 : 0 : memcpy(dst, br_data + rindex, fraglen);
185 : 0 : memcpy(dst + fraglen, br_data, cplen - fraglen);
186 : : } else {
187 : 0 : memcpy(dst, br_data + rindex, cplen);
188 : : }
189 : :
190 [ # # ]: 0 : return vmbus_br_idxinc(rindex, cplen, br_dsize);
191 : : }
192 : :
193 : : /* Copy data from receive ring but don't change index */
194 : : int
195 [ # # ]: 0 : vmbus_rxbr_peek(const struct vmbus_br *rbr, void *data, size_t dlen)
196 : : {
197 : : uint32_t avail;
198 : :
199 : : /*
200 : : * The requested data and the 64bits channel packet
201 : : * offset should be there at least.
202 : : */
203 : : avail = vmbus_br_availread(rbr);
204 [ # # ]: 0 : if (avail < dlen + sizeof(uint64_t))
205 : : return -EAGAIN;
206 : :
207 : 0 : vmbus_rxbr_copyfrom(rbr, rbr->vbr->rindex, data, dlen);
208 : 0 : return 0;
209 : : }
210 : :
211 : : /*
212 : : * Copy data from receive ring and change index
213 : : * NOTE:
214 : : * We assume (dlen + skip) == sizeof(channel packet).
215 : : */
216 : : int
217 : 0 : vmbus_rxbr_read(struct vmbus_br *rbr, void *data, size_t dlen, size_t skip)
218 : : {
219 : 0 : struct vmbus_bufring *vbr = rbr->vbr;
220 [ # # ]: 0 : uint32_t br_dsize = rbr->dsize;
221 : : uint32_t rindex;
222 : :
223 [ # # ]: 0 : if (vmbus_br_availread(rbr) < dlen + skip + sizeof(uint64_t))
224 : : return -EAGAIN;
225 : :
226 : : /* Record where host was when we started read (for debug) */
227 : 0 : rbr->windex = rbr->vbr->windex;
228 : :
229 : : /*
230 : : * Copy channel packet from RX bufring.
231 : : */
232 [ # # ]: 0 : rindex = vmbus_br_idxinc(rbr->vbr->rindex, skip, br_dsize);
233 : 0 : rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);
234 : :
235 : : /*
236 : : * Discard this channel packet's 64bits offset, which is useless to us.
237 : : */
238 : : rindex = vmbus_br_idxinc(rindex, sizeof(uint64_t), br_dsize);
239 : :
240 : : /* Update the read index _after_ the channel packet is fetched. */
241 : 0 : rte_compiler_barrier();
242 : :
243 : 0 : vbr->rindex = rindex;
244 : :
245 : 0 : return 0;
246 : : }
|