[Cerowrt-devel] [bug #442] tearing apart ath_tx_start in search of the hang
Dave Taht
dave.taht at gmail.com
Mon Apr 14 19:08:00 EDT 2014
So what we are seeing here is some sort of problem with accounting
for pending_frames on a given queue. And since we've had a reminder
of how useful code review can be - and I'd rather like to understand
this logic anyway -
// my comments in //
/* Upon failure caller should free skb */
int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ath_tx_control *txctl)
{
struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = txctl->sta;
struct ieee80211_vif *vif = info->control.vif;
struct ath_softc *sc = hw->priv;
struct ath_txq *txq = txctl->txq;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf;
int q;
int ret;
ret = ath_tx_prepare(hw, skb, txctl);
if (ret)
return ret;
hdr = (struct ieee80211_hdr *) skb->data;
/*
* At this point, the vif, hw_key and sta pointers in the tx control
* info are no longer valid (overwritten by the ath_frame_info data.
*/
// I haven't looked at what skb_get_queue_mapping can return yet
q = skb_get_queue_mapping(skb);
ath_txq_lock(sc, txq);
if (txq == sc->tx.txq_map[q] &&
++txq->pending_frames > sc->tx.txq_max_pending[q] &&
!txq->stopped) {
ieee80211_stop_queue(sc->hw, q);
txq->stopped = true;
// is there a difference between stopped and sleeping?
}
// So if the queue is not mapped properly we don't increment pending
// frames. Also we are dependent on C processing the if left to right,
// which is a good assumption, but it leaves the ++txq as a side effect
if (txctl->an && ieee80211_is_data_present(hdr->frame_control))
tid = ath_get_skb_tid(sc, txctl->an, skb);
if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
ath_txq_unlock(sc, txq);
txq = sc->tx.uapsdq;
// So here we have a bit of state that changes after we've got some pending
// state above that's been changed. I imagine this lock could stay unlocked
// for a while and lead to races elsewhere.
// haven't a clue what tx.uapsdq is
ath_txq_lock(sc, txq);
} else if (txctl->an &&
ieee80211_is_data_present(hdr->frame_control)) {
WARN_ON(tid->ac->txq != txctl->txq);
if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
tid->ac->clear_ps_filter = true;
/*
* Add this frame to software queue for scheduling later
* for aggregation.
*/
TX_STAT_INC(txq->axq_qnum, a_queued_sw);
__skb_queue_tail(&tid->buf_q, skb);
if (!txctl->an->sleeping)
ath_tx_queue_tid(txq, tid);
// so if we're not sleeping, queue it up
// and regardless if we're sleeping or not, schedule it
ath_txq_schedule(sc, txq);
goto out;
}
// So if data is not present OR txctl->an is invalid OR
IEEE80211_TX_CTL_PS_RESPONSE is set in flags
/// we fall through to here.
bf = ath_tx_setup_buffer(sc, txq, tid, skb);
// if we fell through to here, tid can be null unless data was present
if (!bf) {
ath_txq_skb_done(sc, txq, skb);
if (txctl->paprd)
dev_kfree_skb_any(skb);
else
ieee80211_free_txskb(sc->hw, skb);
goto out;
}
// Well, I note that we incremented the frames earlier in some cases
// should they be decremented above?
bf->bf_state.bfs_paprd = txctl->paprd;
if (txctl->paprd)
bf->bf_state.bfs_paprd_timestamp = jiffies;
ath_set_rates(vif, sta, bf);
ath_tx_send_normal(sc, txq, tid, skb);
// Not clear as to why you set_rates here, and I assume tx_send_normal
// sends a non-aggregate
out:
ath_txq_unlock(sc, txq);
return 0;
}
--
Dave Täht
NSFW: https://w2.eff.org/Censorship/Internet_censorship_bills/russell_0296_indecent.article
More information about the Cerowrt-devel
mailing list