* [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes
@ 2026-09-22 18:02 Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access Jacob Keller
` (16 more replies)
0 siblings, 17 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Maciek Machnikowski, Aleksandr Loktionov,
Arkadiusz Kubalewski, Przemyslaw Korba, Petr Oros, Paul Menzel
Jake Keller says:
This series contains several related fixes for the ice driver PTP logic
relating to timestamp handling and device (re)initialization.
Of particular note is some changes around the handling of timestamps that
are requested near device state changes such as administrative up/down
cycles and link change events.
The E825 device logic in the PHY has an internal counter which is used as
part of the "threshold" logic which determines when the device will trigger
an interrupt signal from a given PHY port to the MAC. This internal counter
requires some precise handling to ensure that the internal device state
remains in sync with software expectations. Otherwise, the device can be
finagled into a state where the PHY stops producing new timestamp interrupt
notifications to the MAC indefinitely. This in turn degrades the timestamp
processing latency and results in application failures for common
timestamping applications such as ptp4l.
There are four major categories of problem resolved by this series:
* Timestamp requests made while the PHY_REG_TX_OFFSET_READY bit is cleared
will increment the internal counter, but leave their valid bit set to 0.
Upon read, the counter is not decremented. This leads to a desync of the
counter and blocks the PHY interrupt.
* Software logic for tracking timestamps incorrectly cleared in-use bits
without waiting for completion in certain cases. If the timestamp *does*
later complete, it leaves an "orphaned" ready bit which is not
tracked by software. This results in the internal counter becoming
desynced if that index is re-used.
* The PHY timestamp memory region lacks pull-down zero-initialization at
power on, resulting in uninitialized random data in the memory region.
If software reads these values, an entry with its valid bit set to 1 can
trigger a counter decrement and cause an underflow which results in the
counter becoming desynced.
* Timestamp request which complete near the beginning of the PHY losing
link can become "stuck" such that the hardware logic triggered by a
timestamp read does not activate. The memory status bit and the valid
bit in the timestamp index are not cleared. This window where this
may occur begins *before* the firmware notifies the driver of link loss.
When this occurs, the driver may accidentally re-use a stale timestamp,
and the IRQ re-trigger logic triggers a repeated IRQ "storm" that can
consume significant excess CPU time.
The series' primary focus is towards preventing driver flows that can
trigger the above sequences. It is based on work from Przemyslaw Korba which
was previously posted at [1]. During that series development, Petr from
RedHat reported the 3rd issue mentioned above. While attempting to root
cause that issue, several other issues were uncovered and those fixes have
also been included in this series.
First, the locking around the PTP ports list in the adapter structure is
converted to use RCU primitives and a spinlock, resolving a couple of
reports from Petr about places where the original list was accessed without
lock protection. Note that an older version of this fix used an xarray
instead of the list. The xarray has more overhead and results in an
increase of ~25 microseconds to the average latency for processing Tx
timestamps. The list is simpler and avoids this overhead.
Next, the PHY restart locking is fixed to avoid an issue with a concurrent
execution of ice_ptp_restart_all_phy() and a link change on a given port.
Additionally, the PHY port locks are merged into a single per-adapter lock
to reduce locking complexity.
Next, the PTP reset flow is fixed to stop tearing down the Tx tracker
during a CORE or GLOBAL reset. This avoids causing Tx timestamps to break
permanently after such a reset. The fix also ensures that all teardown
paths properly release the tracker. This issue was found by Sashiko during
review of a previous version of this series.
Next, the ice_ptp_request_ts() function is updated to sequence the marking
of the in_use bitmap in order to work properly with the lockless reader in
the IRQ thread. This issue was reported by Sashiko during review of a
previous version of this series.
Next, come two fixes for E822 hardware that were originally posted as part
of Przemyslaw Korba's work [1]. The E822-only "vernier" offset validation
work task is properly canceled during device reset, and new timestamp
requests are kept disabled until the validation task completes.
Next, Arkadiusz modifies the driver to stop pretending that the link has
gone down during ice_down(). This removes "virtual" PTP link changes that
occurred on several flows including MTU change, Eswitch setup, and others.
Now, the driver only triggers a PTP PHY timer reinitialization when the
physical PHY link has changed instead of during many other actions.
Next, the driver is modified to stop clearing the PHY_REG_TX_OFFSET_READY
bit. This bits only purpose is to tell hardware to mark any captured
timestamps as invalid. Since this also disables the necessary side effects
on read it is problematic to have cleared. According to hardware engineers,
keeping it enabled should not have any other side effects. Instead, the
tx.calibrating field is used to disable new timestamps from software in a
similar manner to the older E822 devices.
Next, the driver is modified to clear the PHY_REG_TX_MEMORY_STATUS by
reading each index *prior* to the PHY soft reset. This ensures that any
stale or invalid data left in the memory array is cleared, followed by the
counter being reset via the PHY soft reset procedure.
Next, the E825 timer start procedure is modified to first include a soft
reset. This ensures that upon link up the device is reconfigured from a
known-good state with its internal counter reset and everything cleared.
Next, Petr modifies the ice_ptp_flush_tx_tracker() function to wait a little
bit for any outstanding timestamps before flushing.
Next, Petr modifies the ice_ptp_process_tx_tstamp() function to avoid
releasing any index from software unless either a) it is actually completed
by hardware or b) it is timed out waiting for a full two seconds. This
closes the final gap from the second issue mentioned above. Instead of
immediately releasing the index, the software now waits until hardware has
completed it or the driver has waited long enough to be sufficiently sure
that no such timestamp will be done.
Next, the ice_ptp_process_tx_tstamp() function is modified to verify
that hardware actually cleared the ready bitmap. This ensures that we do
not report false timestamps near a link down event.
Finally, Maciek adds a needed PHY recalibration for E825-C after large system
time adjustments. Without recalibration, PHY timestamps do not properly
converge to the new time, resulting in inaccurate timestamp readings.
Link: [1] https://lore.kernel.org/intel-wired-lan/20260720120151.2675206-1-przemyslaw.korba@intel.com/
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
Changes since v1:
- Convert to sleepable RCU instead of the convoluted and likely broken
dropping of RCU readlock critical sections.
- Update the messaging to be clear this does not solve the ctrl_pf access
issues.
- Update the comments regarding the teardown and 15 second timeout to
better reflect the intention.
- Drop the patch that reverted marking timestamps as stale during an
adjustment. It may be safe for some smaller atomic adjustments, however
possible issues were reported for larger adjustments by multiple models.
Since we do not have many reports of missing timestamps, just keep the
logic as-is.
- Add a new patch to correct serialization of the PHY port restarts to
avoid a potential re-ordering of the link_up status causing the port to
be left in a disabled state if a race occurs near a link up transition.
- Fix the PTP teardown during a failed reset to avoid leaking the Tx
timestamp tracker and other PTP state.
- Add smp_rmb() to the lockless in_use reads in
ice_ptp_process_tx_tstamp() ensuring that weak ordered architectures do
not re-order the start time in the event of a race with a new timestamp
request.
- Update the patch which drops the E825 clearing of PHY_REG_TX_OFFSET_READY
with a software gate of new timestamp requests via the tx.cablibrating
field, so that new requests will be rejected until the PHY has been
initialized.
- Update the fixes tag for one of the E822 fixes to better reflect the
actual kernel that introduced the problem.
- Update several commit messages and comments for clarity and accuracy as
suggested by Sashiko during review.
- Since the E822 offset validation work already checks and reschedules when
the driver is resetting, replace the kthread_cancel_delayed_work_sync
with kthread_flush_work() to ensure that it sees an updated state. This
avoids some complex changes that would otherwise be required to be
certain that a reset with a concurrent link change wouldn't result in the
offset task being canceled indefinitely.
- Use READ_ONCE/WRITE_ONCE when checking the link_up field of the PTP
state.
- Treat failure to read a timestamp during the E825 sweep before a soft
reset as non-fatal with a warning. In practice, if a read is skipped we
still should not have a problem unless other code incorrectly reads the
index without checking the PHY ready bitmap first. To avoid log spam if
the device is truly inaccessible, the total read failure count is
summarized in one message per port.
- Remove the unused soft_reset parameter from one of the E825 functions.
- Fix the watchdog re-trigger of the timestamp interrupt to work for
devices which manage their own interrupt, instead of only on the clock
owner.
- Fix the accounting for timed out timestamps in the unlikely case that a
timestamp will timeout but suddenly have its ready bitmap bit stuck high.
Now, the timeout counter is only incremented once the timestamp is
actually dropped.
- Update commit message and comments to better reflect current
understanding of the PHY soft reset behavior including its (non)impact on
configuration registers.
- Switched to read_poll_timeout for the tracker drain logic to get better
timing behavior.
- Skip the tracker drain on E810 with a has_ready_bitmap flag check.
Possible outstanding issues not addressed:
- ctrl_pf serialization and access is not handled by this series. Another
developer has been investigating this and is still undergoing feedback.
Perhaps the solution could piggyback on the port reference count, but we
do not yet have a complete solution.
- Recent reports of missing PTP semaphore locking on certain flows are
being investigated by another engineer and will be handled as a follow
up.
- The low latency timestamp interface for E810 appears to possibly have
some gaps and potential to override an in-flight timestamp request. This
will be investigated as part of a separate follow-up series.
AI reports that may still remain:
- Sashiko pointed out some ideas about flushing the tracker when we restart
the PHY port for E825, which I have not opted to implement in this
series. It still needs investigation and I am currently thinking that it
is best if we always keep timestamp indexes locked by software until we
wait that 2 seconds. There are just so many ways this can race and go
wrong :\ Relatedly, Sashiko also pointed out some potential races with
flushing the trackers, which I will investigate but do not think should
hold this series up.
- Sashiko pointed out that the behavior of restarting the PHY ports
post-reset may be somewhat problematic. This needs investigation and I do
not yet have a solution. In particular, we can't just let each port do
its own restart because we need to be sure that the clock owner has
finished setting the time, but we also cannot necessarily just let the
clock owner handle it either because the clock owner cannot reliably know
the link state of the other ports. This is being investigated as well,
but I would prefer to not hold this already large series up for this.
- Sashiko suggested clearing the PHY soft reset device state if the
function exits early. I opted not to change this flow as it was the
suggested flow from hardware engineers, and the chance of failure in this
flow is low and likely already implies a more catastrophic failure (i.e.
failure to access the sideband queue).
- Some models love to point out places where we bail out on failure to
access the PHY and leave various flags or state in a disabled way, such
as not clearing the calibrating flag or leaving the PHY with its soft
reset bit set. I did not make an attempt to fix these. It is very
unexpected to be unable to access the PHY, and we're more or less
treating such failures as catastrophic.
For those interested, here is a summary of the latency numbers for Tx
timestamps on my setup for comparison throughout the series. In all cases,
the ptp4l test used a profile with a sync rate of 16/second and the
"torture" test additionally operated a second thread on another port
operating a burst of 32 concurrent timestamp requests every 10
milliseconds, all operating on E825 hardware, with a 3minute capture time
for each test.
1. Before this series
ptp4l only
Mean: 298.67 microseconds, stddev: 28.60
ptp4l + torture
Mean: 637.02 microseconds, stddev: 351.59
2. After SRCU rework
ptp4l only
Mean: 314.10 microseconds, stddev: 32.99
ptp4l + torture
Mean: 622.68 microseconds, stddev: 343.57
3. Everything up to keeping Tx timestamps tracked until completion
ptp4l only
Mean: 317.43 microseconds, stddev: 34.51
ptp4l + torture
Mean: 622.31 microseconds, stddev: 339.26
4. After rechecking the HW ready bitmap
ptp4l only
Mean: 184.98 microseconds, stddev: 53.79
ptp4l + torture
Mean: 719.71 microseconds, stdev: 321.195
5. After the full series
ptp4l only
Mean: 195.92 microseconds, stddev: 25.28
ptp4l + torture
Mean: 717.77 microseconds, stddev: 321.58
The run-to-run variance here is somewhat high, but its clear that
timestamping across multiple ports under heavy load has a significant
latency cost. This is to be expected due to the nature of serializing the
timestamps to a single IRQ. In the usual cases with a lower timestamp load
and especially if ports do not have active timestamp requests, the series
has a decent reduction in timestamp latency. The use of Sleepable RCU does
seem to have a minor latency cost but it is overshadowed by the improvement
to elide checking when there are no requests in software.
Hopefully this version will pass testing and AI review @_@
---
Arkadiusz Kubalewski (1):
ice: call PTP link change only from link events
Jacob Keller (9):
ice: use reference counting and SRCU for PTP port access
ice: fix PHY port restart serialization
ice: fix removal of PTP timestamp tracker during reset
ice: set in_use only after preparing Tx timestamp index
ice: E825: stop clearing PHY_REG_TX_OFFSET_READY
ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset
ice: E825: perform a soft reset when starting the PHY timer
ice: skip reading Tx ready bitmap on ports with no timestamps
ice: don't clear in_use until HW clears ready bitmap
Karol Kolacinski (2):
ice: E822: keep Tx timestamps disabled during offset calibration
ice: E822: flush offset verification work during reset preparation
Maciek Machnikowski (1):
ice: Recalibrate PHY after settime64 on E825-C
Petr Oros (2):
ice: wait for in-flight Tx timestamps before flushing the tracker
ice: keep Tx timestamp slots tracked until completion or timeout
drivers/net/ethernet/intel/ice/ice_adapter.h | 20 +-
drivers/net/ethernet/intel/ice/ice_ptp.h | 16 +-
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 2 +-
drivers/net/ethernet/intel/ice/ice_adapter.c | 14 +-
drivers/net/ethernet/intel/ice/ice_main.c | 12 +-
drivers/net/ethernet/intel/ice/ice_ptp.c | 484 +++++++++++++++++++--------
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 133 ++++----
7 files changed, 466 insertions(+), 215 deletions(-)
---
base-commit: ceac0de741bfb47ca255eee075257b3bb31f0651
change-id: 20260917-jk-e825c-timestamp-processing-logic-fixes-srcu-fb0b50d33e10
Best regards,
--
Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-23 9:47 ` Loktionov, Aleksandr
2026-09-22 18:02 ` [PATCH iwl-net v2 02/15] ice: fix PHY port restart serialization Jacob Keller
` (15 subsequent siblings)
16 siblings, 1 reply; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Maciek Machnikowski
The ice adapter structure maintains a list of ports associated with the
adapter. This is used for supporting PTP, where the clock owner must handle
many operations that require access to the PTP port structures of the
associated PFs.
This is implemented using a linked list and a mutex. This sort of works,
but a few places within the code do not acquire the mutex when iterating
the list. This includes ice_ptp_flush_all_tx_tracker(),
ice_ptp_restart_all_phy(), and ice_ptp_prepare_rebuild_sec().
Fixing this is tricky, especially since it is not clear if we can simply
acquire the lock around the complete iterations.
The pattern of use for the port list is read-mostly with modifications only
happening during PF initialization when elements are inserted. This
typically only happens during early boot, though a PF could in principle be
removed or loaded at arbitrary times via bind and unbind operations.
The use of a mutex does mean the driver can sleep while holding it, but it
still creates complicates with lock ordering and prevents iterating the
list in any code path that *can't* sleep.
Instead, use SRCU primitives for the port linked list, along with a
reference count on the port. The kref reference counter ensures that a PF
will have a valid lifetime and not be removed until the reference is
released. Note that this change focuses solely on the port list and does
not make an effort to resolve access to ctrl_pf, which is currently being
investigated by another developer.
Use of sleepable RCU is required because we often iterate the PTP port list
and perform operations that might sleep. Attempts at implementing regular
RCU have thus far not proven to be acceptable.
The remove path first removes the port from the list, and we use
kref_get_unless_zero to ensure that such ports are skipped when iterating
the list. This ensures that once a port starts removing we will drop
references and no longer be able to acquire new ones. This avoids loop
iterations chaining together to indefinitely block removal.
The ice_ptp_release_port_srcu() function is used as the release function for
the kref_put() call. This uses wake_up_var() to wake the removing thread.
The waiting thread will block until the final reference has been removed,
then it will use synchronize_srcu() to ensure any outstanding SRCU critical
sections have had the necessary grace period.
This flow ensures that accesses to ports via the adapter port list will
remain valid until both the SRCU critical sections have ended and all the
references to the ports have been dropped. Strictly speaking, SRCU alone
might be sufficient for existing code paths, but the reference count allows
the option for passing a pointer to the port on to other functions if
necessary in the future.
One major complication of this reference count is that ice_ptp_port is
embedded inside of other structures and not merely allocated. As a result,
we can't use the standard pattern of kfree_rcu() to just delay freeing
until references are dropped, and instead are delaying PF port teardown. If
any code path leaks the reference, the driver will be unable to teardown.
Instead, a 15 second timeout with a WARN() is used when waiting to finally
allow PF teardown to continue. This has the risk of potentially allowing
use-after-free, assuming some path really is stuck for 15 seconds. However,
this both less likely and a less bad outcome compared to blocking
indefinitely on a reference leak.
Fixes: e800654e85b5 ("ice: Use ice_adapter for PTP shared data instead of auxdev")
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_adapter.h | 16 +--
drivers/net/ethernet/intel/ice/ice_ptp.h | 4 +
drivers/net/ethernet/intel/ice/ice_adapter.c | 11 +-
drivers/net/ethernet/intel/ice/ice_ptp.c | 145 +++++++++++++++++++++------
4 files changed, 134 insertions(+), 42 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h b/drivers/net/ethernet/intel/ice/ice_adapter.h
index 4f695f32da3d..93f041943bdd 100644
--- a/drivers/net/ethernet/intel/ice/ice_adapter.h
+++ b/drivers/net/ethernet/intel/ice/ice_adapter.h
@@ -17,15 +17,19 @@ struct ice_pf;
/**
* struct ice_port_list - data used to store the list of adapter ports
*
- * This structure contains data used to maintain a list of adapter ports
+ * This structure contains data used to maintain a list of adapter ports.
+ * Writers *must* acquire the lock, and use SRCU safe operations. Readers may
+ * use SRCU or acquire the lock.
*
- * @ports: list of ports
- * @lock: protect access to the ports list
+ * @list: list of ports
+ * @lock: protect write access to the list
+ * @srcu: Sleepable RCU domain for this adapter
*/
struct ice_port_list {
- struct list_head ports;
- /* To synchronize the ports list operations */
- struct mutex lock;
+ struct list_head list;
+ /* To synchronize write operations on the port list */
+ spinlock_t lock;
+ struct srcu_struct srcu;
};
/**
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index c4b0da7ce20e..da2003ba3bb0 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -5,6 +5,8 @@
#define _ICE_PTP_H_
#include <linux/ptp_clock_kernel.h>
+#include <linux/rculist.h>
+#include <linux/kref.h>
#include <linux/kthread.h>
#include "ice_ptp_hw.h"
@@ -138,6 +140,7 @@ struct ice_ptp_tx {
* and determine when the port's PHY offset is valid.
*
* @list_node: list member structure
+ * @ref: reference counter for use with adapter ports list
* @tx: Tx timestamp tracking for this port
* @ov_work: delayed work task for tracking when PHY offset is valid
* @ps_lock: mutex used to protect the overall PTP PHY start procedure
@@ -149,6 +152,7 @@ struct ice_ptp_tx {
*/
struct ice_ptp_port {
struct list_head list_node;
+ struct kref ref;
struct ice_ptp_tx tx;
struct kthread_delayed_work ov_work;
struct mutex ps_lock; /* protects overall PTP PHY start procedure */
diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c b/drivers/net/ethernet/intel/ice/ice_adapter.c
index 2dc3629d6d0f..84ac5ee5a739 100644
--- a/drivers/net/ethernet/intel/ice/ice_adapter.c
+++ b/drivers/net/ethernet/intel/ice/ice_adapter.c
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/srcu.h>
#include <linux/xarray.h>
#include "ice_adapter.h"
#include "ice.h"
@@ -66,18 +67,20 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev)
mutex_init(&adapter->cpi_phy_lock[i]);
refcount_set(&adapter->refcount, 1);
- mutex_init(&adapter->ports.lock);
- INIT_LIST_HEAD(&adapter->ports.ports);
+ spin_lock_init(&adapter->ports.lock);
+ INIT_LIST_HEAD(&adapter->ports.list);
+ init_srcu_struct(&adapter->ports.srcu);
return adapter;
}
static void ice_adapter_free(struct ice_adapter *adapter)
{
- WARN_ON(!list_empty(&adapter->ports.ports));
+ WARN_ON(!list_empty(&adapter->ports.list));
for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++)
mutex_destroy(&adapter->cpi_phy_lock[i]);
- mutex_destroy(&adapter->ports.lock);
+
+ cleanup_srcu_struct(&adapter->ports.srcu);
kfree(adapter);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index eaec36ab6ae3..9881a7a9e570 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2021, Intel Corporation. */
+#include <linux/rculist.h>
+#include <linux/srcu.h>
+#include <linux/wait_bit.h>
#include "ice.h"
#include "ice_lib.h"
#include "ice_trace.h"
@@ -673,20 +676,33 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
pf->ptp.tx_hwtstamp_good += tstamp_good;
}
+static void ice_ptp_release_port_srcu(struct kref *ref)
+{
+ wake_up_var(ref);
+}
+
static void ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
{
+ struct ice_port_list *ports = &pf->adapter->ports;
struct ice_ptp_port *port;
+ int srcu_idx;
- mutex_lock(&pf->adapter->ports.lock);
- list_for_each_entry(port, &pf->adapter->ports.ports, list_node) {
+ srcu_idx = srcu_read_lock(&ports->srcu);
+ list_for_each_entry_srcu(port, &ports->list, list_node,
+ srcu_read_lock_held(&ports->srcu)) {
struct ice_ptp_tx *tx = &port->tx;
- if (!tx || !tx->init)
+ if (!tx->init)
+ continue;
+
+ if (!kref_get_unless_zero(&port->ref))
continue;
ice_ptp_process_tx_tstamp(tx);
+
+ kref_put(&port->ref, ice_ptp_release_port_srcu);
}
- mutex_unlock(&pf->adapter->ports.lock);
+ srcu_read_unlock(&ports->srcu, srcu_idx);
}
/**
@@ -806,10 +822,19 @@ ice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx)
static void
ice_ptp_flush_all_tx_tracker(struct ice_pf *pf)
{
+ struct ice_port_list *ports = &pf->adapter->ports;
struct ice_ptp_port *port;
+ int srcu_idx;
- list_for_each_entry(port, &pf->adapter->ports.ports, list_node)
+ srcu_idx = srcu_read_lock(&ports->srcu);
+ list_for_each_entry_srcu(port, &ports->list, list_node,
+ srcu_read_lock_held(&ports->srcu)) {
+ if (!kref_get_unless_zero(&port->ref))
+ continue;
ice_ptp_flush_tx_tracker(ptp_port_to_pf(port), &port->tx);
+ kref_put(&port->ref, ice_ptp_release_port_srcu);
+ }
+ srcu_read_unlock(&ports->srcu, srcu_idx);
}
/**
@@ -1424,16 +1449,22 @@ static void ice_ptp_reset_phy_timestamping(struct ice_pf *pf)
*/
static void ice_ptp_restart_all_phy(struct ice_pf *pf)
{
- struct list_head *entry;
+ struct ice_port_list *ports = &pf->adapter->ports;
+ struct ice_ptp_port *port;
+ int srcu_idx;
- list_for_each(entry, &pf->adapter->ports.ports) {
- struct ice_ptp_port *port = list_entry(entry,
- struct ice_ptp_port,
- list_node);
+ srcu_idx = srcu_read_lock(&ports->srcu);
+ list_for_each_entry_srcu(port, &ports->list, list_node,
+ srcu_read_lock_held(&ports->srcu)) {
+ if (!kref_get_unless_zero(&port->ref))
+ continue;
if (port->link_up)
ice_ptp_port_phy_restart(port);
+
+ kref_put(&port->ref, ice_ptp_release_port_srcu);
}
+ srcu_read_unlock(&ports->srcu, srcu_idx);
}
/**
@@ -2694,19 +2725,30 @@ static bool ice_port_has_timestamps(struct ice_ptp_tx *tx)
static bool ice_any_port_has_timestamps(struct ice_pf *pf)
{
+ struct ice_port_list *ports = &pf->adapter->ports;
+ bool have_tstamps = false;
struct ice_ptp_port *port;
+ int srcu_idx;
- scoped_guard(mutex, &pf->adapter->ports.lock) {
- list_for_each_entry(port, &pf->adapter->ports.ports,
- list_node) {
- struct ice_ptp_tx *tx = &port->tx;
+ srcu_idx = srcu_read_lock(&ports->srcu);
+ list_for_each_entry_srcu(port, &ports->list, list_node,
+ srcu_read_lock_held(&ports->srcu)) {
+
+ if (!kref_get_unless_zero(&port->ref))
+ continue;
+
+ if (ice_port_has_timestamps(&port->tx))
+ have_tstamps = true;
+
+ kref_put(&port->ref, ice_ptp_release_port_srcu);
+
+ if (have_tstamps)
+ break;
- if (ice_port_has_timestamps(tx))
- return true;
- }
}
+ srcu_read_unlock(&ports->srcu, srcu_idx);
- return false;
+ return have_tstamps;
}
bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
@@ -2890,14 +2932,18 @@ void ice_ptp_queue_work(struct ice_pf *pf)
static void ice_ptp_prepare_rebuild_sec(struct ice_pf *pf, bool rebuild,
enum ice_reset_req reset_type)
{
- struct list_head *entry;
+ struct ice_port_list *ports = &pf->adapter->ports;
+ struct ice_ptp_port *port;
+ int srcu_idx;
- list_for_each(entry, &pf->adapter->ports.ports) {
- struct ice_ptp_port *port = list_entry(entry,
- struct ice_ptp_port,
- list_node);
+ srcu_idx = srcu_read_lock(&ports->srcu);
+ list_for_each_entry_srcu(port, &ports->list, list_node,
+ srcu_read_lock_held(&ports->srcu)) {
struct ice_pf *peer_pf = ptp_port_to_pf(port);
+ if (!kref_get_unless_zero(&port->ref))
+ continue;
+
if (!ice_is_primary(&peer_pf->hw)) {
if (rebuild) {
/* TODO: When implementing rebuild=true:
@@ -2909,7 +2955,10 @@ static void ice_ptp_prepare_rebuild_sec(struct ice_pf *pf, bool rebuild,
ice_ptp_prepare_for_reset(peer_pf, reset_type);
}
}
+
+ kref_put(&port->ref, ice_ptp_release_port_srcu);
}
+ srcu_read_unlock(&ports->srcu, srcu_idx);
}
/**
@@ -3086,11 +3135,11 @@ static int ice_ptp_setup_pf(struct ice_pf *pf)
return -ENODEV;
INIT_LIST_HEAD(&ptp->port.list_node);
- mutex_lock(&pf->adapter->ports.lock);
+ kref_init(&ptp->port.ref);
- list_add(&ptp->port.list_node,
- &pf->adapter->ports.ports);
- mutex_unlock(&pf->adapter->ports.lock);
+ spin_lock(&pf->adapter->ports.lock);
+ list_add_rcu(&ptp->port.list_node, &pf->adapter->ports.list);
+ spin_unlock(&pf->adapter->ports.lock);
/* Seed the per-PHY Tx reference clock usage map for this port.
* Only meaningful on E825 (other MAC types don't expose tx-clk
@@ -3112,13 +3161,45 @@ static int ice_ptp_setup_pf(struct ice_pf *pf)
static void ice_ptp_cleanup_pf(struct ice_pf *pf)
{
+ struct ice_port_list *ports = &pf->adapter->ports;
struct ice_ptp *ptp = &pf->ptp;
+ struct kref *ref;
- if (pf->hw.mac_type != ICE_MAC_UNKNOWN) {
- mutex_lock(&pf->adapter->ports.lock);
- list_del(&ptp->port.list_node);
- mutex_unlock(&pf->adapter->ports.lock);
- }
+ if (pf->hw.mac_type == ICE_MAC_UNKNOWN)
+ return;
+
+ /* The PF should not be removed until there are no more outstanding
+ * references on the PTP port. First, remove the port from the list to
+ * prevent new references from being acquired. Then, drop the primary
+ * reference this PF holds on the port. Wait until the references are
+ * dropped and then finally synchronize_srcu() to ensure the SRCU
+ * critical sections have finished.
+ *
+ * Since this blocks PF removal (and doesn't merely result in a memory
+ * leak), have a maximum timeout of 15 seconds before continuing
+ * removal. Since the port is already removed from the list, new
+ * references will not be acquired. The only way to trigger
+ * use-after-free should be for a single thread already holding
+ * a reference becoming blocked for 15 seconds.
+ *
+ * This intentionally trades off allowing a possible but unlikely
+ * use-after-free for avoiding permanently blocking the ability to
+ * remove the driver due a programming bug resulting in a true
+ * reference leak.
+ */
+
+ spin_lock(&ports->lock);
+ list_del_rcu(&ptp->port.list_node);
+ spin_unlock(&ports->lock);
+
+ ref = &ptp->port.ref;
+ kref_put(ref, ice_ptp_release_port_srcu);
+
+ dev_WARN_ONCE(ice_pf_to_dev(pf),
+ !wait_var_event_timeout(ref, !kref_read(ref), 15 * HZ),
+ "Timed out waiting for port references to release. Continuing to unload anyways.");
+
+ synchronize_srcu(&ports->srcu);
}
/**
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 02/15] ice: fix PHY port restart serialization
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 03/15] ice: fix removal of PTP timestamp tracker during reset Jacob Keller
` (14 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller
The ice_port_phy_restart() function is responsible for restarting a PHY
port, primarily after a link transition. PHY ports must also all be
restarted after a reset, and the E822 devices also restart the PHY after
the .settime64 operation.
If a PHY restart occurs concurrently with a link transition, it is possible
that the link state read could be re-ordered such that the link transition
would start the PHY but a concurrent restart (such as from .settime64)
could see the old link state and decide it must stop the PHY.
This occurs because the restart procedure depends on the link state value
but setting that value is not serialized with the per-port ps_lock. A
following fix for E825 is going to modify the .setttime64 to also correctly
restart the PHY timers, opening the E825 device to a race between
.settime64 and a concurrent link change.
To avoid this, we need to ensure that the port link_up field is set within
the same critical section as the restart procedure. This way we ensure that
the end result is the PHY programmed to the correct state.
Additionally note that the PHY restart procedure already cannot run
concurrently across two ports. The procedure requires executing timer
commands which in turn require the PTP hardware semaphore.
Thus, to simplify the locking behavior, convert the per-port ps_lock to a
single mutex in the ice_adapter. Acquire this in ice_ptp_restart_all_phy(),
and hold it while calling ice_ptp_port_phy_stop() and
ice_ptp_port_phy_restart(). Modify the ice_ptp_link_change() function so
that the ptp_port-link_up state is set under the lock.
The switch to a single lock for the adapter instead of one per port is
easier to reason about. It also could be a first step in a plan to replace
the PTP hardware semaphore completely, which is under investigation for the
future.
A final note, this does "delay" the assignment of the link_up field which
is used by the Tx timestamp flow to discard timestamps that were expected
to never complete. That entire flow is modified significantly in this
series. The potential delay in reporting link_up is safe, as it is used as
an indicator but the logic must be correct even if a new request comes in
just before we report that link is down. See the following changes for more
details.
This fixes one of the issues reported by Sashiko during a previous review
of this series, linked here as the Closes tag.
Closes: https://lore.kernel.org/netdev/20260916011228.1632848-1-kuba@kernel.org/
Fixes: 3a7496234d17 ("ice: implement basic E822 PTP support")
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_adapter.h | 4 ++
drivers/net/ethernet/intel/ice/ice_ptp.h | 2 -
drivers/net/ethernet/intel/ice/ice_adapter.c | 3 ++
drivers/net/ethernet/intel/ice/ice_ptp.c | 66 ++++++++++++++++------------
4 files changed, 44 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h b/drivers/net/ethernet/intel/ice/ice_adapter.h
index 93f041943bdd..926918e9d98d 100644
--- a/drivers/net/ethernet/intel/ice/ice_adapter.h
+++ b/drivers/net/ethernet/intel/ice/ice_adapter.h
@@ -40,6 +40,7 @@ struct ice_port_list {
* @txq_ctx_lock: Spinlock protecting access to the GLCOMM_QTX_CNTX_CTL register
* @cpi_phy_lock: Per-PHY mutex serializing CPI REQ/ACK transactions.
* Index 0 = PHY0, index 1 = PHY1. Used on E825C devices.
+ * @ps_lock: Mutex to serialize PHY port start/stop across adapter.
* @ctrl_pf: Control PF of the adapter
* @ports: Ports list
* @index: 64-bit index cached for collision detection on 32bit systems
@@ -53,6 +54,9 @@ struct ice_adapter {
/* Serialize CPI REQ/ACK transactions per PHY (E825C only) */
struct mutex cpi_phy_lock[ICE_E825_MAX_PHYS];
+ /* For serializing PHY port start/stop sequences */
+ struct mutex ps_lock;
+
struct ice_pf *ctrl_pf;
struct ice_port_list ports;
u64 index;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index da2003ba3bb0..27ea502b7576 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -143,7 +143,6 @@ struct ice_ptp_tx {
* @ref: reference counter for use with adapter ports list
* @tx: Tx timestamp tracking for this port
* @ov_work: delayed work task for tracking when PHY offset is valid
- * @ps_lock: mutex used to protect the overall PTP PHY start procedure
* @link_up: indicates whether the link is up
* @tx_fifo_busy_cnt: number of times the Tx FIFO was busy
* @port_num: the port number this structure represents
@@ -155,7 +154,6 @@ struct ice_ptp_port {
struct kref ref;
struct ice_ptp_tx tx;
struct kthread_delayed_work ov_work;
- struct mutex ps_lock; /* protects overall PTP PHY start procedure */
bool link_up;
u8 tx_fifo_busy_cnt;
u8 port_num;
diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c b/drivers/net/ethernet/intel/ice/ice_adapter.c
index 84ac5ee5a739..cdfca26e8631 100644
--- a/drivers/net/ethernet/intel/ice/ice_adapter.c
+++ b/drivers/net/ethernet/intel/ice/ice_adapter.c
@@ -71,6 +71,8 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev)
INIT_LIST_HEAD(&adapter->ports.list);
init_srcu_struct(&adapter->ports.srcu);
+ mutex_init(&adapter->ps_lock);
+
return adapter;
}
@@ -81,6 +83,7 @@ static void ice_adapter_free(struct ice_adapter *adapter)
mutex_destroy(&adapter->cpi_phy_lock[i]);
cleanup_srcu_struct(&adapter->ports.srcu);
+ mutex_destroy(&adapter->ps_lock);
kfree(adapter);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 9881a7a9e570..4422588472d0 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -1191,6 +1191,8 @@ static void ice_ptp_wait_for_offsets(struct kthread_work *work)
/**
* ice_ptp_port_phy_stop - Stop timestamping for a PHY port
* @ptp_port: PTP port to stop
+ *
+ * Context: must hold the adapter ps_lock.
*/
static int
ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
@@ -1200,7 +1202,7 @@ ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
struct ice_hw *hw = &pf->hw;
int err;
- mutex_lock(&ptp_port->ps_lock);
+ lockdep_assert_held(&pf->adapter->ps_lock);
switch (hw->mac_type) {
case ICE_MAC_E810:
@@ -1222,8 +1224,6 @@ ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
dev_err(ice_pf_to_dev(pf), "PTP failed to set PHY port %d down, err %d\n",
port, err);
- mutex_unlock(&ptp_port->ps_lock);
-
return err;
}
@@ -1234,6 +1234,8 @@ ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
* Start the PHY timestamping block, and initiate Vernier timestamping
* calibration. If timestamping cannot be calibrated (such as if link is down)
* then disable the timestamping block instead.
+ *
+ * Context: must hold the adapter ps_lock.
*/
static int
ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
@@ -1244,11 +1246,11 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
unsigned long flags;
int err;
+ lockdep_assert_held(&pf->adapter->ps_lock);
+
if (!ptp_port->link_up)
return ice_ptp_port_phy_stop(ptp_port);
- mutex_lock(&ptp_port->ps_lock);
-
switch (hw->mac_type) {
case ICE_MAC_E810:
case ICE_MAC_E830:
@@ -1290,8 +1292,6 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
dev_err(ice_pf_to_dev(pf), "PTP failed to set PHY port %d up, err %d\n",
port, err);
- mutex_unlock(&ptp_port->ps_lock);
-
return err;
}
@@ -1310,12 +1310,13 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
ptp_port = &pf->ptp.port;
- /* Update cached link status for this port immediately */
- ptp_port->link_up = linkup;
-
/* Skip HW writes if reset is in progress */
- if (pf->hw.reset_ongoing)
+ if (pf->hw.reset_ongoing) {
+ mutex_lock(&pf->adapter->ps_lock);
+ ptp_port->link_up = linkup;
+ mutex_unlock(&pf->adapter->ps_lock);
return;
+ }
if (hw->mac_type == ICE_MAC_GENERIC_3K_E825 &&
test_bit(ICE_FLAG_DPLL, pf->flags)) {
@@ -1354,21 +1355,31 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
ice_txclk_update_and_notify(pf);
}
+ mutex_lock(&pf->adapter->ps_lock);
+
+ /* Update link_up under lock to ensure a concurrent restart attempt
+ * can't re-order the read and end up stopping the PHY after we start
+ * it due to a link transition.
+ */
+ ptp_port->link_up = linkup;
+
switch (hw->mac_type) {
case ICE_MAC_E810:
case ICE_MAC_E830:
/* Do not reconfigure E810 or E830 PHY */
- return;
+ break;
case ICE_MAC_GENERIC:
ice_ptp_port_phy_restart(ptp_port);
- return;
+ break;
case ICE_MAC_GENERIC_3K_E825:
if (linkup)
ice_ptp_port_phy_restart(ptp_port);
- return;
+ break;
default:
dev_warn(ice_pf_to_dev(pf), "%s: Unknown PHY type\n", __func__);
}
+
+ mutex_unlock(&pf->adapter->ps_lock);
}
/**
@@ -1434,18 +1445,11 @@ static int ice_ptp_cfg_phy_interrupt(struct ice_pf *pf, bool ena, u32 threshold)
}
}
-/**
- * ice_ptp_reset_phy_timestamping - Reset PHY timestamping block
- * @pf: Board private structure
- */
-static void ice_ptp_reset_phy_timestamping(struct ice_pf *pf)
-{
- ice_ptp_port_phy_restart(&pf->ptp.port);
-}
-
/**
* ice_ptp_restart_all_phy - Restart all PHYs to recalibrate timestamping
* @pf: Board private structure
+ *
+ * Context: acquires the adapter ps_lock
*/
static void ice_ptp_restart_all_phy(struct ice_pf *pf)
{
@@ -1453,6 +1457,8 @@ static void ice_ptp_restart_all_phy(struct ice_pf *pf)
struct ice_ptp_port *port;
int srcu_idx;
+ mutex_lock(&pf->adapter->ps_lock);
+
srcu_idx = srcu_read_lock(&ports->srcu);
list_for_each_entry_srcu(port, &ports->list, list_node,
srcu_read_lock_held(&ports->srcu)) {
@@ -1465,6 +1471,8 @@ static void ice_ptp_restart_all_phy(struct ice_pf *pf)
kref_put(&port->ref, ice_ptp_release_port_srcu);
}
srcu_read_unlock(&ports->srcu, srcu_idx);
+
+ mutex_unlock(&pf->adapter->ps_lock);
}
/**
@@ -3329,8 +3337,6 @@ static int ice_ptp_init_port(struct ice_pf *pf, struct ice_ptp_port *ptp_port)
{
struct ice_hw *hw = &pf->hw;
- mutex_init(&ptp_port->ps_lock);
-
switch (hw->mac_type) {
case ICE_MAC_E810:
case ICE_MAC_E830:
@@ -3437,7 +3443,9 @@ void ice_ptp_init(struct ice_pf *pf)
goto err_clean_pf;
/* Start the PHY timestamping block */
- ice_ptp_reset_phy_timestamping(pf);
+ mutex_lock(&pf->adapter->ps_lock);
+ ice_ptp_port_phy_restart(&ptp->port);
+ mutex_unlock(&pf->adapter->ps_lock);
/* Configure initial Tx interrupt settings */
ice_ptp_cfg_tx_interrupt(pf);
@@ -3452,7 +3460,6 @@ void ice_ptp_init(struct ice_pf *pf)
return;
err_clean_pf:
- mutex_destroy(&ptp->port.ps_lock);
ice_ptp_cleanup_pf(pf);
err_exit:
/* If we registered a PTP clock, release it */
@@ -3480,7 +3487,6 @@ void ice_ptp_release(struct ice_pf *pf)
return;
if (pf->ptp.state != ICE_PTP_READY) {
- mutex_destroy(&pf->ptp.port.ps_lock);
ice_ptp_cleanup_pf(pf);
if (pf->ptp.clock) {
ptp_clock_unregister(pf->ptp.clock);
@@ -3502,8 +3508,10 @@ void ice_ptp_release(struct ice_pf *pf)
kthread_cancel_delayed_work_sync(&pf->ptp.work);
+ mutex_lock(&pf->adapter->ps_lock);
ice_ptp_port_phy_stop(&pf->ptp.port);
- mutex_destroy(&pf->ptp.port.ps_lock);
+ mutex_unlock(&pf->adapter->ps_lock);
+
if (pf->ptp.kworker) {
kthread_destroy_worker(pf->ptp.kworker);
pf->ptp.kworker = NULL;
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 03/15] ice: fix removal of PTP timestamp tracker during reset
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 02/15] ice: fix PHY port restart serialization Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 04/15] ice: set in_use only after preparing Tx timestamp index Jacob Keller
` (13 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller
Commit 7a25fe5cd5fb ("ice: stop destroying and reinitalizing Tx tracker
during reset") intended to modify the PTP reset flow of the driver so that
it stopped calling ice_ptp_reset_tx_tracker() during teardown and stopped
calling ice_ptp_init_tx_*() during rebuild.
Unfortunately, the commit only removed the calls to ice_ptp_init_tx_*().
This fixed a memory leak in PF reset. However, now a CORE, GLOBAL, or EMP
reset will leave the device unable to initiate Tx timestamp requests
indefinitely.
In practice the CORE and GLOBAL resets rarely happen in production
environments, while EMP resets happen after a firmware update that is often
followed by a platform reboot. This explains why this has not been caught
until now. However, it is trivial to verify by triggering the reset from
userspace via ethtool. For ice the following command will trigger a GLOBAL
reset:
$ ethtool --reset eno8303np0 irq-shared dma-shared filter-shared \
offload-shared ram-shared mac-shared phy-shared
Remove the call of ice_ptp_release_tx_tracker() from
ice_ptp_prepare_for_reset(), to keep the tracker memory in place so that
timestamping can resume after a reset.
During review of a previous version of this change, sashiko pointed out
that the teardown flows for ice_ptp_init() and ice_ptp_release() could
potentially leak the PTP timestamp tracker. Fix ice_ptp_init() so that it
correctly calls ice_ptp_release_tx_tracker() as part of its teardown on
exit. Fix ice_ptp_release() so that it handles the cleanup if PTP is in the
error state by cancelling the kworker items and releasing the Tx tracker as
appropriate.
This was found by Sashiko review during feedback for an unrelated change,
and iterated based on further feedback from Sashiko after the initial fix
to remove the call to ice_ptp_release_tx_tracker();
Closes: https://sashiko.dev/#/patchset/20260821-jk-e825c-minimized-fixes-v1-0-9d0731eb4858%40intel.com?part=8
Closes: https://lore.kernel.org/netdev/20260916011213.1632286-1-kuba@kernel.org/
Fixes: 7a25fe5cd5fb ("ice: stop destroying and reinitalizing Tx tracker during reset")
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 4422588472d0..2bb9beb94806 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -2996,8 +2996,6 @@ void ice_ptp_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type)
if (ice_pf_src_tmr_owned(pf) && hw->mac_type == ICE_MAC_GENERIC_3K_E825)
ice_ptp_prepare_rebuild_sec(pf, false, reset_type);
- ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
-
/* Disable periodic outputs */
ice_ptp_disable_all_perout(pf);
@@ -3454,11 +3452,13 @@ void ice_ptp_init(struct ice_pf *pf)
err = ice_ptp_init_work(pf, ptp);
if (err)
- goto err_exit;
+ goto err_release_tx_tracker;
dev_info(ice_pf_to_dev(pf), "PTP init successful\n");
return;
+err_release_tx_tracker:
+ ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
err_clean_pf:
ice_ptp_cleanup_pf(pf);
err_exit:
@@ -3488,6 +3488,14 @@ void ice_ptp_release(struct ice_pf *pf)
if (pf->ptp.state != ICE_PTP_READY) {
ice_ptp_cleanup_pf(pf);
+ ice_ptp_release_tx_tracker(pf, &pf->ptp.port.tx);
+ if (pf->ptp.kworker) {
+ kthread_cancel_delayed_work_sync(&pf->ptp.work);
+ if (pf->hw.mac_type == ICE_MAC_GENERIC)
+ kthread_cancel_delayed_work_sync(&pf->ptp.port.ov_work);
+ kthread_destroy_worker(pf->ptp.kworker);
+ pf->ptp.kworker = NULL;
+ }
if (pf->ptp.clock) {
ptp_clock_unregister(pf->ptp.clock);
pf->ptp.clock = NULL;
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 04/15] ice: set in_use only after preparing Tx timestamp index
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (2 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 03/15] ice: fix removal of PTP timestamp tracker during reset Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 05/15] ice: E822: keep Tx timestamps disabled during offset calibration Jacob Keller
` (12 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller
The ice_ptp_request_ts() function is used to request a timestamp index for
use with a packet. When reserving an index, it sets the start time and
saves a pointer to the skb into the appropriate index. The function marks
the in_use bit first before doing any of these steps. The IRQ handler which
clears the timestamps reads the in_use bits uses a lockless flow for
reading the in_use bits to determine which ones are in-use. This is
necessary as actually processing a complete timestamp must be able to sleep
so we cannot hold the timestamp tracker lock over the entire sequence.
Additionally, blocking the Tx hotpath with such a lock indefinitely would
be problematic.
However, the existing flow now has a very narrow window where the IRQ
handler could see a timestamp as in-use but read a stale value for its
"start" time.
Fix this by ordering the sequence to mark the in_use bit last, and add a
memory barrier to prevent re-ordering of the previous writes to setup the
index.
This was found and reported by Sashiko while reviewing an unrelated change.
Closes: https://sashiko.dev/#/patchset/20260821-jk-e825c-minimized-fixes-v1-0-9d0731eb4858%40intel.com?part=7
Fixes: ea9b847cda64 ("ice: enable transmit timestamps for E810 devices")
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 2bb9beb94806..1bcc78d08d2f 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -592,6 +592,9 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
bool drop_ts = !link_up;
struct sk_buff *skb;
+ /* Prevent speculative re-ordering of start and skb */
+ smp_rmb();
+
/* Drop packets which have waited for more than 2 seconds */
if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) {
drop_ts = true;
@@ -2677,11 +2680,13 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb)
* a reference to the skb and the start time to allow discarding old
* requests.
*/
- set_bit(idx, tx->in_use);
- clear_bit(idx, tx->stale);
tx->tstamps[idx].start = jiffies;
tx->tstamps[idx].skb = skb_get(skb);
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ clear_bit(idx, tx->stale);
+ /* Ensure index is setup before marking it as used */
+ smp_mb__before_atomic();
+ set_bit(idx, tx->in_use);
ice_trace(tx_tstamp_request, skb, idx);
}
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 05/15] ice: E822: keep Tx timestamps disabled during offset calibration
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (3 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 04/15] ice: set in_use only after preparing Tx timestamp index Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 06/15] ice: E822: flush offset verification work during reset preparation Jacob Keller
` (11 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Aleksandr Loktionov, Arkadiusz Kubalewski,
Przemyslaw Korba
From: Karol Kolacinski <karol.kolacinski@intel.com>
Do not clear the tx.calibrating flag immediately after starting the PHY
timer in ice_ptp_port_phy_restart(). Instead, keep Tx timestamps
disabled until the offset verification work (ice_ptp_wait_for_offsets)
has confirmed that the Tx PHY offset is properly configured.
Previously, tx.calibrating was set to true, then immediately back to
false right after ice_start_phy_timer_e82x() returned. This allowed Tx
timestamp requests to be served during the window where offset
verification was still pending. Timestamps produced during this window
should have their valid bit cleared, but it is better to prevent requests
until the driver has confirmed calibration is complete.
Move the tx.calibrating = false to ice_ptp_wait_for_offsets(), after
Tx offset configuration has completed successfully. This ensures that new
Tx timestamp requests are only accepted after the PHY has properly
calibrated the Tx offset.
If ice_start_phy_timer_e82x() fails, do not restore calibrating to false.
The device is in a state where timestamps cannot succeed properly anyways.
A dev_err message is already logged on failure to start the timer at the
end of the function.
Log a debug message while offset calibration is still pending, including
the specific Tx/Rx error codes to aid debugging stalled calibration.
This path is expected on every routine link-up: ov_work is first queued
with no delay and the vernier offset cannot be computed until at least
one packet has been transmitted, so the first several invocations
normally land here. Use dev_dbg() rather than a rate-limited warning to
avoid emitting KERN_WARNING on every link-up during normal operation.
Log a debug message when calibration completes successfully.
Note that sashiko review has previously complained that leaving the
calibrating flag enabled "permanently" disables Tx timestamps if vernier
calibration never completes. This is true, but its important to realize
that timestamps would still fail regardless of whether the flag is set.
Until vernier calibration completes the device will not report valid
timestamps regardless. Thus, keeping the calibrating flag set simply
prevents new timestamp requests from software while it is known that the
hardware will not complete them.
Fixes: 3a7496234d17 ("ice: implement basic E822 PTP support")
Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.h | 2 +-
drivers/net/ethernet/intel/ice/ice_ptp.c | 29 +++++++++++++++++++++++------
2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index 27ea502b7576..029ee4612d76 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -107,7 +107,7 @@ enum ice_tx_tstamp_work {
* @len: length of the tstamps and in_use fields.
* @init: if true, the tracker is initialized;
* @calibrating: if true, the PHY is calibrating the Tx offset. During this
- * window, timestamps are temporarily disabled.
+ * window, timestamp requests are disabled.
* @has_ready_bitmap: if true, the hardware has a valid Tx timestamp ready
* bitmap register. If false, fall back to verifying new
* timestamp values against previously cached copy.
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 1bcc78d08d2f..364f0f389d85 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -1161,6 +1161,7 @@ static int ice_ptp_check_tx_fifo(struct ice_ptp_port *port)
static void ice_ptp_wait_for_offsets(struct kthread_work *work)
{
struct ice_ptp_port *port;
+ unsigned long flags;
struct ice_pf *pf;
struct ice_hw *hw;
int tx_err;
@@ -1181,9 +1182,26 @@ static void ice_ptp_wait_for_offsets(struct kthread_work *work)
tx_err = ice_ptp_check_tx_fifo(port);
if (!tx_err)
tx_err = ice_phy_cfg_tx_offset_e82x(hw, port->port_num);
+ if (!tx_err) {
+ /* Tx offset has been configured, re-enable Tx timestamps */
+ spin_lock_irqsave(&port->tx.lock, flags);
+ if (port->tx.calibrating) {
+ port->tx.calibrating = false;
+ dev_dbg(ice_pf_to_dev(pf), "PTP Tx offset valid for port %u, Tx timestamps enabled\n",
+ port->port_num);
+ }
+ spin_unlock_irqrestore(&port->tx.lock, flags);
+ }
+
rx_err = ice_phy_cfg_rx_offset_e82x(hw, port->port_num);
if (tx_err || rx_err) {
- /* Tx and/or Rx offset not yet configured, try again later */
+ /* Tx and/or Rx offset not yet configured, try again later.
+ * This is expected during normal link-up: the vernier offset
+ * calibration cannot complete until at least one packet has
+ * been transmitted, so the first retries routinely land here.
+ */
+ dev_dbg(ice_pf_to_dev(pf), "PTP offset not yet valid for port %u (tx_err=%d rx_err=%d)\n",
+ port->port_num, tx_err, rx_err);
kthread_queue_delayed_work(pf->ptp.kworker,
&port->ov_work,
msecs_to_jiffies(100));
@@ -1276,11 +1294,10 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
if (err)
break;
- /* Enable Tx timestamps right away */
- spin_lock_irqsave(&ptp_port->tx.lock, flags);
- ptp_port->tx.calibrating = false;
- spin_unlock_irqrestore(&ptp_port->tx.lock, flags);
-
+ /* Do not clear calibrating flag here. Tx timestamp requests
+ * remain disabled until ice_ptp_wait_for_offsets() has
+ * verified that the Tx offset calibration has completed.
+ */
kthread_queue_delayed_work(pf->ptp.kworker, &ptp_port->ov_work,
0);
break;
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 06/15] ice: E822: flush offset verification work during reset preparation
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (4 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 05/15] ice: E822: keep Tx timestamps disabled during offset calibration Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 07/15] ice: call PTP link change only from link events Jacob Keller
` (10 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Aleksandr Loktionov, Arkadiusz Kubalewski,
Przemyslaw Korba, Maciek Machnikowski
From: Karol Kolacinski <karol.kolacinski@intel.com>
The offset verification work task for E822 reschedules itself if the device
driver is resetting. However, it may already be executing and attempting to
access the PHY when a reset has begun. Flush the work to ensure that any
current execution has completed and will see the updated reset state. This
avoids the potential accesses to the PHY state while resetting. It is also
simpler than attempts to cancel the work which could lead to a race between
the clock owner PF and the port PF.
Fixes: 95af1f1c4c9f ("ice: reschedule ice_ptp_wait_for_offset_valid during reset")
Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 364f0f389d85..fccea0511eb2 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -3015,6 +3015,9 @@ void ice_ptp_prepare_for_reset(struct ice_pf *pf, enum ice_reset_req reset_type)
if (reset_type == ICE_RESET_PFR)
return;
+ if (hw->mac_type == ICE_MAC_GENERIC)
+ kthread_flush_work(&ptp->port.ov_work.work);
+
if (ice_pf_src_tmr_owned(pf) && hw->mac_type == ICE_MAC_GENERIC_3K_E825)
ice_ptp_prepare_rebuild_sec(pf, false, reset_type);
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 07/15] ice: call PTP link change only from link events
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (5 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 06/15] ice: E822: flush offset verification work during reset preparation Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 08/15] ice: E825: stop clearing PHY_REG_TX_OFFSET_READY Jacob Keller
` (9 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Arkadiusz Kubalewski, Aleksandr Loktionov,
Przemyslaw Korba, Petr Oros, Maciek Machnikowski
From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Remove redundant ice_ptp_link_change() calls from ice_up_complete() and
ice_down(). These duplicate the call already made from
ice_handle_link_event(), creating three problems:
1. Double initialization on link-up: ice_handle_link_event() calls
ice_ptp_link_change(true), then ice_up_complete() calls it again.
The second call re-enters ice_ptp_port_phy_restart(), re-setting the
calibrating flag and restarting the PHY timer while the first
invocation's offset verification work (ov_work) may still be running.
2. Premature cleanup on administrative down: ice_down() calls
ice_ptp_link_change(false) during ifconfig down or reset preparation,
even when the physical link is still up. This clears timestamp state
unnecessarily and can interfere with ongoing PTP operations.
3. Ordering dependency: ice_down()/ice_up_complete() are called during
reset sequences where PTP may not be fully initialized, creating
edge cases with partially configured state.
The link event handler is the correct and sufficient place to drive PTP
link state changes, as it reflects actual physical link transitions. Remove
the calls of ice_ptp_link_change from the ice_down()/ice_up() flows.
Initialize the link_up in ice_ptp_init() and ensure that we check and
restore the link status at the end of the rebuild flow, ensuring that we
initialize the PHY timer appropriately after a reset.
Fixes: 6b1ff5d39228 ("ice: always call ice_ptp_link_change and make it void")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
Signed-off-by: Petr Oros <poros@redhat.com>
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_main.c | 10 +++++--
drivers/net/ethernet/intel/ice/ice_ptp.c | 44 ++++++++++++++++++++++---------
2 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index d88835482d3a..f32041dd8b27 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -6745,7 +6745,6 @@ static int ice_up_complete(struct ice_vsi *vsi)
ice_print_link_msg(vsi, true);
netif_tx_start_all_queues(vsi->netdev);
netif_carrier_on(vsi->netdev);
- ice_ptp_link_change(pf, true);
}
/* Perform an initial read of the statistics registers now to
@@ -7273,7 +7272,6 @@ int ice_down(struct ice_vsi *vsi)
if (vsi->netdev) {
vlan_err = ice_vsi_del_vlan_zero(vsi);
- ice_ptp_link_change(vsi->back, false);
netif_carrier_off(vsi->netdev);
netif_tx_disable(vsi->netdev);
}
@@ -7794,6 +7792,14 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type)
ice_update_pf_netdev_link(pf);
+ if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags) && pf->hw.port_info) {
+ bool link_up;
+
+ link_up = !!(pf->hw.port_info->phy.link_info.link_info &
+ ICE_AQ_LINK_UP);
+ ice_ptp_link_change(pf, link_up);
+ }
+
/* tell the firmware we are up */
err = ice_send_version(pf);
if (err) {
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index fccea0511eb2..328e90dc51aa 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -583,7 +583,7 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
}
/* Drop packets if the link went down */
- link_up = ptp_port->link_up;
+ link_up = READ_ONCE(ptp_port->link_up);
for_each_set_bit(idx, tx->in_use, tx->len) {
struct skb_shared_hwtstamps shhwtstamps = {};
@@ -1269,7 +1269,7 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
lockdep_assert_held(&pf->adapter->ps_lock);
- if (!ptp_port->link_up)
+ if (!READ_ONCE(ptp_port->link_up))
return ice_ptp_port_phy_stop(ptp_port);
switch (hw->mac_type) {
@@ -1333,7 +1333,7 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
/* Skip HW writes if reset is in progress */
if (pf->hw.reset_ongoing) {
mutex_lock(&pf->adapter->ps_lock);
- ptp_port->link_up = linkup;
+ WRITE_ONCE(ptp_port->link_up, linkup);
mutex_unlock(&pf->adapter->ps_lock);
return;
}
@@ -1381,7 +1381,7 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
* can't re-order the read and end up stopping the PHY after we start
* it due to a link transition.
*/
- ptp_port->link_up = linkup;
+ WRITE_ONCE(ptp_port->link_up, linkup);
switch (hw->mac_type) {
case ICE_MAC_E810:
@@ -1485,7 +1485,7 @@ static void ice_ptp_restart_all_phy(struct ice_pf *pf)
if (!kref_get_unless_zero(&port->ref))
continue;
- if (port->link_up)
+ if (READ_ONCE(port->link_up))
ice_ptp_port_phy_restart(port);
kref_put(&port->ref, ice_ptp_release_port_srcu);
@@ -3322,9 +3322,13 @@ static int ice_ptp_init_owner(struct ice_pf *pf)
}
/**
- * ice_ptp_init_work - Initialize PTP work threads
+ * ice_ptp_init_work - Initialize the PTP kworker
* @pf: Board private structure
* @ptp: PF PTP structure
+ *
+ * Allocate the kworker and initialize the periodic work function. The
+ * periodic work is not queued here; the caller starts it once the PTP
+ * state is ICE_PTP_READY.
*/
static int ice_ptp_init_work(struct ice_pf *pf, struct ice_ptp *ptp)
{
@@ -3343,9 +3347,6 @@ static int ice_ptp_init_work(struct ice_pf *pf, struct ice_ptp *ptp)
ptp->kworker = kworker;
- /* Start periodic work going */
- kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0);
-
return 0;
}
@@ -3465,8 +3466,24 @@ void ice_ptp_init(struct ice_pf *pf)
if (err)
goto err_clean_pf;
- /* Start the PHY timestamping block */
+ /* Create the kworker before restarting the PHY, which queues work on
+ * it in the E82x restart path. This prevents concurrent link events
+ * from reaching ice_ptp_port_phy_restart() while kworker is still NULL
+ */
+ err = ice_ptp_init_work(pf, ptp);
+ if (err)
+ goto err_release_tx_tracker;
+
+ /* Seed link_up from current PHY status, since link may already be up
+ * (e.g. after PXE boot) with no link-change edge to catch it later.
+ */
mutex_lock(&pf->adapter->ps_lock);
+ if (pf->hw.port_info)
+ WRITE_ONCE(ptp->port.link_up,
+ !!(pf->hw.port_info->phy.link_info.link_info &
+ ICE_AQ_LINK_UP));
+
+ /* Start the PHY timestamping block */
ice_ptp_port_phy_restart(&ptp->port);
mutex_unlock(&pf->adapter->ps_lock);
@@ -3475,9 +3492,10 @@ void ice_ptp_init(struct ice_pf *pf)
ptp->state = ICE_PTP_READY;
- err = ice_ptp_init_work(pf, ptp);
- if (err)
- goto err_release_tx_tracker;
+ /* Start periodic work only after the state is READY; the worker
+ * returns without rescheduling while the state is not READY.
+ */
+ kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0);
dev_info(ice_pf_to_dev(pf), "PTP init successful\n");
return;
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 08/15] ice: E825: stop clearing PHY_REG_TX_OFFSET_READY
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (6 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 07/15] ice: call PTP link change only from link events Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset Jacob Keller
` (8 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Maciej Machnikowski
The ice_stop_phy_timer_eth56g() function is called by the driver for E825
devices to ensure that the PHY timer has been stopped. The equivalent
function for older E822 devices performed many steps. However, on E825 it
only clears the PHY_REG_TX_OFFSET_READY and PHY_REG_RX_OFFSET_READY bits to
indicate to HW that it should no longer treat the PHY offset as valid.
When PHY_REG_TX_OFFSET_READY is cleared, the hardware still captures Tx
timestamps, but it no longer sets the valid bit for these timestamps. This
sounds reasonable at first glance. However, this results in the internal
outstanding timestamp counter becoming out of sync.
When capturing a timestamp, hardware increments its internal counter and
sets the associated "ready" bit in the timestamp memory status. Then it
compares the timestamp count to the threshold to determine if it should
trigger an interrupt to the MAC.
Upon reading the timestamp hardware is supposed to decrement the counter,
clear the valid bit, and clear the associated bit from the memory status
register. However, it only performs these steps *if* the valid bit is set.
Since the valid bit is not set while PHY_REG_TX_OFFSET_READY is clear, the
timestamp counter is not decremented and the memory status is not cleared.
This leaves the counter out-of-sync until a PHY soft reset.
According to the hardware engineers, the PHY_REG_TX_OFFSET_READY bit has no
other effects. It only controls whether hardware captures timestamps with
the valid bit set or not. Since capturing timestamps with the valid bit
clear is problematic, they recommend simply not clearing
PHY_REG_TX_OFFSET_READY.
Note that the PHY_REG_RX_OFFSET_READY performs a similar task. However,
clearing it is fine as there is no associated timestamp counter on the Rx
side. Receive timestamps are simply inserted into the descriptor. Clearing
this register clears the valid bit for timestamps until we complete
calibration and re-enable the register.
Notice that the soft_reset parameter of ice_stop_phy_timer_eth56g() is
totally unused. It is a relic from a copy-paste of
ice_stop_phy_timer_e82x() that is unnecessary, so remove it.
Now that we do not clear the PHY_REG_TX_OFFSET_READY, new timestamp
requests could happen while the PHY is calibrating. To avoid this, set the
tx.calibrating field of the Tx timestamp tracker when stopping the timer
and clear it when finishing the restart. This ensures that any new requests
will be rejected until the PHY timer calibration has completed. Unlike E822
devices, set the calibrating flag when "stopping" the PHY and clear it
immediately after the start procedure. The E825 device does not perform
vernier calibration and thus does not need to wait for hardware to mark the
offsets as valid.
Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
Suggested-by: Maciej Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 2 +-
drivers/net/ethernet/intel/ice/ice_ptp.c | 24 +++++++++++++++++++++++-
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 21 +++++++++++----------
3 files changed, 35 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 16b1988e993d..17000df77ce9 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -378,7 +378,7 @@ int ice_cgu_get_output_pin_state_caps(struct ice_hw *hw, u8 pin_id,
/* ETH56G family functions */
int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status);
-int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset);
+int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port);
int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port);
int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold);
int ice_phy_cfg_ptp_1step_eth56g(struct ice_hw *hw, u8 port);
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 328e90dc51aa..657cd78ec738 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -1221,6 +1221,7 @@ ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
struct ice_pf *pf = ptp_port_to_pf(ptp_port);
u8 port = ptp_port->port_num;
struct ice_hw *hw = &pf->hw;
+ unsigned long flags;
int err;
lockdep_assert_held(&pf->adapter->ps_lock);
@@ -1236,7 +1237,12 @@ ice_ptp_port_phy_stop(struct ice_ptp_port *ptp_port)
err = ice_stop_phy_timer_e82x(hw, port, true);
break;
case ICE_MAC_GENERIC_3K_E825:
- err = ice_stop_phy_timer_eth56g(hw, port, true);
+ /* Disable new Tx timestamp requests */
+ spin_lock_irqsave(&ptp_port->tx.lock, flags);
+ ptp_port->tx.calibrating = true;
+ spin_unlock_irqrestore(&ptp_port->tx.lock, flags);
+
+ err = ice_stop_phy_timer_eth56g(hw, port);
break;
default:
err = -ENODEV;
@@ -1302,7 +1308,23 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port)
0);
break;
case ICE_MAC_GENERIC_3K_E825:
+ /* ice_ptp_port_phy_stop() may have already disabled
+ * timestamps, but some restarts occur without first stopping
+ * the timer, so we ensure that new requests are disabled
+ * here.
+ */
+ spin_lock_irqsave(&ptp_port->tx.lock, flags);
+ ptp_port->tx.calibrating = true;
+ spin_unlock_irqrestore(&ptp_port->tx.lock, flags);
+
err = ice_start_phy_timer_eth56g(hw, port);
+ if (err)
+ break;
+
+ spin_lock_irqsave(&ptp_port->tx.lock, flags);
+ ptp_port->tx.calibrating = false;
+ spin_unlock_irqrestore(&ptp_port->tx.lock, flags);
+
break;
default:
err = -ENODEV;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index 3a41c711e751..c8a67a307832 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -2101,24 +2101,25 @@ static int ice_sync_phy_timer_eth56g(struct ice_hw *hw, u8 port)
* ice_stop_phy_timer_eth56g - Stop the PHY clock timer
* @hw: pointer to the HW struct
* @port: the PHY port to stop
- * @soft_reset: if true, hold the SOFT_RESET bit of PHY_REG_PS
*
- * Stop the clock of a PHY port. This must be done as part of the flow to
- * re-calibrate Tx and Rx timestamping offsets whenever the clock time is
- * initialized or when link speed changes.
+ * Disable Rx timestamping by clearing the PHY_REG_RX_OFFSET_READY. This
+ * causes Rx timestamps to be captured with their valid bit clear, ensuring we
+ * discard any timestamp captured while the PHY is being recalibrated.
+ *
+ * Note this does *not* clear PHY_REG_TX_OFFSET_READY. Clearing it would
+ * cause the Tx timestamps to be captured with their valid bit clear.
+ * Unfortunately the captured timestamps still increment the internal counter
+ * and result in off-by-one accounting. Instead, Tx timestamp requests should
+ * be disabled by other means.
*
* Return:
* * %0 - success
* * %other - failed to write to PHY
*/
-int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset)
+int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port)
{
int err;
- err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TX_OFFSET_READY, 0);
- if (err)
- return err;
-
err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_RX_OFFSET_READY, 0);
if (err)
return err;
@@ -2151,7 +2152,7 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port)
tmr_idx = ice_get_ptp_src_clock_index(hw);
- err = ice_stop_phy_timer_eth56g(hw, port, false);
+ err = ice_stop_phy_timer_eth56g(hw, port);
if (err)
return err;
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (7 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 08/15] ice: E825: stop clearing PHY_REG_TX_OFFSET_READY Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-23 9:41 ` Loktionov, Aleksandr
2026-09-22 18:02 ` [PATCH iwl-net v2 10/15] ice: E825: perform a soft reset when starting the PHY timer Jacob Keller
` (7 subsequent siblings)
16 siblings, 1 reply; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Maciek Machnikowski
The current implementation of ice_ptp_reset_ts_memory_eth56g() is flawed.
It tries to clear the timestamp memory by writing to the
PHY_REG_TX_MEMORY_STATUS region. This does not work properly, as it does
not trigger appropriate PHY actions.
To clear outstanding timestamp memory, the driver must read the timestamps.
However, naively doing this as part of ice_ptp_reset_ts_memory() is
problematic. When reading the timestamp index, hardware kicks off a chain
of actions including clearing the ready bitmap index, and decrementing an
internal counter if the timestamp index was marked as valid.
This can potentially leave the internal hardware counter out of sync with
the actual number of timestamps. This occurs because the
PHY_REG_TX_MEMORY_STATUS region is not zero-initialized when the device
boots up. Instead, it is filled with garbage. On a cold power on, attempts
to read the stale data result in the hardware triggering a counter
decrement for a timestamp that never happened. This underflows the counter,
and prevents new timestamp interrupts from being triggered for real
timestamp requests.
We must read the PHY_REG_TX_MEMORY_STATUS in order to clear stale
timestamps. But doing so may cause a desync with the counter. To prevent
issues, perform this clearing always and only right before initiating a PHY
soft reset.
The soft reset will clear and reset the internal counter and the ready
bitmap. The reads to PHY_REG_TX_MEMORY_STATUS will reset the region valid
bits ensuring that no stale data is left behind. This combination ensures
that we always have a clean slate with no stale data and with the counter
properly reset to zero.
If a read fails (i.e. due to a transient sideband queue failure) it is not
treated as a fatal error. There is already a dynamic debug message for each
read failure. Keep track of the total number of timestamp registers that
fail for a given port and log a warning message indicating the total number
of failures. Continuing is acceptable as clearing the memory is a
precaution against faulty software. Correct software access won't read the
index twice and won't read it at all except when it has already confirmed
that the index is valid by reading ice_get_phy_tx_tstamp_ready().
Fixes: 3ec46e157c7f ("ice: perform PHY soft reset for E825C ports at initialization")
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 100 +++++++++++++++-------------
1 file changed, 52 insertions(+), 48 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index c8a67a307832..e1a5ff5793d1 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -737,24 +737,6 @@ static int ice_read_port_mem_eth56g(struct ice_hw *hw, u8 port, u16 offset,
return ice_read_port_eth56g(hw, port, offset, val, ETH56G_PHY_MEM_PTP);
}
-/**
- * ice_write_port_mem_eth56g - Write a PHY port memory location
- * @hw: pointer to the HW struct
- * @port: Port number to be read
- * @offset: Offset from PHY port register base
- * @val: Pointer to the value to read (out param)
- *
- * Return:
- * * %0 - success
- * * %EINVAL - invalid port number or resource type
- * * %other - failed to write to PHY
- */
-static int ice_write_port_mem_eth56g(struct ice_hw *hw, u8 port, u16 offset,
- u32 val)
-{
- return ice_write_port_eth56g(hw, port, offset, val, ETH56G_PHY_MEM_PTP);
-}
-
/**
* ice_write_quad_ptp_reg_eth56g - Write a PHY quad register
* @hw: pointer to the HW struct
@@ -1134,16 +1116,12 @@ static int ice_read_ptp_tstamp_eth56g(struct ice_hw *hw, u8 port, u8 idx,
* @port: the quad to read from
* @idx: the timestamp index to reset
*
- * Read and then forcibly clear the timestamp index to ensure the valid bit is
- * cleared and the timestamp status bit is reset in the PHY port memory of
- * internal PHYs of the 56G devices.
+ * Read the timestamp index to ensure that the valid bit is cleared and the
+ * timestamp status bit is reset in the PHY port memory.
*
- * To directly clear the contents of the timestamp block entirely, discarding
- * all timestamp data at once, software should instead use
- * ice_ptp_reset_ts_memory_quad_eth56g().
- *
- * This function should only be called on an idx whose bit is set according to
- * ice_get_phy_tx_tstamp_ready().
+ * This function should only be called on an index whose bit is set according
+ * to ice_get_phy_tx_tstamp_ready(), or as part of a full sweep when paired
+ * with a PHY soft reset via ice_ptp_phy_soft_reset_eth56g().
*
* Return:
* * %0 - success
@@ -1152,24 +1130,16 @@ static int ice_read_ptp_tstamp_eth56g(struct ice_hw *hw, u8 port, u8 idx,
static int ice_clear_ptp_tstamp_eth56g(struct ice_hw *hw, u8 port, u8 idx)
{
u64 unused_tstamp;
- u16 lo_addr;
int err;
- /* Read the timestamp register to ensure the timestamp status bit is
- * cleared.
+ /* Per the PHY spec, reading the timestamp memory location is what
+ * clears the entry's valid bit and its corresponding (read-only)
+ * ts_memory_status bit.
*/
err = ice_read_ptp_tstamp_eth56g(hw, port, idx, &unused_tstamp);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read the PHY timestamp register for port %u, idx %u, err %d\n",
port, idx, err);
- }
-
- lo_addr = (u16)PHY_TSTAMP_L(idx);
-
- err = ice_write_port_mem_eth56g(hw, port, lo_addr, 0);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to clear low PTP timestamp register for port %u, idx %u, err %d\n",
- port, idx, err);
return err;
}
@@ -1177,19 +1147,43 @@ static int ice_clear_ptp_tstamp_eth56g(struct ice_hw *hw, u8 port, u8 idx)
}
/**
- * ice_ptp_reset_ts_memory_eth56g - Clear all timestamps from the port block
+ * ice_ptp_clear_tx_memory_status_eth56g - Reset one port's Tx timestamp memory
* @hw: pointer to the HW struct
+ * @port: port number to clear
+ *
+ * Fully reset a single PHY port's Tx timestamp memory. Per the PHY spec, the
+ * only way to clear a timestamp valid bit (and its read-only ts_memory_status
+ * bit) is to read the timestamp memory location, so read every entry for the
+ * port (two 32-bit reads each). This discards all timestamp data on the port,
+ * so it must only be used for a full reset; callers that must preserve
+ * in-flight timestamps clear individual indices via ice_clear_phy_tstamp().
+ *
+ * Due to interactions with an internal HW counter for the number of
+ * outstanding Tx timestamps, this *must* only be called as part of the
+ * ice_ptp_phy_soft_reset_eth56g() procedure. Otherwise, the internal counter
+ * may become out of sync and prevent new timestamp interrupts.
+ *
+ * Failure to read a given index (i.e. due to a transient sideband queue
+ * failure) is not considered fatal as the PHY port is about to be soft reset.
+ * While the soft reset does not clear the timestamp memory, software
+ * shouldn't be reading the timestamp memory without already knowing it is
+ * valid via ice_get_phy_tx_tstamp_ready(), and this sweep is just
+ * a precaution to ensure the memory is in a known state.
*/
-static void ice_ptp_reset_ts_memory_eth56g(struct ice_hw *hw)
+static void ice_ptp_clear_tx_memory_status_eth56g(struct ice_hw *hw, u8 port)
{
- unsigned int port;
+ int err, failed = 0;
+ u8 idx;
- for (port = 0; port < hw->ptp.num_lports; port++) {
- ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TX_MEMORY_STATUS_L,
- 0);
- ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TX_MEMORY_STATUS_U,
- 0);
+ for (idx = 0; idx < INDEX_PER_PORT; idx++) {
+ err = ice_clear_ptp_tstamp_eth56g(hw, port, idx);
+ if (err)
+ failed++;
}
+
+ if (failed)
+ dev_warn(ice_hw_to_dev(hw), "Failed to clear %d PHY timestamp registers for port %u\n",
+ port, failed);
}
/**
@@ -2295,6 +2289,7 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status)
*
* Trigger a soft reset of the ETH56G PHY by toggling the soft reset
* bit in the PHY global register. The reset sequence consists of:
+ * 0. Reading every timestamp memory register to clear its valid bit
* 1. Clearing the soft reset bit
* 2. Asserting the soft reset bit
* 3. Clearing the soft reset bit again
@@ -2303,6 +2298,12 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status)
* to settle. This provides a controlled way to reinitialize the PHY
* without requiring a full device reset.
*
+ * To ensure that the internal counter matches the contents of the
+ * PHY_REG_TX_MEMORY_STATUS, read every timestamp index prior to performing
+ * the soft reset. The PHY_REG_TX_MEMORY_STATUS reads ensure that the region
+ * is cleared, while the soft reset procedure ensures that the timestamp
+ * counter is reset to zero.
+ *
* Return: 0 on success, or a negative error code on failure when
* reading or writing the PHY register.
*/
@@ -2311,6 +2312,8 @@ int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port)
u32 global_val;
int err;
+ ice_ptp_clear_tx_memory_status_eth56g(hw, port);
+
err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, &global_val);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read PHY_REG_GLOBAL for port %d, err %d\n",
@@ -5802,8 +5805,9 @@ void ice_ptp_reset_ts_memory(struct ice_hw *hw)
ice_ptp_reset_ts_memory_e82x(hw);
break;
case ICE_MAC_GENERIC_3K_E825:
- ice_ptp_reset_ts_memory_eth56g(hw);
- break;
+ /* E825 hardware must only reset timestamp memory as part of
+ * the soft reset procedure.
+ */
case ICE_MAC_E810:
default:
return;
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 10/15] ice: E825: perform a soft reset when starting the PHY timer
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (8 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 11/15] ice: wait for in-flight Tx timestamps before flushing the tracker Jacob Keller
` (6 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Maciek Machnikowski
To ensure that the E825 PHY timer begins in a clean state, initiate a PHY
soft reset prior to programming the PHY. This ensures that we clear any
outstanding Tx timestamp memory, and ensures that the PHY internal state
including its timestamp counter has been reset.
Note that despite the name, this PHY soft reset only impacts the timer
block of the PHY, and not its regular Tx/Rx functionality. Additionally,
many configuration registers such as the PHY_REG_TS_INT_CONFIG and
PHY_REG_TX_OFFSET_READY are not reset.
Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index e1a5ff5793d1..f8f985a5d039 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -2128,9 +2128,11 @@ int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port)
* @hw: pointer to the HW struct
* @port: the PHY port to start
*
- * Start the clock of a PHY port. This must be done as part of the flow to
- * re-calibrate Tx and Rx timestamping offsets whenever the clock time is
- * initialized or when link speed changes.
+ * Perform a PHY soft reset and then start the clock for the PHY port.
+ *
+ * This must be done as part of the flow to re-calibrate Tx and Rx
+ * timestamping offsets whenever the clock time is initialized or when link
+ * speed changes.
*
* Return:
* * %0 - success
@@ -2150,6 +2152,10 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port)
if (err)
return err;
+ err = ice_ptp_phy_soft_reset_eth56g(hw, port);
+ if (err)
+ return err;
+
ice_ptp_src_cmd(hw, ICE_PTP_NOP);
err = ice_phy_cfg_parpcs_eth56g(hw, port);
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 11/15] ice: wait for in-flight Tx timestamps before flushing the tracker
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (9 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 10/15] ice: E825: perform a soft reset when starting the PHY timer Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 12/15] ice: keep Tx timestamp slots tracked until completion or timeout Jacob Keller
` (5 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Petr Oros, Maciek Machnikowski
From: Petr Oros <poros@redhat.com>
ice_ptp_flush_tx_tracker() frees every tracked request, but a request
whose timestamp is still being captured by the PHY at that moment is
freed without touching the PHY entry. The ready bit published shortly
after has no tracked owner, and the PHY does not raise another Tx
timestamp interrupt until every outstanding ready bit is read, so
delivery for the whole quad degrades to the periodic work.
Wait up to 10 ms for in-flight captures to publish their ready bits
before flushing, so the flush clears them together with the rest.
Note that the ice_ptp_flush_tx_tracker() function was introduced along with
the original E810 support, but that device does not have a ready bitmap.
Only later devices (E822, E825, E830) have the bitmap and potential issues
with internal tracking. Thus, skip the wait for E810 by checking the
tx->has_ready_bitmap flag.
Fixes: 10e4b4a3a3e1 ("ice: check Tx timestamp memory register for ready timestamps")
Signed-off-by: Petr Oros <poros@redhat.com>
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 43 ++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 657cd78ec738..142d9e1c1f2e 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -744,6 +744,47 @@ ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx)
return 0;
}
+static bool
+ice_ptp_is_tracker_drained(struct ice_pf *pf, struct ice_ptp_tx *tx)
+{
+ struct ice_hw *hw = &pf->hw;
+ bool pending = false;
+ u64 tstamp_ready;
+ u8 idx;
+
+ /* If HW reset is ongoing, we can't access SBQ */
+ if (hw->reset_ongoing)
+ return true;
+
+ if (ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready))
+ return true;
+
+ for_each_set_bit(idx, tx->in_use, tx->len) {
+ if (!(tstamp_ready & BIT_ULL(idx + tx->offset)))
+ pending = true;
+ }
+
+ return !pending;
+}
+
+static void
+ice_ptp_wait_for_tracker_drain(struct ice_pf *pf, struct ice_ptp_tx *tx)
+{
+ bool drained;
+ int err;
+
+ if (!tx->has_ready_bitmap)
+ return;
+
+ err = read_poll_timeout(ice_ptp_is_tracker_drained,
+ drained, drained, 500, 10 * USEC_PER_MSEC, false,
+ pf, tx);
+ if (err) {
+ dev_dbg(ice_pf_to_dev(pf), "Timed out waiting for in-flight Tx timestamps on block %u\n",
+ tx->block);
+ }
+}
+
/**
* ice_ptp_flush_tx_tracker - Flush any remaining timestamps from the tracker
* @pf: Board private structure
@@ -760,6 +801,8 @@ ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx)
int err;
u8 idx;
+ ice_ptp_wait_for_tracker_drain(pf, tx);
+
err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready);
if (err) {
dev_dbg(ice_pf_to_dev(pf), "Failed to get the Tx tstamp ready bitmap for block %u, err %d\n",
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 12/15] ice: keep Tx timestamp slots tracked until completion or timeout
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (10 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 11/15] ice: wait for in-flight Tx timestamps before flushing the tracker Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 13/15] ice: skip reading Tx ready bitmap on ports with no timestamps Jacob Keller
` (4 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Petr Oros, Maciek Machnikowski
From: Petr Oros <poros@redhat.com>
When the link goes down the processing loop drops every outstanding
request, and a request whose timestamp is not ready yet is freed
without reading the PHY slot. The hardware completes the capture a
moment later, the orphaned ready bit blocks the port interrupt until
the next link-up sweep, and the freed index can meanwhile be reused
by a new request whose slot the hardware then overwrites. Captured on
a reproducer as ready bits with no in_use owner right after a link
bounce.
Stop dropping on link down. Mark the outstanding requests stale so
their completions are read and discarded, reject new requests while
the link is down, and free a not yet ready slot only after the two
second timeout. This way an index is never reused while the hardware
can still write it and never left untracked while a completion can
still arrive.
To avoid an IRQ storm in the event that we really do have a stale packet
that is not timestamped, modify ice_ptp_tx_tstamps_pending() to ignore
stale timestamps when checking for whether to re-arm the IRQ from the
miscellaneous thread function. Instead, only check for stale packets in the
auxiliary work thread. This way we do not check in a tight loop waiting for
a timestamp that may never come.
To ensure that forward progress is made on clearing stale timestamps, fix
the check for ice_ptp_maybe_trigger_tx_interrupt to properly apply for
devices that manage their own interrupt, instead of only checking on the
clock owner.
This effectively reverts commit fcc2cef37fed ("ice/ptp: fix the PTP worker
retrying indefinitely if the link went down"), which tried to release an
index before this 2 second wait period.
Fixes: fcc2cef37fed ("ice/ptp: fix the PTP worker retrying indefinitely if the link went down")
Suggested-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Petr Oros <poros@redhat.com>
Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.h | 8 +++--
drivers/net/ethernet/intel/ice/ice_main.c | 2 +-
drivers/net/ethernet/intel/ice/ice_ptp.c | 51 ++++++++++++++++---------------
3 files changed, 34 insertions(+), 27 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index 029ee4612d76..7810fefc546e 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -132,6 +132,9 @@ struct ice_ptp_tx {
#define INDEX_PER_PORT_E82X 16
#define INDEX_PER_PORT 64
+/* Maximum number of timestamp indexes across all devices */
+#define INDEX_PER_PORT_MAX INDEX_PER_PORT
+
/**
* struct ice_ptp_port - data used to initialize an external port for PTP
*
@@ -314,7 +317,7 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx);
void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx);
void ice_ptp_process_ts(struct ice_pf *pf);
irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf);
-bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf);
+bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq);
u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf,
struct ptp_system_timestamp *sts);
@@ -362,7 +365,8 @@ static inline irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
return IRQ_HANDLED;
}
-static inline bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
+static inline bool
+ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq)
{
return false;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index f32041dd8b27..d12952171a99 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -3248,7 +3248,7 @@ static irqreturn_t ice_misc_intr_thread_fn(int __always_unused irq, void *data)
ice_irq_dynamic_ena(hw, NULL, NULL);
ice_flush(hw);
- if (ice_ptp_tx_tstamps_pending(pf)) {
+ if (ice_ptp_tx_tstamps_pending(pf, true)) {
/* If any new Tx timestamps happened while in interrupt,
* re-arm the interrupt to trigger it again.
*/
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 142d9e1c1f2e..06f383415d07 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -365,9 +365,12 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp)
static bool
ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
{
+ struct ice_ptp_port *ptp_port =
+ container_of(tx, struct ice_ptp_port, tx);
+
lockdep_assert_held(&tx->lock);
- return tx->init && !tx->calibrating;
+ return tx->init && !tx->calibrating && READ_ONCE(ptp_port->link_up);
}
/**
@@ -564,7 +567,6 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
struct ice_pf *pf;
struct ice_hw *hw;
u64 tstamp_ready;
- bool link_up;
int err;
u8 idx;
@@ -582,14 +584,11 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
return;
}
- /* Drop packets if the link went down */
- link_up = READ_ONCE(ptp_port->link_up);
-
for_each_set_bit(idx, tx->in_use, tx->len) {
struct skb_shared_hwtstamps shhwtstamps = {};
u8 phy_idx = idx + tx->offset;
u64 raw_tstamp = 0, tstamp;
- bool drop_ts = !link_up;
+ bool drop_ts = false;
struct sk_buff *skb;
/* Prevent speculative re-ordering of start and skb */
@@ -1399,6 +1398,10 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
if (pf->hw.reset_ongoing) {
mutex_lock(&pf->adapter->ps_lock);
WRITE_ONCE(ptp_port->link_up, linkup);
+
+ if (!linkup)
+ ice_ptp_mark_tx_tracker_stale(&ptp_port->tx);
+
mutex_unlock(&pf->adapter->ps_lock);
return;
}
@@ -1448,6 +1451,9 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
*/
WRITE_ONCE(ptp_port->link_up, linkup);
+ if (!linkup)
+ ice_ptp_mark_tx_tracker_stale(&ptp_port->tx);
+
switch (hw->mac_type) {
case ICE_MAC_E810:
case ICE_MAC_E830:
@@ -2804,21 +2810,22 @@ void ice_ptp_process_ts(struct ice_pf *pf)
}
}
-static bool ice_port_has_timestamps(struct ice_ptp_tx *tx)
+static bool ice_port_has_timestamps(struct ice_ptp_tx *tx, bool in_irq)
{
- bool more_timestamps;
+ DECLARE_BITMAP(tstamps, INDEX_PER_PORT_MAX) = {};
scoped_guard(spinlock_irqsave, &tx->lock) {
if (!tx->init)
return false;
- more_timestamps = !bitmap_empty(tx->in_use, tx->len);
+ if (in_irq)
+ return bitmap_andnot(tstamps, tx->in_use, tx->stale, tx->len);
+ else
+ return !bitmap_empty(tx->in_use, tx->len);
}
-
- return more_timestamps;
}
-static bool ice_any_port_has_timestamps(struct ice_pf *pf)
+static bool ice_any_port_has_timestamps(struct ice_pf *pf, bool in_irq)
{
struct ice_port_list *ports = &pf->adapter->ports;
bool have_tstamps = false;
@@ -2832,7 +2839,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf)
if (!kref_get_unless_zero(&port->ref))
continue;
- if (ice_port_has_timestamps(&port->tx))
+ if (ice_port_has_timestamps(&port->tx, in_irq))
have_tstamps = true;
kref_put(&port->ref, ice_ptp_release_port_srcu);
@@ -2846,7 +2853,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf)
return have_tstamps;
}
-bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
+bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq)
{
struct ice_hw *hw = &pf->hw;
int ret;
@@ -2856,11 +2863,11 @@ bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
case ICE_PTP_TX_INTERRUPT_NONE:
return false;
case ICE_PTP_TX_INTERRUPT_SELF:
- if (ice_port_has_timestamps(&pf->ptp.port.tx))
+ if (ice_port_has_timestamps(&pf->ptp.port.tx, in_irq))
return true;
break;
case ICE_PTP_TX_INTERRUPT_ALL:
- if (ice_any_port_has_timestamps(pf))
+ if (ice_any_port_has_timestamps(pf, in_irq))
return true;
break;
default:
@@ -2936,7 +2943,7 @@ irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
/* E830 can read timestamps in the top half using rd32() */
ice_ptp_process_ts(pf);
- if (ice_ptp_tx_tstamps_pending(pf)) {
+ if (ice_ptp_tx_tstamps_pending(pf, true)) {
/* Process outstanding Tx timestamps. If there
* is more work, re-arm the interrupt to trigger again.
*/
@@ -2966,19 +2973,15 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
- int ret;
if (!pf->ptp.port.tx.has_ready_bitmap)
return;
- if (!ice_pf_src_tmr_owned(pf))
+ if (pf->ptp.tx_interrupt_mode != ICE_PTP_TX_INTERRUPT_SELF &&
+ !ice_pf_src_tmr_owned(pf))
return;
- ret = ice_check_phy_tx_tstamp_ready(hw);
- if (ret < 0) {
- dev_dbg(dev, "PTP periodic task unable to read PHY timestamp ready bitmap, err %d\n",
- ret);
- } else if (ret) {
+ if (ice_ptp_tx_tstamps_pending(pf, false)) {
dev_dbg(dev, "PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n");
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 13/15] ice: skip reading Tx ready bitmap on ports with no timestamps
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (11 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 12/15] ice: keep Tx timestamp slots tracked until completion or timeout Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 14/15] ice: don't clear in_use until HW clears ready bitmap Jacob Keller
` (3 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller
On E82x devices, the interrupt for Tx timestamps are handled by the clock
owner. When an interrupt with the Tx timestamp cause is fired, the clock
owner PF iterates the list of ports and checks for timestamps across all
ports.
The existing logic reads the PHY timestamp ready bitmap before iterating
the list of in-use timestamp indexes, even for ports which have no
timestamps waiting in the software timestamp tracker. This has a
significant and measurable latency impact on reporting Tx timestamps.
Check the bitmap and exit early in the event that there are no timestamps
waiting on a port. Observant reviewers may notice that the check is done
without acquiring the lock. This is fine, as the only thread that can clear
in_use bits is the miscellaneous interrupt handler. Whether the thread sees
or fails to see a new outstanding timestamp does not affect correctness,
only determining whether or not it should do extra work.
The average latency of a Tx timestamp is impacted by several factors
including system load, the number of timestamp requests, and some random
factors that are difficult to control. However for comparison on my system
with these changes, while operating ptp4l on a single port with a sync rate
of 16/second:
Before: mean 317.43 microseconds, standard deviation 34.51
After: mean 189.71 microseconds, standard deviation 43.24
Of course this level of improvement may not be indicative of a production
setup as one might expect timestamps to be operating out of multiple ports
on the device. However, even in that case an improvement is still likely
depending on the actual timestamp rates.
Fixes: d938a8cca88a ("ice: Auxbus devices & driver for E822 TS")
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 06f383415d07..890c2e8d4ece 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -574,7 +574,7 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
pf = ptp_port_to_pf(ptp_port);
hw = &pf->hw;
- if (!tx->init)
+ if (!tx->init || bitmap_empty(tx->in_use, tx->len))
return;
/* Read the Tx ready status first */
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 14/15] ice: don't clear in_use until HW clears ready bitmap
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (12 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 13/15] ice: skip reading Tx ready bitmap on ports with no timestamps Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 15/15] ice: Recalibrate PHY after settime64 on E825-C Jacob Keller
` (2 subsequent siblings)
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller
During a link down transition, there is a small window where hardware
does not properly respond to reading the PHY timestamp registers. When this
occurs, the PHY does not automatically clear the ready bitmap or the valid
bit for the timestamp. This begins happening slightly before a link
transition even before the firmware has notified the driver of the state
change.
The driver happily completes the timestamp, releasing the in_use bit. This
allows another request to reuse the bit potentially reporting an invalid
stale timestamp. Additionally, with the ready bit still set high the driver
continues to re-trigger the IRQ and check for timestamps in a tight loop,
wasting CPU cycles.
To fix this, re-read the PHY timestamp memory status after each read of a
PHY index. Double check if the hardware cleared the index properly. If it
hasn't, mark the timestamp index as stale and keep the index locked. The
index will be re-checked once another interrupt occurs (either from a real
timestamp or from the watchdog kick). Marking the packet as stale makes
sense since we know this begins happening when link is going down.
This flow has been observed on E825, but allowing the driver to free an
index which is not cleared would be incorrect regardless of which device
type it occurs on. Thus, this re-read is applied to all device types.
In the unlikely event that a timestamp has timed
out the 2 second wait *and* somehow suddenly has its ready bit set but
unable to clear on read, this could accidentally increment the timeout
counter. It is intentional that we do *not* release the index even in a
timed out case, as we must not allow reuse of that index until we can be
certain it has cleared. Instead, refactor so that the timeout counter is
only incremented after the skip_ts_read label, ensuring that we don't
count the number of timeouts incorrectly. This does mean that a "stuck"
ready bit will be locked *indefinitely* until the hardware reaches a state
where the clear works as expected.
Stale timestamps are already ignored by the ice_any_port_has_timestamps()
function. However, the ice_ptp_tx_tstamps_pending() function also checks
the ready bitmap. Instead, modify it to only check the software tracker.
Additionally, stop re-triggering the interrupt from the IRQ if the
timestamp tracker is calibrating or has the link marked as down. Continue
to check the hardware ready bitmap from the watchdog to catch cases of
unexpected timestamps.
With these changes, the timestamp processing no longer triggers a repeated
spamming of the IRQ during link down events where timestamps get stuck as
the PHY transitions to link down. Once link is restored, the PHY will be
reset and the stuck timestamps are cleared.
Measuring CPU utilization of the miscellaneous IRQ thread function during
timestamp storms near a link reset shows that this prevents the spikes
caused by the "stuck" ready bit. Without this fix, the CPU handling the IRQ
becomes slammed due to the IRQ re-triggering logic.
Measuring latency using the ice Tx timestamp traces does show that this fix
comes at a latency cost. Latency is measured using the ice Tx timestamp
traces for the request to completion time. I measured a couple of different
workloads both before and after this fix:
* ptp4l using a profile with ~16 SYNC messages per second
before: 189.71 microseconds mean, stdev 43.24
after: 195.92 microseconds mean, stdev 25.28
* a C program generating 16 timestamp requests every 10 milliseconds on
two different ports:
before: 457.11 microseconds mean, stdev 180.63
after: 717.77 microseconds mean, stdev 321.58
In the normal work flows this comes with about a 10-20 microsecond penalty on
the average, and the standard deviation remains similar (with some variance
between run to run comparison).
For heavy workloads with many more timestamps than expected for typical
applications this comes at a significant cost. This is because we handle
all timestamps in a single thread. If there are many concurrent timestamps
being requested at once, any which use the later slots on ports later in
the port list will take much longer to be processed once the interrupt is
fired. Since each timestamp now requires an additional PHY register access,
this cost is much higher in the case where the device is under unusually
heavy load. The high standard deviation indicates a very high variance in
timestamp latency, with many timestamps completing in the usual time but
some taking significantly longer when multiple timestamps are outstanding
in a single IRQ.
Ultimately, *correctness* is more important than speed here. Additionally,
we still remain well below the default limit of 10 milliseconds that ptp4l
will wait before complaining about missing timestamps.
Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 69 +++++++++++++++++---------------
1 file changed, 37 insertions(+), 32 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 890c2e8d4ece..e654f8962d13 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -586,9 +586,9 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
for_each_set_bit(idx, tx->in_use, tx->len) {
struct skb_shared_hwtstamps shhwtstamps = {};
+ bool drop_ts = false, timeout = false;
u8 phy_idx = idx + tx->offset;
u64 raw_tstamp = 0, tstamp;
- bool drop_ts = false;
struct sk_buff *skb;
/* Prevent speculative re-ordering of start and skb */
@@ -597,9 +597,7 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
/* Drop packets which have waited for more than 2 seconds */
if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) {
drop_ts = true;
-
- /* Count the number of Tx timestamps that timed out */
- pf->ptp.tx_hwtstamp_timeouts++;
+ timeout = true;
}
/* Only read a timestamp from the PHY if its marked as ready
@@ -624,6 +622,20 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
if (err && !drop_ts)
continue;
+ /* verify ready bit cleared */
+ if (tx->has_ready_bitmap) {
+ err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready);
+ if (err || tstamp_ready & BIT_ULL(phy_idx)) {
+ spin_lock_irqsave(&tx->lock, flags);
+ if (test_bit(idx, tx->in_use) &&
+ !test_and_set_bit(idx, tx->stale))
+ dev_dbg(ice_pf_to_dev(pf), "PHY port %u failed to clear ready bit for idx %u\n",
+ ptp_port->port_num, phy_idx);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ continue;
+ }
+ }
+
ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx);
/* For PHYs which don't implement a proper timestamp ready
@@ -640,6 +652,9 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
drop_ts = true;
skip_ts_read:
+ if (timeout)
+ pf->ptp.tx_hwtstamp_timeouts++;
+
spin_lock_irqsave(&tx->lock, flags);
if (!tx->has_ready_bitmap && raw_tstamp)
tx->tstamps[idx].cached_tstamp = raw_tstamp;
@@ -2818,10 +2833,14 @@ static bool ice_port_has_timestamps(struct ice_ptp_tx *tx, bool in_irq)
if (!tx->init)
return false;
- if (in_irq)
+ if (in_irq) {
+ if (!ice_ptp_is_tx_tracker_up(tx))
+ return false;
+
return bitmap_andnot(tstamps, tx->in_use, tx->stale, tx->len);
- else
+ } else {
return !bitmap_empty(tx->in_use, tx->len);
+ }
}
}
@@ -2855,41 +2874,18 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf, bool in_irq)
bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf, bool in_irq)
{
- struct ice_hw *hw = &pf->hw;
- int ret;
-
- /* Check software indicator */
switch (pf->ptp.tx_interrupt_mode) {
case ICE_PTP_TX_INTERRUPT_NONE:
return false;
case ICE_PTP_TX_INTERRUPT_SELF:
- if (ice_port_has_timestamps(&pf->ptp.port.tx, in_irq))
- return true;
- break;
+ return ice_port_has_timestamps(&pf->ptp.port.tx, in_irq);
case ICE_PTP_TX_INTERRUPT_ALL:
- if (ice_any_port_has_timestamps(pf, in_irq))
- return true;
- break;
+ return ice_any_port_has_timestamps(pf, in_irq);
default:
WARN_ONCE(1, "Unexpected Tx timestamp interrupt mode %u\n",
pf->ptp.tx_interrupt_mode);
- break;
- }
-
- /* Check hardware indicator */
- ret = ice_check_phy_tx_tstamp_ready(hw);
- if (ret < 0) {
- dev_dbg(ice_pf_to_dev(pf), "Unable to read PHY Tx timestamp ready bitmap, err %d\n",
- ret);
- /* Stop triggering IRQs if we're unable to read PHY */
return false;
}
-
- /* ice_check_phy_tx_tstamp_ready() returns 1 if there are timestamps
- * available, 0 if there are no waiting timestamps, and a negative
- * value if there was an error (which we checked for above).
- */
- return ret > 0;
}
/**
@@ -2973,6 +2969,7 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
+ int ret;
if (!pf->ptp.port.tx.has_ready_bitmap)
return;
@@ -2981,7 +2978,15 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf)
!ice_pf_src_tmr_owned(pf))
return;
- if (ice_ptp_tx_tstamps_pending(pf, false)) {
+ ret = ice_check_phy_tx_tstamp_ready(hw);
+ if (ret < 0) {
+ dev_dbg(dev, "Unable to read PHY Tx timestamp ready bitmap, err %pe\n",
+ ERR_PTR(ret));
+ /* Don't trigger an IRQ if we are unable to access the PHY */
+ return;
+ }
+
+ if (ret > 0 || ice_ptp_tx_tstamps_pending(pf, false)) {
dev_dbg(dev, "PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n");
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iwl-net v2 15/15] ice: Recalibrate PHY after settime64 on E825-C
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (13 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 14/15] ice: don't clear in_use until HW clears ready bitmap Jacob Keller
@ 2026-09-22 18:02 ` Jacob Keller
2026-09-22 18:22 ` [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jakub Kicinski
2026-09-24 1:05 ` Jacob Keller
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-22 18:02 UTC (permalink / raw)
To: Jacob Keller, Grzegorz Nitka, Arkadiusz Kubalewski,
Intel Wired LAN, Maciej Machnikowski, Przemyslaw Korba, netdev,
Anthony Nguyen
Cc: Jacob Keller, Maciek Machnikowski, Paul Menzel,
Aleksandr Loktionov
From: Maciek Machnikowski <maciej.machnikowski@intel.com>
The PHY on E825-C requires recalibration after large jumps of the
system time. This step is required and is done for E822 devices
previously, but was accidentally skipped due to oversight when
E825-C support was added.
Lack of recalibration will fail to converge quickly as the effective
adjustment requested is not applied properly to the PHY timestamps,
so the readings from the timestamp are incorrect.
Without the fix:
> ptp4l[12591.505]: port 1 (eno8303np0): SLAVE to UNCALIBRATED on SYNCHRONIZATION_FAULT
> ptp4l[12591.951]: port 1 (eno8303np0): UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED
> ptp4l[12592.027]: rms 752851973 max 1102277559 freq -84408798 +/- 53560133 delay 6009 +/- 92308
> ptp4l[12593.038]: rms 91302045 max 136894784 freq +100000000 +/- 0 delay 93700 +/- 24153
> ptp4l[12594.050]: rms 16036314 max 35711594 freq +47433070 +/- 46746070 delay 44899 +/- 45255
> ptp4l[12595.061]: rms 5558880 max 12292103 freq -13092285 +/- 6627317 delay -13499 +/- 6560
> ptp4l[12596.073]: rms 759533 max 1081638 freq +837063 +/- 802691 delay 711 +/- 979
> ptp4l[12597.085]: rms 60485 max 106800 freq +146706 +/- 263535 delay 145 +/- 263
> ptp4l[12598.096]: rms 16428 max 41896 freq -46707 +/- 34325 delay -40 +/- 34
> ptp4l[12599.108]: rms 3049 max 5356 freq +5337 +/- 1730 delay 7 +/- 2
> ptp4l[12600.120]: rms 284 max 381 freq +104 +/- 759 delay 2 +/- 1
> ptp4l[12601.131]: rms 42 max 119 freq -124 +/- 143 delay 2 +/- 0
> ptp4l[12602.144]: rms 11 max 25 freq +39 +/- 12 delay 3 +/- 0
> ptp4l[12603.156]: rms 2 max 4 freq +14 +/- 5 delay 3 +/- 0
> ptp4l[12604.167]: rms 1 max 3 freq +15 +/- 5 delay 2 +/- 0
> ptp4l[12605.179]: rms 1 max 4 freq +16 +/- 5 delay 2 +/- 1
> ptp4l[12606.191]: rms 1 max 3 freq +15 +/- 5 delay 3 +/- 0
With the fix:
> ptp4l[12834.266]: rms 27178238388098 max 30079328952737 freq +479164 +/- 1040869 delay 5 +/- 1
> ptp4l[12834.522]: port 1 (eno8703np0): minimum delay request interval 2^-8
> ptp4l[12835.315]: rms 86919 max 139746 freq +111289 +/- 539214 delay -35 +/- 28
> ptp4l[12836.376]: rms 5415 max 8884 freq +579 +/- 22798 delay 4 +/- 5
> ptp4l[12837.429]: rms 335 max 559 freq -305 +/- 842 delay 4 +/- 1
> ptp4l[12838.471]: rms 20 max 45 freq +79 +/- 33 delay 4 +/- 0
> ptp4l[12839.512]: rms 2 max 5 freq +44 +/- 8 delay 4 +/- 0
> ptp4l[12840.545]: rms 1 max 4 freq +46 +/- 8 delay 4 +/- 0
> ptp4l[12841.586]: rms 1 max 3 freq +46 +/- 7 delay 4 +/- 0
> ptp4l[12842.637]: rms 1 max 3 freq +46 +/- 7 delay 4 +/- 0
Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
Signed-off-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index e654f8962d13..b9018d8940d4 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -2072,8 +2072,9 @@ ice_ptp_settime64(struct ptp_clock_info *info, const struct timespec64 *ts)
/* Reenable periodic outputs */
ice_ptp_enable_all_perout(pf);
- /* Recalibrate and re-enable timestamp blocks for E822/E823 */
- if (hw->mac_type == ICE_MAC_GENERIC)
+ /* Recalibrate and re-enable timestamp blocks for E822/E823/E825-C */
+ if (hw->mac_type == ICE_MAC_GENERIC ||
+ hw->mac_type == ICE_MAC_GENERIC_3K_E825)
ice_ptp_restart_all_phy(pf);
exit:
if (err) {
--
2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (14 preceding siblings ...)
2026-09-22 18:02 ` [PATCH iwl-net v2 15/15] ice: Recalibrate PHY after settime64 on E825-C Jacob Keller
@ 2026-09-22 18:22 ` Jakub Kicinski
2026-09-23 20:31 ` Jacob Keller
2026-09-24 1:05 ` Jacob Keller
16 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2026-09-22 18:22 UTC (permalink / raw)
To: Jacob Keller
Cc: Grzegorz Nitka, Arkadiusz Kubalewski, Intel Wired LAN,
Maciej Machnikowski, Przemyslaw Korba, netdev, Anthony Nguyen,
Aleksandr Loktionov, Petr Oros, Paul Menzel
On Tue, 22 Sep 2026 11:02:33 -0700 Jacob Keller wrote:
> Subject: [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes
I think we need to reconsider iwl cross-posting to netdev.
I can't get my unread email count to stay under 1000, and
I don't think the cross-posting is really bringing much
benefit to the community. IOW only Simon and Intel people
look at the Intel postings, anyway.
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset
2026-09-22 18:02 ` [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset Jacob Keller
@ 2026-09-23 9:41 ` Loktionov, Aleksandr
2026-09-23 20:28 ` Jacob Keller
0 siblings, 1 reply; 23+ messages in thread
From: Loktionov, Aleksandr @ 2026-09-23 9:41 UTC (permalink / raw)
To: Keller, Jacob E, Keller, Jacob E, Nitka, Grzegorz,
Kubalewski, Arkadiusz, Intel Wired LAN, Machnikowski, Maciej,
Korba, Przemyslaw, netdev@vger.kernel.org, Nguyen, Anthony L
Cc: Keller, Jacob E, Machnikowski, Maciej
> -----Original Message-----
> From: Jacob Keller <jacob.e.keller@intel.com>
> Sent: Tuesday, September 22, 2026 8:03 PM
> To: Keller, Jacob E <jacob.e.keller@intel.com>; Nitka, Grzegorz
> <grzegorz.nitka@intel.com>; Kubalewski, Arkadiusz
> <arkadiusz.kubalewski@intel.com>; Intel Wired LAN <intel-wired-
> lan@lists.osuosl.org>; Machnikowski, Maciej
> <maciej.machnikowski@intel.com>; Korba, Przemyslaw
> <przemyslaw.korba@intel.com>; netdev@vger.kernel.org; Nguyen,
> Anthony L <anthony.l.nguyen@intel.com>
> Cc: Keller, Jacob E <jacob.e.keller@intel.com>; Machnikowski, Maciej
> <maciej.machnikowski@intel.com>
> Subject: [PATCH iwl-net v2 09/15] ice: E825: clear
> PHY_REG_TX_MEMORY_STATUS prior to soft reset
>
> The current implementation of ice_ptp_reset_ts_memory_eth56g() is
> flawed.
> It tries to clear the timestamp memory by writing to the
> PHY_REG_TX_MEMORY_STATUS region. This does not work properly, as it
> does not trigger appropriate PHY actions.
>
> To clear outstanding timestamp memory, the driver must read the
> timestamps.
> However, naively doing this as part of ice_ptp_reset_ts_memory() is
> problematic. When reading the timestamp index, hardware kicks off a
> chain of actions including clearing the ready bitmap index, and
> decrementing an internal counter if the timestamp index was marked
> as valid.
>
> This can potentially leave the internal hardware counter out of sync
> with the actual number of timestamps. This occurs because the
> PHY_REG_TX_MEMORY_STATUS region is not zero-initialized when the
> device boots up. Instead, it is filled with garbage. On a cold power
> on, attempts to read the stale data result in the hardware
> triggering a counter decrement for a timestamp that never happened.
> This underflows the counter, and prevents new timestamp interrupts
> from being triggered for real timestamp requests.
>
> We must read the PHY_REG_TX_MEMORY_STATUS in order to clear stale
> timestamps. But doing so may cause a desync with the counter. To
> prevent issues, perform this clearing always and only right before
> initiating a PHY soft reset.
>
> The soft reset will clear and reset the internal counter and the
> ready bitmap. The reads to PHY_REG_TX_MEMORY_STATUS will reset the
> region valid bits ensuring that no stale data is left behind. This
> combination ensures that we always have a clean slate with no stale
> data and with the counter properly reset to zero.
>
> If a read fails (i.e. due to a transient sideband queue failure) it
> is not treated as a fatal error. There is already a dynamic debug
> message for each read failure. Keep track of the total number of
> timestamp registers that fail for a given port and log a warning
> message indicating the total number of failures. Continuing is
> acceptable as clearing the memory is a precaution against faulty
> software. Correct software access won't read the index twice and
> won't read it at all except when it has already confirmed that the
> index is valid by reading ice_get_phy_tx_tstamp_ready().
>
> Fixes: 3ec46e157c7f ("ice: perform PHY soft reset for E825C ports at
> initialization")
> Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 100 +++++++++++++++--
> -----------
> 1 file changed, 52 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> index c8a67a307832..e1a5ff5793d1 100644
> --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> @@ -737,24 +737,6 @@ static int ice_read_port_mem_eth56g(struct
> ice_hw *hw, u8 port, u16 offset,
> return ice_read_port_eth56g(hw, port, offset, val,
> ETH56G_PHY_MEM_PTP); }
>
...
> return err;
> }
>
> @@ -1177,19 +1147,43 @@ static int
> ice_clear_ptp_tstamp_eth56g(struct ice_hw *hw, u8 port, u8 idx) }
>
> /**
> - * ice_ptp_reset_ts_memory_eth56g - Clear all timestamps from the
> port block
> + * ice_ptp_clear_tx_memory_status_eth56g - Reset one port's Tx
> + timestamp memory
> * @hw: pointer to the HW struct
> + * @port: port number to clear
> + *
> + * Fully reset a single PHY port's Tx timestamp memory. Per the PHY
> + spec, the
> + * only way to clear a timestamp valid bit (and its read-only
> + ts_memory_status
> + * bit) is to read the timestamp memory location, so read every
> entry
> + for the
> + * port (two 32-bit reads each). This discards all timestamp data
> on
> + the port,
> + * so it must only be used for a full reset; callers that must
> preserve
> + * in-flight timestamps clear individual indices via
> ice_clear_phy_tstamp().
> + *
> + * Due to interactions with an internal HW counter for the number
> of
> + * outstanding Tx timestamps, this *must* only be called as part of
> the
> + * ice_ptp_phy_soft_reset_eth56g() procedure. Otherwise, the
> internal
> + counter
> + * may become out of sync and prevent new timestamp interrupts.
> + *
> + * Failure to read a given index (i.e. due to a transient sideband
> + queue
> + * failure) is not considered fatal as the PHY port is about to be
> soft reset.
> + * While the soft reset does not clear the timestamp memory,
> software
> + * shouldn't be reading the timestamp memory without already
> knowing it
> + is
> + * valid via ice_get_phy_tx_tstamp_ready(), and this sweep is just
> + * a precaution to ensure the memory is in a known state.
> */
> -static void ice_ptp_reset_ts_memory_eth56g(struct ice_hw *hw)
> +static void ice_ptp_clear_tx_memory_status_eth56g(struct ice_hw
> *hw, u8
> +port)
> {
> - unsigned int port;
> + int err, failed = 0;
> + u8 idx;
>
> - for (port = 0; port < hw->ptp.num_lports; port++) {
> - ice_write_ptp_reg_eth56g(hw, port,
> PHY_REG_TX_MEMORY_STATUS_L,
> - 0);
> - ice_write_ptp_reg_eth56g(hw, port,
> PHY_REG_TX_MEMORY_STATUS_U,
> - 0);
> + for (idx = 0; idx < INDEX_PER_PORT; idx++) {
> + err = ice_clear_ptp_tstamp_eth56g(hw, port, idx);
> + if (err)
> + failed++;
> }
> +
> + if (failed)
> + dev_warn(ice_hw_to_dev(hw), "Failed to clear %d PHY
> timestamp registers for port %u\n",
> + port, failed);
Failed, port arguments looks like swaped.
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> }
>
> /**
> @@ -2295,6 +2289,7 @@ int
> ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32
> *ts_status)
> *
> * Trigger a soft reset of the ETH56G PHY by toggling the soft
> reset
> * bit in the PHY global register. The reset sequence consists of:
...
> case ICE_MAC_E810:
> default:
> return;
>
> --
> 2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access
2026-09-22 18:02 ` [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access Jacob Keller
@ 2026-09-23 9:47 ` Loktionov, Aleksandr
2026-09-23 20:28 ` Jacob Keller
0 siblings, 1 reply; 23+ messages in thread
From: Loktionov, Aleksandr @ 2026-09-23 9:47 UTC (permalink / raw)
To: Keller, Jacob E, Keller, Jacob E, Nitka, Grzegorz,
Kubalewski, Arkadiusz, Intel Wired LAN, Machnikowski, Maciej,
Korba, Przemyslaw, netdev@vger.kernel.org, Nguyen, Anthony L
Cc: Keller, Jacob E, Machnikowski, Maciej
> -----Original Message-----
> From: Jacob Keller <jacob.e.keller@intel.com>
> Sent: Tuesday, September 22, 2026 8:03 PM
> To: Keller, Jacob E <jacob.e.keller@intel.com>; Nitka, Grzegorz
> <grzegorz.nitka@intel.com>; Kubalewski, Arkadiusz
> <arkadiusz.kubalewski@intel.com>; Intel Wired LAN <intel-wired-
> lan@lists.osuosl.org>; Machnikowski, Maciej
> <maciej.machnikowski@intel.com>; Korba, Przemyslaw
> <przemyslaw.korba@intel.com>; netdev@vger.kernel.org; Nguyen, Anthony
> L <anthony.l.nguyen@intel.com>
> Cc: Keller, Jacob E <jacob.e.keller@intel.com>; Machnikowski, Maciej
> <maciej.machnikowski@intel.com>
> Subject: [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU
> for PTP port access
>
> The ice adapter structure maintains a list of ports associated with
> the adapter. This is used for supporting PTP, where the clock owner
> must handle many operations that require access to the PTP port
> structures of the associated PFs.
>
> This is implemented using a linked list and a mutex. This sort of
> works, but a few places within the code do not acquire the mutex when
> iterating the list. This includes ice_ptp_flush_all_tx_tracker(),
> ice_ptp_restart_all_phy(), and ice_ptp_prepare_rebuild_sec().
>
> Fixing this is tricky, especially since it is not clear if we can
> simply acquire the lock around the complete iterations.
>
> The pattern of use for the port list is read-mostly with modifications
> only happening during PF initialization when elements are inserted.
> This typically only happens during early boot, though a PF could in
> principle be removed or loaded at arbitrary times via bind and unbind
> operations.
>
> The use of a mutex does mean the driver can sleep while holding it,
> but it still creates complicates with lock ordering and prevents
> iterating the list in any code path that *can't* sleep.
>
> Instead, use SRCU primitives for the port linked list, along with a
> reference count on the port. The kref reference counter ensures that a
> PF will have a valid lifetime and not be removed until the reference
> is released. Note that this change focuses solely on the port list and
> does not make an effort to resolve access to ctrl_pf, which is
> currently being investigated by another developer.
>
> Use of sleepable RCU is required because we often iterate the PTP port
> list and perform operations that might sleep. Attempts at implementing
> regular RCU have thus far not proven to be acceptable.
>
> The remove path first removes the port from the list, and we use
> kref_get_unless_zero to ensure that such ports are skipped when
> iterating the list. This ensures that once a port starts removing we
> will drop references and no longer be able to acquire new ones. This
> avoids loop iterations chaining together to indefinitely block
> removal.
>
> The ice_ptp_release_port_srcu() function is used as the release
> function for the kref_put() call. This uses wake_up_var() to wake the
> removing thread.
> The waiting thread will block until the final reference has been
> removed, then it will use synchronize_srcu() to ensure any outstanding
> SRCU critical sections have had the necessary grace period.
>
> This flow ensures that accesses to ports via the adapter port list
> will remain valid until both the SRCU critical sections have ended and
> all the references to the ports have been dropped. Strictly speaking,
> SRCU alone might be sufficient for existing code paths, but the
> reference count allows the option for passing a pointer to the port on
> to other functions if necessary in the future.
>
> One major complication of this reference count is that ice_ptp_port is
> embedded inside of other structures and not merely allocated. As a
> result, we can't use the standard pattern of kfree_rcu() to just delay
> freeing until references are dropped, and instead are delaying PF port
> teardown. If any code path leaks the reference, the driver will be
> unable to teardown.
> Instead, a 15 second timeout with a WARN() is used when waiting to
> finally allow PF teardown to continue. This has the risk of
> potentially allowing use-after-free, assuming some path really is
> stuck for 15 seconds. However, this both less likely and a less bad
> outcome compared to blocking indefinitely on a reference leak.
>
> Fixes: e800654e85b5 ("ice: Use ice_adapter for PTP shared data instead
> of auxdev")
> Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_adapter.h | 16 +--
> drivers/net/ethernet/intel/ice/ice_ptp.h | 4 +
> drivers/net/ethernet/intel/ice/ice_adapter.c | 11 +-
> drivers/net/ethernet/intel/ice/ice_ptp.c | 145
> +++++++++++++++++++++------
> 4 files changed, 134 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h
> b/drivers/net/ethernet/intel/ice/ice_adapter.h
> index 4f695f32da3d..93f041943bdd 100644
> --- a/drivers/net/ethernet/intel/ice/ice_adapter.h
> +++ b/drivers/net/ethernet/intel/ice/ice_adapter.h
> @@ -17,15 +17,19 @@ struct ice_pf;
> /**
> * struct ice_port_list - data used to store the list of adapter
> ports
> *
...
> struct ice_ptp_tx tx;
> struct kthread_delayed_work ov_work;
> struct mutex ps_lock; /* protects overall PTP PHY start
> procedure */ diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c
> b/drivers/net/ethernet/intel/ice/ice_adapter.c
> index 2dc3629d6d0f..84ac5ee5a739 100644
> --- a/drivers/net/ethernet/intel/ice/ice_adapter.c
> +++ b/drivers/net/ethernet/intel/ice/ice_adapter.c
> @@ -6,6 +6,7 @@
> #include <linux/pci.h>
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> +#include <linux/srcu.h>
> #include <linux/xarray.h>
> #include "ice_adapter.h"
> #include "ice.h"
> @@ -66,18 +67,20 @@ static struct ice_adapter *ice_adapter_new(struct
> pci_dev *pdev)
> mutex_init(&adapter->cpi_phy_lock[i]);
> refcount_set(&adapter->refcount, 1);
>
> - mutex_init(&adapter->ports.lock);
> - INIT_LIST_HEAD(&adapter->ports.ports);
> + spin_lock_init(&adapter->ports.lock);
> + INIT_LIST_HEAD(&adapter->ports.list);
> + init_srcu_struct(&adapter->ports.srcu);
In theory init_srcu_struct() can fail, so error code should be handled, isn't it?
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
>
> return adapter;
> }
>
> static void ice_adapter_free(struct ice_adapter *adapter) {
...
> }
>
> /**
>
> --
> 2.56.0.rc0.395.gd1f3524e15dc
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access
2026-09-23 9:47 ` Loktionov, Aleksandr
@ 2026-09-23 20:28 ` Jacob Keller
0 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-23 20:28 UTC (permalink / raw)
To: Loktionov, Aleksandr, Nitka, Grzegorz, Kubalewski, Arkadiusz,
Intel Wired LAN, Machnikowski, Maciej, Korba, Przemyslaw,
netdev@vger.kernel.org, Nguyen, Anthony L
On 9/23/2026 2:47 AM, Loktionov, Aleksandr wrote:
>
>
>> -----Original Message-----
>> From: Jacob Keller <jacob.e.keller@intel.com>
>> Sent: Tuesday, September 22, 2026 8:03 PM
>> To: Keller, Jacob E <jacob.e.keller@intel.com>; Nitka, Grzegorz
>> <grzegorz.nitka@intel.com>; Kubalewski, Arkadiusz
>> <arkadiusz.kubalewski@intel.com>; Intel Wired LAN <intel-wired-
>> lan@lists.osuosl.org>; Machnikowski, Maciej
>> <maciej.machnikowski@intel.com>; Korba, Przemyslaw
>> <przemyslaw.korba@intel.com>; netdev@vger.kernel.org; Nguyen, Anthony
>> L <anthony.l.nguyen@intel.com>
>> Cc: Keller, Jacob E <jacob.e.keller@intel.com>; Machnikowski, Maciej
>> <maciej.machnikowski@intel.com>
>> Subject: [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU
>> for PTP port access
>>
>> The ice adapter structure maintains a list of ports associated with
>> the adapter. This is used for supporting PTP, where the clock owner
>> must handle many operations that require access to the PTP port
>> structures of the associated PFs.
>>
>> This is implemented using a linked list and a mutex. This sort of
>> works, but a few places within the code do not acquire the mutex when
>> iterating the list. This includes ice_ptp_flush_all_tx_tracker(),
>> ice_ptp_restart_all_phy(), and ice_ptp_prepare_rebuild_sec().
>>
>> Fixing this is tricky, especially since it is not clear if we can
>> simply acquire the lock around the complete iterations.
>>
>> The pattern of use for the port list is read-mostly with modifications
>> only happening during PF initialization when elements are inserted.
>> This typically only happens during early boot, though a PF could in
>> principle be removed or loaded at arbitrary times via bind and unbind
>> operations.
>>
>> The use of a mutex does mean the driver can sleep while holding it,
>> but it still creates complicates with lock ordering and prevents
>> iterating the list in any code path that *can't* sleep.
>>
>> Instead, use SRCU primitives for the port linked list, along with a
>> reference count on the port. The kref reference counter ensures that a
>> PF will have a valid lifetime and not be removed until the reference
>> is released. Note that this change focuses solely on the port list and
>> does not make an effort to resolve access to ctrl_pf, which is
>> currently being investigated by another developer.
>>
>> Use of sleepable RCU is required because we often iterate the PTP port
>> list and perform operations that might sleep. Attempts at implementing
>> regular RCU have thus far not proven to be acceptable.
>>
>> The remove path first removes the port from the list, and we use
>> kref_get_unless_zero to ensure that such ports are skipped when
>> iterating the list. This ensures that once a port starts removing we
>> will drop references and no longer be able to acquire new ones. This
>> avoids loop iterations chaining together to indefinitely block
>> removal.
>>
>> The ice_ptp_release_port_srcu() function is used as the release
>> function for the kref_put() call. This uses wake_up_var() to wake the
>> removing thread.
>> The waiting thread will block until the final reference has been
>> removed, then it will use synchronize_srcu() to ensure any outstanding
>> SRCU critical sections have had the necessary grace period.
>>
>> This flow ensures that accesses to ports via the adapter port list
>> will remain valid until both the SRCU critical sections have ended and
>> all the references to the ports have been dropped. Strictly speaking,
>> SRCU alone might be sufficient for existing code paths, but the
>> reference count allows the option for passing a pointer to the port on
>> to other functions if necessary in the future.
>>
>> One major complication of this reference count is that ice_ptp_port is
>> embedded inside of other structures and not merely allocated. As a
>> result, we can't use the standard pattern of kfree_rcu() to just delay
>> freeing until references are dropped, and instead are delaying PF port
>> teardown. If any code path leaks the reference, the driver will be
>> unable to teardown.
>> Instead, a 15 second timeout with a WARN() is used when waiting to
>> finally allow PF teardown to continue. This has the risk of
>> potentially allowing use-after-free, assuming some path really is
>> stuck for 15 seconds. However, this both less likely and a less bad
>> outcome compared to blocking indefinitely on a reference leak.
>>
>> Fixes: e800654e85b5 ("ice: Use ice_adapter for PTP shared data instead
>> of auxdev")
>> Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
>> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
>> ---
>> drivers/net/ethernet/intel/ice/ice_adapter.h | 16 +--
>> drivers/net/ethernet/intel/ice/ice_ptp.h | 4 +
>> drivers/net/ethernet/intel/ice/ice_adapter.c | 11 +-
>> drivers/net/ethernet/intel/ice/ice_ptp.c | 145
>> +++++++++++++++++++++------
>> 4 files changed, 134 insertions(+), 42 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h
>> b/drivers/net/ethernet/intel/ice/ice_adapter.h
>> index 4f695f32da3d..93f041943bdd 100644
>> --- a/drivers/net/ethernet/intel/ice/ice_adapter.h
>> +++ b/drivers/net/ethernet/intel/ice/ice_adapter.h
>> @@ -17,15 +17,19 @@ struct ice_pf;
>> /**
>> * struct ice_port_list - data used to store the list of adapter
>> ports
>> *
>
> ...
>
>> struct ice_ptp_tx tx;
>> struct kthread_delayed_work ov_work;
>> struct mutex ps_lock; /* protects overall PTP PHY start
>> procedure */ diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c
>> b/drivers/net/ethernet/intel/ice/ice_adapter.c
>> index 2dc3629d6d0f..84ac5ee5a739 100644
>> --- a/drivers/net/ethernet/intel/ice/ice_adapter.c
>> +++ b/drivers/net/ethernet/intel/ice/ice_adapter.c
>> @@ -6,6 +6,7 @@
>> #include <linux/pci.h>
>> #include <linux/slab.h>
>> #include <linux/spinlock.h>
>> +#include <linux/srcu.h>
>> #include <linux/xarray.h>
>> #include "ice_adapter.h"
>> #include "ice.h"
>> @@ -66,18 +67,20 @@ static struct ice_adapter *ice_adapter_new(struct
>> pci_dev *pdev)
>> mutex_init(&adapter->cpi_phy_lock[i]);
>> refcount_set(&adapter->refcount, 1);
>>
>> - mutex_init(&adapter->ports.lock);
>> - INIT_LIST_HEAD(&adapter->ports.ports);
>> + spin_lock_init(&adapter->ports.lock);
>> + INIT_LIST_HEAD(&adapter->ports.list);
>> + init_srcu_struct(&adapter->ports.srcu);
> In theory init_srcu_struct() can fail, so error code should be handled, isn't it?
>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
>
Oop. I think I was looking at some sample code that didn't check it.
Will fix.
>>
>> return adapter;
>> }
>>
>> static void ice_adapter_free(struct ice_adapter *adapter) {
>
> ...
>
>> }
>>
>> /**
>>
>> --
>> 2.56.0.rc0.395.gd1f3524e15dc
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset
2026-09-23 9:41 ` Loktionov, Aleksandr
@ 2026-09-23 20:28 ` Jacob Keller
0 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-23 20:28 UTC (permalink / raw)
To: Loktionov, Aleksandr, Nitka, Grzegorz, Kubalewski, Arkadiusz,
Intel Wired LAN, Machnikowski, Maciej, Korba, Przemyslaw,
netdev@vger.kernel.org, Nguyen, Anthony L
On 9/23/2026 2:41 AM, Loktionov, Aleksandr wrote:
>
>
>> -----Original Message-----
>> From: Jacob Keller <jacob.e.keller@intel.com>
>> Sent: Tuesday, September 22, 2026 8:03 PM
>> To: Keller, Jacob E <jacob.e.keller@intel.com>; Nitka, Grzegorz
>> <grzegorz.nitka@intel.com>; Kubalewski, Arkadiusz
>> <arkadiusz.kubalewski@intel.com>; Intel Wired LAN <intel-wired-
>> lan@lists.osuosl.org>; Machnikowski, Maciej
>> <maciej.machnikowski@intel.com>; Korba, Przemyslaw
>> <przemyslaw.korba@intel.com>; netdev@vger.kernel.org; Nguyen,
>> Anthony L <anthony.l.nguyen@intel.com>
>> Cc: Keller, Jacob E <jacob.e.keller@intel.com>; Machnikowski, Maciej
>> <maciej.machnikowski@intel.com>
>> Subject: [PATCH iwl-net v2 09/15] ice: E825: clear
>> PHY_REG_TX_MEMORY_STATUS prior to soft reset
>>
>> The current implementation of ice_ptp_reset_ts_memory_eth56g() is
>> flawed.
>> It tries to clear the timestamp memory by writing to the
>> PHY_REG_TX_MEMORY_STATUS region. This does not work properly, as it
>> does not trigger appropriate PHY actions.
>>
>> To clear outstanding timestamp memory, the driver must read the
>> timestamps.
>> However, naively doing this as part of ice_ptp_reset_ts_memory() is
>> problematic. When reading the timestamp index, hardware kicks off a
>> chain of actions including clearing the ready bitmap index, and
>> decrementing an internal counter if the timestamp index was marked
>> as valid.
>>
>> This can potentially leave the internal hardware counter out of sync
>> with the actual number of timestamps. This occurs because the
>> PHY_REG_TX_MEMORY_STATUS region is not zero-initialized when the
>> device boots up. Instead, it is filled with garbage. On a cold power
>> on, attempts to read the stale data result in the hardware
>> triggering a counter decrement for a timestamp that never happened.
>> This underflows the counter, and prevents new timestamp interrupts
>> from being triggered for real timestamp requests.
>>
>> We must read the PHY_REG_TX_MEMORY_STATUS in order to clear stale
>> timestamps. But doing so may cause a desync with the counter. To
>> prevent issues, perform this clearing always and only right before
>> initiating a PHY soft reset.
>>
>> The soft reset will clear and reset the internal counter and the
>> ready bitmap. The reads to PHY_REG_TX_MEMORY_STATUS will reset the
>> region valid bits ensuring that no stale data is left behind. This
>> combination ensures that we always have a clean slate with no stale
>> data and with the counter properly reset to zero.
>>
>> If a read fails (i.e. due to a transient sideband queue failure) it
>> is not treated as a fatal error. There is already a dynamic debug
>> message for each read failure. Keep track of the total number of
>> timestamp registers that fail for a given port and log a warning
>> message indicating the total number of failures. Continuing is
>> acceptable as clearing the memory is a precaution against faulty
>> software. Correct software access won't read the index twice and
>> won't read it at all except when it has already confirmed that the
>> index is valid by reading ice_get_phy_tx_tstamp_ready().
>>
>> Fixes: 3ec46e157c7f ("ice: perform PHY soft reset for E825C ports at
>> initialization")
>> Reviewed-by: Maciek Machnikowski <maciej.machnikowski@intel.com>
>> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
>> ---
>> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 100 +++++++++++++++--
>> -----------
>> 1 file changed, 52 insertions(+), 48 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>> b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>> index c8a67a307832..e1a5ff5793d1 100644
>> --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>> +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
>> @@ -737,24 +737,6 @@ static int ice_read_port_mem_eth56g(struct
>> ice_hw *hw, u8 port, u16 offset,
>> return ice_read_port_eth56g(hw, port, offset, val,
>> ETH56G_PHY_MEM_PTP); }
>>
>
> ...
>
>> return err;
>> }
>>
>> @@ -1177,19 +1147,43 @@ static int
>> ice_clear_ptp_tstamp_eth56g(struct ice_hw *hw, u8 port, u8 idx) }
>>
>> /**
>> - * ice_ptp_reset_ts_memory_eth56g - Clear all timestamps from the
>> port block
>> + * ice_ptp_clear_tx_memory_status_eth56g - Reset one port's Tx
>> + timestamp memory
>> * @hw: pointer to the HW struct
>> + * @port: port number to clear
>> + *
>> + * Fully reset a single PHY port's Tx timestamp memory. Per the PHY
>> + spec, the
>> + * only way to clear a timestamp valid bit (and its read-only
>> + ts_memory_status
>> + * bit) is to read the timestamp memory location, so read every
>> entry
>> + for the
>> + * port (two 32-bit reads each). This discards all timestamp data
>> on
>> + the port,
>> + * so it must only be used for a full reset; callers that must
>> preserve
>> + * in-flight timestamps clear individual indices via
>> ice_clear_phy_tstamp().
>> + *
>> + * Due to interactions with an internal HW counter for the number
>> of
>> + * outstanding Tx timestamps, this *must* only be called as part of
>> the
>> + * ice_ptp_phy_soft_reset_eth56g() procedure. Otherwise, the
>> internal
>> + counter
>> + * may become out of sync and prevent new timestamp interrupts.
>> + *
>> + * Failure to read a given index (i.e. due to a transient sideband
>> + queue
>> + * failure) is not considered fatal as the PHY port is about to be
>> soft reset.
>> + * While the soft reset does not clear the timestamp memory,
>> software
>> + * shouldn't be reading the timestamp memory without already
>> knowing it
>> + is
>> + * valid via ice_get_phy_tx_tstamp_ready(), and this sweep is just
>> + * a precaution to ensure the memory is in a known state.
>> */
>> -static void ice_ptp_reset_ts_memory_eth56g(struct ice_hw *hw)
>> +static void ice_ptp_clear_tx_memory_status_eth56g(struct ice_hw
>> *hw, u8
>> +port)
>> {
>> - unsigned int port;
>> + int err, failed = 0;
>> + u8 idx;
>>
>> - for (port = 0; port < hw->ptp.num_lports; port++) {
>> - ice_write_ptp_reg_eth56g(hw, port,
>> PHY_REG_TX_MEMORY_STATUS_L,
>> - 0);
>> - ice_write_ptp_reg_eth56g(hw, port,
>> PHY_REG_TX_MEMORY_STATUS_U,
>> - 0);
>> + for (idx = 0; idx < INDEX_PER_PORT; idx++) {
>> + err = ice_clear_ptp_tstamp_eth56g(hw, port, idx);
>> + if (err)
>> + failed++;
>> }
>> +
>> + if (failed)
>> + dev_warn(ice_hw_to_dev(hw), "Failed to clear %d PHY
>> timestamp registers for port %u\n",
>> + port, failed);
> Failed, port arguments looks like swaped.
>
Yep. Will fix.
>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes
2026-09-22 18:22 ` [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jakub Kicinski
@ 2026-09-23 20:31 ` Jacob Keller
0 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-23 20:31 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Grzegorz Nitka, Arkadiusz Kubalewski, Intel Wired LAN,
Maciej Machnikowski, Przemyslaw Korba, netdev, Anthony Nguyen,
Aleksandr Loktionov, Petr Oros, Paul Menzel
On 9/22/2026 11:22 AM, Jakub Kicinski wrote:
> On Tue, 22 Sep 2026 11:02:33 -0700 Jacob Keller wrote:
>> Subject: [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes
>
> I think we need to reconsider iwl cross-posting to netdev.
> I can't get my unread email count to stay under 1000, and
> I don't think the cross-posting is really bringing much
> benefit to the community. IOW only Simon and Intel people
> look at the Intel postings, anyway.
I can't remember the reason or source of why we started doing this. I'm
happy to avoid spamming netdev for the intermediate versions of series.
I think it makes sense to include netdev only when we're doing something
outside driver work or believe its particularly controversial.
I think Tony reached out to the rest of the team to see if they recall
more. Of course, the maintainers wishes here take precedence.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
` (15 preceding siblings ...)
2026-09-22 18:22 ` [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jakub Kicinski
@ 2026-09-24 1:05 ` Jacob Keller
16 siblings, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-09-24 1:05 UTC (permalink / raw)
To: Grzegorz Nitka, Arkadiusz Kubalewski, Intel Wired LAN,
Maciej Machnikowski, Przemyslaw Korba, Anthony Nguyen
Cc: Aleksandr Loktionov, Petr Oros, Paul Menzel
On 9/22/2026 11:02 AM, Jacob Keller wrote:
> Jake Keller says:
>
> This series contains several related fixes for the ice driver PTP logic
> relating to timestamp handling and device (re)initialization.
Sashiko once again still had a few nits, so i'll need to make a v3. To
help reduce list noise, I'll work with Tony to get a version that fully
passes our internal Sashiko runs without issues before posting a v3.
Thanks,
Jake
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-09-24 1:06 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-22 18:02 [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 01/15] ice: use reference counting and SRCU for PTP port access Jacob Keller
2026-09-23 9:47 ` Loktionov, Aleksandr
2026-09-23 20:28 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 02/15] ice: fix PHY port restart serialization Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 03/15] ice: fix removal of PTP timestamp tracker during reset Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 04/15] ice: set in_use only after preparing Tx timestamp index Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 05/15] ice: E822: keep Tx timestamps disabled during offset calibration Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 06/15] ice: E822: flush offset verification work during reset preparation Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 07/15] ice: call PTP link change only from link events Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 08/15] ice: E825: stop clearing PHY_REG_TX_OFFSET_READY Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 09/15] ice: E825: clear PHY_REG_TX_MEMORY_STATUS prior to soft reset Jacob Keller
2026-09-23 9:41 ` Loktionov, Aleksandr
2026-09-23 20:28 ` Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 10/15] ice: E825: perform a soft reset when starting the PHY timer Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 11/15] ice: wait for in-flight Tx timestamps before flushing the tracker Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 12/15] ice: keep Tx timestamp slots tracked until completion or timeout Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 13/15] ice: skip reading Tx ready bitmap on ports with no timestamps Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 14/15] ice: don't clear in_use until HW clears ready bitmap Jacob Keller
2026-09-22 18:02 ` [PATCH iwl-net v2 15/15] ice: Recalibrate PHY after settime64 on E825-C Jacob Keller
2026-09-22 18:22 ` [PATCH iwl-net v2 00/15] ice: E82x: timestamp processing logic fixes Jakub Kicinski
2026-09-23 20:31 ` Jacob Keller
2026-09-24 1:05 ` Jacob Keller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox