Skip to content

Commit 448a5e6

Browse files
committed
ice: add lock to protect low latency interface
JIRA: https://issues.redhat.com/browse/RHEL-74286 Upstream commit(s): commit 5032722 Author: Jacob Keller <jacob.e.keller@intel.com> Date: Mon Dec 16 09:53:30 2024 -0500 ice: add lock to protect low latency interface Newer firmware for the E810 devices support a 'low latency' interface to interact with the PHY without using the Admin Queue. This is interacted with via the REG_LL_PROXY_L and REG_LL_PROXY_H registers. Currently, this interface is only used for Tx timestamps. There are two different mechanisms, including one which uses an interrupt for firmware to signal completion. However, these two methods are mutually exclusive, so no synchronization between them was necessary. This low latency interface is being extended in future firmware to support also programming the PHY timers. Use of the interface for PHY timers will need synchronization to ensure there is no overlap with a Tx timestamp. The interrupt-based response complicates the locking somewhat. We can't use a simple spinlock. This would require being acquired in ice_ptp_req_tx_single_tstamp, and released in ice_ptp_complete_tx_single_tstamp. The ice_ptp_req_tx_single_tstamp function is called from the threaded IRQ, and the ice_ptp_complete_tx_single_stamp is called from the low latency IRQ, so we would need to acquire the lock with IRQs disabled. To handle this, we'll use a wait queue along with wait_event_interruptible_locked_irq in the update flows which don't use the interrupt. The interrupt flow will acquire the wait queue lock, set the ATQBAL_FLAGS_INTR_IN_PROGRESS, and then initiate the firmware low latency request, and unlock the wait queue lock. Upon receipt of the low latency interrupt, the lock will be acquired, the ATQBAL_FLAGS_INTR_IN_PROGRESS bit will be cleared, and the firmware response will be captured, and wake_up_locked() will be called on the wait queue. The other flows will use wait_event_interruptible_locked_irq() to wait until the ATQBAL_FLAGS_INTR_IN_PROGRESS is clear. This function checks the condition under lock, but does not hold the lock while waiting. On return, the lock is held, and a return of zero indicates we hold the lock and the in-progress flag is not set. This will ensure that threads which need to use the low latency interface will sleep until they can acquire the lock without any pending low latency interrupt flow interfering. Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Milena Olech <milena.olech@intel.com> Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com> Signed-off-by: Petr Oros <poros@redhat.com>
1 parent c16a5a8 commit 448a5e6

File tree

3 files changed

+62
-8
lines changed

3 files changed

+62
-8
lines changed

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

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,9 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
464464
*/
465465
void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
466466
{
467+
struct ice_e810_params *params;
467468
struct ice_ptp_port *ptp_port;
469+
unsigned long flags;
468470
struct sk_buff *skb;
469471
struct ice_pf *pf;
470472

@@ -473,6 +475,7 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
473475

474476
ptp_port = container_of(tx, struct ice_ptp_port, tx);
475477
pf = ptp_port_to_pf(ptp_port);
478+
params = &pf->hw.ptp.phy.e810;
476479

477480
/* Drop packets which have waited for more than 2 seconds */
478481
if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) {
@@ -489,11 +492,17 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
489492

490493
ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx);
491494

495+
spin_lock_irqsave(&params->atqbal_wq.lock, flags);
496+
497+
params->atqbal_flags |= ATQBAL_FLAGS_INTR_IN_PROGRESS;
498+
492499
/* Write TS index to read to the PF register so the FW can read it */
493500
wr32(&pf->hw, REG_LL_PROXY_H,
494501
REG_LL_PROXY_H_TS_INTR_ENA | FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) |
495502
REG_LL_PROXY_H_EXEC);
496503
tx->last_ll_ts_idx_read = idx;
504+
505+
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
497506
}
498507

499508
/**
@@ -504,35 +513,52 @@ void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx)
504513
{
505514
struct skb_shared_hwtstamps shhwtstamps = {};
506515
u8 idx = tx->last_ll_ts_idx_read;
516+
struct ice_e810_params *params;
507517
struct ice_ptp_port *ptp_port;
508518
u64 raw_tstamp, tstamp;
509519
bool drop_ts = false;
510520
struct sk_buff *skb;
521+
unsigned long flags;
522+
struct device *dev;
511523
struct ice_pf *pf;
512-
u32 val;
524+
u32 reg_ll_high;
513525

514526
if (!tx->init || tx->last_ll_ts_idx_read < 0)
515527
return;
516528

517529
ptp_port = container_of(tx, struct ice_ptp_port, tx);
518530
pf = ptp_port_to_pf(ptp_port);
531+
dev = ice_pf_to_dev(pf);
532+
params = &pf->hw.ptp.phy.e810;
519533

520534
ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx);
521535

522-
val = rd32(&pf->hw, REG_LL_PROXY_H);
536+
spin_lock_irqsave(&params->atqbal_wq.lock, flags);
537+
538+
if (!(params->atqbal_flags & ATQBAL_FLAGS_INTR_IN_PROGRESS))
539+
dev_dbg(dev, "%s: low latency interrupt request not in progress?\n",
540+
__func__);
541+
542+
/* Read the low 32 bit value */
543+
raw_tstamp = rd32(&pf->hw, REG_LL_PROXY_L);
544+
/* Read the status together with high TS part */
545+
reg_ll_high = rd32(&pf->hw, REG_LL_PROXY_H);
546+
547+
/* Wake up threads waiting on low latency interface */
548+
params->atqbal_flags &= ~ATQBAL_FLAGS_INTR_IN_PROGRESS;
549+
550+
wake_up_locked(&params->atqbal_wq);
551+
552+
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
523553

