Search Linux Wireless

[RFTv2 3/5] ath10k: rework peer accounting

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

 



It was troublesome to iterate over peers and
perform sleepable calls. This will be necessary
for some upcomming changes to tx flushing.

Make peer allocation and initial setup
protected by both ar->conf_mutex and
ar->data_lock. This way it's possible to iterate
over peers with conf_mutex and call sleepable
functions.

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 5cfbf88..adb5da1 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