Commit 943dbef4 authored by Dan Williams's avatar Dan Williams Committed by John W. Linville

ipw2200: fix ucode assertion for RX queue overrun

Restock the RX queue when there are a lot of unused frames so that the
RX ring buffer doesn't overrun, causing a ucode assertion.  Backport of
patch "iwlwifi: fix ucode assertion for RX queue overrun".
Signed-off-by: default avatarDan Williams <dcbw@redhat.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a6477249
...@@ -3365,7 +3365,6 @@ static void ipw_rx_queue_reset(struct ipw_priv *priv, ...@@ -3365,7 +3365,6 @@ static void ipw_rx_queue_reset(struct ipw_priv *priv,
/* Set us so that we have processed and used all buffers, but have /* Set us so that we have processed and used all buffers, but have
* not restocked the Rx queue with fresh buffers */ * not restocked the Rx queue with fresh buffers */
rxq->read = rxq->write = 0; rxq->read = rxq->write = 0;
rxq->processed = RX_QUEUE_SIZE - 1;
rxq->free_count = 0; rxq->free_count = 0;
spin_unlock_irqrestore(&rxq->lock, flags); spin_unlock_irqrestore(&rxq->lock, flags);
} }
...@@ -3607,7 +3606,22 @@ static int ipw_load(struct ipw_priv *priv) ...@@ -3607,7 +3606,22 @@ static int ipw_load(struct ipw_priv *priv)
* Driver allocates buffers of this size for Rx * Driver allocates buffers of this size for Rx
*/ */
static inline int ipw_queue_space(const struct clx2_queue *q) /**
* ipw_rx_queue_space - Return number of free slots available in queue.
*/
static int ipw_rx_queue_space(const struct ipw_rx_queue *q)
{
int s = q->read - q->write;
if (s <= 0)
s += RX_QUEUE_SIZE;
/* keep some buffer to not confuse full and empty queue */
s -= 2;
if (s < 0)
s = 0;
return s;
}
static inline int ipw_tx_queue_space(const struct clx2_queue *q)
{ {
int s = q->last_used - q->first_empty; int s = q->last_used - q->first_empty;
if (s <= 0) if (s <= 0)
...@@ -4947,7 +4961,7 @@ static int ipw_queue_tx_reclaim(struct ipw_priv *priv, ...@@ -4947,7 +4961,7 @@ static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
priv->tx_packets++; priv->tx_packets++;
} }
done: done:
if ((ipw_queue_space(q) > q->low_mark) && if ((ipw_tx_queue_space(q) > q->low_mark) &&
(qindex >= 0) && (qindex >= 0) &&
(priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev)) (priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev))
netif_wake_queue(priv->net_dev); netif_wake_queue(priv->net_dev);
...@@ -4965,7 +4979,7 @@ static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, ...@@ -4965,7 +4979,7 @@ static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
struct clx2_queue *q = &txq->q; struct clx2_queue *q = &txq->q;
struct tfd_frame *tfd; struct tfd_frame *tfd;
if (ipw_queue_space(q) < (sync ? 1 : 2)) { if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) {
IPW_ERROR("No space for Tx\n"); IPW_ERROR("No space for Tx\n");
return -EBUSY; return -EBUSY;
} }
...@@ -5070,7 +5084,7 @@ static void ipw_rx_queue_restock(struct ipw_priv *priv) ...@@ -5070,7 +5084,7 @@ static void ipw_rx_queue_restock(struct ipw_priv *priv)
spin_lock_irqsave(&rxq->lock, flags); spin_lock_irqsave(&rxq->lock, flags);
write = rxq->write; write = rxq->write;
while ((rxq->write != rxq->processed) && (rxq->free_count)) { while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
element = rxq->rx_free.next; element = rxq->rx_free.next;
rxb = list_entry(element, struct ipw_rx_mem_buffer, list); rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
list_del(element); list_del(element);
...@@ -5187,7 +5201,6 @@ static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv) ...@@ -5187,7 +5201,6 @@ static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
/* Set us so that we have processed and used all buffers, but have /* Set us so that we have processed and used all buffers, but have
* not restocked the Rx queue with fresh buffers */ * not restocked the Rx queue with fresh buffers */
rxq->read = rxq->write = 0; rxq->read = rxq->write = 0;
rxq->processed = RX_QUEUE_SIZE - 1;
rxq->free_count = 0; rxq->free_count = 0;
return rxq; return rxq;
...@@ -8223,13 +8236,17 @@ static void ipw_rx(struct ipw_priv *priv) ...@@ -8223,13 +8236,17 @@ static void ipw_rx(struct ipw_priv *priv)
struct ieee80211_hdr_4addr *header; struct ieee80211_hdr_4addr *header;
u32 r, w, i; u32 r, w, i;
u8 network_packet; u8 network_packet;
u8 fill_rx = 0;
DECLARE_MAC_BUF(mac); DECLARE_MAC_BUF(mac);
DECLARE_MAC_BUF(mac2); DECLARE_MAC_BUF(mac2);
DECLARE_MAC_BUF(mac3); DECLARE_MAC_BUF(mac3);
r = ipw_read32(priv, IPW_RX_READ_INDEX); r = ipw_read32(priv, IPW_RX_READ_INDEX);
w = ipw_read32(priv, IPW_RX_WRITE_INDEX); w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE; i = priv->rxq->read;
if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2))
fill_rx = 1;
while (i != r) { while (i != r) {
rxb = priv->rxq->queue[i]; rxb = priv->rxq->queue[i];
...@@ -8404,11 +8421,17 @@ static void ipw_rx(struct ipw_priv *priv) ...@@ -8404,11 +8421,17 @@ static void ipw_rx(struct ipw_priv *priv)
list_add_tail(&rxb->list, &priv->rxq->rx_used); list_add_tail(&rxb->list, &priv->rxq->rx_used);
i = (i + 1) % RX_QUEUE_SIZE; i = (i + 1) % RX_QUEUE_SIZE;
/* If there are a lot of unsued frames, restock the Rx queue
* so the ucode won't assert */
if (fill_rx) {
priv->rxq->read = i;
ipw_rx_queue_replenish(priv);
}
} }
/* Backtrack one entry */ /* Backtrack one entry */
priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1; priv->rxq->read = i;
ipw_rx_queue_restock(priv); ipw_rx_queue_restock(priv);
} }
...@@ -10336,7 +10359,7 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb, ...@@ -10336,7 +10359,7 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
ipw_write32(priv, q->reg_w, q->first_empty); ipw_write32(priv, q->reg_w, q->first_empty);
if (ipw_queue_space(q) < q->high_mark) if (ipw_tx_queue_space(q) < q->high_mark)
netif_stop_queue(priv->net_dev); netif_stop_queue(priv->net_dev);
return NETDEV_TX_OK; return NETDEV_TX_OK;
...@@ -10357,7 +10380,7 @@ static int ipw_net_is_queue_full(struct net_device *dev, int pri) ...@@ -10357,7 +10380,7 @@ static int ipw_net_is_queue_full(struct net_device *dev, int pri)
struct clx2_tx_queue *txq = &priv->txq[0]; struct clx2_tx_queue *txq = &priv->txq[0];
#endif /* CONFIG_IPW2200_QOS */ #endif /* CONFIG_IPW2200_QOS */
if (ipw_queue_space(&txq->q) < txq->q.high_mark) if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark)
return 1; return 1;
return 0; return 0;
......
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