[RFT 2/4] ath10k: rework peer accounting
Michal Kazior
michal.kazior at tieto.com
Fri Apr 4 07:37:42 EDT 2014
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 at tieto.com>
---
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
More information about the ath10k
mailing list