All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/3] mac80211 powersave improvements
@ 2009-04-15 17:10 Johannes Berg
  2009-04-15 17:10 ` [RFC 1/3] mac80211: improve powersave implementation Johannes Berg
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Johannes Berg @ 2009-04-15 17:10 UTC (permalink / raw)
  To: linux-wireless

Hi,

This series contains the following:

 1) improve powersave to only allow it when a single
    managed interface is active

 2) disable PS when the maximum networking latency
    applications are are willing to put up with is
    smaller than the beacon interval -- in that case
    we cannot allow the AP to buffer frames

 3) enable powersave by default

The last part might be controversial?

Should we have a default setting for the dynamic PS
timeout?

Oh also -- below is a small program to play with the
pm_qos framework, feel free to rip it for anything.

johannes

/*
 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *
 * Compile simply with:
 *	cc -o netlatency netlatency.c
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	int32_t v;
	int fd;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s <latency [us]>\n", argv[0]);
		fprintf(stderr, "\n");
		fprintf(stderr, "	latency: the maximum tolerable network latency you\n");
		fprintf(stderr, "	         are willing to put up with [in microseconds]\n");
		fprintf(stderr, "\n");
		fprintf(stderr, "This program will block until you hit Ctrl-C, at which point\n");
		fprintf(stderr, "the file descriptor is closed and the latency requirement is\n");
		fprintf(stderr, "unregistered again.\n");
		return 2;
	}

	v = atoi(argv[1]);

	printf("setting latency to %d.%.6d seconds\n", v/1000000, v % 1000000);

	fd = open("/dev/network_latency", O_WRONLY);
	if (fd < 0) {
		perror("open /dev/network_latency");
		return 1;
	}
	if (write(fd, &v, sizeof(v)) != sizeof(v)) {
		perror("write to /dev/network_latency");
		return 1;
	}

	while (1) sleep(10);

	return 0;
}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 1/3] mac80211: improve powersave implementation
  2009-04-15 17:10 [RFC 0/3] mac80211 powersave improvements Johannes Berg
@ 2009-04-15 17:10 ` Johannes Berg
  2009-04-15 19:38   ` [RFC 1/3 v2] " Johannes Berg
  2009-04-15 17:10 ` [RFC 2/3] mac80211: disable powersave if pm_qos asks for low latency Johannes Berg
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-04-15 17:10 UTC (permalink / raw)
  To: linux-wireless

When you have multiple virtual interfaces the current
implementation requires setting them up properly from
userspace, which is undesirable when we want to default
to power save mode. Keep track of powersave requested
from userspace per managed mode interface, and only
enable powersave globally when exactly one managed mode
interface is active and has powersave turned on.

Secondly, only start the dynPS timer when PS is turned
on, and properly turn it off when PS is turned off.

Finally, also reorder the code and refactor the code
that enables PS or the dynps timer instead of having
it copied in two places.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/ieee80211_i.h |    5 
 net/mac80211/iface.c       |    4 
 net/mac80211/mlme.c        |  230 ++++++++++++++++++++++++++++-----------------
 net/mac80211/wext.c        |   43 +-------
 4 files changed, 163 insertions(+), 119 deletions(-)

