Skip to contentSkip to conversation

mesh power save - hardware doze

RFC New Marco Porsch 2 participants 9 messages Feb 6, 2013 – Feb 13, 2013

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v2 cover mesh power save - hardware doze

Commits #2 and #3 implement the actual power saving mechanism for mesh.

When the current peer link states allow doze state, the HW is configured accordingly.
STA wake up from doze state for
1) sending their own beacon and tailing awake window
2) receiving beacons of peers they are in light sleep towards In these two situations a Mesh Peer Service Period may be initiated to extend the awake phase to transmit/receive frames.

Design goal was to implement most PS routines in mac80211 to keep maintainability high and allow simple driver adaption. Since mesh PS requires waiting for beacon/multicasts (CAB) per STA, these routines are re-implemented in mac80211.

The implementation should be capable of managing multiple concurrent mesh vif, although this has rarely been tested, yet.

(For more info see https://github.com/cozybit/open80211s/wiki
/Mesh-Powersave-Implementation-Notes.)

On some test devices (WNDR3700/3800 with openwrt r35284) I experience frequent crashes ("Data bus error"). Adding udelay to ath9k_hw_set_power_awake and ath9k_set_power_network_sleep as described in https://dev.openwrt.org/ticket/9107 seems to help. I am unsure if I just stumble over a bug or cause it myself here.

v2:
mac80211:
- trigger wakeups in HW (Johannes)
- got rid of hrtimer usage (Johannes)
- got rid of pre-TBTT wakeup margin
- got rid of HW sleep/awake status variable in local
- fix deep sleep wakeup schedule
ath9k:
- program HW's TIM timer registers to perform wakeups

Marco Porsch (3):
mac80211: move mesh sync beacon handler into neighbour_update
mac80211: mesh power save doze scheduling
ath9k: mesh powersave support

 drivers/net/wireless/ath/ath9k/ath9k.h  |    1 +
 drivers/net/wireless/ath/ath9k/beacon.c |   21 +-
 drivers/net/wireless/ath/ath9k/hw.c     |   20 +-
 drivers/net/wireless/ath/ath9k/main.c   |   67 ++++++-
 include/net/mac80211.h                  |   29 +++
 net/mac80211/ieee80211_i.h              |   17 +-
 net/mac80211/mesh.c                     |   25 ++-
 net/mac80211/mesh.h                     |   22 ++-
 net/mac80211/mesh_plink.c               |   17 +-
 net/mac80211/mesh_ps.c                  |  316 +++++++++++++++++++++++++++++++
 net/mac80211/mesh_sync.c                |   47 ++---
 net/mac80211/sta_info.c                 |    3 +
 net/mac80211/sta_info.h                 |   10 +
 net/mac80211/tx.c                       |    2 +
 14 files changed, 541 insertions(+), 56 deletions(-)
Signature · 1 line
1.7.9.5
#1 lore raw

RFC v2 1/3 mac80211: move mesh sync beacon handler into neighbour_update

Move the beacon handler into mesh_neighbour_update where the STA pointer is already available. This avoids additional overhead and simplifies the handler.
The repositioning will also benefit mesh PS which uses the T_offset value right after it has been updated.

Rename the handler to better reflect its purpose.

---
 net/mac80211/ieee80211_i.h |   10 +++++-----
 net/mac80211/mesh.c        |    8 ++------
 net/mac80211/mesh.h        |    5 +++--
 net/mac80211/mesh_plink.c  |   16 ++++++++++++---
 net/mac80211/mesh_sync.c   |   47 +++++++++++++++-----------------------------
 5 files changed, 39 insertions(+), 47 deletions(-)
net/mac80211/ieee80211_i.h +5 -5
index 650f758..bba2b10 100644
@@ -560,11 +560,11 @@ struct ieee80211_if_ibss {
  */
 struct ieee802_11_elems;
 struct ieee80211_mesh_sync_ops {
-	void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata,
-			     u16 stype,
-			     struct ieee80211_mgmt *mgmt,
-			     struct ieee802_11_elems *elems,
-			     struct ieee80211_rx_status *rx_status);
+	void (*rx_bcn)(struct sta_info *sta,
+		       struct ieee80211_mgmt *mgmt,
+		       struct ieee802_11_elems *elems,
+		       struct ieee80211_rx_status *rx_status,
+		       u64 tsf);
 	void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata);
 	/* add other framework functions here */
 };
net/mac80211/mesh.c +2 -6
index e39a3f8..643262b 100644
@@ -714,7 +714,6 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
 					struct ieee80211_rx_status *rx_status)
 {
 	struct ieee80211_local *local = sdata->local;
-	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 	struct ieee802_11_elems elems;
 	struct ieee80211_channel *channel;
 	size_t baselen;
@@ -750,13 +749,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
 		return;
 
 	if (mesh_matches_local(sdata, &elems))
-		mesh_neighbour_update(sdata, mgmt->sa, &elems);
-
-	if (ifmsh->sync_ops)
-		ifmsh->sync_ops->rx_bcn_presp(sdata,
-			stype, mgmt, &elems, rx_status);
+		mesh_neighbour_update(sdata, mgmt, &elems, rx_status);
 }
 
+
 static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
 					  struct ieee80211_mgmt *mgmt,
 					  size_t len,
net/mac80211/mesh.h +3 -2
index eb33625..d6d9933 100644
@@ -283,8 +283,9 @@ int mesh_path_send_to_gates(struct mesh_path *mpath);
 int mesh_gate_num(struct ieee80211_sub_if_data *sdata);
 /* Mesh plinks */
 void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
-			   u8 *hw_addr,
-			   struct ieee802_11_elems *ie);
+			   struct ieee80211_mgmt *mgmt,
+			   struct ieee802_11_elems *ie,
+			   struct ieee80211_rx_status *rx_status);
 bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
 u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
 void mesh_plink_broken(struct sta_info *sta);
net/mac80211/mesh_plink.c +13 -3
index 57e7267..af6fbfd 100644
@@ -12,6 +12,7 @@
 #include "ieee80211_i.h"
 #include "rate.h"
 #include "mesh.h"
+#include "driver-ops.h"
 
 #define PLINK_GET_LLID(p) (p + 2)
 #define PLINK_GET_PLID(p) (p + 4)
@@ -391,13 +392,19 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
 }
 
 void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
-			   u8 *hw_addr,
-			   struct ieee802_11_elems *elems)
+			   struct ieee80211_mgmt *mgmt,
+			   struct ieee802_11_elems *elems,
+			   struct ieee80211_rx_status *rx_status)
 {
+	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 	struct sta_info *sta;
+	u64 tsf;
+
+	/* get tsf before entering rcu-read section */
+	tsf = drv_get_tsf(sdata->local, sdata);
 
 	rcu_read_lock();
-	sta = mesh_peer_init(sdata, hw_addr, elems);
+	sta = mesh_peer_init(sdata, mgmt->sa, elems);
 	if (!sta)
 		goto out;
 
@@ -408,6 +415,9 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
 	    rssi_threshold_check(sta, sdata))
 		mesh_plink_open(sta);
 
+	if (ifmsh->sync_ops)
+		ifmsh->sync_ops->rx_bcn(sta, mgmt, elems, rx_status, tsf);
+
 	ieee80211_mps_frame_release(sta, elems);
 out:
 	rcu_read_unlock();
net/mac80211/mesh_sync.c +16 -31

92 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#2 lore raw

RFC v2 2/3 mac80211: mesh power save doze scheduling

In reply to Marco Porsch

Configure the HW for PS mode if the local mesh PS parameters allow so.

