1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
2d30ea906Sjfb8856606 * Copyright(c) 2010-2014 Intel Corporation
3a9643ea8Slogwang */
4a9643ea8Slogwang
5a9643ea8Slogwang #ifndef _VIRTQUEUE_H_
6a9643ea8Slogwang #define _VIRTQUEUE_H_
7a9643ea8Slogwang
8a9643ea8Slogwang #include <stdint.h>
9a9643ea8Slogwang
10a9643ea8Slogwang #include <rte_atomic.h>
11a9643ea8Slogwang #include <rte_memory.h>
12a9643ea8Slogwang #include <rte_mempool.h>
13*2d9fd380Sjfb8856606 #include <rte_net.h>
14a9643ea8Slogwang
15a9643ea8Slogwang #include "virtio_pci.h"
16a9643ea8Slogwang #include "virtio_ring.h"
17a9643ea8Slogwang #include "virtio_logs.h"
182bfe3f2eSlogwang #include "virtio_rxtx.h"
19a9643ea8Slogwang
20a9643ea8Slogwang struct rte_mbuf;
21a9643ea8Slogwang
22*2d9fd380Sjfb8856606 #define DEFAULT_TX_FREE_THRESH 32
23*2d9fd380Sjfb8856606 #define DEFAULT_RX_FREE_THRESH 32
24*2d9fd380Sjfb8856606
25*2d9fd380Sjfb8856606 #define VIRTIO_MBUF_BURST_SZ 64
26a9643ea8Slogwang /*
274418919fSjohnjiang * Per virtio_ring.h in Linux.
28a9643ea8Slogwang * For virtio_pci on SMP, we don't need to order with respect to MMIO
29a9643ea8Slogwang * accesses through relaxed memory I/O windows, so smp_mb() et al are
30a9643ea8Slogwang * sufficient.
31a9643ea8Slogwang *
324418919fSjohnjiang * For using virtio to talk to real devices (eg. vDPA) we do need real
334418919fSjohnjiang * barriers.
34a9643ea8Slogwang */
354418919fSjohnjiang static inline void
virtio_mb(uint8_t weak_barriers)364418919fSjohnjiang virtio_mb(uint8_t weak_barriers)
374418919fSjohnjiang {
384418919fSjohnjiang if (weak_barriers)
394418919fSjohnjiang rte_smp_mb();
404418919fSjohnjiang else
414418919fSjohnjiang rte_mb();
424418919fSjohnjiang }
43a9643ea8Slogwang
444418919fSjohnjiang static inline void
virtio_rmb(uint8_t weak_barriers)454418919fSjohnjiang virtio_rmb(uint8_t weak_barriers)
464418919fSjohnjiang {
474418919fSjohnjiang if (weak_barriers)
484418919fSjohnjiang rte_smp_rmb();
494418919fSjohnjiang else
50*2d9fd380Sjfb8856606 rte_io_rmb();
514418919fSjohnjiang }
524418919fSjohnjiang
534418919fSjohnjiang static inline void
virtio_wmb(uint8_t weak_barriers)544418919fSjohnjiang virtio_wmb(uint8_t weak_barriers)
554418919fSjohnjiang {
564418919fSjohnjiang if (weak_barriers)
574418919fSjohnjiang rte_smp_wmb();
584418919fSjohnjiang else
59*2d9fd380Sjfb8856606 rte_io_wmb();
604418919fSjohnjiang }
614418919fSjohnjiang
624418919fSjohnjiang static inline uint16_t
virtqueue_fetch_flags_packed(struct vring_packed_desc * dp,uint8_t weak_barriers)634418919fSjohnjiang virtqueue_fetch_flags_packed(struct vring_packed_desc *dp,
644418919fSjohnjiang uint8_t weak_barriers)
654418919fSjohnjiang {
664418919fSjohnjiang uint16_t flags;
674418919fSjohnjiang
684418919fSjohnjiang if (weak_barriers) {
694418919fSjohnjiang /* x86 prefers to using rte_smp_rmb over __atomic_load_n as it reports
704418919fSjohnjiang * a better perf(~1.5%), which comes from the saved branch by the compiler.
71*2d9fd380Sjfb8856606 * The if and else branch are identical with the smp and io barriers both
724418919fSjohnjiang * defined as compiler barriers on x86.
734418919fSjohnjiang */
744418919fSjohnjiang #ifdef RTE_ARCH_X86_64
754418919fSjohnjiang flags = dp->flags;
764418919fSjohnjiang rte_smp_rmb();
774418919fSjohnjiang #else
784418919fSjohnjiang flags = __atomic_load_n(&dp->flags, __ATOMIC_ACQUIRE);
794418919fSjohnjiang #endif
804418919fSjohnjiang } else {
814418919fSjohnjiang flags = dp->flags;
82*2d9fd380Sjfb8856606 rte_io_rmb();
834418919fSjohnjiang }
844418919fSjohnjiang
854418919fSjohnjiang return flags;
864418919fSjohnjiang }
874418919fSjohnjiang
884418919fSjohnjiang static inline void
virtqueue_store_flags_packed(struct vring_packed_desc * dp,uint16_t flags,uint8_t weak_barriers)894418919fSjohnjiang virtqueue_store_flags_packed(struct vring_packed_desc *dp,
904418919fSjohnjiang uint16_t flags, uint8_t weak_barriers)
914418919fSjohnjiang {
924418919fSjohnjiang if (weak_barriers) {
934418919fSjohnjiang /* x86 prefers to using rte_smp_wmb over __atomic_store_n as it reports
944418919fSjohnjiang * a better perf(~1.5%), which comes from the saved branch by the compiler.
95*2d9fd380Sjfb8856606 * The if and else branch are identical with the smp and io barriers both
964418919fSjohnjiang * defined as compiler barriers on x86.
974418919fSjohnjiang */
984418919fSjohnjiang #ifdef RTE_ARCH_X86_64
994418919fSjohnjiang rte_smp_wmb();
1004418919fSjohnjiang dp->flags = flags;
1014418919fSjohnjiang #else
1024418919fSjohnjiang __atomic_store_n(&dp->flags, flags, __ATOMIC_RELEASE);
1034418919fSjohnjiang #endif
1044418919fSjohnjiang } else {
105*2d9fd380Sjfb8856606 rte_io_wmb();
1064418919fSjohnjiang dp->flags = flags;
1074418919fSjohnjiang }
1084418919fSjohnjiang }
109a9643ea8Slogwang #ifdef RTE_PMD_PACKET_PREFETCH
110a9643ea8Slogwang #define rte_packet_prefetch(p) rte_prefetch1(p)
111a9643ea8Slogwang #else
112a9643ea8Slogwang #define rte_packet_prefetch(p) do {} while(0)
113a9643ea8Slogwang #endif
114a9643ea8Slogwang
115a9643ea8Slogwang #define VIRTQUEUE_MAX_NAME_SZ 32
116a9643ea8Slogwang
117a9643ea8Slogwang #ifdef RTE_VIRTIO_USER
118a9643ea8Slogwang /**
119a9643ea8Slogwang * Return the physical address (or virtual address in case of
120a9643ea8Slogwang * virtio-user) of mbuf data buffer.
1212bfe3f2eSlogwang *
1222bfe3f2eSlogwang * The address is firstly casted to the word size (sizeof(uintptr_t))
1232bfe3f2eSlogwang * before casting it to uint64_t. This is to make it work with different
1242bfe3f2eSlogwang * combination of word size (64 bit and 32 bit) and virtio device
1252bfe3f2eSlogwang * (virtio-pci and virtio-user).
126a9643ea8Slogwang */
1272bfe3f2eSlogwang #define VIRTIO_MBUF_ADDR(mb, vq) \
1282bfe3f2eSlogwang ((uint64_t)(*(uintptr_t *)((uintptr_t)(mb) + (vq)->offset)))
129a9643ea8Slogwang #else
1302bfe3f2eSlogwang #define VIRTIO_MBUF_ADDR(mb, vq) ((mb)->buf_iova)
131a9643ea8Slogwang #endif
132a9643ea8Slogwang
133a9643ea8Slogwang /**
134a9643ea8Slogwang * Return the physical address (or virtual address in case of
135a9643ea8Slogwang * virtio-user) of mbuf data buffer, taking care of mbuf data offset
136a9643ea8Slogwang */
137a9643ea8Slogwang #define VIRTIO_MBUF_DATA_DMA_ADDR(mb, vq) \
138a9643ea8Slogwang (VIRTIO_MBUF_ADDR(mb, vq) + (mb)->data_off)
139a9643ea8Slogwang
140a9643ea8Slogwang #define VTNET_SQ_RQ_QUEUE_IDX 0
141a9643ea8Slogwang #define VTNET_SQ_TQ_QUEUE_IDX 1
142a9643ea8Slogwang #define VTNET_SQ_CQ_QUEUE_IDX 2
143a9643ea8Slogwang
144a9643ea8Slogwang enum { VTNET_RQ = 0, VTNET_TQ = 1, VTNET_CQ = 2 };
145a9643ea8Slogwang /**
146a9643ea8Slogwang * The maximum virtqueue size is 2^15. Use that value as the end of
147a9643ea8Slogwang * descriptor chain terminator since it will never be a valid index
148a9643ea8Slogwang * in the descriptor table. This is used to verify we are correctly
149a9643ea8Slogwang * handling vq_free_cnt.
150a9643ea8Slogwang */
151a9643ea8Slogwang #define VQ_RING_DESC_CHAIN_END 32768
152a9643ea8Slogwang
153a9643ea8Slogwang /**
154a9643ea8Slogwang * Control the RX mode, ie. promiscuous, allmulti, etc...
155a9643ea8Slogwang * All commands require an "out" sg entry containing a 1 byte
156a9643ea8Slogwang * state value, zero = disable, non-zero = enable. Commands
157a9643ea8Slogwang * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
158a9643ea8Slogwang * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
159a9643ea8Slogwang */
160a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX 0
161a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX_PROMISC 0
162a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX_ALLMULTI 1
163a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX_ALLUNI 2
164a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX_NOMULTI 3
165a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX_NOUNI 4
166a9643ea8Slogwang #define VIRTIO_NET_CTRL_RX_NOBCAST 5
167a9643ea8Slogwang
168a9643ea8Slogwang /**
169a9643ea8Slogwang * Control the MAC
170a9643ea8Slogwang *
171a9643ea8Slogwang * The MAC filter table is managed by the hypervisor, the guest should
172a9643ea8Slogwang * assume the size is infinite. Filtering should be considered
173a9643ea8Slogwang * non-perfect, ie. based on hypervisor resources, the guest may
174a9643ea8Slogwang * received packets from sources not specified in the filter list.
175a9643ea8Slogwang *
176a9643ea8Slogwang * In addition to the class/cmd header, the TABLE_SET command requires
177a9643ea8Slogwang * two out scatterlists. Each contains a 4 byte count of entries followed
178a9643ea8Slogwang * by a concatenated byte stream of the ETH_ALEN MAC addresses. The
179a9643ea8Slogwang * first sg list contains unicast addresses, the second is for multicast.
180a9643ea8Slogwang * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
181a9643ea8Slogwang * is available.
182a9643ea8Slogwang *
183a9643ea8Slogwang * The ADDR_SET command requests one out scatterlist, it contains a
184a9643ea8Slogwang * 6 bytes MAC address. This functionality is present if the
185a9643ea8Slogwang * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available.
186a9643ea8Slogwang */
187a9643ea8Slogwang struct virtio_net_ctrl_mac {
188a9643ea8Slogwang uint32_t entries;
1894418919fSjohnjiang uint8_t macs[][RTE_ETHER_ADDR_LEN];
190*2d9fd380Sjfb8856606 } __rte_packed;
191a9643ea8Slogwang
192a9643ea8Slogwang #define VIRTIO_NET_CTRL_MAC 1
193a9643ea8Slogwang #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
194a9643ea8Slogwang #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1
195a9643ea8Slogwang
196a9643ea8Slogwang /**
197a9643ea8Slogwang * Control VLAN filtering
198a9643ea8Slogwang *
199a9643ea8Slogwang * The VLAN filter table is controlled via a simple ADD/DEL interface.
200a9643ea8Slogwang * VLAN IDs not added may be filtered by the hypervisor. Del is the
201a9643ea8Slogwang * opposite of add. Both commands expect an out entry containing a 2
202a9643ea8Slogwang * byte VLAN ID. VLAN filtering is available with the
203a9643ea8Slogwang * VIRTIO_NET_F_CTRL_VLAN feature bit.
204a9643ea8Slogwang */
205a9643ea8Slogwang #define VIRTIO_NET_CTRL_VLAN 2
206a9643ea8Slogwang #define VIRTIO_NET_CTRL_VLAN_ADD 0
207a9643ea8Slogwang #define VIRTIO_NET_CTRL_VLAN_DEL 1
208a9643ea8Slogwang
209d30ea906Sjfb8856606 /*
210d30ea906Sjfb8856606 * Control link announce acknowledgement
211d30ea906Sjfb8856606 *
212d30ea906Sjfb8856606 * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that
213d30ea906Sjfb8856606 * driver has recevied the notification; device would clear the
214d30ea906Sjfb8856606 * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives
215d30ea906Sjfb8856606 * this command.
216d30ea906Sjfb8856606 */
217d30ea906Sjfb8856606 #define VIRTIO_NET_CTRL_ANNOUNCE 3
218d30ea906Sjfb8856606 #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0
219d30ea906Sjfb8856606
220a9643ea8Slogwang struct virtio_net_ctrl_hdr {
221a9643ea8Slogwang uint8_t class;
222a9643ea8Slogwang uint8_t cmd;
223*2d9fd380Sjfb8856606 } __rte_packed;
224a9643ea8Slogwang
225a9643ea8Slogwang typedef uint8_t virtio_net_ctrl_ack;
226a9643ea8Slogwang
227a9643ea8Slogwang #define VIRTIO_NET_OK 0
228a9643ea8Slogwang #define VIRTIO_NET_ERR 1
229a9643ea8Slogwang
230a9643ea8Slogwang #define VIRTIO_MAX_CTRL_DATA 2048
231a9643ea8Slogwang
232a9643ea8Slogwang struct virtio_pmd_ctrl {
233a9643ea8Slogwang struct virtio_net_ctrl_hdr hdr;
234a9643ea8Slogwang virtio_net_ctrl_ack status;
235a9643ea8Slogwang uint8_t data[VIRTIO_MAX_CTRL_DATA];
236a9643ea8Slogwang };
237a9643ea8Slogwang
238a9643ea8Slogwang struct vq_desc_extra {
239a9643ea8Slogwang void *cookie;
240a9643ea8Slogwang uint16_t ndescs;
2414418919fSjohnjiang uint16_t next;
242a9643ea8Slogwang };
243a9643ea8Slogwang
244a9643ea8Slogwang struct virtqueue {
245a9643ea8Slogwang struct virtio_hw *hw; /**< virtio_hw structure pointer. */
2464418919fSjohnjiang union {
2474418919fSjohnjiang struct {
2484418919fSjohnjiang /**< vring keeping desc, used and avail */
2494418919fSjohnjiang struct vring ring;
2504418919fSjohnjiang } vq_split;
2514418919fSjohnjiang
2524418919fSjohnjiang struct {
2534418919fSjohnjiang /**< vring keeping descs and events */
2544418919fSjohnjiang struct vring_packed ring;
2554418919fSjohnjiang bool used_wrap_counter;
2564418919fSjohnjiang uint16_t cached_flags; /**< cached flags for descs */
2574418919fSjohnjiang uint16_t event_flags_shadow;
2584418919fSjohnjiang } vq_packed;
2594418919fSjohnjiang };
2604418919fSjohnjiang
2614418919fSjohnjiang uint16_t vq_used_cons_idx; /**< last consumed descriptor */
262a9643ea8Slogwang uint16_t vq_nentries; /**< vring desc numbers */
263a9643ea8Slogwang uint16_t vq_free_cnt; /**< num of desc available */
264a9643ea8Slogwang uint16_t vq_avail_idx; /**< sync until needed */
265a9643ea8Slogwang uint16_t vq_free_thresh; /**< free threshold */
266a9643ea8Slogwang
267a9643ea8Slogwang void *vq_ring_virt_mem; /**< linear address of vring*/
268a9643ea8Slogwang unsigned int vq_ring_size;
269a9643ea8Slogwang
2702bfe3f2eSlogwang union {
2712bfe3f2eSlogwang struct virtnet_rx rxq;
2722bfe3f2eSlogwang struct virtnet_tx txq;
2732bfe3f2eSlogwang struct virtnet_ctl cq;
2742bfe3f2eSlogwang };
2752bfe3f2eSlogwang
2762bfe3f2eSlogwang rte_iova_t vq_ring_mem; /**< physical address of vring,
277a9643ea8Slogwang * or virtual address for virtio_user. */
278a9643ea8Slogwang
279a9643ea8Slogwang /**
280a9643ea8Slogwang * Head of the free chain in the descriptor table. If
281a9643ea8Slogwang * there are no free descriptors, this will be set to
282a9643ea8Slogwang * VQ_RING_DESC_CHAIN_END.
283a9643ea8Slogwang */
284a9643ea8Slogwang uint16_t vq_desc_head_idx;
285a9643ea8Slogwang uint16_t vq_desc_tail_idx;
286a9643ea8Slogwang uint16_t vq_queue_index; /**< PCI queue index */
287a9643ea8Slogwang uint16_t offset; /**< relative offset to obtain addr in mbuf */
288a9643ea8Slogwang uint16_t *notify_addr;
289a9643ea8Slogwang struct rte_mbuf **sw_ring; /**< RX software ring. */
290a9643ea8Slogwang struct vq_desc_extra vq_descx[0];
291a9643ea8Slogwang };
292a9643ea8Slogwang
293a9643ea8Slogwang /* If multiqueue is provided by host, then we suppport it. */
294a9643ea8Slogwang #define VIRTIO_NET_CTRL_MQ 4
295a9643ea8Slogwang #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
296a9643ea8Slogwang #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
297a9643ea8Slogwang #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
298a9643ea8Slogwang
299a9643ea8Slogwang /**
300a9643ea8Slogwang * This is the first element of the scatter-gather list. If you don't
301a9643ea8Slogwang * specify GSO or CSUM features, you can simply ignore the header.
302a9643ea8Slogwang */
303a9643ea8Slogwang struct virtio_net_hdr {
304a9643ea8Slogwang #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /**< Use csum_start,csum_offset*/
3052bfe3f2eSlogwang #define VIRTIO_NET_HDR_F_DATA_VALID 2 /**< Checksum is valid */
306a9643ea8Slogwang uint8_t flags;
307a9643ea8Slogwang #define VIRTIO_NET_HDR_GSO_NONE 0 /**< Not a GSO frame */
308a9643ea8Slogwang #define VIRTIO_NET_HDR_GSO_TCPV4 1 /**< GSO frame, IPv4 TCP (TSO) */
309a9643ea8Slogwang #define VIRTIO_NET_HDR_GSO_UDP 3 /**< GSO frame, IPv4 UDP (UFO) */
310a9643ea8Slogwang #define VIRTIO_NET_HDR_GSO_TCPV6 4 /**< GSO frame, IPv6 TCP */
311a9643ea8Slogwang #define VIRTIO_NET_HDR_GSO_ECN 0x80 /**< TCP has ECN set */
312a9643ea8Slogwang uint8_t gso_type;
313a9643ea8Slogwang uint16_t hdr_len; /**< Ethernet + IP + tcp/udp hdrs */
314a9643ea8Slogwang uint16_t gso_size; /**< Bytes to append to hdr_len per frame */
315a9643ea8Slogwang uint16_t csum_start; /**< Position to start checksumming from */
316a9643ea8Slogwang uint16_t csum_offset; /**< Offset after that to place checksum */
317a9643ea8Slogwang };
318a9643ea8Slogwang
319a9643ea8Slogwang /**
320a9643ea8Slogwang * This is the version of the header to use when the MRG_RXBUF
321a9643ea8Slogwang * feature has been negotiated.
322a9643ea8Slogwang */
323a9643ea8Slogwang struct virtio_net_hdr_mrg_rxbuf {
324a9643ea8Slogwang struct virtio_net_hdr hdr;
325a9643ea8Slogwang uint16_t num_buffers; /**< Number of merged rx buffers */
326a9643ea8Slogwang };
327a9643ea8Slogwang
328a9643ea8Slogwang /* Region reserved to allow for transmit header and indirect ring */
329a9643ea8Slogwang #define VIRTIO_MAX_TX_INDIRECT 8
330a9643ea8Slogwang struct virtio_tx_region {
331a9643ea8Slogwang struct virtio_net_hdr_mrg_rxbuf tx_hdr;
3320c6bd470Sfengbojiang union {
3330c6bd470Sfengbojiang struct vring_desc tx_indir[VIRTIO_MAX_TX_INDIRECT];
3340c6bd470Sfengbojiang struct vring_packed_desc
3350c6bd470Sfengbojiang tx_packed_indir[VIRTIO_MAX_TX_INDIRECT];
336*2d9fd380Sjfb8856606 } __rte_aligned(16);
337a9643ea8Slogwang };
338a9643ea8Slogwang
3394418919fSjohnjiang static inline int
desc_is_used(struct vring_packed_desc * desc,struct virtqueue * vq)3404418919fSjohnjiang desc_is_used(struct vring_packed_desc *desc, struct virtqueue *vq)
3414418919fSjohnjiang {
3424418919fSjohnjiang uint16_t used, avail, flags;
3434418919fSjohnjiang
3444418919fSjohnjiang flags = virtqueue_fetch_flags_packed(desc, vq->hw->weak_barriers);
3454418919fSjohnjiang used = !!(flags & VRING_PACKED_DESC_F_USED);
3464418919fSjohnjiang avail = !!(flags & VRING_PACKED_DESC_F_AVAIL);
3474418919fSjohnjiang
3484418919fSjohnjiang return avail == used && used == vq->vq_packed.used_wrap_counter;
3494418919fSjohnjiang }
3504418919fSjohnjiang
3514418919fSjohnjiang static inline void
vring_desc_init_packed(struct virtqueue * vq,int n)3524418919fSjohnjiang vring_desc_init_packed(struct virtqueue *vq, int n)
3534418919fSjohnjiang {
3544418919fSjohnjiang int i;
3554418919fSjohnjiang for (i = 0; i < n - 1; i++) {
3564418919fSjohnjiang vq->vq_packed.ring.desc[i].id = i;
3574418919fSjohnjiang vq->vq_descx[i].next = i + 1;
3584418919fSjohnjiang }
3594418919fSjohnjiang vq->vq_packed.ring.desc[i].id = i;
3604418919fSjohnjiang vq->vq_descx[i].next = VQ_RING_DESC_CHAIN_END;
3614418919fSjohnjiang }
3624418919fSjohnjiang
363a9643ea8Slogwang /* Chain all the descriptors in the ring with an END */
364a9643ea8Slogwang static inline void
vring_desc_init_split(struct vring_desc * dp,uint16_t n)3654418919fSjohnjiang vring_desc_init_split(struct vring_desc *dp, uint16_t n)
366a9643ea8Slogwang {
367a9643ea8Slogwang uint16_t i;
368a9643ea8Slogwang
369a9643ea8Slogwang for (i = 0; i < n - 1; i++)
370a9643ea8Slogwang dp[i].next = (uint16_t)(i + 1);
371a9643ea8Slogwang dp[i].next = VQ_RING_DESC_CHAIN_END;
372a9643ea8Slogwang }
373a9643ea8Slogwang
3740c6bd470Sfengbojiang static inline void
vring_desc_init_indirect_packed(struct vring_packed_desc * dp,int n)3750c6bd470Sfengbojiang vring_desc_init_indirect_packed(struct vring_packed_desc *dp, int n)
3760c6bd470Sfengbojiang {
3770c6bd470Sfengbojiang int i;
3780c6bd470Sfengbojiang for (i = 0; i < n; i++) {
3790c6bd470Sfengbojiang dp[i].id = (uint16_t)i;
3800c6bd470Sfengbojiang dp[i].flags = VRING_DESC_F_WRITE;
3810c6bd470Sfengbojiang }
3820c6bd470Sfengbojiang }
3830c6bd470Sfengbojiang
384a9643ea8Slogwang /**
3854418919fSjohnjiang * Tell the backend not to interrupt us. Implementation for packed virtqueues.
3864418919fSjohnjiang */
3874418919fSjohnjiang static inline void
virtqueue_disable_intr_packed(struct virtqueue * vq)3884418919fSjohnjiang virtqueue_disable_intr_packed(struct virtqueue *vq)
3894418919fSjohnjiang {
3904418919fSjohnjiang if (vq->vq_packed.event_flags_shadow != RING_EVENT_FLAGS_DISABLE) {
3914418919fSjohnjiang vq->vq_packed.event_flags_shadow = RING_EVENT_FLAGS_DISABLE;
3924418919fSjohnjiang vq->vq_packed.ring.driver->desc_event_flags =
3934418919fSjohnjiang vq->vq_packed.event_flags_shadow;
3944418919fSjohnjiang }
3954418919fSjohnjiang }
3964418919fSjohnjiang
3974418919fSjohnjiang /**
3984418919fSjohnjiang * Tell the backend not to interrupt us. Implementation for split virtqueues.
3994418919fSjohnjiang */
4004418919fSjohnjiang static inline void
virtqueue_disable_intr_split(struct virtqueue * vq)4014418919fSjohnjiang virtqueue_disable_intr_split(struct virtqueue *vq)
4024418919fSjohnjiang {
4034418919fSjohnjiang vq->vq_split.ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
4044418919fSjohnjiang }
4054418919fSjohnjiang
4064418919fSjohnjiang /**
407a9643ea8Slogwang * Tell the backend not to interrupt us.
408a9643ea8Slogwang */
4092bfe3f2eSlogwang static inline void
virtqueue_disable_intr(struct virtqueue * vq)4102bfe3f2eSlogwang virtqueue_disable_intr(struct virtqueue *vq)
4112bfe3f2eSlogwang {
4124418919fSjohnjiang if (vtpci_packed_queue(vq->hw))
4134418919fSjohnjiang virtqueue_disable_intr_packed(vq);
4144418919fSjohnjiang else
4154418919fSjohnjiang virtqueue_disable_intr_split(vq);
4164418919fSjohnjiang }
4174418919fSjohnjiang
4184418919fSjohnjiang /**
4194418919fSjohnjiang * Tell the backend to interrupt. Implementation for packed virtqueues.
4204418919fSjohnjiang */
4214418919fSjohnjiang static inline void
virtqueue_enable_intr_packed(struct virtqueue * vq)4224418919fSjohnjiang virtqueue_enable_intr_packed(struct virtqueue *vq)
4234418919fSjohnjiang {
4244418919fSjohnjiang if (vq->vq_packed.event_flags_shadow == RING_EVENT_FLAGS_DISABLE) {
4254418919fSjohnjiang vq->vq_packed.event_flags_shadow = RING_EVENT_FLAGS_ENABLE;
4264418919fSjohnjiang vq->vq_packed.ring.driver->desc_event_flags =
4274418919fSjohnjiang vq->vq_packed.event_flags_shadow;
4284418919fSjohnjiang }
4294418919fSjohnjiang }
4304418919fSjohnjiang
4314418919fSjohnjiang /**
4324418919fSjohnjiang * Tell the backend to interrupt. Implementation for split virtqueues.
4334418919fSjohnjiang */
4344418919fSjohnjiang static inline void
virtqueue_enable_intr_split(struct virtqueue * vq)4354418919fSjohnjiang virtqueue_enable_intr_split(struct virtqueue *vq)
4364418919fSjohnjiang {
4374418919fSjohnjiang vq->vq_split.ring.avail->flags &= (~VRING_AVAIL_F_NO_INTERRUPT);
4382bfe3f2eSlogwang }
4392bfe3f2eSlogwang
4402bfe3f2eSlogwang /**
4412bfe3f2eSlogwang * Tell the backend to interrupt us.
4422bfe3f2eSlogwang */
4432bfe3f2eSlogwang static inline void
virtqueue_enable_intr(struct virtqueue * vq)4442bfe3f2eSlogwang virtqueue_enable_intr(struct virtqueue *vq)
4452bfe3f2eSlogwang {
4464418919fSjohnjiang if (vtpci_packed_queue(vq->hw))
4474418919fSjohnjiang virtqueue_enable_intr_packed(vq);
4484418919fSjohnjiang else
4494418919fSjohnjiang virtqueue_enable_intr_split(vq);
4502bfe3f2eSlogwang }
4512bfe3f2eSlogwang
452a9643ea8Slogwang /**
453a9643ea8Slogwang * Dump virtqueue internal structures, for debug purpose only.
454a9643ea8Slogwang */
455a9643ea8Slogwang void virtqueue_dump(struct virtqueue *vq);
456a9643ea8Slogwang /**
457a9643ea8Slogwang * Get all mbufs to be freed.
458a9643ea8Slogwang */
459d30ea906Sjfb8856606 struct rte_mbuf *virtqueue_detach_unused(struct virtqueue *vq);
460a9643ea8Slogwang
4612bfe3f2eSlogwang /* Flush the elements in the used ring. */
4622bfe3f2eSlogwang void virtqueue_rxvq_flush(struct virtqueue *vq);
4632bfe3f2eSlogwang
4644418919fSjohnjiang int virtqueue_rxvq_reset_packed(struct virtqueue *vq);
4654418919fSjohnjiang
4664418919fSjohnjiang int virtqueue_txvq_reset_packed(struct virtqueue *vq);
4674418919fSjohnjiang
468a9643ea8Slogwang static inline int
virtqueue_full(const struct virtqueue * vq)469a9643ea8Slogwang virtqueue_full(const struct virtqueue *vq)
470a9643ea8Slogwang {
471a9643ea8Slogwang return vq->vq_free_cnt == 0;
472a9643ea8Slogwang }
473a9643ea8Slogwang
4742bfe3f2eSlogwang static inline int
virtio_get_queue_type(struct virtio_hw * hw,uint16_t vtpci_queue_idx)4752bfe3f2eSlogwang virtio_get_queue_type(struct virtio_hw *hw, uint16_t vtpci_queue_idx)
4762bfe3f2eSlogwang {
4772bfe3f2eSlogwang if (vtpci_queue_idx == hw->max_queue_pairs * 2)
4782bfe3f2eSlogwang return VTNET_CQ;
4792bfe3f2eSlogwang else if (vtpci_queue_idx % 2 == 0)
4802bfe3f2eSlogwang return VTNET_RQ;
4812bfe3f2eSlogwang else
4822bfe3f2eSlogwang return VTNET_TQ;
4832bfe3f2eSlogwang }
4842bfe3f2eSlogwang
485*2d9fd380Sjfb8856606 /* virtqueue_nused has load-acquire or rte_io_rmb insed */
486*2d9fd380Sjfb8856606 static inline uint16_t
virtqueue_nused(const struct virtqueue * vq)487*2d9fd380Sjfb8856606 virtqueue_nused(const struct virtqueue *vq)
488*2d9fd380Sjfb8856606 {
489*2d9fd380Sjfb8856606 uint16_t idx;
490*2d9fd380Sjfb8856606
491*2d9fd380Sjfb8856606 if (vq->hw->weak_barriers) {
492*2d9fd380Sjfb8856606 /**
493*2d9fd380Sjfb8856606 * x86 prefers to using rte_smp_rmb over __atomic_load_n as it
494*2d9fd380Sjfb8856606 * reports a slightly better perf, which comes from the saved
495*2d9fd380Sjfb8856606 * branch by the compiler.
496*2d9fd380Sjfb8856606 * The if and else branches are identical with the smp and io
497*2d9fd380Sjfb8856606 * barriers both defined as compiler barriers on x86.
498*2d9fd380Sjfb8856606 */
499*2d9fd380Sjfb8856606 #ifdef RTE_ARCH_X86_64
500*2d9fd380Sjfb8856606 idx = vq->vq_split.ring.used->idx;
501*2d9fd380Sjfb8856606 rte_smp_rmb();
502*2d9fd380Sjfb8856606 #else
503*2d9fd380Sjfb8856606 idx = __atomic_load_n(&(vq)->vq_split.ring.used->idx,
504*2d9fd380Sjfb8856606 __ATOMIC_ACQUIRE);
505*2d9fd380Sjfb8856606 #endif
506*2d9fd380Sjfb8856606 } else {
507*2d9fd380Sjfb8856606 idx = vq->vq_split.ring.used->idx;
508*2d9fd380Sjfb8856606 rte_io_rmb();
509*2d9fd380Sjfb8856606 }
510*2d9fd380Sjfb8856606 return idx - vq->vq_used_cons_idx;
511*2d9fd380Sjfb8856606 }
512a9643ea8Slogwang
5132bfe3f2eSlogwang void vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx);
5144418919fSjohnjiang void vq_ring_free_chain_packed(struct virtqueue *vq, uint16_t used_idx);
515d30ea906Sjfb8856606 void vq_ring_free_inorder(struct virtqueue *vq, uint16_t desc_idx,
516d30ea906Sjfb8856606 uint16_t num);
5172bfe3f2eSlogwang
518a9643ea8Slogwang static inline void
vq_update_avail_idx(struct virtqueue * vq)519a9643ea8Slogwang vq_update_avail_idx(struct virtqueue *vq)
520a9643ea8Slogwang {
521*2d9fd380Sjfb8856606 if (vq->hw->weak_barriers) {
522*2d9fd380Sjfb8856606 /* x86 prefers to using rte_smp_wmb over __atomic_store_n as
523*2d9fd380Sjfb8856606 * it reports a slightly better perf, which comes from the
524*2d9fd380Sjfb8856606 * saved branch by the compiler.
525*2d9fd380Sjfb8856606 * The if and else branches are identical with the smp and
526*2d9fd380Sjfb8856606 * io barriers both defined as compiler barriers on x86.
527*2d9fd380Sjfb8856606 */
528*2d9fd380Sjfb8856606 #ifdef RTE_ARCH_X86_64
529*2d9fd380Sjfb8856606 rte_smp_wmb();
5304418919fSjohnjiang vq->vq_split.ring.avail->idx = vq->vq_avail_idx;
531*2d9fd380Sjfb8856606 #else
532*2d9fd380Sjfb8856606 __atomic_store_n(&vq->vq_split.ring.avail->idx,
533*2d9fd380Sjfb8856606 vq->vq_avail_idx, __ATOMIC_RELEASE);
534*2d9fd380Sjfb8856606 #endif
535*2d9fd380Sjfb8856606 } else {
536*2d9fd380Sjfb8856606 rte_io_wmb();
537*2d9fd380Sjfb8856606 vq->vq_split.ring.avail->idx = vq->vq_avail_idx;
538*2d9fd380Sjfb8856606 }
539a9643ea8Slogwang }
540a9643ea8Slogwang
541a9643ea8Slogwang static inline void
vq_update_avail_ring(struct virtqueue * vq,uint16_t desc_idx)542a9643ea8Slogwang vq_update_avail_ring(struct virtqueue *vq, uint16_t desc_idx)
543a9643ea8Slogwang {
544a9643ea8Slogwang uint16_t avail_idx;
545a9643ea8Slogwang /*
546a9643ea8Slogwang * Place the head of the descriptor chain into the next slot and make
547a9643ea8Slogwang * it usable to the host. The chain is made available now rather than
548a9643ea8Slogwang * deferring to virtqueue_notify() in the hopes that if the host is
549a9643ea8Slogwang * currently running on another CPU, we can keep it processing the new
550a9643ea8Slogwang * descriptor.
551a9643ea8Slogwang */
552a9643ea8Slogwang avail_idx = (uint16_t)(vq->vq_avail_idx & (vq->vq_nentries - 1));
5534418919fSjohnjiang if (unlikely(vq->vq_split.ring.avail->ring[avail_idx] != desc_idx))
5544418919fSjohnjiang vq->vq_split.ring.avail->ring[avail_idx] = desc_idx;
555a9643ea8Slogwang vq->vq_avail_idx++;
556a9643ea8Slogwang }
557a9643ea8Slogwang
558a9643ea8Slogwang static inline int
virtqueue_kick_prepare(struct virtqueue * vq)559a9643ea8Slogwang virtqueue_kick_prepare(struct virtqueue *vq)
560a9643ea8Slogwang {
5611646932aSjfb8856606 /*
5621646932aSjfb8856606 * Ensure updated avail->idx is visible to vhost before reading
5631646932aSjfb8856606 * the used->flags.
5641646932aSjfb8856606 */
5654418919fSjohnjiang virtio_mb(vq->hw->weak_barriers);
5664418919fSjohnjiang return !(vq->vq_split.ring.used->flags & VRING_USED_F_NO_NOTIFY);
567a9643ea8Slogwang }
568a9643ea8Slogwang
5694418919fSjohnjiang static inline int
virtqueue_kick_prepare_packed(struct virtqueue * vq)5704418919fSjohnjiang virtqueue_kick_prepare_packed(struct virtqueue *vq)
5714418919fSjohnjiang {
5724418919fSjohnjiang uint16_t flags;
5734418919fSjohnjiang
5744418919fSjohnjiang /*
5754418919fSjohnjiang * Ensure updated data is visible to vhost before reading the flags.
5764418919fSjohnjiang */
5774418919fSjohnjiang virtio_mb(vq->hw->weak_barriers);
5784418919fSjohnjiang flags = vq->vq_packed.ring.device->desc_event_flags;
5794418919fSjohnjiang
5804418919fSjohnjiang return flags != RING_EVENT_FLAGS_DISABLE;
5814418919fSjohnjiang }
5824418919fSjohnjiang
5834418919fSjohnjiang /*
5844418919fSjohnjiang * virtqueue_kick_prepare*() or the virtio_wmb() should be called
5854418919fSjohnjiang * before this function to be sure that all the data is visible to vhost.
5864418919fSjohnjiang */
587a9643ea8Slogwang static inline void
virtqueue_notify(struct virtqueue * vq)588a9643ea8Slogwang virtqueue_notify(struct virtqueue *vq)
589a9643ea8Slogwang {
5902bfe3f2eSlogwang VTPCI_OPS(vq->hw)->notify_queue(vq->hw, vq);
591a9643ea8Slogwang }
592a9643ea8Slogwang
593a9643ea8Slogwang #ifdef RTE_LIBRTE_VIRTIO_DEBUG_DUMP
594a9643ea8Slogwang #define VIRTQUEUE_DUMP(vq) do { \
595a9643ea8Slogwang uint16_t used_idx, nused; \
596*2d9fd380Sjfb8856606 used_idx = __atomic_load_n(&(vq)->vq_split.ring.used->idx, \
597*2d9fd380Sjfb8856606 __ATOMIC_RELAXED); \
598a9643ea8Slogwang nused = (uint16_t)(used_idx - (vq)->vq_used_cons_idx); \
5994418919fSjohnjiang if (vtpci_packed_queue((vq)->hw)) { \
6004418919fSjohnjiang PMD_INIT_LOG(DEBUG, \
6014418919fSjohnjiang "VQ: - size=%d; free=%d; used_cons_idx=%d; avail_idx=%d;" \
6024418919fSjohnjiang " cached_flags=0x%x; used_wrap_counter=%d", \
6034418919fSjohnjiang (vq)->vq_nentries, (vq)->vq_free_cnt, (vq)->vq_used_cons_idx, \
6044418919fSjohnjiang (vq)->vq_avail_idx, (vq)->vq_packed.cached_flags, \
6054418919fSjohnjiang (vq)->vq_packed.used_wrap_counter); \
6064418919fSjohnjiang break; \
6074418919fSjohnjiang } \
608a9643ea8Slogwang PMD_INIT_LOG(DEBUG, \
609a9643ea8Slogwang "VQ: - size=%d; free=%d; used=%d; desc_head_idx=%d;" \
610a9643ea8Slogwang " avail.idx=%d; used_cons_idx=%d; used.idx=%d;" \
611a9643ea8Slogwang " avail.flags=0x%x; used.flags=0x%x", \
612*2d9fd380Sjfb8856606 (vq)->vq_nentries, (vq)->vq_free_cnt, nused, (vq)->vq_desc_head_idx, \
613*2d9fd380Sjfb8856606 (vq)->vq_split.ring.avail->idx, (vq)->vq_used_cons_idx, \
614*2d9fd380Sjfb8856606 __atomic_load_n(&(vq)->vq_split.ring.used->idx, __ATOMIC_RELAXED), \
6154418919fSjohnjiang (vq)->vq_split.ring.avail->flags, (vq)->vq_split.ring.used->flags); \
616a9643ea8Slogwang } while (0)
617a9643ea8Slogwang #else
618a9643ea8Slogwang #define VIRTQUEUE_DUMP(vq) do { } while (0)
619a9643ea8Slogwang #endif
620a9643ea8Slogwang
621*2d9fd380Sjfb8856606 /* avoid write operation when necessary, to lessen cache issues */
622*2d9fd380Sjfb8856606 #define ASSIGN_UNLESS_EQUAL(var, val) do { \
623*2d9fd380Sjfb8856606 typeof(var) *const var_ = &(var); \
624*2d9fd380Sjfb8856606 typeof(val) const val_ = (val); \
625*2d9fd380Sjfb8856606 if (*var_ != val_) \
626*2d9fd380Sjfb8856606 *var_ = val_; \
627*2d9fd380Sjfb8856606 } while (0)
628*2d9fd380Sjfb8856606
629*2d9fd380Sjfb8856606 #define virtqueue_clear_net_hdr(hdr) do { \
630*2d9fd380Sjfb8856606 typeof(hdr) hdr_ = (hdr); \
631*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL((hdr_)->csum_start, 0); \
632*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL((hdr_)->csum_offset, 0); \
633*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL((hdr_)->flags, 0); \
634*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL((hdr_)->gso_type, 0); \
635*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL((hdr_)->gso_size, 0); \
636*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL((hdr_)->hdr_len, 0); \
637*2d9fd380Sjfb8856606 } while (0)
638*2d9fd380Sjfb8856606
639*2d9fd380Sjfb8856606 static inline void
virtqueue_xmit_offload(struct virtio_net_hdr * hdr,struct rte_mbuf * cookie,bool offload)640*2d9fd380Sjfb8856606 virtqueue_xmit_offload(struct virtio_net_hdr *hdr,
641*2d9fd380Sjfb8856606 struct rte_mbuf *cookie,
642*2d9fd380Sjfb8856606 bool offload)
643*2d9fd380Sjfb8856606 {
644*2d9fd380Sjfb8856606 if (offload) {
645*2d9fd380Sjfb8856606 if (cookie->ol_flags & PKT_TX_TCP_SEG)
646*2d9fd380Sjfb8856606 cookie->ol_flags |= PKT_TX_TCP_CKSUM;
647*2d9fd380Sjfb8856606
648*2d9fd380Sjfb8856606 switch (cookie->ol_flags & PKT_TX_L4_MASK) {
649*2d9fd380Sjfb8856606 case PKT_TX_UDP_CKSUM:
650*2d9fd380Sjfb8856606 hdr->csum_start = cookie->l2_len + cookie->l3_len;
651*2d9fd380Sjfb8856606 hdr->csum_offset = offsetof(struct rte_udp_hdr,
652*2d9fd380Sjfb8856606 dgram_cksum);
653*2d9fd380Sjfb8856606 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
654*2d9fd380Sjfb8856606 break;
655*2d9fd380Sjfb8856606
656*2d9fd380Sjfb8856606 case PKT_TX_TCP_CKSUM:
657*2d9fd380Sjfb8856606 hdr->csum_start = cookie->l2_len + cookie->l3_len;
658*2d9fd380Sjfb8856606 hdr->csum_offset = offsetof(struct rte_tcp_hdr, cksum);
659*2d9fd380Sjfb8856606 hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
660*2d9fd380Sjfb8856606 break;
661*2d9fd380Sjfb8856606
662*2d9fd380Sjfb8856606 default:
663*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL(hdr->csum_start, 0);
664*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL(hdr->csum_offset, 0);
665*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL(hdr->flags, 0);
666*2d9fd380Sjfb8856606 break;
667*2d9fd380Sjfb8856606 }
668*2d9fd380Sjfb8856606
669*2d9fd380Sjfb8856606 /* TCP Segmentation Offload */
670*2d9fd380Sjfb8856606 if (cookie->ol_flags & PKT_TX_TCP_SEG) {
671*2d9fd380Sjfb8856606 hdr->gso_type = (cookie->ol_flags & PKT_TX_IPV6) ?
672*2d9fd380Sjfb8856606 VIRTIO_NET_HDR_GSO_TCPV6 :
673*2d9fd380Sjfb8856606 VIRTIO_NET_HDR_GSO_TCPV4;
674*2d9fd380Sjfb8856606 hdr->gso_size = cookie->tso_segsz;
675*2d9fd380Sjfb8856606 hdr->hdr_len =
676*2d9fd380Sjfb8856606 cookie->l2_len +
677*2d9fd380Sjfb8856606 cookie->l3_len +
678*2d9fd380Sjfb8856606 cookie->l4_len;
679*2d9fd380Sjfb8856606 } else {
680*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL(hdr->gso_type, 0);
681*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL(hdr->gso_size, 0);
682*2d9fd380Sjfb8856606 ASSIGN_UNLESS_EQUAL(hdr->hdr_len, 0);
683*2d9fd380Sjfb8856606 }
684*2d9fd380Sjfb8856606 }
685*2d9fd380Sjfb8856606 }
686*2d9fd380Sjfb8856606
687*2d9fd380Sjfb8856606 static inline void
virtqueue_enqueue_xmit_packed(struct virtnet_tx * txvq,struct rte_mbuf * cookie,uint16_t needed,int use_indirect,int can_push,int in_order)688*2d9fd380Sjfb8856606 virtqueue_enqueue_xmit_packed(struct virtnet_tx *txvq, struct rte_mbuf *cookie,
689*2d9fd380Sjfb8856606 uint16_t needed, int use_indirect, int can_push,
690*2d9fd380Sjfb8856606 int in_order)
691*2d9fd380Sjfb8856606 {
692*2d9fd380Sjfb8856606 struct virtio_tx_region *txr = txvq->virtio_net_hdr_mz->addr;
693*2d9fd380Sjfb8856606 struct vq_desc_extra *dxp;
694*2d9fd380Sjfb8856606 struct virtqueue *vq = txvq->vq;
695*2d9fd380Sjfb8856606 struct vring_packed_desc *start_dp, *head_dp;
696*2d9fd380Sjfb8856606 uint16_t idx, id, head_idx, head_flags;
697*2d9fd380Sjfb8856606 int16_t head_size = vq->hw->vtnet_hdr_size;
698*2d9fd380Sjfb8856606 struct virtio_net_hdr *hdr;
699*2d9fd380Sjfb8856606 uint16_t prev;
700*2d9fd380Sjfb8856606 bool prepend_header = false;
701*2d9fd380Sjfb8856606 uint16_t seg_num = cookie->nb_segs;
702*2d9fd380Sjfb8856606
703*2d9fd380Sjfb8856606 id = in_order ? vq->vq_avail_idx : vq->vq_desc_head_idx;
704*2d9fd380Sjfb8856606
705*2d9fd380Sjfb8856606 dxp = &vq->vq_descx[id];
706*2d9fd380Sjfb8856606 dxp->ndescs = needed;
707*2d9fd380Sjfb8856606 dxp->cookie = cookie;
708*2d9fd380Sjfb8856606
709*2d9fd380Sjfb8856606 head_idx = vq->vq_avail_idx;
710*2d9fd380Sjfb8856606 idx = head_idx;
711*2d9fd380Sjfb8856606 prev = head_idx;
712*2d9fd380Sjfb8856606 start_dp = vq->vq_packed.ring.desc;
713*2d9fd380Sjfb8856606
714*2d9fd380Sjfb8856606 head_dp = &vq->vq_packed.ring.desc[idx];
715*2d9fd380Sjfb8856606 head_flags = cookie->next ? VRING_DESC_F_NEXT : 0;
716*2d9fd380Sjfb8856606 head_flags |= vq->vq_packed.cached_flags;
717*2d9fd380Sjfb8856606
718*2d9fd380Sjfb8856606 if (can_push) {
719*2d9fd380Sjfb8856606 /* prepend cannot fail, checked by caller */
720*2d9fd380Sjfb8856606 hdr = rte_pktmbuf_mtod_offset(cookie, struct virtio_net_hdr *,
721*2d9fd380Sjfb8856606 -head_size);
722*2d9fd380Sjfb8856606 prepend_header = true;
723*2d9fd380Sjfb8856606
724*2d9fd380Sjfb8856606 /* if offload disabled, it is not zeroed below, do it now */
725*2d9fd380Sjfb8856606 if (!vq->hw->has_tx_offload)
726*2d9fd380Sjfb8856606 virtqueue_clear_net_hdr(hdr);
727*2d9fd380Sjfb8856606 } else if (use_indirect) {
728*2d9fd380Sjfb8856606 /* setup tx ring slot to point to indirect
729*2d9fd380Sjfb8856606 * descriptor list stored in reserved region.
730*2d9fd380Sjfb8856606 *
731*2d9fd380Sjfb8856606 * the first slot in indirect ring is already preset
732*2d9fd380Sjfb8856606 * to point to the header in reserved region
733*2d9fd380Sjfb8856606 */
734*2d9fd380Sjfb8856606 start_dp[idx].addr = txvq->virtio_net_hdr_mem +
735*2d9fd380Sjfb8856606 RTE_PTR_DIFF(&txr[idx].tx_packed_indir, txr);
736*2d9fd380Sjfb8856606 start_dp[idx].len = (seg_num + 1) *
737*2d9fd380Sjfb8856606 sizeof(struct vring_packed_desc);
738*2d9fd380Sjfb8856606 /* reset flags for indirect desc */
739*2d9fd380Sjfb8856606 head_flags = VRING_DESC_F_INDIRECT;
740*2d9fd380Sjfb8856606 head_flags |= vq->vq_packed.cached_flags;
741*2d9fd380Sjfb8856606 hdr = (struct virtio_net_hdr *)&txr[idx].tx_hdr;
742*2d9fd380Sjfb8856606
743*2d9fd380Sjfb8856606 /* loop below will fill in rest of the indirect elements */
744*2d9fd380Sjfb8856606 start_dp = txr[idx].tx_packed_indir;
745*2d9fd380Sjfb8856606 idx = 1;
746*2d9fd380Sjfb8856606 } else {
747*2d9fd380Sjfb8856606 /* setup first tx ring slot to point to header
748*2d9fd380Sjfb8856606 * stored in reserved region.
749*2d9fd380Sjfb8856606 */
750*2d9fd380Sjfb8856606 start_dp[idx].addr = txvq->virtio_net_hdr_mem +
751*2d9fd380Sjfb8856606 RTE_PTR_DIFF(&txr[idx].tx_hdr, txr);
752*2d9fd380Sjfb8856606 start_dp[idx].len = vq->hw->vtnet_hdr_size;
753*2d9fd380Sjfb8856606 hdr = (struct virtio_net_hdr *)&txr[idx].tx_hdr;
754*2d9fd380Sjfb8856606 idx++;
755*2d9fd380Sjfb8856606 if (idx >= vq->vq_nentries) {
756*2d9fd380Sjfb8856606 idx -= vq->vq_nentries;
757*2d9fd380Sjfb8856606 vq->vq_packed.cached_flags ^=
758*2d9fd380Sjfb8856606 VRING_PACKED_DESC_F_AVAIL_USED;
759*2d9fd380Sjfb8856606 }
760*2d9fd380Sjfb8856606 }
761*2d9fd380Sjfb8856606
762*2d9fd380Sjfb8856606 virtqueue_xmit_offload(hdr, cookie, vq->hw->has_tx_offload);
763*2d9fd380Sjfb8856606
764*2d9fd380Sjfb8856606 do {
765*2d9fd380Sjfb8856606 uint16_t flags;
766*2d9fd380Sjfb8856606
767*2d9fd380Sjfb8856606 start_dp[idx].addr = VIRTIO_MBUF_DATA_DMA_ADDR(cookie, vq);
768*2d9fd380Sjfb8856606 start_dp[idx].len = cookie->data_len;
769*2d9fd380Sjfb8856606 if (prepend_header) {
770*2d9fd380Sjfb8856606 start_dp[idx].addr -= head_size;
771*2d9fd380Sjfb8856606 start_dp[idx].len += head_size;
772*2d9fd380Sjfb8856606 prepend_header = false;
773*2d9fd380Sjfb8856606 }
774*2d9fd380Sjfb8856606
775*2d9fd380Sjfb8856606 if (likely(idx != head_idx)) {
776*2d9fd380Sjfb8856606 flags = cookie->next ? VRING_DESC_F_NEXT : 0;
777*2d9fd380Sjfb8856606 flags |= vq->vq_packed.cached_flags;
778*2d9fd380Sjfb8856606 start_dp[idx].flags = flags;
779*2d9fd380Sjfb8856606 }
780*2d9fd380Sjfb8856606 prev = idx;
781*2d9fd380Sjfb8856606 idx++;
782*2d9fd380Sjfb8856606 if (idx >= vq->vq_nentries) {
783*2d9fd380Sjfb8856606 idx -= vq->vq_nentries;
784*2d9fd380Sjfb8856606 vq->vq_packed.cached_flags ^=
785*2d9fd380Sjfb8856606 VRING_PACKED_DESC_F_AVAIL_USED;
786*2d9fd380Sjfb8856606 }
787*2d9fd380Sjfb8856606 } while ((cookie = cookie->next) != NULL);
788*2d9fd380Sjfb8856606
789*2d9fd380Sjfb8856606 start_dp[prev].id = id;
790*2d9fd380Sjfb8856606
791*2d9fd380Sjfb8856606 if (use_indirect) {
792*2d9fd380Sjfb8856606 idx = head_idx;
793*2d9fd380Sjfb8856606 if (++idx >= vq->vq_nentries) {
794*2d9fd380Sjfb8856606 idx -= vq->vq_nentries;
795*2d9fd380Sjfb8856606 vq->vq_packed.cached_flags ^=
796*2d9fd380Sjfb8856606 VRING_PACKED_DESC_F_AVAIL_USED;
797*2d9fd380Sjfb8856606 }
798*2d9fd380Sjfb8856606 }
799*2d9fd380Sjfb8856606
800*2d9fd380Sjfb8856606 vq->vq_free_cnt = (uint16_t)(vq->vq_free_cnt - needed);
801*2d9fd380Sjfb8856606 vq->vq_avail_idx = idx;
802*2d9fd380Sjfb8856606
803*2d9fd380Sjfb8856606 if (!in_order) {
804*2d9fd380Sjfb8856606 vq->vq_desc_head_idx = dxp->next;
805*2d9fd380Sjfb8856606 if (vq->vq_desc_head_idx == VQ_RING_DESC_CHAIN_END)
806*2d9fd380Sjfb8856606 vq->vq_desc_tail_idx = VQ_RING_DESC_CHAIN_END;
807*2d9fd380Sjfb8856606 }
808*2d9fd380Sjfb8856606
809*2d9fd380Sjfb8856606 virtqueue_store_flags_packed(head_dp, head_flags,
810*2d9fd380Sjfb8856606 vq->hw->weak_barriers);
811*2d9fd380Sjfb8856606 }
812*2d9fd380Sjfb8856606
813*2d9fd380Sjfb8856606 static void
vq_ring_free_id_packed(struct virtqueue * vq,uint16_t id)814*2d9fd380Sjfb8856606 vq_ring_free_id_packed(struct virtqueue *vq, uint16_t id)
815*2d9fd380Sjfb8856606 {
816*2d9fd380Sjfb8856606 struct vq_desc_extra *dxp;
817*2d9fd380Sjfb8856606
818*2d9fd380Sjfb8856606 dxp = &vq->vq_descx[id];
819*2d9fd380Sjfb8856606 vq->vq_free_cnt += dxp->ndescs;
820*2d9fd380Sjfb8856606
821*2d9fd380Sjfb8856606 if (vq->vq_desc_tail_idx == VQ_RING_DESC_CHAIN_END)
822*2d9fd380Sjfb8856606 vq->vq_desc_head_idx = id;
823*2d9fd380Sjfb8856606 else
824*2d9fd380Sjfb8856606 vq->vq_descx[vq->vq_desc_tail_idx].next = id;
825*2d9fd380Sjfb8856606
826*2d9fd380Sjfb8856606 vq->vq_desc_tail_idx = id;
827*2d9fd380Sjfb8856606 dxp->next = VQ_RING_DESC_CHAIN_END;
828*2d9fd380Sjfb8856606 }
829*2d9fd380Sjfb8856606
830*2d9fd380Sjfb8856606 static void
virtio_xmit_cleanup_inorder_packed(struct virtqueue * vq,int num)831*2d9fd380Sjfb8856606 virtio_xmit_cleanup_inorder_packed(struct virtqueue *vq, int num)
832*2d9fd380Sjfb8856606 {
833*2d9fd380Sjfb8856606 uint16_t used_idx, id, curr_id, free_cnt = 0;
834*2d9fd380Sjfb8856606 uint16_t size = vq->vq_nentries;
835*2d9fd380Sjfb8856606 struct vring_packed_desc *desc = vq->vq_packed.ring.desc;
836*2d9fd380Sjfb8856606 struct vq_desc_extra *dxp;
837*2d9fd380Sjfb8856606
838*2d9fd380Sjfb8856606 used_idx = vq->vq_used_cons_idx;
839*2d9fd380Sjfb8856606 /* desc_is_used has a load-acquire or rte_io_rmb inside
840*2d9fd380Sjfb8856606 * and wait for used desc in virtqueue.
841*2d9fd380Sjfb8856606 */
842*2d9fd380Sjfb8856606 while (num > 0 && desc_is_used(&desc[used_idx], vq)) {
843*2d9fd380Sjfb8856606 id = desc[used_idx].id;
844*2d9fd380Sjfb8856606 do {
845*2d9fd380Sjfb8856606 curr_id = used_idx;
846*2d9fd380Sjfb8856606 dxp = &vq->vq_descx[used_idx];
847*2d9fd380Sjfb8856606 used_idx += dxp->ndescs;
848*2d9fd380Sjfb8856606 free_cnt += dxp->ndescs;
849*2d9fd380Sjfb8856606 num -= dxp->ndescs;
850*2d9fd380Sjfb8856606 if (used_idx >= size) {
851*2d9fd380Sjfb8856606 used_idx -= size;
852*2d9fd380Sjfb8856606 vq->vq_packed.used_wrap_counter ^= 1;
853*2d9fd380Sjfb8856606 }
854*2d9fd380Sjfb8856606 if (dxp->cookie != NULL) {
855*2d9fd380Sjfb8856606 rte_pktmbuf_free(dxp->cookie);
856*2d9fd380Sjfb8856606 dxp->cookie = NULL;
857*2d9fd380Sjfb8856606 }
858*2d9fd380Sjfb8856606 } while (curr_id != id);
859*2d9fd380Sjfb8856606 }
860*2d9fd380Sjfb8856606 vq->vq_used_cons_idx = used_idx;
861*2d9fd380Sjfb8856606 vq->vq_free_cnt += free_cnt;
862*2d9fd380Sjfb8856606 }
863*2d9fd380Sjfb8856606
864*2d9fd380Sjfb8856606 static void
virtio_xmit_cleanup_normal_packed(struct virtqueue * vq,int num)865*2d9fd380Sjfb8856606 virtio_xmit_cleanup_normal_packed(struct virtqueue *vq, int num)
866*2d9fd380Sjfb8856606 {
867*2d9fd380Sjfb8856606 uint16_t used_idx, id;
868*2d9fd380Sjfb8856606 uint16_t size = vq->vq_nentries;
869*2d9fd380Sjfb8856606 struct vring_packed_desc *desc = vq->vq_packed.ring.desc;
870*2d9fd380Sjfb8856606 struct vq_desc_extra *dxp;
871*2d9fd380Sjfb8856606
872*2d9fd380Sjfb8856606 used_idx = vq->vq_used_cons_idx;
873*2d9fd380Sjfb8856606 /* desc_is_used has a load-acquire or rte_io_rmb inside
874*2d9fd380Sjfb8856606 * and wait for used desc in virtqueue.
875*2d9fd380Sjfb8856606 */
876*2d9fd380Sjfb8856606 while (num-- && desc_is_used(&desc[used_idx], vq)) {
877*2d9fd380Sjfb8856606 id = desc[used_idx].id;
878*2d9fd380Sjfb8856606 dxp = &vq->vq_descx[id];
879*2d9fd380Sjfb8856606 vq->vq_used_cons_idx += dxp->ndescs;
880*2d9fd380Sjfb8856606 if (vq->vq_used_cons_idx >= size) {
881*2d9fd380Sjfb8856606 vq->vq_used_cons_idx -= size;
882*2d9fd380Sjfb8856606 vq->vq_packed.used_wrap_counter ^= 1;
883*2d9fd380Sjfb8856606 }
884*2d9fd380Sjfb8856606 vq_ring_free_id_packed(vq, id);
885*2d9fd380Sjfb8856606 if (dxp->cookie != NULL) {
886*2d9fd380Sjfb8856606 rte_pktmbuf_free(dxp->cookie);
887*2d9fd380Sjfb8856606 dxp->cookie = NULL;
888*2d9fd380Sjfb8856606 }
889*2d9fd380Sjfb8856606 used_idx = vq->vq_used_cons_idx;
890*2d9fd380Sjfb8856606 }
891*2d9fd380Sjfb8856606 }
892*2d9fd380Sjfb8856606
893*2d9fd380Sjfb8856606 /* Cleanup from completed transmits. */
894*2d9fd380Sjfb8856606 static inline void
virtio_xmit_cleanup_packed(struct virtqueue * vq,int num,int in_order)895*2d9fd380Sjfb8856606 virtio_xmit_cleanup_packed(struct virtqueue *vq, int num, int in_order)
896*2d9fd380Sjfb8856606 {
897*2d9fd380Sjfb8856606 if (in_order)
898*2d9fd380Sjfb8856606 virtio_xmit_cleanup_inorder_packed(vq, num);
899*2d9fd380Sjfb8856606 else
900*2d9fd380Sjfb8856606 virtio_xmit_cleanup_normal_packed(vq, num);
901*2d9fd380Sjfb8856606 }
902*2d9fd380Sjfb8856606
903*2d9fd380Sjfb8856606 static inline void
virtio_xmit_cleanup(struct virtqueue * vq,uint16_t num)904*2d9fd380Sjfb8856606 virtio_xmit_cleanup(struct virtqueue *vq, uint16_t num)
905*2d9fd380Sjfb8856606 {
906*2d9fd380Sjfb8856606 uint16_t i, used_idx, desc_idx;
907*2d9fd380Sjfb8856606 for (i = 0; i < num; i++) {
908*2d9fd380Sjfb8856606 struct vring_used_elem *uep;
909*2d9fd380Sjfb8856606 struct vq_desc_extra *dxp;
910*2d9fd380Sjfb8856606
911*2d9fd380Sjfb8856606 used_idx = (uint16_t)(vq->vq_used_cons_idx &
912*2d9fd380Sjfb8856606 (vq->vq_nentries - 1));
913*2d9fd380Sjfb8856606 uep = &vq->vq_split.ring.used->ring[used_idx];
914*2d9fd380Sjfb8856606
915*2d9fd380Sjfb8856606 desc_idx = (uint16_t)uep->id;
916*2d9fd380Sjfb8856606 dxp = &vq->vq_descx[desc_idx];
917*2d9fd380Sjfb8856606 vq->vq_used_cons_idx++;
918*2d9fd380Sjfb8856606 vq_ring_free_chain(vq, desc_idx);
919*2d9fd380Sjfb8856606
920*2d9fd380Sjfb8856606 if (dxp->cookie != NULL) {
921*2d9fd380Sjfb8856606 rte_pktmbuf_free(dxp->cookie);
922*2d9fd380Sjfb8856606 dxp->cookie = NULL;
923*2d9fd380Sjfb8856606 }
924*2d9fd380Sjfb8856606 }
925*2d9fd380Sjfb8856606 }
926*2d9fd380Sjfb8856606
927*2d9fd380Sjfb8856606 /* Cleanup from completed inorder transmits. */
928*2d9fd380Sjfb8856606 static __rte_always_inline void
virtio_xmit_cleanup_inorder(struct virtqueue * vq,uint16_t num)929*2d9fd380Sjfb8856606 virtio_xmit_cleanup_inorder(struct virtqueue *vq, uint16_t num)
930*2d9fd380Sjfb8856606 {
931*2d9fd380Sjfb8856606 uint16_t i, idx = vq->vq_used_cons_idx;
932*2d9fd380Sjfb8856606 int16_t free_cnt = 0;
933*2d9fd380Sjfb8856606 struct vq_desc_extra *dxp = NULL;
934*2d9fd380Sjfb8856606
935*2d9fd380Sjfb8856606 if (unlikely(num == 0))
936*2d9fd380Sjfb8856606 return;
937*2d9fd380Sjfb8856606
938*2d9fd380Sjfb8856606 for (i = 0; i < num; i++) {
939*2d9fd380Sjfb8856606 dxp = &vq->vq_descx[idx++ & (vq->vq_nentries - 1)];
940*2d9fd380Sjfb8856606 free_cnt += dxp->ndescs;
941*2d9fd380Sjfb8856606 if (dxp->cookie != NULL) {
942*2d9fd380Sjfb8856606 rte_pktmbuf_free(dxp->cookie);
943*2d9fd380Sjfb8856606 dxp->cookie = NULL;
944*2d9fd380Sjfb8856606 }
945*2d9fd380Sjfb8856606 }
946*2d9fd380Sjfb8856606
947*2d9fd380Sjfb8856606 vq->vq_free_cnt += free_cnt;
948*2d9fd380Sjfb8856606 vq->vq_used_cons_idx = idx;
949*2d9fd380Sjfb8856606 }
950a9643ea8Slogwang #endif /* _VIRTQUEUE_H_ */
951