From 3b96766f0e643f52ae19e134664df6730c737e87 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Apr 2008 17:56:52 +0200 Subject: mac80211: fix key vs. sta locking problems Up to now, key manipulation is supposed to run under RTNL to avoid concurrent manipulations and also allow the set_key() hardware callback to sleep. This is not feasible because STA structs are rcu-protected and thus a lot of operations there cannot take the RTNL. Also, key references are rcu-protected so we cannot do things atomically. This patch changes key locking completely: * key operations are now atomic * hardware crypto offload is enabled and disabled from a workqueue, due to that key freeing is also delayed * debugfs code is also run from a workqueue * keys reference STAs (and vice versa!) so during STA unlink the STAs key reference is removed but not the keys STA reference, to avoid races key todo work is run before STA destruction. * fewer STA operations now need the RTNL which was required due to key operations This fixes the locking problems lockdep pointed out and also makes things more light-weight because the rtnl isn't required as much. Note that the key todo lock/key mutex are global locks, this is not required, of course, they could be per-hardware instead. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_ioctl.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'net/mac80211/ieee80211_ioctl.c') diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c index 41130b303170..a611c8bc76bd 100644 --- a/net/mac80211/ieee80211_ioctl.c +++ b/net/mac80211/ieee80211_ioctl.c @@ -36,6 +36,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, struct sta_info *sta; struct ieee80211_key *key; struct ieee80211_sub_if_data *sdata; + int err; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -46,23 +47,31 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, } if (remove) { + rcu_read_lock(); + + err = 0; + if (is_broadcast_ether_addr(sta_addr)) { key = sdata->keys[idx]; } else { sta = sta_info_get(local, sta_addr); - if (!sta) - return -ENOENT; + if (!sta) { + err = -ENOENT; + goto out_unlock; + } key = sta->key; } ieee80211_key_free(key); - return 0; } else { key = ieee80211_key_alloc(alg, idx, key_len, _key); if (!key) return -ENOMEM; sta = NULL; + err = 0; + + rcu_read_lock(); if (!is_broadcast_ether_addr(sta_addr)) { set_tx_key = 0; @@ -74,13 +83,15 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, */ if (idx != 0 && alg != ALG_WEP) { ieee80211_key_free(key); - return -EINVAL; + err = -EINVAL; + goto out_unlock; } sta = sta_info_get(local, sta_addr); if (!sta) { ieee80211_key_free(key); - return -ENOENT; + err = -ENOENT; + goto out_unlock; } } @@ -90,7 +101,10 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, ieee80211_set_default_key(sdata, idx); } - return 0; + out_unlock: + rcu_read_unlock(); + + return err; } static int ieee80211_ioctl_siwgenie(struct net_device *dev, -- cgit v1.2.2