mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Multi-buffer XDP stores information about frags in skb_shared_info that
sits at the tailroom of a packet. The storage space is reserved via
xdp_data_hard_end():
((xdp)->data_hard_start + (xdp)->frame_sz - \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
and then we refer to it via macro below:
static inline struct skb_shared_info *
xdp_get_shared_info_from_buff(const struct xdp_buff *xdp)
{
return (struct skb_shared_info *)xdp_data_hard_end(xdp);
}
Currently we do not respect this tailroom space in multi-buffer AF_XDP
ZC scenario. To address this, introduce xsk_pool_get_tailroom() and use
it within xsk_pool_get_rx_frame_size() which is used in ZC drivers to
configure length of HW Rx buffer.
Typically drivers on Rx Hw buffers side work on 128 byte alignment so
let us align the value returned by xsk_pool_get_rx_frame_size() in order
to avoid addressing this on driver's side. This addresses the fact that
idpf uses mentioned function *before* pool->dev being set so we were at
risk that after subtracting tailroom we would not provide 128-byte
aligned value to HW.
Since xsk_pool_get_rx_frame_size() is actively used in xsk_rcv_check()
and __xsk_rcv(), add a variant of this routine that will not include 128
byte alignment and therefore old behavior is preserved.
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Fixes: 24ea50127e ("xsk: support mbuf on ZC RX")
Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Link: https://patch.msgid.link/20260402154958.562179-3-maciej.fijalkowski@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
502 lines
12 KiB
C
502 lines
12 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/* Interface for implementing AF_XDP zero-copy support in drivers.
|
|
* Copyright(c) 2020 Intel Corporation.
|
|
*/
|
|
|
|
#ifndef _LINUX_XDP_SOCK_DRV_H
|
|
#define _LINUX_XDP_SOCK_DRV_H
|
|
|
|
#include <net/xdp_sock.h>
|
|
#include <net/xsk_buff_pool.h>
|
|
|
|
#define XDP_UMEM_MIN_CHUNK_SHIFT 11
|
|
#define XDP_UMEM_MIN_CHUNK_SIZE (1 << XDP_UMEM_MIN_CHUNK_SHIFT)
|
|
|
|
#define NETDEV_XDP_ACT_XSK (NETDEV_XDP_ACT_BASIC | \
|
|
NETDEV_XDP_ACT_REDIRECT | \
|
|
NETDEV_XDP_ACT_XSK_ZEROCOPY)
|
|
|
|
struct xsk_cb_desc {
|
|
void *src;
|
|
u8 off;
|
|
u8 bytes;
|
|
};
|
|
|
|
#ifdef CONFIG_XDP_SOCKETS
|
|
|
|
void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries);
|
|
bool xsk_tx_peek_desc(struct xsk_buff_pool *pool, struct xdp_desc *desc);
|
|
u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 max);
|
|
void xsk_tx_release(struct xsk_buff_pool *pool);
|
|
struct xsk_buff_pool *xsk_get_pool_from_qid(struct net_device *dev,
|
|
u16 queue_id);
|
|
void xsk_set_rx_need_wakeup(struct xsk_buff_pool *pool);
|
|
void xsk_set_tx_need_wakeup(struct xsk_buff_pool *pool);
|
|
void xsk_clear_rx_need_wakeup(struct xsk_buff_pool *pool);
|
|
void xsk_clear_tx_need_wakeup(struct xsk_buff_pool *pool);
|
|
bool xsk_uses_need_wakeup(struct xsk_buff_pool *pool);
|
|
|
|
static inline u32 xsk_pool_get_headroom(struct xsk_buff_pool *pool)
|
|
{
|
|
return XDP_PACKET_HEADROOM + pool->headroom;
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_tailroom(bool mbuf)
|
|
{
|
|
return mbuf ? SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) : 0;
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_chunk_size(struct xsk_buff_pool *pool)
|
|
{
|
|
return pool->chunk_size;
|
|
}
|
|
|
|
static inline u32 __xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool)
|
|
{
|
|
return xsk_pool_get_chunk_size(pool) - xsk_pool_get_headroom(pool);
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool)
|
|
{
|
|
u32 frame_size = __xsk_pool_get_rx_frame_size(pool);
|
|
struct xdp_umem *umem = pool->umem;
|
|
bool mbuf;
|
|
|
|
/* Reserve tailroom only for zero-copy pools that opted into
|
|
* multi-buffer. The reserved area is used for skb_shared_info,
|
|
* matching the XDP core's xdp_data_hard_end() layout.
|
|
*/
|
|
mbuf = pool->dev && (umem->flags & XDP_UMEM_SG_FLAG);
|
|
frame_size -= xsk_pool_get_tailroom(mbuf);
|
|
|
|
return ALIGN_DOWN(frame_size, 128);
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_rx_frag_step(struct xsk_buff_pool *pool)
|
|
{
|
|
return pool->unaligned ? 0 : xsk_pool_get_chunk_size(pool);
|
|
}
|
|
|
|
static inline void xsk_pool_set_rxq_info(struct xsk_buff_pool *pool,
|
|
struct xdp_rxq_info *rxq)
|
|
{
|
|
xp_set_rxq_info(pool, rxq);
|
|
}
|
|
|
|
static inline void xsk_pool_fill_cb(struct xsk_buff_pool *pool,
|
|
struct xsk_cb_desc *desc)
|
|
{
|
|
xp_fill_cb(pool, desc);
|
|
}
|
|
|
|
static inline void xsk_pool_dma_unmap(struct xsk_buff_pool *pool,
|
|
unsigned long attrs)
|
|
{
|
|
xp_dma_unmap(pool, attrs);
|
|
}
|
|
|
|
static inline int xsk_pool_dma_map(struct xsk_buff_pool *pool,
|
|
struct device *dev, unsigned long attrs)
|
|
{
|
|
struct xdp_umem *umem = pool->umem;
|
|
|
|
return xp_dma_map(pool, dev, attrs, umem->pgs, umem->npgs);
|
|
}
|
|
|
|
static inline dma_addr_t xsk_buff_xdp_get_dma(struct xdp_buff *xdp)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
|
|
|
|
return xp_get_dma(xskb);
|
|
}
|
|
|
|
static inline dma_addr_t xsk_buff_xdp_get_frame_dma(struct xdp_buff *xdp)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
|
|
|
|
return xp_get_frame_dma(xskb);
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_alloc(struct xsk_buff_pool *pool)
|
|
{
|
|
return xp_alloc(pool);
|
|
}
|
|
|
|
static inline bool xsk_is_eop_desc(const struct xdp_desc *desc)
|
|
{
|
|
return !xp_mb_desc(desc);
|
|
}
|
|
|
|
/* Returns as many entries as possible up to max. 0 <= N <= max. */
|
|
static inline u32 xsk_buff_alloc_batch(struct xsk_buff_pool *pool, struct xdp_buff **xdp, u32 max)
|
|
{
|
|
return xp_alloc_batch(pool, xdp, max);
|
|
}
|
|
|
|
static inline bool xsk_buff_can_alloc(struct xsk_buff_pool *pool, u32 count)
|
|
{
|
|
return xp_can_alloc(pool, count);
|
|
}
|
|
|
|
static inline void xsk_buff_free(struct xdp_buff *xdp)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
|
|
struct list_head *xskb_list = &xskb->pool->xskb_list;
|
|
struct xdp_buff_xsk *pos, *tmp;
|
|
|
|
if (likely(!xdp_buff_has_frags(xdp)))
|
|
goto out;
|
|
|
|
list_for_each_entry_safe(pos, tmp, xskb_list, list_node) {
|
|
list_del_init(&pos->list_node);
|
|
xp_free(pos);
|
|
}
|
|
|
|
xdp_get_shared_info_from_buff(xdp)->nr_frags = 0;
|
|
out:
|
|
xp_free(xskb);
|
|
}
|
|
|
|
static inline bool xsk_buff_add_frag(struct xdp_buff *head,
|
|
struct xdp_buff *xdp)
|
|
{
|
|
const void *data = xdp->data;
|
|
struct xdp_buff_xsk *frag;
|
|
|
|
if (!__xdp_buff_add_frag(head, virt_to_netmem(data),
|
|
offset_in_page(data), xdp->data_end - data,
|
|
xdp->frame_sz, false))
|
|
return false;
|
|
|
|
frag = container_of(xdp, struct xdp_buff_xsk, xdp);
|
|
list_add_tail(&frag->list_node, &frag->pool->xskb_list);
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_get_frag(const struct xdp_buff *first)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(first, struct xdp_buff_xsk, xdp);
|
|
struct xdp_buff *ret = NULL;
|
|
struct xdp_buff_xsk *frag;
|
|
|
|
frag = list_first_entry_or_null(&xskb->pool->xskb_list,
|
|
struct xdp_buff_xsk, list_node);
|
|
if (frag) {
|
|
list_del_init(&frag->list_node);
|
|
ret = &frag->xdp;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void xsk_buff_del_frag(struct xdp_buff *xdp)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
|
|
|
|
list_del_init(&xskb->list_node);
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_get_head(struct xdp_buff *first)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(first, struct xdp_buff_xsk, xdp);
|
|
struct xdp_buff_xsk *frag;
|
|
|
|
frag = list_first_entry(&xskb->pool->xskb_list, struct xdp_buff_xsk,
|
|
list_node);
|
|
return &frag->xdp;
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_get_tail(struct xdp_buff *first)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(first, struct xdp_buff_xsk, xdp);
|
|
struct xdp_buff_xsk *frag;
|
|
|
|
frag = list_last_entry(&xskb->pool->xskb_list, struct xdp_buff_xsk,
|
|
list_node);
|
|
return &frag->xdp;
|
|
}
|
|
|
|
static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
|
|
{
|
|
xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM;
|
|
xdp->data_meta = xdp->data;
|
|
xdp->data_end = xdp->data + size;
|
|
xdp->flags = 0;
|
|
}
|
|
|
|
static inline dma_addr_t xsk_buff_raw_get_dma(struct xsk_buff_pool *pool,
|
|
u64 addr)
|
|
{
|
|
return xp_raw_get_dma(pool, addr);
|
|
}
|
|
|
|
static inline void *xsk_buff_raw_get_data(struct xsk_buff_pool *pool, u64 addr)
|
|
{
|
|
return xp_raw_get_data(pool, addr);
|
|
}
|
|
|
|
/**
|
|
* xsk_buff_raw_get_ctx - get &xdp_desc context
|
|
* @pool: XSk buff pool desc address belongs to
|
|
* @addr: desc address (from userspace)
|
|
*
|
|
* Wrapper for xp_raw_get_ctx() to be used in drivers, see its kdoc for
|
|
* details.
|
|
*
|
|
* Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata
|
|
* pointer, if it is present and valid (initialized to %NULL otherwise).
|
|
*/
|
|
static inline struct xdp_desc_ctx
|
|
xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
|
|
{
|
|
return xp_raw_get_ctx(pool, addr);
|
|
}
|
|
|
|
#define XDP_TXMD_FLAGS_VALID ( \
|
|
XDP_TXMD_FLAGS_TIMESTAMP | \
|
|
XDP_TXMD_FLAGS_CHECKSUM | \
|
|
XDP_TXMD_FLAGS_LAUNCH_TIME | \
|
|
0)
|
|
|
|
static inline bool
|
|
xsk_buff_valid_tx_metadata(const struct xsk_tx_metadata *meta)
|
|
{
|
|
return !(meta->flags & ~XDP_TXMD_FLAGS_VALID);
|
|
}
|
|
|
|
static inline struct xsk_tx_metadata *
|
|
__xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data)
|
|
{
|
|
struct xsk_tx_metadata *meta;
|
|
|
|
if (!pool->tx_metadata_len)
|
|
return NULL;
|
|
|
|
meta = data - pool->tx_metadata_len;
|
|
if (unlikely(!xsk_buff_valid_tx_metadata(meta)))
|
|
return NULL; /* no way to signal the error to the user */
|
|
|
|
return meta;
|
|
}
|
|
|
|
static inline struct xsk_tx_metadata *
|
|
xsk_buff_get_metadata(struct xsk_buff_pool *pool, u64 addr)
|
|
{
|
|
return __xsk_buff_get_metadata(pool, xp_raw_get_data(pool, addr));
|
|
}
|
|
|
|
static inline void xsk_buff_dma_sync_for_cpu(struct xdp_buff *xdp)
|
|
{
|
|
struct xdp_buff_xsk *xskb = container_of(xdp, struct xdp_buff_xsk, xdp);
|
|
|
|
xp_dma_sync_for_cpu(xskb);
|
|
}
|
|
|
|
static inline void xsk_buff_raw_dma_sync_for_device(struct xsk_buff_pool *pool,
|
|
dma_addr_t dma,
|
|
size_t size)
|
|
{
|
|
xp_dma_sync_for_device(pool, dma, size);
|
|
}
|
|
|
|
#else
|
|
|
|
static inline void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries)
|
|
{
|
|
}
|
|
|
|
static inline bool xsk_tx_peek_desc(struct xsk_buff_pool *pool,
|
|
struct xdp_desc *desc)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 max)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void xsk_tx_release(struct xsk_buff_pool *pool)
|
|
{
|
|
}
|
|
|
|
static inline struct xsk_buff_pool *
|
|
xsk_get_pool_from_qid(struct net_device *dev, u16 queue_id)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void xsk_set_rx_need_wakeup(struct xsk_buff_pool *pool)
|
|
{
|
|
}
|
|
|
|
static inline void xsk_set_tx_need_wakeup(struct xsk_buff_pool *pool)
|
|
{
|
|
}
|
|
|
|
static inline void xsk_clear_rx_need_wakeup(struct xsk_buff_pool *pool)
|
|
{
|
|
}
|
|
|
|
static inline void xsk_clear_tx_need_wakeup(struct xsk_buff_pool *pool)
|
|
{
|
|
}
|
|
|
|
static inline bool xsk_uses_need_wakeup(struct xsk_buff_pool *pool)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_headroom(struct xsk_buff_pool *pool)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_chunk_size(struct xsk_buff_pool *pool)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_rx_frame_size(struct xsk_buff_pool *pool)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline u32 xsk_pool_get_rx_frag_step(struct xsk_buff_pool *pool)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void xsk_pool_set_rxq_info(struct xsk_buff_pool *pool,
|
|
struct xdp_rxq_info *rxq)
|
|
{
|
|
}
|
|
|
|
static inline void xsk_pool_fill_cb(struct xsk_buff_pool *pool,
|
|
struct xsk_cb_desc *desc)
|
|
{
|
|
}
|
|
|
|
static inline void xsk_pool_dma_unmap(struct xsk_buff_pool *pool,
|
|
unsigned long attrs)
|
|
{
|
|
}
|
|
|
|
static inline int xsk_pool_dma_map(struct xsk_buff_pool *pool,
|
|
struct device *dev, unsigned long attrs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline dma_addr_t xsk_buff_xdp_get_dma(struct xdp_buff *xdp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline dma_addr_t xsk_buff_xdp_get_frame_dma(struct xdp_buff *xdp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_alloc(struct xsk_buff_pool *pool)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline bool xsk_is_eop_desc(const struct xdp_desc *desc)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline u32 xsk_buff_alloc_batch(struct xsk_buff_pool *pool, struct xdp_buff **xdp, u32 max)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline bool xsk_buff_can_alloc(struct xsk_buff_pool *pool, u32 count)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline void xsk_buff_free(struct xdp_buff *xdp)
|
|
{
|
|
}
|
|
|
|
static inline bool xsk_buff_add_frag(struct xdp_buff *head,
|
|
struct xdp_buff *xdp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_get_frag(const struct xdp_buff *first)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void xsk_buff_del_frag(struct xdp_buff *xdp)
|
|
{
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_get_head(struct xdp_buff *first)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct xdp_buff *xsk_buff_get_tail(struct xdp_buff *first)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void xsk_buff_set_size(struct xdp_buff *xdp, u32 size)
|
|
{
|
|
}
|
|
|
|
static inline dma_addr_t xsk_buff_raw_get_dma(struct xsk_buff_pool *pool,
|
|
u64 addr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void *xsk_buff_raw_get_data(struct xsk_buff_pool *pool, u64 addr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct xdp_desc_ctx
|
|
xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
|
|
{
|
|
return (struct xdp_desc_ctx){ };
|
|
}
|
|
|
|
static inline bool xsk_buff_valid_tx_metadata(struct xsk_tx_metadata *meta)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline struct xsk_tx_metadata *
|
|
__xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct xsk_tx_metadata *
|
|
xsk_buff_get_metadata(struct xsk_buff_pool *pool, u64 addr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void xsk_buff_dma_sync_for_cpu(struct xdp_buff *xdp)
|
|
{
|
|
}
|
|
|
|
static inline void xsk_buff_raw_dma_sync_for_device(struct xsk_buff_pool *pool,
|
|
dma_addr_t dma,
|
|
size_t size)
|
|
{
|
|
}
|
|
|
|
#endif /* CONFIG_XDP_SOCKETS */
|
|
|
|
#endif /* _LINUX_XDP_SOCK_DRV_H */
|