Commit 805e969f authored by Toshiharu Okada's avatar Toshiharu Okada Committed by David S. Miller

pch_gbe: Fixed the issue on which a network freezes

The pch_gbe driver has an issue which a network stops,
when receiving traffic is high.
In the case, The link down and up are necessary to return a network.

This patch fixed this issue.
Signed-off-by: default avatarToshiharu Okada <toshiharu-linux@dsn.okisemi.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5f3a1141
...@@ -1199,6 +1199,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) ...@@ -1199,6 +1199,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR), iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR),
&hw->reg->INT_EN); &hw->reg->INT_EN);
pch_gbe_stop_receive(adapter); pch_gbe_stop_receive(adapter);
int_st |= ioread32(&hw->reg->INT_ST);
int_st = int_st & ioread32(&hw->reg->INT_EN);
} }
if (int_st & PCH_GBE_INT_RX_DMA_ERR) if (int_st & PCH_GBE_INT_RX_DMA_ERR)
adapter->stats.intr_rx_dma_err_count++; adapter->stats.intr_rx_dma_err_count++;
...@@ -1218,14 +1220,11 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) ...@@ -1218,14 +1220,11 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
/* Set Pause packet */ /* Set Pause packet */
pch_gbe_mac_set_pause_packet(hw); pch_gbe_mac_set_pause_packet(hw);
} }
if ((int_en & (PCH_GBE_INT_RX_DMA_CMPLT | PCH_GBE_INT_TX_CMPLT))
== 0) {
return IRQ_HANDLED;
}
} }
/* When request status is Receive interruption */ /* When request status is Receive interruption */
if ((int_st & (PCH_GBE_INT_RX_DMA_CMPLT | PCH_GBE_INT_TX_CMPLT))) { if ((int_st & (PCH_GBE_INT_RX_DMA_CMPLT | PCH_GBE_INT_TX_CMPLT)) ||
(adapter->rx_stop_flag == true)) {
if (likely(napi_schedule_prep(&adapter->napi))) { if (likely(napi_schedule_prep(&adapter->napi))) {
/* Enable only Rx Descriptor empty */ /* Enable only Rx Descriptor empty */
atomic_inc(&adapter->irq_sem); atomic_inc(&adapter->irq_sem);
...@@ -1385,7 +1384,7 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, ...@@ -1385,7 +1384,7 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
struct sk_buff *skb; struct sk_buff *skb;
unsigned int i; unsigned int i;
unsigned int cleaned_count = 0; unsigned int cleaned_count = 0;
bool cleaned = false; bool cleaned = true;
pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean);
...@@ -1396,7 +1395,6 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, ...@@ -1396,7 +1395,6 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) { while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) {
pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status); pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status);
cleaned = true;
buffer_info = &tx_ring->buffer_info[i]; buffer_info = &tx_ring->buffer_info[i];
skb = buffer_info->skb; skb = buffer_info->skb;
...@@ -1439,9 +1437,11 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, ...@@ -1439,9 +1437,11 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
tx_desc = PCH_GBE_TX_DESC(*tx_ring, i); tx_desc = PCH_GBE_TX_DESC(*tx_ring, i);
/* weight of a sort for tx, to avoid endless transmit cleanup */ /* weight of a sort for tx, to avoid endless transmit cleanup */
if (cleaned_count++ == PCH_GBE_TX_WEIGHT) if (cleaned_count++ == PCH_GBE_TX_WEIGHT) {
cleaned = false;
break; break;
} }
}
pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n", pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n",
cleaned_count); cleaned_count);
/* Recover from running out of Tx resources in xmit_frame */ /* Recover from running out of Tx resources in xmit_frame */
...@@ -2168,7 +2168,6 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) ...@@ -2168,7 +2168,6 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
{ {
struct pch_gbe_adapter *adapter = struct pch_gbe_adapter *adapter =
container_of(napi, struct pch_gbe_adapter, napi); container_of(napi, struct pch_gbe_adapter, napi);
struct net_device *netdev = adapter->netdev;
int work_done = 0; int work_done = 0;
bool poll_end_flag = false; bool poll_end_flag = false;
bool cleaned = false; bool cleaned = false;
...@@ -2176,32 +2175,31 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) ...@@ -2176,32 +2175,31 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
pr_debug("budget : %d\n", budget); pr_debug("budget : %d\n", budget);
/* Keep link state information with original netdev */
if (!netif_carrier_ok(netdev)) {
poll_end_flag = true;
} else {
pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget); pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget);
if (adapter->rx_stop_flag) {
adapter->rx_stop_flag = false;
pch_gbe_start_receive(&adapter->hw);
int_en = ioread32(&adapter->hw.reg->INT_EN);
iowrite32((int_en | PCH_GBE_INT_RX_FIFO_ERR),
&adapter->hw.reg->INT_EN);
}
cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring); cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring);
if (cleaned) if (!cleaned)
work_done = budget; work_done = budget;
/* If no Tx and not enough Rx work done, /* If no Tx and not enough Rx work done,
* exit the polling mode * exit the polling mode
*/ */
if (work_done < budget) if (work_done < budget)
poll_end_flag = true; poll_end_flag = true;
}
if (poll_end_flag) { if (poll_end_flag) {
napi_complete(napi); napi_complete(napi);
if (adapter->rx_stop_flag) {
adapter->rx_stop_flag = false;
pch_gbe_start_receive(&adapter->hw);
}
pch_gbe_irq_enable(adapter); pch_gbe_irq_enable(adapter);
} else
if (adapter->rx_stop_flag) {
adapter->rx_stop_flag = false;
pch_gbe_start_receive(&adapter->hw);
int_en = ioread32(&adapter->hw.reg->INT_EN);
iowrite32((int_en | PCH_GBE_INT_RX_FIFO_ERR),
&adapter->hw.reg->INT_EN);
} }
pr_debug("poll_end_flag : %d work_done : %d budget : %d\n", pr_debug("poll_end_flag : %d work_done : %d budget : %d\n",
......
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