mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
Merge branch 'master' of ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
commit
f6f223039c
79 changed files with 1458 additions and 1088 deletions
|
|
@ -237,6 +237,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
|
|||
sdata->vif.type != NL80211_IFTYPE_AP)
|
||||
return -EINVAL;
|
||||
|
||||
if (test_sta_flags(sta, WLAN_STA_DISASSOC)) {
|
||||
#ifdef CONFIG_MAC80211_HT_DEBUG
|
||||
printk(KERN_DEBUG "Disassociation is in progress. "
|
||||
"Denying BA session request\n");
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
|
||||
#ifdef CONFIG_MAC80211_HT_DEBUG
|
||||
printk(KERN_DEBUG "Suspend in progress. "
|
||||
|
|
|
|||
|
|
@ -515,6 +515,8 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
if (old)
|
||||
memcpy(new->tail, old->tail, new_tail_len);
|
||||
|
||||
sdata->vif.bss_conf.dtim_period = new->dtim_period;
|
||||
|
||||
rcu_assign_pointer(sdata->u.ap.beacon, new);
|
||||
|
||||
synchronize_rcu();
|
||||
|
|
@ -747,9 +749,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;
|
||||
|
|
@ -768,26 +768,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -250,6 +250,38 @@ static const struct file_operations uapsd_max_sp_len_ops = {
|
|||
.open = mac80211_open_file_generic
|
||||
};
|
||||
|
||||
static ssize_t channel_type_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ieee80211_local *local = file->private_data;
|
||||
const char *buf;
|
||||
|
||||
switch (local->hw.conf.channel_type) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
buf = "no ht\n";
|
||||
break;
|
||||
case NL80211_CHAN_HT20:
|
||||
buf = "ht20\n";
|
||||
break;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
buf = "ht40-\n";
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
buf = "ht40+\n";
|
||||
break;
|
||||
default:
|
||||
buf = "???";
|
||||
break;
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static const struct file_operations channel_type_ops = {
|
||||
.read = channel_type_read,
|
||||
.open = mac80211_open_file_generic
|
||||
};
|
||||
|
||||
static ssize_t queues_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
|
|
@ -408,6 +440,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
|
|||
DEBUGFS_ADD(noack);
|
||||
DEBUGFS_ADD(uapsd_queues);
|
||||
DEBUGFS_ADD(uapsd_max_sp_len);
|
||||
DEBUGFS_ADD(channel_type);
|
||||
|
||||
statsd = debugfs_create_dir("statistics", phyd);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -256,14 +290,6 @@ static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_get_tx_stats(struct ieee80211_local *local,
|
||||
struct ieee80211_tx_queue_stats *stats)
|
||||
{
|
||||
int ret = local->ops->get_tx_stats(&local->hw, stats);
|
||||
trace_drv_get_tx_stats(local, stats, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u64 drv_get_tsf(struct ieee80211_local *local)
|
||||
{
|
||||
u64 ret = -1ULL;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -578,29 +630,6 @@ TRACE_EVENT(drv_conf_tx,
|
|||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_get_tx_stats,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_tx_queue_stats *stats,
|
||||
int ret),
|
||||
|
||||
TP_ARGS(local, stats, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT " ret:%d",
|
||||
LOCAL_PR_ARG, __entry->ret
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_get_tsf,
|
||||
TP_PROTO(struct ieee80211_local *local, u64 ret),
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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:
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -652,7 +656,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
if (pos[1] != 0 &&
|
||||
(pos[1] != ifibss->ssid_len ||
|
||||
!memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
|
||||
memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
|
||||
/* Ignore ProbeReq for foreign SSID */
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@ enum ieee80211_sta_flags {
|
|||
IEEE80211_STA_CSA_RECEIVED = BIT(5),
|
||||
IEEE80211_STA_MFP_ENABLED = BIT(6),
|
||||
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
|
||||
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
|
||||
};
|
||||
|
||||
struct ieee80211_if_managed {
|
||||
|
|
@ -688,15 +689,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];
|
||||
|
|
@ -770,10 +774,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;
|
||||
|
|
@ -985,7 +985,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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -485,9 +485,11 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -495,10 +497,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@
|
|||
#include "rate.h"
|
||||
#include "led.h"
|
||||
|
||||
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
|
||||
#define IEEE80211_AUTH_MAX_TRIES 3
|
||||
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
|
||||
#define IEEE80211_ASSOC_MAX_TRIES 3
|
||||
#define IEEE80211_MAX_PROBE_TRIES 5
|
||||
|
||||
/*
|
||||
|
|
@ -438,8 +434,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
|
|||
} else {
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
conf->flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
|
||||
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
|
||||
conf->flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -545,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
|||
container_of(work, struct ieee80211_local,
|
||||
dynamic_ps_enable_work);
|
||||
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
|
||||
/* can only happen when PS was just disabled anyway */
|
||||
if (!sdata)
|
||||
|
|
@ -553,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
|||
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
||||
return;
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
|
||||
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
|
||||
local->hw.conf.flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
|
||||
(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
|
||||
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
||||
local->hw.conf.flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_timer(unsigned long data)
|
||||
|
|
@ -792,8 +797,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, bssid);
|
||||
if (sta)
|
||||
if (sta) {
|
||||
set_sta_flags(sta, WLAN_STA_DISASSOC);
|
||||
ieee80211_sta_tear_down_BA_sessions(sta);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
changed |= ieee80211_reset_erp_info(sdata);
|
||||
|
|
@ -826,19 +833,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,
|
||||
|
|
@ -1844,7 +1839,11 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
|||
wk->probe_auth.algorithm = auth_alg;
|
||||
wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
wk->type = IEEE80211_WORK_DIRECT_PROBE;
|
||||
/* if we already have a probe, don't probe again */
|
||||
if (req->bss->proberesp_ies)
|
||||
wk->type = IEEE80211_WORK_AUTH;
|
||||
else
|
||||
wk->type = IEEE80211_WORK_DIRECT_PROBE;
|
||||
wk->chan = req->bss->channel;
|
||||
wk->sdata = sdata;
|
||||
wk->done = ieee80211_probe_auth_done;
|
||||
|
|
@ -1904,6 +1903,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|||
return -ENOMEM;
|
||||
|
||||
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
|
||||
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
||||
|
||||
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
|
||||
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
|
||||
|
|
@ -2007,12 +2007,18 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
mutex_lock(&local->work_mtx);
|
||||
list_for_each_entry(wk, &local->work_list, list) {
|
||||
if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
|
||||
if (wk->sdata != sdata)
|
||||
continue;
|
||||
|
||||
if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
|
||||
wk->type != IEEE80211_WORK_AUTH)
|
||||
continue;
|
||||
|
||||
if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
|
||||
continue;
|
||||
not_auth_yet = true;
|
||||
list_del(&wk->list);
|
||||
|
||||
not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE;
|
||||
list_del_rcu(&wk->list);
|
||||
free_work(wk);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ static const struct file_operations rcname_ops = {
|
|||
};
|
||||
#endif
|
||||
|
||||
struct rate_control_ref *rate_control_alloc(const char *name,
|
||||
static struct rate_control_ref *rate_control_alloc(const char *name,
|
||||
struct ieee80211_local *local)
|
||||
{
|
||||
struct dentry *debugfsdir = NULL;
|
||||
|
|
|
|||
|
|
@ -26,10 +26,6 @@ struct rate_control_ref {
|
|||
struct kref kref;
|
||||
};
|
||||
|
||||
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
|
||||
* first available algorithm. */
|
||||
struct rate_control_ref *rate_control_alloc(const char *name,
|
||||
struct ieee80211_local *local);
|
||||
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_tx_rate_control *txrc);
|
||||
|
|
@ -116,7 +112,8 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* functions for rate control related to a device */
|
||||
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
|
||||
* first available algorithm. */
|
||||
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
|
||||
const char *name);
|
||||
void rate_control_deinitialize(struct ieee80211_local *local);
|
||||
|
|
|
|||
|
|
@ -1719,6 +1719,7 @@ static ieee80211_rx_result debug_noinline
|
|||
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = rx->sdata;
|
||||
struct ieee80211_local *local = rx->local;
|
||||
struct net_device *dev = sdata->dev;
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
__le16 fc = hdr->frame_control;
|
||||
|
|
@ -1750,6 +1751,13 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
|
|||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += rx->skb->len;
|
||||
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
!is_multicast_ether_addr(hdr->addr1) &&
|
||||
local->hw.conf.dynamic_ps_timeout > 0 && local->ps_sdata) {
|
||||
mod_timer(&local->dynamic_ps_timer, jiffies +
|
||||
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
|
||||
}
|
||||
|
||||
ieee80211_deliver_skb(rx);
|
||||
|
||||
return RX_QUEUED;
|
||||
|
|
@ -2244,8 +2252,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
|
|||
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));
|
||||
rx->sta = ieee80211_ibss_add_sta(sdata, bssid,
|
||||
hdr->addr2, BIT(rate_idx), GFP_ATOMIC);
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
|
|
|
|||
|
|
@ -345,6 +345,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|||
if (local->scan_req)
|
||||
return -EBUSY;
|
||||
|
||||
if (!list_empty(&local->work_list)) {
|
||||
/* wait for the work to finish/time out */
|
||||
local->scan_req = req;
|
||||
local->scan_sdata = sdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->ops->hw_scan) {
|
||||
u8 *ies;
|
||||
|
||||
|
|
@ -364,29 +371,33 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|||
local->hw_scan_req->ie = ies;
|
||||
|
||||
local->hw_scan_band = 0;
|
||||
|
||||
/*
|
||||
* After allocating local->hw_scan_req, we must
|
||||
* go through until ieee80211_prep_hw_scan(), so
|
||||
* anything that might be changed here and leave
|
||||
* this function early must not go after this
|
||||
* allocation.
|
||||
*/
|
||||
}
|
||||
|
||||
local->scan_req = req;
|
||||
local->scan_sdata = sdata;
|
||||
|
||||
if (!list_empty(&local->work_list)) {
|
||||
/* wait for the work to finish/time out */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->ops->hw_scan)
|
||||
__set_bit(SCAN_HW_SCANNING, &local->scanning);
|
||||
else
|
||||
__set_bit(SCAN_SW_SCANNING, &local->scanning);
|
||||
|
||||
/*
|
||||
* Kicking off the scan need not be protected,
|
||||
* only the scan variable stuff, since now
|
||||
* local->scan_req is assigned and other callers
|
||||
* will abort their scan attempts.
|
||||
*
|
||||
* This avoids getting a scan_mtx -> iflist_mtx
|
||||
* dependency, so that the scan completed calls
|
||||
* have more locking freedom.
|
||||
* This avoids too many locking dependencies
|
||||
* so that the scan completed calls have more
|
||||
* locking freedom.
|
||||
*/
|
||||
|
||||
ieee80211_recalc_idle(local);
|
||||
|
|
|
|||
|
|
@ -32,49 +32,33 @@
|
|||
* for faster lookup and a list for iteration. They are managed using
|
||||
* RCU, i.e. access to the list and hash table is protected by RCU.
|
||||
*
|
||||
* Upon allocating a STA info structure with sta_info_alloc(), the caller owns
|
||||
* that structure. It must then either destroy it using sta_info_destroy()
|
||||
* (which is pretty useless) or insert it into the hash table using
|
||||
* sta_info_insert() which demotes the reference from ownership to a regular
|
||||
* RCU-protected reference; if the function is called without protection by an
|
||||
* RCU critical section the reference is instantly invalidated. Note that the
|
||||
* caller may not do much with the STA info before inserting it, in particular,
|
||||
* it may not start any mesh peer link management or add encryption keys.
|
||||
* Upon allocating a STA info structure with sta_info_alloc(), the caller
|
||||
* owns that structure. It must then insert it into the hash table using
|
||||
* either sta_info_insert() or sta_info_insert_rcu(); only in the latter
|
||||
* case (which acquires an rcu read section but must not be called from
|
||||
* within one) will the pointer still be valid after the call. Note that
|
||||
* the caller may not do much with the STA info before inserting it, in
|
||||
* particular, it may not start any mesh peer link management or add
|
||||
* encryption keys.
|
||||
*
|
||||
* When the insertion fails (sta_info_insert()) returns non-zero), the
|
||||
* structure will have been freed by sta_info_insert()!
|
||||
*
|
||||
* sta entries are added by mac80211 when you establish a link with a
|
||||
* Station entries are added by mac80211 when you establish a link with a
|
||||
* peer. This means different things for the different type of interfaces
|
||||
* we support. For a regular station this mean we add the AP sta when we
|
||||
* receive an assocation response from the AP. For IBSS this occurs when
|
||||
* we receive a probe response or a beacon from target IBSS network. For
|
||||
* WDS we add the sta for the peer imediately upon device open. When using
|
||||
* AP mode we add stations for each respective station upon request from
|
||||
* userspace through nl80211.
|
||||
* get to know about a peer on the same IBSS. For WDS we add the sta for
|
||||
* the peer imediately upon device open. When using AP mode we add stations
|
||||
* for each respective station upon request from userspace through nl80211.
|
||||
*
|
||||
* Because there are debugfs entries for each station, and adding those
|
||||
* must be able to sleep, it is also possible to "pin" a station entry,
|
||||
* that means it can be removed from the hash table but not be freed.
|
||||
* See the comment in __sta_info_unlink() for more information, this is
|
||||
* an internal capability only.
|
||||
* In order to remove a STA info structure, various sta_info_destroy_*()
|
||||
* calls are available.
|
||||
*
|
||||
* In order to remove a STA info structure, the caller needs to first
|
||||
* unlink it (sta_info_unlink()) from the list and hash tables and
|
||||
* then destroy it; sta_info_destroy() will wait for an RCU grace period
|
||||
* to elapse before actually freeing it. Due to the pinning and the
|
||||
* possibility of multiple callers trying to remove the same STA info at
|
||||
* the same time, sta_info_unlink() can clear the STA info pointer it is
|
||||
* passed to indicate that the STA info is owned by somebody else now.
|
||||
*
|
||||
* If sta_info_unlink() did not clear the pointer then the caller owns
|
||||
* the STA info structure now and is responsible of destroying it with
|
||||
* a call to sta_info_destroy().
|
||||
*
|
||||
* In all other cases, there is no concept of ownership on a STA entry,
|
||||
* each structure is owned by the global hash table/list until it is
|
||||
* removed. All users of the structure need to be RCU protected so that
|
||||
* the structure won't be freed before they are done using it.
|
||||
* There is no concept of ownership on a STA entry, each structure is
|
||||
* owned by the global hash table/list until it is removed. All users of
|
||||
* the structure need to be RCU protected so that the structure won't be
|
||||
* freed before they are done using it.
|
||||
*/
|
||||
|
||||
/* Caller must hold local->sta_lock */
|
||||
|
|
@ -185,101 +169,6 @@ static void __sta_info_free(struct ieee80211_local *local,
|
|||
kfree(sta);
|
||||
}
|
||||
|
||||
void sta_info_destroy(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_local *local;
|
||||
struct sk_buff *skb;
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
local = sta->local;
|
||||
|
||||
cancel_work_sync(&sta->drv_unblock_wk);
|
||||
|
||||
rate_control_remove_sta_debugfs(sta);
|
||||
ieee80211_sta_debugfs_remove(sta);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
|
||||
mesh_plink_deactivate(sta);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We have only unlinked the key, and actually destroying it
|
||||
* may mean it is removed from hardware which requires that
|
||||
* the key->sta pointer is still valid, so flush the key todo
|
||||
* list here.
|
||||
*
|
||||
* ieee80211_key_todo() will synchronize_rcu() so after this
|
||||
* nothing can reference this sta struct any more.
|
||||
*/
|
||||
ieee80211_key_todo();
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sta->sdata->vif))
|
||||
del_timer_sync(&sta->plink_timer);
|
||||
#endif
|
||||
|
||||
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
|
||||
local->total_ps_buffered--;
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
for (i = 0; i < STA_TID_NUM; i++) {
|
||||
struct tid_ampdu_rx *tid_rx;
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
tid_rx = sta->ampdu_mlme.tid_rx[i];
|
||||
/* Make sure timer won't free the tid_rx struct, see below */
|
||||
if (tid_rx)
|
||||
tid_rx->shutdown = true;
|
||||
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
||||
/*
|
||||
* Outside spinlock - shutdown is true now so that the timer
|
||||
* won't free tid_rx, we have to do that now. Can't let the
|
||||
* timer do it because we have to sync the timer outside the
|
||||
* lock that it takes itself.
|
||||
*/
|
||||
if (tid_rx) {
|
||||
del_timer_sync(&tid_rx->session_timer);
|
||||
kfree(tid_rx);
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to do such complications for TX agg sessions, the
|
||||
* path leading to freeing the tid_tx struct goes via a call
|
||||
* from the driver, and thus needs to look up the sta struct
|
||||
* again, which cannot be found when we get here. Hence, we
|
||||
* just need to delete the timer and free the aggregation
|
||||
* info; we won't be telling the peer about it then but that
|
||||
* doesn't matter if we're not talking to it again anyway.
|
||||
*/
|
||||
tid_tx = sta->ampdu_mlme.tid_tx[i];
|
||||
if (tid_tx) {
|
||||
del_timer_sync(&tid_tx->addba_resp_timer);
|
||||
/*
|
||||
* STA removed while aggregation session being
|
||||
* started? Bit odd, but purge frames anyway.
|
||||
*/
|
||||
skb_queue_purge(&tid_tx->pending);
|
||||
kfree(tid_tx);
|
||||
}
|
||||
}
|
||||
|
||||
__sta_info_free(local, sta);
|
||||
}
|
||||
|
||||
|
||||
/* Caller must hold local->sta_lock */
|
||||
static void sta_info_hash_add(struct ieee80211_local *local,
|
||||
struct sta_info *sta)
|
||||
|
|
@ -376,7 +265,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
return sta;
|
||||
}
|
||||
|
||||
int sta_info_insert(struct sta_info *sta)
|
||||
static int sta_info_finish_insert(struct sta_info *sta, bool async)
|
||||
{
|
||||
struct ieee80211_local *local = sta->local;
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
|
@ -384,6 +273,91 @@ int sta_info_insert(struct sta_info *sta)
|
|||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&local->sta_mtx));
|
||||
|
||||
/* notify driver */
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
sdata = container_of(sdata->bss,
|
||||
struct ieee80211_sub_if_data,
|
||||
u.ap);
|
||||
err = drv_sta_add(local, sdata, &sta->sta);
|
||||
if (err) {
|
||||
if (!async)
|
||||
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 {
|
||||
sta->uploaded = true;
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
if (async)
|
||||
printk(KERN_DEBUG "%s: Finished adding IBSS STA %pM\n",
|
||||
wiphy_name(local->hw.wiphy), sta->sta.addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
sdata = sta->sdata;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
list_add(&sta->list, &local->sta_list);
|
||||
|
||||
ieee80211_sta_debugfs_add(sta);
|
||||
rate_control_add_sta_debugfs(sta);
|
||||
|
||||
sinfo.filled = 0;
|
||||
sinfo.generation = local->sta_generation;
|
||||
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sta_info_finish_pending(struct ieee80211_local *local)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
while (!list_empty(&local->sta_pending_list)) {
|
||||
sta = list_first_entry(&local->sta_pending_list,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
|
||||
sta_info_finish_insert(sta, true);
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
|
||||
static void sta_info_finish_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, sta_finish_work);
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta_info_finish_pending(local);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
}
|
||||
|
||||
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
|
||||
{
|
||||
struct ieee80211_local *local = sta->local;
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* Can't be a WARN_ON because it can be triggered through a race:
|
||||
* something inserts a STA (on one CPU) without holding the RTNL
|
||||
|
|
@ -391,36 +365,87 @@ int sta_info_insert(struct sta_info *sta)
|
|||
*/
|
||||
if (unlikely(!ieee80211_sdata_running(sdata))) {
|
||||
err = -ENETDOWN;
|
||||
rcu_read_lock();
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
|
||||
is_multicast_ether_addr(sta->sta.addr))) {
|
||||
err = -EINVAL;
|
||||
rcu_read_lock();
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* In ad-hoc mode, we sometimes need to insert stations
|
||||
* from tasklet context from the RX path. To avoid races,
|
||||
* always do so in that case -- see the comment below.
|
||||
*/
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
/* check if STA exists already */
|
||||
if (sta_info_get_bss(sdata, sta->sta.addr)) {
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
rcu_read_lock();
|
||||
err = -EEXIST;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
local->num_sta++;
|
||||
local->sta_generation++;
|
||||
smp_mb();
|
||||
sta_info_hash_add(local, sta);
|
||||
|
||||
list_add_tail(&sta->list, &local->sta_pending_list);
|
||||
|
||||
rcu_read_lock();
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
printk(KERN_DEBUG "%s: Added IBSS STA %pM\n",
|
||||
wiphy_name(local->hw.wiphy), sta->sta.addr);
|
||||
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
||||
|
||||
ieee80211_queue_work(&local->hw, &local->sta_finish_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* On first glance, this will look racy, because the code
|
||||
* below this point, which inserts a station with sleeping,
|
||||
* unlocks the sta_lock between checking existence in the
|
||||
* hash table and inserting into it.
|
||||
*
|
||||
* However, it is not racy against itself because it keeps
|
||||
* the mutex locked. It still seems to race against the
|
||||
* above code that atomically inserts the station... That,
|
||||
* however, is not true because the above code can only
|
||||
* be invoked for IBSS interfaces, and the below code will
|
||||
* not be -- and the two do not race against each other as
|
||||
* the hash table also keys off the interface.
|
||||
*/
|
||||
|
||||
might_sleep();
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
/* check if STA exists already */
|
||||
if (sta_info_get(sdata, sta->sta.addr)) {
|
||||
if (sta_info_get_bss(sdata, sta->sta.addr)) {
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
rcu_read_lock();
|
||||
err = -EEXIST;
|
||||
goto out_free;
|
||||
}
|
||||
list_add(&sta->list, &local->sta_list);
|
||||
local->sta_generation++;
|
||||
local->num_sta++;
|
||||
sta_info_hash_add(local, sta);
|
||||
|
||||
/* notify driver */
|
||||
if (local->ops->sta_notify) {
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
sdata = container_of(sdata->bss,
|
||||
struct ieee80211_sub_if_data,
|
||||
u.ap);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
|
||||
drv_sta_notify(local, sdata, STA_NOTIFY_ADD, &sta->sta);
|
||||
sdata = sta->sdata;
|
||||
err = sta_info_finish_insert(sta, false);
|
||||
if (err) {
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
rcu_read_lock();
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
|
|
@ -428,22 +453,9 @@ int sta_info_insert(struct sta_info *sta)
|
|||
wiphy_name(local->hw.wiphy), sta->sta.addr);
|
||||
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
||||
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
|
||||
sinfo.filled = 0;
|
||||
sinfo.generation = local->sta_generation;
|
||||
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_ATOMIC);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/*
|
||||
* Debugfs entry adding might sleep, so schedule process
|
||||
* context task for adding entry for STAs that do not yet
|
||||
* have one.
|
||||
* NOTE: due to auto-freeing semantics this may only be done
|
||||
* if the insertion is successful!
|
||||
*/
|
||||
schedule_work(&local->sta_debugfs_add);
|
||||
#endif
|
||||
/* move reference to rcu-protected */
|
||||
rcu_read_lock();
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
mesh_accept_plinks_update(sdata);
|
||||
|
|
@ -455,6 +467,15 @@ int sta_info_insert(struct sta_info *sta)
|
|||
return err;
|
||||
}
|
||||
|
||||
int sta_info_insert(struct sta_info *sta)
|
||||
{
|
||||
int err = sta_info_insert_rcu(sta);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
|
||||
{
|
||||
/*
|
||||
|
|
@ -523,108 +544,6 @@ void sta_info_clear_tim_bit(struct sta_info *sta)
|
|||
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
|
||||
}
|
||||
|
||||
static void __sta_info_unlink(struct sta_info **sta)
|
||||
{
|
||||
struct ieee80211_local *local = (*sta)->local;
|
||||
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
|
||||
/*
|
||||
* pull caller's reference if we're already gone.
|
||||
*/
|
||||
if (sta_info_hash_del(local, *sta)) {
|
||||
*sta = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*sta)->key) {
|
||||
ieee80211_key_free((*sta)->key);
|
||||
WARN_ON((*sta)->key);
|
||||
}
|
||||
|
||||
list_del(&(*sta)->list);
|
||||
(*sta)->dead = true;
|
||||
|
||||
if (test_and_clear_sta_flags(*sta,
|
||||
WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
|
||||
BUG_ON(!sdata->bss);
|
||||
|
||||
atomic_dec(&sdata->bss->num_sta_ps);
|
||||
__sta_info_clear_tim_bit(sdata->bss, *sta);
|
||||
}
|
||||
|
||||
local->num_sta--;
|
||||
local->sta_generation++;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
rcu_assign_pointer(sdata->u.vlan.sta, NULL);
|
||||
|
||||
if (local->ops->sta_notify) {
|
||||
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);
|
||||
sdata = (*sta)->sdata;
|
||||
}
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
mesh_accept_plinks_update(sdata);
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
del_timer(&(*sta)->plink_timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
printk(KERN_DEBUG "%s: Removed STA %pM\n",
|
||||
wiphy_name(local->hw.wiphy), (*sta)->sta.addr);
|
||||
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
||||
|
||||
/*
|
||||
* Finally, pull caller's reference if the STA is pinned by the
|
||||
* task that is adding the debugfs entries. In that case, we
|
||||
* leave the STA "to be freed".
|
||||
*
|
||||
* The rules are not trivial, but not too complex either:
|
||||
* (1) pin_status is only modified under the sta_lock
|
||||
* (2) STAs may only be pinned under the RTNL so that
|
||||
* sta_info_flush() is guaranteed to actually destroy
|
||||
* all STAs that are active for a given interface, this
|
||||
* is required for correctness because otherwise we
|
||||
* could notify a driver that an interface is going
|
||||
* away and only after that (!) notify it about a STA
|
||||
* on that interface going away.
|
||||
* (3) sta_info_debugfs_add_work() will set the status
|
||||
* to PINNED when it found an item that needs a new
|
||||
* debugfs directory created. In that case, that item
|
||||
* must not be freed although all *RCU* users are done
|
||||
* with it. Hence, we tell the caller of _unlink()
|
||||
* that the item is already gone (as can happen when
|
||||
* two tasks try to unlink/destroy at the same time)
|
||||
* (4) We set the pin_status to DESTROY here when we
|
||||
* find such an item.
|
||||
* (5) sta_info_debugfs_add_work() will reset the pin_status
|
||||
* from PINNED to NORMAL when it is done with the item,
|
||||
* but will check for DESTROY before resetting it in
|
||||
* which case it will free the item.
|
||||
*/
|
||||
if ((*sta)->pin_status == STA_INFO_PIN_STAT_PINNED) {
|
||||
(*sta)->pin_status = STA_INFO_PIN_STAT_DESTROY;
|
||||
*sta = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void sta_info_unlink(struct sta_info **sta)
|
||||
{
|
||||
struct ieee80211_local *local = (*sta)->local;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
__sta_info_unlink(sta);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
|
||||
static int sta_info_buffer_expired(struct sta_info *sta,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -681,6 +600,178 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
|
|||
}
|
||||
}
|
||||
|
||||
static int __must_check __sta_info_destroy(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_local *local;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
int ret, i;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!sta)
|
||||
return -ENOENT;
|
||||
|
||||
local = sta->local;
|
||||
sdata = sta->sdata;
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
ret = sta_info_hash_del(local, sta);
|
||||
/* this might still be the pending list ... which is fine */
|
||||
if (!ret)
|
||||
list_del(&sta->list);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (sta->key) {
|
||||
ieee80211_key_free(sta->key);
|
||||
/*
|
||||
* We have only unlinked the key, and actually destroying it
|
||||
* may mean it is removed from hardware which requires that
|
||||
* the key->sta pointer is still valid, so flush the key todo
|
||||
* list here.
|
||||
*
|
||||
* ieee80211_key_todo() will synchronize_rcu() so after this
|
||||
* nothing can reference this sta struct any more.
|
||||
*/
|
||||
ieee80211_key_todo();
|
||||
|
||||
WARN_ON(sta->key);
|
||||
}
|
||||
|
||||
sta->dead = true;
|
||||
|
||||
if (test_and_clear_sta_flags(sta,
|
||||
WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
|
||||
BUG_ON(!sdata->bss);
|
||||
|
||||
atomic_dec(&sdata->bss->num_sta_ps);
|
||||
__sta_info_clear_tim_bit(sdata->bss, sta);
|
||||
}
|
||||
|
||||
local->num_sta--;
|
||||
local->sta_generation++;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
rcu_assign_pointer(sdata->u.vlan.sta, NULL);
|
||||
|
||||
if (sta->uploaded) {
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
sdata = container_of(sdata->bss,
|
||||
struct ieee80211_sub_if_data,
|
||||
u.ap);
|
||||
drv_sta_remove(local, sdata, &sta->sta);
|
||||
sdata = sta->sdata;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
mesh_accept_plinks_update(sdata);
|
||||
del_timer(&sta->plink_timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
printk(KERN_DEBUG "%s: Removed STA %pM\n",
|
||||
wiphy_name(local->hw.wiphy), sta->sta.addr);
|
||||
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
||||
cancel_work_sync(&sta->drv_unblock_wk);
|
||||
|
||||
rate_control_remove_sta_debugfs(sta);
|
||||
ieee80211_sta_debugfs_remove(sta);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
|
||||
mesh_plink_deactivate(sta);
|
||||
del_timer_sync(&sta->plink_timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
|
||||
local->total_ps_buffered--;
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
for (i = 0; i < STA_TID_NUM; i++) {
|
||||
struct tid_ampdu_rx *tid_rx;
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
tid_rx = sta->ampdu_mlme.tid_rx[i];
|
||||
/* Make sure timer won't free the tid_rx struct, see below */
|
||||
if (tid_rx)
|
||||
tid_rx->shutdown = true;
|
||||
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
||||
/*
|
||||
* Outside spinlock - shutdown is true now so that the timer
|
||||
* won't free tid_rx, we have to do that now. Can't let the
|
||||
* timer do it because we have to sync the timer outside the
|
||||
* lock that it takes itself.
|
||||
*/
|
||||
if (tid_rx) {
|
||||
del_timer_sync(&tid_rx->session_timer);
|
||||
kfree(tid_rx);
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to do such complications for TX agg sessions, the
|
||||
* path leading to freeing the tid_tx struct goes via a call
|
||||
* from the driver, and thus needs to look up the sta struct
|
||||
* again, which cannot be found when we get here. Hence, we
|
||||
* just need to delete the timer and free the aggregation
|
||||
* info; we won't be telling the peer about it then but that
|
||||
* doesn't matter if we're not talking to it again anyway.
|
||||
*/
|
||||
tid_tx = sta->ampdu_mlme.tid_tx[i];
|
||||
if (tid_tx) {
|
||||
del_timer_sync(&tid_tx->addba_resp_timer);
|
||||
/*
|
||||
* STA removed while aggregation session being
|
||||
* started? Bit odd, but purge frames anyway.
|
||||
*/
|
||||
skb_queue_purge(&tid_tx->pending);
|
||||
kfree(tid_tx);
|
||||
}
|
||||
}
|
||||
|
||||
__sta_info_free(local, sta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sdata->local->sta_mtx);
|
||||
sta = sta_info_get(sdata, addr);
|
||||
ret = __sta_info_destroy(sta);
|
||||
mutex_unlock(&sdata->local->sta_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sdata->local->sta_mtx);
|
||||
sta = sta_info_get_bss(sdata, addr);
|
||||
ret = __sta_info_destroy(sta);
|
||||
mutex_unlock(&sdata->local->sta_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sta_info_cleanup(unsigned long data)
|
||||
{
|
||||
|
|
@ -700,90 +791,18 @@ static void sta_info_cleanup(unsigned long data)
|
|||
add_timer(&local->sta_cleanup);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/*
|
||||
* See comment in __sta_info_unlink,
|
||||
* caller must hold local->sta_lock.
|
||||
*/
|
||||
static void __sta_info_pin(struct sta_info *sta)
|
||||
{
|
||||
WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL);
|
||||
sta->pin_status = STA_INFO_PIN_STAT_PINNED;
|
||||
}
|
||||
|
||||
/*
|
||||
* See comment in __sta_info_unlink, returns sta if it
|
||||
* needs to be destroyed.
|
||||
*/
|
||||
static struct sta_info *__sta_info_unpin(struct sta_info *sta)
|
||||
{
|
||||
struct sta_info *ret = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sta->local->sta_lock, flags);
|
||||
WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_DESTROY &&
|
||||
sta->pin_status != STA_INFO_PIN_STAT_PINNED);
|
||||
if (sta->pin_status == STA_INFO_PIN_STAT_DESTROY)
|
||||
ret = sta;
|
||||
sta->pin_status = STA_INFO_PIN_STAT_NORMAL;
|
||||
spin_unlock_irqrestore(&sta->local->sta_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sta_info_debugfs_add_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, sta_debugfs_add);
|
||||
struct sta_info *sta, *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
/* We need to keep the RTNL across the whole pinned status. */
|
||||
rtnl_lock();
|
||||
while (1) {
|
||||
sta = NULL;
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry(tmp, &local->sta_list, list) {
|
||||
/*
|
||||
* debugfs.add_has_run will be set by
|
||||
* ieee80211_sta_debugfs_add regardless
|
||||
* of what else it does.
|
||||
*/
|
||||
if (!tmp->debugfs.add_has_run) {
|
||||
sta = tmp;
|
||||
__sta_info_pin(sta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
|
||||
if (!sta)
|
||||
break;
|
||||
|
||||
ieee80211_sta_debugfs_add(sta);
|
||||
rate_control_add_sta_debugfs(sta);
|
||||
|
||||
sta = __sta_info_unpin(sta);
|
||||
sta_info_destroy(sta);
|
||||
}
|
||||
rtnl_unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
void sta_info_init(struct ieee80211_local *local)
|
||||
{
|
||||
spin_lock_init(&local->sta_lock);
|
||||
mutex_init(&local->sta_mtx);
|
||||
INIT_LIST_HEAD(&local->sta_list);
|
||||
INIT_LIST_HEAD(&local->sta_pending_list);
|
||||
INIT_WORK(&local->sta_finish_work, sta_info_finish_work);
|
||||
|
||||
setup_timer(&local->sta_cleanup, sta_info_cleanup,
|
||||
(unsigned long)local);
|
||||
local->sta_cleanup.expires =
|
||||
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sta_info_start(struct ieee80211_local *local)
|
||||
|
|
@ -795,16 +814,6 @@ int sta_info_start(struct ieee80211_local *local)
|
|||
void sta_info_stop(struct ieee80211_local *local)
|
||||
{
|
||||
del_timer(&local->sta_cleanup);
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/*
|
||||
* Make sure the debugfs adding work isn't pending after this
|
||||
* because we're about to be destroyed. It doesn't matter
|
||||
* whether it ran or not since we're going to flush all STAs
|
||||
* anyway.
|
||||
*/
|
||||
cancel_work_sync(&local->sta_debugfs_add);
|
||||
#endif
|
||||
|
||||
sta_info_flush(local, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -820,26 +829,19 @@ int sta_info_flush(struct ieee80211_local *local,
|
|||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct sta_info *sta, *tmp;
|
||||
LIST_HEAD(tmp_list);
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
might_sleep();
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
if (!sdata || sdata == sta->sdata) {
|
||||
__sta_info_unlink(&sta);
|
||||
if (sta) {
|
||||
list_add_tail(&sta->list, &tmp_list);
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
|
||||
sta_info_destroy(sta);
|
||||
sta_info_finish_pending(local);
|
||||
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
if (!sdata || sdata == sta->sdata)
|
||||
WARN_ON(__sta_info_destroy(sta));
|
||||
}
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -849,24 +851,17 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
|||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta, *tmp;
|
||||
LIST_HEAD(tmp_list);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
mutex_lock(&local->sta_mtx);
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
|
||||
if (time_after(jiffies, sta->last_rx + exp_time)) {
|
||||
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
||||
printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
|
||||
sdata->name, sta->sta.addr);
|
||||
#endif
|
||||
__sta_info_unlink(&sta);
|
||||
if (sta)
|
||||
list_add(&sta->list, &tmp_list);
|
||||
WARN_ON(__sta_info_destroy(sta));
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(sta, tmp, &tmp_list, list)
|
||||
sta_info_destroy(sta);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
}
|
||||
|
||||
struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
* be in the queues
|
||||
* @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
|
||||
* station in power-save mode, reply when the driver unblocks.
|
||||
* @WLAN_STA_DISASSOC: Disassociation in progress.
|
||||
* This is used to reject TX BA session requests when disassociation
|
||||
* is in progress.
|
||||
*/
|
||||
enum ieee80211_sta_info_flags {
|
||||
WLAN_STA_AUTH = 1<<0,
|
||||
|
|
@ -57,6 +60,7 @@ enum ieee80211_sta_info_flags {
|
|||
WLAN_STA_SUSPEND = 1<<11,
|
||||
WLAN_STA_PS_DRIVER = 1<<12,
|
||||
WLAN_STA_PSPOLL = 1<<13,
|
||||
WLAN_STA_DISASSOC = 1<<14,
|
||||
};
|
||||
|
||||
#define STA_TID_NUM 16
|
||||
|
|
@ -162,11 +166,6 @@ struct sta_ampdu_mlme {
|
|||
};
|
||||
|
||||
|
||||
/* see __sta_info_unlink */
|
||||
#define STA_INFO_PIN_STAT_NORMAL 0
|
||||
#define STA_INFO_PIN_STAT_PINNED 1
|
||||
#define STA_INFO_PIN_STAT_DESTROY 2
|
||||
|
||||
/**
|
||||
* struct sta_info - STA information
|
||||
*
|
||||
|
|
@ -187,7 +186,6 @@ struct sta_ampdu_mlme {
|
|||
* @flaglock: spinlock for flags accesses
|
||||
* @drv_unblock_wk: used for driver PS unblocking
|
||||
* @listen_interval: listen interval of this station, when we're acting as AP
|
||||
* @pin_status: used internally for pinning a STA struct into memory
|
||||
* @flags: STA flags, see &enum ieee80211_sta_info_flags
|
||||
* @ps_tx_buf: buffer of frames to transmit to this station
|
||||
* when it leaves power saving state
|
||||
|
|
@ -226,6 +224,7 @@ struct sta_ampdu_mlme {
|
|||
* @debugfs: debug filesystem info
|
||||
* @sta: station information we share with the driver
|
||||
* @dead: set to true when sta is unlinked
|
||||
* @uploaded: set to true when sta is uploaded to the driver
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
|
|
@ -245,11 +244,7 @@ struct sta_info {
|
|||
|
||||
bool dead;
|
||||
|
||||
/*
|
||||
* for use by the internal lifetime management,
|
||||
* see __sta_info_unlink
|
||||
*/
|
||||
u8 pin_status;
|
||||
bool uploaded;
|
||||
|
||||
/*
|
||||
* frequently updated, locked with own spinlock (flaglock),
|
||||
|
|
@ -449,18 +444,19 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
* Insert STA info into hash table/list, returns zero or a
|
||||
* -EEXIST if (if the same MAC address is already present).
|
||||
*
|
||||
* Calling this without RCU protection makes the caller
|
||||
* relinquish its reference to @sta.
|
||||
* Calling the non-rcu version makes the caller relinquish,
|
||||
* the _rcu version calls read_lock_rcu() and must be called
|
||||
* without it held.
|
||||
*/
|
||||
int sta_info_insert(struct sta_info *sta);
|
||||
/*
|
||||
* Unlink a STA info from the hash table/list.
|
||||
* This can NULL the STA pointer if somebody else
|
||||
* has already unlinked it.
|
||||
*/
|
||||
void sta_info_unlink(struct sta_info **sta);
|
||||
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU);
|
||||
int sta_info_insert_atomic(struct sta_info *sta);
|
||||
|
||||
int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *addr);
|
||||
int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *addr);
|
||||
|
||||
void sta_info_destroy(struct sta_info *sta);
|
||||
void sta_info_set_tim_bit(struct sta_info *sta);
|
||||
void sta_info_clear_tim_bit(struct sta_info *sta);
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
rcu_read_lock();
|
||||
|
||||
sband = local->hw.wiphy->bands[info->band];
|
||||
fc = hdr->frame_control;
|
||||
|
||||
for_each_sta_info(local, hdr->addr1, sta, tmp) {
|
||||
/* skip wrong virtual interface */
|
||||
|
|
@ -205,8 +206,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
return;
|
||||
}
|
||||
|
||||
fc = hdr->frame_control;
|
||||
|
||||
if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
|
||||
(ieee80211_is_data_qos(fc))) {
|
||||
u16 tid, ssn;
|
||||
|
|
@ -275,6 +274,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
local->dot11FailedCount++;
|
||||
}
|
||||
|
||||
if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
|
||||
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
|
||||
!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
|
||||
local->ps_sdata && !(local->scanning)) {
|
||||
if (info->flags & IEEE80211_TX_STAT_ACK) {
|
||||
local->ps_sdata->u.mgd.flags |=
|
||||
IEEE80211_STA_NULLFUNC_ACKED;
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&local->dynamic_ps_enable_work);
|
||||
} else
|
||||
mod_timer(&local->dynamic_ps_timer, jiffies +
|
||||
msecs_to_jiffies(10));
|
||||
}
|
||||
|
||||
/* this was a transmitted frame, but now we want to reuse it */
|
||||
skb_orphan(skb);
|
||||
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ ieee80211_tx_h_sta(struct ieee80211_tx_data *tx)
|
|||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
|
||||
|
||||
if (tx->sta)
|
||||
if (tx->sta && tx->sta->uploaded)
|
||||
info->control.sta = &tx->sta->sta;
|
||||
|
||||
return TX_CONTINUE;
|
||||
|
|
@ -1010,7 +1010,8 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
|
|||
(struct ieee80211_radiotap_header *) skb->data;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
|
||||
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
|
||||
NULL);
|
||||
|
||||
sband = tx->local->hw.wiphy->bands[tx->channel->band];
|
||||
|
||||
|
|
@ -1046,7 +1047,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
|
|||
* because it will be recomputed and added
|
||||
* on transmission
|
||||
*/
|
||||
if (skb->len < (iterator.max_length + FCS_LEN))
|
||||
if (skb->len < (iterator._max_length + FCS_LEN))
|
||||
return false;
|
||||
|
||||
skb_trim(skb, skb->len - FCS_LEN);
|
||||
|
|
@ -1073,10 +1074,10 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
|
|||
|
||||
/*
|
||||
* remove the radiotap header
|
||||
* iterator->max_length was sanity-checked against
|
||||
* iterator->_max_length was sanity-checked against
|
||||
* skb->len by iterator init
|
||||
*/
|
||||
skb_pull(skb, iterator.max_length);
|
||||
skb_pull(skb, iterator._max_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1082,7 +1082,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
struct ieee80211_hw *hw = &local->hw;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct sta_info *sta;
|
||||
unsigned long flags;
|
||||
int res;
|
||||
|
||||
if (local->suspended)
|
||||
|
|
@ -1116,20 +1115,19 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
}
|
||||
|
||||
/* add STAs back */
|
||||
if (local->ops->sta_notify) {
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
mutex_lock(&local->sta_mtx);
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
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_ADD,
|
||||
&sta->sta);
|
||||
WARN_ON(drv_sta_add(local, sdata, &sta->sta));
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
|
||||
/* Clear Suspend state so that ADDBA requests can be processed */
|
||||
|
||||
|
|
@ -1180,6 +1178,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
}
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
ieee80211_sta_tear_down_BA_sessions(sta);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add back keys */
|
||||
list_for_each_entry(sdata, &local->interfaces, list)
|
||||
if (ieee80211_sdata_running(sdata))
|
||||
|
|
@ -1219,10 +1225,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
|
||||
add_timer(&local->sta_cleanup);
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
mutex_lock(&local->sta_mtx);
|
||||
list_for_each_entry(sta, &local->sta_list, list)
|
||||
mesh_plink_restart(sta);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
#else
|
||||
WARN_ON(1);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue