Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  xdp.c  Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */

#define DEFAULT_SYMBOL_NAMESPACE "LIBETH_XDP"

#include <linux/export.h>

#include <net/libeth/xdp.h>

#include "priv.h"

/* XDPSQ sharing */

DEFINE_STATIC_KEY_FALSE(libeth_xdpsq_share);
EXPORT_SYMBOL_GPL(libeth_xdpsq_share);

void __libeth_xdpsq_get(struct libeth_xdpsq_lock *lock,
   const struct net_device *dev)
{
 bool warn;

 spin_lock_init(&lock->lock);
 lock->share = true;

 warn = !static_key_enabled(&libeth_xdpsq_share);
 static_branch_inc(&libeth_xdpsq_share);

 if (warn && net_ratelimit())
  netdev_warn(dev, "XDPSQ sharing enabled, possible XDP Tx slowdown\n");
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_get);

void __libeth_xdpsq_put(struct libeth_xdpsq_lock *lock,
   const struct net_device *dev)
{
 static_branch_dec(&libeth_xdpsq_share);

 if (!static_key_enabled(&libeth_xdpsq_share) && net_ratelimit())
  netdev_notice(dev, "XDPSQ sharing disabled\n");

 lock->share = false;
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_put);

void __acquires(&lock->lock)
__libeth_xdpsq_lock(struct libeth_xdpsq_lock *lock)
{
 spin_lock(&lock->lock);
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_lock);

void __releases(&lock->lock)
__libeth_xdpsq_unlock(struct libeth_xdpsq_lock *lock)
{
 spin_unlock(&lock->lock);
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_unlock);

/* XDPSQ clean-up timers */

/**
 * libeth_xdpsq_init_timer - initialize an XDPSQ clean-up timer
 * @timer: timer to initialize
 * @xdpsq: queue this timer belongs to
 * @lock: corresponding XDPSQ lock
 * @poll: queue polling/completion function
 *
 * XDPSQ clean-up timers must be set up before using at the queue configuration
 * time. Set the required pointers and the cleaning callback.
 */

void libeth_xdpsq_init_timer(struct libeth_xdpsq_timer *timer, void *xdpsq,
        struct libeth_xdpsq_lock *lock,
        void (*poll)(struct work_struct *work))
{
 timer->xdpsq = xdpsq;
 timer->lock = lock;

 INIT_DELAYED_WORK(&timer->dwork, poll);
}
EXPORT_SYMBOL_GPL(libeth_xdpsq_init_timer);

/* ``XDP_TX`` bulking */

static void __cold
libeth_xdp_tx_return_one(const struct libeth_xdp_tx_frame *frm)
{
 if (frm->len_fl & LIBETH_XDP_TX_MULTI)
  libeth_xdp_return_frags(frm->data + frm->soff, true);

 libeth_xdp_return_va(frm->data, true);
}

static void __cold
libeth_xdp_tx_return_bulk(const struct libeth_xdp_tx_frame *bq, u32 count)
{
 for (u32 i = 0; i < count; i++) {
  const struct libeth_xdp_tx_frame *frm = &bq[i];

  if (!(frm->len_fl & LIBETH_XDP_TX_FIRST))
   continue;

  libeth_xdp_tx_return_one(frm);
 }
}

static void __cold libeth_trace_xdp_exception(const struct net_device *dev,
           const struct bpf_prog *prog,
           u32 act)
{
 trace_xdp_exception(dev, prog, act);
}

/**
 * libeth_xdp_tx_exception - handle Tx exceptions of XDP frames
 * @bq: XDP Tx frame bulk
 * @sent: number of frames sent successfully (from this bulk)
 * @flags: internal libeth_xdp flags (XSk, .ndo_xdp_xmit etc.)
 *
 * Cold helper used by __libeth_xdp_tx_flush_bulk(), do not call directly.
 * Reports XDP Tx exceptions, frees the frames that won't be sent or adjust
 * the Tx bulk to try again later.
 */