524554
/* When the bit is cleared, the TS is ready in the register */
525-
if (val & REG_LL_PROXY_H_EXEC) {
555+
if (reg_ll_high & REG_LL_PROXY_H_EXEC) {
526556
dev_err(ice_pf_to_dev(pf), "Failed to get the Tx tstamp - FW not ready");
527557
return;
528558
}
529559

530560
/* High 8 bit value of the TS is on the bits 16:23 */
531-
raw_tstamp = FIELD_GET(REG_LL_PROXY_H_TS_HIGH, val);
532-
raw_tstamp <<= 32;
533-
534-
/* Read the low 32 bit value */
535-
raw_tstamp |= (u64)rd32(&pf->hw, REG_LL_PROXY_L);
561+
raw_tstamp |= ((u64)FIELD_GET(REG_LL_PROXY_H_TS_HIGH, reg_ll_high)) << 32;
536562

537563
/* Devices using this interface always verify the timestamp differs
538564
* relative to the last cached timestamp value.

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4856,9 +4856,22 @@ static int ice_write_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 val)
48564856
static int
48574857
ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
48584858
{
4859+
struct ice_e810_params *params = &hw->ptp.phy.e810;
4860+
unsigned long flags;
48594861
u32 val;
48604862
int err;
48614863

4864+
spin_lock_irqsave(&params->atqbal_wq.lock, flags);
4865+
4866+
/* Wait for any pending in-progress low latency interrupt */
4867+
err = wait_event_interruptible_locked_irq(params->atqbal_wq,
4868+
!(params->atqbal_flags &
4869+
ATQBAL_FLAGS_INTR_IN_PROGRESS));
4870+
if (err) {
4871+
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
4872+
return err;
4873+
}
4874+
48624875
/* Write TS index to read to the PF register so the FW can read it */
48634876
val = FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) | REG_LL_PROXY_H_EXEC;
48644877
wr32(hw, REG_LL_PROXY_H, val);
@@ -4870,6 +4883,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
48704883
REG_LL_PROXY_H);
48714884
if (err) {
48724885
ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
4886+
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
48734887
return err;
48744888
}
48754889

@@ -4879,6 +4893,8 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
48794893
/* Read the low 32 bit value and set the TS valid bit */
48804894
*lo = rd32(hw, REG_LL_PROXY_L) | TS_VALID;
48814895

4896+
spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
4897+
48824898
return 0;
48834899
}
48844900

@@ -5400,6 +5416,8 @@ static void ice_ptp_init_phy_e810(struct ice_ptp_hw *ptp)
54005416
ptp->phy_model = ICE_PHY_E810;
54015417
ptp->num_lports = 8;
54025418
ptp->ports_per_phy = 4;
5419+
5420+
init_waitqueue_head(&ptp->phy.e810.atqbal_wq);
54035421
}
54045422

54055423
/* Device agnostic functions

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ice_sbq_cmd.h"
1919
#include "ice_vlan_mode.h"
2020
#include "ice_fwlog.h"
21+
#include <linux/wait.h>
2122

2223
static inline bool ice_is_tc_ena(unsigned long bitmap, u8 tc)
2324
{
@@ -848,6 +849,14 @@ struct ice_mbx_data {
848849
#define ICE_PORTS_PER_QUAD 4
849850
#define ICE_GET_QUAD_NUM(port) ((port) / ICE_PORTS_PER_QUAD)
850851

852+
#define ATQBAL_FLAGS_INTR_IN_PROGRESS BIT(0)
853+
854+
struct ice_e810_params {
855+
/* The wait queue lock also protects the low latency interface */
856+
wait_queue_head_t atqbal_wq;
857+
unsigned int atqbal_flags;
858+
};
859+
851860
struct ice_eth56g_params {
852861
u8 num_phys;
853862
u8 phy_addr[2];
@@ -857,6 +866,7 @@ struct ice_eth56g_params {
857866
};
858867

859868
union ice_phy_params {
869+
struct ice_e810_params e810;
860870
struct ice_eth56g_params eth56g;
861871
};
862872

0 commit comments

Comments
 (0)