--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-04-15 15:38:57.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-04-15 18:56:33.000000000 +0200
@@ -295,6 +295,8 @@ struct ieee80211_if_managed {
 	int auth_tries; /* retries for auth req */
 	int assoc_tries; /* retries for assoc req */
 
+	bool powersave; /* powersave requested for this iface */
+
 	unsigned long request;
 
 	unsigned long last_probe;
@@ -739,7 +741,7 @@ struct ieee80211_local {
 	int wifi_wme_noack_test;
 	unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
 
-	bool powersave;
+	bool powersave; /* ps enabled -- only one mgd iface with ps on */
 	bool pspolling;
 	struct work_struct dynamic_ps_enable_work;
 	struct work_struct dynamic_ps_disable_work;
@@ -932,6 +934,7 @@ int ieee80211_sta_deauthenticate(struct 
 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);
 
 /* IBSS code */
 int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata);
--- wireless-testing.orig/net/mac80211/iface.c	2009-04-15 15:38:47.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2009-04-15 18:56:33.000000000 +0200
@@ -317,6 +317,8 @@ static int ieee80211_open(struct net_dev
 		ieee80211_set_wmm_default(sdata);
 	}
 
+	ieee80211_recalc_ps(local);
+
 	/*
 	 * ieee80211_sta_work is disabled while network interface
 	 * is down. Therefore, some configuration changes may not
@@ -572,6 +574,8 @@ static int ieee80211_stop(struct net_dev
 		hw_reconf_flags = 0;
 	}
 
+	ieee80211_recalc_ps(local);
+
 	/* do after stop to avoid reconfiguring when we stop anyway */
 	if (hw_reconf_flags)
 		ieee80211_hw_config(local, hw_reconf_flags);
--- wireless-testing.orig/net/mac80211/wext.c	2009-04-15 15:38:47.000000000 +0200
+++ wireless-testing/net/mac80211/wext.c	2009-04-15 18:56:37.000000000 +0200
@@ -747,7 +747,7 @@ static int ieee80211_ioctl_siwpower(stru
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_conf *conf = &local->hw.conf;
-	int ret = 0, timeout = 0;
+	int timeout = 0;
 	bool ps;
 
 	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -779,42 +779,19 @@ static int ieee80211_ioctl_siwpower(stru
 		timeout = wrq->value / 1000;
 
  set:
-	if (ps == local->powersave && timeout == conf->dynamic_ps_timeout)
-		return ret;
+	if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
+		return 0;
 
-	local->powersave = ps;
+	sdata->u.mgd.powersave = ps;
 	conf->dynamic_ps_timeout = timeout;
 
 	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-		ret = ieee80211_hw_config(local,
-					  IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
+		ieee80211_hw_config(local,
+				    IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-	if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
-		return ret;
+	ieee80211_recalc_ps(local);
 
-	if (conf->dynamic_ps_timeout > 0 &&
-	    !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
-		mod_timer(&local->dynamic_ps_timer, jiffies +
-			  msecs_to_jiffies(conf->dynamic_ps_timeout));
-	} else {
-		if (local->powersave) {
-			if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-				ieee80211_send_nullfunc(local, sdata, 1);
-			conf->flags |= IEEE80211_CONF_PS;
-			ret = ieee80211_hw_config(local,
-					IEEE80211_CONF_CHANGE_PS);
-		} else {
-			conf->flags &= ~IEEE80211_CONF_PS;
-			ret = ieee80211_hw_config(local,
-					IEEE80211_CONF_CHANGE_PS);
-			if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-				ieee80211_send_nullfunc(local, sdata, 0);
-			del_timer_sync(&local->dynamic_ps_timer);
-			cancel_work_sync(&local->dynamic_ps_enable_work);
-		}
-	}
-
-	return ret;
+	return 0;
 }
 
 static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -822,9 +799,9 @@ static int ieee80211_ioctl_giwpower(stru
 				    union iwreq_data *wrqu,
 				    char *extra)
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	wrqu->power.disabled = !local->powersave;
+	wrqu->power.disabled = !sdata->u.mgd.powersave;
 
 	return 0;
 }
--- wireless-testing.orig/net/mac80211/mlme.c	2009-04-15 15:38:47.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-04-15 18:56:33.000000000 +0200
@@ -446,6 +446,149 @@ void ieee80211_send_pspoll(struct ieee80
 	ieee80211_tx_skb(sdata, skb, 0);
 }
 
+void ieee80211_send_nullfunc(struct ieee80211_local *local,
+			     struct ieee80211_sub_if_data *sdata,
+			     int powersave)
+{
+	struct sk_buff *skb;
+	struct ieee80211_hdr *nullfunc;
+	__le16 fc;
+
+	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+		return;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
+	if (!skb) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
+		       "frame\n", sdata->dev->name);
+		return;
+	}
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
+	memset(nullfunc, 0, 24);
+	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+			 IEEE80211_FCTL_TODS);
+	if (powersave)
+		fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+	nullfunc->frame_control = fc;
+	memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
+	memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
+	memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
+
+	ieee80211_tx_skb(sdata, skb, 0);
+}
+
+/* powersave */
+static void ieee80211_enable_ps(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (conf->dynamic_ps_timeout > 0 &&
+	    !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
+		mod_timer(&local->dynamic_ps_timer, jiffies +
+			  msecs_to_jiffies(conf->dynamic_ps_timeout));
+	} else {
+		if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+			ieee80211_send_nullfunc(local, sdata, 1);
+		conf->flags |= IEEE80211_CONF_PS;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+	}
+}
+
+static void ieee80211_change_ps(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
+		return;
+
+	if (local->powersave) {
+		ieee80211_enable_ps(local, sdata);
+	} else if (conf->flags & IEEE80211_CONF_PS) {
+		conf->flags &= ~IEEE80211_CONF_PS;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+		if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+			ieee80211_send_nullfunc(local, sdata, 0);
+		del_timer_sync(&local->dynamic_ps_timer);
+		cancel_work_sync(&local->dynamic_ps_enable_work);
+	}
+}
+
+/* need to hold RTNL or interface lock */
+void ieee80211_recalc_ps(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
+		local->powersave = false;
+		return;
+	}
+
+	if (local->open_count != 1) {
+		local->powersave = false;
+		return;
+	}
+
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (!netif_running(sdata->dev))
+			continue;
+		/* can only hit once due to open count test */
+		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+			if (sdata->u.mgd.powersave) {
+				local->powersave = true;
+				goto change;
+			}
+		}
+		break;
+	}
+
+	local->powersave = false;
+ change:
+	ieee80211_change_ps(local, sdata);
+}
+
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local,
+			     dynamic_ps_disable_work);
+
+	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+	}
+
+	ieee80211_wake_queues_by_reason(&local->hw,
+					IEEE80211_QUEUE_STOP_REASON_PS);
+}
+
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local,
+			     dynamic_ps_enable_work);
+	struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+	if (local->hw.conf.flags & IEEE80211_CONF_PS)
+		return;
+
+	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+		ieee80211_send_nullfunc(local, sdata, 1);
+
+	local->hw.conf.flags |= IEEE80211_CONF_PS;
+	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+}
+
+void ieee80211_dynamic_ps_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *) data;
+
+	queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+}
+
 /* MLME */
 static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
 				     struct ieee80211_if_managed *ifmgd,
@@ -718,19 +861,8 @@ static void ieee80211_set_associated(str
 	bss_info_changed |= BSS_CHANGED_BASIC_RATES;
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
-	if (local->powersave) {
-		if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) &&
-		    local->hw.conf.dynamic_ps_timeout > 0) {
-			mod_timer(&local->dynamic_ps_timer, jiffies +
-				  msecs_to_jiffies(
-					local->hw.conf.dynamic_ps_timeout));
-		} else {
-			if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-				ieee80211_send_nullfunc(local, sdata, 1);
-			conf->flags |= IEEE80211_CONF_PS;
-			ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-		}
-	}
+	if (local->powersave)
+		ieee80211_enable_ps(local, sdata);
 
 	netif_tx_start_all_queues(sdata->dev);
 	netif_carrier_on(sdata->dev);
