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 | |
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')
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/iface.c | 31 | ||||
-rw-r--r-- | net/mac80211/main.c | 3 | ||||
-rw-r--r-- | net/mac80211/util.c | 4 |
4 files changed, 38 insertions, 2 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 927cbde8c19c..eaf3603862b7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -643,7 +643,9 @@ struct ieee80211_local { | |||
643 | struct crypto_blkcipher *wep_rx_tfm; | 643 | struct crypto_blkcipher *wep_rx_tfm; |
644 | u32 wep_iv; | 644 | u32 wep_iv; |
645 | 645 | ||
646 | /* see iface.c */ | ||
646 | struct list_head interfaces; | 647 | struct list_head interfaces; |
648 | struct mutex iflist_mtx; | ||
647 | 649 | ||
648 | /* | 650 | /* |
649 | * Key lock, protects sdata's key_list and sta_info's | 651 | * Key lock, protects sdata's key_list and sta_info's |
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 | } |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 210dfe3cf6c3..a109c06e8e4e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -758,6 +758,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
758 | local->hw.conf.radio_enabled = true; | 758 | local->hw.conf.radio_enabled = true; |
759 | 759 | ||
760 | INIT_LIST_HEAD(&local->interfaces); | 760 | INIT_LIST_HEAD(&local->interfaces); |
761 | mutex_init(&local->iflist_mtx); | ||
761 | 762 | ||
762 | spin_lock_init(&local->key_lock); | 763 | spin_lock_init(&local->key_lock); |
763 | 764 | ||
@@ -1008,6 +1009,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) | |||
1008 | { | 1009 | { |
1009 | struct ieee80211_local *local = hw_to_local(hw); | 1010 | struct ieee80211_local *local = hw_to_local(hw); |
1010 | 1011 | ||
1012 | mutex_destroy(&local->iflist_mtx); | ||
1013 | |||
1011 | wiphy_free(local->hw.wiphy); | 1014 | wiphy_free(local->hw.wiphy); |
1012 | } | 1015 | } |
1013 | EXPORT_SYMBOL(ieee80211_free_hw); | 1016 | EXPORT_SYMBOL(ieee80211_free_hw); |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index fc30f2940e1e..73c7d7345abd 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -468,7 +468,7 @@ void ieee80211_iterate_active_interfaces( | |||
468 | struct ieee80211_local *local = hw_to_local(hw); | 468 | struct ieee80211_local *local = hw_to_local(hw); |
469 | struct ieee80211_sub_if_data *sdata; | 469 | struct ieee80211_sub_if_data *sdata; |
470 | 470 | ||
471 | rtnl_lock(); | 471 | mutex_lock(&local->iflist_mtx); |
472 | 472 | ||
473 | list_for_each_entry(sdata, &local->interfaces, list) { | 473 | list_for_each_entry(sdata, &local->interfaces, list) { |
474 | switch (sdata->vif.type) { | 474 | switch (sdata->vif.type) { |
@@ -489,7 +489,7 @@ void ieee80211_iterate_active_interfaces( | |||
489 | &sdata->vif); | 489 | &sdata->vif); |
490 | } | 490 | } |
491 | 491 | ||
492 | rtnl_unlock(); | 492 | mutex_unlock(&local->iflist_mtx); |
493 | } | 493 | } |
494 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); | 494 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); |
495 | 495 | ||