mac80211: avoid using synchronize_rcu in ieee80211_set_probe_resp

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Sat Sep 29 10:59:40 EDT 2012


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=aa7a00809cf6afe3cd6f5af2889110b47b798667
Commit:     aa7a00809cf6afe3cd6f5af2889110b47b798667
Parent:     f609a43dca2964a8a604ef554be92fa11c3b4c41
Author:     Eyal Shapira <eyal at wizery.com>
AuthorDate: Mon Aug 6 14:26:16 2012 +0300
Committer:  Johannes Berg <johannes.berg at intel.com>
CommitDate: Mon Aug 20 13:20:56 2012 +0200

    mac80211: avoid using synchronize_rcu in ieee80211_set_probe_resp
    
    This could take a while (100ms+) and may delay sending assoc resp
    in AP mode with WPS or P2P GO (as setting the probe resp takes place
    there). We've encountered situations where the delay was big enough
    to cause connection problems with devices like Galaxy Nexus.
    Switch to using call_rcu with a free handler.
    
    [Arik - rework to use plain buffer and instead of skb]
    
    Signed-off-by: Eyal Shapira <eyal at wizery.com>
    Signed-off-by: Arik Nemtsov <arik at wizery.com>
    Signed-off-by: Johannes Berg <johannes.berg at intel.com>
---
 net/mac80211/cfg.c         |   16 +++++++---------
 net/mac80211/ieee80211_i.h |    8 +++++++-
 net/mac80211/iface.c       |    4 ++--
 net/mac80211/tx.c          |    7 +++++--
 4 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index df64b45..8052a7a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -725,25 +725,23 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
 				    const u8 *resp, size_t resp_len)
 {
-	struct sk_buff *new, *old;
+	struct probe_resp *new, *old;
 
 	if (!resp || !resp_len)
-		return 1;
+		return -EINVAL;
 
 	old = rtnl_dereference(sdata->u.ap.probe_resp);
 
-	new = dev_alloc_skb(resp_len);
+	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
 	if (!new)
 		return -ENOMEM;
 
-	memcpy(skb_put(new, resp_len), resp, resp_len);
+	new->len = resp_len;
+	memcpy(new->data, resp, resp_len);
 
 	rcu_assign_pointer(sdata->u.ap.probe_resp, new);
-	if (old) {
-		/* TODO: use call_rcu() */
-		synchronize_rcu();
-		dev_kfree_skb(old);
-	}
+	if (old)
+		kfree_rcu(old, rcu_head);
 
 	return 0;
 }
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d1a7c58..e22aee8 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -272,9 +272,15 @@ struct beacon_data {
 	struct rcu_head rcu_head;
 };
 
+struct probe_resp {
+	struct rcu_head rcu_head;
+	int len;
+	u8 data[0];
+};
+
 struct ieee80211_if_ap {
 	struct beacon_data __rcu *beacon;
-	struct sk_buff __rcu *probe_resp;
+	struct probe_resp __rcu *probe_resp;
 
 	struct list_head vlans;
 
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index abee3a0..fbab7a8 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -715,7 +715,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 		struct ieee80211_sub_if_data *vlan, *tmpsdata;
 		struct beacon_data *old_beacon =
 			rtnl_dereference(sdata->u.ap.beacon);
-		struct sk_buff *old_probe_resp =
+		struct probe_resp *old_probe_resp =
 			rtnl_dereference(sdata->u.ap.probe_resp);
 
 		/* sdata_running will return false, so this will disable */
@@ -727,7 +727,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 		RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
 		synchronize_rcu();
 		kfree(old_beacon);
-		kfree_skb(old_probe_resp);
+		kfree(old_probe_resp);
 
 		/* down all dependent devices, that is VLANs */
 		list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 7dbcf29..2d004ba 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2481,7 +2481,8 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
 					struct ieee80211_vif *vif)
 {
 	struct ieee80211_if_ap *ap = NULL;
-	struct sk_buff *presp = NULL, *skb = NULL;
+	struct sk_buff *skb = NULL;
+	struct probe_resp *presp = NULL;
 	struct ieee80211_hdr *hdr;
 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
@@ -2495,10 +2496,12 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
 	if (!presp)
 		goto out;
 
-	skb = skb_copy(presp, GFP_ATOMIC);
+	skb = dev_alloc_skb(presp->len);
 	if (!skb)
 		goto out;
 
+	memcpy(skb_put(skb, presp->len), presp->data, presp->len);
+
 	hdr = (struct ieee80211_hdr *) skb->data;
 	memset(hdr->addr1, 0, sizeof(hdr->addr1));
 



More information about the linux-mtd-cvs mailing list