@@ -2176,75 +2308,3 @@ void ieee80211_mlme_notify_scan_complete
 		ieee80211_restart_sta_timer(sdata);
 	rcu_read_unlock();
 }
-
-void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
-{
-	struct ieee80211_local *local =
-		container_of(work, struct ieee80211_local,
-			     dynamic_ps_disable_work);
-
-	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-	}
-
-	ieee80211_wake_queues_by_reason(&local->hw,
-					IEEE80211_QUEUE_STOP_REASON_PS);
-}
-
-void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
-{
-	struct ieee80211_local *local =
-		container_of(work, struct ieee80211_local,
-			     dynamic_ps_enable_work);
-	struct ieee80211_sub_if_data *sdata = local->scan_sdata;
-
-	if (local->hw.conf.flags & IEEE80211_CONF_PS)
-		return;
-
-	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-		ieee80211_send_nullfunc(local, sdata, 1);
-
-	local->hw.conf.flags |= IEEE80211_CONF_PS;
-	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-}
-
-void ieee80211_dynamic_ps_timer(unsigned long data)
-{
-	struct ieee80211_local *local = (void *) data;
-
-	queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
-}
-
-void ieee80211_send_nullfunc(struct ieee80211_local *local,
-			     struct ieee80211_sub_if_data *sdata,
-			     int powersave)
-{
-	struct sk_buff *skb;
-	struct ieee80211_hdr *nullfunc;
-	__le16 fc;
-
-	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
-		return;
-
-	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
-	if (!skb) {
-		printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
-		       "frame\n", sdata->dev->name);
-		return;
-	}
-	skb_reserve(skb, local->hw.extra_tx_headroom);
-
-	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
-	memset(nullfunc, 0, 24);
-	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
-			 IEEE80211_FCTL_TODS);
-	if (powersave)
-		fc |= cpu_to_le16(IEEE80211_FCTL_PM);
-	nullfunc->frame_control = fc;
-	memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
-	memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
-	memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
-
-	ieee80211_tx_skb(sdata, skb, 0);
-}

-- 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 2/3] mac80211: disable powersave if pm_qos asks for low latency
  2009-04-15 17:10 [RFC 0/3] mac80211 powersave improvements Johannes Berg
  2009-04-15 17:10 ` [RFC 1/3] mac80211: improve powersave implementation Johannes Berg
@ 2009-04-15 17:10 ` Johannes Berg
  2009-04-15 19:38   ` [RFC 2/3 v2] " Johannes Berg
  2009-04-15 17:10 ` [RFC 3/3] mac80211: enable PS by default Johannes Berg
  2009-04-16 11:01 ` [RFC 0/3] mac80211 powersave improvements Kalle Valo
  3 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-04-15 17:10 UTC (permalink / raw)
  To: linux-wireless

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>
---
 include/linux/ieee80211.h  |    9 ++++++++
 net/mac80211/ieee80211_i.h |    5 +++-
 net/mac80211/iface.c       |    4 +--
 net/mac80211/main.c        |   31 +++++++++++++++++++++++-------
 net/mac80211/mlme.c        |   46 ++++++++++++++++++++++++++++++++++-----------
 net/mac80211/wext.c        |    2 -
 6 files changed, 75 insertions(+), 22 deletions(-)

--- wireless-testing.orig/net/mac80211/mlme.c	2009-04-15 15:39:00.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-04-15 18:44:39.000000000 +0200
@@ -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>
 
@@ -502,10 +503,10 @@ static void ieee80211_change_ps(struct i
 {
 	struct ieee80211_conf *conf = &local->hw.conf;
 
-	if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
-		return;
-
 	if (local->powersave) {
+		if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
+			return;
+
 		ieee80211_enable_ps(local, sdata);
 	} else if (conf->flags & IEEE80211_CONF_PS) {
 		conf->flags &= ~IEEE80211_CONF_PS;
@@ -518,9 +519,10 @@ static void ieee80211_change_ps(struct i
 }
 
 /* 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;
+	s32 beaconint_us;
 
 	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
 		local->powersave = false;
@@ -532,17 +534,24 @@ void ieee80211_recalc_ps(struct ieee8021
 		return;
 	}
 
+	if (latency < 0)
+		latency = pm_qos_requirement(PM_QOS_NETWORK_LATENCY);
+
 	list_for_each_entry(sdata, &local->interfaces, list) {
 		if (!netif_running(sdata->dev))
 			continue;
 		/* can only hit once due to open count test */
-		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-			if (sdata->u.mgd.powersave) {
-				local->powersave = true;
-				goto change;
-			}
-		}
-		break;
+		if (sdata->vif.type != NL80211_IFTYPE_STATION)
+			break;
+		if (!sdata->u.mgd.powersave)
+			break;
+		beaconint_us = ieee80211_tu_to_usec(
+					sdata->vif.bss_conf.beacon_int);
+		if (beaconint_us > latency)
+			break;
+		/* all conditions fulfilled - enable PS :) */
+		local->powersave = true;
+		goto change;
 	}
 
 	local->powersave = false;
@@ -2308,3 +2317,18 @@ void ieee80211_mlme_notify_scan_complete
 		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;
+}
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-04-15 15:39:00.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-04-15 15:39:00.000000000 +0200
@@ -746,6 +746,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 */
@@ -934,7 +935,9 @@ int ieee80211_sta_deauthenticate(struct 
 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);
--- wireless-testing.orig/net/mac80211/main.c	2009-04-15 15:38:57.000000000 +0200
+++ wireless-testing/net/mac80211/main.c	2009-04-15 15:39:08.000000000 +0200
@@ -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 ieee802
 		}
 	}
 
+	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 ieee
 	tasklet_kill(&local->tx_pending_tasklet);
 	tasklet_kill(&local->tasklet);
 
+	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
+			       &local->network_latency_notifier);
+
 	rtnl_lock();
 
 	/*
--- wireless-testing.orig/net/mac80211/iface.c	2009-04-15 15:39:00.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2009-04-15 15:39:08.000000000 +0200
@@ -317,7 +317,7 @@ static int ieee80211_open(struct net_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_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)
--- wireless-testing.orig/net/mac80211/wext.c	2009-04-15 15:39:00.000000000 +0200
+++ wireless-testing/net/mac80211/wext.c	2009-04-15 15:39:08.000000000 +0200
@@ -789,7 +789,7 @@ static int ieee80211_ioctl_siwpower(stru
 		ieee80211_hw_config(local,
 				    IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-	ieee80211_recalc_ps(local);
+	ieee80211_recalc_ps(local, -1);
 
 	return 0;
 }
--- wireless-testing.orig/include/linux/ieee80211.h	2009-04-15 15:38:45.000000000 +0200
+++ wireless-testing/include/linux/ieee80211.h	2009-04-15 15:39:00.000000000 +0200
@@ -1383,4 +1383,13 @@ static inline int ieee80211_freq_to_ofdm
 		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 */

-- 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 3/3] mac80211: enable PS by default
  2009-04-15 17:10 [RFC 0/3] mac80211 powersave improvements Johannes Berg
  2009-04-15 17:10 ` [RFC 1/3] mac80211: improve powersave implementation Johannes Berg
  2009-04-15 17:10 ` [RFC 2/3] mac80211: disable powersave if pm_qos asks for low latency Johannes Berg
@ 2009-04-15 17:10 ` Johannes Berg
  2009-04-16 10:39   ` [RFC 3/3 v2] " Johannes Berg
  2009-04-16 11:01 ` [RFC 0/3] mac80211 powersave improvements Kalle Valo
  3 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-04-15 17:10 UTC (permalink / raw)
  To: linux-wireless

Enable PS by default -- rely on drivers to control the level
using pm_qos. Due to the previous patch we turn off PS when
necessary due to latency requirements.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/mlme.c |    3 +++
 1 file changed, 3 insertions(+)

--- wireless-testing.orig/net/mac80211/mlme.c	2009-04-15 18:56:41.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-04-15 18:56:42.000000000 +0200
@@ -2163,6 +2163,9 @@ void ieee80211_sta_setup_sdata(struct ie
 		IEEE80211_STA_AUTO_CHANNEL_SEL;
 	if (sdata->local->hw.queues >= 4)
 		ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
+
+	if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
+		ifmgd->powersave = 1;
 }
 
 /* configuration hooks */

-- 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 1/3 v2] mac80211: improve powersave implementation
  2009-04-15 17:10 ` [RFC 1/3] mac80211: improve powersave implementation Johannes Berg
@ 2009-04-15 19:38   ` Johannes Berg
  2009-04-16 10:55     ` Kalle Valo
  0 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-04-15 19:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Kalle Valo

When you have multiple virtual interfaces the current
implementation requires setting them up properly from
userspace, which is undesirable when we want to default
to power save mode. Keep track of powersave requested
from userspace per managed mode interface, and only
enable powersave globally when exactly one managed mode
interface is active and has powersave turned on.

Second, only start the dynPS timer when PS is turned
on, and properly turn it off when PS is turned off.

Third, fix the scan_sdata abuse in the dynps code.

Finally, also reorder the code and refactor the code
that enables PS or the dynps timer instead of having
it copied in two places.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
v2: * don't disable PS when adding monitor interfaces
    * fix scan_sdata use for powersave timer bug by
      keeping track of which interface is _the_ managed
      interface (can only be one)

 net/mac80211/ieee80211_i.h |    9 +
 net/mac80211/iface.c       |    4 
 net/mac80211/mlme.c        |  228 ++++++++++++++++++++++++++++-----------------
 net/mac80211/scan.c        |    2 
 net/mac80211/wext.c        |   43 +-------
 5 files changed, 165 insertions(+), 121 deletions(-)

--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-04-15 20:48:49.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-04-15 21:30:19.000000000 +0200
@@ -295,6 +295,8 @@ struct ieee80211_if_managed {
 	int auth_tries; /* retries for auth req */
 	int assoc_tries; /* retries for assoc req */
 
+	bool powersave; /* powersave requested for this iface */
+
 	unsigned long request;
 
 	unsigned long last_probe;
@@ -739,8 +741,12 @@ struct ieee80211_local {
 	int wifi_wme_noack_test;
 	unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
 
-	bool powersave;
 	bool pspolling;
+	/*
+	 * PS can only be enabled when we have exactly one managed
+	 * interface (and monitors) in PS, this then points there.
+	 */
+	struct ieee80211_sub_if_data *ps_sdata;
 	struct work_struct dynamic_ps_enable_work;
 	struct work_struct dynamic_ps_disable_work;
 	struct timer_list dynamic_ps_timer;
@@ -932,6 +938,7 @@ int ieee80211_sta_deauthenticate(struct 
 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);
 
 /* IBSS code */
 int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata);
--- wireless-testing.orig/net/mac80211/iface.c	2009-04-15 20:48:49.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2009-04-15 21:30:19.000000000 +0200
@@ -317,6 +317,8 @@ static int ieee80211_open(struct net_dev
 		ieee80211_set_wmm_default(sdata);
 	}
 
+	ieee80211_recalc_ps(local);
+
 	/*
 	 * ieee80211_sta_work is disabled while network interface
 	 * is down. Therefore, some configuration changes may not
@@ -572,6 +574,8 @@ static int ieee80211_stop(struct net_dev
 		hw_reconf_flags = 0;
 	}
 
+	ieee80211_recalc_ps(local);
+
 	/* do after stop to avoid reconfiguring when we stop anyway */
 	if (hw_reconf_flags)
 		ieee80211_hw_config(local, hw_reconf_flags);
--- wireless-testing.orig/net/mac80211/wext.c	2009-04-15 20:48:49.000000000 +0200
+++ wireless-testing/net/mac80211/wext.c	2009-04-15 21:30:19.000000000 +0200
@@ -747,7 +747,7 @@ static int ieee80211_ioctl_siwpower(stru
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_conf *conf = &local->hw.conf;
-	int ret = 0, timeout = 0;
+	int timeout = 0;
 	bool ps;
 
 	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -779,42 +779,19 @@ static int ieee80211_ioctl_siwpower(stru
 		timeout = wrq->value / 1000;
 
  set:
-	if (ps == local->powersave && timeout == conf->dynamic_ps_timeout)
-		return ret;
+	if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
+		return 0;
 
-	local->powersave = ps;
+	sdata->u.mgd.powersave = ps;
 	conf->dynamic_ps_timeout = timeout;
 
 	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-		ret = ieee80211_hw_config(local,
-					  IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
+		ieee80211_hw_config(local,
+				    IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-	if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
-		return ret;
+	ieee80211_recalc_ps(local);
 
-	if (conf->dynamic_ps_timeout > 0 &&
-	    !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
-		mod_timer(&local->dynamic_ps_timer, jiffies +
-			  msecs_to_jiffies(conf->dynamic_ps_timeout));
-	} else {
-		if (local->powersave) {
-			if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-				ieee80211_send_nullfunc(local, sdata, 1);
-			conf->flags |= IEEE80211_CONF_PS;
-			ret = ieee80211_hw_config(local,
-					IEEE80211_CONF_CHANGE_PS);
-		} else {
-			conf->flags &= ~IEEE80211_CONF_PS;
-			ret = ieee80211_hw_config(local,
-					IEEE80211_CONF_CHANGE_PS);
-			if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-				ieee80211_send_nullfunc(local, sdata, 0);
-			del_timer_sync(&local->dynamic_ps_timer);
-			cancel_work_sync(&local->dynamic_ps_enable_work);
-		}
-	}
-
-	return ret;
+	return 0;
 }
 
 static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -822,9 +799,9 @@ static int ieee80211_ioctl_giwpower(stru
 				    union iwreq_data *wrqu,
 				    char *extra)
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	wrqu->power.disabled = !local->powersave;
+	wrqu->power.disabled = !sdata->u.mgd.powersave;
 
 	return 0;
 }
--- wireless-testing.orig/net/mac80211/mlme.c	2009-04-15 21:29:25.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-04-15 21:30:19.000000000 +0200
@@ -446,6 +446,145 @@ void ieee80211_send_pspoll(struct ieee80
 	ieee80211_tx_skb(sdata, skb, 0);
 }
 
+void ieee80211_send_nullfunc(struct ieee80211_local *local,
+			     struct ieee80211_sub_if_data *sdata,
+			     int powersave)
+{
+	struct sk_buff *skb;
+	struct ieee80211_hdr *nullfunc;
+	__le16 fc;
+
+	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+		return;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
+	if (!skb) {
+		printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
+		       "frame\n", sdata->dev->name);
+		return;
+	}
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
+	memset(nullfunc, 0, 24);
+	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+			 IEEE80211_FCTL_TODS);
+	if (powersave)
+		fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+	nullfunc->frame_control = fc;
+	memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
+	memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
+	memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
+
+	ieee80211_tx_skb(sdata, skb, 0);
+}
+
+/* powersave */
+static void ieee80211_enable_ps(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (conf->dynamic_ps_timeout > 0 &&
+	    !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
+		mod_timer(&local->dynamic_ps_timer, jiffies +
+			  msecs_to_jiffies(conf->dynamic_ps_timeout));
+	} else {
+		if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+			ieee80211_send_nullfunc(local, sdata, 1);
+		conf->flags |= IEEE80211_CONF_PS;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+	}
+}
+
+static void ieee80211_change_ps(struct ieee80211_local *local)
+{
+	struct ieee80211_conf *conf = &local->hw.conf;
+
+	if (local->ps_sdata) {
+		if (!(local->ps_sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
+			return;
+
+		ieee80211_enable_ps(local, local->ps_sdata);
+	} else if (conf->flags & IEEE80211_CONF_PS) {
+		conf->flags &= ~IEEE80211_CONF_PS;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+		del_timer_sync(&local->dynamic_ps_timer);
+		cancel_work_sync(&local->dynamic_ps_enable_work);
+	}
+}
+
+/* need to hold RTNL or interface lock */
+void ieee80211_recalc_ps(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata, *found = NULL;
+	int count = 0;
+
+	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
+		local->ps_sdata = NULL;
+		return;
+	}
+
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (!netif_running(sdata->dev))
+			continue;
+		if (sdata->vif.type != NL80211_IFTYPE_STATION)
+			continue;
+		found = sdata;
+		count++;
+	}
+
+	if (count == 1 && found->u.mgd.powersave)
+		local->ps_sdata = found;
+	else
+		local->ps_sdata = NULL;
+
+	ieee80211_change_ps(local);
+}
+
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local,
+			     dynamic_ps_disable_work);
+
+	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+	}
+
+	ieee80211_wake_queues_by_reason(&local->hw,
+					IEEE80211_QUEUE_STOP_REASON_PS);
+}
+
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local,
+			     dynamic_ps_enable_work);
+	struct ieee80211_sub_if_data *sdata = local->ps_sdata;
+
+	/* can only happen when PS was just disabled anyway */
+	if (!sdata)
+		return;
+
+	if (local->hw.conf.flags & IEEE80211_CONF_PS)
+		return;
+
+	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+		ieee80211_send_nullfunc(local, sdata, 1);
+
+	local->hw.conf.flags |= IEEE80211_CONF_PS;
+	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+}
+
+void ieee80211_dynamic_ps_timer(unsigned long data)
+{
+	struct ieee80211_local *local = (void *) data;
+
+	queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+}
+
 /* MLME */
 static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
 				     struct ieee80211_if_managed *ifmgd,
@@ -718,19 +857,9 @@ static void ieee80211_set_associated(str
 	bss_info_changed |= BSS_CHANGED_BASIC_RATES;
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
-	if (local->powersave) {
-		if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) &&
-		    local->hw.conf.dynamic_ps_timeout > 0) {
-			mod_timer(&local->dynamic_ps_timer, jiffies +
-				  msecs_to_jiffies(
-					local->hw.conf.dynamic_ps_timeout));
-		} else {
-			if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-				ieee80211_send_nullfunc(local, sdata, 1);
-			conf->flags |= IEEE80211_CONF_PS;
-			ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-		}
-	}
+	/* will be same as sdata */
+	if (local->ps_sdata)
+		ieee80211_enable_ps(local, sdata);
 
 	netif_tx_start_all_queues(sdata->dev);
 	netif_carrier_on(sdata->dev);
@@ -2176,76 +2305,3 @@ void ieee80211_mlme_notify_scan_complete
 		ieee80211_restart_sta_timer(sdata);
 	rcu_read_unlock();
 }
-
-void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
-{
-	struct ieee80211_local *local =
-		container_of(work, struct ieee80211_local,
-			     dynamic_ps_disable_work);
-
-	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-	}
-
-	ieee80211_wake_queues_by_reason(&local->hw,
-					IEEE80211_QUEUE_STOP_REASON_PS);
-}
-
-void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
-{
-	struct ieee80211_local *local =
-		container_of(work, struct ieee80211_local,
-			     dynamic_ps_enable_work);
-	/* XXX: using scan_sdata is completely broken! */
-	struct ieee80211_sub_if_data *sdata = local->scan_sdata;
-
-	if (local->hw.conf.flags & IEEE80211_CONF_PS)
-		return;
-
-	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK && sdata)
-		ieee80211_send_nullfunc(local, sdata, 1);
-
-	local->hw.conf.flags |= IEEE80211_CONF_PS;
-	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-}
-
-void ieee80211_dynamic_ps_timer(unsigned long data)
-{
-	struct ieee80211_local *local = (void *) data;
-
-	queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
-}
-
-void ieee80211_send_nullfunc(struct ieee80211_local *local,
-			     struct ieee80211_sub_if_data *sdata,
-			     int powersave)
-{
-	struct sk_buff *skb;
-	struct ieee80211_hdr *nullfunc;
-	__le16 fc;
-
-	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
-		return;
-
-	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
-	if (!skb) {
-		printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
-		       "frame\n", sdata->dev->name);
-		return;
-	}
-	skb_reserve(skb, local->hw.extra_tx_headroom);
-
-	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
-	memset(nullfunc, 0, 24);
-	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
-			 IEEE80211_FCTL_TODS);
-	if (powersave)
-		fc |= cpu_to_le16(IEEE80211_FCTL_PM);
-	nullfunc->frame_control = fc;
-	memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
-	memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
-	memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
-
-	ieee80211_tx_skb(sdata, skb, 0);
-}
--- wireless-testing.orig/net/mac80211/scan.c	2009-04-15 20:48:49.000000000 +0200
+++ wireless-testing/net/mac80211/scan.c	2009-04-15 21:30:19.000000000 +0200
@@ -253,7 +253,7 @@ static void ieee80211_scan_ps_disable(st
 {
 	struct ieee80211_local *local = sdata->local;
 
-	if (!local->powersave)
+	if (!local->ps_sdata)
 		ieee80211_send_nullfunc(local, sdata, 0);
 	else {
 		/*



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 2/3 v2] mac80211: disable powersave if pm_qos asks for low latency
  2009-04-15 17:10 ` [RFC 2/3] mac80211: disable powersave if pm_qos asks for low latency Johannes Berg
@ 2009-04-15 19:38   ` Johannes Berg
  2009-04-16 10:58     ` Kalle Valo
  0 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-04-15 19:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Kalle Valo

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>
---
v2: rebased

 include/linux/ieee80211.h  |    9 +++++++++
 net/mac80211/ieee80211_i.h |    5 ++++-
 net/mac80211/iface.c       |    4 ++--
 net/mac80211/main.c        |   31 ++++++++++++++++++++++++-------
 net/mac80211/mlme.c        |   36 ++++++++++++++++++++++++++++++++----
 net/mac80211/wext.c        |    2 +-
 6 files changed, 72 insertions(+), 15 deletions(-)

--- wireless-testing.orig/net/mac80211/mlme.c	2009-04-15 20:32:42.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-04-15 20:40:22.000000000 +0200
@@ -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 i
 }
 
 /* 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 ieee8021
 		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);
 }
@@ -2305,3 +2318,18 @@ void ieee80211_mlme_notify_scan_complete
 		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;
+}
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2009-04-15 20:32:42.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h	2009-04-15 20:40:22.000000000 +0200
@@ -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 
 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);
--- wireless-testing.orig/net/mac80211/main.c	2009-04-15 20:32:42.000000000 +0200
+++ wireless-testing/net/mac80211/main.c	2009-04-15 20:40:22.000000000 +0200
@@ -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 ieee802
 		}
 	}
 
+	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 ieee
 	tasklet_kill(&local->tx_pending_tasklet);
 	tasklet_kill(&local->tasklet);
 
+	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
+			       &local->network_latency_notifier);
+
 	rtnl_lock();
 
 	/*
--- wireless-testing.orig/net/mac80211/iface.c	2009-04-15 20:32:42.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2009-04-15 20:40:22.000000000 +0200
@@ -317,7 +317,7 @@ static int ieee80211_open(struct net_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_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)
--- wireless-testing.orig/net/mac80211/wext.c	2009-04-15 20:32:42.000000000 +0200
+++ wireless-testing/net/mac80211/wext.c	2009-04-15 20:40:22.000000000 +0200
@@ -789,7 +789,7 @@ static int ieee80211_ioctl_siwpower(stru
 		ieee80211_hw_config(local,
 				    IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-	ieee80211_recalc_ps(local);
+	ieee80211_recalc_ps(local, -1);
 
 	return 0;
 }
--- wireless-testing.orig/include/linux/ieee80211.h	2009-04-15 20:32:42.000000000 +0200
+++ wireless-testing/include/linux/ieee80211.h	2009-04-15 20:40:22.000000000 +0200
@@ -1383,4 +1383,13 @@ static inline int ieee80211_freq_to_ofdm
 		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 */



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 3/3 v2] mac80211: enable PS by default
  2009-04-15 17:10 ` [RFC 3/3] mac80211: enable PS by default Johannes Berg
@ 2009-04-16 10:39   ` Johannes Berg
  0 siblings, 0 replies; 12+ messages in thread