void __cold libeth_xdp_tx_exception(struct libeth_xdp_tx_bulk *bq, u32 sent,
        u32 flags)
{
 const struct libeth_xdp_tx_frame *pos = &bq->bulk[sent];
 u32 left = bq->count - sent;

 if (!(flags & LIBETH_XDP_TX_NDO))
  libeth_trace_xdp_exception(bq->dev, bq->prog, XDP_TX);

 if (!(flags & LIBETH_XDP_TX_DROP)) {
  memmove(bq->bulk, pos, left * sizeof(*bq->bulk));
  bq->count = left;

  return;
 }

 if (flags & LIBETH_XDP_TX_XSK)
  libeth_xsk_tx_return_bulk(pos, left);
 else if (!(flags & LIBETH_XDP_TX_NDO))
  libeth_xdp_tx_return_bulk(pos, left);
 else
  libeth_xdp_xmit_return_bulk(pos, left, bq->dev);

 bq->count = 0;
}
EXPORT_SYMBOL_GPL(libeth_xdp_tx_exception);

/* .ndo_xdp_xmit() implementation */

u32 __cold libeth_xdp_xmit_return_bulk(const struct libeth_xdp_tx_frame *bq,
           u32 count, const struct net_device *dev)
{
 u32 n = 0;

 for (u32 i = 0; i < count; i++) {
  const struct libeth_xdp_tx_frame *frm = &bq[i];
  dma_addr_t dma;

  if (frm->flags & LIBETH_XDP_TX_FIRST)
   dma = *libeth_xdp_xmit_frame_dma(frm->xdpf);
  else
   dma = dma_unmap_addr(frm, dma);

  dma_unmap_page(dev->dev.parent, dma, dma_unmap_len(frm, len),
          DMA_TO_DEVICE);

  /* Actual xdp_frames are freed by the core */
  n += !!(frm->flags & LIBETH_XDP_TX_FIRST);
 }

 return n;
}
EXPORT_SYMBOL_GPL(libeth_xdp_xmit_return_bulk);

/* Rx polling path */

/**
 * libeth_xdp_load_stash - recreate an &xdp_buff from libeth_xdp buffer stash
 * @dst: target &libeth_xdp_buff to initialize
 * @src: source stash
 *
 * External helper used by libeth_xdp_init_buff(), do not call directly.
 * Recreate an onstack &libeth_xdp_buff using the stash saved earlier.
 * The only field untouched (rxq) is initialized later in the
 * abovementioned function.
 */

void libeth_xdp_load_stash(struct libeth_xdp_buff *dst,
      const struct libeth_xdp_buff_stash *src)
{
 dst->data = src->data;
 dst->base.data_end = src->data + src->len;
 dst->base.data_meta = src->data;
 dst->base.data_hard_start = src->data - src->headroom;

 dst->base.frame_sz = src->frame_sz;
 dst->base.flags = src->flags;
}
EXPORT_SYMBOL_GPL(libeth_xdp_load_stash);

/**
 * libeth_xdp_save_stash - convert &xdp_buff to a libeth_xdp buffer stash
 * @dst: target &libeth_xdp_buff_stash to initialize
 * @src: source XDP buffer
 *
 * External helper used by libeth_xdp_save_buff(), do not call directly.
 * Use the fields from the passed XDP buffer to initialize the stash on the
 * queue, so that a partially received frame can be finished later during
 * the next NAPI poll.
 */

void libeth_xdp_save_stash(struct libeth_xdp_buff_stash *dst,
      const struct libeth_xdp_buff *src)
{
 dst->data = src->data;
 dst->headroom = src->data - src->base.data_hard_start;
 dst->len = src->base.data_end - src->data;

 dst->frame_sz = src->base.frame_sz;
 dst->flags = src->base.flags;

 WARN_ON_ONCE(dst->flags != src->base.flags);
}
EXPORT_SYMBOL_GPL(libeth_xdp_save_stash);

void __libeth_xdp_return_stash(struct libeth_xdp_buff_stash *stash)
{
 LIBETH_XDP_ONSTACK_BUFF(xdp);

 libeth_xdp_load_stash(xdp, stash);
 libeth_xdp_return_buff_slow(xdp);

 stash->data = NULL;
}
EXPORT_SYMBOL_GPL(__libeth_xdp_return_stash);

