Skip to content

Commit

Permalink
mac80211: batch key free synchronize_net()
Browse files Browse the repository at this point in the history
Instead of calling synchronize_net() for every key
on an interface or when a station is removed, do it
only once for all keys in both of these cases.

As a side-effect, removing station keys now always
calls synchronize_net() even if there are no keys,
which fixes an issue with station removal happening
in the driver while the station could still be used
for TX.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Johannes Berg committed Mar 11, 2013
1 parent 3b8d9c2 commit 6d10e46
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 22 deletions.
81 changes: 68 additions & 13 deletions net/mac80211/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,18 +406,9 @@ static void ieee80211_key_free_common(struct ieee80211_key *key)
kfree(key);
}

static void ieee80211_key_destroy(struct ieee80211_key *key,
bool delay_tailroom)
static void __ieee80211_key_destroy(struct ieee80211_key *key,
bool delay_tailroom)
{
if (!key)
return;

/*
* Synchronize so the TX path can no longer be using
* this key before we free/remove it.
*/
synchronize_net();

if (key->local)
ieee80211_key_disable_hw_accel(key);

Expand All @@ -439,6 +430,21 @@ static void ieee80211_key_destroy(struct ieee80211_key *key,
ieee80211_key_free_common(key);
}

static void ieee80211_key_destroy(struct ieee80211_key *key,
bool delay_tailroom)
{
if (!key)
return;

/*
* Synchronize so the TX path can no longer be using
* this key before we free/remove it.
*/
synchronize_net();

__ieee80211_key_destroy(key, delay_tailroom);
}

void ieee80211_key_free_unused(struct ieee80211_key *key)
{
WARN_ON(key->sdata || key->local);
Expand Down Expand Up @@ -560,6 +566,7 @@ EXPORT_SYMBOL(ieee80211_iter_keys);
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_key *key, *tmp;
LIST_HEAD(keys);

cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);

Expand All @@ -571,17 +578,65 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)

ieee80211_debugfs_key_remove_mgmt_default(sdata);

list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
ieee80211_key_free(key, false);
list_for_each_entry_safe(key, tmp, &sdata->key_list, list) {
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
list_add_tail(&key->list, &keys);
}

ieee80211_debugfs_key_update_default(sdata);

if (!list_empty(&keys)) {
synchronize_net();
list_for_each_entry_safe(key, tmp, &keys, list)
__ieee80211_key_destroy(key, false);
}

WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
sdata->crypto_tx_tailroom_pending_dec);

mutex_unlock(&sdata->local->key_mtx);
}

void ieee80211_free_sta_keys(struct ieee80211_local *local,
struct sta_info *sta)
{
struct ieee80211_key *key, *tmp;
LIST_HEAD(keys);
int i;

mutex_lock(&local->key_mtx);
for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
key = key_mtx_dereference(local, sta->gtk[i]);
if (!key)
continue;
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
list_add(&key->list, &keys);
}

key = key_mtx_dereference(local, sta->ptk);
if (key) {
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
list_add(&key->list, &keys);
}

/*
* NB: the station code relies on this being
* done even if there aren't any keys
*/
synchronize_net();

list_for_each_entry_safe(key, tmp, &keys, list)
__ieee80211_key_destroy(key, true);

mutex_unlock(&local->key_mtx);
}

void ieee80211_delayed_tailroom_dec(struct work_struct *wk)
{
struct ieee80211_sub_if_data *sdata;
Expand Down
2 changes: 2 additions & 0 deletions net/mac80211/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
int idx);
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
void ieee80211_free_sta_keys(struct ieee80211_local *local,
struct sta_info *sta);
void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);

#define key_mtx_dereference(local, ref) \
Expand Down
12 changes: 3 additions & 9 deletions net/mac80211/sta_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
{
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
int ret, i;
int ret;

might_sleep();

Expand All @@ -810,14 +810,8 @@ int __must_check __sta_info_destroy(struct sta_info *sta)

list_del_rcu(&sta->list);

mutex_lock(&local->key_mtx);
for (i = 0; i < NUM_DEFAULT_KEYS; i++)
ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i]),
true);
if (sta->ptk)
ieee80211_key_free(key_mtx_dereference(local, sta->ptk),
true);
mutex_unlock(&local->key_mtx);
/* this always calls synchronize_net() */
ieee80211_free_sta_keys(local, sta);

sta->dead = true;

Expand Down

0 comments on commit 6d10e46

Please sign in to comment.