Commit 09847108 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Tony Nguyen says:

====================
Intel Wired LAN Driver Updates 2025-09-16 (ice, i40e, ixgbe, igc)

For ice:
Jake resolves leaking pages with multi-buffer frames when a 0-sized
descriptor is encountered.

For i40e:
Maciej removes a redundant, and incorrect, memory barrier.

For ixgbe:
Jedrzej adjusts lifespan of ACI lock to ensure uses are while it is
valid.

For igc:
Kohei Enju does not fail probe on LED setup failure which resolves a
kernel panic in the cleanup path, if we were to fail.

* '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue:
  igc: don't fail igc_probe() on LED setup error
  ixgbe: destroy aci.lock later within ixgbe_remove path
  ixgbe: initialize aci.lock before it's used
  i40e: remove redundant memory barrier when cleaning Tx descs
  ice: fix Rx page leak on multi-buffer frames
====================

Link: https://patch.msgid.link/20250916212801.2818440-1-anthony.l.nguyen@intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 934da21f 528eb4e1
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -948,9 +948,6 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
		if (!eop_desc)
			break;

		/* prevent any other reads prior to eop_desc */
		smp_rmb();

		i40e_trace(clean_tx_irq, tx_ring, tx_desc, tx_buf);
		/* we have caught up to head, no work left to do */
		if (tx_head == tx_desc)
