From: sashiko-bot@kernel.org
To: "Jacob Keller" <jacob.e.keller@intel.com>,
"Intel Wired LAN" <intel-wired-lan@lists.osuosl.org>
Subject: Re: [PATCH iwl-next v2 4/4] ice: use struct timecounter for E825 and E830
Date: Fri, 13 Nov 2026 23:46:10 +0000 [thread overview]
Message-ID: <sashiko-outbox-213873@kernel.org> (raw)
In-Reply-To: <20261113-jk-e825-e830-timecounter-v2-4-9c357a7ed349@intel.com>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] ice: perout_rqs updated outside phc.lock in
ice_ptp_gpio_enable()
- [Medium] ice: premature update_jiffies store in ice_ptp_read_cycles()
--
commit 2dbce654e6813d30b319e71f8e68ca2b82a4b244
Author: Jacob Keller <jacob.e.keller@intel.com>
ice: use struct timecounter for E825 and E830
This commit switches E825 and E830 devices from direct hardware PHC time
adjustments and legacy 32-bit timestamp extension to a latched
timecounter/cyclecounter stored in struct ice_phc.
> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
> index a0451c1083a41..b48d1bf3bbf59 100644
> --- a/drivers/net/ethernet/intel/ice/ice_ptp.c
> +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
> @@ -272,6 +272,202 @@ int ice_ptp_read_src_clk_reg(struct ice_pf *pf,
> return 0;
> }
>
> +/**
> + * ice_ptp_read_cycles - cyclecounter read() callback function
> + * @cc: the hardware cycle counter structure
> + *
> + * Read the PHC time via ice_ptp_read_src_clk_reg() and report it for use
> + * within timecounter_read() and timecounter_init().
> + *
> + * Although the cycle counter is masked to 32-bits for timestamp extension,
> + * this still reports the full 64-bit time to enable determining the phase
> + * difference between the PHC hardware value and the timecounter properly.
> + *
> + * Return: The current PHC timer value from the hardware on success.
> + * Since failure cannot be meaningfully reported, return the previous
> + * cycle_last value when unable to access the device.
> + */
> +u64 ice_ptp_read_cycles(struct cyclecounter *cc)
> +{
> + struct ice_adapter *adapter;
> + struct ice_phc *phc;
> + struct ice_pf *pf;
> + u64 time;
> + int err;
> +
> + phc = container_of(cc, struct ice_phc, cc);
> + adapter = container_of(phc, struct ice_adapter, phc);
> +
> + scoped_guard(rcu) {
> + pf = rcu_dereference(adapter->ctrl_pf);
> + if (!pf)
> + return phc->tc[0].cycle_last;
> +
> + err = ice_ptp_read_src_clk_reg(pf, NULL, &time);
> + if (err)
> + return phc->tc[0].cycle_last;
> +
> + /* Paired with smp_load_acquire in ice_ptp_extend_32b_ts */
> + smp_store_release(&phc->update_jiffies, jiffies | 1);
> + return time;
> + }
> +}
[ ... ]
> +static void ice_phc_refresh(struct ice_phc *phc)
> +{
> + lockdep_assert_held(&phc->lock);
> +
> + write_seqcount_latch_begin(&phc->seq);
> + (void)timecounter_read(&phc->tc[0]);
> + write_seqcount_latch(&phc->seq);
> + /* Update the secondary copy */
> + phc->tc[1].cycle_last = phc->tc[0].cycle_last;
> + phc->tc[1].nsec = phc->tc[0].nsec;
> + write_seqcount_latch_end(&phc->seq);
> +}
> +
> +/**
> + * ice_phc_init - Initialize PHC timecounter to new start value
> + * @phc: pointer to the PHC structure
> + * @init_tstamp: New initial value for the timecounter
> + *
> + * Initialize the PHC timecounter to a new offset. The latched sequence
> + * counter is used to ensure readers get a consistent view of the timecounter
> + * phase. We always pass the same cyclecounter structure, so the mask, frac,
> + * and cc fields always remain the same, and only the cycle_last and nsec
> + * fields must be propagated to the secondary structure.
> + */
> +static void ice_phc_init(struct ice_phc *phc, u64 init_tstamp)
> +{
> + lockdep_assert_held(&phc->lock);
> +
> + write_seqcount_latch_begin(&phc->seq);
> + timecounter_init(&phc->tc[0], &phc->cc, init_tstamp);
> + write_seqcount_latch(&phc->seq);
> + /* Update the secondary copy */
> + phc->tc[1].cycle_last = phc->tc[0].cycle_last;
> + phc->tc[1].nsec = phc->tc[0].nsec;
> + write_seqcount_latch_end(&phc->seq);
> +}
[Severity: Medium]
Does storing phc->update_jiffies inside ice_ptp_read_cycles() publish
the timestamp before either phc->tc[] copy is updated?
timecounter_init() and timecounter_read() invoke cc->read() before
writing tc[0].cycle_last and tc[0].nsec, while phc->seq is odd and
directing readers to phc->tc[1]:
CPU0 CPU1
ice_phc_init()
write_seqcount_latch_begin(&phc->seq)
timecounter_init(&phc->tc[0], ...)
ice_ptp_read_cycles()
smp_store_release(&phc->update_jiffies, ...)
ice_ptp_extend_32b_ts()
smp_load_acquire(&phc->update_jiffies)
ice_phc_cyc2time_with_trace()
seq = read_seqcount_latch(&phc->seq)
/* idx == 1 */
timecounter_cyc2time(&phc->tc[1], ...)
<-- uses uninitialized or stale tc[1]
tc[0].cycle_last = ...
tc[0].nsec = ...
write_seqcount_latch(&phc->seq)
phc->tc[1] = phc->tc[0]
write_seqcount_latch_end(&phc->seq)
During initial bringup or after a reset or delayed refresh (>2s), CPU1
sees a fresh update_jiffies and extends a packet's 32-bit hardware
timestamp against an uninitialized (zero) or stale phc->tc[1] snapshot
instead of discarding it or using the updated timecounter. Recording the
new jiffies value and calling smp_store_release(&phc->update_jiffies, ...)
after write_seqcount_latch(&phc->seq) (or write_seqcount_latch_end()) when
cc->read() succeeded would ensure readers only see the fresh timestamp
once an updated timecounter copy is active.
[ ... ]
> @@ -1974,7 +2202,10 @@ static int ice_ptp_gpio_enable(struct ptp_clock_info *info,
> if (on && pf->ptp.state != ICE_PTP_READY)
> return -EIO;
>
> + /* Serialize against concurrent timecounter updates */
> + mutex_lock(&pf->adapter->phc.lock);
> err = ice_ptp_cfg_perout(pf, &rq->perout, on);
> + mutex_unlock(&pf->adapter->phc.lock);
> if (!err) {
> *cached = rq->perout;
> } else {
[ ... ]
> @@ -2086,6 +2319,43 @@ ice_ptp_settime64(struct ptp_clock_info *info, const struct timespec64 *ts)
> +static int ice_ptp_settime64_tc(struct ptp_clock_info *info,
> + const struct timespec64 *ts)
> +{
> + struct ice_pf *pf = ptp_info_to_pf(info);
> + struct ice_phc *phc = &pf->adapter->phc;
> + u64 ns;
> +
> + if (pf->ptp.state != ICE_PTP_READY)
> + return -EIO;
> +
> + ns = timespec64_to_ns(ts);
> +
> + mutex_lock(&phc->lock);
> +
> + ice_ptp_disable_all_perout(pf);
> +
> + ice_phc_init(phc, ns);
> +
> + ice_ptp_mark_all_tx_tracker_stale(pf);\n+
> + /* Reenable periodic outputs */
> + ice_ptp_enable_all_perout(pf);
> +
> + mutex_unlock(&phc->lock);
> +
> + return 0;
> +}
[Severity: Medium]
Can releasing pf->adapter->phc.lock before updating *cached
(pf->ptp.perout_rqs[rq->perout.index]) in ice_ptp_gpio_enable() race with
ice_ptp_settime64_tc() or ice_ptp_adjtime_tc()?
The POSIX clock layer only holds down_read(&clk->rwsem) across ioctl,
clock_settime, and clock_adjtime calls, so ice_ptp_gpio_enable() and
ice_ptp_settime64_tc() can run concurrently:
CPU0 CPU1
ice_ptp_gpio_enable()
mutex_lock(&pf->adapter->phc.lock)
ice_ptp_cfg_perout(pf, &rq->perout, 1)
mutex_unlock(&pf->adapter->phc.lock)
ice_ptp_settime64_tc()
mutex_lock(&phc->lock)
ice_ptp_disable_all_perout(pf)
ice_phc_init(phc, ns)
ice_ptp_enable_all_perout(pf)
/* cached->period is still 0 */
mutex_unlock(&phc->lock)
*cached = rq->perout; <-- HW keeps pre-settime phase
When enabling an output, ice_ptp_enable_all_perout() sees a zero period in
perout_rqs[] and skips reprogramming GLTSYN_TGT for the new timecounter
phase; when disabling an output, it sees the old non-zero period and
re-enables the output in hardware right before *cached is cleared.
Writing *cached without holding phc.lock also races with
ice_ptp_disable_all_perout() and ice_ptp_enable_all_perout() reading the
struct under phc.lock. Holding pf->adapter->phc.lock across the *cached
update in ice_ptp_gpio_enable() avoids the race.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-jk-e825-e830-timecounter-v2-0-9c357a7ed349@intel.com?part=4
prev parent reply other threads:[~2026-11-13 23:46 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 23:30 [PATCH iwl-next v2 0/4] ice: E825 timer adjustment fixes Jacob Keller
2026-11-13 23:30 ` [PATCH iwl-next v2 1/4] ice: fix computation of ETF timestamps for E830 Jacob Keller
2026-11-13 23:30 ` [PATCH iwl-next v2 2/4] ice: exit PTP callbacks early if not ICE_PTP_READY Jacob Keller
2026-11-13 23:30 ` [PATCH iwl-next v2 3/4] ice: store cached PHC time in ice_adapter Jacob Keller
2026-11-13 23:52 ` sashiko-bot
2026-11-13 23:30 ` [PATCH iwl-next v2 4/4] ice: use struct timecounter for E825 and E830 Jacob Keller
2026-11-13 23:46 ` sashiko-bot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-213873@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=intel-wired-lan@lists.osuosl.org \
--cc=jacob.e.keller@intel.com \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox