iwlwifi: fix and add missing sta_lock usage

There are a few places where sta_lock is used, but the
station information protected by it is accessed outside
of the lock. Address this in two ways, if the access
won't sleep then just move the access into the lock, if
the access can sleep then copy the needed station
information to the stack to be accessed without risk of
it changing while access in progress.

Additionally, a number of other places access station
station information without holding the sta_lock, fix
those as well.

Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 18b1546..52bec10 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -595,11 +595,17 @@
 	}
 
 	txq_id = get_queue_from_ac(skb_get_queue_mapping(skb));
+
+	/* irqs already disabled/saved above when locking priv->lock */
+	spin_lock(&priv->sta_lock);
+
 	if (ieee80211_is_data_qos(fc)) {
 		qc = ieee80211_get_qos_ctl(hdr);
 		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-		if (unlikely(tid >= MAX_TID_COUNT))
+		if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) {
+			spin_unlock(&priv->sta_lock);
 			goto drop_unlock;
+		}
 		seq_number = priv->stations[sta_id].tid[tid].seq_number;
 		seq_number &= IEEE80211_SCTL_SEQ;
 		hdr->seq_ctrl = hdr->seq_ctrl &
@@ -617,11 +623,18 @@
 	swq_id = txq->swq_id;
 	q = &txq->q;
 
-	if (unlikely(iwl_queue_space(q) < q->high_mark))
+	if (unlikely(iwl_queue_space(q) < q->high_mark)) {
+		spin_unlock(&priv->sta_lock);
 		goto drop_unlock;
+	}
 
-	if (ieee80211_is_data_qos(fc))
+	if (ieee80211_is_data_qos(fc)) {
 		priv->stations[sta_id].tid[tid].tfds_in_queue++;
+		if (!ieee80211_has_morefrags(fc))
+			priv->stations[sta_id].tid[tid].seq_number = seq_number;
+	}
+
+	spin_unlock(&priv->sta_lock);
 
 	/* Set up driver data for this TFD */
 	memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info));
@@ -700,8 +713,6 @@
 
 	if (!ieee80211_has_morefrags(hdr->frame_control)) {
 		txq->need_update = 1;
-		if (qc)
-			priv->stations[sta_id].tid[tid].seq_number = seq_number;
 	} else {
 		wait_write_ptr = 1;
 		txq->need_update = 0;
@@ -1006,6 +1017,8 @@
 	if (ret)
 		return ret;
 
+	spin_lock_irqsave(&priv->sta_lock, flags);
+	tid_data = &priv->stations[sta_id].tid[tid];
 	if (tid_data->tfds_in_queue == 0) {
 		IWL_DEBUG_HT(priv, "HW queue is empty\n");
 		tid_data->agg.state = IWL_AGG_ON;
@@ -1015,6 +1028,7 @@
 			     tid_data->tfds_in_queue);
 		tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
 	}
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	return ret;
 }
 
@@ -1037,11 +1051,14 @@
 		return -ENXIO;
 	}
 
+	spin_lock_irqsave(&priv->sta_lock, flags);
+
 	if (priv->stations[sta_id].tid[tid].agg.state ==
 				IWL_EMPTYING_HW_QUEUE_ADDBA) {
 		IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
+		spin_unlock_irqrestore(&priv->sta_lock, flags);
 		return 0;
 	}
 
@@ -1059,13 +1076,17 @@
 		IWL_DEBUG_HT(priv, "Stopping a non empty AGG HW QUEUE\n");
 		priv->stations[sta_id].tid[tid].agg.state =
 				IWL_EMPTYING_HW_QUEUE_DELBA;
+		spin_unlock_irqrestore(&priv->sta_lock, flags);
 		return 0;
 	}
 
 	IWL_DEBUG_HT(priv, "HW queue is empty\n");
 	priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	/* do not restore/save irqs */
+	spin_unlock(&priv->sta_lock);
+	spin_lock(&priv->lock);
+
 	/*
 	 * the only reason this call can fail is queue number out of range,
 	 * which can happen if uCode is reloaded and all the station
@@ -1089,6 +1110,8 @@
 	u8 *addr = priv->stations[sta_id].sta.sta.addr;
 	struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid];
 
+	WARN_ON(!spin_is_locked(&priv->sta_lock));
+
 	switch (priv->stations[sta_id].tid[tid].agg.state) {
 	case IWL_EMPTYING_HW_QUEUE_DELBA:
 		/* We are reclaiming the last packet of the */
@@ -1113,6 +1136,7 @@
 		}
 		break;
 	}
+
 	return 0;
 }
 
@@ -1276,6 +1300,7 @@
 	int index;
 	int sta_id;
 	int tid;
+	unsigned long flags;
 
 	/* "flow" corresponds to Tx queue */
 	u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
@@ -1298,7 +1323,7 @@
 	/* Find index just before block-ack window */
 	index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
 
-	/* TODO: Need to get this copy more safely - now good for debug */
+	spin_lock_irqsave(&priv->sta_lock, flags);
 
 	IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, "
 			   "sta_id = %d\n",
@@ -1334,4 +1359,6 @@
 
 		iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow);
 	}
+
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
 }