Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+34 −11
Original line number Diff line number Diff line
@@ -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
	 * not restocked the Rx queue with fresh buffers */
	rxq->read = rxq->write = 0;
	rxq->processed = RX_QUEUE_SIZE - 1;
	rxq->free_count = 0;
	spin_unlock_irqrestore(&rxq->lock, flags);
}
@@ -3607,7 +3606,22 @@ static int ipw_load(struct ipw_priv *priv)
 * 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;
	if (s <= 0)
@@ -4947,7 +4961,7 @@ static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
		priv->tx_packets++;
	}
      done:
	if ((ipw_queue_space(q) > q->low_mark) &&
	if ((ipw_tx_queue_space(q) > q->low_mark) &&
	    (qindex >= 0) &&
	    (priv->status & STATUS_ASSOCIATED) && netif_running(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,
	struct clx2_queue *q = &txq->q;
	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");
		return -EBUSY;
	}
@@ -5070,7 +5084,7 @@ static void ipw_rx_queue_restock(struct ipw_priv *priv)

	spin_lock_irqsave(&rxq->lock, flags);
	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;
		rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
		list_del(element);
@@ -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
	 * not restocked the Rx queue with fresh buffers */
	rxq->read = rxq->write = 0;
	rxq->processed = RX_QUEUE_SIZE - 1;
	rxq->free_count = 0;

	return rxq;
@@ -8223,13 +8236,17 @@ static void ipw_rx(struct ipw_priv *priv)
	struct ieee80211_hdr_4addr *header;
	u32 r, w, i;
	u8 network_packet;
	u8 fill_rx = 0;
	DECLARE_MAC_BUF(mac);
	DECLARE_MAC_BUF(mac2);
	DECLARE_MAC_BUF(mac3);

	r = ipw_read32(priv, IPW_RX_READ_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) {
		rxb = priv->rxq->queue[i];
@@ -8404,11 +8421,17 @@ static void ipw_rx(struct ipw_priv *priv)
		list_add_tail(&rxb->list, &priv->rxq->rx_used);

		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 */
	priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;

	priv->rxq->read = i;
	ipw_rx_queue_restock(priv);
}

@@ -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);
	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);

	return NETDEV_TX_OK;
@@ -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];
#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 0;