Skip to content

Commit 88b68f3

Browse files
jacob-kelleranguy11
authored andcommitted
ice: PTP: fix missing timestamps on E825 hardware
The E825 hardware currently has each PF handle the PFINT_TSYN_TX cause of the miscellaneous OICR interrupt vector. The actual interrupt cause underlying this is shared by all ports on the same quad: ┌─────────────────────────────────┐ │ │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ │PF 0│ │PF 1│ │PF 2│ │PF 3│ │ │ └────┘ └────┘ └────┘ └────┘ │ │ │ └────────────────▲────────────────┘ │ │ ┌────────────────┼────────────────┐ │ PHY QUAD │ └───▲────────▲────────▲────────▲──┘ │ │ │ │ ┌───┼──┐ ┌───┴──┐ ┌───┼──┐ ┌───┼──┐ │Port 0│ │Port 1│ │Port 2│ │Port 3│ └──────┘ └──────┘ └──────┘ └──────┘ If multiple PFs issue Tx timestamp requests near simultaneously, it is possible that the correct PF will not be interrupted and will miss its timestamp. Understanding why is somewhat complex. Consider the following sequence of events: CPU 0: Send Tx packet on PF 0 ... PF 0 enqueues packet with Tx request CPU 1, PF1: ... Send Tx packet on PF1 ... PF 1 enqueues packet with Tx request HW: PHY Port 0 sends packet PHY raises Tx timestamp event interrupt MAC raises each PF interrupt CPU 0, PF0: CPU 1, PF1: ice_misc_intr() checks for Tx timestamps ice_misc_intr() checks for Tx timestamp Sees packet ready bit set Sees nothing available ... Exits ... ... HW: PHY port 1 sends packet PHY interrupt ignored because not all packet timestamps read yet. ... Read timestamp, report to stack Because the interrupt event is shared for all ports on the same quad, the PHY will not raise a new interrupt for any PF until all timestamps are read. In the example above, the second timestamp comes in for port 1 before the timestamp from port 0 is read. At this point, there is no longer an interrupt thread running that will read the timestamps, because each PF has checked and found that there was no work to do. Applications such as ptp4l will timeout after waiting a few milliseconds. Eventually, the watchdog service task will re-check for all quads and notice that there are outstanding timestamps, and issue a software interrupt to recover. However, by this point it is far too late, and applications have already failed. All of this occurs because of the underlying hardware behavior. The PHY cannot raise a new interrupt signal until all outstanding timestamps have been read. As a first step to fix this, switch the E825C hardware to the ICE_PTP_TX_INTERRUPT_ALL mode. In this mode, only the clock owner PF will respond to the PFINT_TSYN_TX cause. Other PFs disable this cause and will not wake. In this mode, the clock owner will iterate over all ports and handle timestamps for each connected port. This matches the E822 behavior, and is a necessary but insufficient step to resolve the missing timestamps. Even with use of the ICE_PTP_TX_INTERRUPT_ALL mode, we still sometimes miss a timestamp event. The ice_ptp_tx_tstamp_owner() does re-check the ready bitmap, but does so before re-enabling the OICR interrupt vector. It also only checks the ready bitmap, but not the software Tx timestamp tracker. To avoid risk of losing a timestamp, refactor the logic to check both the software Tx timestamp tracker bitmap *and* the hardware ready bitmap. Additionally, do this outside of ice_ptp_process_ts() after we have already re-enabled the OICR interrupt. Remove the checks from the ice_ptp_tx_tstamp(), ice_ptp_tx_tstamp_owner(), and the ice_ptp_process_ts() functions. This results in ice_ptp_tx_tstamp() being nothing more than a wrapper around ice_ptp_process_tx_tstamp() so we can remove it. Add the ice_ptp_tx_tstamps_pending() function which returns a boolean indicating if there are any pending Tx timestamps. First, check the software timestamp tracker bitmap. In ICE_PTP_TX_INTERRUPT_ALL mode, check *all* ports software trackers. If a tracker has outstanding timestamp requests, return true. Additionally, check the PHY ready bitmap to confirm if the PHY indicates any outstanding timestamps. In the ice_misc_thread_fn(), call ice_ptp_tx_tstamps_pending() just before returning from the IRQ thread handler. If it returns true, write to PFINT_OICR to trigger a PFINT_OICR_TSYN_TX_M software interrupt. This will force the handler to interrupt again and complete the work even if the PHY hardware did not interrupt for any reason. This results in the following new flow for handling Tx timestamps: 1) send Tx packet 2) PHY captures timestamp 3) PHY triggers MAC interrupt 4) clock owner executes ice_misc_intr() with PFINT_OICR_TSYN_TX flag set 5) ice_ptp_ts_irq() returns IRQ_WAKE_THREAD 7) The interrupt thread wakes up and kernel calls ice_misc_intr_thread_fn() 8) ice_ptp_process_ts() is called to handle any outstanding timestamps 9) ice_irq_dynamic_ena() is called to re-enable the OICR hardware interrupt cause 10) ice_ptp_tx_tstamps_pending() is called to check if we missed any more outstanding timestamps, checking both software and hardware indicators. With this change, it should no longer be possible for new timestamps to come in such a way that we lose an interrupt. If a timestamp comes in before the ice_ptp_tx_tstamps_pending() call, it will be noticed by at least one of the software bitmap check or the hardware bitmap check. If the timestamp comes in *after* this check, it should cause a timestamp interrupt as we have already read all timestamps from the PHY and the OICR vector has been re-enabled. Fixes: 7cab44f ("ice: Introduce ETH56G PHY model for E825C products") Signed-off-by: Jacob Keller <[email protected]> Reviewed-by: Aleksandr Loktionov <[email protected]> Reviewed-by: Przemyslaw Korba <[email protected]> Tested-by: Vitaly Grinberg <[email protected]> Tested-by: Sunitha Mekala <[email protected]> (A Contingent worker at Intel) Signed-off-by: Tony Nguyen <[email protected]>
1 parent 99854c1 commit 88b68f3

3 files changed

Lines changed: 103 additions & 78 deletions

File tree

drivers/net/ethernet/intel/ice/ice_main.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3314,18 +3314,20 @@ static irqreturn_t ice_misc_intr_thread_fn(int __always_unused irq, void *data)
33143314
if (ice_is_reset_in_progress(pf->state))
33153315
goto skip_irq;
33163316

3317-
if (test_and_clear_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread)) {
3318-
/* Process outstanding Tx timestamps. If there is more work,
3319-
* re-arm the interrupt to trigger again.
3320-
*/
3321-
if (ice_ptp_process_ts(pf) == ICE_TX_TSTAMP_WORK_PENDING) {
3322-
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
3323-
ice_flush(hw);
3324-
}
3325-
}
3317+
if (test_and_clear_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread))
3318+
ice_ptp_process_ts(pf);
33263319

33273320
skip_irq:
33283321
ice_irq_dynamic_ena(hw, NULL, NULL);
3322+
ice_flush(hw);
3323+
3324+
if (ice_ptp_tx_tstamps_pending(pf)) {
3325+
/* If any new Tx timestamps happened while in interrupt,
3326+
* re-arm the interrupt to trigger it again.
3327+
*/
3328+
wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
3329+
ice_flush(hw);
3330+
}
33293331

33303332
return IRQ_HANDLED;
33313333
}

drivers/net/ethernet/intel/ice/ice_ptp.c

Lines changed: 84 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,9 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
573573
pf = ptp_port_to_pf(ptp_port);
574574
hw = &pf->hw;
575575

576+
if (!tx->init)
577+
return;
578+
576579
/* Read the Tx ready status first */
577580
if (tx->has_ready_bitmap) {
578581
err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready);
@@ -674,14 +677,9 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
674677
pf->ptp.tx_hwtstamp_good += tstamp_good;
675678
}
676679

677-
/**
678-
* ice_ptp_tx_tstamp_owner - Process Tx timestamps for all ports on the device
679-
* @pf: Board private structure
680-
*/
681-
static enum ice_tx_tstamp_work ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
680+
static void ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
682681
{
683682
struct ice_ptp_port *port;
684-
unsigned int i;
685683

686684
mutex_lock(&pf->adapter->ports.lock);
687685
list_for_each_entry(port, &pf->adapter->ports.ports, list_node) {
@@ -693,49 +691,6 @@ static enum ice_tx_tstamp_work ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
693691
ice_ptp_process_tx_tstamp(tx);
694692
}
695693
mutex_unlock(&pf->adapter->ports.lock);
696-
697-
for (i = 0; i < ICE_GET_QUAD_NUM(pf->hw.ptp.num_lports); i++) {
698-
u64 tstamp_ready;
699-
int err;
700-
701-
/* Read the Tx ready status first */
702-
err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
703-
if (err)
704-
break;
705-
else if (tstamp_ready)
706-
return ICE_TX_TSTAMP_WORK_PENDING;
707-
}
708-
709-
return ICE_TX_TSTAMP_WORK_DONE;
710-
}
711-
712-
/**
713-
* ice_ptp_tx_tstamp - Process Tx timestamps for this function.
714-
* @tx: Tx tracking structure to initialize
715-
*
716-
* Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding incomplete
717-
* Tx timestamps, or ICE_TX_TSTAMP_WORK_DONE otherwise.
718-
*/
719-
static enum ice_tx_tstamp_work ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
720-
{
721-
bool more_timestamps;
722-
unsigned long flags;
723-
724-
if (!tx->init)
725-
return ICE_TX_TSTAMP_WORK_DONE;
726-
727-
/* Process the Tx timestamp tracker */
728-
ice_ptp_process_tx_tstamp(tx);
729-
730-
/* Check if there are outstanding Tx timestamps */
731-
spin_lock_irqsave(&tx->lock, flags);
732-
more_timestamps = tx->init && !bitmap_empty(tx->in_use, tx->len);
733-
spin_unlock_irqrestore(&tx->lock, flags);
734-
735-
if (more_timestamps)
736-
return ICE_TX_TSTAMP_WORK_PENDING;
737-
738-
return ICE_TX_TSTAMP_WORK_DONE;
739694
}
740695

741696
/**
@@ -2666,30 +2621,92 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb)
26662621
return idx + tx->offset;
26672622
}
26682623

2669-
/**
2670-
* ice_ptp_process_ts - Process the PTP Tx timestamps
2671-
* @pf: Board private structure
2672-
*
2673-
* Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding Tx
2674-
* timestamps that need processing, and ICE_TX_TSTAMP_WORK_DONE otherwise.
2675-
*/
2676-
enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf)
2624+
void ice_ptp_process_ts(struct ice_pf *pf)
26772625
{
26782626
switch (pf->ptp.tx_interrupt_mode) {
26792627
case ICE_PTP_TX_INTERRUPT_NONE:
26802628
/* This device has the clock owner handle timestamps for it */
2681-
return ICE_TX_TSTAMP_WORK_DONE;
2629+
return;
26822630
case ICE_PTP_TX_INTERRUPT_SELF:
26832631
/* This device handles its own timestamps */
2684-
return ice_ptp_tx_tstamp(&pf->ptp.port.tx);
2632+
ice_ptp_process_tx_tstamp(&pf->ptp.port.tx);
2633+
return;
26852634
case ICE_PTP_TX_INTERRUPT_ALL:
26862635
/* This device handles timestamps for all ports */
2687-
return ice_ptp_tx_tstamp_owner(pf);
2636+
ice_ptp_tx_tstamp_owner(pf);
2637+
return;
2638+
default:
2639+
WARN_ONCE(1, "Unexpected Tx timestamp interrupt mode %u\n",
2640+
pf->ptp.tx_interrupt_mode);
2641+
return;
2642+
}
2643+
}
2644+
2645+
static bool ice_port_has_timestamps(struct ice_ptp_tx *tx)
2646+
{
2647+
bool more_timestamps;
2648+
2649+
scoped_guard(spinlock_irqsave, &tx->lock) {
2650+
if (!tx->init)
2651+
return false;
2652+
2653+
more_timestamps = !bitmap_empty(tx->in_use, tx->len);
2654+
}
2655+
2656+
return more_timestamps;
2657+
}
2658+
2659+
static bool ice_any_port_has_timestamps(struct ice_pf *pf)
2660+
{
2661+
struct ice_ptp_port *port;
2662+
2663+
scoped_guard(mutex, &pf->adapter->ports.lock) {
2664+
list_for_each_entry(port, &pf->adapter->ports.ports,
2665+
list_node) {
2666+
struct ice_ptp_tx *tx = &port->tx;
2667+
2668+
if (ice_port_has_timestamps(tx))
2669+
return true;
2670+
}
2671+
}
2672+
2673+
return false;
2674+
}
2675+
2676+
bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
2677+
{
2678+
struct ice_hw *hw = &pf->hw;
2679+
unsigned int i;
2680+
2681+
/* Check software indicator */
2682+
switch (pf->ptp.tx_interrupt_mode) {
2683+
case ICE_PTP_TX_INTERRUPT_NONE:
2684+
return false;
2685+
case ICE_PTP_TX_INTERRUPT_SELF:
2686+
if (ice_port_has_timestamps(&pf->ptp.port.tx))
2687+
return true;
2688+
break;
2689+
case ICE_PTP_TX_INTERRUPT_ALL:
2690+
if (ice_any_port_has_timestamps(pf))
2691+
return true;
2692+
break;
26882693
default:
26892694
WARN_ONCE(1, "Unexpected Tx timestamp interrupt mode %u\n",
26902695
pf->ptp.tx_interrupt_mode);
2691-
return ICE_TX_TSTAMP_WORK_DONE;
2696+
break;
2697+
}
2698+
2699+
/* Check hardware indicator */
2700+
for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) {
2701+
u64 tstamp_ready = 0;
2702+
int err;
2703+
2704+
err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
2705+
if (err || tstamp_ready)
2706+
return true;
26922707
}
2708+
2709+
return false;
26932710
}
26942711