Expose a callback ieee80211_mps_init for drivers to register mesh powersave ops:
- hw_doze - put the radio to sleep now, wake up at given TBTT
- hw_wakeup - wake the radio up for frame RX
These ops may be extended in the future to allow drivers/HW to implement mesh PS themselves. (The current design goal was to concentrate most mesh PS routines in mac80211 to keep driver modifications minimal.

Track the beacon timing information of peers we are in PS mode towards. Calculate the next TBTT per-STA.

When going to doze state, get the most imminent STA TBTT and configure the driver to trigger a wakeup on time to catch that beacon. After successful receipt put the HW to doze again. Set a timeout for the case that the beacon is not received on time. In this case calculate the following TBTT and go to doze again.

For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry.

---
 include/net/mac80211.h     |   29 ++++
 net/mac80211/ieee80211_i.h |    7 +-
 net/mac80211/mesh.c        |   17 +++
 net/mac80211/mesh.h        |   17 +++
 net/mac80211/mesh_plink.c  |    1 +
 net/mac80211/mesh_ps.c     |  316 ++++++++++++++++++++++++++++++++++++++++++++
 net/mac80211/sta_info.c    |    3 +
 net/mac80211/sta_info.h    |   10 ++
 net/mac80211/tx.c          |    2 +
 9 files changed, 401 insertions(+), 1 deletion(-)
include/net/mac80211.h +29 -0
index 3037f49..e393a49 100644
@@ -3999,6 +3999,35 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
  */
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
 
+/**
+ * struct ieee80211_mps_ops - callbacks from mac80211 to the driver for mesh PS
+ *
+ * @hw_doze: put the radio to doze state to conserve power, schedule wakeup
+ *	at given TSF value to receive peer beacon
+ * @hw_wakeup: wake up the radio to receive frames again
+ */
+struct ieee80211_mps_ops {
+	void (*hw_doze)(struct ieee80211_hw *hw, u64 nexttbtt);
+	void (*hw_wakeup)(struct ieee80211_hw *hw);
+};
+
+#ifdef CONFIG_MAC80211_MESH
+/**
+ * ieee80211_mps_init - register callbacks for mesh PS
+ *
+ * @hw: the hardware
+ * @ops: callbacks for this device
+ *
+ * called by driver on mesh interface add/remove
+ */
+int ieee80211_mps_init(struct ieee80211_hw *hw,
+		       const struct ieee80211_mps_ops *ops);
+#else
+static inline int ieee80211_mps_init(struct ieee80211_hw *hw,
+				     const struct ieee80211_mps_ops *ops)
+{ return 0; }
+#endif
+
 /* Rate control API */
 
 /**
net/mac80211/ieee80211_i.h +6 -1
index bba2b10..cb54bae 100644
@@ -628,6 +628,7 @@ struct ieee80211_if_mesh {
 	int ps_peers_light_sleep;
 	int ps_peers_deep_sleep;
 	struct ps_data ps;
+	struct timer_list awake_window_end_timer;
 };
 
 #ifdef CONFIG_MAC80211_MESH
@@ -1125,7 +1126,7 @@ struct ieee80211_local {
 	bool pspolling;
 	bool offchannel_ps_enabled;
 	/*
-	 * PS can only be enabled when we have exactly one managed
+	 * managed mode 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;
@@ -1145,6 +1146,10 @@ struct ieee80211_local {
 
 	int user_power_level; /* in dBm, for all interfaces */
 
+	/* mesh power save */
+	bool mps_enabled;
+	const struct ieee80211_mps_ops *mps_ops;
+
 	enum ieee80211_smps_mode smps_mode;
 
 	struct work_struct restart_work;
net/mac80211/mesh.c +17 -0
index 643262b..b0deb14 100644
@@ -800,6 +800,7 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 {
+	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
 	if (ifmsh->preq_queue_len &&
@@ -821,6 +822,19 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 
 	if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
 		mesh_sync_adjust_tbtt(sdata);
+
+	if (test_and_clear_bit(MESH_WORK_PS_HW_CONF, &ifmsh->wrkq_flags))
+		ieee80211_mps_hw_conf(sdata);
+
+	/* in case both fired simultaneously, wakeup overrides doze */
+	if (test_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags) &&
+	    test_bit(MESH_WORK_PS_WAKEUP, &ifmsh->wrkq_flags))
+		clear_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags);
+
+	if (test_and_clear_bit(MESH_WORK_PS_WAKEUP, &ifmsh->wrkq_flags))
+		ieee80211_mps_wakeup(local);
+	else if (test_and_clear_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags))
+		ieee80211_mps_doze(local);
 }
 
 void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
@@ -860,6 +874,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
 	setup_timer(&ifmsh->mesh_path_root_timer,
 		    ieee80211_mesh_path_root_timer,
 		    (unsigned long) sdata);
+	setup_timer(&ifmsh->awake_window_end_timer,
+		    ieee80211_mps_awake_window_end,
+		    (unsigned long) sdata);
 	INIT_LIST_HEAD(&ifmsh->preq_queue.list);
 	skb_queue_head_init(&ifmsh->ps.bc_buf);
 	spin_lock_init(&ifmsh->mesh_preq_queue_lock);
