Skip to content

Commit

Permalink
mac80211: allow station add/remove to sleep
Browse files Browse the repository at this point in the history
Many drivers would like to sleep during station
addition and removal, and currently have a high
complexity there from not being able to.

This introduces two new callbacks sta_add() and
sta_remove() that drivers can implement instead
of using sta_notify() and that can sleep, and
the new sta_add() callback is also allowed to
fail.

The reason we didn't do this previously is that
the IBSS code wants to insert stations from the
RX path, which is a tasklet, so cannot sleep.
This patch will keep the station allocation in
that path, but moves adding the station to the
driver out of line. Since the addition can now
fail, we can have IBSS peer structs the driver
rejected -- in that case we still talk to the
station but never tell the driver about it in
the control.sta pointer. If there will ever be
a driver that has a low limit on the number of
stations and that cannot talk to any stations
that are not known to it, we need to do come up
with a new strategy of handling larger IBSSs,
maybe quicker expiry or rejecting peers.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Feb 8, 2010
1 parent 070bb54 commit 34e8950
Show file tree
Hide file tree
Showing 14 changed files with 526 additions and 470 deletions.
21 changes: 15 additions & 6 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ enum set_key_cmd {
* mac80211, any ieee80211_sta pointer you get access to must
* either be protected by rcu_read_lock() explicitly or implicitly,
* or you must take good care to not use such a pointer after a
* call to your sta_notify callback that removed it.
* call to your sta_remove callback that removed it.
*
* @addr: MAC address
* @aid: AID we assigned to the station if we're an AP
Expand All @@ -840,8 +840,8 @@ struct ieee80211_sta {
* indicates addition and removal of a station to station table,
* or if a associated station made a power state transition.
*
* @STA_NOTIFY_ADD: a station was added to the station table
* @STA_NOTIFY_REMOVE: a station being removed from the station table
* @STA_NOTIFY_ADD: (DEPRECATED) a station was added to the station table
* @STA_NOTIFY_REMOVE: (DEPRECATED) a station being removed from the station table
* @STA_NOTIFY_SLEEP: a station is now sleeping
* @STA_NOTIFY_AWAKE: a sleeping station woke up
*/
Expand Down Expand Up @@ -1534,9 +1534,14 @@ enum ieee80211_ampdu_mlme_action {
* @set_rts_threshold: Configuration of RTS threshold (if device needs it)
* The callback can sleep.
*
* @sta_notify: Notifies low level driver about addition, removal or power
* state transition of an associated station, AP, IBSS/WDS/mesh peer etc.
* Must be atomic.
* @sta_add: Notifies low level driver about addition of an associated station,
* AP, IBSS/WDS/mesh peer etc. This callback can sleep.
*
* @sta_remove: Notifies low level driver about removal of an associated
* station, AP, IBSS/WDS/mesh peer etc. This callback can sleep.
*
* @sta_notify: Notifies low level driver about power state transition of an
* associated station, AP, IBSS/WDS/mesh peer etc. Must be atomic.
*
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
Expand Down Expand Up @@ -1635,6 +1640,10 @@ struct ieee80211_ops {
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
u32 *iv32, u16 *iv16);
int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd, struct ieee80211_sta *sta);
int (*conf_tx)(struct ieee80211_hw *hw, u16 queue,
Expand Down
23 changes: 4 additions & 19 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -747,9 +747,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
sdata->vif.type == NL80211_IFTYPE_AP;

rcu_read_lock();

err = sta_info_insert(sta);
err = sta_info_insert_rcu(sta);
if (err) {
rcu_read_unlock();
return err;
Expand All @@ -768,26 +766,13 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;

sdata = IEEE80211_DEV_TO_SUB_IF(dev);

if (mac) {
rcu_read_lock();

sta = sta_info_get_bss(sdata, mac);
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}

sta_info_unlink(&sta);
rcu_read_unlock();

sta_info_destroy(sta);
} else
sta_info_flush(local, sdata);
if (mac)
return sta_info_destroy_addr_bss(sdata, mac);

sta_info_flush(local, sdata);
return 0;
}

Expand Down
34 changes: 34 additions & 0 deletions net/mac80211/driver-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,40 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
trace_drv_sta_notify(local, sdata, cmd, sta);
}

static inline int drv_sta_add(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta)
{
int ret = 0;

might_sleep();

if (local->ops->sta_add)
ret = local->ops->sta_add(&local->hw, &sdata->vif, sta);
else if (local->ops->sta_notify)
local->ops->sta_notify(&local->hw, &sdata->vif,
STA_NOTIFY_ADD, sta);

trace_drv_sta_add(local, sdata, sta, ret);

return ret;
}

static inline void drv_sta_remove(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta)
{
might_sleep();

if (local->ops->sta_remove)
local->ops->sta_remove(&local->hw, &sdata->vif, sta);
else if (local->ops->sta_notify)
local->ops->sta_notify(&local->hw, &sdata->vif,
STA_NOTIFY_REMOVE, sta);

trace_drv_sta_remove(local, sdata, sta);
}

static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
Expand Down
52 changes: 52 additions & 0 deletions net/mac80211/driver-trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,58 @@ TRACE_EVENT(drv_sta_notify,
)
);

TRACE_EVENT(drv_sta_add,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta, int ret),