26952712
/**
@@ -2741,7 +2758,9 @@ irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
27412758
return IRQ_WAKE_THREAD;
27422759
case ICE_MAC_E830:
27432760
/* E830 can read timestamps in the top half using rd32() */
2744-
if (ice_ptp_process_ts(pf) == ICE_TX_TSTAMP_WORK_PENDING) {
2761+
ice_ptp_process_ts(pf);
2762+
2763+
if (ice_ptp_tx_tstamps_pending(pf)) {
27452764
/* Process outstanding Tx timestamps. If there
27462765
* is more work, re-arm the interrupt to trigger again.
27472766
*/
@@ -3194,8 +3213,9 @@ static void ice_ptp_init_tx_interrupt_mode(struct ice_pf *pf)
31943213
{
31953214
switch (pf->hw.mac_type) {
31963215
case ICE_MAC_GENERIC:
3197-
/* E822 based PHY has the clock owner process the interrupt
3198-
* for all ports.
3216+
case ICE_MAC_GENERIC_3K_E825:
3217+
/* E82x hardware has the clock owner process timestamps for
3218+
* all ports.
31993219
*/
32003220
if (ice_pf_src_tmr_owned(pf))
32013221
pf->ptp.tx_interrupt_mode = ICE_PTP_TX_INTERRUPT_ALL;

drivers/net/ethernet/intel/ice/ice_ptp.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,9 @@ void ice_ptp_extts_event(struct ice_pf *pf);
304304
s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb);
305305
void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx);
306306
void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx);
307-
enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf);
307+
void ice_ptp_process_ts(struct ice_pf *pf);
308308
irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf);
309+
bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf);
309310
u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf,
310311
struct ptp_system_timestamp *sts);
311312

@@ -345,16 +346,18 @@ static inline void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
345346

346347
static inline void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx) { }
347348

348-
static inline bool ice_ptp_process_ts(struct ice_pf *pf)
349-
{
350-
return true;
351-
}
349+
static inline void ice_ptp_process_ts(struct ice_pf *pf) { }
352350

353351
static inline irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
354352
{
355353
return IRQ_HANDLED;
356354
}
357355

356+
static inline bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
357+
{
358+
return false;
359+
}
360+
358361
static inline u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf,
359362
struct ptp_system_timestamp *sts)
360363
{

0 commit comments

Comments
 (0)