Skip to content

Single Interface Power Save

doru91 edited this page Jun 5, 2017 · 46 revisions

Power Save Support for one virtual interface

Power Save Support for ath9k_htc driver is buggy: RX wake-up is not properly implemented. When there is no data transmitted, the WiFi card is put to sleep. The WiFi card enters the 'NETWORK SLEEP' state where the MAC subsystem is in low-power mode and no data is received from the AP that the client is associated to. The RTC subsystem is always on and can be programmed to wake up periodically the MAC subsystem (e.g.: for BEACONs with DTIM set). Using the hardware timers represented by MAC_PCU_SLP1 and MAC_PCU_SLP2 registers, the card can be programmed to wake up periodically for management frames. The problem is that the current implementation does not correctly program these hardware timers and the hardware is woken up by software timers instead or by TX operations:

  • there is a mac80211 software timer that detects 7 beacon lost and wake up the card
  • mac80211 wakes up the hardware when there is a new frame for TX.

This commit tries to solve the bug.

Test Bed

  • WiFi Dongle: TP-LINK TL-WN722N
  • AP: Android Wi-Fi Hotsport from a Nexus5 smartphone running CM13, 802.11n, DTIM count = 2, beacon interval = 100ms, SSID: AndroidAP, Password: iarbaeverde
  • Power Monitor: Monsoon

Bug reproduction

Start a connection between the WiFi dongle and the AP (wpa_supplicant), enable power save mode (iw wlanX set power save on), start pinging the AP at 1 ms intervals then capture the WiFi packets using Wireshark in monitor mode. Let's analayze this capture:

  • time 110.49, packet 7573: client sends a ping request
  • time 110.59, packet 7579: client sends a NULL frame announcing that it will enter PS mode
  • time 110.79, packet 7587; time 110.89, packet 7591; time 111.00, packet 7594; time 111.10, packet 7597; time 111.20, packet 7599; time 111.30, packet 7602; time 111.40, packet 7605: beacon frames from the AP, announcing the client that it has buffered data: the corresponding bit from the Partial Virtual Bitmap is set

  • time 111.48, packet 7607: client sends a new ping request and announce the AP that is has woken up
  • time 111.48, packet 7611: AP sends a ping reply

DTIM count = 2 so the client should wake up at most 200ms (according to the IEEE802.11 specification). However, the client sleeps around 700ms before waking up.

Also, we can observe the wrong sleep behavior by analyzing a Power Monitor Capture. The capture is taken without ping traffic, just management traffic. Delta time between cursors is 795ms and this time corresponds with the timeout for the mac80211 software timer responsible for beacon loss.

Solution

If we correctly arm the hardware timers (commit), WiFi card wakes up each 100ms. Wireshark captures show that the client receives each beacon and once the corresponding bit is set in the Partial Virtual Bitmap, the client sends a NULL frame to the AP announcing it that is awake.

Analysis

1. Awake time

The awake time is configured in two ways:

  1. directly writing in MAC_PCU_SLP1/MAC_PCU_SLP2 registers. This mechanism is triggered at driver level.
  2. using the dynamic_ps_timeout timer which clears/sets the RTC Force Wake register on timeout. This mechanism is triggered at mac80211 level. This methods are not mutually exclusive.

First method

According to the datasheet, section 6.11.31, there are two registers that controls how much the card stays awake after a beacon/data frame is received:

  • CAB_TIMEOUT - Time in TU that the PCU waits for CAB after receiving the beacon or the previous CAB, insuring that if no CAB is received after the beacon is received or if a long gap occurs between CABs, the CAB powersave state returns to idle
  • BEACON_TIMEOUT - Time in TU that the PCU waits for a beacon after waking up. If this time expires, the PCU woke due to SLP_NEXT_DTIM, and SLP_ASSUME_DTIM is active, then it assumes the beacon was missed and goes directly to watching for CAB. Otherwise when this time expires, the beacon powersave state returns to idle. These registers are set inside ath9k_hw_set_sta_beacon_timers.

Second Method

If no data is received for a period of dynamic_ps_timeout, then the chip goes to sleep: first it sends a NULL frame with the PWR MGT BIT set then it waits for its ACK before going to sleep. This behavior is controlled by the dynamic_ps_timer which triggers the ieee80211_dynamic_ps_enable_work workqueue. Once the NULL frame is ACKed, ieee80211_hw_config is called with the flag IEEE80211_CONF_CHANGE_PS. From MAC80211 layer, the control is passed to the ath9k_htc: inside ath9k_set_power_network_sleep the RTC force wake bit is cleared to allow the mac to go to sleep (see also section 6.9.4 from AR9271 datasheet).


Tests

  • WiFi Dongle: TP-LINK TL-WN722N
  • AP: Linux AP, DTIM count = 2, beacon interval = 100ms, SSID: uberAP1, No Password
  • Power Monitor: Monsoon

From out tests it seems that as long as the RTC Force Wake is set the CAB_TIMEOUT value has no effect.

Example 1: CAB_TIMEOUT=40ms; and dynamic_ps_timeout=80ms; pings at 1 second interval generated from Linux AP to WiFi Dongle:

From the Wireshark capture we can notice that between the ping reply packet and the NULL function are 80s, which is the dynamic_ps_timeout. Full wireshark capture can be found here.

The main problems seems to be that the Power Monitor capture shows that the card stays awake ~160ms for a ping-request->ping-reply exchange, while we expected to stay awake: x milliseconds for capturing the beacon with the TIM set, y milliseconds for receiving the ping request and generating the ping reply + dynamic_ps_timeout milliseconds. In our case x+y ~= 80ms which is huge.

Example 2: CAB_TIMEOUT=40ms; and dynamic_ps_timeout=40ms; pings at 1 second interval generated from Linux AP to WiFi Dongle:

From the Wireshark capture we can notice that between the ping reply packet and the NULL function are 40s, which is the dynamic_ps_timeout. Full wireshark capture can be found here

The main problems seems to be that the Power Monitor capture shows that the card stays awake ~100ms for a ping-request->ping-reply exchange, while we expected to stay awake: x milliseconds for capturing the beacon with the TIM set, y milliseconds for receiving the ping request and generating the ping reply + dynamic_ps_timeout milliseconds. In our case x+y ~= 60ms which is huge.

Example 3: CAB_TIMEOUT=40ms; and dynamic_ps_timeout=10ms; pings at 1 second interval generated from Linux AP to WiFi Dongle:

The main problems seems to be that the Power Monitor capture shows that the card stays awake ~40ms for a ping-request->ping-reply exchange, while we expected to stay awake: x milliseconds for capturing the beacon with the TIM set, y milliseconds for receiving the ping request and generating the ping reply + dynamic_ps_timeout milliseconds. In our case x+y ~= 30ms which is huge.

2. Ping RTT

Same testbed from the above test. dynamic_ps_timeout=10ms; pings at 0.1 second interval generated from Linux AP to WiFi Dongle:

uberap1:/home/mvochin: ping -i 0.1 10.10.1.85 PING 10.10.1.85 (10.10.1.85) 56(84) bytes of data.

--- 10.10.1.85 ping statistics --- 257 packets transmitted, 255 received, 0% packet loss, time 26629ms rtt min/avg/max/mdev = 85.978/193.638/293.877/57.805 ms, pipe 3

Full statistics can be found here

The main issue here is that the lower limit for RTT is ~DTIM/2. The upper limit for RTT is ~DTIM1.5: looking in the Wireshark trace 1 and Wireshark trace 2 the DTIM1.5 value is explained by the next behavior: the AP generates a ping request packet but this packet can't be sent into the air because the client is asleep. So the AP has to wait a few milliseconds until the next beacon is generated and the TIM is set. The client does not see the first beacon but the second one (DTIM == 200). So the RTT==DTIM+delay waiting for the first beacon after ping request to be generated.

Regarding the lower limit for the RTT, we would have expected to be very low, a few milliseconds: once the client wakes up we should be able to send directly a ping request. However, the client wakes up for a very short amount of time (10ms) and we generate ping requests at 100ms. When the client wakes up, we send just the buffered data and it might be that we're not generating any ping request while the client is awake.

However, this lower limit for RTT is kept when we generate pings at 1ms interval. TODO: find an explanation.


Once we set the dynamic_ps_timeout to 15ms, the behavior is the expected one for pings generated at 10ms interval (we can see pings with very low RTT):

--- 10.10.1.85 ping statistics --- 693 packets transmitted, 677 received, 2% packet loss, time 10345ms rtt min/avg/max/mdev = 0.832/144.368/394.880/86.750 ms, pipe 22

For a single ping-request->ping-reply exchange the Power Monitor capture shows that the card stays awake around 40ms: