Commit 8bf11d8d authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: delay IBSS station insertion

In order to notify drivers and simplify the station
management code, defer IBSS station insertion to a
work item and don't do it directly while receiving
a frame.

This increases the complexity in IBSS a little bit,
but it's pretty straight forward and it allows us
to reduce the station management complexity (next
patch) considerably.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 56544160
...@@ -275,6 +275,80 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ...@@ -275,6 +275,80 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
cbss->tsf); cbss->tsf);
} }
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
__acquires(RCU)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
u8 addr[ETH_ALEN];
memcpy(addr, sta->sta.addr, ETH_ALEN);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(sdata->local->hw.wiphy,
"Adding new IBSS station %pM (dev=%s)\n",
addr, sdata->name);
#endif
sta_info_move_state(sta, IEEE80211_STA_AUTH);
sta_info_move_state(sta, IEEE80211_STA_ASSOC);
sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
rate_control_rate_init(sta);
/* If it fails, maybe we raced another insertion? */
if (sta_info_insert_rcu(sta))
return sta_info_get(sdata, addr);
return sta;
}
static struct sta_info *
ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const u8 *addr,
u32 supp_rates)
__acquires(RCU)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int band = local->hw.conf.channel->band;
/*
* XXX: Consider removing the least recently used entry and
* allow new one to be added.
*/
if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n",
sdata->name, addr);
rcu_read_lock();
return NULL;
}
if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) {
rcu_read_lock();
return NULL;
}
if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) {
rcu_read_lock();
return NULL;
}
sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
if (!sta) {
rcu_read_lock();
return NULL;
}
sta->last_rx = jiffies;
/* make sure mandatory rates are always added */
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(local, band);
return ieee80211_ibss_finish_sta(sta);
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *mgmt,
size_t len, size_t len,
...@@ -334,10 +408,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -334,10 +408,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
#endif #endif
rates_updated = true; rates_updated = true;
} }
} else } else {
rcu_read_unlock();
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
mgmt->sa, supp_rates, mgmt->sa, supp_rates);
GFP_ATOMIC); }
} }
if (sta && elems->wmm_info) if (sta && elems->wmm_info)
...@@ -464,21 +539,17 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ...@@ -464,21 +539,17 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_join_ibss(sdata, bss); ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band); supp_rates = ieee80211_sta_get_rates(local, elems, band);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL); supp_rates);
rcu_read_unlock();
} }
put_bss: put_bss:
ieee80211_rx_bss_put(local, bss); ieee80211_rx_bss_put(local, bss);
} }
/* void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
* Add a new IBSS station, will also be called by the RX code when, const u8 *bssid, const u8 *addr,
* in IBSS mode, receiving a frame from a yet-unknown station, hence u32 supp_rates)
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates,
gfp_t gfp)
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
...@@ -493,40 +564,29 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, ...@@ -493,40 +564,29 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
if (net_ratelimit()) if (net_ratelimit())
printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n", printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n",
sdata->name, addr); sdata->name, addr);
return NULL; return;
} }
if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH)
return NULL; return;
if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) if (compare_ether_addr(bssid, sdata->u.ibss.bssid))
return NULL; return;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Adding new IBSS station %pM (dev=%s)\n",
addr, sdata->name);
#endif
sta = sta_info_alloc(sdata, addr, gfp); sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
if (!sta) if (!sta)
return NULL; return;
sta->last_rx = jiffies; sta->last_rx = jiffies;
sta_info_move_state(sta, IEEE80211_STA_AUTH);
sta_info_move_state(sta, IEEE80211_STA_ASSOC);
sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
/* make sure mandatory rates are always added */ /* make sure mandatory rates are always added */
sta->sta.supp_rates[band] = supp_rates | sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(local, band); ieee80211_mandatory_rates(local, band);
rate_control_rate_init(sta); spin_lock(&ifibss->incomplete_lock);
list_add(&sta->list, &ifibss->incomplete_stations);
/* If it fails, maybe we raced another insertion? */ spin_unlock(&ifibss->incomplete_lock);
if (sta_info_insert(sta)) ieee80211_queue_work(&local->hw, &sdata->work);
return sta_info_get(sdata, addr);
return sta;
} }
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
...@@ -865,6 +925,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ...@@ -865,6 +925,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct sta_info *sta;
mutex_lock(&ifibss->mtx); mutex_lock(&ifibss->mtx);
...@@ -876,6 +937,19 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) ...@@ -876,6 +937,19 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
if (!ifibss->ssid_len) if (!ifibss->ssid_len)
goto out; goto out;
spin_lock_bh(&ifibss->incomplete_lock);
while (!list_empty(&ifibss->incomplete_stations)) {
sta = list_first_entry(&ifibss->incomplete_stations,
struct sta_info, list);
list_del(&sta->list);
spin_unlock_bh(&ifibss->incomplete_lock);
ieee80211_ibss_finish_sta(sta);
rcu_read_unlock();
spin_lock_bh(&ifibss->incomplete_lock);
}
spin_unlock_bh(&ifibss->incomplete_lock);
switch (ifibss->state) { switch (ifibss->state) {
case IEEE80211_IBSS_MLME_SEARCH: case IEEE80211_IBSS_MLME_SEARCH:
ieee80211_sta_find_ibss(sdata); ieee80211_sta_find_ibss(sdata);
...@@ -934,6 +1008,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) ...@@ -934,6 +1008,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
setup_timer(&ifibss->timer, ieee80211_ibss_timer, setup_timer(&ifibss->timer, ieee80211_ibss_timer,
(unsigned long) sdata); (unsigned long) sdata);
mutex_init(&ifibss->mtx); mutex_init(&ifibss->mtx);
INIT_LIST_HEAD(&ifibss->incomplete_stations);
spin_lock_init(&ifibss->incomplete_lock);
} }
/* scan finished notification */ /* scan finished notification */
...@@ -1053,6 +1129,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) ...@@ -1053,6 +1129,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
struct cfg80211_bss *cbss; struct cfg80211_bss *cbss;
u16 capability; u16 capability;
int active_ibss; int active_ibss;
struct sta_info *sta;
mutex_lock(&sdata->u.ibss.mtx); mutex_lock(&sdata->u.ibss.mtx);
...@@ -1081,6 +1158,19 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) ...@@ -1081,6 +1158,19 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
} }
sta_info_flush(sdata->local, sdata); sta_info_flush(sdata->local, sdata);
spin_lock_bh(&ifibss->incomplete_lock);
while (!list_empty(&ifibss->incomplete_stations)) {
sta = list_first_entry(&ifibss->incomplete_stations,
struct sta_info, list);
list_del(&sta->list);
spin_unlock_bh(&ifibss->incomplete_lock);
sta_info_free(local, sta);
spin_lock_bh(&ifibss->incomplete_lock);
}
spin_unlock_bh(&ifibss->incomplete_lock);
netif_carrier_off(sdata->dev); netif_carrier_off(sdata->dev);
/* remove beacon */ /* remove beacon */
......
...@@ -482,6 +482,9 @@ struct ieee80211_if_ibss { ...@@ -482,6 +482,9 @@ struct ieee80211_if_ibss {
struct sk_buff __rcu *presp; struct sk_buff __rcu *presp;
struct sk_buff *skb; struct sk_buff *skb;
spinlock_t incomplete_lock;
struct list_head incomplete_stations;
enum { enum {
IEEE80211_IBSS_MLME_SEARCH, IEEE80211_IBSS_MLME_SEARCH,
IEEE80211_IBSS_MLME_JOINED, IEEE80211_IBSS_MLME_JOINED,
...@@ -1172,9 +1175,8 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); ...@@ -1172,9 +1175,8 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
/* IBSS code */ /* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates, const u8 *bssid, const u8 *addr, u32 supp_rates);
gfp_t gfp);
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params); struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
......
...@@ -2775,8 +2775,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, ...@@ -2775,8 +2775,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
rate_idx = 0; /* TODO: HT rates */ rate_idx = 0; /* TODO: HT rates */
else else
rate_idx = status->rate_idx; rate_idx = status->rate_idx;
rx->sta = ieee80211_ibss_add_sta(sdata, bssid, ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2,
hdr->addr2, BIT(rate_idx), GFP_ATOMIC); BIT(rate_idx));
} }
break; break;
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
......
...@@ -354,35 +354,26 @@ static int sta_info_finish_insert(struct sta_info *sta, ...@@ -354,35 +354,26 @@ static int sta_info_finish_insert(struct sta_info *sta,
/* notify driver */ /* notify driver */
err = drv_sta_add(local, sdata, &sta->sta); err = drv_sta_add(local, sdata, &sta->sta);
if (err) { if (err) {
if (!async) if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
return err; return err;
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
"driver (%d) - keeping it anyway.\n", "driver (%d) - keeping it anyway.\n",
sdata->name, sta->sta.addr, err); sdata->name, sta->sta.addr, err);
} else { } else
sta->uploaded = true; sta->uploaded = true;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (async)
wiphy_debug(local->hw.wiphy,
"Finished adding IBSS STA %pM\n",
sta->sta.addr);
#endif
}
sdata = sta->sdata; sdata = sta->sdata;
} }
if (!dummy_reinsert) { if (!dummy_reinsert) {
if (!async) { local->num_sta++;
local->num_sta++; local->sta_generation++;
local->sta_generation++; smp_mb();
smp_mb();
/* make the station visible */
/* make the station visible */ spin_lock_irqsave(&local->sta_lock, flags);
spin_lock_irqsave(&local->sta_lock, flags); sta_info_hash_add(local, sta);
sta_info_hash_add(local, sta); spin_unlock_irqrestore(&local->sta_lock, flags);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
list_add(&sta->list, &local->sta_list); list_add(&sta->list, &local->sta_list);
} else { } else {
...@@ -1546,7 +1537,7 @@ EXPORT_SYMBOL(ieee80211_sta_set_buffered); ...@@ -1546,7 +1537,7 @@ EXPORT_SYMBOL(ieee80211_sta_set_buffered);
int sta_info_move_state_checked(struct sta_info *sta, int sta_info_move_state_checked(struct sta_info *sta,
enum ieee80211_sta_state new_state) enum ieee80211_sta_state new_state)
{ {
/* might_sleep(); -- for driver notify later, fix IBSS first */ might_sleep();
if (sta->sta_state == new_state) if (sta->sta_state == new_state)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment