Commit 1f6e8178 authored by Matthew Vick's avatar Matthew Vick Committed by Jeff Kirsher

igb: Prevent dropped Tx timestamps via work items and interrupts.

In rare circumstances, it's possible a descriptor writeback will occur
before a timestamped Tx packet will go out on the wire, leading to the
driver believing the hardware failed to timestamp the packet. Schedule a
work item for 82576 and use the available time sync interrupt registers
on 82580 and above to account for this.

Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarMatthew Vick <matthew.vick@intel.com>
Tested-by: default avatarJeff Pieper <jeffrey.e.pieper@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 201987e3
...@@ -360,6 +360,7 @@ ...@@ -360,6 +360,7 @@
#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ #define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */
#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ #define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */
#define E1000_ICR_VMMB 0x00000100 /* VM MB event */ #define E1000_ICR_VMMB 0x00000100 /* VM MB event */
#define E1000_ICR_TS 0x00080000 /* Time Sync Interrupt */
#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */ #define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */
/* If this bit asserted, the driver should claim the interrupt */ /* If this bit asserted, the driver should claim the interrupt */
#define E1000_ICR_INT_ASSERTED 0x80000000 #define E1000_ICR_INT_ASSERTED 0x80000000
...@@ -399,6 +400,7 @@ ...@@ -399,6 +400,7 @@
#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ #define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ #define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */
#define E1000_IMS_VMMB E1000_ICR_VMMB /* Mail box activity */ #define E1000_IMS_VMMB E1000_ICR_VMMB /* Mail box activity */
#define E1000_IMS_TS E1000_ICR_TS /* Time Sync Interrupt */
#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ #define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ #define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ #define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
...@@ -510,6 +512,9 @@ ...@@ -510,6 +512,9 @@
#define E1000_TIMINCA_16NS_SHIFT 24 #define E1000_TIMINCA_16NS_SHIFT 24
#define E1000_TSICR_TXTS 0x00000002
#define E1000_TSIM_TXTS 0x00000002
#define E1000_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */ #define E1000_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */
#define E1000_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */ #define E1000_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */
#define E1000_MDICNFG_PHY_MASK 0x03E00000 #define E1000_MDICNFG_PHY_MASK 0x03E00000
......
...@@ -91,6 +91,8 @@ ...@@ -91,6 +91,8 @@
#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ #define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */
#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */ #define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */
#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */ #define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */
#define E1000_TSICR 0x0B66C /* Interrupt Cause Register */
#define E1000_TSIM 0x0B674 /* Interrupt Mask Register */
/* Filtering Registers */ /* Filtering Registers */
#define E1000_SAQF(_n) (0x5980 + 4 * (_n)) #define E1000_SAQF(_n) (0x5980 + 4 * (_n))
......
...@@ -381,6 +381,8 @@ struct igb_adapter { ...@@ -381,6 +381,8 @@ struct igb_adapter {
struct ptp_clock *ptp_clock; struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_caps; struct ptp_clock_info ptp_caps;
struct delayed_work ptp_overflow_work; struct delayed_work ptp_overflow_work;
struct work_struct ptp_tx_work;
struct sk_buff *ptp_tx_skb;
spinlock_t tmreg_lock; spinlock_t tmreg_lock;
struct cyclecounter cc; struct cyclecounter cc;
struct timecounter tc; struct timecounter tc;
...@@ -394,6 +396,7 @@ struct igb_adapter { ...@@ -394,6 +396,7 @@ struct igb_adapter {
#define IGB_FLAG_QUAD_PORT_A (1 << 2) #define IGB_FLAG_QUAD_PORT_A (1 << 2)
#define IGB_FLAG_QUEUE_PAIRS (1 << 3) #define IGB_FLAG_QUEUE_PAIRS (1 << 3)
#define IGB_FLAG_DMAC (1 << 4) #define IGB_FLAG_DMAC (1 << 4)
#define IGB_FLAG_PTP (1 << 5)
/* DMA Coalescing defines */ /* DMA Coalescing defines */
#define IGB_MIN_TXPBSIZE 20408 #define IGB_MIN_TXPBSIZE 20408
...@@ -440,8 +443,9 @@ extern void igb_set_fw_version(struct igb_adapter *); ...@@ -440,8 +443,9 @@ extern void igb_set_fw_version(struct igb_adapter *);
#ifdef CONFIG_IGB_PTP #ifdef CONFIG_IGB_PTP
extern void igb_ptp_init(struct igb_adapter *adapter); extern void igb_ptp_init(struct igb_adapter *adapter);
extern void igb_ptp_stop(struct igb_adapter *adapter); extern void igb_ptp_stop(struct igb_adapter *adapter);
extern void igb_ptp_tx_hwtstamp(struct igb_q_vector *q_vector, extern void igb_ptp_reset(struct igb_adapter *adapter);
struct igb_tx_buffer *buffer_info); extern void igb_ptp_tx_work(struct work_struct *work);
extern void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter);
extern void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector, extern void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector,
union e1000_adv_rx_desc *rx_desc, union e1000_adv_rx_desc *rx_desc,
struct sk_buff *skb); struct sk_buff *skb);
......
...@@ -1751,6 +1751,11 @@ void igb_reset(struct igb_adapter *adapter) ...@@ -1751,6 +1751,11 @@ void igb_reset(struct igb_adapter *adapter)
/* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */ /* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */
wr32(E1000_VET, ETHERNET_IEEE_VLAN_TYPE); wr32(E1000_VET, ETHERNET_IEEE_VLAN_TYPE);
#ifdef CONFIG_IGB_PTP
/* Re-enable PTP, where applicable. */
igb_ptp_reset(adapter);
#endif /* CONFIG_IGB_PTP */
igb_get_phy_info(hw); igb_get_phy_info(hw);
} }
...@@ -4234,7 +4239,7 @@ static __le32 igb_tx_cmd_type(u32 tx_flags) ...@@ -4234,7 +4239,7 @@ static __le32 igb_tx_cmd_type(u32 tx_flags)
#ifdef CONFIG_IGB_PTP #ifdef CONFIG_IGB_PTP
/* set timestamp bit if present */ /* set timestamp bit if present */
if (tx_flags & IGB_TX_FLAGS_TSTAMP) if (unlikely(tx_flags & IGB_TX_FLAGS_TSTAMP))
cmd_type |= cpu_to_le32(E1000_ADVTXD_MAC_TSTAMP); cmd_type |= cpu_to_le32(E1000_ADVTXD_MAC_TSTAMP);
#endif /* CONFIG_IGB_PTP */ #endif /* CONFIG_IGB_PTP */
...@@ -4445,6 +4450,9 @@ static inline int igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size) ...@@ -4445,6 +4450,9 @@ static inline int igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size)
netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb, netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
struct igb_ring *tx_ring) struct igb_ring *tx_ring)
{ {
#ifdef CONFIG_IGB_PTP
struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
#endif /* CONFIG_IGB_PTP */
struct igb_tx_buffer *first; struct igb_tx_buffer *first;
int tso; int tso;
u32 tx_flags = 0; u32 tx_flags = 0;
...@@ -4468,9 +4476,14 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb, ...@@ -4468,9 +4476,14 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
first->gso_segs = 1; first->gso_segs = 1;
#ifdef CONFIG_IGB_PTP #ifdef CONFIG_IGB_PTP
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
!(adapter->ptp_tx_skb))) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
tx_flags |= IGB_TX_FLAGS_TSTAMP; tx_flags |= IGB_TX_FLAGS_TSTAMP;
adapter->ptp_tx_skb = skb_get(skb);
if (adapter->hw.mac.type == e1000_82576)
schedule_work(&adapter->ptp_tx_work);
} }
#endif /* CONFIG_IGB_PTP */ #endif /* CONFIG_IGB_PTP */
...@@ -4859,6 +4872,19 @@ static irqreturn_t igb_msix_other(int irq, void *data) ...@@ -4859,6 +4872,19 @@ static irqreturn_t igb_msix_other(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1); mod_timer(&adapter->watchdog_timer, jiffies + 1);
} }
#ifdef CONFIG_IGB_PTP
if (icr & E1000_ICR_TS) {
u32 tsicr = rd32(E1000_TSICR);
if (tsicr & E1000_TSICR_TXTS) {
/* acknowledge the interrupt */
wr32(E1000_TSICR, E1000_TSICR_TXTS);
/* retrieve hardware timestamp */
schedule_work(&adapter->ptp_tx_work);
}
}
#endif /* CONFIG_IGB_PTP */
wr32(E1000_EIMS, adapter->eims_other); wr32(E1000_EIMS, adapter->eims_other);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -5650,6 +5676,19 @@ static irqreturn_t igb_intr_msi(int irq, void *data) ...@@ -5650,6 +5676,19 @@ static irqreturn_t igb_intr_msi(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1); mod_timer(&adapter->watchdog_timer, jiffies + 1);
} }
#ifdef CONFIG_IGB_PTP
if (icr & E1000_ICR_TS) {
u32 tsicr = rd32(E1000_TSICR);
if (tsicr & E1000_TSICR_TXTS) {
/* acknowledge the interrupt */
wr32(E1000_TSICR, E1000_TSICR_TXTS);
/* retrieve hardware timestamp */
schedule_work(&adapter->ptp_tx_work);
}
}
#endif /* CONFIG_IGB_PTP */
napi_schedule(&q_vector->napi); napi_schedule(&q_vector->napi);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -5691,6 +5730,19 @@ static irqreturn_t igb_intr(int irq, void *data) ...@@ -5691,6 +5730,19 @@ static irqreturn_t igb_intr(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1); mod_timer(&adapter->watchdog_timer, jiffies + 1);
} }
#ifdef CONFIG_IGB_PTP
if (icr & E1000_ICR_TS) {
u32 tsicr = rd32(E1000_TSICR);
if (tsicr & E1000_TSICR_TXTS) {
/* acknowledge the interrupt */
wr32(E1000_TSICR, E1000_TSICR_TXTS);
/* retrieve hardware timestamp */
schedule_work(&adapter->ptp_tx_work);
}
}
#endif /* CONFIG_IGB_PTP */
napi_schedule(&q_vector->napi); napi_schedule(&q_vector->napi);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -5794,11 +5846,6 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector) ...@@ -5794,11 +5846,6 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
total_bytes += tx_buffer->bytecount; total_bytes += tx_buffer->bytecount;
total_packets += tx_buffer->gso_segs; total_packets += tx_buffer->gso_segs;
#ifdef CONFIG_IGB_PTP
/* retrieve hardware timestamp */
igb_ptp_tx_hwtstamp(q_vector, tx_buffer);
#endif /* CONFIG_IGB_PTP */
/* free the skb */ /* free the skb */
dev_kfree_skb_any(tx_buffer->skb); dev_kfree_skb_any(tx_buffer->skb);
tx_buffer->skb = NULL; tx_buffer->skb = NULL;
......
...@@ -289,6 +289,31 @@ static int igb_ptp_enable(struct ptp_clock_info *ptp, ...@@ -289,6 +289,31 @@ static int igb_ptp_enable(struct ptp_clock_info *ptp,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
/**
* igb_ptp_tx_work
* @work: pointer to work struct
*
* This work function polls the TSYNCTXCTL valid bit to determine when a
* timestamp has been taken for the current stored skb.
*/
void igb_ptp_tx_work(struct work_struct *work)
{
struct igb_adapter *adapter = container_of(work, struct igb_adapter,
ptp_tx_work);
struct e1000_hw *hw = &adapter->hw;
u32 tsynctxctl;
if (!adapter->ptp_tx_skb)
return;
tsynctxctl = rd32(E1000_TSYNCTXCTL);
if (tsynctxctl & E1000_TSYNCTXCTL_VALID)
igb_ptp_tx_hwtstamp(adapter);
else
/* reschedule to check later */
schedule_work(&adapter->ptp_tx_work);
}
static void igb_ptp_overflow_check(struct work_struct *work) static void igb_ptp_overflow_check(struct work_struct *work)
{ {
struct igb_adapter *igb = struct igb_adapter *igb =
...@@ -305,31 +330,25 @@ static void igb_ptp_overflow_check(struct work_struct *work) ...@@ -305,31 +330,25 @@ static void igb_ptp_overflow_check(struct work_struct *work)
/** /**
* igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp * igb_ptp_tx_hwtstamp - utility function which checks for TX time stamp
* @q_vector: pointer to q_vector containing needed info * @adapter: Board private structure.
* @buffer: pointer to igb_tx_buffer structure
* *
* If we were asked to do hardware stamping and such a time stamp is * 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 * available, then it must have been for this skb here because we only
* allow only one such packet into the queue. * allow only one such packet into the queue.
*/ */
void igb_ptp_tx_hwtstamp(struct igb_q_vector *q_vector, void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter)
struct igb_tx_buffer *buffer_info)
{ {
struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
struct skb_shared_hwtstamps shhwtstamps; struct skb_shared_hwtstamps shhwtstamps;
u64 regval; u64 regval;
/* if skb does not support hw timestamp or TX stamp not valid exit */
if (likely(!(buffer_info->tx_flags & IGB_TX_FLAGS_TSTAMP)) ||
!(rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID))
return;
regval = rd32(E1000_TXSTMPL); regval = rd32(E1000_TXSTMPL);
regval |= (u64)rd32(E1000_TXSTMPH) << 32; regval |= (u64)rd32(E1000_TXSTMPH) << 32;
igb_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval); igb_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval);
skb_tstamp_tx(buffer_info->skb, &shhwtstamps); skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
dev_kfree_skb_any(adapter->ptp_tx_skb);
adapter->ptp_tx_skb = NULL;
} }
void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector, void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector,
...@@ -603,16 +622,26 @@ void igb_ptp_init(struct igb_adapter *adapter) ...@@ -603,16 +622,26 @@ void igb_ptp_init(struct igb_adapter *adapter)
spin_lock_init(&adapter->tmreg_lock); spin_lock_init(&adapter->tmreg_lock);
INIT_WORK(&adapter->ptp_tx_work, igb_ptp_tx_work);
schedule_delayed_work(&adapter->ptp_overflow_work, schedule_delayed_work(&adapter->ptp_overflow_work,
IGB_SYSTIM_OVERFLOW_PERIOD); IGB_SYSTIM_OVERFLOW_PERIOD);
/* Initialize the time sync interrupts for devices that support it. */
if (hw->mac.type >= e1000_82580) {
wr32(E1000_TSIM, E1000_TSIM_TXTS);
wr32(E1000_IMS, E1000_IMS_TS);
}
adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps); adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps);
if (IS_ERR(adapter->ptp_clock)) { if (IS_ERR(adapter->ptp_clock)) {
adapter->ptp_clock = NULL; adapter->ptp_clock = NULL;
dev_err(&adapter->pdev->dev, "ptp_clock_register failed\n"); dev_err(&adapter->pdev->dev, "ptp_clock_register failed\n");
} else } else {
dev_info(&adapter->pdev->dev, "added PHC on %s\n", dev_info(&adapter->pdev->dev, "added PHC on %s\n",
adapter->netdev->name); adapter->netdev->name);
adapter->flags |= IGB_FLAG_PTP;
}
} }
/** /**
...@@ -624,20 +653,61 @@ void igb_ptp_init(struct igb_adapter *adapter) ...@@ -624,20 +653,61 @@ void igb_ptp_init(struct igb_adapter *adapter)
void igb_ptp_stop(struct igb_adapter *adapter) void igb_ptp_stop(struct igb_adapter *adapter)
{ {
switch (adapter->hw.mac.type) { switch (adapter->hw.mac.type) {
case e1000_i211:
case e1000_i210:
case e1000_i350:
case e1000_82580:
case e1000_82576: case e1000_82576:
case e1000_82580:
case e1000_i350:
cancel_delayed_work_sync(&adapter->ptp_overflow_work); cancel_delayed_work_sync(&adapter->ptp_overflow_work);
break; break;
case e1000_i210:
case e1000_i211:
/* No delayed work to cancel. */
break;
default: default:
return; return;
} }
cancel_work_sync(&adapter->ptp_tx_work);
if (adapter->ptp_clock) { if (adapter->ptp_clock) {
ptp_clock_unregister(adapter->ptp_clock); ptp_clock_unregister(adapter->ptp_clock);
dev_info(&adapter->pdev->dev, "removed PHC on %s\n", dev_info(&adapter->pdev->dev, "removed PHC on %s\n",
adapter->netdev->name); adapter->netdev->name);
adapter->flags &= ~IGB_FLAG_PTP;
} }
} }
/**
* igb_ptp_reset - Re-enable the adapter for PTP following a reset.
* @adapter: Board private structure.
*
* This function handles the reset work required to re-enable the PTP device.
**/
void igb_ptp_reset(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
if (!(adapter->flags & IGB_FLAG_PTP))
return;
switch (adapter->hw.mac.type) {
case e1000_82576:
/* Dial the nominal frequency. */
wr32(E1000_TIMINCA, INCPERIOD_82576 | INCVALUE_82576);
break;
case e1000_82580:
case e1000_i350:
case e1000_i210:
case e1000_i211:
/* Enable the timer functions and interrupts. */
wr32(E1000_TSAUXC, 0x0);
wr32(E1000_TSIM, E1000_TSIM_TXTS);
wr32(E1000_IMS, E1000_IMS_TS);
break;
default:
/* No work to do. */
return;
}
timecounter_init(&adapter->tc, &adapter->cc,
ktime_to_ns(ktime_get_real()));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment