aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/intel/igb
diff options
context:
space:
mode:
authorJacob Keller <jacob.e.keller@intel.com>2017-05-03 10:28:55 -0700
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2017-06-06 00:53:48 -0700
commit74344e32fcc0d09342b77ed9d23ea74b3799d157 (patch)
tree14409cf87f8b9608d7b99d767376cb214803b6f5 /drivers/net/ethernet/intel/igb
parentigb: fix race condition with PTP_TX_IN_PROGRESS bits (diff)
downloadlinux-dev-74344e32fcc0d09342b77ed9d23ea74b3799d157.tar.xz
linux-dev-74344e32fcc0d09342b77ed9d23ea74b3799d157.zip
igb: avoid permanent lock of *_PTP_TX_IN_PROGRESS
The igb driver uses a state bit lock to avoid handling more than one Tx timestamp request at once. This is required because hardware is limited to a single set of registers for Tx timestamps. The state bit lock is not properly cleaned up during igb_xmit_frame_ring() if the transmit fails such as due to DMA or TSO failure. In some hardware this results in blocking timestamps until the service task times out. In other hardware this results in a permanent lock of the timestamp bit because we never receive an interrupt indicating the timestamp occurred, since indeed the packet was never transmitted. Fix this by checking for DMA and TSO errors in igb_xmit_frame_ring() and properly cleaning up after ourselves when these occur. Reported-by: Reported-by: David Mirabito <davidm@metamako.com> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Tested-by: Aaron Brown <aaron.f.brown@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/igb')
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c23
1 files changed, 18 insertions, 5 deletions
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 2d5bdb1fd37d..fefa46120cbc 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -5197,9 +5197,9 @@ static inline int igb_maybe_stop_tx(struct igb_ring *tx_ring, const u16 size)
return __igb_maybe_stop_tx(tx_ring, size);
}
-static void igb_tx_map(struct igb_ring *tx_ring,
- struct igb_tx_buffer *first,
- const u8 hdr_len)
+static int igb_tx_map(struct igb_ring *tx_ring,
+ struct igb_tx_buffer *first,
+ const u8 hdr_len)
{
struct sk_buff *skb = first->skb;
struct igb_tx_buffer *tx_buffer;
@@ -5310,7 +5310,7 @@ static void igb_tx_map(struct igb_ring *tx_ring,
*/
mmiowb();
}
- return;
+ return 0;
dma_error:
dev_err(tx_ring->dev, "TX DMA map failed\n");
@@ -5341,6 +5341,8 @@ dma_error:
tx_buffer->skb = NULL;
tx_ring->next_to_use = i;
+
+ return -1;
}
netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
@@ -5406,13 +5408,24 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
else if (!tso)
igb_tx_csum(tx_ring, first);
- igb_tx_map(tx_ring, first, hdr_len);
+ if (igb_tx_map(tx_ring, first, hdr_len))
+ goto cleanup_tx_tstamp;
return NETDEV_TX_OK;
out_drop:
dev_kfree_skb_any(first->skb);
first->skb = NULL;
+cleanup_tx_tstamp:
+ if (unlikely(tx_flags & IGB_TX_FLAGS_TSTAMP)) {
+ struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
+
+ dev_kfree_skb_any(adapter->ptp_tx_skb);
+ adapter->ptp_tx_skb = NULL;
+ if (adapter->hw.mac.type == e1000_82576)
+ cancel_work_sync(&adapter->ptp_tx_work);
+ clear_bit_unlock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state);
+ }
return NETDEV_TX_OK;
}