Commit 520eb82076993b7f55ef9b80771d264272e5127b

Authored by Kalle Valo
Committed by John W. Linville
1 parent ce7c9111a9

mac80211: implement dynamic power save

This patch implements dynamic power save for mac80211. Basically it
means enabling power save mode after an idle period. Implementing it
dynamically gives a good compromise of low power consumption and low
latency. Some hardware have support for this in firmware, but some
require the host to do it.

The dynamic power save is implemented by adding an timeout to
ieee80211_subif_start_xmit(). The timeout can be enabled from userspace
with Wireless Extensions. For example, the command below enables the
dynamic power save and sets the time timeout to 500 ms:

iwconfig wlan0 power timeout 500m

Power save now only works with devices which handle power save in firmware.
It's also disabled by default and the heuristics when and how to enable is
considered as a policy decision and will be left for the userspace to handle.
In case the firmware has support for this, drivers can disable this feature
with IEEE80211_HW_NO_STACK_DYNAMIC_PS.

Big thanks to Johannes Berg for the help with the design and code.

Signed-off-by: Kalle Valo <kalle.valo@nokia.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

Showing 6 changed files with 103 additions and 12 deletions Side-by-side Diff

include/net/mac80211.h
... ... @@ -854,6 +854,11 @@
854 854 *
855 855 * @IEEE80211_HW_AMPDU_AGGREGATION:
856 856 * Hardware supports 11n A-MPDU aggregation.
  857 + *
  858 + * @IEEE80211_HW_NO_STACK_DYNAMIC_PS:
  859 + * Hardware which has dynamic power save support, meaning
  860 + * that power save is enabled in idle periods, and don't need support
  861 + * from stack.
857 862 */
858 863 enum ieee80211_hw_flags {
859 864 IEEE80211_HW_RX_INCLUDES_FCS = 1<<1,
... ... @@ -866,6 +871,7 @@
866 871 IEEE80211_HW_NOISE_DBM = 1<<8,
867 872 IEEE80211_HW_SPECTRUM_MGMT = 1<<9,
868 873 IEEE80211_HW_AMPDU_AGGREGATION = 1<<10,
  874 + IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11,
869 875 };
870 876  
871 877 /**
net/mac80211/ieee80211_i.h
... ... @@ -540,6 +540,7 @@
540 540  
541 541 enum queue_stop_reason {
542 542 IEEE80211_QUEUE_STOP_REASON_DRIVER,
  543 + IEEE80211_QUEUE_STOP_REASON_PS,
543 544 };
544 545  
545 546 /* maximum number of hardware queues we support. */
546 547  
... ... @@ -693,7 +694,12 @@
693 694 */
694 695 int wifi_wme_noack_test;
695 696 unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
  697 +
696 698 bool powersave;
  699 + int dynamic_ps_timeout;
  700 + struct work_struct dynamic_ps_enable_work;
  701 + struct work_struct dynamic_ps_disable_work;
  702 + struct timer_list dynamic_ps_timer;
697 703  
698 704 #ifdef CONFIG_MAC80211_DEBUGFS
699 705 struct local_debugfsdentries {
... ... @@ -976,6 +982,10 @@
976 982 int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
977 983 u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
978 984 enum ieee80211_band band);
  985 +
  986 +void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
  987 +void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
  988 +void ieee80211_dynamic_ps_timer(unsigned long data);
979 989  
980 990 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
981 991 enum queue_stop_reason reason);
... ... @@ -729,6 +729,13 @@
729 729  
730 730 INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
731 731  
  732 + INIT_WORK(&local->dynamic_ps_enable_work,
  733 + ieee80211_dynamic_ps_enable_work);
  734 + INIT_WORK(&local->dynamic_ps_disable_work,
  735 + ieee80211_dynamic_ps_disable_work);
  736 + setup_timer(&local->dynamic_ps_timer,
  737 + ieee80211_dynamic_ps_timer, (unsigned long) local);
  738 +
732 739 sta_info_init(local);
733 740  
734 741 tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
... ... @@ -745,8 +745,14 @@
745 745 ieee80211_bss_info_change_notify(sdata, bss_info_changed);
746 746  
747 747 if (local->powersave) {
748   - local->hw.conf.flags |= IEEE80211_CONF_PS;
749   - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
  748 + if (local->dynamic_ps_timeout > 0)
  749 + mod_timer(&local->dynamic_ps_timer, jiffies +
  750 + msecs_to_jiffies(local->dynamic_ps_timeout));
  751 + else {
  752 + conf->flags |= IEEE80211_CONF_PS;
  753 + ieee80211_hw_config(local,
  754 + IEEE80211_CONF_CHANGE_PS);
  755 + }
750 756 }
751 757  
752 758 netif_tx_start_all_queues(sdata->dev);
... ... @@ -866,6 +872,9 @@
866 872 local->oper_channel_type = NL80211_CHAN_NO_HT;
867 873 config_changed |= IEEE80211_CONF_CHANGE_HT;
868 874  
  875 + del_timer_sync(&local->dynamic_ps_timer);
  876 + cancel_work_sync(&local->dynamic_ps_enable_work);
  877 +
