From d0df9eecf9f61f70fd847656f5eb113e06e46a03 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Fri, 13 May 2011 14:16:07 -0700 Subject: mac80211: Deactivate mesh path timers when freeing nodes Mesh paths are deleted via mesh_path_del() which properly deactivates the timer associated to a mesh path. But if paths were deleted by mesh_table_free(..., true) timers would not be deactivated. This fixes this case. Reported-by: Johannes Berg Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh_pathtbl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 0aa96cd6bd2..f775202552e 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -719,8 +719,10 @@ static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) struct mpath_node *node = hlist_entry(p, struct mpath_node, list); mpath = node->mpath; hlist_del_rcu(p); - if (free_leafs) + if (free_leafs) { + del_timer_sync(&mpath->timer); kfree(mpath); + } kfree(node); } -- cgit v1.2.3-70-g09d2 From 8e621fc90b42fa2ca4ff65dd8d9cb21723e47837 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 May 2011 01:55:23 +0200 Subject: mac80211: verify IBSS in interface combinations Drivers shouldn't attempt to advertise support for more than one IBSS interface since mac80211 doesn't support that. Check and return an error from ieee80211_register_hw() in that case. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0d7b08db8e5..96ab1302a09 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -752,11 +752,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); - /* mac80211 doesn't support more than 1 channel */ - for (i = 0; i < hw->wiphy->n_iface_combinations; i++) - if (hw->wiphy->iface_combinations[i].num_different_channels > 1) + /* + * mac80211 doesn't support more than 1 channel, and also not more + * than one IBSS interface + */ + for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + int j; + + c = &hw->wiphy->iface_combinations[i]; + + if (c->num_different_channels > 1) return -EINVAL; + for (j = 0; j < c->n_limits; j++) + if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && + c->limits[j].max > 1) + return -EINVAL; + } + #ifndef CONFIG_MAC80211_MESH /* mesh depends on Kconfig, but drivers should set it if they want */ local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); -- cgit v1.2.3-70-g09d2 From c29acf201007a6d73223f864f52406eb5ba19933 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Sat, 14 May 2011 09:43:28 +0530 Subject: mac80211: abort scan_work immediately when the device goes down As long as no delay is required b/w channel change, scan work is proceeding without scheduling a new work. In such case, we can not abort scan work when the card was unplugged. This patch completes the scanning immediately whenever the device goes down. Reviewed-by: Johannes Berg Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- net/mac80211/iface.c | 4 ++-- net/mac80211/scan.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0d00ac93d95..dee30aea9ab 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -384,11 +384,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, int i; enum nl80211_channel_type orig_ct; + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + if (local->scan_sdata == sdata) ieee80211_scan_cancel(local); - clear_bit(SDATA_STATE_RUNNING, &sdata->state); - /* * Stop TX on this interface first. */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d20046b5d8f..27af6723cb5 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -719,6 +719,11 @@ void ieee80211_scan_work(struct work_struct *work) * without scheduling a new work */ do { + if (!ieee80211_sdata_running(sdata)) { + aborted = true; + goto out_complete; + } + switch (local->next_scan_state) { case SCAN_DECISION: /* if no more bands/channels left, complete scan */ -- cgit v1.2.3-70-g09d2 From d07c7cf49ae7c488e778c4d668f4cc10bd2fa971 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 May 2011 11:04:51 +0200 Subject: mac80211: add missing rcu_barrier mac80211 uses call_rcu() with functions that are defined in the module, so it must use rcu_barrier() at module exit time. Luckily, this seems to not be a problem in practice as module unload and unregistration takes a long time and probably does multiple synchronize_rcu(). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 96ab1302a09..866f269183c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1090,6 +1090,8 @@ static void __exit ieee80211_exit(void) ieee80211s_stop(); ieee80211_iface_exit(); + + rcu_barrier(); } -- cgit v1.2.3-70-g09d2 From 1928ecab620907a0953f811316d05f367f3f4dba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 May 2011 11:00:52 +0200 Subject: mac80211: fix and simplify mesh locking The locking in mesh_{mpath,mpp}_table_grow not only has an rcu_read_unlock() missing, it's also racy (though really only technically since it's invoked from a single function only) since it obtains the new size of the table without any locking, so two invocations of the function could attempt the same resize. Additionally, it uses synchronize_rcu() which is rather expensive and can be avoided trivially here. Modify the functions to only use the table lock and use call_rcu() instead of synchronize_rcu(). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh.h | 3 +++ net/mac80211/mesh_pathtbl.c | 44 ++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index e7c5fddb480..eb733c0d61a 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -120,6 +120,7 @@ struct mesh_path { * buckets * @mean_chain_len: maximum average length for the hash buckets' list, if it is * reached, the table will grow + * rcu_head: RCU head to free the table */ struct mesh_table { /* Number of buckets will be 2^N */ @@ -132,6 +133,8 @@ struct mesh_table { int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); int size_order; int mean_chain_len; + + struct rcu_head rcu_head; }; /* Recent multicast cache */ diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index f775202552e..74021365b8c 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -370,52 +370,52 @@ err_path_alloc: return err; } +static void mesh_table_free_rcu(struct rcu_head *rcu) +{ + struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); + + mesh_table_free(tbl, false); +} + void mesh_mpath_table_grow(void) { struct mesh_table *oldtbl, *newtbl; - rcu_read_lock(); - newtbl = mesh_table_alloc(rcu_dereference(mesh_paths)->size_order + 1); - if (!newtbl) - return; write_lock_bh(&pathtbl_resize_lock); + newtbl = mesh_table_alloc(mesh_paths->size_order + 1); + if (!newtbl) + goto out; oldtbl = mesh_paths; if (mesh_table_grow(mesh_paths, newtbl) < 0) { - rcu_read_unlock(); __mesh_table_free(newtbl); - write_unlock_bh(&pathtbl_resize_lock); - return; + goto out; } - rcu_read_unlock(); rcu_assign_pointer(mesh_paths, newtbl); - write_unlock_bh(&pathtbl_resize_lock); - synchronize_rcu(); - mesh_table_free(oldtbl, false); + call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); + + out: + write_unlock_bh(&pathtbl_resize_lock); } void mesh_mpp_table_grow(void) { struct mesh_table *oldtbl, *newtbl; - rcu_read_lock(); - newtbl = mesh_table_alloc(rcu_dereference(mpp_paths)->size_order + 1); - if (!newtbl) - return; write_lock_bh(&pathtbl_resize_lock); + newtbl = mesh_table_alloc(mpp_paths->size_order + 1); + if (!newtbl) + goto out; oldtbl = mpp_paths; if (mesh_table_grow(mpp_paths, newtbl) < 0) { - rcu_read_unlock(); __mesh_table_free(newtbl); - write_unlock_bh(&pathtbl_resize_lock); - return; + goto out; } - rcu_read_unlock(); rcu_assign_pointer(mpp_paths, newtbl); - write_unlock_bh(&pathtbl_resize_lock); + call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); - synchronize_rcu(); - mesh_table_free(oldtbl, false); + out: + write_unlock_bh(&pathtbl_resize_lock); } int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) -- cgit v1.2.3-70-g09d2 From 349eb8cf45aadd35836fdfde75b3265a01b2aaa1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 14 May 2011 11:56:16 +0200 Subject: mac80211: annotate and fix RCU in mesh code This adds proper RCU annotations to the mesh path table code, and fixes a number of bugs in the code that I found while checking the sparse warnings I got as a result of the annotations. Some things like the changes in mesh_path_add() or mesh_pathtbl_init() only serve to shut up sparse, but other changes like the changes surrounding the for_each_mesh_entry() macro fix real RCU bugs in the code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mesh.h | 4 -- net/mac80211/mesh_pathtbl.c | 154 +++++++++++++++++++++++++++++--------------- 2 files changed, 102 insertions(+), 56 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index eb733c0d61a..249e733362e 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -289,10 +289,6 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP; } -#define for_each_mesh_entry(x, p, node, i) \ - for (i = 0; i <= x->hash_mask; i++) \ - hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list) - void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 74021365b8c..2bda0ac6232 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -36,8 +36,8 @@ struct mpath_node { struct mesh_path *mpath; }; -static struct mesh_table *mesh_paths; -static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ +static struct mesh_table __rcu *mesh_paths; +static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; @@ -48,6 +48,29 @@ int mesh_paths_generation; static DEFINE_RWLOCK(pathtbl_resize_lock); +static inline struct mesh_table *resize_dereference_mesh_paths(void) +{ + return rcu_dereference_protected(mesh_paths, + lockdep_is_held(&pathtbl_resize_lock)); +} + +static inline struct mesh_table *resize_dereference_mpp_paths(void) +{ + return rcu_dereference_protected(mpp_paths, + lockdep_is_held(&pathtbl_resize_lock)); +} + +/* + * CAREFUL -- "tbl" must not be an expression, + * in particular not an rcu_dereference(), since + * it's used twice. So it is illegal to do + * for_each_mesh_entry(rcu_dereference(...), ...) + */ +#define for_each_mesh_entry(tbl, p, node, i) \ + for (i = 0; i <= tbl->hash_mask; i++) \ + hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) + + static struct mesh_table *mesh_table_alloc(int size_order) { int i; @@ -258,12 +281,13 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) */ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl = rcu_dereference(mesh_paths); struct mpath_node *node; struct hlist_node *p; int i; int j = 0; - for_each_mesh_entry(mesh_paths, p, node, i) { + for_each_mesh_entry(tbl, p, node, i) { if (sdata && node->mpath->sdata != sdata) continue; if (j++ == idx) { @@ -293,6 +317,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; + struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -332,10 +357,12 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) spin_lock_init(&new_mpath->state_lock); init_timer(&new_mpath->timer); - hash_idx = mesh_table_hash(dst, sdata, mesh_paths); - bucket = &mesh_paths->hash_buckets[hash_idx]; + tbl = resize_dereference_mesh_paths(); - spin_lock_bh(&mesh_paths->hashwlock[hash_idx]); + hash_idx = mesh_table_hash(dst, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; + + spin_lock_bh(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { @@ -345,13 +372,13 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) } hlist_add_head_rcu(&new_node->list, bucket); - if (atomic_inc_return(&mesh_paths->entries) >= - mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) + if (atomic_inc_return(&tbl->entries) >= + tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; mesh_paths_generation++; - spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); @@ -360,7 +387,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: @@ -382,11 +409,11 @@ void mesh_mpath_table_grow(void) struct mesh_table *oldtbl, *newtbl; write_lock_bh(&pathtbl_resize_lock); - newtbl = mesh_table_alloc(mesh_paths->size_order + 1); + oldtbl = resize_dereference_mesh_paths(); + newtbl = mesh_table_alloc(oldtbl->size_order + 1); if (!newtbl) goto out; - oldtbl = mesh_paths; - if (mesh_table_grow(mesh_paths, newtbl) < 0) { + if (mesh_table_grow(oldtbl, newtbl) < 0) { __mesh_table_free(newtbl); goto out; } @@ -403,11 +430,11 @@ void mesh_mpp_table_grow(void) struct mesh_table *oldtbl, *newtbl; write_lock_bh(&pathtbl_resize_lock); - newtbl = mesh_table_alloc(mpp_paths->size_order + 1); + oldtbl = resize_dereference_mpp_paths(); + newtbl = mesh_table_alloc(oldtbl->size_order + 1); if (!newtbl) goto out; - oldtbl = mpp_paths; - if (mesh_table_grow(mpp_paths, newtbl) < 0) { + if (mesh_table_grow(oldtbl, newtbl) < 0) { __mesh_table_free(newtbl); goto out; } @@ -422,6 +449,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; + struct mesh_table *tbl; struct mesh_path *mpath, *new_mpath; struct mpath_node *node, *new_node; struct hlist_head *bucket; @@ -456,10 +484,12 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); - hash_idx = mesh_table_hash(dst, sdata, mpp_paths); - bucket = &mpp_paths->hash_buckets[hash_idx]; + tbl = resize_dereference_mpp_paths(); - spin_lock_bh(&mpp_paths->hashwlock[hash_idx]); + hash_idx = mesh_table_hash(dst, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; + + spin_lock_bh(&tbl->hashwlock[hash_idx]); err = -EEXIST; hlist_for_each_entry(node, n, bucket, list) { @@ -469,11 +499,11 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) } hlist_add_head_rcu(&new_node->list, bucket); - if (atomic_inc_return(&mpp_paths->entries) >= - mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) + if (atomic_inc_return(&tbl->entries) >= + tbl->mean_chain_len * (tbl->hash_mask + 1)) grow = 1; - spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); @@ -482,7 +512,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) return 0; err_exists: - spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); kfree(new_node); err_node_alloc: @@ -502,6 +532,7 @@ err_path_alloc: */ void mesh_plink_broken(struct sta_info *sta) { + struct mesh_table *tbl; static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct mesh_path *mpath; struct mpath_node *node; @@ -510,10 +541,11 @@ void mesh_plink_broken(struct sta_info *sta) int i; rcu_read_lock(); - for_each_mesh_entry(mesh_paths, p, node, i) { + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; spin_lock_bh(&mpath->state_lock); - if (mpath->next_hop == sta && + if (rcu_dereference(mpath->next_hop) == sta && mpath->flags & MESH_PATH_ACTIVE && !(mpath->flags & MESH_PATH_FIXED)) { mpath->flags &= ~MESH_PATH_ACTIVE; @@ -542,30 +574,38 @@ void mesh_plink_broken(struct sta_info *sta) */ void mesh_path_flush_by_nexthop(struct sta_info *sta) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - for_each_mesh_entry(mesh_paths, p, node, i) { + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; - if (mpath->next_hop == sta) + if (rcu_dereference(mpath->next_hop) == sta) mesh_path_del(mpath->dst, mpath->sdata); } + rcu_read_unlock(); } void mesh_path_flush(struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - for_each_mesh_entry(mesh_paths, p, node, i) { + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { mpath = node->mpath; if (mpath->sdata == sdata) mesh_path_del(mpath->dst, mpath->sdata); } + rcu_read_unlock(); } static void mesh_path_node_reclaim(struct rcu_head *rp) @@ -589,6 +629,7 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) */ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_head *bucket; @@ -597,19 +638,20 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) int err = 0; read_lock_bh(&pathtbl_resize_lock); - hash_idx = mesh_table_hash(addr, sdata, mesh_paths); - bucket = &mesh_paths->hash_buckets[hash_idx]; + tbl = resize_dereference_mesh_paths(); + hash_idx = mesh_table_hash(addr, sdata, tbl); + bucket = &tbl->hash_buckets[hash_idx]; - spin_lock_bh(&mesh_paths->hashwlock[hash_idx]); + spin_lock_bh(&tbl->hashwlock[hash_idx]); hlist_for_each_entry(node, n, bucket, list) { mpath = node->mpath; if (mpath->sdata == sdata && - memcmp(addr, mpath->dst, ETH_ALEN) == 0) { + memcmp(addr, mpath->dst, ETH_ALEN) == 0) { spin_lock_bh(&mpath->state_lock); mpath->flags |= MESH_PATH_RESOLVING; hlist_del_rcu(&node->list); call_rcu(&node->rcu, mesh_path_node_reclaim); - atomic_dec(&mesh_paths->entries); + atomic_dec(&tbl->entries); spin_unlock_bh(&mpath->state_lock); goto enddel; } @@ -618,7 +660,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) err = -ENXIO; enddel: mesh_paths_generation++; - spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); + spin_unlock_bh(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); return err; } @@ -747,52 +789,60 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) int mesh_pathtbl_init(void) { - mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); - if (!mesh_paths) + struct mesh_table *tbl_path, *tbl_mpp; + + tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); + if (!tbl_path) return -ENOMEM; - mesh_paths->free_node = &mesh_path_node_free; - mesh_paths->copy_node = &mesh_path_node_copy; - mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; + tbl_path->free_node = &mesh_path_node_free; + tbl_path->copy_node = &mesh_path_node_copy; + tbl_path->mean_chain_len = MEAN_CHAIN_LEN; - mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); - if (!mpp_paths) { - mesh_table_free(mesh_paths, true); + tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); + if (!tbl_mpp) { + mesh_table_free(tbl_path, true); return -ENOMEM; } - mpp_paths->free_node = &mesh_path_node_free; - mpp_paths->copy_node = &mesh_path_node_copy; - mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; + tbl_mpp->free_node = &mesh_path_node_free; + tbl_mpp->copy_node = &mesh_path_node_copy; + tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; + + /* Need no locking since this is during init */ + RCU_INIT_POINTER(mesh_paths, tbl_path); + RCU_INIT_POINTER(mpp_paths, tbl_mpp); return 0; } void mesh_path_expire(struct ieee80211_sub_if_data *sdata) { + struct mesh_table *tbl; struct mesh_path *mpath; struct mpath_node *node; struct hlist_node *p; int i; - read_lock_bh(&pathtbl_resize_lock); - for_each_mesh_entry(mesh_paths, p, node, i) { + rcu_read_lock(); + tbl = rcu_dereference(mesh_paths); + for_each_mesh_entry(tbl, p, node, i) { if (node->mpath->sdata != sdata) continue; mpath = node->mpath; spin_lock_bh(&mpath->state_lock); if ((!(mpath->flags & MESH_PATH_RESOLVING)) && (!(mpath->flags & MESH_PATH_FIXED)) && - time_after(jiffies, - mpath->exp_time + MESH_PATH_EXPIRE)) { + time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) { spin_unlock_bh(&mpath->state_lock); mesh_path_del(mpath->dst, mpath->sdata); } else spin_unlock_bh(&mpath->state_lock); } - read_unlock_bh(&pathtbl_resize_lock); + rcu_read_unlock(); } void mesh_pathtbl_unregister(void) { - mesh_table_free(mesh_paths, true); - mesh_table_free(mpp_paths, true); + /* no need for locking during exit path */ + mesh_table_free(rcu_dereference_raw(mesh_paths), true); + mesh_table_free(rcu_dereference_raw(mpp_paths), true); } -- cgit v1.2.3-70-g09d2 From ed9d01026f156db2d638cbb045231c7a8fde877d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 16 May 2011 19:40:15 +0300 Subject: cfg80211: Use consistent BSS matching between scan and sme cfg80211 scan code adds separate BSS entries if the same BSS shows up on multiple channels. However, sme implementation does not use the frequency when fetching the BSS entry. Fix this by adding channel information to cfg80211_roamed() and include it in cfg80211_get_bss() calls. Please note that drivers using cfg80211_roamed() need to be modified to fully implement this fix. This commit includes only minimal changes to avoid compilation issues; it maintains the old (broken) behavior for most drivers. ath6kl was the only one that I could test, so I updated it to provide the operating frequency in the roamed event. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/iwmc3200wifi/rx.c | 4 ++-- drivers/net/wireless/rndis_wlan.c | 3 ++- drivers/staging/ath6kl/os/linux/cfg80211.c | 2 +- drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c | 4 ++-- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 5 ++++- net/wireless/core.h | 5 ++++- net/wireless/sme.c | 19 ++++++++++++++----- net/wireless/util.c | 2 +- 9 files changed, 31 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 5665a1a9b99..a414768f40f 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -565,7 +565,7 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status) && iwm->conf.mode == UMAC_MODE_BSS) { cancel_delayed_work(&iwm->disconnect); - cfg80211_roamed(iwm_to_ndev(iwm), + cfg80211_roamed(iwm_to_ndev(iwm), NULL, complete->bssid, iwm->req_ie, iwm->req_ie_len, iwm->resp_ie, iwm->resp_ie_len, @@ -586,7 +586,7 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, WLAN_STATUS_SUCCESS, GFP_KERNEL); else - cfg80211_roamed(iwm_to_ndev(iwm), + cfg80211_roamed(iwm_to_ndev(iwm), NULL, complete->bssid, iwm->req_ie, iwm->req_ie_len, iwm->resp_ie, iwm->resp_ie_len, diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 518542b4bf9..29f93893066 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2830,7 +2830,8 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) req_ie_len, resp_ie, resp_ie_len, 0, GFP_KERNEL); else - cfg80211_roamed(usbdev->net, bssid, req_ie, req_ie_len, + cfg80211_roamed(usbdev->net, NULL, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, GFP_KERNEL); } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL); diff --git a/drivers/staging/ath6kl/os/linux/cfg80211.c b/drivers/staging/ath6kl/os/linux/cfg80211.c index bcca39418f9..5bda24e26c0 100644 --- a/drivers/staging/ath6kl/os/linux/cfg80211.c +++ b/drivers/staging/ath6kl/os/linux/cfg80211.c @@ -570,7 +570,7 @@ ar6k_cfg80211_connect_event(struct ar6_softc *ar, u16 channel, WLAN_STATUS_SUCCESS, GFP_KERNEL); } else { /* inform roam event to cfg80211 */ - cfg80211_roamed(ar->arNetDev, bssid, + cfg80211_roamed(ar->arNetDev, ibss_channel, bssid, assocReqIe, assocReqLen, assocRespIe, assocRespLen, GFP_KERNEL); diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c index 555b056b49b..7aaf99cc3a7 100644 --- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c @@ -2630,7 +2630,7 @@ wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, wl_get_assoc_ies(wl); memcpy(&wl->bssid, &e->addr, ETH_ALEN); wl_update_bss_info(wl); - cfg80211_roamed(ndev, + cfg80211_roamed(ndev, NULL, (u8 *)&wl->bssid, conn_info->req_ie, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); @@ -2663,7 +2663,7 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, WL_DBG("Report connect result - connection %s\n", completed ? "succeeded" : "failed"); } else { - cfg80211_roamed(ndev, + cfg80211_roamed(ndev, NULL, (u8 *)&wl->bssid, conn_info->req_ie, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 6a71f52c59b..473438893a1 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -695,7 +695,7 @@ void prism2_disconnected(wlandevice_t *wlandev) void prism2_roamed(wlandevice_t *wlandev) { - cfg80211_roamed(wlandev->netdev, wlandev->bssid, + cfg80211_roamed(wlandev->netdev, NULL, wlandev->bssid, NULL, 0, NULL, 0, GFP_KERNEL); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bfd6557946b..727131b6742 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2878,6 +2878,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * cfg80211_roamed - notify cfg80211 of roaming * * @dev: network device + * @channel: the channel of the new AP * @bssid: the BSSID of the new AP * @req_ie: association request IEs (maybe be %NULL) * @req_ie_len: association request IEs length @@ -2888,7 +2889,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * It should be called by the underlying driver whenever it roamed * from one AP to another while connected. */ -void cfg80211_roamed(struct net_device *dev, const u8 *bssid, +void cfg80211_roamed(struct net_device *dev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); diff --git a/net/wireless/core.h b/net/wireless/core.h index bf0fb40e3c8..3dce1f167eb 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -245,6 +245,7 @@ struct cfg80211_event { u16 status; } cr; struct { + struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; const u8 *req_ie; const u8 *resp_ie; @@ -392,7 +393,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); -void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, +void __cfg80211_roamed(struct wireless_dev *wdev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index e17b0bee6bd..b7b6ff8be55 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -250,7 +250,8 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) if (wdev->conn->params.privacy) capa |= WLAN_CAPABILITY_PRIVACY; - bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, + bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, + wdev->conn->params.bssid, wdev->conn->params.ssid, wdev->conn->params.ssid_len, WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, @@ -470,7 +471,10 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, } if (!bss) - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + bss = cfg80211_get_bss(wdev->wiphy, + wdev->conn ? wdev->conn->params.channel : + NULL, + bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -538,7 +542,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, } EXPORT_SYMBOL(cfg80211_connect_result); -void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, +void __cfg80211_roamed(struct wireless_dev *wdev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len) { @@ -565,7 +571,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -603,7 +609,9 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, #endif } -void cfg80211_roamed(struct net_device *dev, const u8 *bssid, +void cfg80211_roamed(struct net_device *dev, + struct ieee80211_channel *channel, + const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) { @@ -619,6 +627,7 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, return; ev->type = EVENT_ROAMED; + ev->rm.channel = channel; memcpy(ev->rm.bssid, bssid, ETH_ALEN); ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); ev->rm.req_ie_len = req_ie_len; diff --git a/net/wireless/util.c b/net/wireless/util.c index f0536d44d43..4d7b83fbc32 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -746,7 +746,7 @@ static void cfg80211_process_wdev_events(struct wireless_dev *wdev) NULL); break; case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bssid, + __cfg80211_roamed(wdev, ev->rm.channel, ev->rm.bssid, ev->rm.req_ie, ev->rm.req_ie_len, ev->rm.resp_ie, ev->rm.resp_ie_len); break; -- cgit v1.2.3-70-g09d2 From 7176ba23f8b589b1df3229574ff46fb904ce9ec5 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 16 May 2011 14:41:48 -0700 Subject: net: rfkill: add generic gpio rfkill driver This adds a new generic gpio rfkill driver to support rfkill switches which are controlled by gpios. The driver also supports passing in data about the clock for the radio, so that when rfkill is blocking, it can disable the clock. This driver assumes platform data is passed from the board files to configure it for specific devices. Original-patch-by: Anantha Idapalapati Signed-off-by: Rhyland Klein Signed-off-by: John W. Linville --- include/linux/rfkill-gpio.h | 43 +++++++++ net/rfkill/Kconfig | 9 ++ net/rfkill/Makefile | 1 + net/rfkill/rfkill-gpio.c | 227 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 include/linux/rfkill-gpio.h create mode 100644 net/rfkill/rfkill-gpio.c (limited to 'net') diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h new file mode 100644 index 00000000000..a175d059803 --- /dev/null +++ b/include/linux/rfkill-gpio.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __RFKILL_GPIO_H +#define __RFKILL_GPIO_H + +#include +#include + +/** + * struct rfkill_gpio_platform_data - platform data for rfkill gpio device. + * for unused gpio's, the expected value is -1. + * @name: name for the gpio rf kill instance + * @reset_gpio: GPIO which is used for reseting rfkill switch + * @shutdown_gpio: GPIO which is used for shutdown of rfkill switch + * @power_clk_name: [optional] name of clk to turn off while blocked + */ + +struct rfkill_gpio_platform_data { + char *name; + int reset_gpio; + int shutdown_gpio; + const char *power_clk_name; + enum rfkill_type type; +}; + +#endif /* __RFKILL_GPIO_H */ diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 48464ca13b2..78efe895b66 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -33,3 +33,12 @@ config RFKILL_REGULATOR To compile this driver as a module, choose M here: the module will be called rfkill-regulator. + +config RFKILL_GPIO + tristate "GPIO RFKILL driver" + depends on RFKILL && GPIOLIB && HAVE_CLK + default n + help + If you say yes here you get support of a generic gpio RFKILL + driver. The platform should fill in the appropriate fields in the + rfkill_gpio_platform_data structure and pass that to the driver. diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index d9a5a58ffd8..311768783f4 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -6,3 +6,4 @@ rfkill-y += core.o rfkill-$(CONFIG_RFKILL_INPUT) += input.o obj-$(CONFIG_RFKILL) += rfkill.o obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o +obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c new file mode 100644 index 00000000000..256c5ddd2d7 --- /dev/null +++ b/net/rfkill/rfkill-gpio.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum rfkill_gpio_clk_state { + UNSPECIFIED = 0, + PWR_ENABLED, + PWR_DISABLED +}; + +#define PWR_CLK_SET(_RF, _EN) \ + ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED)) +#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED) +#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED) + +struct rfkill_gpio_data { + struct rfkill_gpio_platform_data *pdata; + struct rfkill *rfkill_dev; + char *reset_name; + char *shutdown_name; + enum rfkill_gpio_clk_state pwr_clk_enabled; + struct clk *pwr_clk; +}; + +static int rfkill_gpio_set_power(void *data, bool blocked) +{ + struct rfkill_gpio_data *rfkill = data; + + if (blocked) { + if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) + gpio_direction_output(rfkill->pdata->shutdown_gpio, 0); + if (gpio_is_valid(rfkill->pdata->reset_gpio)) + gpio_direction_output(rfkill->pdata->reset_gpio, 0); + if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) + clk_disable(rfkill->pwr_clk); + } else { + if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill)) + clk_enable(rfkill->pwr_clk); + if (gpio_is_valid(rfkill->pdata->reset_gpio)) + gpio_direction_output(rfkill->pdata->reset_gpio, 1); + if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) + gpio_direction_output(rfkill->pdata->shutdown_gpio, 1); + } + + if (rfkill->pwr_clk) + PWR_CLK_SET(rfkill, blocked); + + return 0; +} + +static const struct rfkill_ops rfkill_gpio_ops = { + .set_block = rfkill_gpio_set_power, +}; + +static int rfkill_gpio_probe(struct platform_device *pdev) +{ + struct rfkill_gpio_data *rfkill; + struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + int len = 0; + + if (!pdata) { + pr_warn("%s: No platform data specified\n", __func__); + return -EINVAL; + } + + /* make sure at-least one of the GPIO is defined and that + * a name is specified for this instance */ + if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) && + !gpio_is_valid(pdata->shutdown_gpio))) { + pr_warn("%s: invalid platform data\n", __func__); + return -EINVAL; + } + + rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); + if (!rfkill) + return -ENOMEM; + + rfkill->pdata = pdata; + + len = strlen(pdata->name); + rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL); + if (!rfkill->reset_name) { + ret = -ENOMEM; + goto fail_alloc; + } + + rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL); + if (!rfkill->shutdown_name) { + ret = -ENOMEM; + goto fail_reset_name; + } + + snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name); + snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name); + + if (pdata->power_clk_name) { + rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name); + if (IS_ERR(rfkill->pwr_clk)) { + pr_warn("%s: can't find pwr_clk.\n", __func__); + goto fail_shutdown_name; + } + } + + if (gpio_is_valid(pdata->reset_gpio)) { + ret = gpio_request(pdata->reset_gpio, rfkill->reset_name); + if (ret) { + pr_warn("%s: failed to get reset gpio.\n", __func__); + goto fail_clock; + } + } + + if (gpio_is_valid(pdata->shutdown_gpio)) { + ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name); + if (ret) { + pr_warn("%s: failed to get shutdown gpio.\n", __func__); + goto fail_reset; + } + } + + rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, + &rfkill_gpio_ops, rfkill); + if (!rfkill->rfkill_dev) + goto fail_shutdown; + + ret = rfkill_register(rfkill->rfkill_dev); + if (ret < 0) + goto fail_rfkill; + + platform_set_drvdata(pdev, rfkill); + + dev_info(&pdev->dev, "%s device registered.\n", pdata->name); + + return 0; + +fail_rfkill: + rfkill_destroy(rfkill->rfkill_dev); +fail_shutdown: + if (gpio_is_valid(pdata->shutdown_gpio)) + gpio_free(pdata->shutdown_gpio); +fail_reset: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); +fail_clock: + if (rfkill->pwr_clk) + clk_put(rfkill->pwr_clk); +fail_shutdown_name: + kfree(rfkill->shutdown_name); +fail_reset_name: + kfree(rfkill->reset_name); +fail_alloc: + kfree(rfkill); + + return ret; +} + +static int rfkill_gpio_remove(struct platform_device *pdev) +{ + struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); + + rfkill_unregister(rfkill->rfkill_dev); + rfkill_destroy(rfkill->rfkill_dev); + if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) + gpio_free(rfkill->pdata->shutdown_gpio); + if (gpio_is_valid(rfkill->pdata->reset_gpio)) + gpio_free(rfkill->pdata->reset_gpio); + if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) + clk_disable(rfkill->pwr_clk); + if (rfkill->pwr_clk) + clk_put(rfkill->pwr_clk); + kfree(rfkill->shutdown_name); + kfree(rfkill->reset_name); + kfree(rfkill); + + return 0; +} + +static struct platform_driver rfkill_gpio_driver = { + .probe = rfkill_gpio_probe, + .remove = __devexit_p(rfkill_gpio_remove), + .driver = { + .name = "rfkill_gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init rfkill_gpio_init(void) +{ + return platform_driver_register(&rfkill_gpio_driver); +} + +static void __exit rfkill_gpio_exit(void) +{ + platform_driver_unregister(&rfkill_gpio_driver); +} + +module_init(rfkill_gpio_init); +module_exit(rfkill_gpio_exit); + +MODULE_DESCRIPTION("gpio rfkill"); +MODULE_AUTHOR("NVIDIA"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From d676ff493d9dfb8b34892214665028a8c85e2056 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Tue, 17 May 2011 16:13:34 -0700 Subject: mac80211: Don't sleep when growing the mesh path After commit 1928ecab620907a0953f811316d05f367f3f4dba (mac80211: fix and simplify mesh locking) mesh table allocation is performed with the pathtbl_resize_lock taken. Under those conditions one should not sleep. This patch makes the allocations GFP_ATOMIC to prevent that. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/mesh_pathtbl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 2bda0ac6232..51f6fe86d89 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -76,12 +76,12 @@ static struct mesh_table *mesh_table_alloc(int size_order) int i; struct mesh_table *newtbl; - newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL); + newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); if (!newtbl) return NULL; newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * - (1 << size_order), GFP_KERNEL); + (1 << size_order), GFP_ATOMIC); if (!newtbl->hash_buckets) { kfree(newtbl); @@ -89,7 +89,7 @@ static struct mesh_table *mesh_table_alloc(int size_order) } newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * - (1 << size_order), GFP_KERNEL); + (1 << size_order), GFP_ATOMIC); if (!newtbl->hashwlock) { kfree(newtbl->hash_buckets); kfree(newtbl); -- cgit v1.2.3-70-g09d2 From a2cd43c52aa5c676b03d575177536e05ac672c75 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 18 May 2011 11:42:03 +0300 Subject: nl80211: remove some stack variables in trigger_scan and start_sched_scan Some stack variables (name *ssid and *channel) are only used to define the size of the memory block that needs to be allocated for the request structure in the nl80211_trigger_scan() and nl80211_start_sched_scan() functions. This is unnecessary because the sizes of the actual elements in the structure can be used instead. Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2222ce08ee9..ec83f413a7e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3294,8 +3294,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct cfg80211_scan_request *request; - struct cfg80211_ssid *ssid; - struct ieee80211_channel *channel; struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels, i; @@ -3342,8 +3340,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) return -EINVAL; request = kzalloc(sizeof(*request) - + sizeof(*ssid) * n_ssids - + sizeof(channel) * n_channels + + sizeof(*request->ssids) * n_ssids + + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (!request) return -ENOMEM; @@ -3449,8 +3447,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, struct cfg80211_sched_scan_request *request; struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - struct cfg80211_ssid *ssid; - struct ieee80211_channel *channel; struct nlattr *attr; struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_channels, i; @@ -3507,8 +3503,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, return -EINVAL; request = kzalloc(sizeof(*request) - + sizeof(*ssid) * n_ssids - + sizeof(channel) * n_channels + + sizeof(*request->ssids) * n_ssids + + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (!request) return -ENOMEM; -- cgit v1.2.3-70-g09d2