Re: [PATCH iwl-net v1] i40e: fix races in PTP external timestamp work handling

Vadim Fedorenko <[email protected]>
Newsgroups org.kernel.vger.netdev,org.osuosl.intel-wired-lan
Message-ID <[email protected]>
On 24/07/2026 10:34, [email protected] wrote:
> From: Xuanqiang Luo <[email protected]>
> 
> The time sync interrupt queues ptp_extts0_work, which reads device
> registers and reports events through pf->ptp_clock.
> 
> i40e_ptp_stop() unregisters the PHC without first disabling the event
> source or draining the work. The worker can therefore access the clock or
> PF after either has been freed, causing a use-after-free. Disable external
> timestamp events and call disable_work_sync() before unregistering the PHC.
> Disabling the work also prevents an interrupt already in progress from
> queuing it after the drain.
> 
> INIT_WORK() is called from i40e_ptp_set_timestamp_mode(), which runs on
> reset and whenever timestamping is reconfigured. Reinitializing pending or
> running work can corrupt its workqueue state. Initialize the work once from
> i40e_sw_init() instead.
> 
> Also exclude reset recovery before stopping PTP so a concurrent rebuild
> cannot re-enable timestamp events or recreate the PHC during removal.
> 
> Fixes: 1050713026a0 ("i40e: add support for PTP external synchronization clock")
> Signed-off-by: Xuanqiang Luo <[email protected]>
> ---
>   drivers/net/ethernet/intel/i40e/i40e.h      |  1 +
>   drivers/net/ethernet/intel/i40e/i40e_main.c | 17 +++++++-------
>   drivers/net/ethernet/intel/i40e/i40e_ptp.c  | 25 +++++++++++++++------
>   3 files changed, 28 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
> index 83e780919ac97..e2ae61bc38786 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
> @@ -1315,6 +1315,7 @@ int i40e_ptp_hwtstamp_set(struct net_device *netdev,
>   			  struct netlink_ext_ack *extack);
>   void i40e_ptp_save_hw_time(struct i40e_pf *pf);
>   void i40e_ptp_restore_hw_time(struct i40e_pf *pf);
> +void i40e_ptp_init_work(struct i40e_pf *pf);
>   void i40e_ptp_init(struct i40e_pf *pf);
>   void i40e_ptp_stop(struct i40e_pf *pf);
>   int i40e_ptp_alloc_pins(struct i40e_pf *pf);
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index a04683004a567..2909a58d57562 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -12819,6 +12819,7 @@ static int i40e_sw_init(struct i40e_pf *pf)
>   			 "total-port-shutdown was enabled, link-down-on-close is forced on\n");
>   	}
>   	mutex_init(&pf->switch_mutex);
> +	i40e_ptp_init_work(pf);
>   
>   sw_init_done:
>   	return err;
> @@ -16157,14 +16158,6 @@ static void i40e_remove(struct pci_dev *pdev)
>   
>   	i40e_devlink_unregister(pf);
>   
> -	i40e_dbg_pf_exit(pf);
> -
> -	i40e_ptp_stop(pf);
> -
> -	/* Disable RSS in hw */
> -	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), 0);
> -	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), 0);
> -
>   	/* Grab __I40E_RESET_RECOVERY_PENDING and set __I40E_IN_REMOVE
>   	 * flags, once they are set, i40e_rebuild should not be called as
>   	 * i40e_prep_for_reset always returns early.
> @@ -16173,6 +16166,14 @@ static void i40e_remove(struct pci_dev *pdev)
>   		usleep_range(1000, 2000);
>   	set_bit(__I40E_IN_REMOVE, pf->state);
>   
> +	i40e_dbg_pf_exit(pf);
> +
> +	i40e_ptp_stop(pf);
> +
> +	/* Disable RSS in hw */
> +	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), 0);
> +	i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), 0);
> +
>   	if (test_bit(I40E_FLAG_SRIOV_ENA, pf->flags)) {
>   		set_bit(__I40E_VF_RESETS_DISABLED, pf->state);
>   		i40e_free_vfs(pf);
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> index ff62b5f2c8150..fd51ab8c10c20 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> @@ -169,6 +169,17 @@ static void i40e_ptp_extts0_work(struct work_struct *work)
>   	ptp_clock_event(pf->ptp_clock, &event);
>   }
>   
> +/**
> + * i40e_ptp_init_work - Initialize PTP work for a PF
> + * @pf: Board private structure
> + *
> + * Initialize work which must remain valid for the lifetime of the PF.
> + */
> +void i40e_ptp_init_work(struct i40e_pf *pf)
> +{
> +	INIT_WORK(&pf->ptp_extts0_work, i40e_ptp_extts0_work);
> +}
> +

why do you need extra function for a single line of code? why cannot you 
put it into i40e_ptp_init?

>   /**
>    * i40e_is_ptp_pin_dev - check if device supports PTP pins
>    * @hw: pointer to the hardware structure
> @@ -1186,8 +1197,6 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf,
>   	regval |= 1 << I40E_PRTTSYN_CTL0_EVENT_INT_ENA_SHIFT;
>   	wr32(hw, I40E_PRTTSYN_CTL0, regval);
>   
> -	INIT_WORK(&pf->ptp_extts0_work, i40e_ptp_extts0_work);
> -
>   	switch (config->tx_type) {
>   	case HWTSTAMP_TX_OFF:
>   		pf->ptp_tx = false;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.