From: Johannes Berg @ 2009-04-16 10:39 UTC (permalink / raw)
  To: linux-wireless

Enable PS by default (depending on Kconfig) -- rely on drivers
to control the level using pm_qos. Due to the previous patch
we turn off PS when necessary due to latency requirements.

This has a Kconfig symbol so people can, if they really want,
configure the default in their kernels. We may want to keep it
at "default y" only in wireless-testing for a while.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/Kconfig |   16 ++++++++++++++++
 net/mac80211/mlme.c  |    3 +++
 2 files changed, 19 insertions(+)

--- wireless-testing.orig/net/mac80211/mlme.c	2009-04-16 12:22:35.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c	2009-04-16 12:34:21.000000000 +0200
@@ -2200,6 +2200,9 @@ void ieee80211_sta_setup_sdata(struct ie
 		IEEE80211_STA_AUTO_CHANNEL_SEL;
 	if (sdata->local->hw.queues >= 4)
 		ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
+
+	if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
+		ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE;
 }
 
 /* configuration hooks */
--- wireless-testing.orig/net/mac80211/Kconfig	2009-04-16 12:34:28.000000000 +0200
+++ wireless-testing/net/mac80211/Kconfig	2009-04-16 12:36:52.000000000 +0200
@@ -11,6 +11,22 @@ config MAC80211
 	  This option enables the hardware independent IEEE 802.11
 	  networking stack.
 
