mac80211: per interface idle notification
authorJohannes Berg <johannes.berg@intel.com>
Thu, 5 Aug 2010 15:02:38 +0000 (17:02 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 16 Aug 2010 19:26:40 +0000 (15:26 -0400)
Sometimes we don't just need to know whether or
not the device is idle, but also per interface.
This adds that reporting capability to mac80211.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/mac80211.h
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c
net/mac80211/scan.c
net/mac80211/work.c

index 3f1e03b521ec17bf5bf07abafe4d89ed9dbbf8c0..3a3c26f647b707681acac84ed1c2a175daf6f0b3 100644 (file)
@@ -149,6 +149,7 @@ struct ieee80211_low_level_stats {
  * @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed.
  * @BSS_CHANGED_QOS: QoS for this association was enabled/disabled. Note
  *     that it is only ever disabled for station mode.
+ * @BSS_CHANGED_IDLE: Idle changed for this BSS/interface.
  */
 enum ieee80211_bss_change {
        BSS_CHANGED_ASSOC               = 1<<0,
@@ -165,6 +166,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_IBSS                = 1<<11,
        BSS_CHANGED_ARP_FILTER          = 1<<12,
        BSS_CHANGED_QOS                 = 1<<13,
+       BSS_CHANGED_IDLE                = 1<<14,
 
        /* when adding here, make sure to change ieee80211_reconfig */
 };
@@ -223,6 +225,9 @@ enum ieee80211_bss_change {
  *     hardware must not perform any ARP filtering. Note, that the filter will
  *     be enabled also in promiscuous mode.
  * @qos: This is a QoS-enabled BSS.
+ * @idle: This interface is idle. There's also a global idle flag in the
+ *     hardware config which may be more appropriate depending on what
+ *     your driver/device needs to do.
  */
 struct ieee80211_bss_conf {
        const u8 *bssid;
@@ -247,6 +252,7 @@ struct ieee80211_bss_conf {
        u8 arp_addr_cnt;
        bool arp_filter_enabled;
        bool qos;
+       bool idle;
 };
 
 /**
index c691780725a7fcfbaef4ac39802540288696ee6a..32af97108425792cfb727143919d14bb9df43772 100644 (file)
@@ -920,12 +920,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
        sdata->u.ibss.ssid_len = params->ssid_len;
 
+       mutex_unlock(&sdata->u.ibss.mtx);
+
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 
-       mutex_unlock(&sdata->u.ibss.mtx);
-
        return 0;
 }
 
@@ -980,7 +982,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
 
        mutex_unlock(&sdata->u.ibss.mtx);
 
+       mutex_lock(&local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&local->mtx);
 
        return 0;
 }
index b44e03a02da96af90b119fbcd0e4d42e2ba324fe..98e783c6a3638ab3feba3f86fbe108ad64e66b05 100644 (file)
@@ -497,6 +497,9 @@ struct ieee80211_sub_if_data {
         */
        bool ht_opmode_valid;
 
+       /* to detect idle changes */
+       bool old_idle;
+
        /* Fragment table for host-based reassembly */
        struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
        unsigned int fragment_next;
index c1008a9d7bfb73a8b89c38e3b1ab196def6bb1df..9459aeee0ddc4da6cf655cb4c9f9391304f9cff7 100644 (file)
@@ -309,7 +309,9 @@ static int ieee80211_open(struct net_device *dev)
        if (sdata->flags & IEEE80211_SDATA_PROMISC)
                atomic_inc(&local->iff_promiscs);
 
+       mutex_lock(&local->mtx);
        hw_reconf_flags |= __ieee80211_recalc_idle(local);
+       mutex_unlock(&local->mtx);
 
        local->open_count++;
        if (hw_reconf_flags) {
@@ -516,7 +518,9 @@ static int ieee80211_stop(struct net_device *dev)
 
        sdata->bss = NULL;
 
+       mutex_lock(&local->mtx);
        hw_reconf_flags |= __ieee80211_recalc_idle(local);
+       mutex_unlock(&local->mtx);
 
        ieee80211_recalc_ps(local, -1);
 
@@ -1199,28 +1203,61 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata;
        int count = 0;
+       bool working = false, scanning = false;
+       struct ieee80211_work *wk;
 
-       if (!list_empty(&local->work_list))
-               return ieee80211_idle_off(local, "working");
-
-       if (local->scanning)
-               return ieee80211_idle_off(local, "scanning");
+#ifdef CONFIG_PROVE_LOCKING
+       WARN_ON(debug_locks && !lockdep_rtnl_is_held() &&
+               !lockdep_is_held(&local->iflist_mtx));
+#endif
+       lockdep_assert_held(&local->mtx);
 
        list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!ieee80211_sdata_running(sdata))
+               if (!ieee80211_sdata_running(sdata)) {
+                       sdata->vif.bss_conf.idle = true;
                        continue;
+               }
+
+               sdata->old_idle = sdata->vif.bss_conf.idle;
+
                /* do not count disabled managed interfaces */
                if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-                   !sdata->u.mgd.associated)
+                   !sdata->u.mgd.associated) {
+                       sdata->vif.bss_conf.idle = true;
                        continue;
+               }
                /* do not count unused IBSS interfaces */
                if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
-                   !sdata->u.ibss.ssid_len)
+                   !sdata->u.ibss.ssid_len) {
+                       sdata->vif.bss_conf.idle = true;
                        continue;
+               }
                /* count everything else */
                count++;
        }
 
