Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2bfb5092 authored by Johannes Berg's avatar Johannes Berg
Browse files

iwlwifi: use threaded interrupt handler



With new transports coming up, move to threaded
interrupt handling now. This has the advantage
that we can use the same locking scheme with all
different transports we may need to implement.

Note that the TX path obviously still runs in a
tasklet, so some spin_lock() calls need to change
to spin_lock_bh() calls to properly lock out the
TX path.

In my test on a Calpella platform this has no
impact on throughput or latency.

Also add lockdep annotations to avoid lockups due
to catch sending synchronous commands or using
locks that connect with them from the irq thread.

Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent c9f7a8ab
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -790,7 +790,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,

	memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));

	ieee80211_rx(priv->hw, skb);
	ieee80211_rx_ni(priv->hw, skb);
}

static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
+2 −2
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ static int iwl_process_add_sta_resp(struct iwl_priv *priv,
	IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n",
		       sta_id);

	spin_lock(&priv->sta_lock);
	spin_lock_bh(&priv->sta_lock);

	switch (add_sta_resp->status) {
	case ADD_STA_SUCCESS_MSK:
@@ -119,7 +119,7 @@ static int iwl_process_add_sta_resp(struct iwl_priv *priv,
		       priv->stations[sta_id].sta.mode ==
		       STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
		       addsta->sta.addr);
	spin_unlock(&priv->sta_lock);
	spin_unlock_bh(&priv->sta_lock);

	return ret;
}
+8 −8
Original line number Diff line number Diff line
@@ -1117,7 +1117,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
	sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
		IWLAGN_TX_RES_RA_POS;

	spin_lock(&priv->sta_lock);
	spin_lock_bh(&priv->sta_lock);

	if (is_agg)
		iwl_rx_reply_tx_agg(priv, tx_resp);
@@ -1239,11 +1239,11 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
			   le16_to_cpu(tx_resp->seq_ctl));

	iwl_check_abort_status(priv, tx_resp->frame_count, status);
	spin_unlock(&priv->sta_lock);
	spin_unlock_bh(&priv->sta_lock);

	while (!skb_queue_empty(&skbs)) {
		skb = __skb_dequeue(&skbs);
		ieee80211_tx_status(priv->hw, skb);
		ieee80211_tx_status_ni(priv->hw, skb);
	}

	if (is_offchannel_skb)
@@ -1290,12 +1290,12 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
	tid = ba_resp->tid;
	agg = &priv->tid_data[sta_id][tid].agg;

	spin_lock(&priv->sta_lock);
	spin_lock_bh(&priv->sta_lock);

	if (unlikely(!agg->wait_for_ba)) {
		if (unlikely(ba_resp->bitmap))
			IWL_ERR(priv, "Received BA when not expected\n");
		spin_unlock(&priv->sta_lock);
		spin_unlock_bh(&priv->sta_lock);
		return 0;
	}

@@ -1309,7 +1309,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
		IWL_DEBUG_TX_QUEUES(priv,
				    "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n",
				    scd_flow, sta_id, tid, agg->txq_id);
		spin_unlock(&priv->sta_lock);
		spin_unlock_bh(&priv->sta_lock);
		return 0;
	}

@@ -1378,11 +1378,11 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
		}
	}

	spin_unlock(&priv->sta_lock);
	spin_unlock_bh(&priv->sta_lock);

	while (!skb_queue_empty(&reclaimed_skbs)) {
		skb = __skb_dequeue(&reclaimed_skbs);
		ieee80211_tx_status(priv->hw, skb);
		ieee80211_tx_status_ni(priv->hw, skb);
	}

	return 0;
+6 −4
Original line number Diff line number Diff line
@@ -113,13 +113,13 @@ struct iwl_cfg;
 *	May sleep
 * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
 *	HCMD the this Rx responds to.
 *	Must be atomic and called with BH disabled.
 *	This callback may sleep, it is called from a threaded IRQ handler.
 * @queue_full: notifies that a HW queue is full.
 *	Must be atomic and called with BH disabled.
 * @queue_not_full: notifies that a HW queue is not full any more.
 *	Must be atomic and called with BH disabled.
 * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that
 *	the radio is killed. Must be atomic.
 *	the radio is killed. May sleep.
 * @free_skb: allows the transport layer to free skbs that haven't been
 *	reclaimed by the op_mode. This can happen when the driver is freed and
 *	there are Tx packets pending in the transport layer.
@@ -130,8 +130,7 @@ struct iwl_cfg;
 *	called with BH disabled.
 * @nic_config: configure NIC, called before firmware is started.
 *	May sleep
 * @wimax_active: invoked when WiMax becomes active.  Must be atomic and called
 *	with BH disabled.
 * @wimax_active: invoked when WiMax becomes active. May sleep
 */
struct iwl_op_mode_ops {
	struct iwl_op_mode *(*start)(struct iwl_trans *trans,
@@ -178,6 +177,7 @@ static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode,
				  struct iwl_rx_cmd_buffer *rxb,
				  struct iwl_device_cmd *cmd)
{
	might_sleep();
	return op_mode->ops->rx(op_mode, rxb, cmd);
}

@@ -196,6 +196,7 @@ static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode,
static inline void iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode,
					  bool state)
{
	might_sleep();
	op_mode->ops->hw_rf_kill(op_mode, state);
}

@@ -223,6 +224,7 @@ static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode)

static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode)
{
	might_sleep();
	op_mode->ops->wimax_active(op_mode);
}

+27 −2
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@

#include <linux/ieee80211.h>
#include <linux/mm.h> /* for page_address */
#include <linux/lockdep.h>

#include "iwl-debug.h"
#include "iwl-config.h"
@@ -526,6 +527,10 @@ struct iwl_trans {

	struct dentry *dbgfs_dir;

#ifdef CONFIG_LOCKDEP
	struct lockdep_map sync_cmd_lockdep_map;
#endif

	/* pointer to trans specific struct */
	/*Ensure that this pointer will always be aligned to sizeof pointer */
	char trans_specific[0] __aligned(sizeof(void *));
@@ -604,10 +609,20 @@ static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
				     struct iwl_host_cmd *cmd)
{
	int ret;

	WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
		  "%s bad state = %d", __func__, trans->state);

	return trans->ops->send_cmd(trans, cmd);
	if (!(cmd->flags & CMD_ASYNC))
		lock_map_acquire_read(&trans->sync_cmd_lockdep_map);

	ret = trans->ops->send_cmd(trans, cmd);

	if (!(cmd->flags & CMD_ASYNC))
		lock_map_release(&trans->sync_cmd_lockdep_map);

	return ret;
}

static inline struct iwl_device_cmd *
@@ -791,4 +806,14 @@ iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
int __must_check iwl_pci_register_driver(void);
void iwl_pci_unregister_driver(void);

static inline void trans_lockdep_init(struct iwl_trans *trans)
{
#ifdef CONFIG_LOCKDEP
	static struct lock_class_key __key;

	lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map",
			 &__key, 0);
#endif
}

#endif /* __iwl_trans_h__ */
Loading