TP_ARGS(local, sdata, sta, ret),

TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
STA_ENTRY
__field(int, ret)
),

TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
STA_ASSIGN;
__entry->ret = ret;
),

TP_printk(
LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ret:%d",
LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ret
)
);

TRACE_EVENT(drv_sta_remove,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta),

TP_ARGS(local, sdata, sta),

TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
STA_ENTRY
),

TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
STA_ASSIGN;
),

TP_printk(
LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT,
LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
)
);

TRACE_EVENT(drv_conf_tx,
TP_PROTO(struct ieee80211_local *local, u16 queue,
const struct ieee80211_tx_queue_params *params,
Expand Down
22 changes: 13 additions & 9 deletions net/mac80211/ibss.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
(unsigned long long) supp_rates,
(unsigned long long) sta->sta.supp_rates[band]);
#endif
} else
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);

rcu_read_unlock();
rcu_read_unlock();
} else {
rcu_read_unlock();
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL);
}
}

bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
Expand Down Expand Up @@ -368,7 +370,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, bss);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, GFP_KERNEL);
}

put_bss:
Expand All @@ -381,7 +384,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* 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)
u8 *bssid,u8 *addr, u32 supp_rates,
gfp_t gfp)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
Expand Down Expand Up @@ -410,7 +414,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
wiphy_name(local->hw.wiphy), addr, sdata->name);
#endif

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

Expand All @@ -422,9 +426,9 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,

rate_control_rate_init(sta);

/* If it fails, maybe we raced another insertion? */
if (sta_info_insert(sta))
return NULL;

return sta_info_get(sdata, addr);
return sta;
}

Expand Down
18 changes: 9 additions & 9 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -688,15 +688,18 @@ struct ieee80211_local {

/* Station data */
/*
* The lock only protects the list, hash, timer and counter
* against manipulation, reads are done in RCU. Additionally,
* the lock protects each BSS's TIM bitmap.
* The mutex only protects the list and counter,
* reads are done in RCU.
* Additionally, the lock protects the hash table,
* the pending list and each BSS's TIM bitmap.
*/
struct mutex sta_mtx;
spinlock_t sta_lock;
unsigned long num_sta;
struct list_head sta_list;
struct list_head sta_list, sta_pending_list;
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
struct work_struct sta_finish_work;
int sta_generation;

struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
Expand Down Expand Up @@ -770,10 +773,6 @@ struct ieee80211_local {
assoc_led_name[32], radio_led_name[32];
#endif

#ifdef CONFIG_MAC80211_DEBUGFS
struct work_struct sta_debugfs_add;
#endif

#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
/* TX/RX handler statistics */
unsigned int tx_handlers_drop;
Expand Down Expand Up @@ -985,7 +984,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
ieee80211_rx_result
ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates);
u8 *bssid, u8 *addr, u32 supp_rates,
gfp_t gfp);
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
17 changes: 9 additions & 8 deletions net/mac80211/mesh_plink.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
if (local->num_sta >= MESH_MAX_PLINKS)
return NULL;

sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC);
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;

Expand Down Expand Up @@ -236,12 +236,12 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data

sta = sta_info_get(sdata, hw_addr);
if (!sta) {
rcu_read_unlock();

sta = mesh_plink_alloc(sdata, hw_addr, rates);
if (!sta) {
rcu_read_unlock();
if (!sta)
return;
}
if (sta_info_insert(sta)) {
if (sta_info_insert_rcu(sta)) {
rcu_read_unlock();
return;
}
Expand Down Expand Up @@ -485,20 +485,21 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
} else if (!sta) {
/* ftype == PLINK_OPEN */
u32 rates;

rcu_read_unlock();

if (!mesh_plink_free_count(sdata)) {
mpl_dbg("Mesh plink error: no more free plinks\n");
rcu_read_unlock();
return;
}

rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
if (!sta) {
mpl_dbg("Mesh plink error: plink table full\n");
rcu_read_unlock();
return;
}
if (sta_info_insert(sta)) {
if (sta_info_insert_rcu(sta)) {
rcu_read_unlock();
return;
}
Expand Down
14 changes: 1 addition & 13 deletions net/mac80211/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -822,19 +822,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
changed |= BSS_CHANGED_BSSID;
ieee80211_bss_info_change_notify(sdata, changed);

rcu_read_lock();

sta = sta_info_get(sdata, bssid);
if (!sta) {
rcu_read_unlock();
return;
}

sta_info_unlink(&sta);

rcu_read_unlock();

sta_info_destroy(sta);
sta_info_destroy_addr(sdata, bssid);
}

void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
Expand Down
10 changes: 4 additions & 6 deletions net/mac80211/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
unsigned long flags;

ieee80211_scan_cancel(local);

Expand Down Expand Up @@ -55,22 +54,21 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
rcu_read_unlock();

/* remove STAs */
spin_lock_irqsave(&local->sta_lock, flags);
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
if (local->ops->sta_notify) {
if (sta->uploaded) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);

drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
&sta->sta);
drv_sta_remove(local, sdata, &sta->sta);
}

mesh_plink_quiesce(sta);
}
spin_unlock_irqrestore(&local->sta_lock, flags);
mutex_unlock(&local->sta_mtx);

/* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
Expand Down
Loading

0 comments on commit 34e8950

Please sign in to comment.