bes2600: drop sdio_rx_work relay, IRQ→bh-direct (no-relay architecture)
Patch C v3 — match cw1200 mainline architecture (drivers/net/wireless/st/cw1200/). Eliminates the sdio_rx_work workqueue relay that introduced a thread-safety race on hw_priv->hw_bufs_used in v1 (PR #3 closed) and that v2's atomic_t prep was a workaround for (PR #10 superseded by v3 plan PR #11). Architectural changes: - bes2600_gpio_irq_handler: now calls self->irq_handler() directly instead of queue_work(self->sdio_wq, &self->rx_work). Bumps bh_rx atomic + wakes bh_wq. - bes2600_bh_rx_helper (BES_SDIO_RX_MULTIPLE_ENABLE branch): now calls priv->sbus_ops->bus_rx_batch() to do the SDIO read inline. No pipe_read, no skb_dequeue. - bes2600_sdio_read_rx_batch (new): the SDIO read sequence extracted from sdio_rx_work, registered as sbus_ops->bus_rx_batch. Runs in bh thread context. - bes2600_sdio_extract_packets: calls bes2600_bh_handle_rx_skb() directly per parsed SKB. No skb_queue_tail, no rx_queue. - bes2600_bh_handle_rx_skb (new in bh.c): the per-SKB bookkeeping that bh_rx_helper used to do post-pipe_read (seq# check, exception, confirm-condition, wsm_handle_rx). Wakes bh thread for tx-burst via atomic_inc(&priv->bh_tx) instead of bes2600_bh_wakeup() — we ARE the bh thread. - Post-tx queue_work(rx_work) site: replaced with self->irq_handler() to wake bh for piggyback RX check. Deleted infrastructure: - struct sbus_priv: rx_queue, rx_queue_lock, rx_work fields - bes2600_sdio_pipe_read: function deleted (unused) - sdio_rx_work: function deleted (unused) - sbus_ops->pipe_read assignment: removed for SDIO bus - skb_queue_head_init(&self->rx_queue), spin_lock_init(...), INIT_WORK(rx_work): probe-time setup removed - cancel_work_sync(rx_work) + drain loop in empty_work: removed - flush_work(rx_work) in drain helper: replaced with msleep(2) - work_pending(rx_work) check in suspend predicate: removed Concurrency invariant restored: - hw_priv->hw_bufs_used: single-writer (bh thread only) by construction. No atomic_t needed. - hw_priv->hw_bufs_used_vif[]: ditto. - hw_priv->wsm_tx_pending[]: ditto. - All other shared state: unchanged or already protected. Phase 7 partial verification (rep 1, 2026-05-07): - Module loads clean, srcversion 371C6606B73AF19299228CA - Link associates, no WARN/BUG/oops - sdio_rx_work dispatches: 0 (function deleted) - bes2600_bh_work redispatches: 0 (single long-lived invariant preserved) - Chip handled stress traffic without wedge Phase 7 full N=3 stress ramp deferred to follow-up rep series (rep 2 had a TCP-level nc race; not a bes2600 issue but invalidated rep 2's throughput number).
This commit is contained in:
+125
-4
@@ -959,6 +959,119 @@ static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Direct-deliver an RX SKB into the WSM/mac80211 stack.
|
||||
*
|
||||
* Patch C v3 (no-relay architecture, matches cw1200): the bh thread
|
||||
* calls bes2600_sdio_read_rx_batch which calls
|
||||
* bes2600_sdio_extract_packets which calls THIS function per parsed
|
||||
* SKB. No rx_queue, no sdio_rx_work, no inter-thread handoff.
|
||||
*
|
||||
* Single-writer-from-bh invariant on hw_priv->hw_bufs_used,
|
||||
* hw_priv->hw_bufs_used_vif[] and hw_priv->wsm_tx_pending[] is
|
||||
* preserved BY CONSTRUCTION — there is now only one writer (the bh
|
||||
* thread itself), same as cw1200's design. No atomic_t conversion
|
||||
* needed.
|
||||
*
|
||||
* Contract:
|
||||
* - process context, sleepable. wsm_handle_rx (wsm.c, EXPORT_SYMBOL)
|
||||
* acquires wsm_cmd.lock and may sleep on wait_event_timeout.
|
||||
* - caller holds no bes2600 spinlock. bes2600_sdio_unlock(self) is
|
||||
* called inside read_rx_batch before extract_packets is invoked.
|
||||
* - SKB ownership: function frees on every path (success + error).
|
||||
* - No need to wake the bh thread on TX-confirm — we ARE the bh
|
||||
* thread; tx_burst is signalled by returning *tx_out = 1 to the
|
||||
* caller (bh_rx_helper), which propagates it to bh's outer loop.
|
||||
*/
|
||||
int bes2600_bh_handle_rx_skb(struct bes2600_common *priv, struct sk_buff *skb)
|
||||
{
|
||||
struct wsm_hdr *wsm;
|
||||
size_t wsm_len;
|
||||
u16 wsm_id;
|
||||
u8 wsm_seq;
|
||||
int tx = 0;
|
||||
u32 confirm_label = 0x0;
|
||||
|
||||
if (!skb)
|
||||
return 0;
|
||||
|
||||
wsm = (struct wsm_hdr *)skb->data;
|
||||
wsm_len = __le16_to_cpu(wsm->len);
|
||||
if (WARN_ON(wsm_len > skb->len)) {
|
||||
bes_err("wsm_len err %d %d\n", (int)wsm_len, (int)skb->len);
|
||||
dev_kfree_skb(skb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (priv->wsm_enable_wsm_dumps)
|
||||
print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, 16, 1,
|
||||
skb->data, wsm_len, false);
|
||||
|
||||
wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
|
||||
wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
|
||||
bes_devel("bes2600_bh_handle_rx_skb wsm_id:0x%04x seq:%d\n",
|
||||
wsm_id, wsm_seq);
|
||||
|
||||
skb_trim(skb, wsm_len);
|
||||
|
||||
if (wsm_id == 0x0800) {
|
||||
wsm_handle_exception(priv,
|
||||
&skb->data[sizeof(*wsm)],
|
||||
wsm_len - sizeof(*wsm));
|
||||
bes_err("wsm exception\n");
|
||||
dev_kfree_skb(skb);
|
||||
return -1;
|
||||
} else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) {
|
||||
bes_err("seq error! %u. %u. 0x%x.", wsm_seq,
|
||||
priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id);
|
||||
dev_kfree_skb(skb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bes2600_bh_parse_wakeup_event(priv, skb);
|
||||
|
||||
priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7;
|
||||
|
||||
if (IS_DRIVER_TO_MCU_CMD(wsm_id))
|
||||
confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label);
|
||||
|
||||
if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) {
|
||||
int rc = wsm_release_tx_buffer(priv, 1);
|
||||
bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id));
|
||||
|
||||
if (rc < 0) {
|
||||
bes_err("wsm_release_tx_buffer failed: %d\n", rc);
|
||||
dev_kfree_skb(skb);
|
||||
return rc;
|
||||
} else if (rc > 0) {
|
||||
tx = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* wsm_handle_rx takes care of SKB lifetime: zeroes *skb_p if consumed. */
|
||||
if (wsm_handle_rx(priv, wsm_id, wsm, &skb)) {
|
||||
bes_err("wsm_handle_rx failed (id=0x%04x)\n", wsm_id);
|
||||
if (skb)
|
||||
dev_kfree_skb(skb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skb)
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
/*
|
||||
* Signal "tx side has new headroom" via atomic so the bh outer
|
||||
* loop's wait_event predicate notices on its next wait. No
|
||||
* cross-thread wake needed because we are the bh thread; the
|
||||
* outer loop will pick this up after read_rx_batch returns.
|
||||
*/
|
||||
if (tx)
|
||||
atomic_inc(&priv->bh_tx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bes2600_bh_handle_rx_skb);
|
||||
|
||||
static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
|
||||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
@@ -970,10 +1083,18 @@ static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
|
||||
u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */
|
||||
|
||||
#if defined(BES_SDIO_RX_MULTIPLE_ENABLE)
|
||||
skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv);
|
||||
if (!skb)
|
||||
return 0;
|
||||
rx = 1; // always consider rx pipe not empty
|
||||
/*
|
||||
* Patch C v3: the bh thread does the SDIO read inline via
|
||||
* sbus_ops->bus_rx_batch. bes2600_sdio_read_rx_batch reads the
|
||||
* multi-RX coalesced frames out of the chip and delivers each
|
||||
* one inline via bes2600_bh_handle_rx_skb (no rx_queue, no
|
||||
* pipe_read, no inter-thread handoff). Return value: 0 on
|
||||
* success (bh outer loop will check whether to continue),
|
||||
* negative on read error.
|
||||
*/
|
||||
if (priv->sbus_ops->bus_rx_batch)
|
||||
return priv->sbus_ops->bus_rx_batch(priv->sbus_priv);
|
||||
return 0;
|
||||
#else
|
||||
u32 ctrl_reg = 0;
|
||||
size_t read_len = 0;
|
||||
|
||||
Reference in New Issue
Block a user