diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-01-23 16:54:03 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 16:01:45 -0500 |
commit | c771c9d8da1e8292ef8bf7fd4ce135dacc650130 (patch) | |
tree | 79b259a6b0396cbecf9e775c7ba5a80e2c4d94c9 /net/mac80211/iface.c | |
parent | 506d03f97d10e54fd27c58c872a98242326d6419 (diff) |
mac80211: add interface list lock
Using only the RTNL has a number of problems, most notably that
ieee80211_iterate_active_interfaces() and other interface list
traversals cannot be done from the internal workqueue because it
needs to be flushed under the RTNL.
This patch introduces a new mutex that protects the interface list
against modifications. A more detailed explanation is part of the
code change.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r-- | net/mac80211/iface.c | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8dc2c2188d92..00562a8b99cf 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -21,6 +21,23 @@ | |||
21 | #include "mesh.h" | 21 | #include "mesh.h" |
22 | #include "led.h" | 22 | #include "led.h" |
23 | 23 | ||
24 | /** | ||
25 | * DOC: Interface list locking | ||
26 | * | ||
27 | * The interface list in each struct ieee80211_local is protected | ||
28 | * three-fold: | ||
29 | * | ||
30 | * (1) modifications may only be done under the RTNL | ||
31 | * (2) modifications and readers are protected against each other by | ||
32 | * the iflist_mtx. | ||
33 | * (3) modifications are done in an RCU manner so atomic readers | ||
34 | * can traverse the list in RCU-safe blocks. | ||
35 | * | ||
36 | * As a consequence, reads (traversals) of the list can be protected | ||
37 | * by either the RTNL, the iflist_mtx or RCU. | ||
38 | */ | ||
39 | |||
40 | |||
24 | static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) | 41 | static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) |
25 | { | 42 | { |
26 | int meshhdrlen; | 43 | int meshhdrlen; |
@@ -800,7 +817,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, | |||
800 | params->mesh_id_len, | 817 | params->mesh_id_len, |
801 | params->mesh_id); | 818 | params->mesh_id); |
802 | 819 | ||
820 | mutex_lock(&local->iflist_mtx); | ||
803 | list_add_tail_rcu(&sdata->list, &local->interfaces); | 821 | list_add_tail_rcu(&sdata->list, &local->interfaces); |
822 | mutex_unlock(&local->iflist_mtx); | ||
804 | 823 | ||
805 | if (new_dev) | 824 | if (new_dev) |
806 | *new_dev = ndev; | 825 | *new_dev = ndev; |
@@ -816,7 +835,10 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) | |||
816 | { | 835 | { |
817 | ASSERT_RTNL(); | 836 | ASSERT_RTNL(); |
818 | 837 | ||
838 | mutex_lock(&sdata->local->iflist_mtx); | ||
819 | list_del_rcu(&sdata->list); | 839 | list_del_rcu(&sdata->list); |
840 | mutex_unlock(&sdata->local->iflist_mtx); | ||
841 | |||
820 | synchronize_rcu(); | 842 | synchronize_rcu(); |
821 | unregister_netdevice(sdata->dev); | 843 | unregister_netdevice(sdata->dev); |
822 | } | 844 | } |
@@ -832,7 +854,16 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) | |||
832 | ASSERT_RTNL(); | 854 | ASSERT_RTNL(); |
833 | 855 | ||
834 | list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { | 856 | list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { |
857 | /* | ||
858 | * we cannot hold the iflist_mtx across unregister_netdevice, | ||
859 | * but we only need to hold it for list modifications to lock | ||
860 | * out readers since we're under the RTNL here as all other | ||
861 | * writers. | ||
862 | */ | ||
863 | mutex_lock(&local->iflist_mtx); | ||
835 | list_del(&sdata->list); | 864 | list_del(&sdata->list); |
865 | mutex_unlock(&local->iflist_mtx); | ||
866 | |||
836 | unregister_netdevice(sdata->dev); | 867 | unregister_netdevice(sdata->dev); |
837 | } | 868 | } |
838 | } | 869 | } |