Skip to content

Commit

Permalink
mac80211: delay IBSS station insertion
Browse files Browse the repository at this point in the history
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: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Dec 15, 2011
1 parent 5654416 commit 8bf11d8
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 57 deletions.
154 changes: 122 additions & 32 deletions net/mac80211/ibss.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,80 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
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,
struct ieee80211_mgmt *mgmt,
size_t len,
Expand Down Expand Up @@ -334,10 +408,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
#endif
rates_updated = true;
}
} else
} else {
rcu_read_unlock();
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
mgmt->sa, supp_rates,
GFP_ATOMIC);
mgmt->sa, supp_rates);
}
}

if (sta && elems->wmm_info)
Expand Down Expand Up @@ -464,21 +539,17 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL);
supp_rates);
rcu_read_unlock();
}

put_bss:
ieee80211_rx_bss_put(local, bss);
}

/*
* Add a new IBSS station, will also be called by the RX code when,
* in IBSS mode, receiving a frame from a yet-unknown station, hence
* 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)
void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const u8 *addr,
u32 supp_rates)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
Expand All @@ -493,40 +564,29 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
if (net_ratelimit())
printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n",
sdata->name, addr);
return NULL;
return;
}

if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH)
return NULL;
return;

if (compare_ether_addr(bssid, sdata->u.ibss.bssid))
return NULL;

#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Adding new IBSS station %pM (dev=%s)\n",
addr, sdata->name);
#endif
return;

sta = sta_info_alloc(sdata, addr, gfp);
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
if (!sta)
return NULL;
return;

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 */
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(local, band);

rate_control_rate_init(sta);

/* If it fails, maybe we raced another insertion? */
if (sta_info_insert(sta))
return sta_info_get(sdata, addr);
return sta;
spin_lock(&ifibss->incomplete_lock);
list_add(&sta->list, &ifibss->incomplete_stations);
spin_unlock(&ifibss->incomplete_lock);
ieee80211_queue_work(&local->hw, &sdata->work);
}

static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
Expand Down Expand Up @@ -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)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct sta_info *sta;

mutex_lock(&ifibss->mtx);

Expand All @@ -876,6 +937,19 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
if (!ifibss->ssid_len)
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) {
case IEEE80211_IBSS_MLME_SEARCH:
ieee80211_sta_find_ibss(sdata);
Expand Down Expand Up @@ -934,6 +1008,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
setup_timer(&ifibss->timer, ieee80211_ibss_timer,
(unsigned long) sdata);
mutex_init(&ifibss->mtx);
INIT_LIST_HEAD(&ifibss->incomplete_stations);
spin_lock_init(&ifibss->incomplete_lock);
}

/* scan finished notification */
Expand Down Expand Up @@ -1053,6 +1129,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
struct cfg80211_bss *cbss;
u16 capability;
int active_ibss;
struct sta_info *sta;

mutex_lock(&sdata->u.ibss.mtx);

Expand Down Expand Up @@ -1081,6 +1158,19 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *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);

/* remove beacon */
Expand Down
8 changes: 5 additions & 3 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@ struct ieee80211_if_ibss {
struct sk_buff __rcu *presp;
struct sk_buff *skb;

spinlock_t incomplete_lock;
struct list_head incomplete_stations;

enum {
IEEE80211_IBSS_MLME_SEARCH,
IEEE80211_IBSS_MLME_JOINED,
Expand Down Expand Up @@ -1172,9 +1175,8 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates,
gfp_t gfp);
void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const u8 *addr, u32 supp_rates);
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params);
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
Expand Down
4 changes: 2 additions & 2 deletions net/mac80211/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -2775,8 +2775,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
rate_idx = 0; /* TODO: HT rates */
else
rate_idx = status->rate_idx;
rx->sta = ieee80211_ibss_add_sta(sdata, bssid,
hdr->addr2, BIT(rate_idx), GFP_ATOMIC);
ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2,
BIT(rate_idx));
}
break;
case NL80211_IFTYPE_MESH_POINT:
Expand Down
31 changes: 11 additions & 20 deletions net/mac80211/sta_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,35 +354,26 @@ static int sta_info_finish_insert(struct sta_info *sta,
/* notify driver */
err = drv_sta_add(local, sdata, &sta->sta);
if (err) {
if (!async)
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
return err;
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
"driver (%d) - keeping it anyway.\n",
sdata->name, sta->sta.addr, err);
} else {
} else
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;
}

if (!dummy_reinsert) {
if (!async) {
local->num_sta++;
local->sta_generation++;
smp_mb();

/* make the station visible */
spin_lock_irqsave(&local->sta_lock, flags);
sta_info_hash_add(local, sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
local->num_sta++;
local->sta_generation++;
smp_mb();

/* make the station visible */
spin_lock_irqsave(&local->sta_lock, flags);
sta_info_hash_add(local, sta);
spin_unlock_irqrestore(&local->sta_lock, flags);

list_add(&sta->list, &local->sta_list);
} else {
Expand Down Expand Up @@ -1546,7 +1537,7 @@ EXPORT_SYMBOL(ieee80211_sta_set_buffered);
int sta_info_move_state_checked(struct sta_info *sta,
enum ieee80211_sta_state new_state)
{
/* might_sleep(); -- for driver notify later, fix IBSS first */
might_sleep();

if (sta->sta_state == new_state)
return 0;
Expand Down

0 comments on commit 8bf11d8

Please sign in to comment.