Merge branch '200GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue

Tony Nguyen says:

====================
Intel Wired LAN Driver Updates 2026-07-28 (idpf, ice, igc, igbvf, e1000)

Michael Bommarito adds bounds checking to ensure interrupt vector array
stays in-bounds on idpf.

Josh adjusts minimum value for Tx ring descriptors to prevent Tx
timeouts in flow based scheduling mode in idpf.

Yuho Choi frees IRQ name in error path to prevent memory leak for idpf.

Aaron Ma adds a wait for reset completion before returning from resume
on ice driver.

Dawid completely disables and clears VF interrupts during reset on ice.

Dawei Feng adjusts error path for ice loopback test setup and e1000 probe
to prevent memory leaks.

Przemek ignores, expected, -EBUSY errors that can occur during reset
and cause disabling of DPLL on ice.

David Carlier removes napi_synchronize() during igc_down for igc.

Matt Vollrath removes incorrect decrement of count which could cause
leaking due to off-by-one issue.

* '200GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue:
  e1000: fix memory leak in e1000_probe()
  igbvf: Fix leak in TX DMA error cleanup
  igc: remove napi_synchronize() in igc_down()
  ice: suppress DPLL errors during reset recovery
  ice: fix memory leak in ice_lbtest_prepare_rings()
  ice: fix VF interrupts cleanup
  ice: wait for reset completion in ice_resume()
  idpf: Fix mailbox IRQ name leak on request failure
  idpf: adjust TxQ ring count minimum
  idpf: bound interrupt-vector register fill to the allocated array
====================

Link: https://patch.msgid.link/20260728210909.3042004-1-anthony.l.nguyen@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-07-29 17:25:41 -07:00
16 changed files with 87 additions and 28 deletions

View File

@@ -1222,11 +1222,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (hw->flash_address)
iounmap(hw->flash_address);
err_mdio_ioremap:
kfree(adapter->tx_ring);
kfree(adapter->rx_ring);
err_dma:
err_sw_init:
err_mdio_ioremap:
iounmap(hw->ce4100_gbe_mdio_base_virt);
iounmap(hw->hw_addr);
err_ioremap:

View File

@@ -793,7 +793,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
ret,
libie_aq_str(pf->hw.adminq.sq_last_status),
pin_type_name[pin_type], pin->idx);
else
else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
dev_err_ratelimited(ice_pf_to_dev(pf),
"err:%d %s failed to update %s pin:%u\n",
ret,
@@ -3024,7 +3024,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf,
*phase_offset_pins_updated = 0;
ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas,
ARRAY_SIZE(meas));
if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) {
if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN ||
pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) {
return 0;
} else if (ret) {
dev_err(ice_pf_to_dev(pf),
@@ -3086,10 +3087,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
d->dpll_idx, d->prev_input_idx, d->input_idx,
d->dpll_state, d->prev_dpll_state, d->mode);
if (ret) {
dev_err(ice_pf_to_dev(pf),
"update dpll=%d state failed, ret=%d %s\n",
d->dpll_idx, ret,
libie_aq_str(pf->hw.adminq.sq_last_status));
/* EBUSY is expected during reset recovery, don't log error */
if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
dev_err(ice_pf_to_dev(pf),
"update dpll=%d state failed, ret=%d %s\n",
d->dpll_idx, ret,
libie_aq_str(pf->hw.adminq.sq_last_status));
return ret;
}
if (init) {
@@ -3158,7 +3161,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work)
d->periodic_counter % dp->phase_offset_monitor_period == 0)
ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf);
if (ret) {
d->cgu_state_acq_err_num++;
/* EBUSY is expected during reset recovery */
if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY)
d->cgu_state_acq_err_num++;
/* stop rescheduling this worker */
if (d->cgu_state_acq_err_num >
ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {

View File

@@ -1069,18 +1069,18 @@ static int ice_lbtest_prepare_rings(struct ice_vsi *vsi)
status = ice_vsi_cfg_lan(vsi);
if (status)
goto err_setup_rx_ring;
goto err_cfg_lan;
status = ice_vsi_start_all_rx_rings(vsi);
if (status)
goto err_start_rx_ring;
goto err_cfg_lan;
return 0;
err_start_rx_ring:
ice_vsi_free_rx_rings(vsi);
err_setup_rx_ring:
err_cfg_lan:
ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0);
err_setup_rx_ring:
ice_vsi_free_rx_rings(vsi);
err_setup_tx_ring:
ice_vsi_free_tx_rings(vsi);

View File

@@ -5637,6 +5637,16 @@ static int ice_resume(struct device *dev)
/* Restart the service task */
mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
/* Best-effort wait for the scheduled reset to finish so that the
* device is operational before returning. Without this, userspace
* (e.g. NetworkManager) may try to open the net device while the
* asynchronous reset is still in progress, hitting -EBUSY.
*/
ret = ice_wait_for_reset(pf, secs_to_jiffies(10));
if (ret)
dev_err(dev, "Wait for reset timed out (10s) during resume: %d\n",
ret);
return 0;
}

View File

@@ -848,6 +848,30 @@ static void ice_notify_vf_reset(struct ice_vf *vf)
NULL);
}
/**
* ice_reset_interrupts - clear all queue interrupt configuration for a VSI
* @vsi: the VSI whose interrupt registers should be cleared
*
* Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues
* in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX,
* CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only
* clears the CAUSE_ENA bit.
*/
void ice_reset_interrupts(struct ice_vsi *vsi)
{
struct ice_pf *pf = vsi->back;
struct ice_hw *hw = &pf->hw;
int i;
ice_for_each_alloc_rxq(vsi, i)
wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0);
ice_for_each_alloc_txq(vsi, i)
wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0);
ice_flush(hw);
}
/**
* ice_reset_vf - Reset a particular VF
* @vf: pointer to the VF structure
@@ -919,6 +943,9 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags)
ice_dis_vf_qs(vf);
/* cleanup interrupt registers */
ice_reset_interrupts(vsi);
/* Call Disable LAN Tx queue AQ whether or not queues are
* enabled. This is needed for successful completion of VFR.
*/

View File

@@ -26,6 +26,7 @@
void ice_initialize_vf_entry(struct ice_vf *vf);
void ice_deinitialize_vf_entry(struct ice_vf *vf);
void ice_dis_vf_qs(struct ice_vf *vf);
void ice_reset_interrupts(struct ice_vsi *vsi);
int ice_check_vf_init(struct ice_vf *vf);
enum virtchnl_status_code ice_err_to_virt_err(int err);
struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf);

View File

@@ -224,6 +224,24 @@ void ice_vf_ena_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
wr32(hw, QINT_RQCTL(pfq), reg | QINT_RQCTL_CAUSE_ENA_M);
}
/**
* ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL
* @vsi: VSI of the VF to configure
* @q_idx: VF queue index used to determine the queue in the PF's space
*/
static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
{
struct ice_hw *hw = &vsi->back->hw;
u32 pfq = vsi->rxq_map[q_idx];
u32 reg;
reg = rd32(hw, QINT_RQCTL(pfq));
reg &= ~QINT_RQCTL_CAUSE_ENA_M;
wr32(hw, QINT_RQCTL(pfq), reg);
ice_flush(hw);
}
/**
* ice_vc_ena_qs_msg
* @vf: pointer to the VF info
@@ -416,6 +434,8 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg)
goto error_param;
}
for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF)
ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF);
} else if (q_map) {
for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
@@ -436,6 +456,7 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg)
goto error_param;
}
ice_vf_dis_rxq_interrupt(vsi, vf_q_id);
/* Clear enabled queues flag */
clear_bit(vf_q_id, vf->rxq_ena);
}

View File

@@ -87,7 +87,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport,
if (!reg_vals)
return -ENOMEM;
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals);
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
if (num_regs < num_vecs) {
err = -EINVAL;
goto free_reg_vals;

View File

@@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter)
if (err) {
dev_err(&adapter->pdev->dev,
"IRQ request for mailbox failed, error: %d\n", err);
kfree(name);
return err;
}

View File

@@ -3097,10 +3097,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
/* Set the RE bit to periodically "clean" the descriptor ring.
* MIN_GAP is set to MIN_RING size to ensure it will be set at
* least once each time around the ring.
*/
/* Set the RE bit periodically to "clean" the descriptor ring */
if (idpf_tx_splitq_need_re(tx_q)) {
tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE;
tx_q->txq_grp->num_completions_pending++;

View File

@@ -21,7 +21,7 @@
/* Mailbox Queue */
#define IDPF_MAX_MBXQ 1
#define IDPF_MIN_TXQ_DESC 64
#define IDPF_MIN_TXQ_DESC 128
#define IDPF_MIN_RXQ_DESC 64
#define IDPF_MIN_TXQ_COMPLQ_DESC 256
#define IDPF_MAX_QIDS 256

View File

@@ -86,7 +86,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport,
if (!reg_vals)
return -ENOMEM;
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals);
num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs);
if (num_regs < num_vecs) {
err = -EINVAL;
goto free_reg_vals;

View File

@@ -1318,11 +1318,12 @@ idpf_vport_init_queue_reg_chunks(struct idpf_vport_config *vport_config,
* idpf_get_reg_intr_vecs - Get vector queue register offset
* @adapter: adapter structure to get the vector chunks
* @reg_vals: Register offsets to store in
* @num_vecs: number of entries the @reg_vals array can hold
*
* Return: number of registers that got populated
*/
int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
struct idpf_vec_regs *reg_vals)
struct idpf_vec_regs *reg_vals, int num_vecs)
{
struct virtchnl2_vector_chunks *chunks;
struct idpf_vec_regs reg_val;
@@ -1346,7 +1347,7 @@ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
dynctl_reg_spacing = le32_to_cpu(chunk->dynctl_reg_spacing);
itrn_reg_spacing = le32_to_cpu(chunk->itrn_reg_spacing);
for (i = 0; i < num_vec; i++) {
for (i = 0; i < num_vec && num_regs < num_vecs; i++) {
reg_vals[num_regs].dyn_ctl_reg = reg_val.dyn_ctl_reg;
reg_vals[num_regs].itrn_reg = reg_val.itrn_reg;
reg_vals[num_regs].itrn_index_spacing =

View File

@@ -104,7 +104,7 @@ int idpf_vc_core_init(struct idpf_adapter *adapter);
void idpf_vc_core_deinit(struct idpf_adapter *adapter);
int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter,
struct idpf_vec_regs *reg_vals);
struct idpf_vec_regs *reg_vals, int num_vecs);
int idpf_queue_reg_init(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc,
struct idpf_queue_id_reg_info *chunks);

View File

@@ -2190,8 +2190,6 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
buffer_info->time_stamp = 0;
buffer_info->length = 0;
buffer_info->mapped_as_page = false;
if (count)
count--;
/* clear timestamp and dma mappings for remaining portion of packet */
while (count--) {

View File

@@ -5352,9 +5352,8 @@ void igc_down(struct igc_adapter *adapter)
for (i = 0; i < adapter->num_q_vectors; i++) {
if (adapter->q_vector[i]) {
napi_synchronize(&adapter->q_vector[i]->napi);
igc_set_queue_napi(adapter, i, NULL);
napi_disable(&adapter->q_vector[i]->napi);
igc_set_queue_napi(adapter, i, NULL);
}
}