1: 96c920b08ab4 ! 1: a81b47442eb5 wifi: rt2x00: start the link tuner when an interface is added @@ Commit message  Fixes: d96324703ffa ("rt2x00: merge agc and vco works with link tuner") Signed-off-by: Ho Jie Feng  + Acked-by: Stanislaw Gruszka   ## drivers/net/wireless/ralink/rt2x00/rt2x00mac.c ##  @@ drivers/net/wireless/ralink/rt2x00/rt2x00mac.c: int rt2x00mac_add_interface(struct ieee80211_hw *hw, 2: 5503e17f1d45 ! 2: 5ce993491504 wifi: rt2800: calibrate the crystal of RT3593 with the beacons @@ drivers/net/wireless/ralink/rt2x00/rt2800.h: enum rt2800_eeprom_word { * Macros for converting txpower from EEPROM to mac80211 value  ## drivers/net/wireless/ralink/rt2x00/rt2800lib.c ## -@@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) - return (int)max(rssi0, rssi2); - } -  -+static void rt2800_rx_freq_offset(struct queue_entry *entry, -+ struct rxdone_entry_desc *rxdesc) -+{ -+ struct rt2800_drv_data *drv_data = entry->queue->rt2x00dev->drv_data; -+ __le32 *rxwi = (__le32 *) entry->skb->data; -+ struct ieee80211_hdr *hdr = -+ (void *)(entry->skb->data + entry->queue->winfo_size); -+ u32 word; -+ -+ if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || -+ (rxdesc->flags & RX_FLAG_FAILED_FCS_CRC) || -+ !ieee80211_is_beacon(hdr->frame_control)) -+ return; -+ -+ word = rt2x00_desc_read(rxwi, 3); -+ -+ spin_lock_bh(&drv_data->bcn_lock); -+ drv_data->bcn_freq_offset = rt2x00_get_field32(word, RXWI_W3_FOFFSET); -+ drv_data->bcn_rate_mode = rxdesc->rate_mode; -+ spin_unlock_bh(&drv_data->bcn_lock); -+} -+ - void rt2800_process_rxwi(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) - {  @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: void rt2800_process_rxwi(struct queue_entry *entry, * Convert descriptor AGC value to RSSI value. */ rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word);  +  + /* -+ * The frequency offset of the beacons is used for the frequency -+ * calibration. ++ * The frequency offset is used for the frequency calibration.  + */ -+ if (rt2x00_rt(entry->queue->rt2x00dev, RT3593)) -+ rt2800_rx_freq_offset(entry, rxdesc); ++ if (rt2x00_rt(entry->queue->rt2x00dev, RT3593)) { ++ word = rt2x00_desc_read(rxwi, 3); ++ rxdesc->freq_offset = rt2x00_get_field32(word, RXWI_W3_FOFFSET); ++ rxdesc->dev_flags |= RXDONE_FREQ_OFFSET; ++ }  + /* * Remove RXWI descriptor from start of the buffer. @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: void rt2800_vco_calibration(stru   +void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev)  +{ -+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;  + struct link *link = &rt2x00dev->link;  + u8 activate_threshold, deactivate_threshold;  + u8 freq_offset, rate_mode; @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: void rt2800_vco_calibration(stru  + return;  + }  + -+ spin_lock_bh(&drv_data->bcn_lock); -+ offset = drv_data->bcn_freq_offset; -+ rate_mode = drv_data->bcn_rate_mode; -+ drv_data->bcn_freq_offset = FREQ_OFFSET_INVALID; -+ spin_unlock_bh(&drv_data->bcn_lock); ++ spin_lock_bh(&link->bcn_lock); ++ offset = link->bcn_freq_offset; ++ rate_mode = link->bcn_rate_mode; ++ link->bcn_freq_offset = FREQ_OFFSET_INVALID; ++ spin_unlock_bh(&link->bcn_lock);  +  + /* No beacons received */  + if (offset == FREQ_OFFSET_INVALID) @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: void rt2800_vco_calibration(stru static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { -@@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev) -  - int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev) - { -+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - int retval; - u32 reg; -  -@@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev) - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; -  -+ spin_lock_init(&drv_data->bcn_lock); -+ - return 0; - } - EXPORT_SYMBOL_GPL(rt2800_probe_hw);  ## drivers/net/wireless/ralink/rt2x00/rt2800lib.h ## -@@ drivers/net/wireless/ralink/rt2x00/rt2800lib.h: struct rt2800_drv_data { - unsigned int ampdu_factor_cnt[4]; - DECLARE_BITMAP(sta_ids, STA_IDS_SIZE); - struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE]; -+ spinlock_t bcn_lock; /* protects bcn_freq_offset, bcn_rate_mode */ -+ s8 bcn_freq_offset; -+ u8 bcn_rate_mode; - }; -  - struct rt2800_ops {  @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.h: void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, const u32 count); void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev); @@ drivers/net/wireless/ralink/rt2x00/rt2x00.h: struct link {  + * Controls interval of frequency calibration  + */  + bool freq_cal_adjusting; ++ ++ /* ++ * Frequency offset and rate mode of the last beacon from the AP ++ */ ++ spinlock_t bcn_lock; /* protects bcn_freq_offset, bcn_rate_mode */ ++ s8 bcn_freq_offset; ++ u8 bcn_rate_mode;  + /* * Work structure for scheduling periodic link tuning. @@ drivers/net/wireless/ralink/rt2x00/rt2x00.h: struct rt2x00lib_ops { /* * Data queue handlers.  + ## drivers/net/wireless/ralink/rt2x00/rt2x00dev.c ## +@@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, + queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work); + } +  ++static void rt2x00lib_rxdone_freq_offset(struct rt2x00_dev *rt2x00dev, ++ struct sk_buff *skb, ++ struct rxdone_entry_desc *rxdesc) ++{ ++ struct ieee80211_hdr *hdr = (void *)skb->data; ++ struct link *link = &rt2x00dev->link; ++ ++ if (!(rxdesc->dev_flags & RXDONE_FREQ_OFFSET) || ++ !(rxdesc->dev_flags & RXDONE_MY_BSS) || ++ (rxdesc->flags & RX_FLAG_FAILED_FCS_CRC) || ++ !ieee80211_is_beacon(hdr->frame_control)) ++ return; ++ ++ spin_lock_bh(&link->bcn_lock); ++ link->bcn_freq_offset = rxdesc->freq_offset; ++ link->bcn_rate_mode = rxdesc->rate_mode; ++ spin_unlock_bh(&link->bcn_lock); ++} ++ + static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) + { +@@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) + */ + rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); +  ++ /* ++ * Store frequency offset of the beacons for frequency calibration. ++ */ ++ rt2x00lib_rxdone_freq_offset(rt2x00dev, entry->skb, &rxdesc); ++ + /* + * Update extra components + */ + ## drivers/net/wireless/ralink/rt2x00/rt2x00lib.h ##  @@ #define LINK_TUNE_INTERVAL round_jiffies_relative(LINK_TUNE_SECONDS * HZ) @@ drivers/net/wireless/ralink/rt2x00/rt2x00link.c: static void rt2x00link_tuner(st if (rt2x00dev->ops->lib->vco_calibration && rt2x00_has_cap_vco_recalibration(rt2x00dev) && (link->count % (VCO_SECONDS / LINK_TUNE_SECONDS)) == 0) +@@ drivers/net/wireless/ralink/rt2x00/rt2x00link.c: void rt2x00link_register(struct rt2x00_dev *rt2x00dev) +  + INIT_DELAYED_WORK(&link->work, rt2x00link_tuner); + INIT_DELAYED_WORK(&link->watchdog_work, rt2x00link_watchdog); ++ spin_lock_init(&link->bcn_lock); +  + if (link->watchdog_interval == 0) + link->watchdog_interval = WATCHDOG_INTERVAL; + + ## drivers/net/wireless/ralink/rt2x00/rt2x00queue.h ## +@@ drivers/net/wireless/ralink/rt2x00/rt2x00queue.h: static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb) + * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data. + * @RXDONE_CRYPTO_ICV: Driver provided ICV data. + * @RXDONE_L2PAD: 802.11 payload has been padded to 4-byte boundary. ++ * @RXDONE_FREQ_OFFSET: Driver provided the frequency offset. + */ + enum rxdone_entry_desc_flags { + RXDONE_SIGNAL_PLCP = BIT(0), +@@ drivers/net/wireless/ralink/rt2x00/rt2x00queue.h: enum rxdone_entry_desc_flags { + RXDONE_CRYPTO_IV = BIT(4), + RXDONE_CRYPTO_ICV = BIT(5), + RXDONE_L2PAD = BIT(6), ++ RXDONE_FREQ_OFFSET = BIT(7), + }; +  + /** +@@ drivers/net/wireless/ralink/rt2x00/rt2x00queue.h: enum rxdone_entry_desc_flags { + * @cipher_status: Decryption status. + * @iv: IV/EIV data used during decryption. + * @icv: ICV data used during decryption. ++ * @freq_offset: Frequency offset of the received frame. + */ + struct rxdone_entry_desc { + u64 timestamp; +@@ drivers/net/wireless/ralink/rt2x00/rt2x00queue.h: struct rxdone_entry_desc { +  + __le32 iv[2]; + __le32 icv; ++ ++ s8 freq_offset; + }; +  + /** 3: 15a25ec7a91c ! 3: 2f4afc902fb2 wifi: rt2800: make MCS 16 fall back to MCS 8 on devices with 3 TX chains @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: void rt2800_txdone_entry(struct  + * frame that was sent below MCS16 after a three stream rate  + * skipped MCS9 to MCS15.  + */ -+ if (rt2x00_is_3t(rt2x00dev) && mcs >= 16 && real_mcs < 16) ++ if (rt2x00_is_3t(rt2x00dev) && mcs >= 16 && real_mcs <= 8)  + txdesc.retry -= 7; } else { /* 4: a849dbe14ded ! 4: f4bb0ce43229 wifi: rt2800: don't report an A-MPDU length to minstrel_ht @@ Commit message below the TODO comment.  Signed-off-by: Ho Jie Feng  + Acked-by: Stanislaw Gruszka   ## drivers/net/wireless/ralink/rt2x00/rt2800lib.c ##  @@ drivers/net/wireless/ralink/rt2x00/rt2800lib.c: static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) 6: 85c8faf9d4b0 ! 5: a2fdeaded17c wifi: rt2800usb: enable RX bulk aggregation @@ drivers/net/wireless/ralink/rt2x00/rt2800usb.c: static int rt2800usb_enable_radi rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0);  - rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0);  + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, -+ rt2x00dev->rx->usb_maxpacket == 512); ++ rt2x00dev->rx->usb_maxpacket >= 512); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); /* * Total room for RX frames in kilobytes, PBF might still exceed @@ drivers/net/wireless/ralink/rt2x00/rt2800usb.c: static void rt2800usb_work_txdon  + word = rt2x00_desc_read(data, 0);  + rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN);  + -+ if (!rx_pkt_len || -+ RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE > len || -+ rx_pkt_len & 0x3) ++ if (WARN_ON_ONCE(RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE > len) || ++ WARN_ON_ONCE(rx_pkt_len & 0x3) || ++ WARN_ON_ONCE(rx_pkt_len < min_seg_len))  + return 0;  +  + return RXINFO_DESC_SIZE + rx_pkt_len + RXD_DESC_SIZE; 5: 0c4eebe3772c = 6: 68ddbbf39763 wifi: rt2800: support A-MSDU in A-MPDU 7: c37126e5aa78 ! 7: b76e3994ba6b wifi: rt2x00usb: use a threaded NAPI with GRO for RX @@ Commit message This code is referenced from commit 5323d3e50c20 ("wifi: mt76: mt76u: use a threaded NAPI for the RX path")  + The RX locking is referenced from commit 3298b1f866fb ("mt76: use + ieee80211_rx_list to pass frames to the network stack as a batch") and + commit 5b8ccdfb943f ("wifi: mt76: add missing locking to protect + against concurrent rx/status calls") + Test results: before after TX 30.5 30.5 @@ Commit message  ## drivers/net/wireless/ralink/rt2x00/rt2x00.h ##  @@ drivers/net/wireless/ralink/rt2x00/rt2x00.h: struct rt2x00_dev { + struct work_struct intf_work; +  /** - * Scheduled work for TX/RX done handling (USB devices) +- * Scheduled work for TX/RX done handling (USB devices) ++ * Scheduled work for TX done handling (USB devices) */  - struct work_struct rxdone_work; struct work_struct txdone_work; @@ drivers/net/wireless/ralink/rt2x00/rt2x00.h: struct rt2x00_dev {  + */  + struct net_device *napi_dev;  + struct napi_struct napi; ++ struct sk_buff_head rx_skb; ++ ++ /* ++ * Serialize the RX in NAPI with the TX status reports to mac80211. ++ */ ++ spinlock_t rx_lock;  + /* * Powersaving work */ +@@ drivers/net/wireless/ralink/rt2x00/rt2x00.h: void rt2x00lib_txdone_nomatch(struct queue_entry *entry, + struct txdone_entry_desc *txdesc); + void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); + void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp); ++void rt2x00lib_rx_complete(struct rt2x00_dev *rt2x00dev); +  + /* + * mac80211 handlers.  ## drivers/net/wireless/ralink/rt2x00/rt2x00dev.c ## +@@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: void rt2x00lib_txdone_nomatch(struct queue_entry *entry, +  + rt2x00lib_fill_tx_status(rt2x00dev, &txinfo, skbdesc, txdesc, + success); ++ spin_lock_bh(&rt2x00dev->rx_lock); + ieee80211_tx_status_noskb(rt2x00dev->hw, skbdesc->sta, &txinfo); ++ spin_unlock_bh(&rt2x00dev->rx_lock); + } +  + dev_kfree_skb_any(entry->skb); +@@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: void rt2x00lib_txdone(struct queue_entry *entry, + * send the status report back. + */ + if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { ++ spin_lock_bh(&rt2x00dev->rx_lock); + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT)) + ieee80211_tx_status_skb(rt2x00dev->hw, entry->skb); + else + ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); ++ spin_unlock_bh(&rt2x00dev->rx_lock); + } else { + dev_kfree_skb_any(entry->skb); + }  @@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: static void rt2x00lib_rxdone_skb(struct queue_entry *entry) rx_status->bw = rxdesc.bw; rx_status->antenna = rt2x00dev->link.ant.active.rx;   - ieee80211_rx_ni(rt2x00dev->hw, entry->skb);  + if (rt2x00dev->napi_dev) -+ ieee80211_rx_napi(rt2x00dev->hw, NULL, entry->skb, -+ &rt2x00dev->napi); ++ __skb_queue_tail(&rt2x00dev->rx_skb, entry->skb);  + else  + ieee80211_rx_ni(rt2x00dev->hw, entry->skb); }  void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp) +@@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: void rt2x00lib_rxdone(struct queue_entry *entry, unsigned int len, gfp_t gfp) + } + EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); +  ++void rt2x00lib_rx_complete(struct rt2x00_dev *rt2x00dev) ++{ ++ struct sk_buff *skb, *tmp; ++ LIST_HEAD(list); ++ ++ spin_lock(&rt2x00dev->rx_lock); ++ while ((skb = __skb_dequeue(&rt2x00dev->rx_skb)) != NULL) ++ ieee80211_rx_list(rt2x00dev->hw, NULL, skb, &list); ++ spin_unlock(&rt2x00dev->rx_lock); ++ ++ list_for_each_entry_safe(skb, tmp, &list, list) { ++ skb_list_del_init(skb); ++ napi_gro_receive(&rt2x00dev->napi, skb); ++ } ++} ++EXPORT_SYMBOL_GPL(rt2x00lib_rx_complete); ++ + /* + * Driver initialization handlers. + */ +@@ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c: int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) + mutex_init(&rt2x00dev->conf_mutex); + INIT_LIST_HEAD(&rt2x00dev->bar_list); + spin_lock_init(&rt2x00dev->bar_list_lock); ++ spin_lock_init(&rt2x00dev->rx_lock); ++ skb_queue_head_init(&rt2x00dev->rx_skb); + hrtimer_setup(&rt2x00dev->txstatus_timer, hrtimer_dummy_timeout, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); +   ## drivers/net/wireless/ralink/rt2x00/rt2x00usb.c ##  @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data) @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: static bool rt2x00usb_kick_tx_en struct queue_entry_priv_usb *entry_priv; struct queue_entry *entry; struct skb_frame_desc *skbdesc; + u8 rxd[32]; +  ++ rcu_read_lock(); + while (!rt2x00queue_empty(rt2x00dev->rx)) { + entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); +   @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: static void rt2x00usb_work_rxdone(struct work_struct *work) */ entry_priv = entry->priv_data; @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: static void rt2x00usb_work_rxdon  - GFP_KERNEL);  + GFP_ATOMIC); } ++ rt2x00lib_rx_complete(rt2x00dev); ++ rcu_read_unlock();  +  + napi_complete(napi);  + @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: static void rt2x00usb_interrupt_ }  static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data) +@@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data) +  + void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) + { +- struct work_struct *completion; ++ bool is_tx; + unsigned int i; +  + if (drop)  @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) - completion = &queue->rt2x00dev->txdone_work; + rt2x00usb_flush_entry); +  + /* +- * Obtain the queue completion handler ++ * Obtain the queue type + */ + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: +- completion = &queue->rt2x00dev->txdone_work; ++ is_tx = true; break; case QID_RX:  - completion = &queue->rt2x00dev->rxdone_work; -+ completion = NULL; ++ is_tx = false; break; default: return; @@ drivers/net/wireless/ralink/rt2x00/rt2x00usb.c: void rt2x00usb_flush_queue(struc * worker function runs, it should cleanup the queue. */  - queue_work(queue->rt2x00dev->workqueue, completion); -+ if (completion) { -+ queue_work(queue->rt2x00dev->workqueue, completion); ++ if (is_tx) { ++ queue_work(queue->rt2x00dev->workqueue, ++ &queue->rt2x00dev->txdone_work);  + } else {  + local_bh_disable();  + napi_schedule(&queue->rt2x00dev->napi);