Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / offchannel.c
index cc79b4a2e821c9232c41135da3f4397dbba67b8d..cce795871ab1fa2373a7828d4c9872695bb0ef1e 100644 (file)
@@ -118,9 +118,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
         * Stop queues and transmit all frames queued by the driver
         * before sending nullfunc to enable powersave at the AP.
         */
-       ieee80211_stop_queues_by_reason(&local->hw,
+       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
-       drv_flush(local, false);
+       ieee80211_flush_queues(local, NULL);
 
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
@@ -181,7 +181,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
        }
        mutex_unlock(&local->iflist_mtx);
 
-       ieee80211_wake_queues_by_reason(&local->hw,
+       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
 }
 
@@ -277,7 +277,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
                        duration = 10;
 
                ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
-                                           duration);
+                                           duration, roc->type);
 
                roc->started = true;
 
@@ -297,10 +297,13 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
        }
 }
 
-void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
+void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free)
 {
        struct ieee80211_roc_work *dep, *tmp;
 
+       if (WARN_ON(roc->to_be_freed))
+               return;
+
        /* was never transmitted */
        if (roc->frame) {
                cfg80211_mgmt_tx_status(&roc->sdata->wdev,
@@ -316,9 +319,12 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
                                                   GFP_KERNEL);
 
        list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
-               ieee80211_roc_notify_destroy(dep);
+               ieee80211_roc_notify_destroy(dep, true);
 
-       kfree(roc);
+       if (free)
+               kfree(roc);
+       else
+               roc->to_be_freed = true;
 }
 
 void ieee80211_sw_roc_work(struct work_struct *work)
@@ -331,6 +337,9 @@ void ieee80211_sw_roc_work(struct work_struct *work)
 
        mutex_lock(&local->mtx);
 
+       if (roc->to_be_freed)
+               goto out_unlock;
+
        if (roc->abort)
                goto finish;
 
@@ -370,10 +379,10 @@ void ieee80211_sw_roc_work(struct work_struct *work)
  finish:
                list_del(&roc->list);
                started = roc->started;
-               ieee80211_roc_notify_destroy(roc);
+               ieee80211_roc_notify_destroy(roc, !roc->abort);
 
                if (started) {
-                       drv_flush(local, false);
+                       ieee80211_flush_queues(local, NULL);
 
                        local->tmp_channel = NULL;
                        ieee80211_hw_config(local, 0);
@@ -410,7 +419,7 @@ static void ieee80211_hw_roc_done(struct work_struct *work)
 
        list_del(&roc->list);
 
-       ieee80211_roc_notify_destroy(roc);
+       ieee80211_roc_notify_destroy(roc, true);
 
        /* if there's another roc, start it now */
        ieee80211_start_next_roc(local);
@@ -460,12 +469,14 @@ void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)
        list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
                if (local->ops->remain_on_channel) {
                        list_del(&roc->list);
-                       ieee80211_roc_notify_destroy(roc);
+                       ieee80211_roc_notify_destroy(roc, true);
                } else {
                        ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
 
                        /* work will clean up etc */
                        flush_delayed_work(&roc->work);
+                       WARN_ON(!roc->to_be_freed);
+                       kfree(roc);
                }
        }