diff options
author | Vinicius Costa Gomes <vinicius.gomes@intel.com> | 2023-07-28 17:17:59 -0700 |
---|---|---|
committer | Tony Nguyen <anthony.l.nguyen@intel.com> | 2023-08-24 12:55:24 -0700 |
commit | 3ed247e789114c5bbb3380c3666eb819336b94e5 (patch) | |
tree | 4e63f67ac24ea3a791de8deb7d525daf3a9efe98 /drivers/net/ethernet/intel/igc/igc_ptp.c | |
parent | 35b4b6d0c53a3872e846dbcda9074117efdc078a (diff) |
igc: Add support for multiple in-flight TX timestamps
Add support for using the four sets of timestamping registers that
i225/i226 have available for TX.
In some workloads, where multiple applications request hardware
transmission timestamps, it was possible that some of those requests
were denied because the only in use register was already occupied.
This is also in preparation to future support for hardware
timestamping with multiple PTP domains. With multiple domains chances
of multiple TX timestamps being requested at the same time increase.
Before:
$ sudo ./ntpperf -i enp3s0 -m 10:22:22:22:22:21 -d 192.168.1.3 -s 172.18.0.0/16 -I -H -o 37
| responses | TX timestamp offset (ns)
rate clients | lost invalid basic xleave | min mean max stddev
1000 100 0.00% 0.00% 0.00% 100.00% +1 +41 +73 13
1500 150 0.00% 0.00% 0.00% 100.00% +9 +49 +87 15
2250 225 0.00% 0.00% 0.00% 100.00% +9 +42 +79 13
3375 337 0.00% 0.00% 0.00% 100.00% +11 +46 +81 13
5062 506 0.00% 0.00% 0.00% 100.00% +7 +44 +80 13
7593 759 0.00% 0.00% 0.00% 100.00% +9 +44 +79 12
11389 1138 0.00% 0.00% 0.00% 100.00% +14 +51 +87 13
17083 1708 0.00% 0.00% 0.00% 100.00% +1 +41 +80 14
25624 2562 0.00% 0.00% 0.00% 100.00% +11 +50 +5107 51
38436 3843 0.00% 0.00% 0.00% 100.00% -2 +36 +7843 38
57654 5765 0.00% 0.00% 0.00% 100.00% +4 +42 +10503 69
86481 8648 0.00% 0.00% 0.00% 100.00% +11 +54 +5492 65
129721 12972 0.00% 0.00% 0.00% 100.00% +31 +2680 +6942 2606
194581 16384 16.79% 0.00% 0.87% 82.34% +73 +4444 +15879 3116
291871 16384 35.05% 0.00% 1.53% 63.42% +188 +5381 +17019 3035
437806 16384 54.95% 0.00% 2.55% 42.50% +233 +6302 +13885 2846
After:
$ sudo ./ntpperf -i enp3s0 -m 10:22:22:22:22:21 -d 192.168.1.3 -s 172.18.0.0/16 -I -H -o 37
| responses | TX timestamp offset (ns)
rate clients | lost invalid basic xleave | min mean max stddev
1000 100 0.00% 0.00% 0.00% 100.00% -20 +12 +43 13
1500 150 0.00% 0.00% 0.00% 100.00% -23 +18 +57 14
2250 225 0.00% 0.00% 0.00% 100.00% -2 +33 +67 13
3375 337 0.00% 0.00% 0.00% 100.00% +1 +38 +76 13
5062 506 0.00% 0.00% 0.00% 100.00% +9 +52 +93 14
7593 759 0.00% 0.00% 0.00% 100.00% +11 +47 +82 13
11389 1138 0.00% 0.00% 0.00% 100.00% -9 +27 +74 13
17083 1708 0.00% 0.00% 0.00% 100.00% -13 +25 +66 14
25624 2562 0.00% 0.00% 0.00% 100.00% -8 +28 +65 13
38436 3843 0.00% 0.00% 0.00% 100.00% -13 +28 +69 13
57654 5765 0.00% 0.00% 0.00% 100.00% -11 +32 +71 14
86481 8648 0.00% 0.00% 0.00% 100.00% +2 +44 +83 14
129721 12972 15.36% 0.00% 0.35% 84.29% -2 +2248 +22907 4252
194581 16384 42.98% 0.00% 1.98% 55.04% -4 +5278 +65039 5856
291871 16384 54.33% 0.00% 2.21% 43.46% -3 +6306 +22608 5665
We can see that with 4 registers, as expected, we are able to handle a
increasing number of requests more consistently, but as soon as all
registers are in use, the decrease in quality of service happens in a
sharp step.
Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
Tested-by: Naama Meir <naamax.meir@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/igc/igc_ptp.c')
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 174 |
1 files changed, 119 insertions, 55 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index f0b979a70655..928f38792203 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -558,11 +558,16 @@ static void igc_ptp_enable_rx_timestamp(struct igc_adapter *adapter) static void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter) { unsigned long flags; + int i; spin_lock_irqsave(&adapter->ptp_tx_lock, flags); - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; + for (i = 0; i < IGC_MAX_TX_TSTAMP_REGS; i++) { + struct igc_tx_timestamp_request *tstamp = &adapter->tx_tstamp[i]; + + dev_kfree_skb_any(tstamp->skb); + tstamp->skb = NULL; + } spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); } @@ -659,61 +664,106 @@ static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, } /* Requires adapter->ptp_tx_lock held by caller. */ -static void igc_ptp_tx_timeout(struct igc_adapter *adapter) +static void igc_ptp_tx_timeout(struct igc_adapter *adapter, + struct igc_tx_timestamp_request *tstamp) { - struct igc_hw *hw = &adapter->hw; - - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; + dev_kfree_skb_any(tstamp->skb); + tstamp->skb = NULL; adapter->tx_hwtstamp_timeouts++; - /* Clear the tx valid bit in TSYNCTXCTL register to enable interrupt. */ - rd32(IGC_TXSTMPH); + netdev_warn(adapter->netdev, "Tx timestamp timeout\n"); } void igc_ptp_tx_hang(struct igc_adapter *adapter) { + struct igc_tx_timestamp_request *tstamp; + struct igc_hw *hw = &adapter->hw; unsigned long flags; + bool found = false; + int i; spin_lock_irqsave(&adapter->ptp_tx_lock, flags); - if (!adapter->ptp_tx_skb) - goto unlock; + for (i = 0; i < IGC_MAX_TX_TSTAMP_REGS; i++) { + tstamp = &adapter->tx_tstamp[i]; + + if (!tstamp->skb) + continue; - if (time_is_after_jiffies(adapter->ptp_tx_start + IGC_PTP_TX_TIMEOUT)) - goto unlock; + if (time_is_after_jiffies(tstamp->start + IGC_PTP_TX_TIMEOUT)) + continue; - igc_ptp_tx_timeout(adapter); + igc_ptp_tx_timeout(adapter, tstamp); + found = true; + } + + if (found) { + /* Reading the high register of the first set of timestamp registers + * clears all the equivalent bits in the TSYNCTXCTL register. + */ + rd32(IGC_TXSTMPH_0); + } -unlock: spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); } +static void igc_ptp_tx_reg_to_stamp(struct igc_adapter *adapter, + struct igc_tx_timestamp_request *tstamp, u64 regval) +{ + struct skb_shared_hwtstamps shhwtstamps; + struct sk_buff *skb; + int adjust = 0; + + skb = tstamp->skb; + if (!skb) + return; + + if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval)) + return; + + switch (adapter->link_speed) { + case SPEED_10: + adjust = IGC_I225_TX_LATENCY_10; + break; + case SPEED_100: + adjust = IGC_I225_TX_LATENCY_100; + break; + case SPEED_1000: + adjust = IGC_I225_TX_LATENCY_1000; + break; + case SPEED_2500: + adjust = IGC_I225_TX_LATENCY_2500; + break; + } + + shhwtstamps.hwtstamp = + ktime_add_ns(shhwtstamps.hwtstamp, adjust); + + tstamp->skb = NULL; + + skb_tstamp_tx(skb, &shhwtstamps); + dev_kfree_skb_any(skb); +} + /** * igc_ptp_tx_hwtstamp - utility function which checks for TX time stamp * @adapter: Board private structure * - * If we were asked to do hardware stamping and such a time stamp is - * available, then it must have been for this skb here because we only - * allow only one such packet into the queue. + * Check against the ready mask for which of the timestamp register + * sets are ready to be retrieved, then retrieve that and notify the + * rest of the stack. * * Context: Expects adapter->ptp_tx_lock to be held by caller. */ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) { - struct sk_buff *skb = adapter->ptp_tx_skb; - struct skb_shared_hwtstamps shhwtstamps; struct igc_hw *hw = &adapter->hw; - u32 tsynctxctl; - int adjust = 0; u64 regval; + u32 mask; + int i; - if (WARN_ON_ONCE(!skb)) - return; - - tsynctxctl = rd32(IGC_TSYNCTXCTL); - tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0; - if (tsynctxctl) { + mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY; + if (mask & IGC_TSYNCTXCTL_TXTT_0) { regval = rd32(IGC_TXSTMPL); regval |= (u64)rd32(IGC_TXSTMPH) << 32; } else { @@ -742,37 +792,30 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) txstmpl_new = rd32(IGC_TXSTMPL); if (txstmpl_old == txstmpl_new) - return; + goto done; regval = txstmpl_new; regval |= (u64)rd32(IGC_TXSTMPH) << 32; } - if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval)) - return; - switch (adapter->link_speed) { - case SPEED_10: - adjust = IGC_I225_TX_LATENCY_10; - break; - case SPEED_100: - adjust = IGC_I225_TX_LATENCY_100; - break; - case SPEED_1000: - adjust = IGC_I225_TX_LATENCY_1000; - break; - case SPEED_2500: - adjust = IGC_I225_TX_LATENCY_2500; - break; - } + igc_ptp_tx_reg_to_stamp(adapter, &adapter->tx_tstamp[0], regval); - shhwtstamps.hwtstamp = - ktime_add_ns(shhwtstamps.hwtstamp, adjust); +done: + /* Now that the problematic first register was handled, we can + * use retrieve the timestamps from the other registers + * (starting from '1') with less complications. + */ + for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) { + struct igc_tx_timestamp_request *tstamp = &adapter->tx_tstamp[i]; - adapter->ptp_tx_skb = NULL; + if (!(tstamp->mask & mask)) + continue; - /* Notify the stack and free the skb after we've unlocked */ - skb_tstamp_tx(skb, &shhwtstamps); - dev_kfree_skb_any(skb); + regval = rd32(tstamp->regl); + regval |= (u64)rd32(tstamp->regh) << 32; + + igc_ptp_tx_reg_to_stamp(adapter, tstamp, regval); + } } /** @@ -788,12 +831,8 @@ void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter) spin_lock_irqsave(&adapter->ptp_tx_lock, flags); - if (!adapter->ptp_tx_skb) - goto unlock; - igc_ptp_tx_hwtstamp(adapter); -unlock: spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags); } @@ -1006,9 +1045,34 @@ static int igc_ptp_getcrosststamp(struct ptp_clock_info *ptp, void igc_ptp_init(struct igc_adapter *adapter) { struct net_device *netdev = adapter->netdev; + struct igc_tx_timestamp_request *tstamp; struct igc_hw *hw = &adapter->hw; int i; + tstamp = &adapter->tx_tstamp[0]; + tstamp->mask = IGC_TSYNCTXCTL_TXTT_0; + tstamp->regl = IGC_TXSTMPL_0; + tstamp->regh = IGC_TXSTMPH_0; + tstamp->flags = 0; + + tstamp = &adapter->tx_tstamp[1]; + tstamp->mask = IGC_TSYNCTXCTL_TXTT_1; + tstamp->regl = IGC_TXSTMPL_1; + tstamp->regh = IGC_TXSTMPH_1; + tstamp->flags = IGC_TX_FLAGS_TSTAMP_1; + + tstamp = &adapter->tx_tstamp[2]; + tstamp->mask = IGC_TSYNCTXCTL_TXTT_2; + tstamp->regl = IGC_TXSTMPL_2; + tstamp->regh = IGC_TXSTMPH_2; + tstamp->flags = IGC_TX_FLAGS_TSTAMP_2; + + tstamp = &adapter->tx_tstamp[3]; + tstamp->mask = IGC_TSYNCTXCTL_TXTT_3; + tstamp->regl = IGC_TXSTMPL_3; + tstamp->regh = IGC_TXSTMPH_3; + tstamp->flags = IGC_TX_FLAGS_TSTAMP_3; + switch (hw->mac.type) { case igc_i225: for (i = 0; i < IGC_N_SDP; i++) { |