/**
 * libeth_xdp_return_buff_slow - free &libeth_xdp_buff
 * @xdp: buffer to free/return
 *
 * Slowpath version of libeth_xdp_return_buff() to be called on exceptions,
 * queue clean-ups etc., without unwanted inlining.
 */

void __cold libeth_xdp_return_buff_slow(struct libeth_xdp_buff *xdp)
{
 __libeth_xdp_return_buff(xdp, false);
}
EXPORT_SYMBOL_GPL(libeth_xdp_return_buff_slow);

/**
 * libeth_xdp_buff_add_frag - add frag to XDP buffer
 * @xdp: head XDP buffer
 * @fqe: Rx buffer containing the frag
 * @len: frag length reported by HW
 *
 * External helper used by libeth_xdp_process_buff(), do not call directly.
 * Frees both head and frag buffers on error.
 *
 * Return: true success, false on error (no space for a new frag).
 */

bool libeth_xdp_buff_add_frag(struct libeth_xdp_buff *xdp,
         const struct libeth_fqe *fqe,
         u32 len)
{
 netmem_ref netmem = fqe->netmem;

 if (!xdp_buff_add_frag(&xdp->base, netmem,
          fqe->offset + netmem_get_pp(netmem)->p.offset,
          len, fqe->truesize))
  goto recycle;

 return true;

recycle:
 libeth_rx_recycle_slow(netmem);
 libeth_xdp_return_buff_slow(xdp);

 return false;
}
EXPORT_SYMBOL_GPL(libeth_xdp_buff_add_frag);

/**
 * libeth_xdp_prog_exception - handle XDP prog exceptions
 * @bq: XDP Tx bulk
 * @xdp: buffer to process
 * @act: original XDP prog verdict
 * @ret: error code if redirect failed
 *
 * External helper used by __libeth_xdp_run_prog() and
 * __libeth_xsk_run_prog_slow(), do not call directly.
 * Reports invalid @act, XDP exception trace event and frees the buffer.
 *
 * Return: libeth_xdp XDP prog verdict.
 */

u32 __cold libeth_xdp_prog_exception(const struct libeth_xdp_tx_bulk *bq,
         struct libeth_xdp_buff *xdp,
         enum xdp_action act, int ret)
{
 if (act > XDP_REDIRECT)
  bpf_warn_invalid_xdp_action(bq->dev, bq->prog, act);

 libeth_trace_xdp_exception(bq->dev, bq->prog, act);

 if (xdp->base.rxq->mem.type == MEM_TYPE_XSK_BUFF_POOL)
  return libeth_xsk_prog_exception(xdp, act, ret);

 libeth_xdp_return_buff_slow(xdp);

 return LIBETH_XDP_DROP;
}
EXPORT_SYMBOL_GPL(libeth_xdp_prog_exception);

/* Tx buffer completion */

static void libeth_xdp_put_netmem_bulk(netmem_ref netmem,
           struct xdp_frame_bulk *bq)
{
 if (unlikely(bq->count == XDP_BULK_QUEUE_SIZE))
  xdp_flush_frame_bulk(bq);

 bq->q[bq->count++] = netmem;
}

/**
 * libeth_xdp_return_buff_bulk - free &xdp_buff as part of a bulk
 * @sinfo: shared info corresponding to the buffer
 * @bq: XDP frame bulk to store the buffer
 * @frags: whether the buffer has frags
 *
 * Same as xdp_return_frame_bulk(), but for &libeth_xdp_buff, speeds up Tx
 * completion of ``XDP_TX`` buffers and allows to free them in same bulks
 * with &xdp_frame buffers.
 */

void libeth_xdp_return_buff_bulk(const struct skb_shared_info *sinfo,
     struct xdp_frame_bulk *bq, bool frags)
{
 if (!frags)
  goto head;

 for (u32 i = 0; i < sinfo->nr_frags; i++)
  libeth_xdp_put_netmem_bulk(skb_frag_netmem(&sinfo->frags[i]),
        bq);

head:
 libeth_xdp_put_netmem_bulk(virt_to_netmem(sinfo), bq);
}
EXPORT_SYMBOL_GPL(libeth_xdp_return_buff_bulk);

