summaryrefslogtreecommitdiff
path: root/net/xdp
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2026-08-10 09:27:22 +0200
committerArnd Bergmann <arnd@arndb.de>2026-08-10 09:27:22 +0200
commiteedb69c7b457dde8f6caf114a9bcd8b6fb1bdb91 (patch)
tree0083a5116cde511ac32312bf3ad0871e7051a211 /net/xdp
parentb9a43aeabca0162b4a405c1c093e0764a549ba77 (diff)
parent37397cf956dca69c1a0764b1a50994047d5e5367 (diff)
downloadlinux-next-eedb69c7b457dde8f6caf114a9bcd8b6fb1bdb91.tar.gz
linux-next-eedb69c7b457dde8f6caf114a9bcd8b6fb1bdb91.zip
Merge branch 'arm/fixes' into for-next
* arm/fixes: (953 commits) Linux 7.2-rc7 ring-buffer: Fix crash passing ERR_PTR to kthread_stop() ring-buffer: Initialise reader page order in rb_allocate_cpu_buffer() ring-buffer: Prevent subbuf order change when resizing is disabled ring-buffer: Prevent resizing of persistent ring buffer ftrace: Fix off-by-one fentry site disable in ftrace_free_mem() ring-buffer: Use current_context for safe per-CPU buffer swap ftrace: Drop extra comma in trace_buffered_event_enable ftrace: Protect direct_functions in update_ftrace_direct_mod ftrace: Protect direct_functions in update_ftrace_direct_del ftrace: Protect direct_functions in ftrace_find_rec_direct eventfs: Use children field for rcu head and add memory barriers eventfs: Fix use-after-free in eventfs_remove_rec() fbdev: bitblit: bound-check glyph index in bit_cursor() fbdev: Fix out-of-bounds access when rotating console after font resize fbdev: core: Fix pointer desynchronization in fb_io_read() fbdev: serialize mode sysfs access with lock_fb_info() fbdev: clear fb_info->mode before deleting a videomode fbdev: bound mode sysfs output to the sysfs buffer powerpc/pseries: lparcfg - fix kbuf[] underflow ...
Diffstat (limited to 'net/xdp')
-rw-r--r--net/xdp/xdp_umem.c2
-rw-r--r--net/xdp/xsk.c266
-rw-r--r--net/xdp/xsk_buff_pool.c19
-rw-r--r--net/xdp/xsk_queue.h65
4 files changed, 292 insertions, 60 deletions
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 58da2f4f4397..cd8643360eb3 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -210,6 +210,8 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
if (mr->flags & XDP_UMEM_TX_METADATA_LEN) {
if (mr->tx_metadata_len >= 256 || mr->tx_metadata_len % 8)
return -EINVAL;
+ if (mr->tx_metadata_len < 16)
+ return -EINVAL;
umem->tx_metadata_len = mr->tx_metadata_len;
}
diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
index b970f30ea9b9..7855ee09c4b6 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -499,6 +499,23 @@ void __xsk_map_flush(struct list_head *flush_list)
void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries)
{
+ u32 reclaim_descs = READ_ONCE(pool->reclaim_descs);
+
+ if (unlikely(reclaim_descs)) {
+ u32 pending_descs = READ_ONCE(pool->tx_zc_pending_descs);
+
+ if (nb_entries < pending_descs) {
+ WRITE_ONCE(pool->tx_zc_pending_descs,
+ pending_descs - nb_entries);
+ xskq_prod_submit_n(pool->cq, nb_entries);
+ return;
+ }
+
+ WRITE_ONCE(pool->tx_zc_pending_descs, 0);
+ nb_entries += reclaim_descs;
+ WRITE_ONCE(pool->reclaim_descs, 0);
+ }
+
xskq_prod_submit_n(pool->cq, nb_entries);
}
EXPORT_SYMBOL(xsk_tx_completed);
@@ -574,24 +591,157 @@ static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, u32 max_entr
return nb_pkts;
}
+static void xsk_tx_commit_batch(struct xsk_buff_pool *pool,
+ struct xsk_tx_batch *batch)
+{
+ u32 nb_descs = xsk_tx_batch_cq_descs(batch);
+ u32 cq_cached_prod;
+
+ if (!nb_descs)
+ return;
+
+ cq_cached_prod = pool->cq->cached_prod;
+ xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_descs);
+
+ if (unlikely(batch->reclaim_descs)) {
+ u32 cq_pending_descs;
+
+ /* CQ is positional. Descriptors already written but not
+ * submitted must complete before any reclaim-only descriptors
+ * appended below.
+ */
+ cq_pending_descs = cq_cached_prod - xskq_get_prod(pool->cq);
+
+ WRITE_ONCE(pool->tx_zc_pending_descs,
+ batch->tx_descs + cq_pending_descs);
+ WRITE_ONCE(pool->reclaim_descs, batch->reclaim_descs);
+ if (unlikely(!pool->tx_zc_pending_descs))
+ xsk_tx_completed(pool, 0);
+ }
+}
+
+static struct xsk_tx_batch
+__xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_sock *xs,
+ struct xdp_desc *descs, u32 max_descs)
+{
+ struct xsk_tx_batch batch = {};
+ u32 entries;
+
+ entries = xskq_cons_nb_entries(xs->tx, max_descs);
+ if (!entries)
+ return batch;
+
+ batch = xskq_cons_read_desc_batch(xs, pool, descs, max_descs);
+ if (!xsk_tx_batch_cq_descs(&batch)) {
+ xs->tx->queue_empty_descs++;
+ } else {
+ __xskq_cons_release(xs->tx);
+ xs->sk.sk_write_space(&xs->sk);
+ }
+ return batch;
+}
+
+static struct xsk_tx_batch
+xsk_tx_peek_release_shared_desc_batch(struct xsk_buff_pool *pool, u32 max_descs)
+{
+ u32 cq_descs_before, cq_descs_after;
+ struct xsk_tx_batch sum_batch = {};
+ bool budget_exhausted;
+ u32 per_socket_budget;
+ struct xdp_sock *xs;
+
+ /* The fairness quota must allow one maximum-sized valid packet. */
+ per_socket_budget = max_t(u32, MAX_PER_SOCKET_BUDGET,
+ pool->xdp_zc_max_segs);
+
+again:
+ budget_exhausted = false;
+ cq_descs_before = xsk_tx_batch_cq_descs(&sum_batch);
+ list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list) {
+ u32 budget, budget_left, offset, remaining, used;
+ struct xsk_tx_batch curr_batch;
+
+ /* Once reclaim-only descriptors have been appended to the CQ
+ * address area, do not append driver-visible Tx descriptors
+ * from another socket after them. xsk_tx_completed() relies on
+ * all driver-visible descriptors preceding all reclaim-only
+ * descriptors in CQ order.
+ */
+ if (sum_batch.reclaim_descs)
+ break;
+
+ /* be gentle when playing with pool->tx_descs */
+ offset = xsk_tx_batch_cq_descs(&sum_batch);
+ if (offset >= max_descs)
+ break;
+
+ if (xs->tx_budget_spent >= per_socket_budget) {
+ if (xskq_cons_nb_entries(xs->tx, 1))
+ budget_exhausted = true;
+ continue;
+ }
+
+ budget_left = per_socket_budget - xs->tx_budget_spent;
+ remaining = max_descs - offset;
+ budget = min(remaining, budget_left);
+
+ curr_batch = __xsk_tx_peek_release_desc_batch(pool, xs,
+ pool->tx_descs + offset,
+ budget);
+ used = xsk_tx_batch_cq_descs(&curr_batch);
+ if (!used) {
+ if (curr_batch.budget_limited && budget_left < remaining)
+ budget_exhausted = true;
+ continue;
+ }
+
+ xs->tx_budget_spent += used;
+ sum_batch.tx_descs += curr_batch.tx_descs;
+ sum_batch.reclaim_descs = curr_batch.reclaim_descs;
+ }
+
+ cq_descs_after = xsk_tx_batch_cq_descs(&sum_batch);
+
+ if (sum_batch.reclaim_descs || cq_descs_after >= max_descs)
+ return sum_batch;
+
+ /* Continue filling the batch while this pass made progress */
+ if (cq_descs_before != cq_descs_after)
+ goto again;
+
+ if (!budget_exhausted)
+ return sum_batch;
+
+ list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list)
+ xs->tx_budget_spent = 0;
+ goto again;
+}
+
u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
{
+ struct xsk_tx_batch batch = {};
struct xdp_sock *xs;
+ bool umem_shared;
rcu_read_lock();
- if (!list_is_singular(&pool->xsk_tx_list)) {
- /* Fallback to the non-batched version */
- rcu_read_unlock();
- return xsk_tx_peek_release_fallback(pool, nb_pkts);
- }
+ if (unlikely(READ_ONCE(pool->reclaim_descs)))
+ goto out;
- xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, tx_list);
- if (!xs) {
- nb_pkts = 0;
+ xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock,
+ tx_list);
+ if (!xs)
goto out;
- }
- nb_pkts = xskq_cons_nb_entries(xs->tx, nb_pkts);
+ nb_pkts = min(nb_pkts, pool->tx_descs_nentries);
+ if (!nb_pkts)
+ goto out;
+
+ umem_shared = !list_is_singular(&pool->xsk_tx_list);
+
+ if (umem_shared && !(pool->umem->flags & XDP_UMEM_SG_FLAG)) {
+ rcu_read_unlock();
+ return xsk_tx_peek_release_fallback(pool, nb_pkts);
+ }
/* This is the backpressure mechanism for the Tx path. Try to
* reserve space in the completion queue for all packets, but
@@ -603,19 +753,16 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts)
if (!nb_pkts)
goto out;
- nb_pkts = xskq_cons_read_desc_batch(xs->tx, pool, nb_pkts);
- if (!nb_pkts) {
- xs->tx->queue_empty_descs++;
- goto out;
- }
-
- __xskq_cons_release(xs->tx);
- xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_pkts);
- xs->sk.sk_write_space(&xs->sk);
+ batch = umem_shared ?
+ xsk_tx_peek_release_shared_desc_batch(pool, nb_pkts) :
+ __xsk_tx_peek_release_desc_batch(pool, xs,
+ pool->tx_descs,
+ nb_pkts);
+ xsk_tx_commit_batch(pool, &batch);
out:
rcu_read_unlock();
- return nb_pkts;
+ return batch.tx_descs;
}
EXPORT_SYMBOL(xsk_tx_peek_release_desc_batch);
@@ -737,6 +884,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool,
spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
}
+static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool,
+ struct xdp_desc *desc)
+{
+ unsigned long flags;
+ u32 idx;
+
+ spin_lock_irqsave(&pool->cq_prod_lock, flags);
+ idx = xskq_get_prod(pool->cq);
+ xskq_prod_write_addr(pool->cq, idx, desc->addr);
+ xskq_prod_submit_n(pool->cq, 1);
+ spin_unlock_irqrestore(&pool->cq_prod_lock, flags);
+}
+
static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n)
{
spin_lock(&pool->cq->cq_cached_prod_lock);
@@ -794,8 +954,11 @@ static void xsk_consume_skb(struct sk_buff *skb)
static void xsk_drop_skb(struct sk_buff *skb)
{
- xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb);
- xsk_consume_skb(skb);
+ struct xdp_sock *xs = xdp_sk(skb->sk);
+
+ xs->tx->invalid_descs += xsk_get_num_desc(skb);
+ consume_skb(skb);
+ xs->skb = NULL;
}
static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
@@ -804,15 +967,16 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
{
struct xsk_tx_metadata *meta = NULL;
u16 csum_start, csum_offset;
+ u64 flags;
if (unlikely(pool->tx_metadata_len == 0))
return -EINVAL;
meta = buffer - pool->tx_metadata_len;
- if (unlikely(!xsk_buff_valid_tx_metadata(meta)))
+ if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags)))
return -EINVAL;
- if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) {
+ if (flags & XDP_TXMD_FLAGS_CHECKSUM) {
csum_start = READ_ONCE(meta->request.csum_start);
csum_offset = READ_ONCE(meta->request.csum_offset);
@@ -833,8 +997,10 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
}
}
- if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
- skb->skb_mstamp_ns = meta->request.launch_time;
+ if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
+ skb->skb_mstamp_ns = READ_ONCE(meta->request.launch_time);
+ if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP))
+ meta = NULL;
xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta);
return 0;
@@ -877,7 +1043,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
return ERR_PTR(-ENOMEM);
/* in case of -EOVERFLOW that could happen below,
- * xsk_consume_skb() will release this node as whole skb
+ * xsk_drop_skb() will release this node as whole skb
* would be dropped, which implies freeing all list elements
*/
xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
@@ -969,6 +1135,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
goto free_err;
}
+ xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
+
if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) {
err = -EOVERFLOW;
goto free_err;
@@ -986,8 +1154,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE);
refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc);
-
- xsk_addr->addrs[xsk_addr->num_descs] = desc->addr;
}
}
@@ -1025,13 +1191,14 @@ free_err:
static int __xsk_generic_xmit(struct sock *sk)
{
struct xdp_sock *xs = xdp_sk(sk);
- bool sent_frame = false;
struct xdp_desc desc;
struct sk_buff *skb;
+ u32 cached_cons;
u32 max_batch;
int err = 0;
mutex_lock(&xs->mutex);
+ cached_cons = xs->tx->cached_cons;
/* Since we dropped the RCU read lock, the socket state might have changed. */
if (unlikely(!xsk_is_bound(xs))) {
@@ -1060,11 +1227,21 @@ static int __xsk_generic_xmit(struct sock *sk)
goto out;
}
+ if (unlikely(xs->drain_cont)) {
+ xsk_cq_submit_addr_single_locked(xs->pool, &desc);
+ xs->tx->invalid_descs++;
+ xskq_cons_release(xs->tx);
+ xs->drain_cont = xp_mb_desc(&desc);
+ continue;
+ }
+
skb = xsk_build_skb(xs, &desc);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
if (err != -EOVERFLOW)
goto out;
+ if (xp_mb_desc(&desc))
+ xs->drain_cont = true;
err = 0;
continue;
}
@@ -1093,18 +1270,33 @@ static int __xsk_generic_xmit(struct sock *sk)
goto out;
}
- sent_frame = true;
xs->skb = NULL;
}
if (xskq_has_descs(xs->tx)) {
+ bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc);
+
+ err = xsk_cq_reserve_locked(xs->pool);
+ if (err) {
+ xs->tx->invalid_descs--;
+ if (xs->skb)
+ xsk_drop_skb(xs->skb);
+ xs->drain_cont = drain;
+ err = -EAGAIN;
+ goto out;
+ }
+
if (xs->skb)
xsk_drop_skb(xs->skb);
+
+ xsk_cq_submit_addr_single_locked(xs->pool, &desc);
+
xskq_cons_release(xs->tx);
+ xs->drain_cont = xp_mb_desc(&desc);
}
out:
- if (sent_frame)
+ if (xs->tx->cached_cons != cached_cons)
__xsk_tx_release(xs);
mutex_unlock(&xs->mutex);
@@ -1483,7 +1675,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
* and/or device.
*/
xs->pool = xp_create_and_assign_umem(xs,
- umem_xs->umem);
+ umem_xs->umem,
+ dev->xdp_zc_max_segs);
if (!xs->pool) {
err = -ENOMEM;
sockfd_put(sock);
@@ -1515,7 +1708,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
* utilizes
*/
if (xs->tx && !xs->pool->tx_descs) {
- err = xp_alloc_tx_descs(xs->pool, xs);
+ err = xp_alloc_tx_descs(xs->pool, xs,
+ dev->xdp_zc_max_segs);
if (err) {
xp_put_pool(xs->pool);
xs->pool = NULL;
@@ -1533,7 +1727,9 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr
goto out_unlock;
} else {
/* This xsk has its own umem. */
- xs->pool = xp_create_and_assign_umem(xs, xs->umem);
+ xs->pool = xp_create_and_assign_umem(xs, xs->umem,
+ dev->xdp_zc_max_segs);
+
if (!xs->pool) {
err = -ENOMEM;
goto out_unlock;
diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
index 1f28a9641571..78c14f106395 100644
--- a/net/xdp/xsk_buff_pool.c
+++ b/net/xdp/xsk_buff_pool.c
@@ -42,17 +42,22 @@ void xp_destroy(struct xsk_buff_pool *pool)
kvfree(pool);
}
-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs)
+int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs,
+ u32 max_segs)
{
- pool->tx_descs = kvzalloc_objs(*pool->tx_descs, xs->tx->nentries);
+ u32 nentries = max(xs->tx->nentries, max_segs);
+
+ pool->tx_descs = kvzalloc_objs(*pool->tx_descs, nentries);
if (!pool->tx_descs)
return -ENOMEM;
+ pool->tx_descs_nentries = nentries;
return 0;
}
struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
- struct xdp_umem *umem)
+ struct xdp_umem *umem,
+ u32 max_segs)
{
bool unaligned = umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG;
struct xsk_buff_pool *pool;
@@ -69,7 +74,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
goto out;
if (xs->tx)
- if (xp_alloc_tx_descs(pool, xs))
+ if (xp_alloc_tx_descs(pool, xs, max_segs))
goto out;
pool->chunk_mask = ~((u64)umem->chunk_size - 1);
@@ -760,11 +765,11 @@ EXPORT_SYMBOL(xp_raw_get_dma);
* @addr: desc address (from userspace)
*
* Helper for getting desc's DMA address and metadata pointer, if present.
- * Saves one call on hotpath, double calculation of the actual address,
- * and inline checks for metadata presence and sanity.
+ * Saves one call on hotpath and double calculation of the actual address.
+ * Metadata is validated later by xsk_tx_metadata_request().
*
* Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata
- * pointer, if it is present and valid (initialized to %NULL otherwise).
+ * pointer, if it is present (initialized to %NULL otherwise).
*/
struct xdp_desc_ctx xp_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
{
diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h
index 3e3fbb73d23e..1bc42c8902f4 100644
--- a/net/xdp/xsk_queue.h
+++ b/net/xdp/xsk_queue.h
@@ -58,6 +58,17 @@ struct parsed_desc {
u32 valid;
};
+struct xsk_tx_batch {
+ u32 tx_descs;
+ u32 reclaim_descs;
+ bool budget_limited;
+};
+
+static inline u32 xsk_tx_batch_cq_descs(const struct xsk_tx_batch *batch)
+{
+ return batch->tx_descs + batch->reclaim_descs;
+}
+
/* The structure of the shared state of the rings are a simple
* circular buffer, as outlined in
* Documentation/core-api/circular-buffers.rst. For the Rx and
@@ -263,17 +274,18 @@ static inline void parse_desc(struct xsk_queue *q, struct xsk_buff_pool *pool,
parsed->mb = xp_mb_desc(desc);
}
-static inline
-u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
- u32 max)
+static inline struct xsk_tx_batch
+xskq_cons_read_desc_batch(struct xdp_sock *xs, struct xsk_buff_pool *pool,
+ struct xdp_desc *descs, u32 max)
{
- u32 cached_cons = q->cached_cons, nb_entries = 0;
- struct xdp_desc *descs = pool->tx_descs;
- u32 total_descs = 0, nr_frags = 0;
+ bool drain = READ_ONCE(xs->drain_cont);
+ u32 cached_cons, nb_entries = 0;
+ struct xsk_tx_batch batch = {};
+ struct xsk_queue *q = xs->tx;
+ u32 nr_frags = 0;
+
+ cached_cons = q->cached_cons;
- /* track first entry, if stumble upon *any* invalid descriptor, rewind
- * current packet that consists of frags and stop the processing
- */
while (cached_cons != q->cached_prod && nb_entries < max) {
struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring;
u32 idx = cached_cons & q->ring_mask;
@@ -283,25 +295,42 @@ u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool,
cached_cons++;
parse_desc(q, pool, &descs[nb_entries], &parsed);
if (unlikely(!parsed.valid))
- break;
+ drain = true;
+
+ nr_frags++;
+ nb_entries++;
if (likely(!parsed.mb)) {
- total_descs += (nr_frags + 1);
- nr_frags = 0;
- } else {
- nr_frags++;
- if (nr_frags == pool->xdp_zc_max_segs) {
+ if (unlikely(drain)) {
+ batch.reclaim_descs = nr_frags;
+ WRITE_ONCE(xs->drain_cont, false);
nr_frags = 0;
break;
}
+
+ batch.tx_descs += nr_frags;
+ nr_frags = 0;
+ continue;
+ }
+
+ if (nr_frags == pool->xdp_zc_max_segs)
+ drain = true;
+ }
+
+ if (nr_frags) {
+ if (drain) {
+ batch.reclaim_descs = nr_frags;
+ WRITE_ONCE(xs->drain_cont, true);
+ } else {
+ if (nb_entries == max)
+ batch.budget_limited = true;
+ cached_cons -= nr_frags;
}
- nb_entries++;
}
- cached_cons -= nr_frags;
/* Release valid plus any invalid entries */
xskq_cons_release_n(q, cached_cons - q->cached_cons);
- return total_descs;
+ return batch;
}
/* Functions for consumers */