869 878 if (local->hw.conf.flags & IEEE80211_CONF_PS) {
870 879 local->hw.conf.flags &= ~IEEE80211_CONF_PS;
871 880 config_changed |= IEEE80211_CONF_CHANGE_PS;
... ... @@ -2592,5 +2601,41 @@
2592 2601 list_for_each_entry_rcu(sdata, &local->interfaces, list)
2593 2602 ieee80211_restart_sta_timer(sdata);
2594 2603 rcu_read_unlock();
  2604 +}
  2605 +
  2606 +void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
  2607 +{
  2608 + struct ieee80211_local *local =
  2609 + container_of(work, struct ieee80211_local,
  2610 + dynamic_ps_disable_work);
  2611 +
  2612 + if (local->hw.conf.flags & IEEE80211_CONF_PS) {
  2613 + local->hw.conf.flags &= ~IEEE80211_CONF_PS;
  2614 + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
  2615 + }
  2616 +
  2617 + ieee80211_wake_queues_by_reason(&local->hw,
  2618 + IEEE80211_QUEUE_STOP_REASON_PS);
  2619 +}
  2620 +
  2621 +void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
  2622 +{
  2623 + struct ieee80211_local *local =
  2624 + container_of(work, struct ieee80211_local,
  2625 + dynamic_ps_enable_work);
  2626 +
  2627 + if (local->hw.conf.flags & IEEE80211_CONF_PS)
  2628 + return;
  2629 +
  2630 + local->hw.conf.flags |= IEEE80211_CONF_PS;
  2631 +
  2632 + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
  2633 +}
  2634 +
  2635 +void ieee80211_dynamic_ps_timer(unsigned long data)
  2636 +{
  2637 + struct ieee80211_local *local = (void *) data;
  2638 +
  2639 + queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
2595 2640 }
... ... @@ -1473,6 +1473,19 @@
1473 1473 goto fail;
1474 1474 }
1475 1475  
  1476 + if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
  1477 + local->dynamic_ps_timeout > 0) {
  1478 + if (local->hw.conf.flags & IEEE80211_CONF_PS) {
  1479 + ieee80211_stop_queues_by_reason(&local->hw,
  1480 + IEEE80211_QUEUE_STOP_REASON_PS);
  1481 + queue_work(local->hw.workqueue,
  1482 + &local->dynamic_ps_disable_work);
  1483 + }
  1484 +
  1485 + mod_timer(&local->dynamic_ps_timer, jiffies +
  1486 + msecs_to_jiffies(local->dynamic_ps_timeout));
  1487 + }
  1488 +
1476 1489 nh_pos = skb_network_header(skb) - skb->data;
1477 1490 h_pos = skb_transport_header(skb) - skb->data;
1478 1491  
... ... @@ -833,7 +833,7 @@
833 833 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
834 834 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
835 835 struct ieee80211_conf *conf = &local->hw.conf;
836   - int ret = 0;
  836 + int ret = 0, timeout = 0;
837 837 bool ps;
838 838  
839 839 if (sdata->vif.type != NL80211_IFTYPE_STATION)
... ... @@ -841,6 +841,7 @@
841 841  
842 842 if (wrq->disabled) {
843 843 ps = false;
  844 + timeout = 0;
844 845 goto set;
845 846 }
846 847  
847 848  
848 849  
849 850  
850 851  
... ... @@ -850,22 +851,31 @@
850 851 case IW_POWER_ALL_R: /* If explicitely state all */
851 852 ps = true;
852 853 break;
853   - default: /* Otherwise we don't support it */
854   - return -EINVAL;
  854 + default: /* Otherwise we ignore */
  855 + break;
855 856 }
856 857  
857   - if (ps == local->powersave)
858   - return ret;
  858 + if (wrq->flags & IW_POWER_TIMEOUT)
  859 + timeout = wrq->value / 1000;
859 860  
860 861 set:
  862 + if (ps == local->powersave && timeout == local->dynamic_ps_timeout)
  863 + return ret;
  864 +
861 865 local->powersave = ps;
  866 + local->dynamic_ps_timeout = timeout;
862 867  
863 868 if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
864   - if (local->powersave)
865   - conf->flags |= IEEE80211_CONF_PS;
866   - else
867   - conf->flags &= ~IEEE80211_CONF_PS;
868   -
  869 + if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
  870 + local->dynamic_ps_timeout > 0)
  871 + mod_timer(&local->dynamic_ps_timer, jiffies +
  872 + msecs_to_jiffies(local->dynamic_ps_timeout));
  873 + else {
  874 + if (local->powersave)
  875 + conf->flags |= IEEE80211_CONF_PS;
  876 + else
  877 + conf->flags &= ~IEEE80211_CONF_PS;
  878 + }
869 879 ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
870 880 }
871 881