/* Misc */

/**
 * libeth_xdp_queue_threshold - calculate XDP queue clean/refill threshold
 * @count: number of descriptors in the queue
 *
 * The threshold is the limit at which RQs start to refill (when the number of
 * empty buffers exceeds it) and SQs get cleaned up (when the number of free
 * descriptors goes below it). To speed up hotpath processing, threshold is
 * always pow-2, closest to 1/4 of the queue length.
 * Don't call it on hotpath, calculate and cache the threshold during the
 * queue initialization.
 *
 * Return: the calculated threshold.
 */

u32 libeth_xdp_queue_threshold(u32 count)
{
 u32 quarter, low, high;

 if (likely(is_power_of_2(count)))
  return count >> 2;

 quarter = DIV_ROUND_CLOSEST(count, 4);
 low = rounddown_pow_of_two(quarter);
 high = roundup_pow_of_two(quarter);

 return high - quarter <= quarter - low ? high : low;
}
EXPORT_SYMBOL_GPL(libeth_xdp_queue_threshold);

/**
 * __libeth_xdp_set_features - set XDP features for netdev
 * @dev: &net_device to configure
 * @xmo: XDP metadata ops (Rx hints)
 * @zc_segs: maximum number of S/G frags the HW can transmit
 * @tmo: XSk Tx metadata ops (Tx hints)
 *
 * Set all the features libeth_xdp supports. Only the first argument is
 * necessary; without the third one (zero), XSk support won't be advertised.
 * Use the non-underscored versions in drivers instead.
 */

void __libeth_xdp_set_features(struct net_device *dev,
          const struct xdp_metadata_ops *xmo,
          u32 zc_segs,
          const struct xsk_tx_metadata_ops *tmo)
{
 xdp_set_features_flag(dev,
         NETDEV_XDP_ACT_BASIC |
         NETDEV_XDP_ACT_REDIRECT |
         NETDEV_XDP_ACT_NDO_XMIT |
         (zc_segs ? NETDEV_XDP_ACT_XSK_ZEROCOPY : 0) |
         NETDEV_XDP_ACT_RX_SG |
         NETDEV_XDP_ACT_NDO_XMIT_SG);
 dev->xdp_metadata_ops = xmo;

 tmo = tmo == libeth_xsktmo ? &libeth_xsktmo_slow : tmo;

 dev->xdp_zc_max_segs = zc_segs ? : 1;
 dev->xsk_tx_metadata_ops = zc_segs ? tmo : NULL;
}
EXPORT_SYMBOL_GPL(__libeth_xdp_set_features);

/**
 * libeth_xdp_set_redirect - toggle the XDP redirect feature
 * @dev: &net_device to configure
 * @enable: whether XDP is enabled
 *
 * Use this when XDPSQs are not always available to dynamically enable
 * and disable redirect feature.
 */

void libeth_xdp_set_redirect(struct net_device *dev, bool enable)
{
 if (enable)
  xdp_features_set_redirect_target(dev, true);
 else
  xdp_features_clear_redirect_target(dev);
}
EXPORT_SYMBOL_GPL(libeth_xdp_set_redirect);

/* Module */

static const struct libeth_xdp_ops xdp_ops __initconst = {
 .bulk = libeth_xdp_return_buff_bulk,
 .xsk = libeth_xsk_buff_free_slow,
};

static int __init libeth_xdp_module_init(void)
{
 libeth_attach_xdp(&xdp_ops);

 return 0;
}
module_init(libeth_xdp_module_init);

static void __exit libeth_xdp_module_exit(void)
{
 libeth_detach_xdp();
}
module_exit(libeth_xdp_module_exit);

MODULE_DESCRIPTION("Common Ethernet library - XDP infra");
MODULE_IMPORT_NS("LIBETH");
MODULE_LICENSE("GPL");

Messung V0.5 in Prozent
C=95 H=94 G=94

[Dauer der Verarbeitung: 0.20 Sekunden, vorverarbeitet 2026-10-12]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1127926
#Domains=1947682