These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / wireless / mlme.c
index 7aae329..fb44fa3 100644 (file)
@@ -2,6 +2,7 @@
  * cfg80211 MLME SAP interface
  *
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015          Intel Deutschland GmbH
  */
 
 #include <linux/kernel.h>
@@ -389,6 +390,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
 
 struct cfg80211_mgmt_registration {
        struct list_head list;
+       struct wireless_dev *wdev;
 
        u32 nlportid;
 
@@ -399,6 +401,46 @@ struct cfg80211_mgmt_registration {
        u8 match[];
 };
 
+static void
+cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_mgmt_registration *reg;
+
+       ASSERT_RTNL();
+
+       spin_lock_bh(&rdev->mlme_unreg_lock);
+       while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
+                                              struct cfg80211_mgmt_registration,
+                                              list))) {
+               list_del(&reg->list);
+               spin_unlock_bh(&rdev->mlme_unreg_lock);
+
+               if (rdev->ops->mgmt_frame_register) {
+                       u16 frame_type = le16_to_cpu(reg->frame_type);
+
+                       rdev_mgmt_frame_register(rdev, reg->wdev,
+                                                frame_type, false);
+               }
+
+               kfree(reg);
+
+               spin_lock_bh(&rdev->mlme_unreg_lock);
+       }
+       spin_unlock_bh(&rdev->mlme_unreg_lock);
+}
+
+void cfg80211_mlme_unreg_wk(struct work_struct *wk)
+{
+       struct cfg80211_registered_device *rdev;
+
+       rdev = container_of(wk, struct cfg80211_registered_device,
+                           mlme_unreg_wk);
+
+       rtnl_lock();
+       cfg80211_process_mlme_unregistrations(rdev);
+       rtnl_unlock();
+}
+
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
                                u16 frame_type, const u8 *match_data,
                                int match_len)
@@ -449,11 +491,18 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
        nreg->match_len = match_len;
        nreg->nlportid = snd_portid;
        nreg->frame_type = cpu_to_le16(frame_type);
+       nreg->wdev = wdev;
        list_add(&nreg->list, &wdev->mgmt_registrations);
+       spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+       /* process all unregistrations to avoid driver confusion */
+       cfg80211_process_mlme_unregistrations(rdev);
 
        if (rdev->ops->mgmt_frame_register)
                rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
 
+       return 0;
+
  out:
        spin_unlock_bh(&wdev->mgmt_registrations_lock);
 
@@ -472,15 +521,12 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
                if (reg->nlportid != nlportid)
                        continue;
 
-               if (rdev->ops->mgmt_frame_register) {
-                       u16 frame_type = le16_to_cpu(reg->frame_type);
-
-                       rdev_mgmt_frame_register(rdev, wdev,
-                                                frame_type, false);
-               }
-
                list_del(&reg->list);
-               kfree(reg);
+               spin_lock(&rdev->mlme_unreg_lock);
+               list_add_tail(&reg->list, &rdev->mlme_unreg);
+               spin_unlock(&rdev->mlme_unreg_lock);
+
+               schedule_work(&rdev->mlme_unreg_wk);
        }
 
        spin_unlock_bh(&wdev->mgmt_registrations_lock);
@@ -496,16 +542,15 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
 
 void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
 {
-       struct cfg80211_mgmt_registration *reg, *tmp;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        spin_lock_bh(&wdev->mgmt_registrations_lock);
-
-       list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
-               list_del(&reg->list);
-               kfree(reg);
-       }
-
+       spin_lock(&rdev->mlme_unreg_lock);
+       list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
+       spin_unlock(&rdev->mlme_unreg_lock);
        spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+       cfg80211_process_mlme_unregistrations(rdev);
 }
 
 int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,