+       list_for_each_entry(wk, &local->work_list, list) {
+               working = true;
+               wk->sdata->vif.bss_conf.idle = false;
+       }
+
+       if (local->scan_sdata) {
+               scanning = true;
+               local->scan_sdata->vif.bss_conf.idle = false;
+       }
+
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (sdata->old_idle == sdata->vif.bss_conf.idle)
+                       continue;
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
+       }
+
+       if (working)
+               return ieee80211_idle_off(local, "working");
+       if (scanning)
+               return ieee80211_idle_off(local, "scanning");
        if (!count)
                return ieee80211_idle_on(local);
        else
index 17e9257a61d892fd89501681b04186e92aaa752c..82e7cec5179c7c5f6d4fbbf567ea506ecaef5425 100644 (file)
@@ -1103,8 +1103,11 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
        printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
 
        ieee80211_set_disassoc(sdata, true);
-       ieee80211_recalc_idle(local);
        mutex_unlock(&ifmgd->mtx);
+
+       mutex_lock(&local->mtx);
+       ieee80211_recalc_idle(local);
+       mutex_unlock(&local->mtx);
        /*
         * must be outside lock due to cfg80211,
         * but that's not a problem.
@@ -1173,7 +1176,9 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                        sdata->name, bssid, reason_code);
 
        ieee80211_set_disassoc(sdata, true);
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        return RX_MGMT_CFG80211_DEAUTH;
 }
@@ -1203,7 +1208,9 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
                        sdata->name, mgmt->sa, reason_code);
 
        ieee80211_set_disassoc(sdata, true);
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
        return RX_MGMT_CFG80211_DISASSOC;
 }
 
@@ -1840,8 +1847,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                                " after %dms, disconnecting.\n",
                                bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
                        ieee80211_set_disassoc(sdata, true);
-                       ieee80211_recalc_idle(local);
                        mutex_unlock(&ifmgd->mtx);
+                       mutex_lock(&local->mtx);
+                       ieee80211_recalc_idle(local);
+                       mutex_unlock(&local->mtx);
                        /*
                         * must be outside lock due to cfg80211,
                         * but that's not a problem.
@@ -2319,7 +2328,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
        if (assoc_bss)
                sta_info_destroy_addr(sdata, bssid);
 
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        return 0;
 }
@@ -2357,7 +2368,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                        cookie, !req->local_state_change);
        sta_info_destroy_addr(sdata, bssid);
 
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        return 0;
 }
index f31f549733b15895d84c98aecb4a11e089231eb3..31f233f7f51af0cecae3b59d49eff090efb0fe8c 100644 (file)
@@ -304,7 +304,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
        ieee80211_offchannel_return(local, true);
 
  done:
+       mutex_lock(&local->mtx);
        ieee80211_recalc_idle(local);
+       mutex_unlock(&local->mtx);
        ieee80211_mlme_notify_scan_completed(local);
        ieee80211_ibss_notify_scan_completed(local);
        ieee80211_mesh_notify_scan_completed(local);
index b98af64f586279dbc60cef4c311420c668fc4b54..ae344d1ba0560480665e1b939b15b058f5fddc4c 100644 (file)
@@ -888,10 +888,10 @@ static void ieee80211_work_work(struct work_struct *work)
        while ((skb = skb_dequeue(&local->work_skb_queue)))
                ieee80211_work_rx_queued_mgmt(local, skb);
 
-       ieee80211_recalc_idle(local);
-
        mutex_lock(&local->mtx);
 
+       ieee80211_recalc_idle(local);
+
        list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
                bool started = wk->started;
 
@@ -1001,10 +1001,10 @@ static void ieee80211_work_work(struct work_struct *work)
                                             &local->scan_work,
                                             round_jiffies_relative(0));
 
-       mutex_unlock(&local->mtx);
-
        ieee80211_recalc_idle(local);
 
+       mutex_unlock(&local->mtx);
+
        list_for_each_entry_safe(wk, tmp, &free_work, list) {
                wk->done(wk, NULL);
                list_del(&wk->list);