net/mac80211/mesh.h +17 -0
index d6d9933..686f991 100644
@@ -58,6 +58,10 @@ enum mesh_path_flags {
  * @MESH_WORK_ROOT: the mesh root station needs to send a frame
  * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
  * mesh nodes
+ * @MESH_WORK_PS_HW_CONF: configure hardware according to the link-specific
+ * mesh power modes
+ * @MESH_WORK_PS_DOZE: put the hardware to sleep after checking all conditions
+ * @MESH_WORK_PS_WAKEUP: wakeup the hardware immediately
  */
 enum mesh_deferred_task_flags {
 	MESH_WORK_HOUSEKEEPING,
@@ -65,6 +69,9 @@ enum mesh_deferred_task_flags {
 	MESH_WORK_GROW_MPP_TABLE,
 	MESH_WORK_ROOT,
 	MESH_WORK_DRIFT_ADJUST,
+	MESH_WORK_PS_HW_CONF,
+	MESH_WORK_PS_DOZE,
+	MESH_WORK_PS_WAKEUP,
 };
 
 /**
@@ -258,6 +265,16 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
 				    bool tx, bool acked);
 void ieee80211_mps_frame_release(struct sta_info *sta,
 				 struct ieee802_11_elems *elems);
+void ieee80211_mps_hw_conf(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mps_sta_tbtt_update(struct sta_info *sta,
+				   struct ieee80211_mgmt *mgmt,
+				   struct ieee80211_tim_ie *tim,
+				   u64 tsf);
+void ieee80211_mps_sta_tbtt_timeout(unsigned long data);
+void ieee80211_mps_awake_window_start(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mps_awake_window_end(unsigned long data);
+void ieee80211_mps_doze(struct ieee80211_local *local);
+void ieee80211_mps_wakeup(struct ieee80211_local *local);
 
 /* Mesh paths */
 int mesh_nexthop_lookup(struct sk_buff *skb,
net/mac80211/mesh_plink.c +1 -0
index af6fbfd..f41b4bb 100644
@@ -419,6 +419,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
 		ifmsh->sync_ops->rx_bcn(sta, mgmt, elems, rx_status, tsf);
 
 	ieee80211_mps_frame_release(sta, elems);
+	ieee80211_mps_sta_tbtt_update(sta, mgmt, elems->tim, tsf);
 out:
 	rcu_read_unlock();
 }
net/mac80211/mesh_ps.c +316 -0

337 lines not on this page · raw message

net/mac80211/sta_info.c +3 -0
index f920154..2905f2a 100644
@@ -142,6 +142,7 @@ static void cleanup_single_sta(struct sta_info *sta)
 		mesh_accept_plinks_update(sdata);
 		mesh_plink_deactivate(sta);
 		del_timer_sync(&sta->plink_timer);
+		del_timer_sync(&sta->nexttbtt_timer);
 	}
 #endif
 
@@ -385,6 +386,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 #ifdef CONFIG_MAC80211_MESH
 	sta->plink_state = NL80211_PLINK_LISTEN;
 	init_timer(&sta->plink_timer);
+	setup_timer(&sta->nexttbtt_timer, ieee80211_mps_sta_tbtt_timeout,
+		    (unsigned long) sta);
 #endif
 
 	return sta;
net/mac80211/sta_info.h +10 -0
index 5a1deba..624d32a 100644
@@ -58,6 +58,7 @@
  * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
  * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
  * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
+ * @WLAN_STA_MPS_WAIT_FOR_CAB: multicast frames from this STA are imminent.
  */
 enum ieee80211_sta_info_flags {
 	WLAN_STA_AUTH,
@@ -82,6 +83,7 @@ enum ieee80211_sta_info_flags {
 	WLAN_STA_TOFFSET_KNOWN,
 	WLAN_STA_MPSP_OWNER,
 	WLAN_STA_MPSP_RECIPIENT,
+	WLAN_STA_MPS_WAIT_FOR_CAB,
 };
 
 #define ADDBA_RESP_INTERVAL HZ
@@ -289,6 +291,10 @@ struct sta_ampdu_mlme {
  * @local_pm: local link-specific power save mode
  * @peer_pm: peer-specific power save mode towards local STA
  * @nonpeer_pm: STA power save mode towards non-peer neighbors
+ * @beacon_interval: beacon interval of neighbor STA (in us)
+ * @nexttbtt_tsf: next TBTT in local TSF units
+ * @nexttbtt_jiffies: next TBTT in jiffies units
+ * @nexttbtt_timer: timeout for missed beacons
  * @debugfs: debug filesystem info
  * @dead: set to true when sta is unlinked
  * @uploaded: set to true when sta is uploaded to the driver
@@ -390,6 +396,10 @@ struct sta_info {
 	enum nl80211_mesh_power_mode local_pm;
 	enum nl80211_mesh_power_mode peer_pm;
 	enum nl80211_mesh_power_mode nonpeer_pm;
+	u32 beacon_interval;
+	u64 nexttbtt_tsf;
+	unsigned long nexttbtt_jiffies;
+	struct timer_list nexttbtt_timer;
 #endif
 
 #ifdef CONFIG_MAC80211_DEBUGFS
net/mac80211/tx.c +2 -0
index 75ae941..4ad45f0 100644
@@ -2495,6 +2495,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 			pr_err("o11s: couldn't add ies!\n");
 			goto out;
 		}
+
+		ieee80211_mps_awake_window_start(sdata);
 	} else {
 		WARN_ON(1);
 		goto out;
Signature · 1 line
1.7.9.5
maintainer
#3 lore raw

RFC v2 2/3 mac80211: mesh power save doze scheduling

Quoted 2 lines · Marco Porsch

For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry.

That seems questionable -- drivers are not required to request each beacon. I know you only want to make it work on ath9k, but I don't think "stretching" the API, without even documenting it, is a good idea.

net/mac80211/mesh_ps.c line 23

Quoted 6 lines
net/mac80211/mesh_ps.c +6 -0
+static inline void mps_queue_work(struct ieee80211_sub_if_data *sdata,
+				  enum mesh_deferred_task_flags flag)
+{
+	set_bit(flag, &sdata->u.mesh.wrkq_flags);
+	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+}

Doing any sort of wakeup from here is also undesirable -- the workqueue might actually sometimes be blocked for quite a while, I believe.

Quoted 13 lines
net/mac80211/mesh_ps.c +13 -0
+/**
+ * ieee80211_mps_hw_conf - check conditions for mesh PS and configure driver
+ *
+ * @sdata: local mesh subif
+ */
+void ieee80211_mps_hw_conf(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	bool enable;
+
+	enable = mps_hw_conf_check(local);
+
+	if (local->mps_enabled == enable)

net/mac80211/mesh_ps.c void ieee80211_mps_frame_release(struct sta_info *sta, · line 691

Quoted 10 lines
net/mac80211/mesh_ps.c +10 -0
+		return;
+
+	if (enable) {
+		mps_hw_conf_sta_prepare(local);
+		local->hw.conf.flags |= IEEE80211_CONF_PS;
+	} else {
+		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+	}
+
+	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);

For some reason I thought this was supposed to be covered by the new mesh PS callbacks, why do you still need hw conf?

Or wait -- this is only for general enable/disable, depending on the other interfaces? I guess that's ok.

Quoted 10 lines
net/mac80211/mesh_ps.c +10 -0
+	/* simple Deep Sleep implementation: only wake up for DTIM beacons */
+	if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
+		skip = tim->dtim_count ? tim->dtim_count : tim->dtim_period;
+	/*
+	 * determine time to peer TBTT (TSF % beacon_interval = 0).
+	 * This approach is robust to delayed beacons.
+	 */
+	tsf_peer = tsf_local + sta->t_offset;
+	nexttbtt_interval = sta->beacon_interval * skip -
+			do_div(tsf_peer, sta->beacon_interval * skip);

net/mac80211/mesh_ps.c void ieee80211_mps_frame_release(struct sta_info *sta, · line 723

Quoted 10 lines
net/mac80211/mesh_ps.c +10 -0
+
+	mps_dbg(sta->sdata, "updating %pM next TBTT in %dus (%lldus awake)\n",
+		sta->sta.addr, nexttbtt_interval,
+		(long long) tsf_local - sta->nexttbtt_tsf);
+
+	sta->nexttbtt_tsf = tsf_local + nexttbtt_interval;
+	sta->nexttbtt_jiffies = jiffies + usecs_to_jiffies(nexttbtt_interval);
+
+	mod_timer(&sta->nexttbtt_timer, sta->nexttbtt_jiffies +
+			usecs_to_jiffies(BEACON_TIMEOUT));

Is that some sort of recovery? jiffies can be up to 20ms (I think, in that order of magnitude anyway) inaccurate.

johannes

#4 lore raw

RFC v2 2/3 mac80211: mesh power save doze scheduling

Quoted 8 lines · Johannes Berg
Quoted 2 lines · Marco Porsch

For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry.

That seems questionable -- drivers are not required to request each beacon. I know you only want to make it work on ath9k, but I don't think "stretching" the API, without even documenting it, is a good idea.

Currently, we already use ieee80211_beacon_get_tim as time reference for mesh sync's adjust_tbtt. And, as far as I know, all mesh-capable drivers use the call for each and every beacon.
So what would you recommend: keep using beacon_get and adding documentation - or - creating an exported callback for awake_window_start?

Quoted 9 lines

net/mac80211/mesh_ps.c line 23

Quoted 6 lines
net/mac80211/mesh_ps.c +6 -0
+static inline void mps_queue_work(struct ieee80211_sub_if_data *sdata,
+				  enum mesh_deferred_task_flags flag)
+{
+	set_bit(flag, &sdata->u.mesh.wrkq_flags);
+	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+}

Doing any sort of wakeup from here is also undesirable -- the workqueue might actually sometimes be blocked for quite a while, I believe.

Yeah, right. Now that the hrtimer stuff is gone, I will check if the remaining contexts allows skipping the work queue for wakeups.

Quoted 45 lines
Quoted 13 lines
net/mac80211/mesh_ps.c +13 -0
+/**
+ * ieee80211_mps_hw_conf - check conditions for mesh PS and configure driver
+ *
+ * @sdata: local mesh subif
+ */
+void ieee80211_mps_hw_conf(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_local *local = sdata->local;
+	bool enable;
+
+	enable = mps_hw_conf_check(local);
+
+	if (local->mps_enabled == enable)

net/mac80211/mesh_ps.c void ieee80211_mps_frame_release(struct sta_info *sta, · line 691

Quoted 10 lines
net/mac80211/mesh_ps.c +10 -0
+		return;
+
+	if (enable) {
+		mps_hw_conf_sta_prepare(local);
+		local->hw.conf.flags |= IEEE80211_CONF_PS;
+	} else {
+		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+	}
+
+	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);

For some reason I thought this was supposed to be covered by the new mesh PS callbacks, why do you still need hw conf?

Or wait -- this is only for general enable/disable, depending on the other interfaces? I guess that's ok.

Quoted 15 lines
net/mac80211/mesh_ps.c +15 -0
+	/* simple Deep Sleep implementation: only wake up for DTIM beacons */
+	if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
+		skip = tim->dtim_count ? tim->dtim_count : tim->dtim_period;
+	/*
+	 * determine time to peer TBTT (TSF % beacon_interval = 0).
+	 * This approach is robust to delayed beacons.
+	 */
+	tsf_peer = tsf_local + sta->t_offset;
+	nexttbtt_interval = sta->beacon_interval * skip -
+			do_div(tsf_peer, sta->beacon_interval * skip);
+
+	mps_dbg(sta->sdata, "updating %pM next TBTT in %dus (%lldus awake)\n",
+		sta->sta.addr, nexttbtt_interval,
+		(long long) tsf_local - sta->nexttbtt_tsf);
+
Quoted 8 lines

net/mac80211/mesh_ps.c void ieee80211_mps_frame_release(struct sta_info *sta, · line 723

Quoted 5 lines
net/mac80211/mesh_ps.c +5 -0
+	sta->nexttbtt_tsf = tsf_local + nexttbtt_interval;
+	sta->nexttbtt_jiffies = jiffies + usecs_to_jiffies(nexttbtt_interval);
+
+	mod_timer(&sta->nexttbtt_timer, sta->nexttbtt_jiffies +
+			usecs_to_jiffies(BEACON_TIMEOUT));

Is that some sort of recovery? jiffies can be up to 20ms (I think, in that order of magnitude anyway) inaccurate.

Yeah, that timer just hits as recovery in case a beacon is not received. Then it projects the next TBTT and goes to sleep again.

Thanks,
--Marco

maintainer
#5 lore raw

RFC v2 2/3 mac80211: mesh power save doze scheduling

Quoted 10 lines · Marco Porsch
Quoted 6 lines
Quoted 2 lines

For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry.

That seems questionable -- drivers are not required to request each beacon. I know you only want to make it work on ath9k, but I don't think "stretching" the API, without even documenting it, is a good idea.

Currently, we already use ieee80211_beacon_get_tim as time reference for mesh sync's adjust_tbtt. And, as far as I know, all mesh-capable drivers use the call for each and every beacon.

Oops, why did I miss that before? :-)

Quoted 2 lines

So what would you recommend: keep using beacon_get and adding documentation - or - creating an exported callback for awake_window_start?

I guess you could add it... However, I don't really fully understand. There's no guarantee that fetching the beacon is done anywhere close to TBTT? Or does ath9k happen to do it just after TXing a beacon? You're encoding quite a lot of ath9k-specific assumptions here it seems?

johannes

#6 lore raw

RFC v2 2/3 mac80211: mesh power save doze scheduling

Quoted 15 lines · Johannes Berg
Quoted 10 lines · Marco Porsch
Quoted 6 lines
Quoted 2 lines

For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry.

That seems questionable -- drivers are not required to request each beacon. I know you only want to make it work on ath9k, but I don't think "stretching" the API, without even documenting it, is a good idea.

Currently, we already use ieee80211_beacon_get_tim as time reference for mesh sync's adjust_tbtt. And, as far as I know, all mesh-capable drivers use the call for each and every beacon.

Oops, why did I miss that before? :-)

Quoted 7 lines
Quoted 2 lines

So what would you recommend: keep using beacon_get and adding documentation - or - creating an exported callback for awake_window_start?

I guess you could add it... However, I don't really fully understand. There's no guarantee that fetching the beacon is done anywhere close to TBTT? Or does ath9k happen to do it just after TXing a beacon? You're encoding quite a lot of ath9k-specific assumptions here it seems?

I think the assumption is currently correct for ath5k, ath9k, ath9k_htc, carl9170 and rt2800 (that's as far as I checked). All of these fetch a beacon on SWBA/PRETBTT interrupt (more or less) immediately before TBTT.

--Marco

maintainer
#7 lore raw

RFC v2 2/3 mac80211: mesh power save doze scheduling

Quoted 19 lines · Marco Porsch
Quoted 18 lines · Johannes Berg
Quoted 10 lines · Marco Porsch
Quoted 6 lines
Quoted 2 lines

For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry.

That seems questionable -- drivers are not required to request each beacon. I know you only want to make it work on ath9k, but I don't think "stretching" the API, without even documenting it, is a good idea.

Currently, we already use ieee80211_beacon_get_tim as time reference for mesh sync's adjust_tbtt. And, as far as I know, all mesh-capable drivers use the call for each and every beacon.

Oops, why did I miss that before? :-)

Quoted 2 lines

So what would you recommend: keep using beacon_get and adding documentation - or - creating an exported callback for awake_window_start?

Quoted 8 lines
Quoted 4 lines

I guess you could add it... However, I don't really fully understand. There's no guarantee that fetching the beacon is done anywhere close to TBTT? Or does ath9k happen to do it just after TXing a beacon? You're encoding quite a lot of ath9k-specific assumptions here it seems?

I think the assumption is currently correct for ath5k, ath9k, ath9k_htc, carl9170 and rt2800 (that's as far as I checked). All of these fetch a beacon on SWBA/PRETBTT interrupt (more or less) immediately before TBTT.

It's still pretty bad to make that assumption implicitly -- I think you should at least document it as a big todo item :)

johannes

#8 lore raw

RFC v2 3/3 ath9k: mesh powersave support

In reply to Marco Porsch

Register mesh PS ops on interface add and de-register on removal.

React to doze/wakeup calls issued by mac80211. Add a PS status flag PS_MAC80211_CTL to store last mesh PS command from mac80211.

On doze call configure the HW to wakeup on the given TSF value.

---
 drivers/net/wireless/ath/ath9k/ath9k.h  |    1 +
 drivers/net/wireless/ath/ath9k/beacon.c |   21 +++++++++-
 drivers/net/wireless/ath/ath9k/hw.c     |   20 ++++++---
 drivers/net/wireless/ath/ath9k/main.c   |   67 ++++++++++++++++++++++++++++++-
 4 files changed, 101 insertions(+), 8 deletions(-)
drivers/net/wireless/ath/ath9k/ath9k.h +1 -0
index b2d6c18..3dc83e5 100644
@@ -650,6 +650,7 @@ enum sc_op_flags {
 #define PS_WAIT_FOR_TX_ACK        BIT(3)
 #define PS_BEACON_SYNC            BIT(4)
 #define PS_WAIT_FOR_ANI           BIT(5)
+#define PS_MAC80211_CTL           BIT(6)
 
 struct ath_rate_table;
 
drivers/net/wireless/ath/ath9k/beacon.c +20 -1
index dd37719..7ef698b 100644
@@ -599,6 +599,23 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
 	ath9k_beacon_init(sc, nexttbtt, intval);
 }
 
+static void ath9k_beacon_config_mesh(struct ath_softc *sc,
+				     struct ath_beacon_config *conf)
+{
+	struct ath9k_beacon_state bs;
+
+	/*
+	 * when PS is enabled, ath9k_hw_setrxabort is set.
+	 * to wake up again to receive peers' beacons, we set an
+	 * arbitrary initial value for sleepduration here
+	 */
+	memset(&bs, 0, sizeof(bs));
+	bs.bs_sleepduration = IEEE80211_MS_TO_TU(100);
+	ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
+
+	ath9k_beacon_config_adhoc(sc, conf);
+}
+
 bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -707,9 +724,11 @@ void ath9k_set_beacon(struct ath_softc *sc)
 		ath9k_beacon_config_ap(sc, cur_conf);
 		break;
 	case NL80211_IFTYPE_ADHOC:
-	case NL80211_IFTYPE_MESH_POINT:
 		ath9k_beacon_config_adhoc(sc, cur_conf);
 		break;
+	case NL80211_IFTYPE_MESH_POINT:
+		ath9k_beacon_config_mesh(sc, cur_conf);
+		break;
 	case NL80211_IFTYPE_STATION:
 		ath9k_beacon_config_sta(sc, cur_conf);
 		break;
drivers/net/wireless/ath/ath9k/hw.c +15 -5
index 42cf3c7..f851678 100644
@@ -2254,16 +2254,24 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
 }
 EXPORT_SYMBOL(ath9k_hw_beaconinit);
 
+/**
+ * ath9k_hw_set_sta_beacon_timers
+ *
+ * in mesh mode overwriting AR_NEXT_TBTT_TIMER and setting AR_TBTT_TIMER_EN
+ * would shift the own TBTT
+ * TODO rename?
+ */
 void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
 				    const struct ath9k_beacon_state *bs)
 {
-	u32 nextTbtt, beaconintval, dtimperiod, beacontimeout;
+	u32 nextTbtt, beaconintval, dtimperiod, beacontimeout, ar_timer_mode;
 	struct ath9k_hw_capabilities *pCap = &ah->caps;
 	struct ath_common *common = ath9k_hw_common(ah);
 
 	ENABLE_REGWRITE_BUFFER(ah);
 
-	REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
+	if (ah->opmode != NL80211_IFTYPE_MESH_POINT)
+		REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
 
 	REG_WRITE(ah, AR_BEACON_PERIOD,
 		  TU_TO_USEC(bs->bs_intval));
@@ -2317,9 +2325,11 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
 
 	REGWRITE_BUFFER_FLUSH(ah);
 
-	REG_SET_BIT(ah, AR_TIMER_MODE,
-		    AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN |
-		    AR_DTIM_TIMER_EN);
+	ar_timer_mode = AR_DTIM_TIMER_EN | AR_TIM_TIMER_EN;
+	if (ah->opmode != NL80211_IFTYPE_MESH_POINT)
+		ar_timer_mode |= AR_TBTT_TIMER_EN;
+
+	REG_SET_BIT(ah, AR_TIMER_MODE, ar_timer_mode);
 
 	/* TSF Out of Range Threshold */
 	REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold);
drivers/net/wireless/ath/ath9k/main.c +65 -2

91 lines not on this page · raw message

Signature · 1 line
1.7.9.5