]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
mac80211: disable powersave if pm_qos asks for low latency
authorJohannes Berg <johannes@sipsolutions.net>
Thu, 16 Apr 2009 11:17:25 +0000 (13:17 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Apr 2009 20:57:16 +0000 (16:57 -0400)
When an application asks for a latency lower than the beacon interval
there's nothing we can do -- we need to stay awake and not have the
AP buffer frames for us. Add code to automatically calculate this
constraint in mac80211 so drivers need not concern themselves with it.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/ieee80211.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/wext.c

index 4b501b48ce86efdb43dffd84d62ed8116d53d7ce..53563d53b5ad1edbf46d7cae34421736a235f5b1 100644 (file)
@@ -1383,4 +1383,13 @@ static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq)
                return -1;
 }
 
+/**
+ * ieee80211_tu_to_usec - convert time units (TU) to microseconds
+ * @tu: the TUs
+ */
+static inline unsigned long ieee80211_tu_to_usec(unsigned long tu)
+{
+       return 1024 * tu;
+}
+
 #endif /* LINUX_IEEE80211_H */
index ff40dd7b523adc69639b4e47c8052392e861edd9..b1d18d967d8c5f9015ec8a3fb03f56aecede3bb7 100644 (file)
@@ -750,6 +750,7 @@ struct ieee80211_local {
        struct work_struct dynamic_ps_enable_work;
        struct work_struct dynamic_ps_disable_work;
        struct timer_list dynamic_ps_timer;
+       struct notifier_block network_latency_notifier;
 
        int user_power_level; /* in dBm */
        int power_constr_level; /* in dBm */
@@ -938,7 +939,9 @@ int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason
 int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
 void ieee80211_send_pspoll(struct ieee80211_local *local,
                           struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_ps(struct ieee80211_local *local);
+void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
+int ieee80211_max_network_latency(struct notifier_block *nb,
+                                 unsigned long data, void *dummy);
 
 /* IBSS code */
 int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata);
index 6240f76e2a43f00e7d0654ea5f05f08d3fad9737..5d60deb219d39149a708f5832d104f3224464e63 100644 (file)
@@ -317,7 +317,7 @@ static int ieee80211_open(struct net_device *dev)
                ieee80211_set_wmm_default(sdata);
        }
 
-       ieee80211_recalc_ps(local);
+       ieee80211_recalc_ps(local, -1);
 
        /*
         * ieee80211_sta_work is disabled while network interface
@@ -574,7 +574,7 @@ static int ieee80211_stop(struct net_device *dev)
                hw_reconf_flags = 0;
        }
 
-       ieee80211_recalc_ps(local);
+       ieee80211_recalc_ps(local, -1);
 
        /* do after stop to avoid reconfiguring when we stop anyway */
        if (hw_reconf_flags)
index 80c0e28bf54968fd134967a9de33993374b687d7..049ce86398062075412cfb884dfc5f53182735b6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/wireless.h>
 #include <linux/rtnetlink.h>
 #include <linux/bitmap.h>
+#include <linux/pm_qos_params.h>
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
 
@@ -1038,25 +1039,38 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                }
        }
 
+       local->network_latency_notifier.notifier_call =
+               ieee80211_max_network_latency;
+       result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
+                                    &local->network_latency_notifier);
+
+       if (result) {
+               rtnl_lock();
+               goto fail_pm_qos;
+       }
+
        return 0;
 
-fail_wep:
+ fail_pm_qos:
+       ieee80211_led_exit(local);
+       ieee80211_remove_interfaces(local);
+ fail_wep:
        rate_control_deinitialize(local);
-fail_rate:
+ fail_rate:
        unregister_netdevice(local->mdev);
        local->mdev = NULL;
-fail_dev:
+ fail_dev:
        rtnl_unlock();
        sta_info_stop(local);
-fail_sta_info:
+ fail_sta_info:
        debugfs_hw_del(local);
        destroy_workqueue(local->hw.workqueue);
-fail_workqueue:
+ fail_workqueue:
        if (local->mdev)
                free_netdev(local->mdev);
-fail_mdev_alloc:
+ fail_mdev_alloc:
        wiphy_unregister(local->hw.wiphy);
-fail_wiphy_register:
+ fail_wiphy_register:
        kfree(local->int_scan_req.channels);
        return result;
 }
@@ -1069,6 +1083,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        tasklet_kill(&local->tx_pending_tasklet);
        tasklet_kill(&local->tasklet);
 
+       pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
+                              &local->network_latency_notifier);
+
        rtnl_lock();
 
        /*
index 06d9a1d23252510657790ada62962cfd0702f1eb..c39a214e7ad0b344888c44864cfbda2898fc3014 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/rtnetlink.h>
+#include <linux/pm_qos_params.h>
 #include <net/mac80211.h>
 #include <asm/unaligned.h>
 
@@ -515,7 +516,7 @@ static void ieee80211_change_ps(struct ieee80211_local *local)
 }
 
 /* need to hold RTNL or interface lock */
-void ieee80211_recalc_ps(struct ieee80211_local *local)
+void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 {
        struct ieee80211_sub_if_data *sdata, *found = NULL;
        int count = 0;
@@ -534,10 +535,22 @@ void ieee80211_recalc_ps(struct ieee80211_local *local)
                count++;
        }
 
-       if (count == 1 && found->u.mgd.powersave)
-               local->ps_sdata = found;
-       else
+       if (count == 1 && found->u.mgd.powersave) {
+               s32 beaconint_us;
+
+               if (latency < 0)
+                       latency = pm_qos_requirement(PM_QOS_NETWORK_LATENCY);
+
+               beaconint_us = ieee80211_tu_to_usec(
+                                       found->vif.bss_conf.beacon_int);
+
+               if (beaconint_us > latency)
+                       local->ps_sdata = NULL;
+               else
+                       local->ps_sdata = found;
+       } else {
                local->ps_sdata = NULL;
+       }
 
        ieee80211_change_ps(local);
 }
@@ -2324,3 +2337,18 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
                ieee80211_restart_sta_timer(sdata);
        rcu_read_unlock();
 }
+
+int ieee80211_max_network_latency(struct notifier_block *nb,
+                                 unsigned long data, void *dummy)
+{
+       s32 latency_usec = (s32) data;
+       struct ieee80211_local *local =
+               container_of(nb, struct ieee80211_local,
+                            network_latency_notifier);
+
+       mutex_lock(&local->iflist_mtx);
+       ieee80211_recalc_ps(local, latency_usec);
+       mutex_unlock(&local->iflist_mtx);
+
+       return 0;
+}
index 81f63e57027f6b05aacb28756317fc6dc242d365..1c4664b8b1a0029ac80def145a6b4e1ee72b5dff 100644 (file)
@@ -789,7 +789,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
                ieee80211_hw_config(local,
                                    IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-       ieee80211_recalc_ps(local);
+       ieee80211_recalc_ps(local, -1);
 
        return 0;
 }