+34 −46
Original line number Diff line number Diff line
@@ -894,10 +894,6 @@ ice_add_xdp_frag(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
	__skb_fill_page_desc_noacc(sinfo, sinfo->nr_frags++, rx_buf->page,
				   rx_buf->page_offset, size);
	sinfo->xdp_frags_size += size;
	/* remember frag count before XDP prog execution; bpf_xdp_adjust_tail()
	 * can pop off frags but driver has to handle it on its own
	 */
	rx_ring->nr_frags = sinfo->nr_frags;

	if (page_is_pfmemalloc(rx_buf->page))
		xdp_buff_set_frag_pfmemalloc(xdp);
@@ -968,20 +964,20 @@ ice_get_rx_buf(struct ice_rx_ring *rx_ring, const unsigned int size,
/**
 * ice_get_pgcnts - grab page_count() for gathered fragments
 * @rx_ring: Rx descriptor ring to store the page counts on
 * @ntc: the next to clean element (not included in this frame!)
 *
 * This function is intended to be called right before running XDP
 * program so that the page recycling mechanism will be able to take
 * a correct decision regarding underlying pages; this is done in such
 * way as XDP program can change the refcount of page
 */
static void ice_get_pgcnts(struct ice_rx_ring *rx_ring)
static void ice_get_pgcnts(struct ice_rx_ring *rx_ring, unsigned int ntc)
{
	u32 nr_frags = rx_ring->nr_frags + 1;
	u32 idx = rx_ring->first_desc;
	struct ice_rx_buf *rx_buf;
	u32 cnt = rx_ring->count;

	for (int i = 0; i < nr_frags; i++) {
	while (idx != ntc) {
		rx_buf = &rx_ring->rx_buf[idx];
		rx_buf->pgcnt = page_count(rx_buf->page);

@@ -1154,62 +1150,51 @@ ice_put_rx_buf(struct ice_rx_ring *rx_ring, struct ice_rx_buf *rx_buf)
}

/**
 * ice_put_rx_mbuf - ice_put_rx_buf() caller, for all frame frags
 * ice_put_rx_mbuf - ice_put_rx_buf() caller, for all buffers in frame
 * @rx_ring: Rx ring with all the auxiliary data
 * @xdp: XDP buffer carrying linear + frags part
 * @xdp_xmit: XDP_TX/XDP_REDIRECT verdict storage
 * @ntc: a current next_to_clean value to be stored at rx_ring
 * @ntc: the next to clean element (not included in this frame!)
 * @verdict: return code from XDP program execution
 *
 * Walk through gathered fragments and satisfy internal page
 * recycle mechanism; we take here an action related to verdict
 * returned by XDP program;
 * Called after XDP program is completed, or on error with verdict set to
 * ICE_XDP_CONSUMED.
 *
 * Walk through buffers from first_desc to the end of the frame, releasing
 * buffers and satisfying internal page recycle mechanism. The action depends
 * on verdict from XDP program.
 */
static void ice_put_rx_mbuf(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
			    u32 *xdp_xmit, u32 ntc, u32 verdict)
			    u32 ntc, u32 verdict)
{
	u32 nr_frags = rx_ring->nr_frags + 1;
	u32 idx = rx_ring->first_desc;
	u32 cnt = rx_ring->count;
	u32 post_xdp_frags = 1;
	struct ice_rx_buf *buf;
	int i;
	u32 xdp_frags = 0;
	int i = 0;

	if (unlikely(xdp_buff_has_frags(xdp)))
		post_xdp_frags += xdp_get_shared_info_from_buff(xdp)->nr_frags;
		xdp_frags = xdp_get_shared_info_from_buff(xdp)->nr_frags;

	for (i = 0; i < post_xdp_frags; i++) {
	while (idx != ntc) {
		buf = &rx_ring->rx_buf[idx];
		if (++idx == cnt)
			idx = 0;

		if (verdict & (ICE_XDP_TX | ICE_XDP_REDIR)) {
		/* An XDP program could release fragments from the end of the
		 * buffer. For these, we need to keep the pagecnt_bias as-is.
		 * To do this, only adjust pagecnt_bias for fragments up to
		 * the total remaining after the XDP program has run.
		 */
		if (verdict != ICE_XDP_CONSUMED)
			ice_rx_buf_adjust_pg_offset(buf, xdp->frame_sz);
			*xdp_xmit |= verdict;
		} else if (verdict & ICE_XDP_CONSUMED) {
		else if (i++ <= xdp_frags)
			buf->pagecnt_bias++;
		} else if (verdict == ICE_XDP_PASS) {
			ice_rx_buf_adjust_pg_offset(buf, xdp->frame_sz);
		}

		ice_put_rx_buf(rx_ring, buf);

		if (++idx == cnt)
			idx = 0;
	}
	/* handle buffers that represented frags released by XDP prog;
	 * for these we keep pagecnt_bias as-is; refcount from struct page
	 * has been decremented within XDP prog and we do not have to increase
	 * the biased refcnt
	 */
	for (; i < nr_frags; i++) {
		buf = &rx_ring->rx_buf[idx];
		ice_put_rx_buf(rx_ring, buf);
		if (++idx == cnt)
			idx = 0;
	}

	xdp->data = NULL;
	rx_ring->first_desc = ntc;
	rx_ring->nr_frags = 0;
}

/**
@@ -1317,6 +1302,10 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
		/* retrieve a buffer from the ring */
		rx_buf = ice_get_rx_buf(rx_ring, size, ntc);

		/* Increment ntc before calls to ice_put_rx_mbuf() */
		if (++ntc == cnt)
			ntc = 0;

		if (!xdp->data) {
			void *hard_start;

@@ -1325,24 +1314,23 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
			xdp_prepare_buff(xdp, hard_start, offset, size, !!offset);
			xdp_buff_clear_frags_flag(xdp);
		} else if (ice_add_xdp_frag(rx_ring, xdp, rx_buf, size)) {
			ice_put_rx_mbuf(rx_ring, xdp, NULL, ntc, ICE_XDP_CONSUMED);
			ice_put_rx_mbuf(rx_ring, xdp, ntc, ICE_XDP_CONSUMED);
			break;
		}
		if (++ntc == cnt)
			ntc = 0;

		/* skip if it is NOP desc */
		if (ice_is_non_eop(rx_ring, rx_desc))
			continue;

		ice_get_pgcnts(rx_ring);
		ice_get_pgcnts(rx_ring, ntc);
		xdp_verdict = ice_run_xdp(rx_ring, xdp, xdp_prog, xdp_ring, rx_desc);
		if (xdp_verdict == ICE_XDP_PASS)
			goto construct_skb;
		total_rx_bytes += xdp_get_buff_len(xdp);
		total_rx_pkts++;

		ice_put_rx_mbuf(rx_ring, xdp, &xdp_xmit, ntc, xdp_verdict);
		ice_put_rx_mbuf(rx_ring, xdp, ntc, xdp_verdict);
		xdp_xmit |= xdp_verdict & (ICE_XDP_TX | ICE_XDP_REDIR);

		continue;
construct_skb:
@@ -1355,7 +1343,7 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
			rx_ring->ring_stats->rx_stats.alloc_buf_failed++;
			xdp_verdict = ICE_XDP_CONSUMED;
		}
		ice_put_rx_mbuf(rx_ring, xdp, &xdp_xmit, ntc, xdp_verdict);
		ice_put_rx_mbuf(rx_ring, xdp, ntc, xdp_verdict);

		if (!skb)
			break;
+0 −1
Original line number Diff line number Diff line
@@ -358,7 +358,6 @@ struct ice_rx_ring {
	struct ice_tx_ring *xdp_ring;
	struct ice_rx_ring *next;	/* pointer to next ring in q_vector */
	struct xsk_buff_pool *xsk_pool;
	u32 nr_frags;
	u16 max_frame;
	u16 rx_buf_len;
	dma_addr_t dma;			/* physical address of ring */
+1 −0
Original line number Diff line number Diff line
@@ -345,6 +345,7 @@ struct igc_adapter {
	/* LEDs */
	struct mutex led_mutex;
	struct igc_led_classdev *leds;
	bool leds_available;
};

void igc_up(struct igc_adapter *adapter);
+9 −3
Original line number Diff line number Diff line
@@ -7335,8 +7335,14 @@ static int igc_probe(struct pci_dev *pdev,

	if (IS_ENABLED(CONFIG_IGC_LEDS)) {
		err = igc_led_setup(adapter);
		if (err)
			goto err_register;
		if (err) {
			netdev_warn_once(netdev,
					 "LED init failed (%d); continuing without LED support\n",
					 err);
			adapter->leds_available = false;
		} else {
			adapter->leds_available = true;
		}
	}

	return 0;
@@ -7392,7 +7398,7 @@ static void igc_remove(struct pci_dev *pdev)
	cancel_work_sync(&adapter->watchdog_task);
	hrtimer_cancel(&adapter->hrtimer);

	if (IS_ENABLED(CONFIG_IGC_LEDS))
	if (IS_ENABLED(CONFIG_IGC_LEDS) && adapter->leds_available)
		igc_led_free(adapter);

	/* Release control of h/w to f/w.  If f/w is AMT enabled, this
Loading