From 2e3c8736820bf72a8ad10721c7e31d36d4fa7790 Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Sat, 23 Feb 2008 15:17:09 +0100 Subject: mac80211: support functions for mesh The two important features coded in mesh.c are: Recently Multicast Cache: in on-demand HWMP, multicast traffic is retransmitted by every receiving node. Even though a mesh TTL counter avoids infinite loops, it is also necessary to avoid traffic explosion by keeping a cache of multicast mesh frame that have been received recently. With this feature, maximum number of retransmissions of a multicast frame for the case of N nodes within the range of each other would be N. Without it, the maximum number of retransmissions would be in the order of N^(MESH_TTL - 1). Code to support mesh tables. Signed-off-by: Luis Carlos Cobo Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 net/mac80211/mesh.c (limited to 'net/mac80211/mesh.c') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c new file mode 100644 index 000000000000..8ff533005d92 --- /dev/null +++ b/net/mac80211/mesh.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2008 open80211s Ltd. + * Authors: Luis Carlos Cobo + * Javier Cardona + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "ieee80211_i.h" +#include "mesh.h" + +#define PP_OFFSET 1 /* Path Selection Protocol */ +#define PM_OFFSET 5 /* Path Selection Metric */ +#define CC_OFFSET 9 /* Congestion Control Mode */ +#define CAPAB_OFFSET 17 +#define ACCEPT_PLINKS 0x80 + +int mesh_allocated; +static struct kmem_cache *rm_cache; + +void ieee80211s_init(void) +{ + mesh_pathtbl_init(); + mesh_allocated = 1; + rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry), + 0, 0, NULL); +} + +void ieee80211s_stop(void) +{ + mesh_pathtbl_unregister(); + kmem_cache_destroy(rm_cache); +} + +/** + * mesh_matches_local - check if the config of a mesh point matches ours + * + * @ie: information elements of a management frame from the mesh peer + * @dev: local mesh interface + * + * This function checks if the mesh configuration of a mesh point matches the + * local mesh configuration, i.e. if both nodes belong to the same mesh network. + */ +bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *sta = &sdata->u.sta; + + if (sta->mesh_id_len == ie->mesh_id_len && + memcmp(sta->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && + memcmp(sta->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 && + memcmp(sta->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 && + memcmp(sta->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0) + /* + * As support for each feature is added, check for matching + * - On mesh config capabilities + * - Power Save Support En + * - Sync support enabled + * - Sync support active + * - Sync support required from peer + * - MDA enabled + * - Power management control on fc + */ + return true; + + return false; +} + +/** + * mesh_peer_accepts_plinks - check if an mp is willing to establish peer links + * + * @ie: information elements of a management frame from the mesh peer + * @dev: local mesh interface + */ +bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie, + struct net_device *dev) +{ + return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0; +} + +/** + * mesh_accept_plinks_update: update accepting_plink in local mesh beacons + * + * @dev: mesh interface in which mesh beacons are going to be updated + */ +void mesh_accept_plinks_update(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + bool free_plinks; + + /* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0, + * the mesh interface might be able to establish plinks with peers that + * are already on the table but are not on ESTAB state. However, in + * general the mesh interface is not accepting peer link requests from + * new peers, and that must be reflected in the beacon + */ + free_plinks = mesh_plink_availables(sdata); + + if (free_plinks != sdata->u.sta.accepting_plinks) + ieee80211_sta_timer((unsigned long) sdata); +} + +void mesh_ids_set_default(struct ieee80211_if_sta *sta) +{ + u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff}; + + memcpy(sta->mesh_pp_id, def_id, 4); + memcpy(sta->mesh_pm_id, def_id, 4); + memcpy(sta->mesh_cc_id, def_id, 4); +} + +int mesh_rmc_init(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int i; + + sdata->u.sta.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL); + if (!sdata->u.sta.rmc) + return -ENOMEM; + sdata->u.sta.rmc->idx_mask = RMC_BUCKETS - 1; + for (i = 0; i < RMC_BUCKETS; i++) + INIT_LIST_HEAD(&sdata->u.sta.rmc->bucket[i].list); + return 0; +} + +void mesh_rmc_free(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct mesh_rmc *rmc = sdata->u.sta.rmc; + struct rmc_entry *p, *n; + int i; + + if (!sdata->u.sta.rmc) + return; + + for (i = 0; i < RMC_BUCKETS; i++) + list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) { + list_del(&p->list); + kmem_cache_free(rm_cache, p); + } + + kfree(rmc); + sdata->u.sta.rmc = NULL; +} + +/** + * mesh_rmc_check - Check frame in recent multicast cache and add if absent. + * + * @sa: source address + * @mesh_hdr: mesh_header + * + * Returns: 0 if the frame is not in the cache, nonzero otherwise. + * + * Checks using the source address and the mesh sequence number if we have + * received this frame lately. If the frame is not in the cache, it is added to + * it. + */ +int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, + struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct mesh_rmc *rmc = sdata->u.sta.rmc; + u32 seqnum = 0; + int entries = 0; + u8 idx; + struct rmc_entry *p, *n; + + /* Don't care about endianness since only match matters */ + memcpy(&seqnum, mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum)); + idx = mesh_hdr->seqnum[0] & rmc->idx_mask; + list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) { + ++entries; + if (time_after(jiffies, p->exp_time) || + (entries == RMC_QUEUE_MAX_LEN)) { + list_del(&p->list); + kmem_cache_free(rm_cache, p); + --entries; + } else if ((seqnum == p->seqnum) + && (memcmp(sa, p->sa, ETH_ALEN) == 0)) + return -1; + } + + p = kmem_cache_alloc(rm_cache, GFP_ATOMIC); + if (!p) { + printk(KERN_DEBUG "o11s: could not allocate RMC entry\n"); + return 0; + } + p->seqnum = seqnum; + p->exp_time = jiffies + RMC_TIMEOUT; + memcpy(p->sa, sa, ETH_ALEN); + list_add(&p->list, &rmc->bucket[idx].list); + return 0; +} + +void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_supported_band *sband; + u8 *pos; + int len, i, rate; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + len = sband->n_bitrates; + if (len > 8) + len = 8; + pos = skb_put(skb, len + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = len; + for (i = 0; i < len; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + + if (sband->n_bitrates > len) { + pos = skb_put(skb, sband->n_bitrates - len + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = sband->n_bitrates - len; + for (i = len; i < sband->n_bitrates; i++) { + rate = sband->bitrates[i].bitrate; + *pos++ = (u8) (rate / 5); + } + } + + pos = skb_put(skb, 2 + sdata->u.sta.mesh_id_len); + *pos++ = WLAN_EID_MESH_ID; + *pos++ = sdata->u.sta.mesh_id_len; + if (sdata->u.sta.mesh_id_len) + memcpy(pos, sdata->u.sta.mesh_id, sdata->u.sta.mesh_id_len); + + pos = skb_put(skb, 21); + *pos++ = WLAN_EID_MESH_CONFIG; + *pos++ = MESH_CFG_LEN; + /* Version */ + *pos++ = 1; + + /* Active path selection protocol ID */ + memcpy(pos, sdata->u.sta.mesh_pp_id, 4); + pos += 4; + + /* Active path selection metric ID */ + memcpy(pos, sdata->u.sta.mesh_pm_id, 4); + pos += 4; + + /* Congestion control mode identifier */ + memcpy(pos, sdata->u.sta.mesh_cc_id, 4); + pos += 4; + + /* Channel precedence: + * Not running simple channel unification protocol + */ + memset(pos, 0x00, 4); + pos += 4; + + /* Mesh capability */ + sdata->u.sta.accepting_plinks = mesh_plink_availables(sdata); + *pos++ = sdata->u.sta.accepting_plinks ? ACCEPT_PLINKS : 0x00; + *pos++ = 0x00; + + return; +} + +u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl) +{ + /* Use last four bytes of hw addr and interface index as hash index */ + return jhash_2words(*(u32 *)(addr+2), dev->ifindex, tbl->hash_rnd) + & tbl->hash_mask; +} + +u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len) +{ + if (!mesh_id_len) + return 1; + else if (mesh_id_len == 1) + return (u8) mesh_id[0]; + else + return (u8) (mesh_id[0] + 2 * mesh_id[1]); +} + +struct mesh_table *mesh_table_alloc(int size_order) +{ + int i; + struct mesh_table *newtbl; + + newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL); + if (!newtbl) + return NULL; + + newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * + (1 << size_order), GFP_KERNEL); + + if (!newtbl->hash_buckets) { + kfree(newtbl); + return NULL; + } + + newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * + (1 << size_order), GFP_KERNEL); + if (!newtbl->hashwlock) { + kfree(newtbl->hash_buckets); + kfree(newtbl); + return NULL; + } + + newtbl->size_order = size_order; + newtbl->hash_mask = (1 << size_order) - 1; + atomic_set(&newtbl->entries, 0); + get_random_bytes(&newtbl->hash_rnd, + sizeof(newtbl->hash_rnd)); + for (i = 0; i <= newtbl->hash_mask; i++) + spin_lock_init(&newtbl->hashwlock[i]); + + return newtbl; +} + +void mesh_table_free(struct mesh_table *tbl, bool free_leafs) +{ + struct hlist_head *mesh_hash; + struct hlist_node *p, *q; + int i; + + mesh_hash = tbl->hash_buckets; + for (i = 0; i <= tbl->hash_mask; i++) { + spin_lock(&tbl->hashwlock[i]); + hlist_for_each_safe(p, q, &mesh_hash[i]) { + tbl->free_node(p, free_leafs); + atomic_dec(&tbl->entries); + } + spin_unlock(&tbl->hashwlock[i]); + } + kfree(tbl->hash_buckets); + kfree(tbl->hashwlock); + kfree(tbl); +} + +static void ieee80211_mesh_path_timer(unsigned long data) +{ + struct ieee80211_sub_if_data *sdata = + (struct ieee80211_sub_if_data *) data; + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = wdev_priv(&sdata->wdev); + + queue_work(local->hw.workqueue, &ifsta->work); +} + +struct mesh_table *mesh_table_grow(struct mesh_table *tbl) +{ + struct mesh_table *newtbl; + struct hlist_head *oldhash; + struct hlist_node *p; + int err = 0; + int i; + + if (atomic_read(&tbl->entries) + < tbl->mean_chain_len * (tbl->hash_mask + 1)) { + err = -EPERM; + goto endgrow; + } + + newtbl = mesh_table_alloc(tbl->size_order + 1); + if (!newtbl) { + err = -ENOMEM; + goto endgrow; + } + + newtbl->free_node = tbl->free_node; + newtbl->mean_chain_len = tbl->mean_chain_len; + newtbl->copy_node = tbl->copy_node; + atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); + + oldhash = tbl->hash_buckets; + for (i = 0; i <= tbl->hash_mask; i++) + hlist_for_each(p, &oldhash[i]) + tbl->copy_node(p, newtbl); + +endgrow: + if (err) + return NULL; + else + return newtbl; +} -- cgit v1.2.3-59-g8ed1b From 902acc7896d7649fb30e4b22bd4e643c7f34b02c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 23 Feb 2008 15:17:19 +0100 Subject: mac80211: clean up mesh code Various cleanups, reducing the #ifdef mess and other things. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 8 +++ net/mac80211/cfg.c | 32 +++-------- net/mac80211/ieee80211.c | 9 +-- net/mac80211/ieee80211_i.h | 56 ++++++++++++++++--- net/mac80211/ieee80211_iface.c | 66 ++++------------------ net/mac80211/ieee80211_sta.c | 72 +++++++++++------------- net/mac80211/mesh.c | 67 ++++++++++++++++++++++ net/mac80211/mesh.h | 13 ++++- net/mac80211/mesh_plink.c | 28 +++++++--- net/mac80211/rc80211_pid_algo.c | 3 - net/mac80211/rx.c | 28 +++++----- net/mac80211/sta_info.c | 13 +---- net/mac80211/sta_info.h | 2 - net/mac80211/tx.c | 120 +++++++++++++++++----------------------- net/mac80211/util.c | 32 ----------- 15 files changed, 277 insertions(+), 272 deletions(-) (limited to 'net/mac80211/mesh.c') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 934cc25f757a..6aca472d7a02 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -465,6 +465,14 @@ struct ieee80211_vif { u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; +static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) +{ +#ifdef CONFIG_MAC80211_MESH + return vif->type == IEEE80211_IF_TYPE_MESH_POINT; +#endif + return false; +} + /** * struct ieee80211_if_init_conf - initial configuration of an interface * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b1befac1736a..6ac49231efa9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -15,9 +15,7 @@ #include "ieee80211_i.h" #include "cfg.h" #include "ieee80211_rate.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif #define DEFAULT_RATES 0 @@ -119,14 +117,10 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, ieee80211_if_reinit(dev); ieee80211_if_set_type(dev, itype); -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT && - params->mesh_id_len) { - sdata->u.sta.mesh_id_len = params->mesh_id_len; - memcpy(sdata->u.sta.mesh_id, params->mesh_id, - params->mesh_id_len); - } -#endif + if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len) + ieee80211_if_sta_set_mesh_id(&sdata->u.sta, + params->mesh_id_len, + params->mesh_id); if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags) return 0; @@ -317,9 +311,7 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { -#ifdef CONFIG_MAC80211_MESH struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); -#endif sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | @@ -329,8 +321,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->rx_bytes = sta->rx_bytes; sinfo->tx_bytes = sta->tx_bytes; + if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) { sinfo->filled |= STATION_INFO_LLID | STATION_INFO_PLID | STATION_INFO_PLINK_STATE; @@ -338,8 +330,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->llid = le16_to_cpu(sta->llid); sinfo->plid = le16_to_cpu(sta->plid); sinfo->plink_state = sta->plink_state; - } #endif + } } @@ -580,9 +572,7 @@ static void sta_apply_parameters(struct ieee80211_local *local, u32 rates; int i, j; struct ieee80211_supported_band *sband; -#ifdef CONFIG_MAC80211_MESH struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); -#endif if (params->station_flags & STATION_FLAG_CHANGED) { sta->flags &= ~WLAN_STA_AUTHORIZED; @@ -621,9 +611,7 @@ static void sta_apply_parameters(struct ieee80211_local *local, sta->supp_rates[local->oper_channel->band] = rates; } -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT && - params->plink_action) + if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { switch (params->plink_action) { case PLINK_ACTION_OPEN: mesh_plink_open(sta); @@ -632,7 +620,7 @@ static void sta_apply_parameters(struct ieee80211_local *local, mesh_plink_block(sta); break; } -#endif + } } static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, @@ -655,11 +643,9 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } else sdata = IEEE80211_DEV_TO_SUB_IF(dev); -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) + if (ieee80211_vif_is_mesh(&sdata->vif)) sta = mesh_plink_add(mac, DEFAULT_RATES, dev); else -#endif sta = sta_info_add(local, dev, mac, GFP_KERNEL); if (IS_ERR(sta)) diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 7106d651f4f9..727af295c969 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -26,9 +26,7 @@ #include "ieee80211_i.h" #include "ieee80211_rate.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif #include "wep.h" #include "wme.h" #include "aes_ccm.h" @@ -938,11 +936,9 @@ static int __ieee80211_if_config(struct net_device *dev, conf.bssid = sdata->u.sta.bssid; conf.ssid = sdata->u.sta.ssid; conf.ssid_len = sdata->u.sta.ssid_len; -#ifdef CONFIG_MAC80211_MESH - } else if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) { + } else if (ieee80211_vif_is_mesh(&sdata->vif)) { conf.beacon = beacon; ieee80211_start_mesh(dev); -#endif } else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) { conf.ssid = sdata->u.ap.ssid; conf.ssid_len = sdata->u.ap.ssid_len; @@ -1824,10 +1820,9 @@ static void __exit ieee80211_exit(void) rc80211_simple_exit(); rc80211_pid_exit(); -#ifdef CONFIG_MAC80211_MESH if (mesh_allocated) ieee80211s_stop(); -#endif + ieee80211_wme_unregister(); ieee80211_debugfs_netdev_exit(); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 49466b6996d1..7394c9b783b8 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -93,9 +93,8 @@ struct ieee80211_sta_bss { #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; -#endif - /* mesh_cfg left out the ifdef to reduce clutter on bss handling */ u8 *mesh_cfg; +#endif #define IEEE80211_MAX_SUPP_RATES 32 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; @@ -113,6 +112,30 @@ struct ieee80211_sta_bss { u8 erp_value; }; +static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss) +{ +#ifdef CONFIG_MAC80211_MESH + return bss->mesh_cfg; +#endif + return NULL; +} + +static inline u8 *bss_mesh_id(struct ieee80211_sta_bss *bss) +{ +#ifdef CONFIG_MAC80211_MESH + return bss->mesh_id; +#endif + return NULL; +} + +static inline u8 bss_mesh_id_len(struct ieee80211_sta_bss *bss) +{ +#ifdef CONFIG_MAC80211_MESH + return bss->mesh_id_len; +#endif + return 0; +} + typedef unsigned __bitwise__ ieee80211_tx_result; #define TX_CONTINUE ((__force ieee80211_tx_result) 0u) @@ -233,7 +256,6 @@ struct ieee80211_if_vlan { struct list_head list; }; -#ifdef CONFIG_MAC80211_MESH struct mesh_stats { __u32 fwded_frames; /* Mesh forwarded frames */ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ @@ -249,7 +271,6 @@ struct mesh_preq_queue { u8 flags; }; - struct mesh_config { /* Timeouts in ms */ /* Mesh plink management parameters */ @@ -268,7 +289,7 @@ struct mesh_config { u32 path_refresh_time; u16 min_discovery_timeout; }; -#endif + /* flags used in struct ieee80211_if_sta.flags */ #define IEEE80211_STA_SSID_SET BIT(0) @@ -361,6 +382,22 @@ struct ieee80211_if_sta { int num_beacons; /* number of TXed beacon frames by this STA */ }; +static inline void ieee80211_if_sta_set_mesh_id(struct ieee80211_if_sta *ifsta, + u8 mesh_id_len, u8 *mesh_id) +{ +#ifdef CONFIG_MAC80211_MESH + ifsta->mesh_id_len = mesh_id_len; + memcpy(ifsta->mesh_id, mesh_id, mesh_id_len); +#endif +} + +#ifdef CONFIG_MAC80211_MESH +#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \ + do { (sta)->mshstats.name++; } while (0) +#else +#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \ + do { } while (0) +#endif /* flags used in struct ieee80211_sub_if_data.flags */ #define IEEE80211_SDATA_ALLMULTI BIT(0) @@ -472,7 +509,7 @@ struct ieee80211_sub_if_data { struct dentry *dropped_frames_ttl; struct dentry *dropped_frames_no_route; struct dentry *estab_plinks; - struct timer_list mesh_path_timer; + struct timer_list mesh_path_timer; } mesh_stats; struct dentry *mesh_config_dir; @@ -884,12 +921,17 @@ void sta_addba_resp_timer_expired(unsigned long data); u64 ieee80211_sta_get_rates(struct ieee80211_local *local, struct ieee802_11_elems *elems, enum ieee80211_band band); -void ieee80211_start_mesh(struct net_device *dev); void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, int encrypt); void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); +#ifdef CONFIG_MAC80211_MESH +void ieee80211_start_mesh(struct net_device *dev); +#else +static inline void ieee80211_start_mesh(struct net_device *dev) +{} +#endif /* ieee80211_iface.c */ int ieee80211_if_add(struct net_device *dev, const char *name, diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index c2f92b78bfc9..b0f17a2b1a42 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c @@ -15,9 +15,7 @@ #include "ieee80211_i.h" #include "sta_info.h" #include "debugfs_netdev.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata) { @@ -82,14 +80,11 @@ int ieee80211_if_add(struct net_device *dev, const char *name, ieee80211_debugfs_add_netdev(sdata); ieee80211_if_set_type(ndev, type); -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT && - params && params->mesh_id_len) { - sdata->u.sta.mesh_id_len = params->mesh_id_len; - memcpy(sdata->u.sta.mesh_id, params->mesh_id, - params->mesh_id_len); - } -#endif + if (ieee80211_vif_is_mesh(&sdata->vif) && + params && params->mesh_id_len) + ieee80211_if_sta_set_mesh_id(&sdata->u.sta, + params->mesh_id_len, + params->mesh_id); /* we're under RTNL so all this is fine */ if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) { @@ -170,47 +165,8 @@ void ieee80211_if_set_type(struct net_device *dev, int type) msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev); sdata->bss = &msdata->u.ap; -#ifdef CONFIG_MAC80211_MESH - if (type == IEEE80211_IF_TYPE_MESH_POINT) { - ifsta->mshcfg.dot11MeshRetryTimeout = MESH_RET_T; - ifsta->mshcfg.dot11MeshConfirmTimeout = MESH_CONF_T; - ifsta->mshcfg.dot11MeshHoldingTimeout = MESH_HOLD_T; - ifsta->mshcfg.dot11MeshMaxRetries = MESH_MAX_RETR; - ifsta->mshcfg.dot11MeshTTL = MESH_TTL; - ifsta->mshcfg.auto_open_plinks = true; - ifsta->mshcfg.dot11MeshMaxPeerLinks = - MESH_MAX_ESTAB_PLINKS; - ifsta->mshcfg.dot11MeshHWMPactivePathTimeout = - MESH_PATH_TIMEOUT; - ifsta->mshcfg.dot11MeshHWMPpreqMinInterval = - MESH_PREQ_MIN_INT; - ifsta->mshcfg.dot11MeshHWMPnetDiameterTraversalTime = - MESH_DIAM_TRAVERSAL_TIME; - ifsta->mshcfg.dot11MeshHWMPmaxPREQretries = - MESH_MAX_PREQ_RETRIES; - ifsta->mshcfg.path_refresh_time = - MESH_PATH_REFRESH_TIME; - ifsta->mshcfg.min_discovery_timeout = - MESH_MIN_DISCOVERY_TIMEOUT; - ifsta->accepting_plinks = true; - ifsta->preq_id = 0; - ifsta->dsn = 0; - atomic_set(&ifsta->mpaths, 0); - mesh_rmc_init(dev); - ifsta->last_preq = jiffies; - /* Allocate all mesh structures when creating the first - * mesh interface. - */ - if (!mesh_allocated) - ieee80211s_init(); - mesh_ids_set_default(ifsta); - setup_timer(&ifsta->mesh_path_timer, - ieee80211_mesh_path_timer, - (unsigned long) sdata); - INIT_LIST_HEAD(&ifsta->preq_queue.list); - spin_lock_init(&ifsta->mesh_preq_queue_lock); - } -#endif + if (ieee80211_vif_is_mesh(&sdata->vif)) + ieee80211_mesh_init_sdata(sdata); break; } case IEEE80211_IF_TYPE_MNTR: @@ -240,6 +196,10 @@ void ieee80211_if_reinit(struct net_device *dev) ieee80211_if_sdata_deinit(sdata); + /* Need to handle mesh specially to allow eliding the function call */ + if (ieee80211_vif_is_mesh(&sdata->vif)) + mesh_rmc_free(dev); + switch (sdata->vif.type) { case IEEE80211_IF_TYPE_INVALID: /* cannot happen */ @@ -292,10 +252,6 @@ void ieee80211_if_reinit(struct net_device *dev) } break; case IEEE80211_IF_TYPE_MESH_POINT: -#ifdef CONFIG_MAC80211_MESH - mesh_rmc_free(dev); -#endif - /* fall through */ case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: kfree(sdata->u.sta.extra_ie); diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index d2dedcb5a954..9f933aeca719 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c @@ -31,9 +31,7 @@ #include "ieee80211_i.h" #include "ieee80211_rate.h" #include "ieee80211_led.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_MAX_TRIES 3 @@ -1897,12 +1895,13 @@ static void __ieee80211_rx_bss_hash_add(struct net_device *dev, { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); u8 hash_idx; -#ifdef CONFIG_MAC80211_MESH - if (bss->mesh_cfg) - hash_idx = mesh_id_hash(bss->mesh_id, bss->mesh_id_len); + + if (bss_mesh_cfg(bss)) + hash_idx = mesh_id_hash(bss_mesh_id(bss), + bss_mesh_id_len(bss)); else -#endif hash_idx = STA_HASH(bss->bssid); + bss->hnext = local->sta_bss_hash[hash_idx]; local->sta_bss_hash[hash_idx] = bss; } @@ -1967,7 +1966,8 @@ ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq, spin_lock_bh(&local->sta_bss_lock); bss = local->sta_bss_hash[STA_HASH(bssid)]; while (bss) { - if (!bss->mesh_cfg && !memcmp(bss->bssid, bssid, ETH_ALEN) && + if (!bss_mesh_cfg(bss) && + !memcmp(bss->bssid, bssid, ETH_ALEN) && bss->freq == freq && bss->ssid_len == ssid_len && (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) { @@ -1991,8 +1991,8 @@ ieee80211_rx_mesh_bss_get(struct net_device *dev, u8 *mesh_id, int mesh_id_len, spin_lock_bh(&local->sta_bss_lock); bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)]; while (bss) { - if (bss->mesh_cfg && - !memcmp(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN) && + if (bss_mesh_cfg(bss) && + !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) && bss->freq == freq && mesh_id_len == bss->mesh_id_len && (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id, @@ -2053,10 +2053,8 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) kfree(bss->rsn_ie); kfree(bss->wmm_ie); kfree(bss->ht_ie); -#ifdef CONFIG_MAC80211_MESH - kfree(bss->mesh_id); - kfree(bss->mesh_cfg); -#endif + kfree(bss_mesh_id(bss)); + kfree(bss_mesh_cfg(bss)); kfree(bss); } @@ -2322,16 +2320,14 @@ static void ieee80211_rx_bss_info(struct net_device *dev, beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp); ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT && elems.mesh_id - && elems.mesh_config) - if (mesh_matches_local(&elems, dev)) { - u64 rates = ieee80211_sta_get_rates(local, &elems, - rx_status->band); - mesh_neighbour_update(mgmt->sa, rates, dev, - mesh_peer_accepts_plinks(&elems, dev)); - } -#endif + if (ieee80211_vif_is_mesh(&sdata->vif) && elems.mesh_id && + elems.mesh_config && mesh_matches_local(&elems, dev)) { + u64 rates = ieee80211_sta_get_rates(local, &elems, + rx_status->band); + + mesh_neighbour_update(mgmt->sa, rates, dev, + mesh_peer_accepts_plinks(&elems, dev)); + } if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates && memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 && @@ -2712,9 +2708,7 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev, size_t len, struct ieee80211_rx_status *rx_status) { -#ifdef CONFIG_MAC80211_MESH struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -#endif if (len < IEEE80211_MIN_ACTION_SIZE) return; @@ -2747,17 +2741,14 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev, break; } break; -#ifdef CONFIG_MAC80211_MESH case PLINK_CATEGORY: - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) + if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_rx_plink_frame(dev, mgmt, len, rx_status); break; - case MESH_PATH_SEL_CATEGORY: - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) + if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_rx_path_sel_frame(dev, mgmt, len); break; -#endif default: if (net_ratelimit()) printk(KERN_DEBUG "%s: Rx unknown action frame - " @@ -3027,8 +3018,9 @@ void ieee80211_sta_work(struct work_struct *work) ieee80211_sta_rx_queued_mgmt(dev, skb); #ifdef CONFIG_MAC80211_MESH - if (ifsta->preq_queue_len && time_after(jiffies, ifsta->last_preq + - msecs_to_jiffies(ifsta->mshcfg.dot11MeshHWMPpreqMinInterval))) + if (ifsta->preq_queue_len && + time_after(jiffies, + ifsta->last_preq + msecs_to_jiffies(ifsta->mshcfg.dot11MeshHWMPpreqMinInterval))) mesh_path_start_discovery(dev); #endif @@ -3810,13 +3802,11 @@ ieee80211_sta_scan_result(struct net_device *dev, memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWESSID; - if (bss->mesh_cfg) { -#ifdef CONFIG_MAC80211_MESH - iwe.u.data.length = bss->mesh_id_len; + if (bss_mesh_cfg(bss)) { + iwe.u.data.length = bss_mesh_id_len(bss); iwe.u.data.flags = 1; current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->mesh_id); -#endif + bss_mesh_id(bss)); } else { iwe.u.data.length = bss->ssid_len; iwe.u.data.flags = 1; @@ -3825,10 +3815,10 @@ ieee80211_sta_scan_result(struct net_device *dev, } if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS - || bss->mesh_cfg)) { + || bss_mesh_cfg(bss))) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWMODE; - if (bss->mesh_cfg) + if (bss_mesh_cfg(bss)) iwe.u.mode = IW_MODE_MESH; else if (bss->capability & WLAN_CAPABILITY_ESS) iwe.u.mode = IW_MODE_MASTER; @@ -3919,9 +3909,9 @@ ieee80211_sta_scan_result(struct net_device *dev, } } - if (bss->mesh_cfg) { + if (bss_mesh_cfg(bss)) { char *buf; - u8 *cfg = bss->mesh_cfg; + u8 *cfg = bss_mesh_cfg(bss); buf = kmalloc(200, GFP_ATOMIC); if (buf) { memset(&iwe, 0, sizeof(iwe)); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8ff533005d92..ebe1a7a80bad 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -381,3 +381,70 @@ endgrow: else return newtbl; } + +/** + * ieee80211_new_mesh_header - create a new mesh header + * @meshhdr: uninitialized mesh header + * @sdata: mesh interface to be used + * + * Return the header length. + */ +int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, + struct ieee80211_sub_if_data *sdata) +{ + meshhdr->flags = 0; + meshhdr->ttl = sdata->u.sta.mshcfg.dot11MeshTTL; + + meshhdr->seqnum[0] = sdata->u.sta.mesh_seqnum[0]++; + meshhdr->seqnum[1] = sdata->u.sta.mesh_seqnum[1]; + meshhdr->seqnum[2] = sdata->u.sta.mesh_seqnum[2]; + + if (sdata->u.sta.mesh_seqnum[0] == 0) { + sdata->u.sta.mesh_seqnum[1]++; + if (sdata->u.sta.mesh_seqnum[1] == 0) + sdata->u.sta.mesh_seqnum[2]++; + } + + return 5; +} + +void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + + ifsta->mshcfg.dot11MeshRetryTimeout = MESH_RET_T; + ifsta->mshcfg.dot11MeshConfirmTimeout = MESH_CONF_T; + ifsta->mshcfg.dot11MeshHoldingTimeout = MESH_HOLD_T; + ifsta->mshcfg.dot11MeshMaxRetries = MESH_MAX_RETR; + ifsta->mshcfg.dot11MeshTTL = MESH_TTL; + ifsta->mshcfg.auto_open_plinks = true; + ifsta->mshcfg.dot11MeshMaxPeerLinks = + MESH_MAX_ESTAB_PLINKS; + ifsta->mshcfg.dot11MeshHWMPactivePathTimeout = + MESH_PATH_TIMEOUT; + ifsta->mshcfg.dot11MeshHWMPpreqMinInterval = + MESH_PREQ_MIN_INT; + ifsta->mshcfg.dot11MeshHWMPnetDiameterTraversalTime = + MESH_DIAM_TRAVERSAL_TIME; + ifsta->mshcfg.dot11MeshHWMPmaxPREQretries = + MESH_MAX_PREQ_RETRIES; + ifsta->mshcfg.path_refresh_time = + MESH_PATH_REFRESH_TIME; + ifsta->mshcfg.min_discovery_timeout = + MESH_MIN_DISCOVERY_TIMEOUT; + ifsta->accepting_plinks = true; + ifsta->preq_id = 0; + ifsta->dsn = 0; + atomic_set(&ifsta->mpaths, 0); + mesh_rmc_init(sdata->dev); + ifsta->last_preq = jiffies; + /* Allocate all mesh structures when creating the first mesh interface. */ + if (!mesh_allocated) + ieee80211s_init(); + mesh_ids_set_default(ifsta); + setup_timer(&ifsta->mesh_path_timer, + ieee80211_mesh_path_timer, + (unsigned long) sdata); + INIT_LIST_HEAD(&ifsta->preq_queue.list); + spin_lock_init(&ifsta->mesh_preq_queue_lock); +} diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index ac8923793908..d565b3fb9e6a 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -11,10 +11,10 @@ #ifndef IEEE80211S_H #define IEEE80211S_H -#include "ieee80211_i.h" +#include #include +#include "ieee80211_i.h" -extern int mesh_allocated; /* Data structures */ @@ -211,6 +211,8 @@ void mesh_rmc_free(struct net_device *dev); int mesh_rmc_init(struct net_device *dev); void ieee80211s_init(void); void ieee80211s_stop(void); +void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); + /* Mesh paths */ int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb, struct net_device *dev); @@ -257,6 +259,9 @@ void mesh_path_timer(unsigned long data); void mesh_path_flush_by_nexthop(struct sta_info *sta); void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev); +#ifdef CONFIG_MAC80211_MESH +extern int mesh_allocated; + static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata) { return sdata->u.sta.mshcfg.dot11MeshMaxPeerLinks - @@ -278,6 +283,10 @@ static inline void mesh_path_activate(struct mesh_path *mpath) for (i = 0; i <= x->hash_mask; i++) \ hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list) +#else +#define mesh_allocated 0 +#endif + #define MESH_PREQ(skb) (skb->cb + 30) #endif /* IEEE80211S_H */ diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 5cd97e99be62..0b0e8d7eb9c7 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -6,11 +6,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - +#include +#include #include "ieee80211_i.h" #include "ieee80211_rate.h" #include "mesh.h" -#include #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG #define mpl_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args) @@ -131,7 +131,7 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) } /** - * mesh_plink_deactivate - deactivate mesh peer link + * __mesh_plink_deactivate - deactivate mesh peer link * * @sta: mesh peer link to deactivate * @@ -139,7 +139,7 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) * * Locking: the caller must hold sta->plink_lock */ -void mesh_plink_deactivate(struct sta_info *sta) +static void __mesh_plink_deactivate(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); if (sta->plink_state == ESTAB) @@ -148,6 +148,20 @@ void mesh_plink_deactivate(struct sta_info *sta) mesh_path_flush_by_nexthop(sta); } +/** + * __mesh_plink_deactivate - deactivate mesh peer link + * + * @sta: mesh peer link to deactivate + * + * All mesh paths with this peer as next hop will be flushed + */ +void mesh_plink_deactivate(struct sta_info *sta) +{ + spin_lock_bh(&sta->plink_lock); + __mesh_plink_deactivate(sta); + spin_unlock_bh(&sta->plink_lock); +} + static int mesh_plink_frame_tx(struct net_device *dev, enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, __le16 reason) { @@ -365,7 +379,7 @@ void mesh_plink_block(struct sta_info *sta) #endif spin_lock_bh(&sta->plink_lock); - mesh_plink_deactivate(sta); + __mesh_plink_deactivate(sta); sta->plink_state = BLOCKED; spin_unlock_bh(&sta->plink_lock); } @@ -390,7 +404,7 @@ int mesh_plink_close(struct sta_info *sta) sta_info_put(sta); return 0; } else if (sta->plink_state == ESTAB) { - mesh_plink_deactivate(sta); + __mesh_plink_deactivate(sta); /* The timer should not be running */ if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) __sta_info_get(sta); @@ -699,7 +713,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, case CLS_ACPT: reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - mesh_plink_deactivate(sta); + __mesh_plink_deactivate(sta); sta->plink_state = HOLDING; llid = sta->llid; if (!mod_plink_timer(sta, diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 4a51647a41af..217c0f487bba 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -15,10 +15,7 @@ #include #include #include "ieee80211_rate.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif - #include "rc80211_pid.h" diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index cc4a896c617f..d0018fc40b09 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -20,9 +20,7 @@ #include "ieee80211_i.h" #include "ieee80211_led.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif #include "wep.h" #include "wpa.h" #include "tkip.h" @@ -439,6 +437,13 @@ ieee80211_rx_mesh_check(struct ieee80211_txrx_data *rx) else return RX_CONTINUE; } +#undef msh_h_get +#else +static inline ieee80211_rx_result +ieee80211_rx_mesh_check(struct ieee80211_txrx_data *rx) +{ + return RX_CONTINUE; +} #endif @@ -477,10 +482,8 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx) * responsible for filtering on both auth and assoc states. */ -#ifdef CONFIG_MAC80211_MESH - if (rx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) + if (ieee80211_vif_is_mesh(&rx->sdata->vif)) return ieee80211_rx_mesh_check(rx); -#endif if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA || ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL && @@ -1111,8 +1114,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx) hdrlen = ieee80211_get_hdrlen(fc); -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) { + if (ieee80211_vif_is_mesh(&sdata->vif)) { int meshhdrlen = ieee80211_get_mesh_hdrlen( (struct ieee80211s_hdr *) (skb->data + hdrlen)); /* Copy on cb: @@ -1126,7 +1128,6 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx) memcpy(MESH_PREQ(skb), hdr->addr2, ETH_ALEN); hdrlen += meshhdrlen; } -#endif /* convert IEEE 802.11 header + possible LLC headers into Ethernet * header @@ -1306,9 +1307,8 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx) } } -#ifdef CONFIG_MAC80211_MESH /* Mesh forwarding */ - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) { + if (ieee80211_vif_is_mesh(&sdata->vif)) { u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl; (*mesh_ttl)--; @@ -1321,12 +1321,13 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx) else xmit_skb->pkt_type = PACKET_OTHERHOST; } else - sdata->u.sta.mshstats.dropped_frames_ttl++; - + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta, + dropped_frames_ttl); } else if (skb->pkt_type != PACKET_OTHERHOST && compare_ether_addr(dev->dev_addr, skb->data) != 0) { if (*mesh_ttl == 0) { - sdata->u.sta.mshstats.dropped_frames_ttl++; + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta, + dropped_frames_ttl); dev_kfree_skb(skb); skb = NULL; } else { @@ -1337,7 +1338,6 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx) } } } -#endif if (skb) { /* deliver to local stack */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 1f3c9eb98500..81c4e3392f40 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -21,9 +21,7 @@ #include "ieee80211_rate.h" #include "sta_info.h" #include "debugfs_sta.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif /* Caller must hold local->sta_lock */ static void sta_info_hash_add(struct ieee80211_local *local, @@ -309,10 +307,8 @@ void sta_info_remove(struct sta_info *sta) } local->num_sta--; -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) + if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata->dev); -#endif } void sta_info_free(struct sta_info *sta) @@ -329,13 +325,8 @@ void sta_info_free(struct sta_info *sta) sta_info_remove(sta); write_unlock_bh(&local->sta_lock); -#ifdef CONFIG_MAC80211_MESH - if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) { - spin_lock_bh(&sta->plink_lock); + if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_plink_deactivate(sta); - spin_unlock_bh(&sta->plink_lock); - } -#endif while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { local->total_ps_buffered--; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 9d1d7a0e3114..4ad500373d5a 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -107,7 +107,6 @@ struct tid_ampdu_rx { struct timer_list session_timer; }; -#ifdef CONFIG_MAC80211_MESH enum plink_state { LISTEN, OPN_SNT, @@ -117,7 +116,6 @@ enum plink_state { HOLDING, BLOCKED }; -#endif /** * struct sta_ampdu_mlme - STA aggregation information. diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index fc1ffb55ed5c..3b06e0d8f35c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -26,9 +26,7 @@ #include "ieee80211_i.h" #include "ieee80211_led.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif #include "wep.h" #include "wpa.h" #include "wme.h" @@ -1460,7 +1458,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, - sdata); + sdata); } hdrlen = 30; break; @@ -1778,40 +1776,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, read_unlock_bh(&local->sta_lock); } -#ifdef CONFIG_MAC80211_MESH -static struct sk_buff *ieee80211_mesh_beacon_get(struct net_device *dev) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); - struct ieee80211_mgmt *mgmt; - u8 *pos; - - if (!skb) - return NULL; - skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 24 + sizeof(mgmt->u.beacon)); - memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); - mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, - IEEE80211_STYPE_BEACON); - memset(mgmt->da, 0xff, ETH_ALEN); - memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); - /* BSSID is left zeroed, wildcard value */ - mgmt->u.beacon.beacon_int = - cpu_to_le16(local->hw.conf.beacon_int); - mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */ - - pos = skb_put(skb, 2); - *pos++ = WLAN_EID_SSID; - *pos++ = 0x0; - - mesh_mgmt_ies_add(skb, dev); - - return skb; -} -#endif - - struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_tx_control *control) @@ -1824,8 +1788,10 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, struct rate_selection rsel; struct beacon_data *beacon; struct ieee80211_supported_band *sband; + struct ieee80211_mgmt *mgmt; int *num_beacons; - int err = 0; + bool err = true; + u8 *pos; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; @@ -1834,47 +1800,65 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, sdata = vif_to_sdata(vif); bdev = sdata->dev; - switch (sdata->vif.type) { - case IEEE80211_IF_TYPE_AP: + if (sdata->vif.type == IEEE80211_IF_TYPE_AP) { ap = &sdata->u.ap; beacon = rcu_dereference(ap->beacon); - if (!ap || !beacon) { - err = -1; - break; - } + if (ap && beacon) { + /* + * headroom, head length, + * tail length and maximum TIM length + */ + skb = dev_alloc_skb(local->tx_headroom + + beacon->head_len + + beacon->tail_len + 256); + if (!skb) + goto out; - /* headroom, head length, tail length and maximum TIM length */ - skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + - beacon->tail_len + 256); - if (!skb) - goto out; + skb_reserve(skb, local->tx_headroom); + memcpy(skb_put(skb, beacon->head_len), beacon->head, + beacon->head_len); - skb_reserve(skb, local->tx_headroom); - memcpy(skb_put(skb, beacon->head_len), beacon->head, - beacon->head_len); + ieee80211_include_sequence(sdata, + (struct ieee80211_hdr *)skb->data); - ieee80211_include_sequence(sdata, - (struct ieee80211_hdr *)skb->data); + ieee80211_beacon_add_tim(local, ap, skb, beacon); - ieee80211_beacon_add_tim(local, ap, skb, beacon); + if (beacon->tail) + memcpy(skb_put(skb, beacon->tail_len), + beacon->tail, beacon->tail_len); - if (beacon->tail) - memcpy(skb_put(skb, beacon->tail_len), beacon->tail, - beacon->tail_len); + num_beacons = &ap->num_beacons; - num_beacons = &ap->num_beacons; - break; + err = false; + } + } else if (ieee80211_vif_is_mesh(&sdata->vif)) { + /* headroom, head length, tail length and maximum TIM length */ + skb = dev_alloc_skb(local->tx_headroom + 400); + if (!skb) + goto out; + + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = (struct ieee80211_mgmt *) + skb_put(skb, 24 + sizeof(mgmt->u.beacon)); + memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_BEACON); + memset(mgmt->da, 0xff, ETH_ALEN); + memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); + /* BSSID is left zeroed, wildcard value */ + mgmt->u.beacon.beacon_int = + cpu_to_le16(local->hw.conf.beacon_int); + mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */ + + pos = skb_put(skb, 2); + *pos++ = WLAN_EID_SSID; + *pos++ = 0x0; + + mesh_mgmt_ies_add(skb, sdata->dev); -#ifdef CONFIG_MAC80211_MESH - case IEEE80211_IF_TYPE_MESH_POINT: - skb = ieee80211_mesh_beacon_get(bdev); num_beacons = &sdata->u.sta.num_beacons; - break; -#endif - default: - err = -1; - break; + err = false; } if (err) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6b50b6c12da3..b46496fa2e10 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -26,9 +26,7 @@ #include "ieee80211_i.h" #include "ieee80211_rate.h" -#ifdef CONFIG_MAC80211_MESH #include "mesh.h" -#endif #include "wme.h" /* privid for wiphys to determine whether they belong to us or not */ @@ -149,7 +147,6 @@ int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); -#ifdef CONFIG_MAC80211_MESH int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) { int ae = meshhdr->flags & IEEE80211S_FLAGS_AE; @@ -167,7 +164,6 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) return 5; } } -#endif void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx) { @@ -418,31 +414,3 @@ void ieee80211_iterate_active_interfaces( rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); - -#ifdef CONFIG_MAC80211_MESH -/** - * ieee80211_new_mesh_header - create a new mesh header - * @meshhdr: uninitialized mesh header - * @sdata: mesh interface to be used - * - * Return the header length. - */ -int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata) -{ - meshhdr->flags = 0; - meshhdr->ttl = sdata->u.sta.mshcfg.dot11MeshTTL; - - meshhdr->seqnum[0] = sdata->u.sta.mesh_seqnum[0]++; - meshhdr->seqnum[1] = sdata->u.sta.mesh_seqnum[1]; - meshhdr->seqnum[2] = sdata->u.sta.mesh_seqnum[2]; - - if (sdata->u.sta.mesh_seqnum[0] == 0) { - sdata->u.sta.mesh_seqnum[1]++; - if (sdata->u.sta.mesh_seqnum[1] == 0) - sdata->u.sta.mesh_seqnum[2]++; - } - - return 5; -} -#endif -- cgit v1.2.3-59-g8ed1b From d0709a65181beb787ef3f58cfe45536a2bb254c8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 25 Feb 2008 16:27:46 +0100 Subject: mac80211: RCU-ify STA info structure access This makes access to the STA hash table/list use RCU to protect against freeing of items. However, it's not a true RCU, the copy step is missing: whenever somebody changes a STA item it is simply updated. This is an existing race condition that is now somewhat understandable. This patch also fixes the race key freeing vs. STA destruction by making sure that sta_info_destroy() is always called under RTNL and frees the key. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945-rs.c | 31 ++- drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 27 +- net/mac80211/cfg.c | 110 ++++---- net/mac80211/debugfs_sta.c | 4 +- net/mac80211/debugfs_sta.h | 2 + net/mac80211/ieee80211.c | 78 ++++-- net/mac80211/ieee80211_i.h | 13 +- net/mac80211/ieee80211_iface.c | 11 +- net/mac80211/ieee80211_ioctl.c | 42 ++-- net/mac80211/ieee80211_rate.c | 8 +- net/mac80211/ieee80211_rate.h | 1 + net/mac80211/ieee80211_sta.c | 123 ++++++--- net/mac80211/key.c | 8 +- net/mac80211/mesh.c | 5 +- net/mac80211/mesh.h | 16 +- net/mac80211/mesh_hwmp.c | 6 +- net/mac80211/mesh_pathtbl.c | 30 ++- net/mac80211/mesh_plink.c | 101 ++++---- net/mac80211/rc80211_pid_algo.c | 25 +- net/mac80211/rc80211_simple.c | 18 +- net/mac80211/rx.c | 24 +- net/mac80211/sta_info.c | 387 ++++++++++++++++++----------- net/mac80211/sta_info.h | 61 +++-- net/mac80211/tx.c | 48 ++-- net/mac80211/wme.c | 8 +- 25 files changed, 701 insertions(+), 486 deletions(-) (limited to 'net/mac80211/mesh.c') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index a8223c4cc97c..c4bfba6f3c2b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -471,10 +471,11 @@ static void rs_tx_status(void *priv_rate, return; } + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); if (!sta || !sta->rate_ctrl_priv) { - if (sta) - sta_info_put(sta); + rcu_read_unlock(); IWL_DEBUG_RATE("leave: No STA priv data to update!\n"); return; } @@ -547,7 +548,7 @@ static void rs_tx_status(void *priv_rate, spin_unlock_irqrestore(&rs_sta->lock, flags); - sta_info_put(sta); + rcu_read_unlock(); IWL_DEBUG_RATE("leave\n"); @@ -658,6 +659,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, IWL_DEBUG_RATE("enter\n"); + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); /* Send management frames and broadcast/multicast data using lowest @@ -668,8 +671,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, !sta || !sta->rate_ctrl_priv) { IWL_DEBUG_RATE("leave: No STA priv data to update!\n"); sel->rate = rate_lowest(local, band, sta); - if (sta) - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -811,7 +813,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, else sta->txrate_idx = sta->last_txrate_idx; - sta_info_put(sta); + rcu_read_unlock(); IWL_DEBUG_RATE("leave: %d\n", index); @@ -843,13 +845,15 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) unsigned long now = jiffies; u32 max_time = 0; + rcu_read_lock(); + sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr); if (!sta || !sta->rate_ctrl_priv) { - if (sta) { - sta_info_put(sta); + if (sta) IWL_DEBUG_RATE("leave - no private rate data!\n"); - } else + else IWL_DEBUG_RATE("leave - no station!\n"); + rcu_read_unlock(); return sprintf(buf, "station %d not found\n", sta_id); } @@ -890,7 +894,7 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) i = j; } spin_unlock_irqrestore(&rs_sta->lock, flags); - sta_info_put(sta); + rcu_read_unlock(); /* Display the average rate of all samples taken. * @@ -927,11 +931,12 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) return; } + rcu_read_lock(); + sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr); if (!sta || !sta->rate_ctrl_priv) { - if (sta) - sta_info_put(sta); IWL_DEBUG_RATE("leave - no private rate data!\n"); + rcu_read_unlock(); return; } @@ -958,7 +963,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) break; } - sta_info_put(sta); + rcu_read_unlock(); spin_unlock_irqrestore(&rs_sta->lock, flags); rssi = priv->last_rx_rssi; diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c index 48a6a85355ec..46d85fd07faa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c @@ -847,12 +847,12 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev, if (retries > 15) retries = 15; + rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); if (!sta || !sta->rate_ctrl_priv) { - if (sta) - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -891,7 +891,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev, if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) { IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n", rs_index, tx_mcs.rate_n_flags); - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -909,7 +909,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev, IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n", tx_mcs.rate_n_flags, le32_to_cpu(table->rs_table[0].rate_n_flags)); - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -1025,7 +1025,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev, /* See if there's a better rate or modulation mode to try. */ rs_rate_scale_perform(priv, dev, hdr, sta); - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -2219,6 +2219,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n"); + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); /* Send management frames and broadcast/multicast data using lowest @@ -2227,8 +2229,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) || !sta || !sta->rate_ctrl_priv) { sel->rate = rate_lowest(local, sband, sta); - if (sta) - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -2261,7 +2262,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, sel->rate = rate_lowest(local, sband, sta); return; } - sta_info_put(sta); + rcu_read_unlock(); sel->rate = &priv->ieee_rates[i]; } @@ -2735,13 +2736,15 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) u32 max_time = 0; u8 lq_type, antenna; + rcu_read_lock(); + sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr); if (!sta || !sta->rate_ctrl_priv) { - if (sta) { - sta_info_put(sta); + if (sta) IWL_DEBUG_RATE("leave - no private rate data!\n"); - } else + else IWL_DEBUG_RATE("leave - no station!\n"); + rcu_read_unlock(); return sprintf(buf, "station %d not found\n", sta_id); } @@ -2808,7 +2811,7 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) "active_search %d rate index %d\n", lq_type, antenna, lq_sta->search_better_tbl, sta->last_txrate_idx); - sta_info_put(sta); + rcu_read_unlock(); return cnt; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6ac49231efa9..e9ba6fcc0e45 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -136,7 +136,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; struct sta_info *sta = NULL; enum ieee80211_key_alg alg; - int ret; struct ieee80211_key *key; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -170,12 +169,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, ieee80211_key_link(key, sdata, sta); - ret = 0; - - if (sta) - sta_info_put(sta); - - return ret; + return 0; } static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, @@ -184,7 +178,6 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; struct sta_info *sta; int ret; - struct ieee80211_key *key; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -195,21 +188,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, ret = 0; if (sta->key) { - key = sta->key; - ieee80211_key_free(key); + ieee80211_key_free(sta->key); WARN_ON(sta->key); } else ret = -ENOENT; - sta_info_put(sta); return ret; } if (!sdata->keys[key_idx]) return -ENOENT; - key = sdata->keys[key_idx]; - ieee80211_key_free(key); + ieee80211_key_free(sdata->keys[key_idx]); WARN_ON(sdata->keys[key_idx]); return 0; @@ -292,8 +282,6 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, err = 0; out: - if (sta) - sta_info_put(sta); return err; } @@ -311,7 +299,7 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + struct ieee80211_sub_if_data *sdata = sta->sdata; sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | @@ -340,16 +328,20 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; + int ret = -ENOENT; + + rcu_read_lock(); sta = sta_info_get_by_idx(local, idx, dev); - if (!sta) - return -ENOENT; + if (sta) { + ret = 0; + memcpy(mac, sta->addr, ETH_ALEN); + sta_set_sinfo(sta, sinfo); + } - memcpy(mac, sta->addr, ETH_ALEN); - sta_set_sinfo(sta, sinfo); - sta_info_put(sta); + rcu_read_unlock(); - return 0; + return ret; } static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, @@ -357,16 +349,21 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; + int ret = -ENOENT; - sta = sta_info_get(local, mac); - if (!sta) - return -ENOENT; + rcu_read_lock(); /* XXX: verify sta->dev == dev */ - sta_set_sinfo(sta, sinfo); - sta_info_put(sta); - return 0; + sta = sta_info_get(local, mac); + if (sta) { + ret = 0; + sta_set_sinfo(sta, sinfo); + } + + rcu_read_unlock(); + + return ret; } /* @@ -559,8 +556,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ - skb->dev = sta->dev; - skb->protocol = eth_type_trans(skb, sta->dev); + skb->dev = sta->sdata->dev; + skb->protocol = eth_type_trans(skb, sta->sdata->dev); memset(skb->cb, 0, sizeof(skb->cb)); netif_rx(skb); } @@ -572,7 +569,7 @@ static void sta_apply_parameters(struct ieee80211_local *local, u32 rates; int i, j; struct ieee80211_supported_band *sband; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + struct ieee80211_sub_if_data *sdata = sta->sdata; if (params->station_flags & STATION_FLAG_CHANGED) { sta->flags &= ~WLAN_STA_AUTHORIZED; @@ -644,14 +641,13 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (ieee80211_vif_is_mesh(&sdata->vif)) - sta = mesh_plink_add(mac, DEFAULT_RATES, dev); + sta = mesh_plink_add(mac, DEFAULT_RATES, sdata); else - sta = sta_info_add(local, dev, mac, GFP_KERNEL); + sta = sta_info_add(sdata, mac); if (IS_ERR(sta)) return PTR_ERR(sta); - sta->dev = sdata->dev; if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN || sdata->vif.type == IEEE80211_IF_TYPE_AP) ieee80211_send_layer2_update(sta); @@ -662,15 +658,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, rate_control_rate_init(sta, local); - sta_info_put(sta); - return 0; } static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct sta_info *sta; if (mac) { @@ -679,10 +674,14 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, if (!sta) return -ENOENT; - sta_info_free(sta); - sta_info_put(sta); + sta_info_unlink(&sta); + + if (sta) { + synchronize_rcu(); + sta_info_destroy(sta); + } } else - sta_info_flush(local, dev); + sta_info_flush(local, sdata); return 0; } @@ -701,21 +700,19 @@ static int ieee80211_change_station(struct wiphy *wiphy, if (!sta) return -ENOENT; - if (params->vlan && params->vlan != sta->dev) { + if (params->vlan && params->vlan != sta->sdata->dev) { vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN || vlansdata->vif.type != IEEE80211_IF_TYPE_AP) return -EINVAL; - sta->dev = params->vlan; + sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); ieee80211_send_layer2_update(sta); } sta_apply_parameters(local, sta, params); - sta_info_put(sta); - return 0; } @@ -735,23 +732,26 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) return -ENOTSUPP; + rcu_read_lock(); sta = sta_info_get(local, next_hop); - if (!sta) + if (!sta) { + rcu_read_unlock(); return -ENOENT; + } err = mesh_path_add(dst, dev); - if (err) + if (err) { + rcu_read_unlock(); return err; + } - rcu_read_lock(); mpath = mesh_path_lookup(dst, dev); if (!mpath) { rcu_read_unlock(); - sta_info_put(sta); return -ENXIO; } mesh_path_fix_nexthop(mpath, sta); - sta_info_put(sta); + rcu_read_unlock(); return 0; } @@ -760,7 +760,7 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, u8 *dst) { if (dst) - return mesh_path_del(dst, dev); + return mesh_path_del(dst, dev, false); mesh_path_flush(dev); return 0; @@ -781,20 +781,22 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) return -ENOTSUPP; + rcu_read_lock(); + sta = sta_info_get(local, next_hop); - if (!sta) + if (!sta) { + rcu_read_unlock(); return -ENOENT; + } - rcu_read_lock(); mpath = mesh_path_lookup(dst, dev); if (!mpath) { rcu_read_unlock(); - sta_info_put(sta); return -ENOENT; } mesh_path_fix_nexthop(mpath, sta); - sta_info_put(sta); + rcu_read_unlock(); return 0; } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index ed7c9f3b4602..73cfb4da464d 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -51,7 +51,7 @@ static const struct file_operations sta_ ##name## _ops = { \ STA_OPS(name) STA_FILE(aid, aid, D); -STA_FILE(dev, dev->name, S); +STA_FILE(dev, sdata->dev->name, S); STA_FILE(rx_packets, rx_packets, LU); STA_FILE(tx_packets, tx_packets, LU); STA_FILE(rx_bytes, rx_bytes, LU); @@ -200,7 +200,7 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; - struct net_device *dev = sta->dev; + struct net_device *dev = sta->sdata->dev; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hw *hw = &local->hw; u8 *da = sta->addr; diff --git a/net/mac80211/debugfs_sta.h b/net/mac80211/debugfs_sta.h index 574a1cd54b96..8b608903259f 100644 --- a/net/mac80211/debugfs_sta.h +++ b/net/mac80211/debugfs_sta.h @@ -1,6 +1,8 @@ #ifndef __MAC80211_DEBUGFS_STA_H #define __MAC80211_DEBUGFS_STA_H +#include "sta_info.h" + #ifdef CONFIG_MAC80211_DEBUGFS void ieee80211_sta_debugfs_add(struct sta_info *sta); void ieee80211_sta_debugfs_remove(struct sta_info *sta); diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 727af295c969..85b1391375c0 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -375,15 +375,19 @@ static int ieee80211_stop(struct net_device *dev) sdata = IEEE80211_DEV_TO_SUB_IF(dev); - list_for_each_entry(sta, &local->sta_list, list) { - if (sta->dev == dev) + rcu_read_lock(); + + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sta->sdata == sdata) for (i = 0; i < STA_TID_NUM; i++) - ieee80211_sta_stop_rx_ba_session(sta->dev, + ieee80211_sta_stop_rx_ba_session(sdata->dev, sta->addr, i, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_LEAVE_QBSS); } + rcu_read_unlock(); + netif_stop_queue(dev); /* @@ -449,7 +453,7 @@ static int ieee80211_stop(struct net_device *dev) netif_tx_unlock_bh(local->mdev); break; case IEEE80211_IF_TYPE_MESH_POINT: - sta_info_flush(local, dev); + sta_info_flush(local, sdata); /* fall through */ case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: @@ -522,9 +526,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) print_mac(mac, ra), tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ + rcu_read_lock(); + sta = sta_info_get(local, ra); if (!sta) { printk(KERN_DEBUG "Could not find the station\n"); + rcu_read_unlock(); return -ENOENT; } @@ -564,7 +571,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) spin_unlock_bh(&local->mdev->queue_lock); goto start_ba_exit; } - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the * call back right away, it must see that the flow has begun */ @@ -601,7 +608,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->ampdu_mlme.dialog_token_allocator; sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num; - ieee80211_send_addba_request(sta->dev, ra, tid, + ieee80211_send_addba_request(sta->sdata->dev, ra, tid, sta->ampdu_mlme.tid_tx[tid].dialog_token, sta->ampdu_mlme.tid_tx[tid].ssn, 0x40, 5000); @@ -614,7 +621,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) start_ba_exit: spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); return ret; } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); @@ -637,9 +644,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, print_mac(mac, ra), tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ + rcu_read_lock(); sta = sta_info_get(local, ra); - if (!sta) + if (!sta) { + rcu_read_unlock(); return -ENOENT; + } /* check if the TID is in aggregation */ state = &sta->ampdu_mlme.tid_tx[tid].state; @@ -673,7 +683,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, stop_BA_exit: spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); return ret; } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); @@ -691,8 +701,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) return; } + rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { + rcu_read_unlock(); printk(KERN_DEBUG "Could not find station: %s\n", print_mac(mac, ra)); return; @@ -705,7 +717,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", *state); spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -718,7 +730,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); } spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_start_tx_ba_cb); @@ -739,10 +751,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n", print_mac(mac, ra), tid); + rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { printk(KERN_DEBUG "Could not find station: %s\n", print_mac(mac, ra)); + rcu_read_unlock(); return; } state = &sta->ampdu_mlme.tid_tx[tid].state; @@ -750,13 +764,13 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); - sta_info_put(sta); spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); + rcu_read_unlock(); return; } if (*state & HT_AGG_STATE_INITIATOR_MSK) - ieee80211_send_delba(sta->dev, ra, tid, + ieee80211_send_delba(sta->sdata->dev, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); agg_queue = sta->tid_to_tx_q[tid]; @@ -777,7 +791,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0; spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb); @@ -887,32 +901,41 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr) struct sta_info *sta; DECLARE_MAC_BUF(mac); + might_sleep(); + if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0) return 0; + rcu_read_lock(); + /* Create STA entry for the new peer */ - sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL); - if (IS_ERR(sta)) + sta = sta_info_add(sdata, remote_addr); + if (IS_ERR(sta)) { + rcu_read_unlock(); return PTR_ERR(sta); + } sta->flags |= WLAN_STA_AUTHORIZED; - sta_info_put(sta); - /* Remove STA entry for the old peer */ sta = sta_info_get(local, sdata->u.wds.remote_addr); - if (sta) { - sta_info_free(sta); - sta_info_put(sta); - } else { + if (sta) + sta_info_unlink(&sta); + else printk(KERN_DEBUG "%s: could not find STA entry for WDS link " "peer %s\n", dev->name, print_mac(mac, sdata->u.wds.remote_addr)); - } /* Update WDS link data */ memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN); + rcu_read_unlock(); + + if (sta) { + synchronize_rcu(); + sta_info_destroy(sta); + } + return 0; } @@ -1330,6 +1353,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, return; } + rcu_read_lock(); + if (status->excessive_retries) { struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); @@ -1343,10 +1368,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; ieee80211_handle_filtered_frame(local, sta, skb, status); - sta_info_put(sta); + rcu_read_unlock(); return; } - sta_info_put(sta); } } @@ -1356,12 +1380,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, if (sta) { ieee80211_handle_filtered_frame(local, sta, skb, status); - sta_info_put(sta); + rcu_read_unlock(); return; } } else rate_control_tx_status(local->mdev, skb, status); + rcu_read_unlock(); + ieee80211_led_tx(local, 0); /* SNMP counters diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d3b5cc57af40..8e440c5706dd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -574,6 +574,7 @@ struct ieee80211_local { unsigned int filter_flags; /* FIF_* */ struct iw_statistics wstats; u8 wstats_flags; + bool tim_in_locked_section; /* see ieee80211_beacon_get() */ int tx_headroom; /* required headroom for hardware/radiotap */ enum { @@ -591,9 +592,15 @@ struct ieee80211_local { struct sk_buff_head skb_queue; struct sk_buff_head skb_queue_unreliable; - /* Station data structures */ - rwlock_t sta_lock; /* protects STA data structures */ - int num_sta; /* number of stations in sta_list */ + /* 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 and a few items + * in a STA info structure. + */ + spinlock_t sta_lock; + unsigned long num_sta; struct list_head sta_list; struct sta_info *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index b0f17a2b1a42..98b22736e883 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c @@ -240,16 +240,21 @@ void ieee80211_if_reinit(struct net_device *dev) break; } case IEEE80211_IF_TYPE_WDS: + rcu_read_lock(); sta = sta_info_get(local, sdata->u.wds.remote_addr); if (sta) { - sta_info_free(sta); - sta_info_put(sta); + sta_info_unlink(&sta); } else { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: Someone had deleted my STA " "entry for the WDS link\n", dev->name); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ } + rcu_read_unlock(); + if (sta) { + synchronize_rcu(); + sta_info_destroy(sta); + } break; case IEEE80211_IF_TYPE_MESH_POINT: case IEEE80211_IF_TYPE_STA: @@ -275,7 +280,7 @@ void ieee80211_if_reinit(struct net_device *dev) } /* remove all STAs that are bound to this virtual interface */ - sta_info_flush(local, dev); + sta_info_flush(local, sdata); memset(&sdata->u, 0, sizeof(sdata->u)); ieee80211_if_sdata_init(sdata); diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c index 38e2d83e15f4..5147152b9268 100644 --- a/net/mac80211/ieee80211_ioctl.c +++ b/net/mac80211/ieee80211_ioctl.c @@ -33,8 +33,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, size_t key_len) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - int ret; - struct sta_info *sta = NULL; + struct sta_info *sta; struct ieee80211_key *key; struct ieee80211_sub_if_data *sdata; @@ -51,24 +50,23 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, key = sdata->keys[idx]; } else { sta = sta_info_get(local, sta_addr); - if (!sta) { - ret = -ENOENT; - key = NULL; - goto err_out; - } - + if (!sta) + return -ENOENT; key = sta->key; } if (!key) - ret = -ENOENT; - else - ret = 0; + return -ENOENT; + + ieee80211_key_free(key); + return 0; } else { key = ieee80211_key_alloc(alg, idx, key_len, _key); if (!key) return -ENOMEM; + sta = NULL; + if (!is_broadcast_ether_addr(sta_addr)) { set_tx_key = 0; /* @@ -78,14 +76,14 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, * work around this. */ if (idx != 0 && alg != ALG_WEP) { - ret = -EINVAL; - goto err_out; + ieee80211_key_free(key); + return -EINVAL; } sta = sta_info_get(local, sta_addr); if (!sta) { - ret = -ENOENT; - goto err_out; + ieee80211_key_free(key); + return -ENOENT; } } @@ -93,18 +91,9 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, if (set_tx_key || (!sta && !sdata->default_key && key)) ieee80211_set_default_key(sdata, idx); - - /* don't free key later */ - key = NULL; - - ret = 0; } - err_out: - if (sta) - sta_info_put(sta); - ieee80211_key_free(key); - return ret; + return 0; } static int ieee80211_ioctl_siwgenie(struct net_device *dev, @@ -625,7 +614,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev, else rate->value = 0; rate->value *= 100000; - sta_info_put(sta); + return 0; } @@ -1000,7 +989,6 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev wstats->qual.qual = sta->last_signal; wstats->qual.noise = sta->last_noise; wstats->qual.updated = local->wstats_flags; - sta_info_put(sta); } return wstats; } diff --git a/net/mac80211/ieee80211_rate.c b/net/mac80211/ieee80211_rate.c index ebe29b716b27..4de06f128d90 100644 --- a/net/mac80211/ieee80211_rate.c +++ b/net/mac80211/ieee80211_rate.c @@ -170,9 +170,12 @@ void rate_control_get_rate(struct net_device *dev, struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct sta_info *sta = sta_info_get(local, hdr->addr1); + struct sta_info *sta; int i; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); + memset(sel, 0, sizeof(struct rate_selection)); ref->ops->get_rate(ref->priv, dev, sband, skb, sel); @@ -190,8 +193,7 @@ void rate_control_get_rate(struct net_device *dev, } } - if (sta) - sta_info_put(sta); + rcu_read_unlock(); } struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) diff --git a/net/mac80211/ieee80211_rate.h b/net/mac80211/ieee80211_rate.h index 5f9a2ca49a57..bfd0a1982e4a 100644 --- a/net/mac80211/ieee80211_rate.h +++ b/net/mac80211/ieee80211_rate.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" #include "sta_info.h" diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index 9f933aeca719..a3e96eb59eb0 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -845,6 +846,8 @@ static void ieee80211_associated(struct net_device *dev, ifsta->state = IEEE80211_ASSOCIATED; + rcu_read_lock(); + sta = sta_info_get(local, ifsta->bssid); if (!sta) { printk(KERN_DEBUG "%s: No STA entry for own AP %s\n", @@ -860,7 +863,7 @@ static void ieee80211_associated(struct net_device *dev, "range\n", dev->name, print_mac(mac, ifsta->bssid)); disassoc = 1; - sta_info_free(sta); + sta_info_unlink(&sta); } else ieee80211_send_probe_req(dev, ifsta->bssid, local->scan_ssid, @@ -876,8 +879,17 @@ static void ieee80211_associated(struct net_device *dev, ifsta->ssid_len); } } - sta_info_put(sta); } + + rcu_read_unlock(); + + if (disassoc && sta) { + synchronize_rcu(); + rtnl_lock(); + sta_info_destroy(sta); + rtnl_unlock(); + } + if (disassoc) { ifsta->state = IEEE80211_DISABLED; ieee80211_set_associated(dev, ifsta, 0); @@ -1103,9 +1115,13 @@ static void ieee80211_sta_process_addba_request(struct net_device *dev, int ret = -EOPNOTSUPP; DECLARE_MAC_BUF(mac); + rcu_read_lock(); + sta = sta_info_get(local, mgmt->sa); - if (!sta) + if (!sta) { + rcu_read_unlock(); return; + } /* extract session parameters from addba request frame */ dialog_token = mgmt->u.action.u.addba_req.dialog_token; @@ -1197,9 +1213,9 @@ end: spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); end_no_lock: - ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token, - status, 1, buf_size, timeout); - sta_info_put(sta); + ieee80211_send_addba_resp(sta->sdata->dev, sta->addr, tid, + dialog_token, status, 1, buf_size, timeout); + rcu_read_unlock(); } static void ieee80211_sta_process_addba_resp(struct net_device *dev, @@ -1213,9 +1229,13 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev, u16 tid; u8 *state; + rcu_read_lock(); + sta = sta_info_get(local, mgmt->sa); - if (!sta) + if (!sta) { + rcu_read_unlock(); return; + } capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; @@ -1230,7 +1250,7 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev, #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -1244,7 +1264,7 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev, spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); printk(KERN_DEBUG "state not HT_ADDBA_REQUESTED_MSK:" "%d\n", *state); - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -1271,7 +1291,7 @@ static void ieee80211_sta_process_addba_resp(struct net_device *dev, ieee80211_stop_tx_ba_session(hw, sta->addr, tid, WLAN_BACK_INITIATOR); } - sta_info_put(sta); + rcu_read_unlock(); } void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, @@ -1326,16 +1346,20 @@ void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid, struct sta_info *sta; int ret, i; + rcu_read_lock(); + sta = sta_info_get(local, ra); - if (!sta) + if (!sta) { + rcu_read_unlock(); return; + } /* check if TID is in operational state */ spin_lock_bh(&sta->ampdu_mlme.ampdu_rx); if (sta->ampdu_mlme.tid_rx[tid].state != HT_AGG_STATE_OPERATIONAL) { spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx); - sta_info_put(sta); + rcu_read_unlock(); return; } sta->ampdu_mlme.tid_rx[tid].state = @@ -1374,7 +1398,7 @@ void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid, kfree(sta->ampdu_mlme.tid_rx[tid].reorder_buf); sta->ampdu_mlme.tid_rx[tid].state = HT_AGG_STATE_IDLE; - sta_info_put(sta); + rcu_read_unlock(); } @@ -1387,9 +1411,13 @@ static void ieee80211_sta_process_delba(struct net_device *dev, u16 initiator; DECLARE_MAC_BUF(mac); + rcu_read_lock(); + sta = sta_info_get(local, mgmt->sa); - if (!sta) + if (!sta) { + rcu_read_unlock(); return; + } params = le16_to_cpu(mgmt->u.action.u.delba.params); tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; @@ -1414,7 +1442,7 @@ static void ieee80211_sta_process_delba(struct net_device *dev, ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid, WLAN_BACK_RECIPIENT); } - sta_info_put(sta); + rcu_read_unlock(); } /* @@ -1437,9 +1465,13 @@ void sta_addba_resp_timer_expired(unsigned long data) struct sta_info *sta; u8 *state; + rcu_read_lock(); + sta = sta_info_get(local, temp_sta->addr); - if (!sta) + if (!sta) { + rcu_read_unlock(); return; + } state = &sta->ampdu_mlme.tid_tx[tid].state; /* check if the TID waits for addBA response */ @@ -1461,7 +1493,7 @@ void sta_addba_resp_timer_expired(unsigned long data) WLAN_BACK_INITIATOR); timer_expired_exit: - sta_info_put(sta); + rcu_read_unlock(); } /* @@ -1481,8 +1513,8 @@ void sta_rx_agg_session_timer_expired(unsigned long data) timer_to_tid[0]); printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); - ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, (u16)*ptid, - WLAN_BACK_TIMER, + ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr, + (u16)*ptid, WLAN_BACK_TIMER, WLAN_REASON_QSTA_TIMEOUT); } @@ -1791,14 +1823,18 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (ifsta->assocresp_ies) memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len); + rcu_read_lock(); + /* Add STA entry for the AP */ sta = sta_info_get(local, ifsta->bssid); if (!sta) { struct ieee80211_sta_bss *bss; - sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL); + + sta = sta_info_add(sdata, ifsta->bssid); if (IS_ERR(sta)) { printk(KERN_DEBUG "%s: failed to add STA entry for the" " AP (error %ld)\n", dev->name, PTR_ERR(sta)); + rcu_read_unlock(); return; } bss = ieee80211_rx_bss_get(dev, ifsta->bssid, @@ -1812,7 +1848,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, } } - sta->dev = dev; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP | WLAN_STA_AUTHORIZED; @@ -1883,7 +1918,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, bss_conf->aid = aid; ieee80211_set_associated(dev, ifsta, 1); - sta_info_put(sta); + rcu_read_unlock(); ieee80211_associated(dev, ifsta); } @@ -2329,6 +2364,8 @@ static void ieee80211_rx_bss_info(struct net_device *dev, mesh_peer_accepts_plinks(&elems, dev)); } + rcu_read_lock(); + if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates && memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 && (sta = sta_info_get(local, mgmt->sa))) { @@ -2354,9 +2391,10 @@ static void ieee80211_rx_bss_info(struct net_device *dev, (unsigned long long) supp_rates, (unsigned long long) sta->supp_rates[rx_status->band]); } - sta_info_put(sta); } + rcu_read_unlock(); + if (elems.ds_params && elems.ds_params_len == 1) freq = ieee80211_channel_to_frequency(elems.ds_params[0]); else @@ -2550,8 +2588,10 @@ static void ieee80211_rx_bss_info(struct net_device *dev, "local TSF - IBSS merge with BSSID %s\n", dev->name, print_mac(mac, mgmt->bssid)); ieee80211_sta_join_ibss(dev, &sdata->u.sta, bss); + rcu_read_lock(); ieee80211_ibss_add_sta(dev, NULL, mgmt->bssid, mgmt->sa); + rcu_read_unlock(); } } @@ -2893,17 +2933,20 @@ static int ieee80211_sta_active_ibss(struct net_device *dev) struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); int active = 0; struct sta_info *sta; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - read_lock_bh(&local->sta_lock); - list_for_each_entry(sta, &local->sta_list, list) { - if (sta->dev == dev && + rcu_read_lock(); + + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sta->sdata == sdata && time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, jiffies)) { active++; break; } } - read_unlock_bh(&local->sta_lock); + + rcu_read_unlock(); return active; } @@ -2915,22 +2958,25 @@ static void ieee80211_sta_expire(struct net_device *dev, unsigned long exp_time) struct sta_info *sta, *tmp; LIST_HEAD(tmp_list); DECLARE_MAC_BUF(mac); + unsigned long flags; - write_lock_bh(&local->sta_lock); + spin_lock_irqsave(&local->sta_lock, flags); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) if (time_after(jiffies, sta->last_rx + exp_time)) { printk(KERN_DEBUG "%s: expiring inactive STA %s\n", dev->name, print_mac(mac, sta->addr)); - __sta_info_get(sta); - sta_info_remove(sta); - list_add(&sta->list, &tmp_list); + sta_info_unlink(&sta); + if (sta) + list_add(&sta->list, &tmp_list); } - write_unlock_bh(&local->sta_lock); + spin_unlock_irqrestore(&local->sta_lock, flags); - list_for_each_entry_safe(sta, tmp, &tmp_list, list) { - sta_info_free(sta); - sta_info_put(sta); - } + synchronize_rcu(); + + rtnl_lock(); + list_for_each_entry_safe(sta, tmp, &tmp_list, list) + sta_info_destroy(sta); + rtnl_unlock(); } @@ -3977,6 +4023,7 @@ int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len) } +/* must be called under RCU read lock */ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, struct sk_buff *skb, u8 *bssid, u8 *addr) @@ -3999,7 +4046,7 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n", wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name); - sta = sta_info_add(local, dev, addr, GFP_ATOMIC); + sta = sta_info_add(sdata, addr); if (IS_ERR(sta)) return NULL; @@ -4010,7 +4057,7 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, rate_control_rate_init(sta, local); - return sta; /* caller will call sta_info_put() */ + return sta; } diff --git a/net/mac80211/key.c b/net/mac80211/key.c index eac9c59dbc4d..df0c04cedbe4 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -240,14 +240,17 @@ void ieee80211_key_link(struct ieee80211_key *key, if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { struct sta_info *ap; + rcu_read_lock(); + /* same here, the AP could be using QoS */ ap = sta_info_get(key->local, key->sdata->u.sta.bssid); if (ap) { if (ap->flags & WLAN_STA_WME) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; - sta_info_put(ap); } + + rcu_read_unlock(); } } @@ -290,6 +293,9 @@ void ieee80211_key_free(struct ieee80211_key *key) __ieee80211_key_replace(key->sdata, key->sta, key, NULL); + /* + * Do NOT remove this without looking at sta_info_destroy() + */ synchronize_rcu(); /* diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index ebe1a7a80bad..9de1ccc11cf9 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -83,11 +83,10 @@ bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie, /** * mesh_accept_plinks_update: update accepting_plink in local mesh beacons * - * @dev: mesh interface in which mesh beacons are going to be updated + * @sdata: mesh interface in which mesh beacons are going to be updated */ -void mesh_accept_plinks_update(struct net_device *dev) +void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); bool free_plinks; /* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index d565b3fb9e6a..576eee83d859 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -65,9 +65,10 @@ enum mesh_path_flags { * @state_lock: mesh pat state lock * * - * The combination of dst and dev is unique in the mesh path table. A reference - * to the next_hop sta will be kept and in case this sta is removed, the - * mesh_path structure must be also removed or substitued in a rcu safe way + * The combination of dst and dev is unique in the mesh path table. Since the + * next_hop STA is only protected by RCU as well, deleting the STA must also + * remove/substitute the mesh_path structure and wait until that is no longer + * reachable before destroying the STA completely. */ struct mesh_path { u8 dst[ETH_ALEN]; @@ -230,8 +231,9 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, bool add); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie, struct net_device *dev); -void mesh_accept_plinks_update(struct net_device *dev); -struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev); +void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); +struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, + struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); void mesh_plink_deactivate(struct sta_info *sta); int mesh_plink_open(struct sta_info *sta); @@ -254,7 +256,7 @@ void mesh_path_flush_pending(struct mesh_path *mpath); void mesh_path_tx_pending(struct mesh_path *mpath); int mesh_pathtbl_init(void); void mesh_pathtbl_unregister(void); -int mesh_path_del(u8 *addr, struct net_device *dev); +int mesh_path_del(u8 *addr, struct net_device *dev, bool force); void mesh_path_timer(unsigned long data); void mesh_path_flush_by_nexthop(struct sta_info *sta); void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev); @@ -270,7 +272,7 @@ static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata) static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata) { - return (min(mesh_plink_free_count(sdata), + return (min_t(long, mesh_plink_free_count(sdata), MESH_MAX_PLINKS - sdata->local->num_sta)) > 0; } diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index c2f40ef418cf..d8530fe7a0b3 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -294,7 +294,6 @@ static u32 hwmp_route_info_get(struct net_device *dev, orig_metric = PREP_IE_METRIC(hwmp_ie); break; default: - sta_info_put(sta); rcu_read_unlock(); return 0; } @@ -330,7 +329,6 @@ static u32 hwmp_route_info_get(struct net_device *dev, mpath = mesh_path_lookup(orig_addr, dev); if (!mpath) { rcu_read_unlock(); - sta_info_put(sta); return 0; } spin_lock_bh(&mpath->state_lock); @@ -372,7 +370,6 @@ static u32 hwmp_route_info_get(struct net_device *dev, mpath = mesh_path_lookup(ta, dev); if (!mpath) { rcu_read_unlock(); - sta_info_put(sta); return 0; } spin_lock_bh(&mpath->state_lock); @@ -391,7 +388,6 @@ static u32 hwmp_route_info_get(struct net_device *dev, spin_unlock_bh(&mpath->state_lock); } - sta_info_put(sta); rcu_read_unlock(); return process ? new_metric : 0; @@ -861,5 +857,5 @@ void mesh_path_timer(unsigned long data) endmpathtimer: rcu_read_unlock(); if (delete) - mesh_path_del(mpath->dst, mpath->dev); + mesh_path_del(mpath->dst, mpath->dev, false); } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 3cbdbb23d75a..a17f2b299045 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -55,10 +55,7 @@ static DEFINE_RWLOCK(pathtbl_resize_lock); */ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) { - __sta_info_get(sta); - if (mpath->next_hop) - sta_info_put(mpath->next_hop); - mpath->next_hop = sta; + rcu_assign_pointer(mpath->next_hop, sta); } @@ -236,7 +233,7 @@ void mesh_plink_broken(struct sta_info *sta) struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; - struct net_device *dev = sta->dev; + struct net_device *dev = sta->sdata->dev; int i; rcu_read_lock(); @@ -266,9 +263,9 @@ EXPORT_SYMBOL(mesh_plink_broken); * * RCU notes: this function is called when a mesh plink transitions from ESTAB * to any other state, since ESTAB state is the only one that allows path - * creation. This will happen before the sta can be freed (since we hold - * a reference to it) so any reader in a rcu read block will be protected - * against the plink dissapearing. + * creation. This will happen before the sta can be freed (because + * sta_info_destroy() calls this) so any reader in a rcu read block will be + * protected against the plink disappearing. */ void mesh_path_flush_by_nexthop(struct sta_info *sta) { @@ -280,7 +277,7 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta) for_each_mesh_entry(mesh_paths, p, node, i) { mpath = node->mpath; if (mpath->next_hop == sta) - mesh_path_del(mpath->dst, mpath->dev); + mesh_path_del(mpath->dst, mpath->dev, true); } } @@ -294,7 +291,7 @@ void mesh_path_flush(struct net_device *dev) for_each_mesh_entry(mesh_paths, p, node, i) { mpath = node->mpath; if (mpath->dev == dev) - mesh_path_del(mpath->dst, mpath->dev); + mesh_path_del(mpath->dst, mpath->dev, false); } } @@ -303,8 +300,8 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) struct mpath_node *node = container_of(rp, struct mpath_node, rcu); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(node->mpath->dev); - if (node->mpath->next_hop) - sta_info_put(node->mpath->next_hop); + + rcu_assign_pointer(node->mpath->next_hop, NULL); atomic_dec(&sdata->u.sta.mpaths); kfree(node->mpath); kfree(node); @@ -319,9 +316,10 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) * Returns: 0 if succesful * * State: if the path is being resolved, the deletion will be postponed until - * the path resolution completes or times out. + * the path resolution completes or times out, unless the force parameter + * is given. */ -int mesh_path_del(u8 *addr, struct net_device *dev) +int mesh_path_del(u8 *addr, struct net_device *dev, bool force) { struct mesh_path *mpath; struct mpath_node *node; @@ -340,7 +338,7 @@ int mesh_path_del(u8 *addr, struct net_device *dev) if (mpath->dev == dev && memcmp(addr, mpath->dst, ETH_ALEN) == 0) { spin_lock_bh(&mpath->state_lock); - if (mpath->flags & MESH_PATH_RESOLVING) { + if (!force && mpath->flags & MESH_PATH_RESOLVING) { mpath->flags |= MESH_PATH_DELETE; } else { mpath->flags |= MESH_PATH_RESOLVING; @@ -510,7 +508,7 @@ void mesh_path_expire(struct net_device *dev) time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) { spin_unlock_bh(&mpath->state_lock); - mesh_path_del(mpath->dst, mpath->dev); + mesh_path_del(mpath->dst, mpath->dev, false); } else spin_unlock_bh(&mpath->state_lock); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index b5fbe970e48f..c2b80500ae72 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -65,14 +65,14 @@ static inline void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) { atomic_inc(&sdata->u.sta.mshstats.estab_plinks); - mesh_accept_plinks_update(sdata->dev); + mesh_accept_plinks_update(sdata); } static inline void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) { atomic_dec(&sdata->u.sta.mshstats.estab_plinks); - mesh_accept_plinks_update(sdata->dev); + mesh_accept_plinks_update(sdata); } /** @@ -99,12 +99,13 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) * * Returns: non-NULL on success, ERR_PTR() on error. */ -struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) +struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, + struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_local *local = sdata->local; struct sta_info *sta; - if (memcmp(hw_addr, dev->dev_addr, ETH_ALEN) == 0) + if (compare_ether_addr(hw_addr, sdata->dev->dev_addr) == 0) /* never add ourselves as neighbours */ return ERR_PTR(-EINVAL); @@ -114,7 +115,7 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) if (local->num_sta >= MESH_MAX_PLINKS) return ERR_PTR(-ENOSPC); - sta = sta_info_add(local, dev, hw_addr, GFP_KERNEL); + sta = sta_info_add(sdata, hw_addr); if (IS_ERR(sta)) return sta; @@ -125,7 +126,7 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) sta->supp_rates[local->hw.conf.channel->band] = rates; rate_control_rate_init(sta, local); - mesh_accept_plinks_update(dev); + mesh_accept_plinks_update(sdata); return sta; } @@ -141,7 +142,8 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) */ static void __mesh_plink_deactivate(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + struct ieee80211_sub_if_data *sdata = sta->sdata; + if (sta->plink_state == ESTAB) mesh_plink_dec_estab_count(sdata); sta->plink_state = BLOCKED; @@ -246,11 +248,15 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; + rcu_read_lock(); + sta = sta_info_get(local, hw_addr); if (!sta) { - sta = mesh_plink_add(hw_addr, rates, dev); - if (IS_ERR(sta)) + sta = mesh_plink_add(hw_addr, rates, sdata); + if (IS_ERR(sta)) { + rcu_read_unlock(); return; + } } sta->last_rx = jiffies; @@ -260,7 +266,7 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, sdata->u.sta.mshcfg.auto_open_plinks) mesh_plink_open(sta); - sta_info_put(sta); + rcu_read_unlock(); } static void mesh_plink_timer(unsigned long data) @@ -273,6 +279,11 @@ static void mesh_plink_timer(unsigned long data) DECLARE_MAC_BUF(mac); #endif + /* + * This STA is valid because sta_info_destroy() will + * del_timer_sync() this timer after having made sure + * it cannot be readded (by deleting the plink.) + */ sta = (struct sta_info *) data; spin_lock_bh(&sta->plink_lock); @@ -286,8 +297,8 @@ static void mesh_plink_timer(unsigned long data) reason = 0; llid = sta->llid; plid = sta->plid; - dev = sta->dev; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + sdata = sta->sdata; + dev = sdata->dev; switch (sta->plink_state) { case OPN_RCVD: @@ -302,8 +313,7 @@ static void mesh_plink_timer(unsigned long data) sta->plink_timeout = sta->plink_timeout + rand % sta->plink_timeout; ++sta->plink_retries; - if (!mod_plink_timer(sta, sta->plink_timeout)) - __sta_info_get(sta); + mod_plink_timer(sta, sta->plink_timeout); spin_unlock_bh(&sta->plink_lock); mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid, 0, 0); @@ -316,16 +326,14 @@ static void mesh_plink_timer(unsigned long data) if (!reason) reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); sta->plink_state = HOLDING; - if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) - __sta_info_get(sta); + mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->plink_lock); mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid, reason); break; case HOLDING: /* holding timer */ - if (del_timer(&sta->plink_timer)) - sta_info_put(sta); + del_timer(&sta->plink_timer); mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->plink_lock); break; @@ -333,8 +341,6 @@ static void mesh_plink_timer(unsigned long data) spin_unlock_bh(&sta->plink_lock); break; } - - sta_info_put(sta); } static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) @@ -343,14 +349,13 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) sta->plink_timer.data = (unsigned long) sta; sta->plink_timer.function = mesh_plink_timer; sta->plink_timeout = timeout; - __sta_info_get(sta); add_timer(&sta->plink_timer); } int mesh_plink_open(struct sta_info *sta) { __le16 llid; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + struct ieee80211_sub_if_data *sdata = sta->sdata; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG DECLARE_MAC_BUF(mac); #endif @@ -360,7 +365,6 @@ int mesh_plink_open(struct sta_info *sta) sta->llid = llid; if (sta->plink_state != LISTEN) { spin_unlock_bh(&sta->plink_lock); - sta_info_put(sta); return -EBUSY; } sta->plink_state = OPN_SNT; @@ -369,7 +373,8 @@ int mesh_plink_open(struct sta_info *sta) mpl_dbg("Mesh plink: starting establishment with %s\n", print_mac(mac, sta->addr)); - return mesh_plink_frame_tx(sta->dev, PLINK_OPEN, sta->addr, llid, 0, 0); + return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN, + sta->addr, llid, 0, 0); } void mesh_plink_block(struct sta_info *sta) @@ -386,7 +391,7 @@ void mesh_plink_block(struct sta_info *sta) int mesh_plink_close(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + struct ieee80211_sub_if_data *sdata = sta->sdata; int llid, plid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG DECLARE_MAC_BUF(mac); @@ -401,13 +406,11 @@ int mesh_plink_close(struct sta_info *sta) if (sta->plink_state == LISTEN || sta->plink_state == BLOCKED) { mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->plink_lock); - sta_info_put(sta); return 0; } else if (sta->plink_state == ESTAB) { __mesh_plink_deactivate(sta); /* The timer should not be running */ - if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) - __sta_info_get(sta); + mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -415,15 +418,16 @@ int mesh_plink_close(struct sta_info *sta) llid = sta->llid; plid = sta->plid; spin_unlock_bh(&sta->plink_lock); - mesh_plink_frame_tx(sta->dev, PLINK_CLOSE, sta->addr, llid, plid, - reason); + mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid, + plid, reason); return 0; } void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct ieee802_11_elems elems; struct sta_info *sta; enum plink_event event; @@ -435,7 +439,6 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG DECLARE_MAC_BUF(mac); #endif - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (is_multicast_ether_addr(mgmt->da)) { mpl_dbg("Mesh plink: ignore frame from multicast address"); @@ -474,14 +477,17 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7)) memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); + rcu_read_lock(); + sta = sta_info_get(local, mgmt->sa); if (!sta && ftype != PLINK_OPEN) { mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); + rcu_read_unlock(); return; } if (sta && sta->plink_state == BLOCKED) { - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -505,13 +511,15 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, u64 rates; 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_add(mgmt->sa, rates, dev); + sta = mesh_plink_add(mgmt->sa, rates, sdata); if (IS_ERR(sta)) { mpl_dbg("Mesh plink error: plink table full\n"); + rcu_read_unlock(); return; } event = OPN_ACPT; @@ -521,14 +529,14 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, switch (ftype) { case PLINK_OPEN: if (!mesh_plink_free_count(sdata) || - (sta->plid && sta->plid != plid)) + (sta->plid && sta->plid != plid)) event = OPN_IGNR; else event = OPN_ACPT; break; case PLINK_CONFIRM: if (!mesh_plink_free_count(sdata) || - (sta->llid != llid || sta->plid != plid)) + (sta->llid != llid || sta->plid != plid)) event = CNF_IGNR; else event = CNF_ACPT; @@ -555,7 +563,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, default: mpl_dbg("Mesh plink: unknown frame subtype\n"); spin_unlock_bh(&sta->plink_lock); - sta_info_put(sta); + rcu_read_unlock(); return; } } @@ -659,8 +667,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, plid, 0); break; case CNF_ACPT: - if (del_timer(&sta->plink_timer)) - sta_info_put(sta); + del_timer(&sta->plink_timer); sta->plink_state = ESTAB; mesh_plink_inc_estab_count(sdata); spin_unlock_bh(&sta->plink_lock); @@ -693,8 +700,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, plid, reason); break; case OPN_ACPT: - if (del_timer(&sta->plink_timer)) - sta_info_put(sta); + del_timer(&sta->plink_timer); sta->plink_state = ESTAB; mesh_plink_inc_estab_count(sdata); spin_unlock_bh(&sta->plink_lock); @@ -717,9 +723,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, __mesh_plink_deactivate(sta); sta->plink_state = HOLDING; llid = sta->llid; - if (!mod_plink_timer(sta, - dot11MeshHoldingTimeout(sdata))) - __sta_info_get(sta); + mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->plink_lock); mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid, reason); @@ -738,10 +742,8 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, case HOLDING: switch (event) { case CLS_ACPT: - if (del_timer(&sta->plink_timer)) { + if (del_timer(&sta->plink_timer)) sta->ignore_plink_timer = 1; - sta_info_put(sta); - } mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->plink_lock); break; @@ -766,5 +768,6 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, spin_unlock_bh(&sta->plink_lock); break; } - sta_info_put(sta); + + rcu_read_unlock(); } diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 217c0f487bba..a1993161de99 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -77,7 +77,7 @@ static void rate_control_pid_adjust_rate(struct ieee80211_local *local, int cur_sorted, new_sorted, probe, tmp, n_bitrates, band; int cur = sta->txrate_idx; - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; band = sband->band; n_bitrates = sband->n_bitrates; @@ -149,7 +149,7 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo, struct sta_info *sta) { #ifdef CONFIG_MAC80211_MESH - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + struct ieee80211_sub_if_data *sdata = sta->sdata; #endif struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv; struct rc_pid_rateinfo *rinfo = pinfo->rinfo; @@ -249,23 +249,25 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev, unsigned long period; struct ieee80211_supported_band *sband; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; if (!sta) - return; + goto unlock; /* Don't update the state if we're not controlling the rate. */ - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { sta->txrate_idx = sdata->bss->max_ratectrl_rateidx; - return; + goto unlock; } /* Ignore all frames that were sent with a different rate than the rate * we currently advise mac80211 to use. */ if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx]) - goto ignore; + goto unlock; spinfo = sta->rate_ctrl_priv; spinfo->tx_num_xmit++; @@ -303,8 +305,8 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev, if (time_after(jiffies, spinfo->last_sample + period)) rate_control_pid_sample(pinfo, local, sta); -ignore: - sta_info_put(sta); + unlock: + rcu_read_unlock(); } static void rate_control_pid_get_rate(void *priv, struct net_device *dev, @@ -319,6 +321,8 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev, int rateidx; u16 fc; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); /* Send management frames and broadcast/multicast data using lowest @@ -327,8 +331,7 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev, if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || is_multicast_ether_addr(hdr->addr1) || !sta) { sel->rate = rate_lowest(local, sband, sta); - if (sta) - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -344,7 +347,7 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev, sta->last_txrate_idx = rateidx; - sta_info_put(sta); + rcu_read_unlock(); sel->rate = &sband->bitrates[rateidx]; diff --git a/net/mac80211/rc80211_simple.c b/net/mac80211/rc80211_simple.c index bcc541d4b95c..4f72fdca7f12 100644 --- a/net/mac80211/rc80211_simple.c +++ b/net/mac80211/rc80211_simple.c @@ -40,7 +40,7 @@ static void rate_control_rate_inc(struct ieee80211_local *local, int i = sta->txrate_idx; int maxrate; - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { /* forced unicast rate - do not change STA rate */ return; @@ -70,7 +70,7 @@ static void rate_control_rate_dec(struct ieee80211_local *local, struct ieee80211_supported_band *sband; int i = sta->txrate_idx; - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { /* forced unicast rate - do not change STA rate */ return; @@ -118,10 +118,12 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev, struct sta_info *sta; struct sta_rate_control *srctrl; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); if (!sta) - return; + goto unlock; srctrl = sta->rate_ctrl_priv; srctrl->tx_num_xmit++; @@ -191,7 +193,8 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev, } } - sta_info_put(sta); + unlock: + rcu_read_unlock(); } @@ -208,6 +211,8 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev, int rateidx; u16 fc; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); /* Send management frames and broadcast/multicast data using lowest @@ -216,8 +221,7 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev, if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || is_multicast_ether_addr(hdr->addr1) || !sta) { sel->rate = rate_lowest(local, sband, sta); - if (sta) - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -233,7 +237,7 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev, sta->last_txrate_idx = rateidx; - sta_info_put(sta); + rcu_read_unlock(); sel->rate = &sband->bitrates[rateidx]; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 2e65ca1cd1aa..8e1e2859bfd5 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -631,7 +631,7 @@ static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta) struct ieee80211_sub_if_data *sdata; DECLARE_MAC_BUF(mac); - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; if (sdata->bss) atomic_inc(&sdata->bss->num_sta_ps); @@ -652,7 +652,7 @@ static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta) struct ieee80211_tx_packet_data *pkt_data; DECLARE_MAC_BUF(mac); - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; if (sdata->bss) atomic_dec(&sdata->bss->num_sta_ps); @@ -1287,7 +1287,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) "multicast frame\n", dev->name); } else { dsta = sta_info_get(local, skb->data); - if (dsta && dsta->dev == dev) { + if (dsta && dsta->sdata->dev == dev) { /* * The destination station is associated to * this AP (in this VLAN), so send the frame @@ -1297,8 +1297,6 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) xmit_skb = skb; skb = NULL; } - if (dsta) - sta_info_put(dsta); } } @@ -1905,13 +1903,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, rx.sta = sta_info_get(local, hdr->addr2); if (rx.sta) { - rx.dev = rx.sta->dev; - rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev); + rx.sdata = rx.sta->sdata; + rx.dev = rx.sta->sdata->dev; } if ((status->flag & RX_FLAG_MMIC_ERROR)) { ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx); - goto end; + return; } if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning)) @@ -1970,10 +1968,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ieee80211_invoke_rx_handlers(prev, &rx, skb); } else dev_kfree_skb(skb); - - end: - if (rx.sta) - sta_info_put(rx.sta); } #define SEQ_MODULO 0x1000 @@ -2150,7 +2144,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, /* if this mpdu is fragmented - terminate rx aggregation session */ sc = le16_to_cpu(hdr->seq_ctrl); if (sc & IEEE80211_SCTL_FRAG) { - ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, + ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr, tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP); ret = 1; goto end_reorder; @@ -2160,9 +2154,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, mpdu_seq_num, 0); -end_reorder: - if (sta) - sta_info_put(sta); + end_reorder: return ret; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 81c4e3392f40..ee5b66abc0f1 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" @@ -23,14 +24,43 @@ #include "debugfs_sta.h" #include "mesh.h" -/* Caller must hold local->sta_lock */ -static void sta_info_hash_add(struct ieee80211_local *local, - struct sta_info *sta) -{ - sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; - local->sta_hash[STA_HASH(sta->addr)] = sta; -} - +/** + * DOC: STA information lifetime rules + * + * STA info structures (&struct sta_info) are managed in a hash table + * 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. + * + * STA info structures are always "alive" when they are added with + * @sta_info_add() [this may be changed in the future to allow allocating + * outside of a critical section!], they are then added to the hash + * table and list. Therefore, @sta_info_add() must also be RCU protected, + * also, the caller of @sta_info_add() cannot assume that it owns the + * structure. + * + * 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. + * + * 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 wait for an RCU synchronisation before it can be freed. 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(), not before RCU synchronisation, of + * course. Note that sta_info_destroy() must be protected by the RTNL. + * + * 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. + */ /* Caller must hold local->sta_lock */ static int sta_info_hash_del(struct ieee80211_local *local, @@ -42,46 +72,39 @@ static int sta_info_hash_del(struct ieee80211_local *local, if (!s) return -ENOENT; if (s == sta) { - local->sta_hash[STA_HASH(sta->addr)] = s->hnext; + rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], + s->hnext); return 0; } while (s->hnext && s->hnext != sta) s = s->hnext; if (s->hnext) { - s->hnext = sta->hnext; + rcu_assign_pointer(s->hnext, sta->hnext); return 0; } return -ENOENT; } -/* must hold local->sta_lock */ +/* protected by RCU */ static struct sta_info *__sta_info_find(struct ieee80211_local *local, u8 *addr) { struct sta_info *sta; - sta = local->sta_hash[STA_HASH(addr)]; + sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]); while (sta) { if (compare_ether_addr(sta->addr, addr) == 0) break; - sta = sta->hnext; + sta = rcu_dereference(sta->hnext); } return sta; } struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) { - struct sta_info *sta; - - read_lock_bh(&local->sta_lock); - sta = __sta_info_find(local, addr); - if (sta) - __sta_info_get(sta); - read_unlock_bh(&local->sta_lock); - - return sta; + return __sta_info_find(local, addr); } EXPORT_SYMBOL(sta_info_get); @@ -91,81 +114,101 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx, struct sta_info *sta; int i = 0; - read_lock_bh(&local->sta_lock); - list_for_each_entry(sta, &local->sta_list, list) { + list_for_each_entry_rcu(sta, &local->sta_list, list) { if (i < idx) { ++i; continue; - } else if (!dev || dev == sta->dev) { - __sta_info_get(sta); - read_unlock_bh(&local->sta_lock); + } else if (!dev || dev == sta->sdata->dev) { return sta; } } - read_unlock_bh(&local->sta_lock); return NULL; } -static void sta_info_release(struct kref *kref) +void sta_info_destroy(struct sta_info *sta) { - struct sta_info *sta = container_of(kref, struct sta_info, kref); struct ieee80211_local *local = sta->local; struct sk_buff *skb; int i; - /* free sta structure; it has already been removed from - * hash table etc. external structures. Make sure that all - * buffered frames are release (one might have been added - * after sta_info_free() was called). */ + ASSERT_RTNL(); + might_sleep(); + + 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 + + /* + * NOTE: This will call synchronize_rcu() internally to + * make sure no key references can be in use. We rely on + * that here for the mesh code! + */ + ieee80211_key_free(sta->key); + WARN_ON(sta->key); + +#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) { + + while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) dev_kfree_skb_any(skb); - } + for (i = 0; i < STA_TID_NUM; i++) { del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer); del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer); } rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); rate_control_put(sta->rate_ctrl); + kfree(sta); } -void sta_info_put(struct sta_info *sta) +/* Caller must hold local->sta_lock */ +static void sta_info_hash_add(struct ieee80211_local *local, + struct sta_info *sta) { - kref_put(&sta->kref, sta_info_release); + sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; + rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], sta); } -EXPORT_SYMBOL(sta_info_put); - -struct sta_info *sta_info_add(struct ieee80211_local *local, - struct net_device *dev, u8 *addr, gfp_t gfp) +struct sta_info *sta_info_add(struct ieee80211_sub_if_data *sdata, + u8 *addr) { + struct ieee80211_local *local = sdata->local; struct sta_info *sta; int i; DECLARE_MAC_BUF(mac); + unsigned long flags; - sta = kzalloc(sizeof(*sta), gfp); + sta = kzalloc(sizeof(*sta), GFP_ATOMIC); if (!sta) return ERR_PTR(-ENOMEM); - kref_init(&sta->kref); + memcpy(sta->addr, addr, ETH_ALEN); + sta->local = local; + sta->sdata = sdata; sta->rate_ctrl = rate_control_get(local->rate_ctrl); - sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); + sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, + GFP_ATOMIC); if (!sta->rate_ctrl_priv) { rate_control_put(sta->rate_ctrl); kfree(sta); return ERR_PTR(-ENOMEM); } - memcpy(sta->addr, addr, ETH_ALEN); - sta->local = local; - sta->dev = dev; spin_lock_init(&sta->ampdu_mlme.ampdu_rx); spin_lock_init(&sta->ampdu_mlme.ampdu_tx); for (i = 0; i < STA_TID_NUM; i++) { @@ -190,29 +233,26 @@ struct sta_info *sta_info_add(struct ieee80211_local *local, } skb_queue_head_init(&sta->ps_tx_buf); skb_queue_head_init(&sta->tx_filtered); - write_lock_bh(&local->sta_lock); - /* mark sta as used (by caller) */ - __sta_info_get(sta); + spin_lock_irqsave(&local->sta_lock, flags); /* check if STA exists already */ if (__sta_info_find(local, addr)) { - write_unlock_bh(&local->sta_lock); - sta_info_put(sta); + spin_unlock_irqrestore(&local->sta_lock, flags); return ERR_PTR(-EEXIST); } list_add(&sta->list, &local->sta_list); local->num_sta++; sta_info_hash_add(local, sta); - if (local->ops->sta_notify) { - struct ieee80211_sub_if_data *sdata; - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + /* notify driver */ + if (local->ops->sta_notify) { if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) sdata = sdata->u.vlan.ap; local->ops->sta_notify(local_to_hw(local), &sdata->vif, STA_NOTIFY_ADD, addr); } - write_unlock_bh(&local->sta_lock); + + spin_unlock_irqrestore(&local->sta_lock, flags); #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: Added STA %s\n", @@ -252,19 +292,20 @@ static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss, { if (bss) __bss_tim_set(bss, sta->aid); - if (sta->local->ops->set_tim) + if (sta->local->ops->set_tim) { + sta->local->tim_in_locked_section = true; sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 1); + sta->local->tim_in_locked_section = false; + } } void sta_info_set_tim_bit(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + unsigned long flags; - read_lock_bh(&sta->local->sta_lock); - __sta_info_set_tim_bit(sdata->bss, sta); - read_unlock_bh(&sta->local->sta_lock); + spin_lock_irqsave(&sta->local->sta_lock, flags); + __sta_info_set_tim_bit(sta->sdata->bss, sta); + spin_unlock_irqrestore(&sta->local->sta_lock, flags); } static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss, @@ -272,93 +313,135 @@ static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss, { if (bss) __bss_tim_clear(bss, sta->aid); - if (sta->local->ops->set_tim) + if (sta->local->ops->set_tim) { + sta->local->tim_in_locked_section = true; sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 0); + sta->local->tim_in_locked_section = false; + } } void sta_info_clear_tim_bit(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + unsigned long flags; - read_lock_bh(&sta->local->sta_lock); - __sta_info_clear_tim_bit(sdata->bss, sta); - read_unlock_bh(&sta->local->sta_lock); + spin_lock_irqsave(&sta->local->sta_lock, flags); + __sta_info_clear_tim_bit(sta->sdata->bss, sta); + spin_unlock_irqrestore(&sta->local->sta_lock, flags); } -/* Caller must hold local->sta_lock */ -void sta_info_remove(struct sta_info *sta) +/* + * See comment in __sta_info_unlink, + * caller must hold local->sta_lock. + */ +static void __sta_info_pin(struct sta_info *sta) { - struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata; + WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL); + sta->pin_status = STA_INFO_PIN_STAT_PINNED; +} - /* don't do anything if we've been removed already */ - if (sta_info_hash_del(local, sta)) - return; +/* + * 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; - list_del(&sta->list); - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); - if (sta->flags & WLAN_STA_PS) { - sta->flags &= ~WLAN_STA_PS; - if (sdata->bss) - atomic_dec(&sdata->bss->num_sta_ps); - __sta_info_clear_tim_bit(sdata->bss, sta); - } - local->num_sta--; + 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); - if (ieee80211_vif_is_mesh(&sdata->vif)) - mesh_accept_plinks_update(sdata->dev); + return ret; } -void sta_info_free(struct sta_info *sta) +static void __sta_info_unlink(struct sta_info **sta) { - struct sk_buff *skb; - struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); - - DECLARE_MAC_BUF(mac); - - might_sleep(); + struct ieee80211_local *local = (*sta)->local; + struct ieee80211_sub_if_data *sdata = (*sta)->sdata; +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + DECLARE_MAC_BUF(mbuf); +#endif + /* + * pull caller's reference if we're already gone. + */ + if (sta_info_hash_del(local, *sta)) { + *sta = NULL; + return; + } - write_lock_bh(&local->sta_lock); - sta_info_remove(sta); - write_unlock_bh(&local->sta_lock); + /* + * Also 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) 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) + * (3) We set the pin_status to DESTROY here when we + * find such an item. + * (4) 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; + } - if (ieee80211_vif_is_mesh(&sdata->vif)) - mesh_plink_deactivate(sta); + list_del(&(*sta)->list); - while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { - local->total_ps_buffered--; - dev_kfree_skb(skb); - } - while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { - dev_kfree_skb(skb); + if ((*sta)->flags & WLAN_STA_PS) { + (*sta)->flags &= ~WLAN_STA_PS; + if (sdata->bss) + atomic_dec(&sdata->bss->num_sta_ps); + __sta_info_clear_tim_bit(sdata->bss, *sta); } -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - printk(KERN_DEBUG "%s: Removed STA %s\n", - wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr)); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - - ieee80211_key_free(sta->key); - WARN_ON(sta->key); + local->num_sta--; if (local->ops->sta_notify) { - if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN) sdata = sdata->u.vlan.ap; local->ops->sta_notify(local_to_hw(local), &sdata->vif, - STA_NOTIFY_REMOVE, sta->addr); + STA_NOTIFY_REMOVE, (*sta)->addr); } - rate_control_remove_sta_debugfs(sta); - ieee80211_sta_debugfs_remove(sta); + if (ieee80211_vif_is_mesh(&sdata->vif)) { + mesh_accept_plinks_update(sdata); +#ifdef CONFIG_MAC80211_MESH + del_timer(&(*sta)->plink_timer); +#endif + } - sta_info_put(sta); +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Removed STA %s\n", + wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->addr)); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ } +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 inline int sta_info_buffer_expired(struct ieee80211_local *local, struct sta_info *sta, @@ -404,7 +487,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local, if (!skb) break; - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; local->total_ps_buffered--; printk(KERN_DEBUG "Buffered frame expired (STA " "%s)\n", print_mac(mac, sta->addr)); @@ -421,13 +504,10 @@ static void sta_info_cleanup(unsigned long data) struct ieee80211_local *local = (struct ieee80211_local *) data; struct sta_info *sta; - read_lock_bh(&local->sta_lock); - list_for_each_entry(sta, &local->sta_list, list) { - __sta_info_get(sta); + rcu_read_lock(); + list_for_each_entry_rcu(sta, &local->sta_list, list) sta_info_cleanup_expire_buffered(local, sta); - sta_info_put(sta); - } - read_unlock_bh(&local->sta_lock); + rcu_read_unlock(); local->sta_cleanup.expires = round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); @@ -435,37 +515,45 @@ static void sta_info_cleanup(unsigned long data) } #ifdef CONFIG_MAC80211_DEBUGFS -static void sta_info_debugfs_add_task(struct work_struct *work) +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; while (1) { sta = NULL; - read_lock_bh(&local->sta_lock); + + spin_lock_irqsave(&local->sta_lock, flags); list_for_each_entry(tmp, &local->sta_list, list) { if (!tmp->debugfs.dir) { sta = tmp; - __sta_info_get(sta); + __sta_info_pin(sta); break; } } - read_unlock_bh(&local->sta_lock); + spin_unlock_irqrestore(&local->sta_lock, flags); if (!sta) break; ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); - sta_info_put(sta); + + sta = __sta_info_unpin(sta); + + if (sta) { + synchronize_rcu(); + sta_info_destroy(sta); + } } } #endif void sta_info_init(struct ieee80211_local *local) { - rwlock_init(&local->sta_lock); + spin_lock_init(&local->sta_lock); INIT_LIST_HEAD(&local->sta_list); setup_timer(&local->sta_cleanup, sta_info_cleanup, @@ -474,7 +562,7 @@ void sta_info_init(struct ieee80211_local *local) round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); #ifdef CONFIG_MAC80211_DEBUGFS - INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task); + INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work); #endif } @@ -493,24 +581,29 @@ void sta_info_stop(struct ieee80211_local *local) /** * sta_info_flush - flush matching STA entries from the STA table * @local: local interface data - * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs + * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs */ -void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) +void sta_info_flush(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { struct sta_info *sta, *tmp; LIST_HEAD(tmp_list); + unsigned long flags; - write_lock_bh(&local->sta_lock); - list_for_each_entry_safe(sta, tmp, &local->sta_list, list) - if (!dev || dev == sta->dev) { - __sta_info_get(sta); - sta_info_remove(sta); - list_add_tail(&sta->list, &tmp_list); - } - write_unlock_bh(&local->sta_lock); + might_sleep(); - list_for_each_entry_safe(sta, tmp, &tmp_list, list) { - sta_info_free(sta); - sta_info_put(sta); + 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); + } } + spin_unlock_irqrestore(&local->sta_lock, flags); + + synchronize_rcu(); + + list_for_each_entry_safe(sta, tmp, &tmp_list, list) + sta_info_destroy(sta); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b9dfb6fa893a..787124c253af 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -12,7 +12,6 @@ #include #include #include -#include #include "ieee80211_key.h" /** @@ -134,8 +133,14 @@ struct sta_ampdu_mlme { u8 dialog_token_allocator; }; + +/* 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 { - struct kref kref; struct list_head list; struct sta_info *hnext; /* next entry in hash table list */ @@ -166,8 +171,8 @@ struct sta_info { /* last rates used to send a frame to this STA */ int last_txrate_idx, last_nonerp_txrate_idx; - struct net_device *dev; /* which net device is this station associated - * to */ + /* sub_if_data this sta belongs to */ + struct ieee80211_sub_if_data *sdata; struct ieee80211_key *key; @@ -199,6 +204,12 @@ struct sta_info { u16 listen_interval; + /* + * for use by the internal lifetime management, + * see __sta_info_unlink + */ + u8 pin_status; + struct ieee80211_ht_info ht_info; /* 802.11n HT capabilities of this STA */ struct sta_ampdu_mlme ampdu_mlme; @@ -262,25 +273,37 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta) */ #define STA_INFO_CLEANUP_INTERVAL (10 * HZ) -static inline void __sta_info_get(struct sta_info *sta) -{ - kref_get(&sta->kref); -} - -struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); +/* + * Get a STA info, must have be under RCU read lock. + */ +struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr); +/* + * Get STA info by index, BROKEN! + */ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx, struct net_device *dev); -void sta_info_put(struct sta_info *sta); -struct sta_info *sta_info_add(struct ieee80211_local *local, - struct net_device *dev, u8 *addr, gfp_t gfp); -void sta_info_remove(struct sta_info *sta); -void sta_info_free(struct sta_info *sta); -void sta_info_init(struct ieee80211_local *local); -int sta_info_start(struct ieee80211_local *local); -void sta_info_stop(struct ieee80211_local *local); -void sta_info_flush(struct ieee80211_local *local, struct net_device *dev); +/* + * Add a new STA info, must be under RCU read lock + * because otherwise the returned reference isn't + * necessarily valid long enough. + */ +struct sta_info *sta_info_add(struct ieee80211_sub_if_data *sdata, + u8 *addr); +/* + * 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); +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); +void sta_info_init(struct ieee80211_local *local); +int sta_info_start(struct ieee80211_local *local); +void sta_info_stop(struct ieee80211_local *local); +void sta_info_flush(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); + #endif /* STA_INFO_H */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 33e314f3aab7..80f4343a3007 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -327,10 +327,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) } total += skb_queue_len(&ap->ps_bc_buf); } - rcu_read_unlock(); - read_lock_bh(&local->sta_lock); - list_for_each_entry(sta, &local->sta_list, list) { + list_for_each_entry_rcu(sta, &local->sta_list, list) { skb = skb_dequeue(&sta->ps_tx_buf); if (skb) { purged++; @@ -338,7 +336,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) } total += skb_queue_len(&sta->ps_tx_buf); } - read_unlock_bh(&local->sta_lock); + + rcu_read_unlock(); local->total_ps_buffered = total; printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n", @@ -1141,20 +1140,17 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, return 0; } + rcu_read_lock(); + /* initialises tx */ res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control); if (res_prepare == TX_DROP) { dev_kfree_skb(skb); + rcu_read_unlock(); return 0; } - /* - * key references are protected using RCU and this requires that - * we are in a read-site RCU section during receive processing - */ - rcu_read_lock(); - sta = tx.sta; tx.channel = local->hw.conf.channel; @@ -1167,9 +1163,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, skb = tx.skb; /* handlers are allowed to change skb */ - if (sta) - sta_info_put(sta); - if (unlikely(res == TX_DROP)) { I802_DEBUG_INC(local->tx_handlers_drop); goto drop; @@ -1489,11 +1482,11 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, * in AP mode) */ if (!is_multicast_ether_addr(hdr.addr1)) { + rcu_read_lock(); sta = sta_info_get(local, hdr.addr1); - if (sta) { + if (sta) sta_flags = sta->flags; - sta_info_put(sta); - } + rcu_read_unlock(); } /* receiver is QoS enabled, use a QoS type frame */ @@ -1722,7 +1715,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, /* Generate bitmap for TIM only if there are any STAs in power save * mode. */ - read_lock_bh(&local->sta_lock); if (atomic_read(&bss->num_sta_ps) > 0) /* in the hope that this is faster than * checking byte-for-byte */ @@ -1773,7 +1765,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, *pos++ = aid0; /* Bitmap control */ *pos++ = 0; /* Part Virt Bitmap */ } - read_unlock_bh(&local->sta_lock); } struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, @@ -1821,7 +1812,22 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data); - ieee80211_beacon_add_tim(local, ap, skb, beacon); + /* + * Not very nice, but we want to allow the driver to call + * ieee80211_beacon_get() as a response to the set_tim() + * callback. That, however, is already invoked under the + * sta_lock to guarantee consistent and race-free update + * of the tim bitmap in mac80211 and the driver. + */ + if (local->tim_in_locked_section) { + ieee80211_beacon_add_tim(local, ap, skb, beacon); + } else { + unsigned long flags; + + spin_lock_irqsave(&local->sta_lock, flags); + ieee80211_beacon_add_tim(local, ap, skb, beacon); + spin_unlock_irqrestore(&local->sta_lock, flags); + } if (beacon->tail) memcpy(skb_put(skb, beacon->tail_len), @@ -1965,7 +1971,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, rcu_read_unlock(); return NULL; } - rcu_read_unlock(); if (bss->dtim_count != 0) return NULL; /* send buffered bc/mc only after DTIM beacon */ @@ -2010,8 +2015,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, skb = NULL; } - if (sta) - sta_info_put(sta); + rcu_read_unlock(); return skb; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 8cc036decc82..4e94e4026e78 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -153,6 +153,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) { queue = pkt_data->queue; + rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); tid = skb->priority & QOS_CONTROL_TAG1D_MASK; if (sta) { @@ -164,8 +165,8 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) } else { pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; } - sta_info_put(sta); } + rcu_read_unlock(); skb_queue_tail(&q->requeued[queue], skb); qd->q.qlen++; return 0; @@ -187,6 +188,8 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) p++; *p = 0; + rcu_read_lock(); + sta = sta_info_get(local, hdr->addr1); if (sta) { int ampdu_queue = sta->tid_to_tx_q[tid]; @@ -197,8 +200,9 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) } else { pkt_data->flags &= ~IEEE80211_TXPD_AMPDU; } - sta_info_put(sta); } + + rcu_read_unlock(); } if (unlikely(queue >= local->hw.queues)) { -- cgit v1.2.3-59-g8ed1b From 3b091cd4941912081730ffa17948da6d148c822d Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Fri, 29 Feb 2008 12:20:39 -0800 Subject: mac80211: move comment to better location Signed-off-by: Luis Carlos Cobo Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net/mac80211/mesh.c') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 9de1ccc11cf9..54d5ced566ef 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -48,11 +48,6 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev) struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_if_sta *sta = &sdata->u.sta; - if (sta->mesh_id_len == ie->mesh_id_len && - memcmp(sta->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && - memcmp(sta->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 && - memcmp(sta->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 && - memcmp(sta->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0) /* * As support for each feature is added, check for matching * - On mesh config capabilities @@ -63,6 +58,11 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev) * - MDA enabled * - Power management control on fc */ + if (sta->mesh_id_len == ie->mesh_id_len && + memcmp(sta->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && + memcmp(sta->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 && + memcmp(sta->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 && + memcmp(sta->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0) return true; return false; -- cgit v1.2.3-59-g8ed1b From b4e08ea141e6d663dec31b31d6289baeaaa2a3a2 Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Fri, 29 Feb 2008 15:46:08 -0800 Subject: mac80211: add PLINK_ prefix and kernel doc to enum plink_state Signed-off-by: Luis Carlos Cobo Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh.c | 6 ++-- net/mac80211/mesh_pathtbl.c | 6 ++-- net/mac80211/mesh_plink.c | 69 +++++++++++++++++++++++---------------------- net/mac80211/rx.c | 2 +- net/mac80211/sta_info.c | 2 +- net/mac80211/sta_info.h | 31 ++++++++++++++------ 6 files changed, 65 insertions(+), 51 deletions(-) (limited to 'net/mac80211/mesh.c') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 54d5ced566ef..594a3356a508 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -91,9 +91,9 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) /* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0, * the mesh interface might be able to establish plinks with peers that - * are already on the table but are not on ESTAB state. However, in - * general the mesh interface is not accepting peer link requests from - * new peers, and that must be reflected in the beacon + * are already on the table but are not on PLINK_ESTAB state. However, + * in general the mesh interface is not accepting peer link requests + * from new peers, and that must be reflected in the beacon */ free_plinks = mesh_plink_availables(sdata); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index f74e4ce40ec7..135022d7ee57 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -261,9 +261,9 @@ EXPORT_SYMBOL(mesh_plink_broken); * * @sta - mesh peer to match * - * RCU notes: this function is called when a mesh plink transitions from ESTAB - * to any other state, since ESTAB state is the only one that allows path - * creation. This will happen before the sta can be freed (because + * RCU notes: this function is called when a mesh plink transitions from + * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that + * allows path creation. This will happen before the sta can be freed (because * sta_info_destroy() calls this) so any reader in a rcu read block will be * protected against the plink disappearing. */ diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 23d951a83668..18fe52436c47 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -84,7 +84,7 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) */ static inline void mesh_plink_fsm_restart(struct sta_info *sta) { - sta->plink_state = LISTEN; + sta->plink_state = PLINK_LISTEN; sta->llid = sta->plid = sta->reason = 0; sta->plink_retries = 0; } @@ -121,9 +121,9 @@ static void __mesh_plink_deactivate(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; - if (sta->plink_state == ESTAB) + if (sta->plink_state == PLINK_ESTAB) mesh_plink_dec_estab_count(sdata); - sta->plink_state = BLOCKED; + sta->plink_state = PLINK_BLOCKED; mesh_path_flush_by_nexthop(sta); } @@ -243,7 +243,7 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, sta->last_rx = jiffies; sta->supp_rates[local->hw.conf.channel->band] = rates; - if (peer_accepting_plinks && sta->plink_state == LISTEN && + if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && sdata->u.sta.accepting_plinks && sdata->u.sta.mshcfg.auto_open_plinks) mesh_plink_open(sta); @@ -283,8 +283,8 @@ static void mesh_plink_timer(unsigned long data) dev = sdata->dev; switch (sta->plink_state) { - case OPN_RCVD: - case OPN_SNT: + case PLINK_OPN_RCVD: + case PLINK_OPN_SNT: /* retry timer */ if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { u32 rand; @@ -303,17 +303,17 @@ static void mesh_plink_timer(unsigned long data) } reason = cpu_to_le16(MESH_MAX_RETRIES); /* fall through on else */ - case CNF_RCVD: + case PLINK_CNF_RCVD: /* confirm timer */ if (!reason) reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); - sta->plink_state = HOLDING; + sta->plink_state = PLINK_HOLDING; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->plink_lock); mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid, reason); break; - case HOLDING: + case PLINK_HOLDING: /* holding timer */ del_timer(&sta->plink_timer); mesh_plink_fsm_restart(sta); @@ -345,11 +345,11 @@ int mesh_plink_open(struct sta_info *sta) spin_lock_bh(&sta->plink_lock); get_random_bytes(&llid, 2); sta->llid = llid; - if (sta->plink_state != LISTEN) { + if (sta->plink_state != PLINK_LISTEN) { spin_unlock_bh(&sta->plink_lock); return -EBUSY; } - sta->plink_state = OPN_SNT; + sta->plink_state = PLINK_OPN_SNT; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->plink_lock); mpl_dbg("Mesh plink: starting establishment with %s\n", @@ -367,7 +367,7 @@ void mesh_plink_block(struct sta_info *sta) spin_lock_bh(&sta->plink_lock); __mesh_plink_deactivate(sta); - sta->plink_state = BLOCKED; + sta->plink_state = PLINK_BLOCKED; spin_unlock_bh(&sta->plink_lock); } @@ -385,18 +385,19 @@ int mesh_plink_close(struct sta_info *sta) sta->reason = cpu_to_le16(MESH_LINK_CANCELLED); reason = sta->reason; - if (sta->plink_state == LISTEN || sta->plink_state == BLOCKED) { + if (sta->plink_state == PLINK_LISTEN || + sta->plink_state == PLINK_BLOCKED) { mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->plink_lock); return 0; - } else if (sta->plink_state == ESTAB) { + } else if (sta->plink_state == PLINK_ESTAB) { __mesh_plink_deactivate(sta); /* The timer should not be running */ mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; - sta->plink_state = HOLDING; + sta->plink_state = PLINK_HOLDING; llid = sta->llid; plid = sta->plid; spin_unlock_bh(&sta->plink_lock); @@ -468,7 +469,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, return; } - if (sta && sta->plink_state == BLOCKED) { + if (sta && sta->plink_state == PLINK_BLOCKED) { rcu_read_unlock(); return; } @@ -529,7 +530,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, event = CNF_ACPT; break; case PLINK_CLOSE: - if (sta->plink_state == ESTAB) + if (sta->plink_state == PLINK_ESTAB) /* Do not check for llid or plid. This does not * follow the standard but since multiple plinks * per sta are not supported, it is necessary in @@ -562,14 +563,14 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, reason = 0; switch (sta->plink_state) { /* spin_unlock as soon as state is updated at each case */ - case LISTEN: + case PLINK_LISTEN: switch (event) { case CLS_ACPT: mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->plink_lock); break; case OPN_ACPT: - sta->plink_state = OPN_RCVD; + sta->plink_state = PLINK_OPN_RCVD; sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; @@ -586,7 +587,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, } break; - case OPN_SNT: + case PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -595,7 +596,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = HOLDING; + sta->plink_state = PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -607,7 +608,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, break; case OPN_ACPT: /* retry timer is left untouched */ - sta->plink_state = OPN_RCVD; + sta->plink_state = PLINK_OPN_RCVD; sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->plink_lock); @@ -615,7 +616,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, plid, 0); break; case CNF_ACPT: - sta->plink_state = CNF_RCVD; + sta->plink_state = PLINK_CNF_RCVD; if (!mod_plink_timer(sta, dot11MeshConfirmTimeout(sdata))) sta->ignore_plink_timer = true; @@ -628,7 +629,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, } break; - case OPN_RCVD: + case PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -637,7 +638,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = HOLDING; + sta->plink_state = PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -655,7 +656,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, break; case CNF_ACPT: del_timer(&sta->plink_timer); - sta->plink_state = ESTAB; + sta->plink_state = PLINK_ESTAB; mesh_plink_inc_estab_count(sdata); spin_unlock_bh(&sta->plink_lock); mpl_dbg("Mesh plink with %s ESTABLISHED\n", @@ -667,7 +668,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, } break; - case CNF_RCVD: + case PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: @@ -676,7 +677,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; - sta->plink_state = HOLDING; + sta->plink_state = PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; @@ -688,7 +689,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, break; case OPN_ACPT: del_timer(&sta->plink_timer); - sta->plink_state = ESTAB; + sta->plink_state = PLINK_ESTAB; mesh_plink_inc_estab_count(sdata); spin_unlock_bh(&sta->plink_lock); mpl_dbg("Mesh plink with %s ESTABLISHED\n", @@ -702,13 +703,13 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, } break; - case ESTAB: + case PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; __mesh_plink_deactivate(sta); - sta->plink_state = HOLDING; + sta->plink_state = PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->plink_lock); @@ -726,7 +727,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, break; } break; - case HOLDING: + case PLINK_HOLDING: switch (event) { case CLS_ACPT: if (del_timer(&sta->plink_timer)) @@ -749,8 +750,8 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, } break; default: - /* should not get here, BLOCKED is dealt with at the beggining - * of the function + /* should not get here, PLINK_BLOCKED is dealt with at the + * beggining of the function */ spin_unlock_bh(&sta->plink_lock); break; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 8e1e2859bfd5..644d2774469d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -411,7 +411,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) * establisment frame, beacon or probe, drop the frame. */ - if (!rx->sta || sta_plink_state(rx->sta) != ESTAB) { + if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) { struct ieee80211_mgmt *mgmt; if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 909fa38edb6c..e27f896dae53 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -248,7 +248,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ #ifdef CONFIG_MAC80211_MESH - sta->plink_state = LISTEN; + sta->plink_state = PLINK_LISTEN; spin_lock_init(&sta->plink_lock); init_timer(&sta->plink_timer); #endif diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 547bfc3b2431..f166c8039f2b 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -106,14 +106,27 @@ struct tid_ampdu_rx { struct timer_list session_timer; }; +/** + * enum plink_state - state of a mesh peer link finite state machine + * + * @PLINK_LISTEN: initial state, considered the implicit state of non existant + * mesh peer links + * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer + * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer + * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh + * peer + * @PLINK_ESTAB: mesh peer link is established + * @PLINK_HOLDING: mesh peer link is being closed or cancelled + * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded + */ enum plink_state { - LISTEN, - OPN_SNT, - OPN_RCVD, - CNF_RCVD, - ESTAB, - HOLDING, - BLOCKED + PLINK_LISTEN, + PLINK_OPN_SNT, + PLINK_OPN_RCVD, + PLINK_CNF_RCVD, + PLINK_ESTAB, + PLINK_HOLDING, + PLINK_BLOCKED }; /** @@ -248,7 +261,7 @@ struct sta_info { */ __le16 llid; /* Local link ID */ __le16 plid; /* Peer link ID */ - __le16 reason; /* Buffer for cancel reason on HOLDING state */ + __le16 reason; /* Cancel reason on PLINK_HOLDING state */ u8 plink_retries; /* Retries in establishment */ bool ignore_plink_timer; enum plink_state plink_state; @@ -280,7 +293,7 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta) #ifdef CONFIG_MAC80211_MESH return sta->plink_state; #endif - return LISTEN; + return PLINK_LISTEN; } -- cgit v1.2.3-59-g8ed1b From 6c4711b4697d93424e4b1f76a9929ba844d714a5 Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Mon, 31 Mar 2008 17:39:18 -0700 Subject: mac80211: use a struct for bss->mesh_config This allows cleaner code when accesing bss->mesh_config components. Signed-off-by: Luis Carlos Cobo Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 13 +++++++++++-- net/mac80211/ieee80211_sta.c | 32 ++++++++++++++++++++------------ net/mac80211/mesh.c | 4 ---- net/mac80211/mesh.h | 10 ++++++++++ 4 files changed, 41 insertions(+), 18 deletions(-) (limited to 'net/mac80211/mesh.c') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0997a0f96203..6c62dd42f915 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -69,6 +69,14 @@ struct ieee80211_fragment_entry { u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ }; +struct bss_mesh_config { + u32 path_proto_id; + u32 path_metric_id; + u32 cong_control_id; + u32 channel_precedence; + u8 mesh_version; +}; + struct ieee80211_sta_bss { struct list_head list; @@ -94,7 +102,7 @@ struct ieee80211_sta_bss { #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; - u8 *mesh_cfg; + struct bss_mesh_config *mesh_cfg; #endif #define IEEE80211_MAX_SUPP_RATES 32 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; @@ -113,7 +121,8 @@ struct ieee80211_sta_bss { u8 erp_value; }; -static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss) +static inline +struct bss_mesh_config *bss_mesh_cfg(struct ieee80211_sta_bss *bss) { #ifdef CONFIG_MAC80211_MESH return bss->mesh_cfg; diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index 927ffbfe8582..110eaf3ab58c 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "ieee80211_i.h" @@ -2123,6 +2124,11 @@ ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq, } #ifdef CONFIG_MAC80211_MESH +static inline u32 bss_mesh_cfg_get(u8 *mesh_cfg, u8 offset) +{ + return be32_to_cpu(get_unaligned((__be32 *) (mesh_cfg + offset))); +} + static struct ieee80211_sta_bss * ieee80211_rx_mesh_bss_get(struct net_device *dev, u8 *mesh_id, int mesh_id_len, u8 *mesh_cfg, int freq) @@ -2162,7 +2168,7 @@ ieee80211_rx_mesh_bss_add(struct net_device *dev, u8 *mesh_id, int mesh_id_len, if (!bss) return NULL; - bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC); + bss->mesh_cfg = kmalloc(sizeof(struct bss_mesh_config), GFP_ATOMIC); if (!bss->mesh_cfg) { kfree(bss); return NULL; @@ -2180,7 +2186,12 @@ ieee80211_rx_mesh_bss_add(struct net_device *dev, u8 *mesh_id, int mesh_id_len, atomic_inc(&bss->users); atomic_inc(&bss->users); - memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN); + bss->mesh_cfg->mesh_version = mesh_cfg[0]; + bss->mesh_cfg->path_proto_id = bss_mesh_cfg_get(mesh_cfg, PP_OFFSET); + bss->mesh_cfg->path_metric_id = bss_mesh_cfg_get(mesh_cfg, PM_OFFSET); + bss->mesh_cfg->cong_control_id = bss_mesh_cfg_get(mesh_cfg, CC_OFFSET); + bss->mesh_cfg->channel_precedence = bss_mesh_cfg_get(mesh_cfg, + CP_OFFSET); bss->mesh_id_len = mesh_id_len; bss->freq = freq; spin_lock_bh(&local->sta_bss_lock); @@ -4057,36 +4068,33 @@ ieee80211_sta_scan_result(struct net_device *dev, if (bss_mesh_cfg(bss)) { char *buf; - u8 *cfg = bss_mesh_cfg(bss); + struct bss_mesh_config *cfg = bss_mesh_cfg(bss); buf = kmalloc(50, GFP_ATOMIC); if (buf) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh network (version %d)", cfg[0]); + sprintf(buf, "Mesh network (version %d)", + cfg->mesh_version); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Path Selection Protocol ID: " - "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], - cfg[4]); + "0x%08X", cfg->path_proto_id); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Path Selection Metric ID: " - "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], - cfg[8]); + "0x%08X", cfg->path_metric_id); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Congestion Control Mode ID: " - "0x%02X%02X%02X%02X", cfg[9], cfg[10], - cfg[11], cfg[12]); + "0x%08X", cfg->cong_control_id); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Channel Precedence: " - "0x%02X%02X%02X%02X", cfg[13], cfg[14], - cfg[15], cfg[16]); + "0x%08X", cfg->channel_precedence); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 594a3356a508..b10f1e543a94 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -11,10 +11,6 @@ #include "ieee80211_i.h" #include "mesh.h" -#define PP_OFFSET 1 /* Path Selection Protocol */ -#define PM_OFFSET 5 /* Path Selection Metric */ -#define CC_OFFSET 9 /* Congestion Control Mode */ -#define CAPAB_OFFSET 17 #define ACCEPT_PLINKS 0x80 int mesh_allocated; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 742003d3a841..8ff46ea0f1d7 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -157,6 +157,16 @@ struct mesh_rmc { */ #define MESH_CFG_CMP_LEN 17 +/* + * Components offset within the mesh configuration IE + */ +#define PP_OFFSET 1 /* Path Selection Protocol */ +#define PM_OFFSET 5 /* Path Selection Metric */ +#define CC_OFFSET 9 /* Congestion Control Mode */ +#define CP_OFFSET 13 /* Channel Precedence */ +#define CAPAB_OFFSET 17 /* Mesh Capabilities */ + + /* Default values, timeouts in ms */ #define MESH_TTL 5 #define MESH_MAX_RETR 3 -- cgit v1.2.3-59-g8ed1b From 247367016305637fb981db020679520e354c80c4 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 8 Apr 2008 14:15:46 -0400 Subject: Revert "mac80211: use a struct for bss->mesh_config" This reverts commit 6c4711b4697d93424e4b1f76a9929ba844d714a5. That patch breaks mesh config comparison between beacons/probe reponses, so every beacon from a mesh network would be added as a new bss. Since the comparison has to be performed for every received beacon I believe it is best to save the mesh config in a format easy to compare, rather than do a bunch of unaligned accesses to compare field by field. Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 13 ++----------- net/mac80211/ieee80211_sta.c | 32 ++++++++++++-------------------- net/mac80211/mesh.c | 4 ++++ net/mac80211/mesh.h | 10 ---------- 4 files changed, 18 insertions(+), 41 deletions(-) (limited to 'net/mac80211/mesh.c') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6c62dd42f915..0997a0f96203 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -69,14 +69,6 @@ struct ieee80211_fragment_entry { u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ }; -struct bss_mesh_config { - u32 path_proto_id; - u32 path_metric_id; - u32 cong_control_id; - u32 channel_precedence; - u8 mesh_version; -}; - struct ieee80211_sta_bss { struct list_head list; @@ -102,7 +94,7 @@ struct ieee80211_sta_bss { #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; - struct bss_mesh_config *mesh_cfg; + u8 *mesh_cfg; #endif #define IEEE80211_MAX_SUPP_RATES 32 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; @@ -121,8 +113,7 @@ struct ieee80211_sta_bss { u8 erp_value; }; -static inline -struct bss_mesh_config *bss_mesh_cfg(struct ieee80211_sta_bss *bss) +static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss) { #ifdef CONFIG_MAC80211_MESH return bss->mesh_cfg; diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index c20ef89acad6..1ee07f0b02e2 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "ieee80211_i.h" @@ -2123,11 +2122,6 @@ ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq, } #ifdef CONFIG_MAC80211_MESH -static inline u32 bss_mesh_cfg_get(u8 *mesh_cfg, u8 offset) -{ - return be32_to_cpu(get_unaligned((__be32 *) (mesh_cfg + offset))); -} - static struct ieee80211_sta_bss * ieee80211_rx_mesh_bss_get(struct net_device *dev, u8 *mesh_id, int mesh_id_len, u8 *mesh_cfg, int freq) @@ -2167,7 +2161,7 @@ ieee80211_rx_mesh_bss_add(struct net_device *dev, u8 *mesh_id, int mesh_id_len, if (!bss) return NULL; - bss->mesh_cfg = kmalloc(sizeof(struct bss_mesh_config), GFP_ATOMIC); + bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC); if (!bss->mesh_cfg) { kfree(bss); return NULL; @@ -2185,12 +2179,7 @@ ieee80211_rx_mesh_bss_add(struct net_device *dev, u8 *mesh_id, int mesh_id_len, atomic_inc(&bss->users); atomic_inc(&bss->users); - bss->mesh_cfg->mesh_version = mesh_cfg[0]; - bss->mesh_cfg->path_proto_id = bss_mesh_cfg_get(mesh_cfg, PP_OFFSET); - bss->mesh_cfg->path_metric_id = bss_mesh_cfg_get(mesh_cfg, PM_OFFSET); - bss->mesh_cfg->cong_control_id = bss_mesh_cfg_get(mesh_cfg, CC_OFFSET); - bss->mesh_cfg->channel_precedence = bss_mesh_cfg_get(mesh_cfg, - CP_OFFSET); + memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN); bss->mesh_id_len = mesh_id_len; bss->freq = freq; spin_lock_bh(&local->sta_bss_lock); @@ -4067,33 +4056,36 @@ ieee80211_sta_scan_result(struct net_device *dev, if (bss_mesh_cfg(bss)) { char *buf; - struct bss_mesh_config *cfg = bss_mesh_cfg(bss); + u8 *cfg = bss_mesh_cfg(bss); buf = kmalloc(50, GFP_ATOMIC); if (buf) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh network (version %d)", - cfg->mesh_version); + sprintf(buf, "Mesh network (version %d)", cfg[0]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Path Selection Protocol ID: " - "0x%08X", cfg->path_proto_id); + "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], + cfg[4]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Path Selection Metric ID: " - "0x%08X", cfg->path_metric_id); + "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], + cfg[8]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Congestion Control Mode ID: " - "0x%08X", cfg->cong_control_id); + "0x%02X%02X%02X%02X", cfg[9], cfg[10], + cfg[11], cfg[12]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); sprintf(buf, "Channel Precedence: " - "0x%08X", cfg->channel_precedence); + "0x%02X%02X%02X%02X", cfg[13], cfg[14], + cfg[15], cfg[16]); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index b10f1e543a94..594a3356a508 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -11,6 +11,10 @@ #include "ieee80211_i.h" #include "mesh.h" +#define PP_OFFSET 1 /* Path Selection Protocol */ +#define PM_OFFSET 5 /* Path Selection Metric */ +#define CC_OFFSET 9 /* Congestion Control Mode */ +#define CAPAB_OFFSET 17 #define ACCEPT_PLINKS 0x80 int mesh_allocated; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8ff46ea0f1d7..742003d3a841 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -157,16 +157,6 @@ struct mesh_rmc { */ #define MESH_CFG_CMP_LEN 17 -/* - * Components offset within the mesh configuration IE - */ -#define PP_OFFSET 1 /* Path Selection Protocol */ -#define PM_OFFSET 5 /* Path Selection Metric */ -#define CC_OFFSET 9 /* Congestion Control Mode */ -#define CP_OFFSET 13 /* Channel Precedence */ -#define CAPAB_OFFSET 17 /* Mesh Capabilities */ - - /* Default values, timeouts in ms */ #define MESH_TTL 5 #define MESH_MAX_RETR 3 -- cgit v1.2.3-59-g8ed1b