Skip to content

Commit

Permalink
mac80211: avoid using synchronize_rcu in ieee80211_set_probe_resp
Browse files Browse the repository at this point in the history
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@wizery.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Eyal Shapira authored and Johannes Berg committed Aug 20, 2012
1 parent f609a43 commit aa7a008
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 14 deletions.
16 changes: 7 additions & 9 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
8 changes: 7 additions & 1 deletion net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions net/mac80211/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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,
Expand Down
7 changes: 5 additions & 2 deletions net/mac80211/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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));

Expand Down

0 comments on commit aa7a008

Please sign in to comment.