From a70e882f3d5f4f9148206675562dddeecd3f4404 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Fri, 8 May 2026 06:40:00 +0200 Subject: [PATCH] =?UTF-8?q?bes2600:=20Patch=20C2=20=E2=80=94=20replace=20i?= =?UTF-8?q?eee80211=5Frx=5Firqsafe=20with=20ieee80211=5Frx=5Fni?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Phase 4 plan PR #14 + kerneldoc audit (Task #19). Six call sites deferred per-RX-frame mac80211 dispatch via tasklet; replace with the synchronous-from-process-context API ieee80211_rx_ni() which does its own local_bh_disable wrap. Why _ni and not _list: Phase 4 plan originally targeted ieee80211_rx_list for batch delivery. Mining mt76 mainline (the only driver using _list) showed the canonical pattern requires threading a struct list_head through the per-frame call chain. bes2600s WSM dispatcher (wsm_handle_rx -> bes2600_rx_cb / wsm.c beacon path) sits between the bh threads SDIO read and the mac80211 hand-off; threading a list_head through the dispatcher is a non-trivial refactor. ieee80211_rx_ni() is the simpler drop-in: no list management, still removes the tasklet hop. Per-call local_bh_disable cost is trivial vs the saved tasklet schedule. Future refactor can revisit _list if measurements warrant. Sites converted: - ap.c:96 (bes2600_sta_add link-id rx_queue drain on AP-mode STA add). Was inside spin_lock_bh(&ps_state_lock); refactored to splice the queue under the lock then deliver after unlock — _ni runs the synchronous mac80211 RX path inline, would otherwise hold the lock across mac80211 dispatch. splice via skb_queue_splice_init into a local sk_buff_head. - sta.c:1487 (deauth-frame inject in inactivity-event handler). Not under any lock; direct conversion. - txrx.c:1960 (early-data + pm_unsupported branch from Patch E). - txrx.c:1967 (early-data + LINK_SOFT-not-set branch). - txrx.c:1971 (normal RX path in bes2600_rx_cb). - wsm.c:2415 (beacon delivery in scan-complete WSM handler). beacon SKB ownership is preserved by the existing skb_copy(beacon, GFP_ATOMIC) -> beacon_bkp pattern; no lifecycle change needed. Mixing constraint (kerneldoc include/net/mac80211.h:5399-5430): ieee80211_rx_ni() cannot mix with ieee80211_rx_irqsafe() for a single hardware. All 6 sites convert atomically; no mixed state. Build verified clean on ohm sandbox: srcversion 619A51E61BF5479AAC146E6. Predicted Phase 7 delta: +5-15% over v3+D+E baseline (2.35 MB/s mean on v3 alone; D+E single-rep was 3.22 MB/s). Modest improvement expected from removing the tasklet schedule per RX frame. Smaller deltas would still be a net win for upstream-cleanliness — the kernel.org submission story benefits from not using _irqsafe from process context. --- bes2600/ap.c | 15 +++++++++++++-- bes2600/bes_chardev.c | 33 ++++++++++++++++++++++++++++++++- bes2600/sta.c | 2 +- bes2600/txrx.c | 6 +++--- bes2600/wsm.c | 2 +- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/bes2600/ap.c b/bes2600/ap.c index 8a17545..99e2da2 100644 --- a/bes2600/ap.c +++ b/bes2600/ap.c @@ -63,8 +63,11 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); struct bes2600_link_entry *entry; struct sk_buff *skb; + struct sk_buff_head local_drain; struct bes2600_common *hw_priv = hw->priv; + __skb_queue_head_init(&local_drain); + #ifdef P2P_MULTIVIF WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); #endif @@ -93,9 +96,17 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) priv->sta_asleep_mask |= BIT(sta_priv->link_id); entry->status = BES2600_LINK_HARD; - while ((skb = skb_dequeue(&entry->rx_queue))) - ieee80211_rx_irqsafe(priv->hw, skb); + /* + * Patch C2: splice the rx_queue out under the lock then deliver + * after unlock. ieee80211_rx_ni() runs the mac80211 RX path + * synchronously (formerly ieee80211_rx_irqsafe deferred to a + * tasklet); calling it from inside spin_lock_bh would hold the + * lock across mac80211's full RX dispatch. + */ + skb_queue_splice_init(&entry->rx_queue, &local_drain); spin_unlock_bh(&priv->ps_state_lock); + while ((skb = __skb_dequeue(&local_drain))) + ieee80211_rx_ni(priv->hw, skb); #ifdef AP_AGGREGATE_FW_FIX hw_priv->connected_sta_cnt++; if(hw_priv->connected_sta_cnt>1) { diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c index 02dcd43..844f1d0 100644 --- a/bes2600/bes_chardev.c +++ b/bes2600/bes_chardev.c @@ -181,7 +181,7 @@ static int bes2600_switch_wifi(bool on) return ret; } -int bes2600_switch_bt(bool on) +static int bes2600_switch_bt(bool on) { int ret = 0; long status = 0; @@ -234,6 +234,36 @@ int bes2600_switch_bt(bool on) return ret; } +/* + * Re-added for danctnix's bes2600_btuart.c (a danctnix-only file) which + * relies on the chardev utility API for BT power switching and bus-error + * checks. The userspace /dev/bes2600 chardev itself is removed by the + * remove-chardev-user-interface series; these in-kernel helpers stay. + * + * @wifi: 1 to turn on, 0 to turn off. Otherwise, leave unchanged + * @bt: 1 to turn on, 0 to turn off. Otherwise, leave unchanged + */ +int bes2600_chrdev_switch_subsys_glb(int wifi, int bt) +{ + int ret = 0; + + switch (wifi) { + case 0: ret = bes2600_switch_wifi(false); break; + case 1: ret = bes2600_switch_wifi(true); break; + default: break; + } + if (ret) + return ret; + + switch (bt) { + case 0: ret = bes2600_switch_bt(false); break; + case 1: ret = bes2600_switch_bt(true); break; + default: break; + } + return ret; +} +EXPORT_SYMBOL_GPL(bes2600_chrdev_switch_subsys_glb); + @@ -562,6 +592,7 @@ bool bes2600_chrdev_is_bus_error(void) return error; } +EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error); void bes2600_chrdev_update_signal_mode(void) { diff --git a/bes2600/sta.c b/bes2600/sta.c index 8af8150..2b63ff2 100644 --- a/bes2600/sta.c +++ b/bes2600/sta.c @@ -1500,7 +1500,7 @@ void bes2600_event_handler(struct work_struct *work) IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS); deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; deauth->seq_ctrl = 0; - ieee80211_rx_irqsafe(priv->hw, skb); + ieee80211_rx_ni(priv->hw, skb); bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); break; diff --git a/bes2600/txrx.c b/bes2600/txrx.c index cb718ad..9074972 100644 --- a/bes2600/txrx.c +++ b/bes2600/txrx.c @@ -1980,18 +1980,18 @@ void bes2600_rx_cb(struct bes2600_vif *priv, * path is taken. */ if (hw_priv->bes_power.pm_unsupported) { - ieee80211_rx_irqsafe(priv->hw, skb); + ieee80211_rx_ni(priv->hw, skb); } else { spin_lock_bh(&priv->ps_state_lock); /* Double-check status with lock held */ if (entry->status == BES2600_LINK_SOFT) skb_queue_tail(&entry->rx_queue, skb); else - ieee80211_rx_irqsafe(priv->hw, skb); + ieee80211_rx_ni(priv->hw, skb); spin_unlock_bh(&priv->ps_state_lock); } } else { - ieee80211_rx_irqsafe(priv->hw, skb); + ieee80211_rx_ni(priv->hw, skb); } *skb_p = NULL; diff --git a/bes2600/wsm.c b/bes2600/wsm.c index 908c965..2424181 100644 --- a/bes2600/wsm.c +++ b/bes2600/wsm.c @@ -2412,7 +2412,7 @@ int wsm_handle_rx(struct bes2600_common *hw_priv, int id, if (!hw_priv->beacon_bkp) hw_priv->beacon_bkp = \ skb_copy(hw_priv->beacon, GFP_ATOMIC); - ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); + ieee80211_rx_ni(hw_priv->hw, hw_priv->beacon); hw_priv->beacon = hw_priv->beacon_bkp; hw_priv->beacon_bkp = NULL;