+config MAC80211_DEFAULT_PS
+	bool "enable powersave by default"
+	depends on MAC80211
+	default y
+	help
+	  This option enables powersave mode by default.
+
+	  If this causes your applications to misbehave you should fix your
+	  applications instead -- they need to register their network
+	  latency requirement, see Documentation/power/pm_qos_interface.txt.
+
+config MAC80211_DEFAULT_PS_VALUE
+	int
+	default 1 if MAC80211_DEFAULT_PS
+	default 0
+
 menu "Rate control algorithm selection"
 	depends on MAC80211 != n
 



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 1/3 v2] mac80211: improve powersave implementation
  2009-04-15 19:38   ` [RFC 1/3 v2] " Johannes Berg
@ 2009-04-16 10:55     ` Kalle Valo
  0 siblings, 0 replies; 12+ messages in thread
From: Kalle Valo @ 2009-04-16 10:55 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

Johannes Berg <johannes@sipsolutions.net> writes:

> When you have multiple virtual interfaces the current
> implementation requires setting them up properly from
> userspace, which is undesirable when we want to default
> to power save mode. Keep track of powersave requested
> from userspace per managed mode interface, and only
> enable powersave globally when exactly one managed mode
> interface is active and has powersave turned on.
>
> Second, only start the dynPS timer when PS is turned
> on, and properly turn it off when PS is turned off.
>
> Third, fix the scan_sdata abuse in the dynps code.
>
> Finally, also reorder the code and refactor the code
> that enables PS or the dynps timer instead of having
> it copied in two places.

The code is a lot cleaner now, thanks for doing this.

