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