* @workqueue: single threaded workqueue available for driver use,
  *     allocated by mac80211 on registration and flushed when an
  *     interface is removed.
- *     NOTICE: All work performed on this workqueue should NEVER
- *     acquire the RTNL lock (i.e. Don't use the function
- *     ieee80211_iterate_active_interfaces())
+ *     NOTICE: All work performed on this workqueue must not
+ *     acquire the RTNL lock.
  *
  * @priv: pointer to private area that was allocated for driver use
  *     along with this structure.
 
 #include "mesh.h"
 #include "led.h"
 
+/**
+ * DOC: Interface list locking
+ *
+ * The interface list in each struct ieee80211_local is protected
+ * three-fold:
+ *
+ * (1) modifications may only be done under the RTNL
+ * (2) modifications and readers are protected against each other by
+ *     the iflist_mtx.
+ * (3) modifications are done in an RCU manner so atomic readers
+ *     can traverse the list in RCU-safe blocks.
+ *
+ * As a consequence, reads (traversals) of the list can be protected
+ * by either the RTNL, the iflist_mtx or RCU.
+ */
+
+
 static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 {
        int meshhdrlen;
                                            params->mesh_id_len,
                                            params->mesh_id);
 
+       mutex_lock(&local->iflist_mtx);
        list_add_tail_rcu(&sdata->list, &local->interfaces);
+       mutex_unlock(&local->iflist_mtx);
 
        if (new_dev)
                *new_dev = ndev;
 {
        ASSERT_RTNL();
 
+       mutex_lock(&sdata->local->iflist_mtx);
        list_del_rcu(&sdata->list);
+       mutex_unlock(&sdata->local->iflist_mtx);
+
        synchronize_rcu();
        unregister_netdevice(sdata->dev);
 }
        ASSERT_RTNL();
 
        list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+               /*
+                * we cannot hold the iflist_mtx across unregister_netdevice,
+                * but we only need to hold it for list modifications to lock
+                * out readers since we're under the RTNL here as all other
+                * writers.
+                */
+               mutex_lock(&local->iflist_mtx);
                list_del(&sdata->list);
+               mutex_unlock(&local->iflist_mtx);
+
                unregister_netdevice(sdata->dev);
        }
 }
 
        local->hw.conf.radio_enabled = true;
 
        INIT_LIST_HEAD(&local->interfaces);
+       mutex_init(&local->iflist_mtx);
 
        spin_lock_init(&local->key_lock);
 
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
+       mutex_destroy(&local->iflist_mtx);
+
        wiphy_free(local->hw.wiphy);
 }
 EXPORT_SYMBOL(ieee80211_free_hw);
 
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
 
-       rtnl_lock();
+       mutex_lock(&local->iflist_mtx);
 
        list_for_each_entry(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                                 &sdata->vif);
        }
 
-       rtnl_unlock();
+       mutex_unlock(&local->iflist_mtx);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);