Search Linux Wireless

[RFT 2/4] ath10k: rework peer accounting

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



It was troublesome to iterate over peers within a
sleepable context (e.g. while holding conf_mutex).
This will be necessary for some upcomming changes
to tx flushing.

Making peer allocation and initial setup
ar->conf_mutex bound instead ar->data_lock solves
the problem.

Signed-off-by: Michal Kazior <michal.kazior@xxxxxxxxx>
---
 drivers/net/wireless/ath/ath10k/core.h |  5 +++
 drivers/net/wireless/ath/ath10k/mac.c  | 69 +++++++++++++++++++++++++++++++---
 drivers/net/wireless/ath/ath10k/txrx.c | 29 +++++++-------
 3 files changed, 81 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8edd6da..61e325a 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -199,9 +199,14 @@ struct ath10k_dfs_stats {
 #define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
 
 struct ath10k_peer {
+	/* protected by conf_mutex + data_lock */
 	struct list_head list;
+
+	/* protected by conf_mutex */
 	int vdev_id;
 	u8 addr[ETH_ALEN];
+
+	/* protected by data_lock */
 	DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
 	struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
 };
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 25362ef..9af3ed5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -318,28 +318,61 @@ static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
 
 static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 {
+	struct ath10k_peer *peer;
 	int ret;
 
 	lockdep_assert_held(&ar->conf_mutex);
 
+	peer = ath10k_peer_find(ar, vdev_id, addr);
+	if (peer) {
+		ath10k_warn("peer %pM on vdev %i already exists\n",
+			    addr, vdev_id);
+		return -EINVAL;
+	}
+
+	peer = kzalloc(sizeof(*peer), GFP_KERNEL);
+	if (!peer) {
+		ath10k_warn("failed to allocate peer %pM on vdev %i: not enough memory\n",
+			    addr, vdev_id);
+		return -ENOMEM;
+	}
+
+	peer->vdev_id = vdev_id;
+	memcpy(peer->addr, addr, ETH_ALEN);
+
+	spin_lock_bh(&ar->data_lock);
+	list_add(&peer->list, &ar->peers);
+	spin_unlock_bh(&ar->data_lock);
+
 	ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
 	if (ret) {
 		ath10k_warn("failed to create wmi peer %pM on vdev %i: %i\n",
 			    addr, vdev_id, ret);
-		return ret;
+		goto err_free;
 	}
 
 	ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
 	if (ret) {
 		ath10k_warn("failed to wait for created wmi peer %pM on vdev %i: %i\n",
 			    addr, vdev_id, ret);
-		return ret;
+		goto err_free;
 	}
 	spin_lock_bh(&ar->data_lock);
 	ar->num_peers++;
 	spin_unlock_bh(&ar->data_lock);
 
 	return 0;
+
+err_free:
+	spin_lock_bh(&ar->data_lock);
+	list_del(&peer->list);
+	/* very unlikely, but check anyway */
+	if (!bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
+		ath10k_warn("removing peer %pM on vdev %i still being mapped in firmware\n",
+			    addr, vdev_id);
+	spin_unlock_bh(&ar->data_lock);
+	kfree(peer);
+	return ret;
 }
 
 static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
@@ -416,22 +449,46 @@ static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
 
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 {
+	struct ath10k_peer *peer;
 	int ret;
 
 	lockdep_assert_held(&ar->conf_mutex);
 
+	peer = ath10k_peer_find(ar, vdev_id, addr);
+	if (!peer) {
+		ath10k_warn("failed to lookup peer %pM on vdev %i\n",
+			    addr, vdev_id);
+		return -ENOENT;
+	}
+
 	ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
-	if (ret)
-		return ret;
+	if (ret) {
+		ath10k_warn("failed to request wmi peer %pM on vdev %i removal: %d\n",
+			    addr, vdev_id, ret);
+		goto out;
+	}
 
 	ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
-	if (ret)
-		return ret;
+	if (ret) {
+		ath10k_warn("failed to wait for wmi peer %pM on vdev %i removal: %d\n",
+			    addr, vdev_id, ret);
+		goto out;
+	}
 
 	spin_lock_bh(&ar->data_lock);
 	ar->num_peers--;
 	spin_unlock_bh(&ar->data_lock);
 
+out:
+	spin_lock_bh(&ar->data_lock);
+	list_del(&peer->list);
+	if (!bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
+		ath10k_warn("removing peer %pM on vdev %i still being mapped in firmware\n",
+			    addr, vdev_id);
+	spin_unlock_bh(&ar->data_lock);
+
+	kfree(peer);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 82669a7..d802569 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -100,13 +100,13 @@ exit:
 		wake_up(&htt->empty_tx_wq);
 }
 
+/* hold conf_mutex for simple iteration, or conf_mutex+data_lock for
+ * modifications */
 struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
 				     const u8 *addr)
 {
 	struct ath10k_peer *peer;
 
-	lockdep_assert_held(&ar->data_lock);
-
 	list_for_each_entry(peer, &ar->peers, list) {
 		if (peer->vdev_id != vdev_id)
 			continue;
@@ -139,10 +139,14 @@ static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id,
 	int ret;
 
 	ret = wait_event_timeout(ar->peer_mapping_wq, ({
-			bool mapped;
+			struct ath10k_peer *peer;
+			bool mapped = false;
 
 			spin_lock_bh(&ar->data_lock);
-			mapped = !!ath10k_peer_find(ar, vdev_id, addr);
+			peer = ath10k_peer_find(ar, vdev_id, addr);
+			if (peer)
+				mapped = !bitmap_empty(peer->peer_ids,
+						       ATH10K_MAX_NUM_PEER_IDS);
 			spin_unlock_bh(&ar->data_lock);
 
 			mapped == expect_mapped;
@@ -173,20 +177,16 @@ void ath10k_peer_map_event(struct ath10k_htt *htt,
 	spin_lock_bh(&ar->data_lock);
 	peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr);
 	if (!peer) {
-		peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
-		if (!peer)
-			goto exit;
-
-		peer->vdev_id = ev->vdev_id;
-		memcpy(peer->addr, ev->addr, ETH_ALEN);
-		list_add(&peer->list, &ar->peers);
-		wake_up(&ar->peer_mapping_wq);
+		ath10k_warn("failed to map peer %pM on vdev %i: no such entry\n",
+			    ev->addr, ev->vdev_id);
+		goto exit;
 	}
 
 	ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
 		   ev->vdev_id, ev->addr, ev->peer_id);
 
 	set_bit(ev->peer_id, peer->peer_ids);
+	wake_up(&ar->peer_mapping_wq);
 exit:
 	spin_unlock_bh(&ar->data_lock);
 }
@@ -210,11 +210,8 @@ void ath10k_peer_unmap_event(struct ath10k_htt *htt,
 
 	clear_bit(ev->peer_id, peer->peer_ids);
 
-	if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
-		list_del(&peer->list);
-		kfree(peer);
+	if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
 		wake_up(&ar->peer_mapping_wq);
-	}
 
 exit:
 	spin_unlock_bh(&ar->data_lock);
-- 
1.8.5.3

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux