Prevent rt61pci txdone register to become flooded during under stress, immediately read the register and mark the reported entries as done. This way the txdone handler only has to search for these particular entries. Signed-off-by: Ivo van Doorn <IvDoorn@xxxxxxxxx> --- diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00.h b/drivers/net/wireless/mac80211/rt2x00/rt2x00.h index b79f860..ef84e1a 100644 --- a/drivers/net/wireless/mac80211/rt2x00/rt2x00.h +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00.h @@ -522,7 +522,8 @@ struct data_entry { */ unsigned int flags; #define ENTRY_OWNER_NIC 0x00000001 -#define ENTRY_RTS_CTS_FRAME 0x00000002 +#define ENTRY_TXDONE 0x00000002 +#define ENTRY_RTS_CTS_FRAME 0x00000004 /* * extra register field diff --git a/drivers/net/wireless/mac80211/rt2x00/rt61pci.c b/drivers/net/wireless/mac80211/rt2x00/rt61pci.c index 5685871..9acae84 100644 --- a/drivers/net/wireless/mac80211/rt2x00/rt61pci.c +++ b/drivers/net/wireless/mac80211/rt2x00/rt61pci.c @@ -2326,116 +2326,56 @@ static void rt61pci_rxdone(struct work_struct *work) } } -static void rt61pci_txdone_entry(struct data_entry *entry, u32 sta_csr4) +static void rt61pci_txdone(struct work_struct *work) { - struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; struct data_desc *txd; u32 word; int tx_status; + int retry; int ack; int rts; - txd = rt2x00_desc_addr(entry); - rt2x00_desc_read(txd, 0, &word); - - if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - !rt2x00_get_field32(word, TXD_W0_VALID)) - return; - - entry->tx_status.flags = 0; - entry->tx_status.queue_length = entry->ring->stats.limit; - entry->tx_status.queue_number = entry->tx_status.control.queue; - - /* - * The TXD_W0_RESULT field will only be set when - * we had requested an ACK. So we have received an - * ACK response when ACK was requested and status - * was succesfull. - */ - ack = rt2x00_get_field32(word, TXD_W0_ACK); - rts = GET_FLAG(entry, ENTRY_RTS_CTS_FRAME); - tx_status = rt2x00_get_field32(sta_csr4, STA_CSR4_TX_RESULT); - rt2x00_update_tx_stats(rt2x00dev, &entry->tx_status, tx_status, - ack, rts); - - rt2x00_bbp_read(rt2x00dev, 32, - (u8*)&entry->tx_status.ack_signal); - - entry->tx_status.retry_count = rt2x00_get_field32( - sta_csr4, STA_CSR4_RETRY_COUNT); - - /* - * If this is not an RTS frame send the tx_status to mac80211, - * that method also cleans up the skb structure. When this - * is a RTS frame, that it is our job to clean this structure up. - */ - if (!rts) - ieee80211_tx_status(rt2x00dev->hw, - entry->skb, &entry->tx_status); - else - dev_kfree_skb(entry->skb); + while (!rt2x00_ring_empty(ring)) { + entry = rt2x00_get_data_entry_done(ring); + txd = entry->priv; + rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_desc_write(txd, 0, word); - CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); - entry->skb = NULL; + if (!GET_FLAG(entry, ENTRY_TXDONE) || + rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + return; - rt2x00_ring_index_done_inc(entry->ring); + ack = rt2x00_get_field32(word, TXD_W0_ACK); + rts = GET_FLAG(entry, ENTRY_RTS_CTS_FRAME); + tx_status = rt2x00_get_field32(entry->reg, STA_CSR4_TX_RESULT); + retry = rt2x00_get_field32(entry->reg, STA_CSR4_RETRY_COUNT); - /* - * If the data ring was full before the txdone handler - * we must make sure the packet queue in the mac80211 stack - * is reenabled when the txdone handler has finished. - */ - if (!rt2x00_ring_full(entry->ring)) - ieee80211_wake_queue(rt2x00dev->hw, - entry->tx_status.control.queue); -} + rt2x00_update_tx_stats(rt2x00dev, &entry->tx_status, tx_status, + ack, rts); -static void rt61pci_txdone(struct work_struct *work) -{ - struct data_ring *ring = - container_of(work, struct data_ring, irq_work); - struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; - int index; - int reg; - - /* - * Keep looping while STA_CSR4 contains value data. - * at each read the value will be reset to a new value, - * which we should check since it contains the ring - * and index number of the entry which has been - * completely transmitted. - */ - while (1) { /* - * Stop looping when the entry is invalid. + * If this is not an RTS frame send the tx_status to mac80211, + * that method also cleans up the skb structure. When this + * is a RTS frame, that it is our job to clean this structure up. */ - rt2x00_register_read(rt2x00dev, STA_CSR4, ®); - if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) - break; + if (!GET_FLAG(entry, ENTRY_RTS_CTS_FRAME)) + ieee80211_tx_status(rt2x00dev->hw, + entry->skb, &entry->tx_status); + else + dev_kfree_skb(entry->skb); - /* - * Skip this entry when it contains an invalid - * ring identication number. - */ - ring = rt2x00_get_ring(rt2x00dev, - rt2x00_get_field32(reg, STA_CSR4_PID_TYPE)); - if (unlikely(!ring)) - continue; + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + entry->skb = NULL; - /* - * Skip this entry when it contains an invalid - * index number. - */ - index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); - if (unlikely(index >= ring->stats.limit)) - continue; + CLEAR_FLAG(entry, ENTRY_TXDONE); + CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); - /* - * Packet received correctly, we can now process it. - */ - rt61pci_txdone_entry(&ring->entry[index], reg); + rt2x00_ring_index_done_inc(entry->ring); } /* @@ -2454,6 +2394,8 @@ static void rt61pci_txdone(struct work_struct *work) static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; + struct data_ring *ring; + int index; u32 reg; /* @@ -2494,13 +2436,41 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) /* * 3 - Tx ring done interrupt. - * Don't care about which ring we use to schedule - * work to. The handler will run for all TX - * related rings except Beacon. + * Keep looping while STA_CSR4 contains value data. + * at each read the value will be reset to a new value, + * which we should check since it contains the ring + * and index number of the entry which has been + * completely transmitted. */ - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) - queue_work(rt2x00dev->workqueue, - &rt2x00dev->ring[RING_AC_VO].irq_work); + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) { + while (1) { + rt2x00_register_read(rt2x00dev, STA_CSR4, ®); + if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) + break; + + /* + * Skip this entry when it contains an invalid + * ring identication number. + */ + ring = rt2x00_get_ring(rt2x00dev, + rt2x00_get_field32(reg, STA_CSR4_PID_TYPE)); + if (unlikely(!ring)) + continue; + + /* + * Skip this entry when it contains an invalid + * index number. + */ + index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); + if (unlikely(index >= ring->stats.limit)) + continue; + + ring->entry[index].reg = reg; + SET_FLAG(&ring->entry[index], ENTRY_TXDONE); + + queue_work(rt2x00dev->workqueue, &ring->irq_work); + } + } return IRQ_HANDLED; } - To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html