Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2018, Microsoft Corporation.
3 : : * All Rights Reserved.
4 : : */
5 : :
6 : : #include <unistd.h>
7 : : #include <stdint.h>
8 : : #include <string.h>
9 : : #include <sys/uio.h>
10 : :
11 : : #include <eal_export.h>
12 : : #include <rte_eal.h>
13 : : #include <rte_tailq.h>
14 : : #include <rte_log.h>
15 : : #include <rte_malloc.h>
16 : : #include <rte_atomic.h>
17 : : #include <rte_memory.h>
18 : : #include <rte_bus_vmbus.h>
19 : :
20 : : #include "private.h"
21 : :
22 : : static inline void
23 : : vmbus_sync_set_bit(volatile RTE_ATOMIC(uint32_t) *addr, uint32_t mask)
24 : : {
25 : 0 : rte_atomic_fetch_or_explicit(addr, mask, rte_memory_order_seq_cst);
26 : : }
27 : :
28 : : static inline void
29 : : vmbus_set_monitor(const struct vmbus_channel *channel, uint32_t monitor_id)
30 : : {
31 : : RTE_ATOMIC(uint32_t) *monitor_addr;
32 : : uint32_t monitor_mask;
33 : : unsigned int trigger_index;
34 : :
35 : 0 : trigger_index = monitor_id / HV_MON_TRIG_LEN;
36 : 0 : monitor_mask = 1u << (monitor_id % HV_MON_TRIG_LEN);
37 : :
38 : 0 : monitor_addr = &channel->monitor_page->trigs[trigger_index].pending;
39 : : vmbus_sync_set_bit(monitor_addr, monitor_mask);
40 : : }
41 : :
42 : : static void
43 : : vmbus_set_event(const struct vmbus_channel *chan)
44 : : {
45 : 0 : vmbus_set_monitor(chan, chan->monitor_id);
46 : 0 : }
47 : :
48 : : /*
49 : : * Set the wait between when hypervisor examines the trigger.
50 : : */
51 : : RTE_EXPORT_SYMBOL(rte_vmbus_set_latency)
52 : : void
53 : 0 : rte_vmbus_set_latency(const struct rte_vmbus_device *dev,
54 : : const struct vmbus_channel *chan,
55 : : uint32_t latency)
56 : : {
57 : 0 : uint32_t trig_idx = chan->monitor_id / VMBUS_MONTRIG_LEN;
58 : 0 : uint32_t trig_offs = chan->monitor_id % VMBUS_MONTRIG_LEN;
59 : :
60 [ # # ]: 0 : if (latency >= UINT16_MAX * 100) {
61 : 0 : VMBUS_LOG(ERR, "invalid latency value %u", latency);
62 : 0 : return;
63 : : }
64 : :
65 [ # # ]: 0 : if (trig_idx >= VMBUS_MONTRIGS_MAX) {
66 : 0 : VMBUS_LOG(ERR, "invalid monitor trigger %u",
67 : : trig_idx);
68 : 0 : return;
69 : : }
70 : :
71 : : /* Host value is expressed in 100 nanosecond units */
72 : 0 : dev->monitor_page->lat[trig_idx][trig_offs] = latency / 100;
73 : : }
74 : :
75 : : /*
76 : : * Notify host that there are data pending on our TX bufring.
77 : : *
78 : : * Since this in userspace, rely on the monitor page.
79 : : * Can't do a hypercall from userspace.
80 : : */
81 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_signal_tx)
82 : : void
83 : 0 : rte_vmbus_chan_signal_tx(const struct vmbus_channel *chan)
84 : : {
85 : : const struct vmbus_br *tbr = &chan->txbr;
86 : :
87 : : /* Make sure all updates are done before signaling host */
88 : 0 : rte_smp_wmb();
89 : :
90 : : /* If host is ignoring interrupts? */
91 [ # # ]: 0 : if (tbr->vbr->imask)
92 : : return;
93 : :
94 : : vmbus_set_event(chan);
95 : : }
96 : :
97 : :
98 : : /* Do a simple send directly using transmit ring. */
99 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_send)
100 : 0 : int rte_vmbus_chan_send(struct vmbus_channel *chan, uint16_t type,
101 : : void *data, uint32_t dlen,
102 : : uint64_t xactid, uint32_t flags, bool *need_sig)
103 : : {
104 : : struct vmbus_chanpkt pkt;
105 : : unsigned int pktlen, pad_pktlen;
106 : : const uint32_t hlen = sizeof(pkt);
107 : 0 : bool send_evt = false;
108 : 0 : uint64_t pad = 0;
109 : : struct iovec iov[3];
110 : : int error;
111 : :
112 : 0 : pktlen = hlen + dlen;
113 : 0 : pad_pktlen = RTE_ALIGN(pktlen, sizeof(uint64_t));
114 : :
115 : 0 : pkt.hdr.type = type;
116 : 0 : pkt.hdr.flags = flags;
117 : 0 : pkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT;
118 : 0 : pkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT;
119 : 0 : pkt.hdr.xactid = xactid;
120 : :
121 : 0 : iov[0].iov_base = &pkt;
122 : 0 : iov[0].iov_len = hlen;
123 : 0 : iov[1].iov_base = data;
124 : 0 : iov[1].iov_len = dlen;
125 : 0 : iov[2].iov_base = &pad;
126 : 0 : iov[2].iov_len = pad_pktlen - pktlen;
127 : :
128 : 0 : error = vmbus_txbr_write(&chan->txbr, iov, 3, &send_evt);
129 : :
130 : : /*
131 : : * caller sets need_sig to non-NULL if it will handle
132 : : * signaling if required later.
133 : : * if need_sig is NULL, signal now if needed.
134 : : */
135 [ # # ]: 0 : if (need_sig)
136 : 0 : *need_sig |= send_evt;
137 [ # # # # ]: 0 : else if (error == 0 && send_evt)
138 : 0 : rte_vmbus_chan_signal_tx(chan);
139 : 0 : return error;
140 : : }
141 : :
142 : : /* Do a scatter/gather send where the descriptor points to data. */
143 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_send_sglist)
144 : 0 : int rte_vmbus_chan_send_sglist(struct vmbus_channel *chan,
145 : : struct vmbus_gpa sg[], uint32_t sglen,
146 : : void *data, uint32_t dlen,
147 : : uint64_t xactid, bool *need_sig)
148 : : {
149 : : struct vmbus_chanpkt_sglist pkt;
150 : : unsigned int pktlen, pad_pktlen, hlen;
151 : 0 : bool send_evt = false;
152 : : struct iovec iov[4];
153 : 0 : uint64_t pad = 0;
154 : : int error;
155 : :
156 : 0 : hlen = offsetof(struct vmbus_chanpkt_sglist, gpa[sglen]);
157 : 0 : pktlen = hlen + dlen;
158 : 0 : pad_pktlen = RTE_ALIGN(pktlen, sizeof(uint64_t));
159 : :
160 : 0 : pkt.hdr.type = VMBUS_CHANPKT_TYPE_GPA;
161 : 0 : pkt.hdr.flags = VMBUS_CHANPKT_FLAG_RC;
162 : 0 : pkt.hdr.hlen = hlen >> VMBUS_CHANPKT_SIZE_SHIFT;
163 : 0 : pkt.hdr.tlen = pad_pktlen >> VMBUS_CHANPKT_SIZE_SHIFT;
164 : 0 : pkt.hdr.xactid = xactid;
165 : 0 : pkt.rsvd = 0;
166 : 0 : pkt.gpa_cnt = sglen;
167 : :
168 : 0 : iov[0].iov_base = &pkt;
169 : 0 : iov[0].iov_len = sizeof(pkt);
170 : 0 : iov[1].iov_base = sg;
171 : 0 : iov[1].iov_len = sizeof(struct vmbus_gpa) * sglen;
172 : 0 : iov[2].iov_base = data;
173 : 0 : iov[2].iov_len = dlen;
174 : 0 : iov[3].iov_base = &pad;
175 : 0 : iov[3].iov_len = pad_pktlen - pktlen;
176 : :
177 : 0 : error = vmbus_txbr_write(&chan->txbr, iov, 4, &send_evt);
178 : :
179 : : /* if caller is batching, just propagate the status */
180 [ # # ]: 0 : if (need_sig)
181 : 0 : *need_sig |= send_evt;
182 [ # # # # ]: 0 : else if (error == 0 && send_evt)
183 : 0 : rte_vmbus_chan_signal_tx(chan);
184 : 0 : return error;
185 : : }
186 : :
187 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_rx_empty)
188 : 0 : bool rte_vmbus_chan_rx_empty(const struct vmbus_channel *channel)
189 : : {
190 : : const struct vmbus_br *br = &channel->rxbr;
191 : :
192 : 0 : rte_smp_rmb();
193 : 0 : return br->vbr->rindex == br->vbr->windex;
194 : : }
195 : :
196 : : /* Signal host after reading N bytes */
197 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_signal_read)
198 : 0 : void rte_vmbus_chan_signal_read(struct vmbus_channel *chan, uint32_t bytes_read)
199 : : {
200 : : struct vmbus_br *rbr = &chan->rxbr;
201 : : uint32_t write_sz, pending_sz;
202 : :
203 : : /* No need for signaling on older versions */
204 [ # # ]: 0 : if (!rbr->vbr->feature_bits.feat_pending_send_sz)
205 : : return;
206 : :
207 : : /* Make sure reading of pending happens after new read index */
208 : : rte_smp_mb();
209 : :
210 : 0 : pending_sz = rbr->vbr->pending_send;
211 [ # # ]: 0 : if (!pending_sz)
212 : : return;
213 : :
214 : 0 : rte_smp_rmb();
215 [ # # ]: 0 : write_sz = vmbus_br_availwrite(rbr, rbr->vbr->windex);
216 : :
217 : : /* If there was space before then host was not blocked */
218 [ # # ]: 0 : if (write_sz - bytes_read > pending_sz)
219 : : return;
220 : :
221 : : /* If pending write will not fit */
222 [ # # ]: 0 : if (write_sz <= pending_sz)
223 : : return;
224 : :
225 : : vmbus_set_event(chan);
226 : : }
227 : :
228 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_recv)
229 : 0 : int rte_vmbus_chan_recv(struct vmbus_channel *chan, void *data, uint32_t *len,
230 : : uint64_t *request_id)
231 : : {
232 : : struct vmbus_chanpkt_hdr pkt;
233 : 0 : uint32_t dlen, hlen, bufferlen = *len;
234 : : int error;
235 : :
236 : 0 : *len = 0;
237 : :
238 : 0 : error = vmbus_rxbr_peek(&chan->rxbr, &pkt, sizeof(pkt));
239 [ # # ]: 0 : if (error)
240 : : return error;
241 : :
242 [ # # ]: 0 : if (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN)) {
243 : 0 : VMBUS_LOG(ERR, "VMBUS recv, invalid hlen %u", pkt.hlen);
244 : : /* XXX this channel is dead actually. */
245 : 0 : return -EIO;
246 : : }
247 : :
248 [ # # ]: 0 : if (unlikely(pkt.hlen > pkt.tlen)) {
249 : 0 : VMBUS_LOG(ERR, "VMBUS recv,invalid hlen %u and tlen %u",
250 : : pkt.hlen, pkt.tlen);
251 : 0 : return -EIO;
252 : : }
253 : :
254 : : /* Length are in quad words */
255 : 0 : hlen = pkt.hlen << VMBUS_CHANPKT_SIZE_SHIFT;
256 : 0 : dlen = (pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT) - hlen;
257 : 0 : *len = dlen;
258 : :
259 : : /* If caller buffer is not large enough */
260 [ # # ]: 0 : if (unlikely(dlen > bufferlen))
261 : : return -ENOBUFS;
262 : :
263 [ # # ]: 0 : if (request_id)
264 : 0 : *request_id = pkt.xactid;
265 : :
266 : : /* Read data and skip packet header */
267 : 0 : error = vmbus_rxbr_read(&chan->rxbr, data, dlen, hlen);
268 [ # # ]: 0 : if (error)
269 : : return error;
270 : :
271 : 0 : rte_vmbus_chan_signal_read(chan, dlen + hlen + sizeof(uint64_t));
272 : 0 : return 0;
273 : : }
274 : :
275 : : /* TODO: replace this with inplace ring buffer (no copy) */
276 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_recv_raw)
277 : 0 : int rte_vmbus_chan_recv_raw(struct vmbus_channel *chan,
278 : : void *data, uint32_t *len)
279 : : {
280 : : struct vmbus_chanpkt_hdr pkt;
281 : 0 : uint32_t dlen, bufferlen = *len;
282 : : int error;
283 : :
284 : 0 : error = vmbus_rxbr_peek(&chan->rxbr, &pkt, sizeof(pkt));
285 [ # # ]: 0 : if (error)
286 : : return error;
287 : :
288 [ # # ]: 0 : if (unlikely(pkt.hlen < VMBUS_CHANPKT_HLEN_MIN)) {
289 : 0 : VMBUS_LOG(ERR, "VMBUS recv, invalid hlen %u", pkt.hlen);
290 : : /* XXX this channel is dead actually. */
291 : 0 : return -EIO;
292 : : }
293 : :
294 [ # # ]: 0 : if (unlikely(pkt.hlen > pkt.tlen)) {
295 : 0 : VMBUS_LOG(ERR, "VMBUS recv,invalid hlen %u and tlen %u",
296 : : pkt.hlen, pkt.tlen);
297 : 0 : return -EIO;
298 : : }
299 : :
300 : : /* Length are in quad words */
301 : 0 : dlen = pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT;
302 : 0 : *len = dlen;
303 : :
304 : : /* If caller buffer is not large enough */
305 [ # # ]: 0 : if (unlikely(dlen > bufferlen))
306 : : return -ENOBUFS;
307 : :
308 : : /* Read data and skip packet header */
309 : 0 : error = vmbus_rxbr_read(&chan->rxbr, data, dlen, 0);
310 [ # # ]: 0 : if (error)
311 : : return error;
312 : :
313 : : /* Return the number of bytes read */
314 : 0 : return dlen + sizeof(uint64_t);
315 : : }
316 : :
317 : 0 : int vmbus_chan_create(const struct rte_vmbus_device *device,
318 : : uint16_t relid, uint16_t subid, uint8_t monitor_id,
319 : : struct vmbus_channel **new_chan)
320 : : {
321 : : struct vmbus_channel *chan;
322 : : int err;
323 : :
324 : 0 : chan = rte_zmalloc_socket("VMBUS", sizeof(*chan), RTE_CACHE_LINE_SIZE,
325 : 0 : device->device.numa_node);
326 [ # # ]: 0 : if (!chan)
327 : : return -ENOMEM;
328 : :
329 : 0 : STAILQ_INIT(&chan->subchannel_list);
330 : 0 : chan->device = device;
331 : 0 : chan->subchannel_id = subid;
332 : 0 : chan->relid = relid;
333 : 0 : chan->monitor_id = monitor_id;
334 : 0 : chan->monitor_page = device->monitor_page;
335 : 0 : *new_chan = chan;
336 : :
337 : 0 : err = vmbus_uio_map_rings(chan);
338 [ # # ]: 0 : if (err) {
339 : 0 : rte_free(chan);
340 : 0 : return err;
341 : : }
342 : :
343 : : return 0;
344 : : }
345 : :
346 : : /* Setup the primary channel */
347 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_open)
348 : 0 : int rte_vmbus_chan_open(struct rte_vmbus_device *device,
349 : : struct vmbus_channel **new_chan)
350 : : {
351 : : struct mapped_vmbus_resource *uio_res;
352 : : int err;
353 : :
354 : 0 : uio_res = vmbus_uio_find_resource(device);
355 [ # # ]: 0 : if (!uio_res) {
356 : 0 : VMBUS_LOG(ERR, "can't find uio resource");
357 : 0 : return -EINVAL;
358 : : }
359 : :
360 : 0 : err = vmbus_chan_create(device, device->relid, 0,
361 : 0 : device->monitor_id, new_chan);
362 [ # # ]: 0 : if (!err)
363 : 0 : device->primary = *new_chan;
364 : :
365 : : return err;
366 : : }
367 : :
368 : : RTE_EXPORT_SYMBOL(rte_vmbus_max_channels)
369 : 0 : int rte_vmbus_max_channels(const struct rte_vmbus_device *device)
370 : : {
371 [ # # ]: 0 : if (vmbus_uio_subchannels_supported(device, device->primary))
372 : : return VMBUS_MAX_CHANNELS;
373 : : else
374 : 0 : return 1;
375 : : }
376 : :
377 : : /* Setup secondary channel */
378 : : RTE_EXPORT_SYMBOL(rte_vmbus_subchan_open)
379 : 0 : int rte_vmbus_subchan_open(struct vmbus_channel *primary,
380 : : struct vmbus_channel **new_chan)
381 : : {
382 : : struct vmbus_channel *chan;
383 : : int err;
384 : :
385 : 0 : err = vmbus_uio_get_subchan(primary, &chan);
386 [ # # ]: 0 : if (err)
387 : : return err;
388 : :
389 : 0 : STAILQ_INSERT_TAIL(&primary->subchannel_list, chan, next);
390 : 0 : *new_chan = chan;
391 : 0 : return 0;
392 : : }
393 : :
394 : : RTE_EXPORT_SYMBOL(rte_vmbus_sub_channel_index)
395 : 0 : uint16_t rte_vmbus_sub_channel_index(const struct vmbus_channel *chan)
396 : : {
397 : 0 : return chan->subchannel_id;
398 : : }
399 : :
400 : : RTE_EXPORT_SYMBOL(rte_vmbus_chan_close)
401 : 0 : void rte_vmbus_chan_close(struct vmbus_channel *chan)
402 : : {
403 : 0 : const struct rte_vmbus_device *device = chan->device;
404 : 0 : struct vmbus_channel *primary = device->primary;
405 : :
406 : : /*
407 : : * intentionally leak primary channel because
408 : : * secondary may still reference it
409 : : */
410 [ # # ]: 0 : if (chan != primary) {
411 [ # # # # : 0 : STAILQ_REMOVE(&primary->subchannel_list, chan,
# # # # ]
412 : : vmbus_channel, next);
413 : 0 : rte_free(chan);
414 : : }
415 : :
416 : 0 : }
417 : :
418 : 0 : static void vmbus_dump_ring(FILE *f, const char *id, const struct vmbus_br *br)
419 : : {
420 : 0 : const struct vmbus_bufring *vbr = br->vbr;
421 : : struct vmbus_chanpkt_hdr pkt;
422 : :
423 : : fprintf(f, "%s windex=%u rindex=%u mask=%u pending=%u feature=%#x\n",
424 : 0 : id, vbr->windex, vbr->rindex, vbr->imask,
425 : 0 : vbr->pending_send, vbr->feature_bits.value);
426 : : fprintf(f, " size=%u avail write=%u read=%u\n",
427 [ # # ]: 0 : br->dsize, vmbus_br_availwrite(br, vbr->windex),
428 : : vmbus_br_availread(br));
429 : :
430 [ # # ]: 0 : if (vmbus_rxbr_peek(br, &pkt, sizeof(pkt)) == 0)
431 : 0 : fprintf(f, " pkt type %#x len %u flags %#x xactid %#"PRIx64"\n",
432 : 0 : pkt.type,
433 : 0 : pkt.tlen << VMBUS_CHANPKT_SIZE_SHIFT,
434 : 0 : pkt.flags, pkt.xactid);
435 : 0 : }
436 : :
437 : 0 : void rte_vmbus_chan_dump(FILE *f, const struct vmbus_channel *chan)
438 : : {
439 : 0 : fprintf(f, "channel[%u] relid=%u monitor=%u\n",
440 : 0 : chan->subchannel_id, chan->relid, chan->monitor_id);
441 : 0 : vmbus_dump_ring(f, "rxbr", &chan->rxbr);
442 : 0 : vmbus_dump_ring(f, "txbr", &chan->txbr);
443 : 0 : }
|