bes2600: Patch D — atomicize ba_lock counters, drop the spinlock

The block-ack policy uses 4 int counters (ba_acc, ba_cnt, ba_acc_rx,
ba_cnt_rx) bumped per data frame in the TX and RX hot paths under
spin_lock_bh(&hw_priv->ba_lock).  The lock was the heaviest per-frame
synchronization cost remaining after Patch C v3 (which fixed the
sdio_rx_work relay).  Per the Opus structural critique (PR #8), this
pattern matches mac80211 driver convention for per-frame statistics:
atomic_t suffices, no lock needed.

Field-by-field changes in struct bes2600_common:
  ba_acc, ba_cnt, ba_acc_rx, ba_cnt_rx: int -> atomic_t
  ba_armed:                              new atomic_t (timer-arm flag)
  ba_ena:                                bool -> atomic_t
  ba_lock:                               removed (spinlock_t deleted)
  ba_hist:                               int (single-writer = ba_timer)

Producer hot path (txrx.c TX submit + RX receive):
  - atomic_add for the byte accumulator
  - atomic_inc for the frame counter
  - atomic_cmpxchg(&ba_armed, 0, 1) to claim the once-per-window
    mod_timer arm — at most ONE producer succeeds; race-free
  - no spin_lock_bh

Consumer paths (sta.c bes2600_ba_timer, sta.c disconnect-reset, sta.c
bes2600_ba_work, debug.c debugfs reader):
  - atomic_read snapshots all 4 counters into locals; the threshold
    predicate (acc/cnt >= THLD) tolerates approximate snapshots — the
    timer fires periodically, a single misclassification just delays
    the policy update by one tick
  - atomic_set zeroes the counters at end of timer-callback window;
    racing producer increments after the snapshot are lost (acceptable
    for stats; same approximation the original lock allowed under
    contention)
  - atomic_set(&ba_armed, 0) re-enables the next window's arm

Followup-amenable simplification: ba_hist remains int because only
the single ba_timer callback writes it; multiple writers would need
to upgrade it too.

This patch follows the cw1200-mainline-idiom established by Patch C v3
(structural fix, not bandaid).  The cw1200 reference doesn't have a
similar lock to compare; bes2600 inherited this from a later
Bestechnic addition rather than the upstream tree.
This commit is contained in:
2026-05-08 00:17:46 +02:00
parent a02f8b7629
commit 93f2aab656
5 changed files with 85 additions and 56 deletions
+13 -10
View File
@@ -996,14 +996,18 @@ bes2600_tx_h_ba_stat(struct bes2600_vif *priv,
if (!ieee80211_is_data(t->hdr->frame_control))
return;
spin_lock_bh(&hw_priv->ba_lock);
hw_priv->ba_acc += t->skb->len - t->hdrlen;
if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
/*
* Patch D: lock-free hot-path BA accounting. atomic_inc + atomic_add
* each per-frame; the once-per-window timer-arm uses cmpxchg on
* ba_armed so concurrent TX/RX can't both try to set the timer and
* we don't need cross-counter coherency on the ba_cnt/ba_cnt_rx pair.
*/
atomic_add(t->skb->len - t->hdrlen, &hw_priv->ba_acc);
atomic_inc(&hw_priv->ba_cnt);
if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) {
mod_timer(&hw_priv->ba_timer,
jiffies + BES2600_BLOCK_ACK_INTERVAL);
}
hw_priv->ba_cnt++;
spin_unlock_bh(&hw_priv->ba_lock);
}
static int
@@ -1651,14 +1655,13 @@ bes2600_rx_h_ba_stat(struct bes2600_vif *priv,
if (!priv->setbssparams_done)
return;
spin_lock_bh(&hw_priv->ba_lock);
hw_priv->ba_acc_rx += skb_len - hdrlen;
if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
/* Patch D: lock-free hot-path BA accounting; see TX side comment. */
atomic_add(skb_len - hdrlen, &hw_priv->ba_acc_rx);
atomic_inc(&hw_priv->ba_cnt_rx);
if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) {
mod_timer(&hw_priv->ba_timer,
jiffies + BES2600_BLOCK_ACK_INTERVAL);
}
hw_priv->ba_cnt_rx++;
spin_unlock_bh(&hw_priv->ba_lock);
}
void bes2600_rx_cb(struct bes2600_vif *priv,