[ath9k-devel] [RFC 2/3] mac80211: mesh power save doze scheduling
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 - 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 mesh PS routines in mac80211 to keep driver modifications minimal. Track the beacon timing information of peers we are in PS mode towards. Set a per-STA hrtimer which will trigger a wakeup right before the peer's next TBTT. Also use the same hrtimer to go to sleep mode after not receiving a beacon in a defined time margin. In this case calculate the next TBTT and increase the margin. For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start a timer which triggers a hw_doze call on expiry. Signed-off-by: Marco Porsch --- include/net/mac80211.h | 34 + net/mac80211/ieee80211_i.h |9 +- net/mac80211/mesh.c| 17 +++ net/mac80211/mesh.h| 17 +++ net/mac80211/mesh_plink.c |1 + net/mac80211/mesh_ps.c | 357 net/mac80211/sta_info.c|4 + net/mac80211/sta_info.h| 13 ++ net/mac80211/tx.c |2 + 9 files changed, 453 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 23daed3..ca6979d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3952,6 +3952,40 @@ 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 driver for mesh PS + * + * This structure contains callbacks that the driver has to or may handle for + * mesh powersave. + * TODO Add further callbacks for HW that performs certain mesh PS tasks on its + * own (e.g. assign list of STA to track). + * + * @hw_doze: put the radio to doze state to conserve power + * @hw_wakeup: wake up the radio to receive frames again + */ +struct ieee80211_mps_ops { + void (*hw_doze)(struct ieee80211_hw *hw); + void (*hw_wakeup)(struct ieee80211_hw *hw); +}; + +#ifdef CONFIG_MAC80211_MESH +/** + * ieee80211_mps_init - register driver callbacks for mesh PS + * + * @hw: the hardware + * @ops: callbacks for this device + * + * called by driver on mesh interface add/remove + * TODO add HW capability flags + */ +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 */ /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e08b4c0..c08f423 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -629,6 +629,8 @@ struct ieee80211_if_mesh { int ps_peers_deep_sleep; struct ps_data ps; atomic_t num_mpsp; /* counts both owner and recipient independently */ + struct timer_list awake_window_end_timer; + bool in_awake_window; }; #ifdef CONFIG_MAC80211_MESH @@ -1126,7 +1128,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; @@ -1146,6 +1148,11 @@ struct ieee80211_local { int user_power_level; /* in dBm, for all interfaces */ + /* mesh power save */ + bool mps_enabled; + bool mps_hw_doze; + const struct ieee80211_mps_ops *mps_ops; + enum ieee80211_smps_mode smps_mode; struct work_struct restart_work; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8ce5d60..740d035 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -803,6 +803,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 && @@ -824,6 +825,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(local); + + /* in case both fired simultaneously, wakeup overrides doze */ + if (test_bit(MESH_WORK_PS_DOZE, &ifmsh->wrkq_flags) && +
[ath9k-devel] [RFC 0/3] 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 all 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. 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/main.c | 46 +++- include/net/mac80211.h | 34 +++ net/mac80211/ieee80211_i.h | 19 +- net/mac80211/mesh.c| 25 ++- net/mac80211/mesh.h| 22 +- net/mac80211/mesh_plink.c | 17 +- net/mac80211/mesh_ps.c | 357 net/mac80211/mesh_sync.c | 47 ++--- net/mac80211/sta_info.c|4 + net/mac80211/sta_info.h| 13 ++ net/mac80211/tx.c |2 + 12 files changed, 536 insertions(+), 51 deletions(-) -- 1.7.9.5 ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [RFC 3/3] ath9k: mesh powersave support
Register mesh PS ops on interface add and de-register on removal. When enabling mesh PS, do not enable the hardware's TIM timer interrupt. React to mac80211 doze/wakeup calls. Add a PS status flag PS_MAC80211_CTL to store last mesh PS command from mac80211. Signed-off-by: Marco Porsch --- drivers/net/wireless/ath/ath9k/ath9k.h |1 + drivers/net/wireless/ath/ath9k/main.c | 46 +--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 8250330..f11c210 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -651,6 +651,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_TX_ACKBIT(3) #define PS_BEACON_SYNCBIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define PS_MAC80211_CTL BIT(6) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0fb53d6..2929808 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -132,7 +132,8 @@ void ath9k_ps_restore(struct ath_softc *sc) PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK | -PS_WAIT_FOR_ANI))) { +PS_WAIT_FOR_ANI | +PS_MAC80211_CTL))) { mode = ATH9K_PM_NETWORK_SLEEP; if (ath9k_hw_btcoex_is_enabled(sc->sc_ah)) ath9k_btcoex_stop_gen_timer(sc); @@ -824,6 +825,38 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath_dbg(common, CONFIG, "Driver halt\n"); } +static void ath9k_mesh_doze(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + unsigned long flags; + + ath9k_ps_wakeup(sc); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + /* in mesh mode mac80211 checks beacons and CAB */ + sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_MAC80211_CTL); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + ath9k_ps_restore(sc); +} + +static void ath9k_mesh_wakeup(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + unsigned long flags; + + ath9k_ps_wakeup(sc); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags |= PS_MAC80211_CTL; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + ath9k_ps_restore(sc); +} + +static const struct ieee80211_mps_ops ath9k_mesh_ps_ops = { + .hw_doze = ath9k_mesh_doze, + .hw_wakeup = ath9k_mesh_wakeup, +}; + bool ath9k_uses_beacons(int type) { switch (type) { @@ -934,6 +967,11 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, ah->opmode = NL80211_IFTYPE_ADHOC; else ah->opmode = NL80211_IFTYPE_STATION; + + if (ah->opmode == NL80211_IFTYPE_MESH_POINT) + ieee80211_mps_init(hw, &ath9k_mesh_ps_ops); + else if (old_opmode == NL80211_IFTYPE_MESH_POINT) + ieee80211_mps_init(hw, NULL); } ath9k_hw_setopmode(ah); @@ -1037,7 +1075,9 @@ static void ath9k_enable_ps(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(ah); sc->ps_enabled = true; - if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP) && + sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) { + /* in mesh mode we trigger wakeups from mac80211 */ if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { ah->imask |= ATH9K_INT_TIM_TIMER; ath9k_hw_set_interrupts(ah); @@ -1178,7 +1218,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) * We just prepare to enable PS. We have to wait until our AP has * ACK'd our null data frame to disable RX otherwise we'll ignore * those ACKs and end up retransmitting the same null data frames. -* IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode. +* IEEE80211_CONF_CHANGE_PS is passed by mac80211 for STA or mesh mode. */ if (changed & IEEE80211_CONF_CHANGE_PS) { unsigned long flags; -- 1.7.9.5 ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [RFC 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. Signed-off-by: Marco Porsch --- 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(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d77d3f7..e08b4c0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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 */ }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e4cb9ae..8ce5d60 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -717,7 +717,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; @@ -753,13 +752,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, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index f38a114..fa1423e 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 57e7267..af6fbfd 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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_thres
Re: [ath9k-devel] [RFC 2/3] mac80211: mesh power save doze scheduling
On 01/31/2013 02:51 PM, Johannes Berg wrote: > On Wed, 2013-01-23 at 11:19 +0100, Marco Porsch wrote: > >> Expose a callback ieee80211_mps_init for drivers to register >> mesh powersave ops: >> - hw_doze - put the radio to sleep now >> - 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 mesh PS routines in mac80211 to keep driver >> modifications minimal. >> >> Track the beacon timing information of peers we are in PS mode >> towards. Set a per-STA hrtimer which will trigger a wakeup right >> before the peer's next TBTT. >> Also use the same hrtimer to go to sleep mode after not >> receiving a beacon in a defined time margin. In this case >> calculate the next TBTT and increase the margin. >> >> For mesh Awake Windows wakeup on SWBA (beacon_get_tim) and start >> a timer which triggers a hw_doze call on expiry. > > Hmm. I'm not completely happy with this hrtimer stuff in mac80211. > There's a lot of (USB) hardware that could never implement such a thing, > but could, conceivably, implement this differently? The use of hrtimers for MPS is debatable currently. The approach of calculating the peer's TSF should be accurate to the usec. Good timing here directly affects the energy savings. On the other handside the margin of 5ms used shows that something is not working as expected yet. If this cannot be fixed, I may as well use regular timers here. How strongly do you oppose hrtimers? :) >> @@ -1146,6 +1148,11 @@ struct ieee80211_local { >> >> int user_power_level; /* in dBm, for all interfaces */ >> >> +/* mesh power save */ >> +bool mps_enabled; >> +bool mps_hw_doze; > > Generally, this also seems wrong, you're making the assumption that mesh > will be the only interface. That might actually be true for many use > cases, but is it really an assumption we should still put into the stack > today, with multi-channel etc? I'm not convinced of that. When trying to enable MPS in mps_hw_conf_check, I check if there are any non-mesh interfaces. If yes, it is not enabled. So these status variables are just used to sync multi-mesh-vif MPS on a single device. Multiple mesh interfaces are ok. An AP vif is a no-go for any PS mode. Mesh + client is theoretically fine, but handled completely differently - also a no for now. > So I think you should (at least attempt to) make an implementation that > is less tied to the exact timing implementation. Maybe program the > wakeup TBTT in advance. Maybe with a small library the driver can > connect to this that uses hrtimers to implement it? That could then also > assume that callbacks need not sleep, thus allowing to reduce > TBTT_MARGIN. > > In theory I think with ath9k you could even use hardware timers & > interrupts to wake the hardware, thus probably being able to reduce > TBTT_MARGIN significantly. Earlier, I had successfully implemented wakeups using ath9k HW before we at cozybit decided to concentrate all code in mac80211. Concerning ath9k it worked fine but required more callbacks to mac80211 and will eventually add redundant code that has to be maintained to multiple drivers. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
Re: [ath9k-devel] [RFC 1/3] mac80211: move mesh sync beacon handler into neighbour_update
On 01/31/2013 02:43 PM, Johannes Berg wrote: > On Wed, 2013-01-23 at 11:19 +0100, Marco Porsch wrote: >> 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. >> >> Signed-off-by: Marco Porsch >> --- >> 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(-) >> >> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h >> index d77d3f7..e08b4c0 100644 >> --- a/net/mac80211/ieee80211_i.h >> +++ b/net/mac80211/ieee80211_i.h >> @@ -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); > > Is anyone actually planning to add more sync ops? I'm tempted to just > remove the entire abstraction here, since there's only a single concrete > implementation. No plans to do so as of now. But since mesh networks are still a research topic, there may already be researchers somewhere, happy to have that extensible interface. Also the standard explicitly defines an "Extensible synchronization framework" in 13.13.2. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [RFCv2 0/3] 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(-) -- 1.7.9.5 ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [RFCv2 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. Signed-off-by: Marco Porsch --- 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(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 650f758..bba2b10 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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 */ }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e39a3f8..643262b 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -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, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index eb33625..d6d9933 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 57e7267..af6fbfd 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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_thres
[ath9k-devel] [RFCv2 2/3] mac80211: mesh power save doze scheduling
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. Signed-off-by: Marco Porsch --- 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(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3037f49..e393a49 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -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 */ /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index bba2b10..cb54bae 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 643262b..b0deb14 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -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); + +
[ath9k-devel] [RFCv2 3/3] ath9k: mesh powersave support
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. Signed-off-by: Marco Porsch --- 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(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b2d6c18..3dc83e5 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -650,6 +650,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_TX_ACKBIT(3) #define PS_BEACON_SYNCBIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define PS_MAC80211_CTL BIT(6) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index dd37719..7ef698b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -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; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 42cf3c7..f851678 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -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); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 4b72b66..a4e6623 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -132,7 +132,8 @@ void ath9k_ps_restore(struct ath_softc *sc) PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK | -
Re: [ath9k-devel] [RFCv2 2/3] mac80211: mesh power save doze scheduling
On 02/08/2013 10:20 AM, Johannes Berg wrote: > On Wed, 2013-02-06 at 12:48 +0100, Marco Porsch wrote: > >> 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? >> +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. >> +/** >> + * 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) >> +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. > >> +/* 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); >> + >> +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 ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
Re: [ath9k-devel] [RFCv2 2/3] mac80211: mesh power save doze scheduling
On 02/08/2013 10:57 PM, Johannes Berg wrote: > On Fri, 2013-02-08 at 11:09 +0100, Marco Porsch wrote: > >>>> 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? :-) > >> 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 ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [PATCH 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 TSF and the just updated T_offset value. Rename the handler to better reflect its purpose. Signed-off-by: Marco Porsch --- changes since RFC: - rebased on todays mac80211-next/master net/mac80211/ieee80211_i.h | 10 - net/mac80211/mesh.c|7 +-- net/mac80211/mesh.h|5 +++-- net/mac80211/mesh_plink.c | 16 --- net/mac80211/mesh_sync.c | 48 5 files changed, 39 insertions(+), 47 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 388580a..62eeddf 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -529,11 +529,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 */ }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a77d40e..4b42237 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -932,7 +932,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; @@ -968,11 +967,7 @@ 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, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 1a1da87..3fd4657 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -286,8 +286,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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f7526e5..fe6fc22 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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) @@ -488,13 +489,19 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, * Initiates peering if appropriate. */ 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; u32 changed = 0; + u64 tsf; - sta = mesh_sta_info_get(sdata, hw_addr, elems); + /* get tsf before entering rcu-read section */ + tsf = drv_get_tsf(sdata->local, sdata); + + sta = mesh_sta_info_get(sdata, mgmt->sa, elems); if (!sta) goto out; @@ -505,6 +512,9 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, rssi_threshold_check(sta, sdata)) changed = mesh_plink_open(st
[ath9k-devel] [PATCH 2/3] mac80211: mesh power save doze scheduling
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. Stay awake to receive multicast traffic after DTIM beacons. 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. Signed-off-by: Marco Porsch --- changes since RFC: - commit logs - resume doze after multicast (CAB) receipt - timer_pending as check if beacons received once - check TIM pointer before use - TODO comment for using ieee80211_beacon_get_tim as time reference (Johannes) - spin_lock_bh instead of rcu_lock in ieee80211_mps_sta_tbtt_timeout include/net/mac80211.h | 29 net/mac80211/ieee80211_i.h |7 +- net/mac80211/mesh.c| 11 ++ net/mac80211/mesh.h| 13 ++ net/mac80211/mesh_plink.c |3 + net/mac80211/mesh_ps.c | 313 net/mac80211/sta_info.h| 10 ++ net/mac80211/tx.c |2 + 8 files changed, 387 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f7eba13..a27383f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2801,6 +2801,18 @@ enum ieee80211_tpt_led_trigger_flags { IEEE80211_TPT_LEDTRIG_FL_CONNECTED = BIT(2), }; +/** + * 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_LEDS extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); @@ -4058,6 +4070,23 @@ 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); +/** + * ieee80211_mps_init - register callbacks for mesh powersave mode + * + * @hw: the hardware + * @ops: callbacks for this device + * + * called by driver on mesh interface add/remove + */ +#ifdef CONFIG_MAC80211_MESH +void ieee80211_mps_init(struct ieee80211_hw *hw, + const struct ieee80211_mps_ops *ops); +#else +static inline void ieee80211_mps_init(struct ieee80211_hw *hw, + const struct ieee80211_mps_ops *ops) +{ return; } +#endif + /* Rate control API */ /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 62eeddf..fb55391 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -600,6 +600,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 @@ -1093,7 +1094,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; @@ -1112,6 +1113,10 @@ struct ieee80211_local { int user_power_level; /* in dBm, for all interfaces */ + /* mesh power save can be enabled for multiple (but only mesh) vif */ + bool mps_enabled; + const struct ieee80211_mps_ops *mps_ops; + enum ieee80211_smps_mode smps_mode; struct work_struct restart_work; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 4b42237..f4af64e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -168,6 +168,7 @@ void mesh_sta_cleanup(struct sta_info *sta) if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { changed |= mesh_plink_deactivate(sta); del_timer_sync(&sta->plink_timer); + del_timer_sync(&sta->nexttbtt_timer); }
[ath9k-devel] [PATCH 3/3] ath9k: mesh powersave support
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. Initialize HW beacon wakeup registers. On doze call configure the HW to wakeup on the given TSF value. Signed-off-by: Marco Porsch --- drivers/net/wireless/ath/ath9k/ath9k.h |1 + drivers/net/wireless/ath/ath9k/beacon.c | 21 ++- drivers/net/wireless/ath/ath9k/hw.c | 17 +++-- drivers/net/wireless/ath/ath9k/main.c | 63 ++- 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 97c90b2..b82727b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -652,6 +652,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_TX_ACKBIT(3) #define PS_BEACON_SYNCBIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define PS_MAC80211_CTL BIT(6) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index dd37719..7ef698b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -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; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 42cf3c7..0e0fe03 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -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 + */ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + u32 ar_timer_mode = AR_DTIM_TIMER_EN | AR_TIM_TIMER_EN; 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,10 @@ 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); + 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); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5432f12..93c66cb 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -132,7 +132,8 @@ void ath9k_ps_restore(struct ath_softc *sc) PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK | -PS_WAIT_FOR_ANI))) { +
Re: [ath9k-devel] [PATCH 1/3] mac80211: move mesh sync beacon handler into neighbour_update
Hi, On 02/15/2013 01:42 PM, Bob Copeland wrote: > On Fri, Feb 15, 2013 at 07:40:46AM -0500, Bob Copeland wrote: >>> I don't think you should drop this comment; also why not just address >>> it? There's a timestamp in rx_status that should be the correct one for >>> ath9k (which is pretty much all this seems to work on anyway :) ) >> >> The comment is just moved? Mesh already uses rx_status->mactime, if >> the driver supplies it, which the comment says :) > > Oh I see - you are saying not to drop the comment about rcu locking. > diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c > index f7526e5..fe6fc22 100644 > --- a/net/mac80211/mesh_plink.c > +++ b/net/mac80211/mesh_plink.c > @@ -488,13 +489,19 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, >* Initiates peering if appropriate. >*/ > 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; > u32 changed = 0; > +u64 tsf; > > -sta = mesh_sta_info_get(sdata, hw_addr, elems); > +/* get tsf before entering rcu-read section */ The comment about RCU lock moved here. > +tsf = drv_get_tsf(sdata->local, sdata); > + > +sta = mesh_sta_info_get(sdata, mgmt->sa, elems); > if (!sta) > goto out; > --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
Re: [ath9k-devel] [PATCH 1/3] mac80211: move mesh sync beacon handler into neighbour_update
Hi, On 02/15/2013 01:46 PM, Johannes Berg wrote: > On Fri, 2013-02-15 at 12:40 +, ma...@cozybit.com wrote: >> Please check again. The comment is split in two and placed on the respective >> new positions. > > Yeah, I see, the API is just total shit. First passing the TSF and then > calculating it to override? Why not do the calculation outside the API > always? The TBTT calculation does intentionally not use the mactime value. Synchronization uses the time in local TSF units and the exact same time point in peers TSF units at the time of sending/receiving (mactime field). The TBTT calculation uses the time of NOW, i.e. the current TSF after possible delays in firmware/driver/rx-handler which may have outdated the mactime field. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
Re: [ath9k-devel] [PATCH 1/3] mac80211: move mesh sync beacon handler into neighbour_update
On 02/15/2013 02:37 PM, Johannes Berg wrote: > On Fri, 2013-02-15 at 14:31 +0100, Marco Porsch wrote: >> Hi, >> >> On 02/15/2013 01:46 PM, Johannes Berg wrote: >>> On Fri, 2013-02-15 at 12:40 +, ma...@cozybit.com wrote: >>>> Please check again. The comment is split in two and placed on the >>>> respective new positions. >>> >>> Yeah, I see, the API is just total shit. First passing the TSF and then >>> calculating it to override? Why not do the calculation outside the API >>> always? >> >> The TBTT calculation does intentionally not use the mactime value. >> >> Synchronization uses the time in local TSF units and the exact same time >> point in peers TSF units at the time of sending/receiving (mactime field). >> The TBTT calculation uses the time of NOW, i.e. the current TSF after >> possible delays in firmware/driver/rx-handler which may have outdated >> the mactime field. > > I'm talking about this API: > > mesh_neighbour_update: > ... > tsf = drv_get_tsf() > ... > sync_ops->rx_bcn(..., tsf) > > > mesh_sync_offset_rx_bcn(..., t_r): > ... > if (have_better_timestamp) > t_r = get_better_timestamp() > > > You can hardly claim that's an intuitive API. Hm, alright. Just saying that ieee80211_mps_sta_tbtt_update still uses the unchanged TSF value. But hey :) What would be more favourable then? a) second variable in mesh_sync_offset_rx_bcn mesh_neighbour_update: ... tsf = drv_get_tsf() ... sync_ops->rx_bcn(..., tsf) ieee80211_mps_sta_tbtt_update(..., tsf); mesh_sync_offset_rx_bcn(..., tsf): ... if (have_better_timestamp) t_r = get_better_timestamp() else t_r = tsf; b) second variable in mesh_neighbour_update mesh_neighbour_update: ... tsf = drv_get_tsf() if (have_better_timestamp) t_r = get_better_timestamp() else t_r = tsf; ... sync_ops->rx_bcn(..., t_r) ieee80211_mps_sta_tbtt_update(..., tsf); --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
Re: [ath9k-devel] [PATCH 1/3] mac80211: move mesh sync beacon handler into neighbour_update
Hi, On 02/15/2013 02:58 PM, Johannes Berg wrote: > On Fri, 2013-02-15 at 14:48 +0100, Marco Porsch wrote: > >>> I'm talking about this API: >>> >>> mesh_neighbour_update: >>> ... >>> tsf = drv_get_tsf() >>> ... >>> sync_ops->rx_bcn(..., tsf) >>> >>> >>> mesh_sync_offset_rx_bcn(..., t_r): >>> ... >>> if (have_better_timestamp) >>> t_r = get_better_timestamp() >>> >>> >>> You can hardly claim that's an intuitive API. >> >> Hm, alright. Just saying that ieee80211_mps_sta_tbtt_update still uses >> the unchanged TSF value. But hey :) > > Well, that function doesn't exist in this patch... > >> What would be more favourable then? > > I guess you can tell I'm not in a good mood today. I think any use of > get_tsf() for operation is a complete waste of time, there's no way you > can get the timings correct. You could be preempted, and suddenly sleep > for a few tens or hundreds milliseconds, so none of this makes any > sense... To properly do it you have to do calculations in relative times > and let the device apply them. I don't get your last sentence here. Maybe you can elaborate? Concerning timestamp vs. TSF usage; wow - I tested it when using the timestamp value for TBTT scheduling. Works fine. Works even better than TSF as it slightly reduces the measured wakeup overhead. Hm, I was sure the TSF as in *now* would be more appropriate than the TSF at receipt time... But either way, if it works better and is less ugly, it is a win-win =) I'll send an updated patch with the more intuitive API. ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [PATCHv2 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 T_r and the updated T_offset value. Rename the handler to better reflect its purpose. Signed-off-by: Marco Porsch --- v2: calculate T_r outside mesh sync handler (Johannes) remove unused variables net/mac80211/ieee80211_i.h |9 - net/mac80211/mesh.c|7 +-- net/mac80211/mesh.h|4 +++- net/mac80211/mesh_plink.c | 31 +- net/mac80211/mesh_sync.c | 45 ++-- 5 files changed, 44 insertions(+), 52 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 388580a..631760f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -529,11 +529,10 @@ 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, + u64 t_r); void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); /* add other framework functions here */ }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 29ce2aa..94682d2 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -938,7 +938,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; @@ -974,11 +973,7 @@ 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, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 336c88a..ac02f38 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -283,7 +283,9 @@ 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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 07d396d..d40fedd 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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) @@ -497,23 +498,43 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, * Initiates peering if appropriate. */ 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_local *local = sdata->local; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct sta_info *sta; u32 changed = 0; + u64 t_r; + + /* +* If available, calculate the time the beacon timestamp field was +* received from the rx_status->mactime field. Otherwise get the +* current TSF as approximation before entering rcu-read section. +*/ + if (ieee80211_have_rx_timestamp(rx_status)) + t_r = ieee80211_calculate_rx_timestamp(local, rx_status, + 24 + 12 + +
[ath9k-devel] [PATCHv2 2/3] mac80211: mesh power save doze scheduling
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. Stay awake to receive multicast traffic after DTIM beacons. 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. Signed-off-by: Marco Porsch --- include/net/mac80211.h | 29 net/mac80211/ieee80211_i.h |7 +- net/mac80211/mesh.c| 11 ++ net/mac80211/mesh.h| 13 ++ net/mac80211/mesh_plink.c |3 + net/mac80211/mesh_ps.c | 313 net/mac80211/sta_info.h| 10 ++ net/mac80211/tx.c |2 + 8 files changed, 387 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f7eba13..a27383f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2801,6 +2801,18 @@ enum ieee80211_tpt_led_trigger_flags { IEEE80211_TPT_LEDTRIG_FL_CONNECTED = BIT(2), }; +/** + * 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_LEDS extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); @@ -4058,6 +4070,23 @@ 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); +/** + * ieee80211_mps_init - register callbacks for mesh powersave mode + * + * @hw: the hardware + * @ops: callbacks for this device + * + * called by driver on mesh interface add/remove + */ +#ifdef CONFIG_MAC80211_MESH +void ieee80211_mps_init(struct ieee80211_hw *hw, + const struct ieee80211_mps_ops *ops); +#else +static inline void ieee80211_mps_init(struct ieee80211_hw *hw, + const struct ieee80211_mps_ops *ops) +{ return; } +#endif + /* Rate control API */ /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 631760f..c42a506 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -599,6 +599,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 @@ -1092,7 +1093,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; @@ -,6 +1112,10 @@ struct ieee80211_local { int user_power_level; /* in dBm, for all interfaces */ + /* mesh power save can be enabled for multiple (but only mesh) vif */ + bool mps_enabled; + const struct ieee80211_mps_ops *mps_ops; + enum ieee80211_smps_mode smps_mode; struct work_struct restart_work; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 94682d2..61cfbe0 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -168,6 +168,7 @@ void mesh_sta_cleanup(struct sta_info *sta) if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { changed |= mesh_plink_deactivate(sta); del_timer_sync(&sta->plink_timer); + del_timer_sync(&sta->nexttbtt_timer); } if (changed) @@ -1026,6 +1027,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;
[ath9k-devel] [PATCHv2 3/3] ath9k: mesh powersave support
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. Initialize HW beacon wakeup registers. On doze call configure the HW to wakeup on the given TSF value. Signed-off-by: Marco Porsch --- drivers/net/wireless/ath/ath9k/ath9k.h |1 + drivers/net/wireless/ath/ath9k/beacon.c | 21 ++- drivers/net/wireless/ath/ath9k/hw.c | 17 +++-- drivers/net/wireless/ath/ath9k/main.c | 63 ++- 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 97c90b2..b82727b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -652,6 +652,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_TX_ACKBIT(3) #define PS_BEACON_SYNCBIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define PS_MAC80211_CTL BIT(6) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index dd37719..7ef698b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -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; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 42cf3c7..0e0fe03 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -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 + */ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + u32 ar_timer_mode = AR_DTIM_TIMER_EN | AR_TIM_TIMER_EN; 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,10 @@ 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); + 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); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5432f12..93c66cb 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -132,7 +132,8 @@ void ath9k_ps_restore(struct ath_softc *sc) PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK | -PS_WAIT_FOR_ANI))) { +
Re: [ath9k-devel] [PATCHv2 2/3] mac80211: mesh power save doze scheduling
Hi Johannes, On 02/20/2013 04:01 PM, Johannes Berg wrote: > On Mon, 2013-02-18 at 17:08 +0100, Marco Porsch wrote: > >> +/** >> + * ieee80211_mps_init - register callbacks for mesh powersave mode >> + * >> + * @hw: the hardware >> + * @ops: callbacks for this device >> + * >> + * called by driver on mesh interface add/remove >> + */ >> +#ifdef CONFIG_MAC80211_MESH >> +void ieee80211_mps_init(struct ieee80211_hw *hw, >> +const struct ieee80211_mps_ops *ops); >> +#else >> +static inline void ieee80211_mps_init(struct ieee80211_hw *hw, >> + const struct ieee80211_mps_ops *ops) >> +{ return; } >> +#endif > > The "return" there is spurious. Is it really worth providing a static > inline? It seems drivers might want to #ifdef it anyway so they don't > have carry around the ops struct and the called functions if mesh isn't > compiled in. Ok, that seems reasonable. And that one function would be gone if I just use additional ieee80211_ops (see below). >> +static bool mps_doze_check_sta(struct ieee80211_local *local, u64 *nexttbtt) >> +{ >> + struct sta_info *sta; >> + bool allow = true; >> + u64 nexttbtt_min = ULLONG_MAX; >> + >> + mutex_lock(&local->sta_mtx); >> + list_for_each_entry(sta, &local->sta_list, list) { >> + if (!ieee80211_vif_is_mesh(&sta->sdata->vif) || >> + !ieee80211_sdata_running(sta->sdata) || >> + sta->plink_state != NL80211_PLINK_ESTAB) { >> + continue; > > This is strange, why bother with the else if there's a continue? I don't quite get this comment. The current logic is like this: if (unrelated cases) { continue; } else if (related and blocking) { allow = false; break; } else if (related, non-blocking and new minimum) { min = sta->nexttbtt; } >> + } else if (test_sta_flag(sta, WLAN_STA_MPS_WAIT_FOR_CAB) || >> + test_sta_flag(sta, WLAN_STA_MPSP_OWNER) || >> + test_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT) || >> + !timer_pending(&sta->nexttbtt_timer) || >> + time_after(jiffies, sta->nexttbtt_jiffies)) { > > Are you sure jiffies are good enough? Some systems have HZ=33 or so I > think, which makes a jiffy like 30ms. Hm, jiffies is what I have available easily. Using the TSF would be obvious but may suffer from delay when obtaining it. Umm... hrtimers again? >> + allow = false; >> + break; >> + } else if (sta->nexttbtt_tsf < nexttbtt_min) { >> + nexttbtt_min = sta->nexttbtt_tsf; >> + } > > ditto, why bother with else after break? > >> + if (nexttbtt_min != ULLONG_MAX) >> + *nexttbtt = nexttbtt_min; > > The API of this function is very strange. Sometimes it might set it, > sometimes it might leave it, but that's not even consistent with the > "allow" return value ... It seems it'd be better to always set it. Alright. Will just have to make sure to hand out zero as invalid value, not ULLONG_MAX. >> +/** >> + * ieee80211_mps_doze - trigger radio doze state after checking conditions >> + * >> + * @local: local interface data > > "interface"? hardly. * @local: mac80211 hw info struct >> +void ieee80211_mps_doze(struct ieee80211_local *local) >> +{ >> +u64 nexttbtt = 0; > > and get rid of the assignment here. > >> + >> +void ieee80211_mps_init(struct ieee80211_hw *hw, >> +const struct ieee80211_mps_ops *ops) >> +{ >> +struct ieee80211_local *local = hw_to_local(hw); >> + >> +if (!ops) >> +local->mps_enabled = false; > > Allowing that seems pointless ... in fact, why is there this assignment > function anyway? It seems these are pretty normal, if #ifdef MESH, > driver callbacks? So may I just add them to the ieee80211_ops under #ifdef? That would certainly make things easier. I would add a HW capability flag for MPS doze as well. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [PATCHv3 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 T_r and the updated T_offset value. Check frame type before calling the handler. Rename it to better reflect its purpose. Signed-off-by: Marco Porsch --- v3: check frame type before rx_bcn handler (Johannes) net/mac80211/ieee80211_i.h |9 - net/mac80211/mesh.c|7 +-- net/mac80211/mesh.h|4 +++- net/mac80211/mesh_plink.c | 38 --- net/mac80211/mesh_sync.c | 47 +--- 5 files changed, 48 insertions(+), 57 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 388580a..631760f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -529,11 +529,10 @@ 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, + u64 t_r); void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); /* add other framework functions here */ }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 29ce2aa..94682d2 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -938,7 +938,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; @@ -974,11 +973,7 @@ 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, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 336c88a..ac02f38 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -283,7 +283,9 @@ 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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 07d396d..e34cfd1 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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) @@ -492,29 +493,52 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, * * @sdata: local meshif * @addr: peer's address - * @elems: IEs from beacon or mesh peering frame + * @elems: IEs from beacon or mesh probe response * * Initiates peering if appropriate. */ 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_local *local = sdata->local; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct sta_info *sta; u32 changed = 0; + u64 t_r = 0; - sta = mesh_sta_info_get(sdata, hw_addr, elems); + /* +* If available, calculate the time the beacon timestamp field was +* received from the rx_status->mactime field. Otherwise get the +* current TSF as approximation before entering rcu-read section. +*/ +
[ath9k-devel] [PATCHv3 3/3] ath9k: mesh powersave support
Add ath9k_ops for .mesh_ps_doze and .mesh_ps_wakeup. React to doze/wakeup calls issued by mac80211. Add a PS status flag PS_MAC80211_CTL to store last mesh PS command from mac80211. Initialize HW beacon wakeup registers. On doze call configure the device to wakeup at the given TSF value. Signed-off-by: Marco Porsch --- v3: use ieee80211_ops (Johannes) use #ifdef around optional API callbacks (Johannes) drivers/net/wireless/ath/ath9k/ath9k.h |1 + drivers/net/wireless/ath/ath9k/beacon.c | 21 ++- drivers/net/wireless/ath/ath9k/hw.c | 17 +++-- drivers/net/wireless/ath/ath9k/main.c | 62 ++- 4 files changed, 94 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 97c90b2..b82727b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -652,6 +652,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_TX_ACKBIT(3) #define PS_BEACON_SYNCBIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define PS_MAC80211_CTL BIT(6) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index dd37719..7ef698b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -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; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 42cf3c7..0e0fe03 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -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 + */ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + u32 ar_timer_mode = AR_DTIM_TIMER_EN | AR_TIM_TIMER_EN; 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,10 @@ 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); + 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); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5432f12..16b7e5d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -132,7 +132,8 @@ void ath9k_ps_restore(struct ath_softc *sc) PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA |
[ath9k-devel] [PATCHv3 2/3] mac80211: mesh power save doze scheduling
Configure the device for PS mode if the local mesh PS parameters allow so and the driver supports it. Add two callbacks to ieee80211_ops for mesh powersave: - mesh_ps_doze - put the device to sleep now, wake up at given TBTT - mesh_ps_wakeup - wake the device up now for frame RX These ops may be extended in the future to allow drivers/HW to implement mesh PS themselves. (Current design goal was to concentrate most mesh PS routines in mac80211 to keep driver modifications minimal.) Track beacon timing information of peers we are in PS mode towards. Calculate the next TBTT per STA. Stay awake to receive multicast traffic after DTIM beacons. When going to doze state, get the most imminent STA TBTT and configure the device to trigger a wakeup on time to catch that beacon. After successful receipt put the device 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. Signed-off-by: Marco Porsch --- v3: use of ieee80211_tu_to_usec (Christian) use ieee80211_ops for mesh PS (Johannes) add tracing routines make mps_doze_check_sta always return nextbtt (Johannes) fixed comments for ieee80211_local (Johannes) include/net/mac80211.h | 10 ++ net/mac80211/driver-ops.h | 24 net/mac80211/ieee80211_i.h |6 +- net/mac80211/mesh.c| 11 ++ net/mac80211/mesh.h| 13 ++ net/mac80211/mesh_plink.c |3 + net/mac80211/mesh_ps.c | 298 net/mac80211/sta_info.h| 10 ++ net/mac80211/trace.h | 30 + net/mac80211/tx.c |2 + 10 files changed, 406 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f7eba13..47bad0d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2557,6 +2557,11 @@ enum ieee80211_rate_control_changed { * @ipv6_addr_change: IPv6 address assignment on the given interface changed. * Currently, this is only called for managed or P2P client interfaces. * This callback is optional; it must not sleep. + * + * @mesh_ps_doze: Put the device to doze state now; schedule wakeup at given + * TSF value (if non-zero). This callback is optional and may sleep. + * @mesh_ps_wakeup: Wake the device up now. This callback is optional and may + * sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2745,6 +2750,11 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct inet6_dev *idev); #endif + +#ifdef CONFIG_MAC80211_MESH + void (*mesh_ps_doze)(struct ieee80211_hw *hw, u64 nexttbtt); + void (*mesh_ps_wakeup)(struct ieee80211_hw *hw); +#endif }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ee56d07..bbf3d24 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1090,4 +1090,28 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local, } #endif +#ifdef CONFIG_MAC80211_MESH + +static inline void drv_mesh_ps_doze(struct ieee80211_local *local, u64 nexttbtt) +{ + might_sleep(); + + trace_drv_mesh_ps_doze(local, nexttbtt); + if (local->ops->mesh_ps_doze) + local->ops->mesh_ps_doze(&local->hw, nexttbtt); + trace_drv_return_void(local); +} + +static inline void drv_mesh_ps_wakeup(struct ieee80211_local *local) +{ + might_sleep(); + + trace_drv_mesh_ps_wakeup(local); + if (local->ops->mesh_ps_wakeup) + local->ops->mesh_ps_wakeup(&local->hw); + trace_drv_return_void(local); +} + +#endif + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 631760f..dbb4bea 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -599,6 +599,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 @@ -1092,7 +1093,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; @@ -,6 +1112,9 @@ struct ieee80211_local { int user_power_level; /* in dBm, for all interfaces */ + /* mesh power save can be enabled for multiple (but only mesh) vif */ + bool mps_enabled; + enum ieee80211_smps_mode smps_mode; struct work_struct restart_work;
Re: [ath9k-devel] [PATCHv3 1/3] mac80211: move mesh sync beacon handler into neighbour_update
Am 26.02.2013 22:31, schrieb Thomas Pedersen: > Hi Marco, > > On Mon, Feb 25, 2013 at 8:00 AM, Marco Porsch wrote: >> 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 T_r and >> the updated T_offset value. >> >> Check frame type before calling the handler. Rename it to better >> reflect its purpose. >> >> Signed-off-by: Marco Porsch >> --- >> >> v3: >> check frame type before rx_bcn handler (Johannes) >> >> net/mac80211/ieee80211_i.h |9 - >> net/mac80211/mesh.c|7 +-- >> net/mac80211/mesh.h|4 +++- >> net/mac80211/mesh_plink.c | 38 --- >> net/mac80211/mesh_sync.c | 47 >> +--- >> 5 files changed, 48 insertions(+), 57 deletions(-) >> >> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h >> index 388580a..631760f 100644 >> --- a/net/mac80211/ieee80211_i.h >> +++ b/net/mac80211/ieee80211_i.h >> @@ -529,11 +529,10 @@ 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, >> + u64 t_r); > > Why not continue to sync against probe responses? > mesh_neighbour_update() gets these as well. Because there was a comment about the standard mentioning only beacon frames. Fooled me here =/ Now I see that the standard explicitly mentions probe responses as well. Is the sync code currently ready to handle probe responses? I am not perfectly sure about the sta->t_offset_setpoint stuff. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
[ath9k-devel] [PATCHv4 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 T_r and the updated T_offset value. Allow calculating T_offset from probe response frames as mandated in IEEE 802.11-2012. Signed-off-by: Marco Porsch --- v4: calculate T_offset from probe response frames (Thomas) net/mac80211/ieee80211_i.h |5 ++--- net/mac80211/mesh.c|7 +-- net/mac80211/mesh.h|4 +++- net/mac80211/mesh_plink.c | 30 +++-- net/mac80211/mesh_sync.c | 45 5 files changed, 38 insertions(+), 53 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 54d09ec..5284ffa 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -529,11 +529,10 @@ 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, + void (*rx_bcn_presp)(struct sta_info *sta, struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, -struct ieee80211_rx_status *rx_status); +u64 t_r); void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); /* add other framework functions here */ }; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index f5d1afa..bfc5ee6 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -885,7 +885,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; @@ -921,11 +920,7 @@ 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, diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 6ffabbe..594c43a 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -283,7 +283,9 @@ 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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 08df966..91dea29 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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) @@ -492,28 +493,45 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, * * @sdata: local meshif * @addr: peer's address - * @elems: IEs from beacon or mesh peering frame + * @elems: IEs from beacon or mesh probe response * * Initiates peering if appropriate. */ 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_local *local = sdata->local; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct sta_info *sta; u32 changed = 0; + u64 t_r; + + /* +* If available, calculate the time the beacon timestamp field was +* received from the rx_status->mactime field. Otherwise get the +* current TSF as approximation before entering rcu-read section. +*/ + if (ieee80211_have_rx_timestamp(rx_status)) + t_r = ieee80211_calculate_rx_timestamp(local, rx_status, + 24 + 1
[ath9k-devel] [PATCHv4 3/3] ath9k: mesh powersave support
Add ath9k_ops for .mesh_ps_doze and .mesh_ps_wakeup. React to doze/wakeup calls issued by mac80211. Add a PS status flag PS_MAC80211_CTL to store last mesh PS command from mac80211. Initialize HW beacon wakeup registers. On doze call configure the device to wakeup at the given TSF value. Signed-off-by: Marco Porsch --- drivers/net/wireless/ath/ath9k/ath9k.h |1 + drivers/net/wireless/ath/ath9k/beacon.c | 21 ++- drivers/net/wireless/ath/ath9k/hw.c | 17 +++-- drivers/net/wireless/ath/ath9k/main.c | 62 ++- 4 files changed, 94 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 97c90b2..b82727b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -652,6 +652,7 @@ enum sc_op_flags { #define PS_WAIT_FOR_TX_ACKBIT(3) #define PS_BEACON_SYNCBIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define PS_MAC80211_CTL BIT(6) struct ath_rate_table; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index dd37719..7ef698b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -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; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 42cf3c7..0e0fe03 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -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 + */ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + u32 ar_timer_mode = AR_DTIM_TIMER_EN | AR_TIM_TIMER_EN; 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,10 @@ 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); + 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); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5432f12..16b7e5d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -132,7 +132,8 @@ void ath9k_ps_restore(struct ath_softc *sc) PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK | -PS_WAIT_FOR_ANI))) { +
[ath9k-devel] [PATCHv4 2/3] mac80211: mesh power save doze scheduling
Configure the device for PS mode if the local mesh PS parameters allow so and the driver supports it. Add two callbacks to ieee80211_ops for mesh powersave: - mesh_ps_doze - put the device to sleep now, wake up at given TBTT - mesh_ps_wakeup - wake the device up now for frame RX These ops may be extended in the future to allow drivers/HW to implement mesh PS themselves. (Current design goal was to concentrate most mesh PS routines in mac80211 to keep driver modifications minimal.) Track beacon timing information of peers we are in PS mode towards. Calculate the next TBTT per STA. Stay awake to receive multicast traffic after DTIM beacons. When going to doze state, get the most imminent STA TBTT and configure the device to trigger a wakeup on time to catch that beacon. After successful receipt put the device 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 PreTBTT/SWBA (beacon_get_tim) and start a timer which triggers a doze call on expiry. Similarly, stay awake for the Awake Window duration after sending probe response frames. Signed-off-by: Marco Porsch --- v4: Awake Window after sending probe responses include/net/mac80211.h | 10 ++ net/mac80211/driver-ops.h | 24 net/mac80211/ieee80211_i.h |6 +- net/mac80211/mesh.c| 12 ++ net/mac80211/mesh.h| 13 ++ net/mac80211/mesh_plink.c |5 + net/mac80211/mesh_ps.c | 298 net/mac80211/sta_info.h| 10 ++ net/mac80211/trace.h | 30 + net/mac80211/tx.c |2 + 10 files changed, 409 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cdd7cea..61f7db6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2571,6 +2571,11 @@ enum ieee80211_roc_type { * @ipv6_addr_change: IPv6 address assignment on the given interface changed. * Currently, this is only called for managed or P2P client interfaces. * This callback is optional; it must not sleep. + * + * @mesh_ps_doze: Put the device to doze state now; schedule wakeup at given + * TSF value (if non-zero). This callback is optional and may sleep. + * @mesh_ps_wakeup: Wake the device up now. This callback is optional and may + * sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2760,6 +2765,11 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct inet6_dev *idev); #endif + +#ifdef CONFIG_MAC80211_MESH + void (*mesh_ps_doze)(struct ieee80211_hw *hw, u64 nexttbtt); + void (*mesh_ps_wakeup)(struct ieee80211_hw *hw); +#endif }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 832acea..accb0ae 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1091,4 +1091,28 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local, } #endif +#ifdef CONFIG_MAC80211_MESH + +static inline void drv_mesh_ps_doze(struct ieee80211_local *local, u64 nexttbtt) +{ + might_sleep(); + + trace_drv_mesh_ps_doze(local, nexttbtt); + if (local->ops->mesh_ps_doze) + local->ops->mesh_ps_doze(&local->hw, nexttbtt); + trace_drv_return_void(local); +} + +static inline void drv_mesh_ps_wakeup(struct ieee80211_local *local) +{ + might_sleep(); + + trace_drv_mesh_ps_wakeup(local); + if (local->ops->mesh_ps_wakeup) + local->ops->mesh_ps_wakeup(&local->hw); + trace_drv_return_void(local); +} + +#endif + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5284ffa..4565b8c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -597,6 +597,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 @@ -1088,7 +1089,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; @@ -1107,6 +1108,9 @@ struct ieee80211_local { int user_power_level; /* in dBm, for all interfaces */ + /* mesh power save can be enabled for multiple (but only mesh) vif */ + bool mps_enabled; + enum ieee80211_smps_mode smps_mode; struct work_struct restart_work; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index bfc5ee6..bdd6208 100644 --- a/net/mac80211/mesh
Re: [ath9k-devel] [PATCHv4 2/3] mac80211: mesh power save doze scheduling
Am 04.03.2013 20:23, schrieb Seth Forshee: > On Mon, Mar 04, 2013 at 06:54:34PM +0100, Marco Porsch wrote: >> Configure the device for PS mode if the local mesh PS parameters >> allow so and the driver supports it. >> >> Add two callbacks to ieee80211_ops for mesh powersave: >> - mesh_ps_doze - put the device to sleep now, wake up at given >>TBTT >> - mesh_ps_wakeup - wake the device up now for frame RX >> These ops may be extended in the future to allow drivers/HW to >> implement mesh PS themselves. (Current design goal was to >> concentrate most mesh PS routines in mac80211 to keep driver >> modifications minimal.) >> >> Track beacon timing information of peers we are in PS mode >> towards. Calculate the next TBTT per STA. Stay awake to receive >> multicast traffic after DTIM beacons. >> >> When going to doze state, get the most imminent STA TBTT and >> configure the device to trigger a wakeup on time to catch that >> beacon. After successful receipt put the device 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 PreTBTT/SWBA (beacon_get_tim) >> and start a timer which triggers a doze call on expiry. >> Similarly, stay awake for the Awake Window duration after >> sending probe response frames. >> >> Signed-off-by: Marco Porsch > > I've been looking at power save in mac80211 over the past few days with > an eye towards allowing multiple interface to be supported, as a result > of comments Johannes made at [1]. It seems like adding driver callbacks > for PS which are specific to the interface type is contrary to this > goal. > > The basic idea that's been forming on my mind is add PS states to vifs > and make the managed, mesh, etc. code manipulate vif PS states rather > than hw states. Then a PS module would manage the hw state based on the > aggregate of the vif states. > > I don't have a lot of the details worked out yet, and my knowledge of PS > in mesh networks (and of mesh network operation in general) is pretty > rudimentary at this point. But afaict any modes which support PS define > the same two hw states, awake and doze. I wonder whether we should > instead aim for a single interface into the driver for PS that's capable > of supporting all interface types. > > Anyway, I just wanted to throw this out for discussion. > > Thanks, > Seth > > [1] http://article.gmane.org/gmane.linux.kernel.wireless.general/104064 > Please mind that these callbacks are not necessarily bound to mesh mode (although the name suggests so) and may as well be incorporated into a more general interface that also suits managed and ad-hoc mode later. It is just that currently only mesh uses this and I lack the overview of mode and driver combinations to create a one-fits-all solution. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel
Re: [ath9k-devel] [PATCHv4 2/3] mac80211: mesh power save doze scheduling
Am 04.03.2013 21:25, schrieb Johannes Berg: > On Mon, 2013-03-04 at 13:23 -0600, Seth Forshee wrote: > >> I've been looking at power save in mac80211 over the past few days with >> an eye towards allowing multiple interface to be supported, as a result >> of comments Johannes made at [1]. It seems like adding driver callbacks >> for PS which are specific to the interface type is contrary to this >> goal. > > Yeah, this is a concern. I didn't really stand in the way of doing mesh > powersave though, and it seemed that the new interface here would > actually be somewhat suitable, since for mesh any kind of powersave code > needs to know when to wake up/sleep. I would've liked to see less > reliance on host timers directly in core mac80211 even for the going to > sleep part though... basically I'm not sure for mesh just having PS > state will cut it. For managed mode this is easy because it only needs > to sync with a single AP, but for mesh that's a bit more complicated and > I think the whole sync should stay in mac80211. In the general case, > just having the TSF might not be enough, but that can be solved as > needed. > >> The basic idea that's been forming on my mind is add PS states to vifs >> and make the managed, mesh, etc. code manipulate vif PS states rather >> than hw states. Then a PS module would manage the hw state based on the >> aggregate of the vif states. > > Yeah, that about matches what I was thinking. But like I said above, > while this is fairly simple for managed mode, at least as required > today, it's clearly not as simple for mesh. > > For managed mode, we also assume that we can send packets while the > device is sleeping, and the device will do the right thing to wake up > for beacons from the AP etc. For mesh, there are many more wakeup > sources, from what I can tell. I also assume that the device deals with TX while sleeping. The complete list of wakeup sources for mesh are: - awake window after beacon and probe response TX - authentication, peering, scanning procedures - frame release in mesh peer service periods - RX peer's beacons and DTIM multicasts (only in mesh light sleep mode) >> I don't have a lot of the details worked out yet, and my knowledge of PS >> in mesh networks (and of mesh network operation in general) is pretty >> rudimentary at this point. But afaict any modes which support PS define >> the same two hw states, awake and doze. I wonder whether we should >> instead aim for a single interface into the driver for PS that's capable >> of supporting all interface types. > > Such an interface would probably have to be the interface now defined > for mesh, telling the device when to wake up and go to sleep? But this > interface is rather inefficient for most chipsets... I guess the case of a single managed mode vif should be handled separately to benefit from the HW support most devices provide. --Marco ___ ath9k-devel mailing list ath9k-devel@lists.ath9k.org https://lists.ath9k.org/mailman/listinfo/ath9k-devel