Unfortunately I don't have time to do proper review or testing right
now, but after a quick look this looks good.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 2/3 v2] mac80211: disable powersave if pm_qos asks for low latency
  2009-04-15 19:38   ` [RFC 2/3 v2] " Johannes Berg
@ 2009-04-16 10:58     ` Kalle Valo
  0 siblings, 0 replies; 12+ messages in thread
From: Kalle Valo @ 2009-04-16 10:58 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

Johannes Berg <johannes@sipsolutions.net> writes:

> 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.

I like this! 

This is a good start, from this can start improving this. We need to
take into account for example DTIM count, U-APSD and what not. But
that's all for the future.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] mac80211 powersave improvements
  2009-04-15 17:10 [RFC 0/3] mac80211 powersave improvements Johannes Berg
                   ` (2 preceding siblings ...)
  2009-04-15 17:10 ` [RFC 3/3] mac80211: enable PS by default Johannes Berg
@ 2009-04-16 11:01 ` Kalle Valo
  2009-04-16 11:06   ` Johannes Berg
  3 siblings, 1 reply; 12+ messages in thread
From: Kalle Valo @ 2009-04-16 11:01 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

Johannes Berg <johannes@sipsolutions.net> writes:

> Hi,

Hallo,

> This series contains the following:
>
>  1) improve powersave to only allow it when a single
>     managed interface is active
>
>  2) disable PS when the maximum networking latency
>     applications are are willing to put up with is
>     smaller than the beacon interval -- in that case
>     we cannot allow the AP to buffer frames

No problem with these.

>  3) enable powersave by default
>
> The last part might be controversial?

I think this a bad idea. For normal users we should not enable powersave
by default, at least not yet. I believe that there would be so much
problems that we would have to revert the change eventually.

> Should we have a default setting for the dynamic PS
> timeout?

Yes. Having timeout zero makes no sense in normal laptop use.

> Oh also -- below is a small program to play with the
> pm_qos framework, feel free to rip it for anything.

Thanks, this is useful.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] mac80211 powersave improvements
  2009-04-16 11:01 ` [RFC 0/3] mac80211 powersave improvements Kalle Valo
@ 2009-04-16 11:06   ` Johannes Berg
  2009-04-16 11:11     ` Kalle Valo
  0 siblings, 1 reply; 12+ messages in thread
From: Johannes Berg @ 2009-04-16 11:06 UTC (permalink / raw)
  To: Kalle Valo; +Cc: linux-wireless

[-- Attachment #1: Type: text/plain, Size: 1171 bytes --]

On Thu, 2009-04-16 at 14:01 +0300, Kalle Valo wrote:

> >  1) improve powersave to only allow it when a single
> >     managed interface is active
> >
> >  2) disable PS when the maximum networking latency
> >     applications are are willing to put up with is
> >     smaller than the beacon interval -- in that case
> >     we cannot allow the AP to buffer frames
> 
> No problem with these.

I'll send these along with the software beacon filtering, I think.

> >  3) enable powersave by default
> >
> > The last part might be controversial?
> 
> I think this a bad idea. For normal users we should not enable powersave
> by default, at least not yet. I believe that there would be so much
> problems that we would have to revert the change eventually.

We can go with the configurable one -- and default y in wireless-testing
but default n in mainline for a while. Get press coverage etc. and at
some point flip the switch.

> > Should we have a default setting for the dynamic PS
> > timeout?
> 
> Yes. Having timeout zero makes no sense in normal laptop use.

So, what's a good default? Fairly long I'd say, maybe 500ms?

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] mac80211 powersave improvements
  2009-04-16 11:06   ` Johannes Berg
@ 2009-04-16 11:11     ` Kalle Valo
  0 siblings, 0 replies; 12+ messages in thread
From: Kalle Valo @ 2009-04-16 11:11 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

Johannes Berg <johannes@sipsolutions.net> writes:

> On Thu, 2009-04-16 at 14:01 +0300, Kalle Valo wrote:
>
>> I think this a bad idea. For normal users we should not enable powersave
>> by default, at least not yet. I believe that there would be so much
>> problems that we would have to revert the change eventually.
>
> We can go with the configurable one -- and default y in wireless-testing
> but default n in mainline for a while. Get press coverage etc. and at
> some point flip the switch.

That's a lot better. Gradually expanding the amount of users makes it
easier. Big bang is always difficult and usually doomed to a failure :)

>> > Should we have a default setting for the dynamic PS
>> > timeout?
>> 
>> Yes. Having timeout zero makes no sense in normal laptop use.
>
> So, what's a good default? Fairly long I'd say, maybe 500ms?

Yes, at least. Maybe even 1s? We can gradually start lowering it.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2009-04-16 11:11 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-15 17:10 [RFC 0/3] mac80211 powersave improvements Johannes Berg
2009-04-15 17:10 ` [RFC 1/3] mac80211: improve powersave implementation Johannes Berg
2009-04-15 19:38   ` [RFC 1/3 v2] " Johannes Berg
2009-04-16 10:55     ` Kalle Valo
2009-04-15 17:10 ` [RFC 2/3] mac80211: disable powersave if pm_qos asks for low latency Johannes Berg
2009-04-15 19:38   ` [RFC 2/3 v2] " Johannes Berg
2009-04-16 10:58     ` Kalle Valo
2009-04-15 17:10 ` [RFC 3/3] mac80211: enable PS by default Johannes Berg
2009-04-16 10:39   ` [RFC 3/3 v2] " Johannes Berg
2009-04-16 11:01 ` [RFC 0/3] mac80211 powersave improvements Kalle Valo
2009-04-16 11:06   ` Johannes Berg
2009-04-16 11:11     ` Kalle Valo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.