2012-11-23 20:19:07

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 00/13] mesh powersave - basics

The following commits cover the basic subset of functions, that each mesh node
must support to communicate in the presence of power saving nodes.
The current patchset does not implement any actual power savings yet (no
hardware configuration, no doze state).

Mesh powersave is different from client mode powersave in the sense that
1) there is not just a singular power mode towards the access point, but a
link-specific power mode towards each peer instead
2) peers also maintain a power mode towards us (the local STA)
3) each node has to buffer frames towards power saving neighbors

With that said the different commits cover the following main topics:
3: maintaining a local power mode for each peer + one non-peer power mode
4: indicate the power mode towards the respective neighbors
5: track neighbors' power modes
9: buffer frames towards power saving neighbors
13: release buffered frames in coordination with neighbor


v2:
-active voice commit logs
-beacon interval and DTIM period set during mesh join (no change during mesh
runtime anymore)
-power mode "unknown" instead of "invalid"
-merged mesh power mode parameters and set/get commits
-"mesh" and "sta" in function names where useful
-sdata check in list_for_each_entry_rcu(sta, &sdata->local->sta_list, list)
-sta timer setup in sta_info_alloc
-BUG_ON -> WARN_ON + return
-clarify comments
-kernel-doc style
-clear separation {cfg,nl}80211 & mac80211
-removed debug strings
-ieee80211_has_qos_pm int -> bool
-#IFDEF CONFIG_MESH around use of sta->plid
-move skb_queue_purge to stop_mesh
-move counter cleanup to stop_mesh
-no use of sta_ps_transition anymore - own routines instead
-no use of ieee80211_sta_ps_deliver_response anymore, no use of ambiguous
UAPSD enum anymore - own function instead
-concentrate PS stuff in ieee80211_mesh_ps_rx_bcn
-less exposed functions in mesh.h

not done:
range checks and FILL_IN_MESH_PARAM_IF_SET - follows later in one cleanup

Concerning the patch series ordering etc., everything *should* be safe.
1-7 just adds lots of overhead to maintain a power mode and track neighbors
without actually having any effects. Before any frames are buffered in 9,
8 adds all the neccessary data structures. In that snapshot state buffered
unicast frames would not be released, but would just time out. 10-12 add more
overhead until 13 allows unicast frame release. Of course for the patch
submission I can merge everything together, if that is helpful. The current
splitted state helps to understand the semantics, though.


Marco Porsch (13):
{nl,cfg,mac}80211: set beacon interval and DTIM period on mesh join
{cfg,nl}80211: mesh power mode config parameters
mac80211: local link-specific mesh power mode logic
mac80211: mesh power mode indication in transmitted frames
mac80211: track neighbor STA power modes
{cfg,nl}80211: allow userspace to set the local link-specific power
mode
{cfg,nl}80211: allow userspace to read the local and peer mesh power
modes
mac80211: add power save support structure to mesh interface
mac80211: enable frame buffering for PS STA
{cfg,nl}80211: add awake window to mesh config
mac80211: add awake window IE to mesh beacons
mac80211: add TIM IE to mesh beacons
mac80211: mesh PS individually-addressed frame release

include/linux/ieee80211.h | 17 ++
include/net/cfg80211.h | 24 ++
include/uapi/linux/nl80211.h | 49 ++++
net/mac80211/Kconfig | 11 +
net/mac80211/Makefile | 3 +-
net/mac80211/cfg.c | 24 +-
net/mac80211/debug.h | 10 +
net/mac80211/debugfs_netdev.c | 6 +
net/mac80211/debugfs_sta.c | 5 +-
net/mac80211/ieee80211_i.h | 7 +
net/mac80211/mesh.c | 36 ++-
net/mac80211/mesh.h | 22 +-
net/mac80211/mesh_hwmp.c | 10 +
net/mac80211/mesh_pathtbl.c | 1 +
net/mac80211/mesh_plink.c | 22 ++
net/mac80211/mesh_ps.c | 632 +++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 24 ++
net/mac80211/sta_info.c | 19 +-
net/mac80211/sta_info.h | 14 +
net/mac80211/status.c | 5 +
net/mac80211/tx.c | 59 +++-
net/mac80211/util.c | 4 +
net/mac80211/wme.c | 13 +-
net/wireless/mesh.c | 8 +
net/wireless/nl80211.c | 56 +++-
25 files changed, 1060 insertions(+), 21 deletions(-)
create mode 100644 net/mac80211/mesh_ps.c

--
1.7.9.5



2012-11-30 12:08:57

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 08/13] mac80211: add power save support structure to mesh interface

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:

> +++ b/net/mac80211/mesh.c
> @@ -657,10 +657,16 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
> ifmsh->mesh_id_len = 0;
> ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
>
> + /* free all potentially still buffered group-addressed frames */
> + local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
> + skb_queue_purge(&ifmsh->ps.bc_buf);
> +
> /* flush STAs and mpaths on this iface */
> sta_info_flush(sdata->local, sdata);
> mesh_path_flush_by_iface(sdata);

the queue purge would seem to be in the wrong spot

> +#ifdef CONFIG_MAC80211_MESH
> + } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {

maybe use vif_is_mesh anyway for consistency?

johannes


2012-11-30 12:14:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 12/13] mac80211: add TIM IE to mesh beacons

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> Create a TIM IE similarly to AP mode and add it to mesh beacons.

This just duplicates the code, please unify.

johannes


2012-11-23 20:19:27

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 10/13] {cfg,nl}80211: add awake window to mesh config

This commit adds the dot11MeshAwakeWindowDuration value to the mesh config.
The awake window is a duration in TU describing how long the STA will stay
awake after transmitting its beacon in PS mode.
The value can be changed during mesh runtime.

Signed-off-by: Marco Porsch <[email protected]>
---
include/net/cfg80211.h | 3 +++
include/uapi/linux/nl80211.h | 3 +++
net/mac80211/debugfs_netdev.c | 3 +++
net/wireless/mesh.c | 2 ++
net/wireless/nl80211.c | 8 +++++++-
5 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 74b6c59..6ee661b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -894,6 +894,8 @@ struct bss_parameters {
* a PREQ element for root path confirmation.
* @power_mode: The default mesh power save mode which will be the initial
* setting for new peer links.
+ * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake
+ * after transmitting its beacon.
*/
struct mesh_config {
u16 dot11MeshRetryTimeout;
@@ -922,6 +924,7 @@ struct mesh_config {
u16 dot11MeshHWMProotInterval;
u16 dot11MeshHWMPconfirmationInterval;
enum nl80211_mesh_power_mode power_mode;
+ u16 dot11MeshAwakeWindowDuration;
};

/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 62e5b68..086d613 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2341,6 +2341,8 @@ enum nl80211_mesh_power_mode {
* @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
* type &enum nl80211_mesh_power_mode (u8)
*
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -2371,6 +2373,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
NL80211_MESHCONF_POWER_MODE,
+ NL80211_MESHCONF_AWAKE_WINDOW,

/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 99bef96..16151be 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -518,6 +518,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC);
IEEE80211_IF_FILE(power_mode,
u.mesh.mshcfg.power_mode, DEC);
+IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
+ u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
#endif

#define DEBUGFS_ADD_MODE(name, mode) \
@@ -623,6 +625,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
MESHPARAMS_ADD(dot11MeshHWMProotInterval);
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
MESHPARAMS_ADD(power_mode);
+ MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
#undef MESHPARAMS_ADD
}
#endif
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 21f668b..e9da6ca 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -46,6 +46,7 @@

#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */
#define MESH_DEFAULT_DTIM_PERIOD 4
+#define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */

const struct mesh_config default_mesh_config = {
.dot11MeshRetryTimeout = MESH_RET_T,
@@ -73,6 +74,7 @@ const struct mesh_config default_mesh_config = {
.dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
.power_mode = NL80211_MESH_POWER_ACTIVE,
+ .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
};

const struct mesh_setup default_mesh_setup = {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 84ebe38..512e7b0 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3800,7 +3800,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
cur_params.dot11MeshHWMPconfirmationInterval) ||
nla_put_u8(msg, NL80211_MESHCONF_POWER_MODE,
- cur_params.power_mode))
+ cur_params.power_mode) ||
+ nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
+ cur_params.dot11MeshAwakeWindowDuration))
goto nla_put_failure;
nla_nest_end(msg, pinfoattr);
genlmsg_end(msg, hdr);
@@ -3840,6 +3842,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
};

static const struct nla_policy
@@ -3961,6 +3964,9 @@ do {\
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
mask, NL80211_MESHCONF_POWER_MODE,
nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
+ mask, NL80211_MESHCONF_AWAKE_WINDOW,
+ nla_get_u16);
if (mask_out)
*mask_out = mask;

--
1.7.9.5


2012-11-23 20:19:21

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 07/13] {cfg,nl}80211: allow userspace to read the local and peer mesh power modes

Allow userspace to read following values:
local PS mode towards remote STA
remote STA's peer-specific power mode towards local STA
remote STA's non-peer power mode

Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
---
include/net/cfg80211.h | 12 ++++++++++++
include/uapi/linux/nl80211.h | 9 +++++++++
net/mac80211/cfg.c | 8 +++++++-
net/wireless/nl80211.c | 12 ++++++++++++
4 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6eb0a6b..74b6c59 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -553,6 +553,9 @@ struct station_parameters {
* @STATION_INFO_STA_FLAGS: @sta_flags filled
* @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled
* @STATION_INFO_T_OFFSET: @t_offset filled
+ * @STATION_INFO_LOCAL_MESH_PS_MODE: @local_ps_mode filled
+ * @STATION_INFO_PEER_MESH_PS_MODE: @peer_ps_mode filled
+ * @STATION_INFO_NONPEER_MESH_PS_MODE: @nonpeer_ps_mode filled
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@@ -576,6 +579,9 @@ enum station_info_flags {
STATION_INFO_STA_FLAGS = 1<<18,
STATION_INFO_BEACON_LOSS_COUNT = 1<<19,
STATION_INFO_T_OFFSET = 1<<20,
+ STATION_INFO_LOCAL_MESH_PS_MODE = 1<<21,
+ STATION_INFO_PEER_MESH_PS_MODE = 1<<22,
+ STATION_INFO_NONPEER_MESH_PS_MODE = 1<<23,
};

/**
@@ -679,6 +685,9 @@ struct sta_bss_parameters {
* @sta_flags: station flags mask & values
* @beacon_loss_count: Number of times beacon loss event has triggered.
* @t_offset: Time offset of the station relative to this host.
+ * @local_ps_mode: local mesh STA power save mode
+ * @peer_ps_mode: peer mesh STA power save mode
+ * @nonpeer_ps_mode: non-peer mesh STA power save mode
*/
struct station_info {
u32 filled;
@@ -708,6 +717,9 @@ struct station_info {

u32 beacon_loss_count;
s64 t_offset;
+ enum nl80211_mesh_power_mode local_ps_mode;
+ enum nl80211_mesh_power_mode peer_ps_mode;
+ enum nl80211_mesh_power_mode nonpeer_ps_mode;

/*
* Note: Add a new enum station_info_flags value for each new field and
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index bd54f21..62e5b68 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1793,6 +1793,12 @@ enum nl80211_sta_bss_param {
* @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
* @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
* @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_MESH_PS_MODE: local mesh STA link-specific power
+ * save mode
+ * @NL80211_STA_INFO_PEER_MESH_PS_MODE: peer mesh STA link-specific power
+ * save mode
+ * @NL80211_STA_INFO_NONPEER_MESH_PS_MODE: neighbor mesh STA power save mode
+ * towards non-peer STA
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -1817,6 +1823,9 @@ enum nl80211_sta_info {
NL80211_STA_INFO_STA_FLAGS,
NL80211_STA_INFO_BEACON_LOSS,
NL80211_STA_INFO_T_OFFSET,
+ NL80211_STA_INFO_LOCAL_MESH_PS_MODE,
+ NL80211_STA_INFO_PEER_MESH_PS_MODE,
+ NL80211_STA_INFO_NONPEER_MESH_PS_MODE,

/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 2ed111f..db27208 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -456,7 +456,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
STATION_INFO_PLID |
- STATION_INFO_PLINK_STATE;
+ STATION_INFO_PLINK_STATE |
+ STATION_INFO_LOCAL_MESH_PS_MODE |
+ STATION_INFO_PEER_MESH_PS_MODE |
+ STATION_INFO_NONPEER_MESH_PS_MODE;

sinfo->llid = le16_to_cpu(sta->llid);
sinfo->plid = le16_to_cpu(sta->plid);
@@ -465,6 +468,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled |= STATION_INFO_T_OFFSET;
sinfo->t_offset = sta->t_offset;
}
+ sinfo->local_ps_mode = sta->local_ps_mode;
+ sinfo->peer_ps_mode = sta->peer_ps_mode;
+ sinfo->nonpeer_ps_mode = sta->nonpeer_ps_mode;
#endif
}

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8749d3c..84ebe38 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2891,6 +2891,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
sinfo->beacon_loss_count))
goto nla_put_failure;
+ if ((sinfo->filled & STATION_INFO_LOCAL_MESH_PS_MODE) &&
+ nla_put_u8(msg, NL80211_STA_INFO_LOCAL_MESH_PS_MODE,
+ sinfo->local_ps_mode))
+ goto nla_put_failure;
+ if ((sinfo->filled & STATION_INFO_PEER_MESH_PS_MODE) &&
+ nla_put_u8(msg, NL80211_STA_INFO_PEER_MESH_PS_MODE,
+ sinfo->peer_ps_mode))
+ goto nla_put_failure;
+ if ((sinfo->filled & STATION_INFO_NONPEER_MESH_PS_MODE) &&
+ nla_put_u8(msg, NL80211_STA_INFO_NONPEER_MESH_PS_MODE,
+ sinfo->nonpeer_ps_mode))
+ goto nla_put_failure;
if (sinfo->filled & STATION_INFO_BSS_PARAM) {
bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
if (!bss_param)
--
1.7.9.5


2012-11-30 12:19:10

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 13/13] mac80211: mesh PS individually-addressed frame release

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:

> +/* RSPI */
> +#define IEEE80211_QOS_CTL_RSPI 0x0400

*NOW* you do this? HAHA.

Ok I'm going to skip this patch for now. This wasn't quite as painful as
the last review, but it's just too much for a single day.

Next time, in order to actually get something done, please compile &
test each single patch, not just the final result. Also, it'd be great
if you could split up the patch series into maybe two or three (maybe
cfg80211/mac80211?) ... it's pretty big.

johannes


2012-11-23 20:19:30

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 12/13] mac80211: add TIM IE to mesh beacons

Create a TIM IE similarly to AP mode and add it to mesh beacons.

Signed-off-by: Marco Porsch <[email protected]>
---
net/mac80211/tx.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1194020..44a597b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2332,6 +2332,31 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
}
}

+static int mesh_add_tim_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata,
+ u16 *tim_offset, u16 *tim_length)
+{
+ struct ieee80211_local *local = sdata->local;
+ u16 tmp = skb->len;
+
+ if (local->tim_in_locked_section) {
+ ieee80211_beacon_add_tim(sdata, &sdata->u.mesh.ps, skb);
+ } else {
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->tim_lock, flags);
+ ieee80211_beacon_add_tim(sdata, &sdata->u.mesh.ps, skb);
+ spin_unlock_irqrestore(&local->tim_lock, flags);
+ }
+
+ if (tim_offset)
+ *tim_offset = tmp - local->hw.extra_tx_headroom;
+ if (tim_length)
+ *tim_length = skb->len - tmp;
+
+ return 0;
+}
+
struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 *tim_offset, u16 *tim_length)
@@ -2439,6 +2464,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2 + /* NULL SSID */
2 + 8 + /* supported rates */
2 + 3 + /* DS params */
+ 256 + /* TIM IE */
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
2 + sizeof(struct ieee80211_ht_cap) +
2 + sizeof(struct ieee80211_ht_operation) +
@@ -2472,6 +2498,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,

if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
mesh_add_ds_params_ie(skb, sdata) ||
+ mesh_add_tim_ie(skb, sdata, tim_offset, tim_length) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_ht_cap_ie(skb, sdata) ||
--
1.7.9.5


2012-11-30 11:46:00

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 02/13] {cfg,nl}80211: mesh power mode config parameters

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> Add the nl80211_mesh_power_mode enumeration which holds possible values for
> the mesh power mode. These modes are unknown, active, light sleep and
> deep sleep.
>
> Add power_mode entry to the mesh config structure to hold the user-configured
> default mesh power mode. This value will be used for new peer links. Make the
> default mesh power mode accessible to userspace as part of the mesh config.

missing range checks.

johannes


2012-11-30 17:06:21

by Marco Porsch

[permalink] [raw]
Subject: Re: [RFCv2 08/13] mac80211: add power save support structure to mesh interface

Thanks again for all your comments!

On 11/30/2012 04:09 AM, Johannes Berg wrote:
> On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
>
>> +++ b/net/mac80211/mesh.c
>> @@ -657,10 +657,16 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
>> ifmsh->mesh_id_len = 0;
>> ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
>>
>> + /* free all potentially still buffered group-addressed frames */
>> + local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
>> + skb_queue_purge(&ifmsh->ps.bc_buf);
>> +
>> /* flush STAs and mpaths on this iface */
>> sta_info_flush(sdata->local, sdata);
>> mesh_path_flush_by_iface(sdata);
>
> the queue purge would seem to be in the wrong spot

Where would you recommend to put it then?
My idea was that after the (DTIM) beacon is disabled these buffered
broadcasts are stuck either way, so they can be purged.

>> +#ifdef CONFIG_MAC80211_MESH
>> + } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
>
> maybe use vif_is_mesh anyway for consistency?
>
> johannes
>
>


2012-11-30 11:58:00

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 04/13] mac80211: mesh power mode indication in transmitted frames

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:

> For performance reasons, calls to the function setting the frame flags are
> placed in HWMP routing routines, as there the STA pointer is already availible.

typo: available

> + /*
> + * Nulls are only sent to direct peers for PS and
> + * should already be addressed
> + */
> + if (ieee80211_is_qos_nullfunc(hdr->frame_control))
> + return err;

return 0 would be easier ...

> + * NOTE: sta must be given when an individually-addressed QoS frame header
> + * is handed, for group-addressed and management frames it not used

typo: handled

> + if (WARN_ON(is_unicast_ether_addr(hdr->addr1) &&
> + ieee80211_is_data_qos(hdr->frame_control) &&
> + !sta))
> + return;

last time I'm commenting on whitespace/indentation issues


> + ieee80211_set_mesh_ps_flags(sdata, NULL,
> + (struct ieee80211_hdr *) mgmt);

if you just do (void *)mgmt it'll fit on one line

> --- a/net/mac80211/wme.c
> +++ b/net/mac80211/wme.c
> @@ -191,6 +191,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
>
> /* qos header is 2 bytes */
> *p++ = ack_policy | tid;
> - *p = ieee80211_vif_is_mesh(&sdata->vif) ?
> - (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
> + if (ieee80211_vif_is_mesh(&sdata->vif)) {
> + /* preserve RSPI and Mesh PS Level bit */
> + *p &= ((IEEE80211_QOS_CTL_RSPI |
> + IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8);
> +
> + /* Nulls don't have a mesh header (frame body) */
> + if (!ieee80211_is_qos_nullfunc(hdr->frame_control))
> + *p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8);
> + } else {
> + *p = 0;
> + }
> }

This looks so much better now :)

johannes


2012-11-30 12:11:51

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 10/13] {cfg,nl}80211: add awake window to mesh config

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> This commit adds the dot11MeshAwakeWindowDuration value to the mesh config.
> The awake window is a duration in TU describing how long the STA will stay
> awake after transmitting its beacon in PS mode.
> The value can be changed during mesh runtime.
>

again missing range checks?


2012-11-29 12:34:58

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 00/13] mesh powersave - basics

On Wed, 2012-11-28 at 18:10 -0800, Javier Cardona wrote:
> Johannes,

> Marco is working in our office until the end of the year and has
> regular feedback from the team. Is there anything specific that you
> think we should review in more detail?

Oh ok, no, I just didn't realize you'd seen it at all.

johannes


2012-11-30 11:44:42

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 01/13] {nl,cfg,mac}80211: set beacon interval and DTIM period on mesh join

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> Move the default mesh beacon interval and DTIM period to cfg80211 and
> make it accessible to nl80211. This enables setting both values when joining
> an MBSS.
>
> NOTE: In a mesh with links in PS mode it is not advised to use the default
> parameters 1000:4, as these cause excessive buffering delays that exceed
> various timeouts (e.g. HWMP path request).

Looks fine, I think you should change the minimum BI to 10 or so though.

johannes



2012-11-30 12:13:35

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 11/13] mac80211: add awake window IE to mesh beacons

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> The awake window value is announced in an IE in beacons if the local STA
> is in light or deep sleep mode towards any peer or non-peer. On receipt STA
> parse the IE.

Since you don't do anything with it I'd suggest moving the parsing to
the patch that uses it.

johannes


2012-11-23 20:19:32

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 13/13] mac80211: mesh PS individually-addressed frame release

When receiving a neighbor beacon, check if the neighbor indicates buffered
frames in its TIM IE and if it announces an awake window duration. The routine
for frame release towards PS neigbors will then evaluate these parameters.

In case the neighbor is an established peer, frames may be released in a
Peer Service Period. Peer Service Periods start and end with a PSP trigger
frame. The setting of RSPI and EOSP frame flags determine if a PSP starts
or ends and which STA is the owner, recipient or both during the PSP.
The indication of buffered frames at the peer and the status of the local
buffers determine which type of PSP is started when a beacon from a peer
is received. The buffered frames are transmitted only after the PSP trigger
frame has been acknowledged.
The function ieee80211_sta_ps_deliver_response will automatically try to
set the EOSP for the last flag. In case this is not a QoS data frame, it
will append a QoS Null frame after the last frame.

The number of PSPs in which the local STA is owner and/or recipient are
counted to be able to disallow doze state during ongoing PSPs.

Currently, in HT channels PSPs behave imperfectly and show large delay spikes
and frame losses.

Signed-off-by: Marco Porsch <[email protected]>
---
include/linux/ieee80211.h | 2 +
net/mac80211/debugfs_sta.c | 5 +-
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mesh.c | 1 +
net/mac80211/mesh.h | 4 +
net/mac80211/mesh_plink.c | 1 +
net/mac80211/mesh_ps.c | 287 ++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 3 +
net/mac80211/sta_info.h | 4 +
net/mac80211/status.c | 5 +
10 files changed, 311 insertions(+), 2 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index a586e47..bfce90a 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -151,6 +151,8 @@

/* mesh power save level subfield mask */
#define IEEE80211_QOS_CTL_MESH_PS_LEVEL 0x0200
+/* RSPI */
+#define IEEE80211_QOS_CTL_RSPI 0x0400

/* U-APSD queue for WMM IEs sent by AP */
#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 5ccec2c..40f58ce 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -63,7 +63,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""

int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
@@ -72,7 +72,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
TEST(INSERTED), TEST(RATE_CONTROL),
- TEST(TOFFSET_KNOWN));
+ TEST(TOFFSET_KNOWN), TEST(PSP_OWNER),
+ TEST(PSP_RECIPIENT));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 3d4c43f..c9f7a1a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -623,6 +623,7 @@ struct ieee80211_if_mesh {
u8 ps_peers_light_sleep;
u8 ps_peers_deep_sleep;
struct ps_data ps;
+ atomic_t num_psp; /* counts both owner and recipient independently */
};

#ifdef CONFIG_MAC80211_MESH
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index ad5dca7..7314848 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -689,6 +689,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
mesh_path_flush_by_iface(sdata);

atomic_set(&ifmsh->ps.num_sta_ps, 0); /* not reset by sta_info_flush */
+ atomic_set(&ifmsh->num_psp, 0);

del_timer_sync(&sdata->u.mesh.housekeeping_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index f7bf9e3..940a18b 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -257,6 +257,10 @@ void ieee80211_set_peer_ps_mode(struct sta_info *sta,
struct ieee80211_hdr *hdr);
void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
struct ieee80211_hdr *hdr);
+void ieee80211_mesh_ps_rx_bcn(struct sta_info *sta,
+ struct ieee802_11_elems *elems);
+void ieee80211_psp_trigger_process(struct ieee80211_hdr *hdr,
+ struct sta_info *sta, bool tx, bool acked);

/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 0572346..207fcff 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -418,6 +418,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
rssi_threshold_check(sta, sdata))
mesh_plink_open(sta);

+ ieee80211_mesh_ps_rx_bcn(sta, elems);
out:
rcu_read_unlock();
}
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
index ee72f3e..6bde6e0 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -8,6 +8,27 @@
*/

#include "mesh.h"
+#include "wme.h"
+
+static inline bool test_and_set_psp_flag(struct sta_info *sta,
+ enum ieee80211_sta_info_flags flag)
+{
+ if (!test_and_set_sta_flag(sta, flag)) {
+ atomic_inc(&sta->sdata->u.mesh.num_psp);
+ return false;
+ }
+ return true;
+}
+
+static inline bool test_and_clear_psp_flag(struct sta_info *sta,
+ enum ieee80211_sta_info_flags flag)
+{
+ if (test_and_clear_sta_flag(sta, flag)) {
+ atomic_dec(&sta->sdata->u.mesh.num_psp);
+ return true;
+ }
+ return false;
+}

/**
* ieee80211_mesh_null_get - create pre-addressed QoS Null frame
@@ -60,6 +81,14 @@ static void ieee80211_send_mesh_null(struct sta_info *sta)
mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n",
sta->sta.addr);

+ /* don't unintentionally start a PSP */
+ if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
+ __le16 *qc = (__le16 *) ieee80211_get_qos_ctl(
+ (struct ieee80211_hdr *) skb->data);
+
+ *qc |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+ }
+
ieee80211_tx_skb(sta->sdata, skb);
}

@@ -270,6 +299,14 @@ void ieee80211_mesh_sta_ps_update(struct sta_info *sta)
} else {
ieee80211_sta_ps_deliver_wakeup(sta);
}
+
+ /* clear the PSP flags for non-peers or active STA */
+ if (sta->plink_state != NL80211_PLINK_ESTAB) {
+ test_and_clear_psp_flag(sta, WLAN_STA_PSP_OWNER);
+ test_and_clear_psp_flag(sta, WLAN_STA_PSP_RECIPIENT);
+ } else if (!do_buffer) {
+ test_and_clear_psp_flag(sta, WLAN_STA_PSP_OWNER);
+ }
}

/**
@@ -343,3 +380,253 @@ void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,

ieee80211_mesh_sta_ps_update(sta);
}
+
+static void ieee80211_send_psp_trigger(struct sta_info *sta,
+ bool rspi, bool eosp)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct sk_buff *skb;
+ struct ieee80211_hdr *nullfunc;
+ struct ieee80211_tx_info *info;
+ __le16 *qc;
+
+ skb = ieee80211_mesh_null_get(sta);
+ if (!skb)
+ return;
+
+ nullfunc = (struct ieee80211_hdr *) skb->data;
+ if (!eosp)
+ nullfunc->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ /*
+ * | RSPI | EOSP | PSP triggering |
+ * +------+------+--------------------+
+ * | 0 | 0 | local STA is owner |
+ * | 0 | 1 | no PSP (PSP end) |
+ * | 1 | 0 | both STA are owner |
+ * | 1 | 1 | peer STA is owner | see IEEE802.11-2012 13.14.9.2
+ */
+ qc = (__le16 *) ieee80211_get_qos_ctl(nullfunc);
+ if (rspi)
+ *qc |= cpu_to_le16(IEEE80211_QOS_CTL_RSPI);
+ if (eosp)
+ *qc |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+
+ info = IEEE80211_SKB_CB(skb);
+
+ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+ mps_dbg(sdata, "sending PSP trigger%s%s to %pM\n",
+ rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr);
+
+ ieee80211_tx_skb(sdata, skb);
+}
+
+/**
+ * ieee80211_qos_null_append - append QoS Null frame to skb queue if needed
+ *
+ * @sta: peer mesh STA we are sending frames to
+ * @frames: skb queue to append to
+ *
+ * To properly end a mesh PSP the last transmitted frame has to set the EOSP
+ * flag in the QoS Control field. In case the current tailing frame is not a
+ * QoS Data frame, append a QoS Null to carry the flag.
+ */
+static void ieee80211_qos_null_append(struct sta_info *sta,
+ struct sk_buff_head *frames)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct sk_buff *new_skb, *skb = skb_peek_tail(frames);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ return;
+
+ new_skb = ieee80211_mesh_null_get(sta);
+ if (!new_skb)
+ return;
+
+ mps_dbg(sdata, "appending QoS Null in PSP towards %pM\n",
+ sta->sta.addr);
+
+ /* should be transmitted last -> lowest priority */
+ new_skb->priority = 1;
+ skb_set_queue_mapping(new_skb, IEEE80211_AC_BK);
+ ieee80211_set_qos_hdr(sdata, new_skb);
+
+ info = IEEE80211_SKB_CB(new_skb);
+ info->control.vif = &sdata->vif;
+ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+
+ __skb_queue_tail(frames, new_skb);
+}
+
+/**
+ * ieee80211_mesh_ps_deliver_psp - transmit frames during a PSP
+ *
+ * @sta: STA info to transmit to
+ * @n_frames: number of frames to transmit. -1 for all
+ */
+static void ieee80211_mesh_ps_deliver(struct sta_info *sta, int n_frames)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ int ac;
+ struct sk_buff_head frames;
+ struct sk_buff *skb;
+
+ skb_queue_head_init(&frames);
+
+ /* collect frame(s) from buffers */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct sk_buff *skb;
+
+ while (n_frames != 0) {
+ skb = skb_dequeue(&sta->tx_filtered[ac]);
+ if (!skb) {
+ skb = skb_dequeue(
+ &sta->ps_tx_buf[ac]);
+ if (skb)
+ local->total_ps_buffered--;
+ }
+ if (!skb)
+ break;
+ n_frames--;
+ skb_queue_tail(&frames, skb);
+ }
+ }
+
+ /* nothing to send? -> EOSP */
+ if (skb_queue_empty(&frames)) {
+ ieee80211_send_psp_trigger(sta, false, true);
+ return;
+ }
+
+ /* in a PSP make sure the last skb is a QoS Data frame */
+ if (test_sta_flag(sta, WLAN_STA_PSP_OWNER))
+ ieee80211_qos_null_append(sta, &frames);
+
+ mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n",
+ skb_queue_len(&frames),sta->sta.addr);
+
+ /* prepare collected frames for transmission */
+ skb_queue_walk(&frames, skb) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *) skb->data;
+
+ /*
+ * Tell TX path to send this frame even though the
+ * STA may still remain is PS mode after this frame
+ * exchange.
+ */
+ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+
+ if (!skb_queue_is_last(&frames, skb)) {
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else {
+ hdr->frame_control &=
+ cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qoshdr = ieee80211_get_qos_ctl(hdr);
+
+ /* PSP trigger frame ends service period */
+ *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ }
+ }
+ }
+
+ ieee80211_add_pending_skbs(local, &frames);
+ sta_info_recalc_tim(sta);
+}
+
+/**
+ * ieee80211_mesh_ps_rx_bcn - react to neighbour beacon with either node in PS
+ *
+ * @sta: neighbour STA that sent the beacon
+ * @awake_window: awake window duration the neighbour announced
+ * @buffer_peer: neighbour has data buffered for the local STA
+ *
+ * If we have individually-addressed frames buffered or a peer indicates
+ * buffered frames, send a corresponding peer service period trigger frame.
+ * Since currently we do not evaluate the awake window duration,
+ * QoS Nulls are used as PSP trigger frames.
+ * Currently, if the neighbour STA is not a peer, only send single frames.
+ */
+void ieee80211_mesh_ps_rx_bcn(struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ int ac, buffer_local = 0;
+ bool has_buffered = false;
+ u16 awake_window = 0;
+
+ /* TIM map only for LLID <= IEEE80211_MAX_AID */
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
+ le16_to_cpu(sta->llid) % IEEE80211_MAX_AID);
+
+ if (has_buffered)
+ mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
+ sta->sta.addr);
+
+ if (elems->awake_window)
+ awake_window = get_unaligned_le16(elems->awake_window);
+
+ /* only transmit towards PS STA with announced awake window */
+ if (test_sta_flag(sta, WLAN_STA_PS_STA) && !awake_window)
+ return;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
+ skb_queue_len(&sta->tx_filtered[ac]);
+
+ if (!has_buffered && !buffer_local)
+ return;
+
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ ieee80211_send_psp_trigger(sta, has_buffered, !buffer_local);
+ else
+ ieee80211_mesh_ps_deliver(sta, 1);
+}
+
+/**
+ * ieee80211_psp_trigger_process - track the status of Peer Service Periods
+ *
+ * @hdr: IEEE 802.11 QoS Header
+ * @sta: peer to start a PSP with
+ * @tx: frame was transmitted by the local STA
+ * @acked: frame has been transmitted successfully
+ *
+ * NOTE: active mode STA may only serve as PSP owner
+ */
+void ieee80211_psp_trigger_process(struct ieee80211_hdr *hdr,
+ struct sta_info *sta, bool tx, bool acked)
+{
+ __le16 *qc = (__le16 *) ieee80211_get_qos_ctl(hdr);
+ __le16 rspi = *qc & cpu_to_le16(IEEE80211_QOS_CTL_RSPI);
+ __le16 eosp = *qc & cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+
+ if (tx) {
+ if (rspi && acked)
+ test_and_set_psp_flag(sta, WLAN_STA_PSP_RECIPIENT);
+
+ if (eosp)
+ test_and_clear_psp_flag(sta, WLAN_STA_PSP_OWNER);
+ else if (acked &&
+ test_sta_flag(sta, WLAN_STA_PS_STA) &&
+ !test_and_set_psp_flag(sta, WLAN_STA_PSP_OWNER))
+ ieee80211_mesh_ps_deliver(sta, -1);
+ } else {
+ if (eosp)
+ test_and_clear_psp_flag(sta, WLAN_STA_PSP_RECIPIENT);
+ else if (sta->local_ps_mode != NL80211_MESH_POWER_ACTIVE)
+ test_and_set_psp_flag(sta, WLAN_STA_PSP_RECIPIENT);
+
+ if (rspi && !test_and_set_psp_flag(sta, WLAN_STA_PSP_OWNER))
+ ieee80211_mesh_ps_deliver(sta, -1);
+ }
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 90f2ce5..c9abe59 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1365,6 +1365,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
* peer's link-specific PS mode towards the local STA
*/
ieee80211_set_peer_ps_mode(sta, hdr);
+
+ /* check for peer service period trigger frames */
+ ieee80211_psp_trigger_process(hdr, sta, false, false);
} else {
/*
* can only determine non-peer PS mode
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index dbb3a10..81b528b 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -56,6 +56,8 @@
* @WLAN_STA_INSERTED: This station is inserted into the hash table.
* @WLAN_STA_RATE_CONTROL: rate control was initialized for this station.
* @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
+ * @WLAN_STA_PSP_OWNER: local STA is owner of a Peer Service Period.
+ * @WLAN_STA_PSP_RECIPIENT: local STA is recipient of a Peer Service Period.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH,
@@ -78,6 +80,8 @@ enum ieee80211_sta_info_flags {
WLAN_STA_INSERTED,
WLAN_STA_RATE_CONTROL,
WLAN_STA_TOFFSET_KNOWN,
+ WLAN_STA_PSP_OWNER,
+ WLAN_STA_PSP_RECIPIENT,
};

#define STA_TID_NUM 16
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 2482478..978508b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -399,6 +399,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

+ /* mesh peer service period support */
+ if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
+ ieee80211_is_data_qos(hdr->frame_control))
+ ieee80211_psp_trigger_process(hdr, sta, true, acked);
+
if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
(rates_idx != -1))
sta->last_tx_rate = info->status.rates[rates_idx];
--
1.7.9.5


2012-11-23 20:19:16

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 05/13] mac80211: track neighbor STA power modes

According to IEEE802.11-2012 a mesh peering is always associated between
two mesh STAs. Both mesh STAs have their own mesh power mode for the
mesh peering. This power mode is called the link-specific power mode.

Add the peer_ps_mode field to the sta_info structure to represent the peer's
link-specific power mode towards the local station. Additionally, the
nonpeer_ps_mode field represents the peer's power mode towards all non-peer
stations.

The peers' link-specific power modes are tracked from the Power Management
field in the Frame Control field and the Mesh Power Save Level field
in the QoS Control field. Peer and non-peer modes are tracked independently
from respective frames. This allows fast reconfiguration after a peering
change.

Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
Signed-off-by: Max Filippov <[email protected]>
---
include/linux/ieee80211.h | 9 ++++++
net/mac80211/mesh.h | 4 +++
net/mac80211/mesh_ps.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 18 ++++++++++++
net/mac80211/sta_info.h | 4 +++
5 files changed, 103 insertions(+)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 2ba972c..a586e47 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -571,6 +571,15 @@ static inline int ieee80211_is_first_frag(__le16 seq_ctrl)
return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0;
}

+/**
+ * ieee80211_has_qos_pm - check Power Save Level in QoS control
+ * @qc - QoS control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_has_qos_pm(__le16 qc)
+{
+ return (qc & cpu_to_le16(IEEE80211_QOS_CTL_MESH_PS_LEVEL)) != 0;
+}
+
struct ieee80211s_hdr {
u8 flags;
u8 ttl;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 49a3114..2b6aee7 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -250,6 +250,10 @@ void ieee80211_sta_mesh_local_ps_mode_timer(unsigned long data);
void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_hdr *hdr);
+void ieee80211_set_peer_ps_mode(struct sta_info *sta,
+ struct ieee80211_hdr *hdr);
+void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
+ struct ieee80211_hdr *hdr);

/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
index 0d496fe..2d00aea 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -233,3 +233,71 @@ void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
else
*qc &= cpu_to_le16(~IEEE80211_QOS_CTL_MESH_PS_LEVEL);
}
+
+/**
+ * ieee80211_set_peer_ps_mode - track the link-specific power mode of peers
+ *
+ * @sta: STA info to update
+ * @hdr: IEEE 802.11 QoS Header
+ */
+void ieee80211_set_peer_ps_mode(struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ enum nl80211_mesh_power_mode pm;
+ __le16 *qc = (__le16 *) ieee80211_get_qos_ctl(hdr);
+
+ WARN_ON(!ieee80211_is_data_qos(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1));
+
+ /*
+ * Test Power Managment field of frame control (PW) and
+ * mesh power save level subfield of QoS control field (PSL)
+ *
+ * | PM | PSL| Mesh Power Mode |
+ * +----+----+-----------------+
+ * | 0 |Rsrv| Active |
+ * | 1 | 0 | Light |
+ * | 1 | 1 | Deep |
+ */
+ if (ieee80211_has_pm(hdr->frame_control)) {
+ if (ieee80211_has_qos_pm(*qc))
+ pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ else
+ pm = NL80211_MESH_POWER_LIGHT_SLEEP;
+ } else {
+ pm = NL80211_MESH_POWER_ACTIVE;
+ }
+
+ if (sta->peer_ps_mode == pm)
+ return;
+
+ mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
+ sta->sta.addr, pm);
+
+ sta->peer_ps_mode = pm;
+}
+
+/**
+ * ieee80211_set_nonpeer_ps_mode - track the non-peer power mode of neighbors
+ *
+ * @sta: STA info to update
+ * @hdr: IEEE 802.11 (QoS) Header
+ */
+void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ enum nl80211_mesh_power_mode pm;
+
+ if (ieee80211_has_pm(hdr->frame_control))
+ pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ else
+ pm = NL80211_MESH_POWER_ACTIVE;
+
+ if (sta->nonpeer_ps_mode == pm)
+ return;
+
+ mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
+ sta->sta.addr, pm);
+
+ sta->nonpeer_ps_mode = pm;
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 67aa26c..90f2ce5 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1356,6 +1356,24 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
}
}

+ /* mesh power save support */
+ if (ieee80211_vif_is_mesh(&rx->sdata->vif)) {
+ if (is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control)) {
+ /*
+ * individually addressed QoS Data/Null frames contain
+ * peer's link-specific PS mode towards the local STA
+ */
+ ieee80211_set_peer_ps_mode(sta, hdr);
+ } else {
+ /*
+ * can only determine non-peer PS mode
+ * (see IEEE802.11-2012 8.2.4.1.7)
+ */
+ ieee80211_set_nonpeer_ps_mode(sta, hdr);
+ }
+ }
+
/*
* Drop (qos-)data::nullfunc frames silently, since they
* are used only to control station power saving mode.
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 046ca70..dbb3a10 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -277,6 +277,8 @@ struct sta_ampdu_mlme {
* @local_ps_mode: local link-specific power save mode
* @local_ps_mode_delayed: temp. storage for delayed setting of local_ps_mode
* @local_ps_mode_timer: timer for delayed setting of local_ps_mode
+ * @peer_ps_mode: peer's link-specific power save mode
+ * @nonpeer_ps_mode: STA's power save mode towards non-peer neighbors
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -376,6 +378,8 @@ struct sta_info {
enum nl80211_mesh_power_mode local_ps_mode;
enum nl80211_mesh_power_mode local_ps_mode_delayed;
struct timer_list local_ps_mode_timer;
+ enum nl80211_mesh_power_mode peer_ps_mode;
+ enum nl80211_mesh_power_mode nonpeer_ps_mode;
#endif

#ifdef CONFIG_MAC80211_DEBUGFS
--
1.7.9.5


2012-11-23 20:19:10

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 02/13] {cfg,nl}80211: mesh power mode config parameters

Add the nl80211_mesh_power_mode enumeration which holds possible values for
the mesh power mode. These modes are unknown, active, light sleep and
deep sleep.

Add power_mode entry to the mesh config structure to hold the user-configured
default mesh power mode. This value will be used for new peer links. Make the
default mesh power mode accessible to userspace as part of the mesh config.

Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
---
include/net/cfg80211.h | 3 +++
include/uapi/linux/nl80211.h | 32 ++++++++++++++++++++++++++++++++
net/wireless/mesh.c | 1 +
net/wireless/nl80211.c | 8 +++++++-
4 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 91ce6234..cc61b4b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -878,6 +878,8 @@ struct bss_parameters {
* @dot11MeshHWMPconfirmationInterval: The minimum interval of time (in TUs)
* during which a mesh STA can send only one Action frame containing
* a PREQ element for root path confirmation.
+ * @power_mode: The default mesh power save mode which will be the initial
+ * setting for new peer links.
*/
struct mesh_config {
u16 dot11MeshRetryTimeout;
@@ -905,6 +907,7 @@ struct mesh_config {
u32 dot11MeshHWMPactivePathToRootTimeout;
u16 dot11MeshHWMProotInterval;
u16 dot11MeshHWMPconfirmationInterval;
+ enum nl80211_mesh_power_mode power_mode;
};

/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 617d0fb..d0efa1e 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2203,6 +2203,34 @@ enum nl80211_mntr_flags {
};

/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ * not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ * in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but will wake up for
+ * neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but may not wake up
+ * for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+ NL80211_MESH_POWER_UNKNOWN,
+ NL80211_MESH_POWER_ACTIVE,
+ NL80211_MESH_POWER_LIGHT_SLEEP,
+ NL80211_MESH_POWER_DEEP_SLEEP,
+
+ __NL80211_MESH_POWER_AFTER_LAST,
+ NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_meshconf_params - mesh configuration parameters
*
* Mesh configuration parameters. These can be changed while the mesh is
@@ -2296,6 +2324,9 @@ enum nl80211_mntr_flags {
* (in TUs) during which a mesh STA can send only one Action frame
* containing a PREQ element for root path confirmation.
*
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ * type &enum nl80211_mesh_power_mode (u8)
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -2325,6 +2356,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ NL80211_MESHCONF_POWER_MODE,

/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index c2060d4..21f668b 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -72,6 +72,7 @@ const struct mesh_config default_mesh_config = {
.dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,
.dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
+ .power_mode = NL80211_MESH_POWER_ACTIVE,
};

const struct mesh_setup default_mesh_setup = {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e99a8ce..3e2aa63 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3770,7 +3770,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
cur_params.dot11MeshHWMProotInterval) ||
nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
- cur_params.dot11MeshHWMPconfirmationInterval))
+ cur_params.dot11MeshHWMPconfirmationInterval) ||
+ nla_put_u8(msg, NL80211_MESHCONF_POWER_MODE,
+ cur_params.power_mode))
goto nla_put_failure;
nla_nest_end(msg, pinfoattr);
genlmsg_end(msg, hdr);
@@ -3809,6 +3811,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
[NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U8 },
};

static const struct nla_policy
@@ -3927,6 +3930,9 @@ do {\
dot11MeshHWMPconfirmationInterval, mask,
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
+ mask, NL80211_MESHCONF_POWER_MODE,
+ nla_get_u8);
if (mask_out)
*mask_out = mask;

--
1.7.9.5


2012-11-30 12:02:22

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 05/13] mac80211: track neighbor STA power modes

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:

> +/**
> + * ieee80211_has_qos_pm - check Power Save Level in QoS control
> + * @qc - QoS control bytes in little-endian byteorder
> + */
> +static inline bool ieee80211_has_qos_pm(__le16 qc)
> +{
> + return (qc & cpu_to_le16(IEEE80211_QOS_CTL_MESH_PS_LEVEL)) != 0;

I think this is confusing. Why not call it "has_qos_mesh_ps" or
something?

johannes


2012-11-23 20:19:12

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 03/13] mac80211: local link-specific mesh power mode logic

According to IEEE802.11-2012 a mesh peering is always associated between
two mesh STAs. Both mesh STAs have their own mesh power mode for the
mesh peering. This power mode is called the link-specific power mode.

Add the local_ps_mode field to the sta_info structure to represent
the link-specific power mode at this station towards the remote station.
When a peer link is established, the local link-specific power mode is
set up equal to the user-configured default power mode.
To avoid frame losses when switching to a lower power state after peering
and for use in a future per-link dynamic powersave, the set_local_ps_mode
function can be called with a delay parameter that sets a per-STA timer
accordingly.

Monitor the status of the local peer-specific power modes for mesh power mode
indication, and for checking if the radio may be set to doze state. Update
the PS status if the peering status of a neighbor changes.

According to IEEE802.11-2012 a mesh STA maintains a mesh power mode
for non-peer mesh STAs. The non-peer mesh power mode determines when
non-peer mesh STAs may send Probe Request and Mesh Peering Open Request
frames to the mesh STA.
Based on the status of the peer-specific power modes also the non-peer
power mode is set: if there is at least one link in light or deep sleep
mode, the non-peer power mode will be deep sleep mode.
To allow successful peering, 802.11s defines that the non-peer power mode is
temporarily set to active mode during peering.

A debug define allows comfortable debugging output.

Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
Signed-off-by: Max Filippov <[email protected]>
---
net/mac80211/Kconfig | 11 ++++
net/mac80211/Makefile | 3 +-
net/mac80211/cfg.c | 4 ++
net/mac80211/debug.h | 10 ++++
net/mac80211/debugfs_netdev.c | 3 +
net/mac80211/ieee80211_i.h | 4 ++
net/mac80211/mesh.c | 1 +
net/mac80211/mesh.h | 6 ++
net/mac80211/mesh_plink.c | 18 ++++++
net/mac80211/mesh_ps.c | 124 +++++++++++++++++++++++++++++++++++++++++
net/mac80211/sta_info.c | 4 ++
net/mac80211/sta_info.h | 6 ++
12 files changed, 193 insertions(+), 1 deletion(-)
create mode 100644 net/mac80211/mesh_ps.c

diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index b4ecf26..0ecf947 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -258,6 +258,17 @@ config MAC80211_MESH_SYNC_DEBUG

Do not select this option.

+config MAC80211_MESH_PS_DEBUG
+ bool "Verbose mesh powersave debugging"
+ depends on MAC80211_DEBUG_MENU
+ depends on MAC80211_MESH
+ ---help---
+ Selecting this option causes mac80211 to print out very verbose mesh
+ powersave debugging messages (when mac80211 is taking part in a
+ mesh network).
+
+ Do not select this option.
+
config MAC80211_TDLS_DEBUG
bool "Verbose TDLS debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 4911202..9d7d840 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -39,7 +39,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
mesh_pathtbl.o \
mesh_plink.o \
mesh_hwmp.o \
- mesh_sync.o
+ mesh_sync.o \
+ mesh_ps.o

mac80211-$(CONFIG_PM) += pm.o

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index d0b7a7e..62564e9 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1669,6 +1669,10 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
conf->dot11MeshHWMPconfirmationInterval =
nconf->dot11MeshHWMPconfirmationInterval;
+ if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
+ conf->power_mode = nconf->power_mode;
+ ieee80211_mesh_local_ps_update(sdata);
+ }
return 0;
}

diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h
index 8f383a5..4ccc5ed 100644
--- a/net/mac80211/debug.h
+++ b/net/mac80211/debug.h
@@ -44,6 +44,12 @@
#define MAC80211_MESH_SYNC_DEBUG 0
#endif

+#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
+#define MAC80211_MESH_PS_DEBUG 1
+#else
+#define MAC80211_MESH_PS_DEBUG 0
+#endif
+
#ifdef CONFIG_MAC80211_TDLS_DEBUG
#define MAC80211_TDLS_DEBUG 1
#else
@@ -151,6 +157,10 @@ do { \
_sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \
sdata, fmt, ##__VA_ARGS__)

+#define mps_dbg(sdata, fmt, ...) \
+ _sdata_dbg(MAC80211_MESH_PS_DEBUG, \
+ sdata, fmt, ##__VA_ARGS__)
+
#define tdls_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_TDLS_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 3393ad5..99bef96 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -516,6 +516,8 @@ IEEE80211_IF_FILE(dot11MeshHWMProotInterval,
u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC);
IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC);
+IEEE80211_IF_FILE(power_mode,
+ u.mesh.mshcfg.power_mode, DEC);
#endif

#define DEBUGFS_ADD_MODE(name, mode) \
@@ -620,6 +622,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout);
MESHPARAMS_ADD(dot11MeshHWMProotInterval);
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
+ MESHPARAMS_ADD(power_mode);
#undef MESHPARAMS_ADD
}
#endif
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 32e4785..0364844 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -618,6 +618,10 @@ struct ieee80211_if_mesh {
s64 sync_offset_clockdrift_max;
spinlock_t sync_offset_lock;
bool adjusting_tbtt;
+ /* mesh power save */
+ enum nl80211_mesh_power_mode nonpeer_ps_mode;
+ u8 ps_peers_light_sleep;
+ u8 ps_peers_deep_sleep;
};

#ifdef CONFIG_MAC80211_MESH
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 5f3ef91..d4e90ba 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -633,6 +633,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
ieee80211_get_sdata_band(sdata));
+ ieee80211_mesh_local_ps_update(sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_HT |
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 7f3a78f..64781dd 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -242,6 +242,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);

+/* mesh power save */
+void ieee80211_mesh_local_ps_update(struct ieee80211_sub_if_data *sdata);
+void ieee80211_set_sta_mesh_local_ps_mode(struct sta_info *sta,
+ enum nl80211_mesh_power_mode pm, u32 delay);
+void ieee80211_sta_mesh_local_ps_mode_timer(unsigned long data);
+
/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 7a47f40..19994e7 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -179,6 +179,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);

+ ieee80211_mesh_local_ps_update(sdata);
+
return changed;
}

@@ -545,6 +547,9 @@ int mesh_plink_open(struct sta_info *sta)
"Mesh plink: starting establishment with %pM\n",
sta->sta.addr);

+ /* set the non-peer mode to active during peering */
+ ieee80211_mesh_local_ps_update(sdata);
+
return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
sta->sta.addr, llid, 0, 0);
}
@@ -776,6 +781,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->llid = llid;
mesh_plink_timer_set(sta,
mshcfg->dot11MeshRetryTimeout);
+
+ /* set the non-peer mode to active during peering */
+ ieee80211_mesh_local_ps_update(sdata);
+
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_OPEN,
@@ -868,6 +877,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
changed |= mesh_set_ht_prot_mode(sdata);
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
sta->sta.addr);
+
+ ieee80211_set_sta_mesh_local_ps_mode(sta,
+ mshcfg->power_mode, 100);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -906,6 +918,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_CONFIRM,
sta->sta.addr, llid, plid, 0);
+ /*
+ * we need some delay here, otherwise
+ * the announcement Null would arrive before the CONFIRM
+ */
+ ieee80211_set_sta_mesh_local_ps_mode(sta,
+ mshcfg->power_mode, 100);
break;
default:
spin_unlock_bh(&sta->lock);
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
new file mode 100644
index 0000000..14bf65b
--- /dev/null
+++ b/net/mac80211/mesh_ps.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012, Marco Porsch <[email protected]>
+ * Copyright 2012, cozybit Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "mesh.h"
+
+/**
+ * ieee80211_mesh_local_ps_update - keep track of link-specific PS modes
+ *
+ * @sdata: local mesh subif
+ *
+ * sets the non-peer power mode and triggers the driver PS (re-)configuration
+ * called by cfg80211, on peer link changes and by a timer for delayed setting
+ */
+void ieee80211_mesh_local_ps_update(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct sta_info *sta;
+ bool peering = false;
+ u8 light_sleep_cnt = 0;
+ u8 deep_sleep_cnt = 0;
+
+ list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+
+ switch (sta->plink_state) {
+ case NL80211_PLINK_OPN_SNT:
+ case NL80211_PLINK_OPN_RCVD:
+ case NL80211_PLINK_CNF_RCVD:
+ peering = true;
+ break;
+ case NL80211_PLINK_ESTAB:
+ if (sta->local_ps_mode ==
+ NL80211_MESH_POWER_LIGHT_SLEEP)
+ light_sleep_cnt++;
+ if (sta->local_ps_mode ==
+ NL80211_MESH_POWER_DEEP_SLEEP)
+ deep_sleep_cnt++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * set non-peer mode to active during peering/scanning/authentication
+ * (see IEEE802.11-2012 13.14.8.3)
+ * the non-peer mesh power mode is deep sleep if the local STA is in
+ * light or deep sleep towards at least one mesh peer (see 13.14.3.1)
+ * otherwise set it to the user-configured default value
+ */
+ if (peering) {
+ mps_dbg(sdata, "setting non-peer PS mode to active until "
+ "peering is complete\n");
+ ifmsh->nonpeer_ps_mode = NL80211_MESH_POWER_ACTIVE;
+ } else if (light_sleep_cnt || deep_sleep_cnt) {
+ mps_dbg(sdata, "setting non-peer PS mode to deep sleep\n");
+ ifmsh->nonpeer_ps_mode = NL80211_MESH_POWER_DEEP_SLEEP;
+ } else {
+ mps_dbg(sdata, "setting non-peer PS mode to user value\n");
+ ifmsh->nonpeer_ps_mode = ifmsh->mshcfg.power_mode;
+ }
+
+ ifmsh->ps_peers_light_sleep = light_sleep_cnt;
+ ifmsh->ps_peers_deep_sleep = deep_sleep_cnt;
+}
+
+/**
+ * ieee80211_set_sta_mesh_local_ps_mode - set local PS mode towards a mesh STA
+ *
+ * @sta: mesh STA
+ * @pm: the power mode to set
+ * @delay: delay in msecs for committing and announcing the new value
+ *
+ * called by cfg80211 and on peer link establishment
+ */
+void ieee80211_set_sta_mesh_local_ps_mode(struct sta_info *sta,
+ enum nl80211_mesh_power_mode pm, u32 delay)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ static const char *modes[] = {
+ [NL80211_MESH_POWER_ACTIVE] = "active",
+ [NL80211_MESH_POWER_LIGHT_SLEEP] = "light sleep",
+ [NL80211_MESH_POWER_DEEP_SLEEP] = "deep sleep",
+ };
+
+ if (delay) {
+ /*
+ * after peering/authentication/scanning it is useful to delay
+ * the transition to a lower power mode to avoid frame losses
+ * also intended for per-link dynamic powersave
+ */
+ sta->local_ps_mode_delayed = pm;
+ mod_timer(&sta->local_ps_mode_timer,
+ jiffies + msecs_to_jiffies(delay));
+ return;
+ }
+
+ mps_dbg(sdata, "local STA operates in %s mode with %pM\n",
+ modes[pm], sta->sta.addr);
+
+ sta->local_ps_mode = pm;
+
+ ieee80211_mesh_local_ps_update(sdata);
+}
+
+void ieee80211_sta_mesh_local_ps_mode_timer(unsigned long data)
+{
+ /*
+ * This STA is valid because free_sta_work() will
+ * del_timer_sync() this timer after having made sure
+ * it cannot be armed (by deleting the plink.)
+ */
+ struct sta_info *sta = (struct sta_info *) data;
+
+ ieee80211_set_sta_mesh_local_ps_mode(sta, sta->local_ps_mode_delayed,
+ 0);
+}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index e9d5768..09e350a 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -131,6 +131,7 @@ static void free_sta_work(struct work_struct *wk)
mesh_accept_plinks_update(sdata);
mesh_plink_deactivate(sta);
del_timer_sync(&sta->plink_timer);
+ del_timer_sync(&sta->local_ps_mode_timer);
}
#endif

@@ -351,6 +352,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_MESH
sta->plink_state = NL80211_PLINK_LISTEN;
init_timer(&sta->plink_timer);
+ setup_timer(&sta->local_ps_mode_timer,
+ ieee80211_sta_mesh_local_ps_mode_timer,
+ (unsigned long) sta);
#endif

return sta;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index c88f161f..046ca70 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -274,6 +274,9 @@ struct sta_ampdu_mlme {
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
* @ch_type: peer's channel type
+ * @local_ps_mode: local link-specific power save mode
+ * @local_ps_mode_delayed: temp. storage for delayed setting of local_ps_mode
+ * @local_ps_mode_timer: timer for delayed setting of local_ps_mode
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -370,6 +373,9 @@ struct sta_info {
s64 t_offset;
s64 t_offset_setpoint;
enum nl80211_channel_type ch_type;
+ enum nl80211_mesh_power_mode local_ps_mode;
+ enum nl80211_mesh_power_mode local_ps_mode_delayed;
+ struct timer_list local_ps_mode_timer;
#endif

#ifdef CONFIG_MAC80211_DEBUGFS
--
1.7.9.5


2012-11-30 11:52:14

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 03/13] mac80211: local link-specific mesh power mode logic

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:

> + /* mesh power save */
> + enum nl80211_mesh_power_mode nonpeer_ps_mode;
> + u8 ps_peers_light_sleep;
> + u8 ps_peers_deep_sleep;

u8? really?


> + ieee80211_set_sta_mesh_local_ps_mode(sta,
> + mshcfg->power_mode, 100);

Maybe this function needs some refactoring if the indentation is this
deep? :)

In any case that looks odd, I think you should either break all
parameters or do the normal lined-up indentation.

> + u8 light_sleep_cnt = 0;
> + u8 deep_sleep_cnt = 0;

Again, u8?

Also in this function: you aren't bothered by the lack of locking at
all, are you? Well I am :-P

> + setup_timer(&sta->local_ps_mode_timer,
> + ieee80211_sta_mesh_local_ps_mode_timer,
> + (unsigned long) sta);

suggest you run checkpatch -- I'm not your whitespace nanny.

johannes


2012-11-28 13:08:23

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 00/13] mesh powersave - basics

Ping mesh folks, any comments?

johannes


2012-11-23 20:19:08

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 01/13] {nl,cfg,mac}80211: set beacon interval and DTIM period on mesh join

Move the default mesh beacon interval and DTIM period to cfg80211 and
make it accessible to nl80211. This enables setting both values when joining
an MBSS.

NOTE: In a mesh with links in PS mode it is not advised to use the default
parameters 1000:4, as these cause excessive buffering delays that exceed
various timeouts (e.g. HWMP path request).

Signed-off-by: Marco Porsch <[email protected]>
---
include/net/cfg80211.h | 4 ++++
net/mac80211/cfg.c | 3 +++
net/mac80211/mesh.c | 1 -
net/mac80211/mesh.h | 2 --
net/wireless/mesh.c | 5 +++++
net/wireless/nl80211.c | 14 ++++++++++++++
6 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index c696457..91ce6234 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -921,6 +921,8 @@ struct mesh_config {
* @is_authenticated: this mesh requires authentication
* @is_secure: this mesh uses security
* @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
+ * @beacon_interval: beacon interval to use
+ * @dtim_period: DTIM period to use
*
* These parameters are fixed when the mesh is created.
*/
@@ -937,6 +939,8 @@ struct mesh_setup {
bool is_authenticated;
bool is_secure;
int mcast_rate[IEEE80211_NUM_BANDS];
+ u16 beacon_interval;
+ u8 dtim_period;
};

/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7669002..d0b7a7e 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1568,6 +1568,9 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
sizeof(setup->mcast_rate));

+ sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
+ sdata->vif.bss_conf.dtim_period = setup->dtim_period;
+
return 0;
}

diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index a350cab..5f3ef91 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -630,7 +630,6 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
ieee80211_queue_work(&local->hw, &sdata->work);
sdata->vif.bss_conf.ht_operation_mode =
ifmsh->mshcfg.ht_opmode;
- sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
ieee80211_get_sdata_band(sdata));
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 7c9215f..7f3a78f 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -191,8 +191,6 @@ struct mesh_rmc {
#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)

-#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */
-
#define MESH_PATH_EXPIRE (600 * HZ)

/* Default maximum number of plinks per interface */
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 966cfc4..c2060d4 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -44,6 +44,9 @@

#define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50

+#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */
+#define MESH_DEFAULT_DTIM_PERIOD 4
+
const struct mesh_config default_mesh_config = {
.dot11MeshRetryTimeout = MESH_RET_T,
.dot11MeshConfirmTimeout = MESH_CONF_T,
@@ -81,6 +84,8 @@ const struct mesh_setup default_mesh_setup = {
.ie = NULL,
.ie_len = 0,
.is_secure = false,
+ .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
+ .dtim_period = MESH_DEFAULT_DTIM_PERIOD,
};

int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8c08578..e99a8ce 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6511,6 +6511,20 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
return -EINVAL;

+ if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+ setup.beacon_interval =
+ nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+ if (setup.beacon_interval < 1 || setup.beacon_interval > 10000)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
+ setup.dtim_period =
+ nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+ if (setup.dtim_period < 1 || setup.dtim_period > 100)
+ return -EINVAL;
+ }
+
if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
/* parse additional setup parameters if given */
err = nl80211_parse_mesh_setup(info, &setup);
--
1.7.9.5


2012-11-30 12:06:55

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 06/13] {cfg,nl}80211: allow userspace to set the local link-specific power mode

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> The local link-specific power mode defines the mesh power mode in which the
> local STA operates towards the peer STA of the corresponding mesh peering.
>
> The idea behind link-specific power modes it that a node in low power mode may
> set a single link to active mode to transport data, without having to notify
> the surrounding peers (+ following routing updates, ...). This commit allows
> userspace to set these local per-link power modes.
>
> Signed-off-by: Marco Porsch <[email protected]>
> Signed-off-by: Ivan Bezyazychnyy <[email protected]>
> Signed-off-by: Mike Krinkin <[email protected]>
> ---
> include/net/cfg80211.h | 2 ++
> include/uapi/linux/nl80211.h | 5 +++++
> net/mac80211/cfg.c | 4 ++++
> net/wireless/nl80211.c | 16 ++++++++++++++++
> 4 files changed, 27 insertions(+)
>
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index cc61b4b..6eb0a6b 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -506,6 +506,7 @@ enum station_parameters_apply_mask {
> * @sta_modify_mask: bitmap indicating which parameters changed
> * (for those that don't have a natural "no change" value),
> * see &enum station_parameters_apply_mask
> + * @local_ps_mode: local link-specific mesh power save mode

"no change when set to unknown" ...

> + if (params->local_ps_mode)

I'd prefer explicitly != UNKNOWN

> @@ -3100,6 +3100,18 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
> params.plink_state =
> nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
>
> + if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
> + enum nl80211_mesh_power_mode pm = nla_get_u8(
> + info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
> +
> + if (pm <= NL80211_MESH_POWER_UNKNOWN ||
> + pm >= __NL80211_MESH_POWER_AFTER_LAST)
> + return -EINVAL;

> NL80211_MESH_POWER_MAX seems much easier to understand

> + params.local_ps_mode =
> + nla_get_u8(info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);

= pm?

Also isn't it allowed to add stations to a mesh interface (SAE daemon)?
Shouldn't it be allowed to be set there as well?

johannes


2012-11-29 02:11:11

by Javier Cardona

[permalink] [raw]
Subject: Re: [RFCv2 00/13] mesh powersave - basics

Johannes,

On Wed, Nov 28, 2012 at 5:08 AM, Johannes Berg
<[email protected]> wrote:
> Ping mesh folks, any comments?

Marco is working in our office until the end of the year and has
regular feedback from the team. Is there anything specific that you
think we should review in more detail?

Cheers,

Javier

--
Javier Cardona
cozybit Inc.
http://www.cozybit.com

2012-11-23 20:19:23

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 08/13] mac80211: add power save support structure to mesh interface

This commit adds the ps_data structure to ieee80211_if_mesh. This structure
contains the TIM map, a counter how of PS neighbors and the actual
group-addressed frame buffer. Functions using this structure are modified
for mesh mode.

Signed-off-by: Marco Porsch <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mesh.c | 7 +++++++
net/mac80211/sta_info.c | 15 +++++++++++++--
net/mac80211/tx.c | 10 ++++++++--
4 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0364844..eec4aac 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -622,6 +622,7 @@ struct ieee80211_if_mesh {
enum nl80211_mesh_power_mode nonpeer_ps_mode;
u8 ps_peers_light_sleep;
u8 ps_peers_deep_sleep;
+ struct ps_data ps;
};

#ifdef CONFIG_MAC80211_MESH
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 5f72739..c61bbc0 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -657,10 +657,16 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
ifmsh->mesh_id_len = 0;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);

+ /* free all potentially still buffered group-addressed frames */
+ local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
+ skb_queue_purge(&ifmsh->ps.bc_buf);
+
/* flush STAs and mpaths on this iface */
sta_info_flush(sdata->local, sdata);
mesh_path_flush_by_iface(sdata);

+ atomic_set(&ifmsh->ps.num_sta_ps, 0); /* not reset by sta_info_flush */
+
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_timer);
@@ -839,4 +845,5 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
INIT_LIST_HEAD(&ifmsh->preq_queue.list);
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
spin_lock_init(&ifmsh->sync_offset_lock);
+ skb_queue_head_init(&ifmsh->ps.bc_buf);
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 09e350a..3fd3a5a 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -111,6 +111,8 @@ static void free_sta_work(struct work_struct *wk)
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
+ else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+ ps = &sdata->u.mesh.ps;
else
return;

@@ -564,6 +566,12 @@ void sta_info_recalc_tim(struct sta_info *sta)

ps = &sta->sdata->bss->ps;
id = sta->sta.aid;
+#ifdef CONFIG_MAC80211_MESH
+ } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+ ps = &sta->sdata->u.mesh.ps;
+ /* TIM map only for PLID <= IEEE80211_MAX_AID */
+ id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
+#endif
} else {
return;
}
@@ -722,8 +730,9 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
bool have_buffered = false;
int ac;

- /* This is only necessary for stations on BSS interfaces */
- if (!sta->sdata->bss)
+ /* This is only necessary for stations on BSS/MBSS interfaces */
+ if (!sta->sdata->bss &&
+ !ieee80211_vif_is_mesh(&sta->sdata->vif))
return false;

for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
@@ -971,6 +980,8 @@ static void clear_sta_ps_flags(void *_sta)
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
+ else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+ ps = &sdata->u.mesh.ps;
else
return;

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 70aa64f..88994dc 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -329,6 +329,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)

if (sdata->vif.type == NL80211_IFTYPE_AP)
ps = &sdata->u.ap.ps;
+ else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+ ps = &sdata->u.mesh.ps;
else
continue;

@@ -372,18 +374,20 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
/*
* broadcast/multicast frame
*
- * If any of the associated stations is in power save mode,
+ * If any of the associated/peer stations is in power save mode,
* the frame is buffered to be sent after DTIM beacon frame.
* This is done either by the hardware or us.
*/

- /* powersaving STAs currently only in AP/VLAN mode */
+ /* powersaving STAs currently only in AP/VLAN/mesh mode */
if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
if (!tx->sdata->bss)
return TX_CONTINUE;

ps = &tx->sdata->bss->ps;
+ } else if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+ ps = &tx->sdata->u.mesh.ps;
} else {
return TX_CONTINUE;
}
@@ -2731,6 +2735,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
goto out;

ps = &sdata->u.ap.ps;
+ } else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+ ps = &sdata->u.mesh.ps;
} else {
goto out;
}
--
1.7.9.5


2012-11-23 20:19:14

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 04/13] mac80211: mesh power mode indication in transmitted frames

According to IEEE802.11-2012 a mesh STA shall indicate the current power mode
in transmitted frames. This differs for individually addressed frames and
group addressed frames as well as whether the frames are addressed to peers
or non-peers.

Indicate the link-specific power mode with the Power Management field in the
Frame Control field and the Mesh Power Save Level field in the QoS Control
field in all individually addressed Mesh Data frames.
Especially indicate a peer-specific mesh power mode transition with an
individually-addressed QoS Null frame to the respective peer.

| PM | PSL| Mesh Power Mode |
+----+----+-----------------+
| 0 |Rsrv| Active |
| 1 | 0 | Light |
| 1 | 1 | Deep |

Indicate the non-peer mesh power mode in frames transmitted to non-peer STA
and in management frames in the Power Management field of the Frame Control
field.
In group addressed Mesh QoS Data frames the Mesh Power Save Level field
in the QoS Control field indicates whether the local STA has any deep
sleep peers.
In beacon (and probe response) frames indicate whether we have any deep sleep
peers, by setting the Mesh Power Save Level field of the Mesh Capability field.

For performance reasons, calls to the function setting the frame flags are
placed in HWMP routing routines, as there the STA pointer is already availible.

Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
Signed-off-by: Max Filippov <[email protected]>
---
include/linux/ieee80211.h | 6 +++
net/mac80211/mesh.c | 3 ++
net/mac80211/mesh.h | 3 ++
net/mac80211/mesh_hwmp.c | 10 ++++
net/mac80211/mesh_pathtbl.c | 1 +
net/mac80211/mesh_ps.c | 111 +++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 3 ++
net/mac80211/tx.c | 18 ++++---
net/mac80211/wme.c | 13 ++++-
9 files changed, 160 insertions(+), 8 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 9a85397..2ba972c 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -149,6 +149,9 @@
/* Mesh Control 802.11s */
#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100

+/* mesh power save level subfield mask */
+#define IEEE80211_QOS_CTL_MESH_PS_LEVEL 0x0200
+
/* U-APSD queue for WMM IEs sent by AP */
#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)
#define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f
@@ -672,11 +675,14 @@ struct ieee80211_meshconf_ie {
* additional mesh peerings with other mesh STAs
* @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
* @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing
+ * @MESHCONF_CAPAB_POWER_SAVE_LEVEL: STA is in deep sleep mode or has
+ * neighbors in deep sleep mode
*/
enum mesh_config_capab_flags {
MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01,
MESHCONF_CAPAB_FORWARDING = 0x08,
MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20,
+ MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40,
};

/**
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index d4e90ba..5f72739 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -272,6 +272,9 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
*pos = MESHCONF_CAPAB_FORWARDING;
*pos |= ifmsh->accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
+ /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
+ *pos |= ifmsh->ps_peers_deep_sleep ?
+ MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 64781dd..49a3114 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -247,6 +247,9 @@ void ieee80211_mesh_local_ps_update(struct ieee80211_sub_if_data *sdata);
void ieee80211_set_sta_mesh_local_ps_mode(struct sta_info *sta,
enum nl80211_mesh_power_mode pm, u32 delay);
void ieee80211_sta_mesh_local_ps_mode_timer(unsigned long data);
+void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_hdr *hdr);

/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 47aeee2..fe1d83c 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -205,6 +205,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;

skb_set_mac_header(skb, 0);
skb_set_network_header(skb, 0);
@@ -216,6 +217,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,

info->control.vif = &sdata->vif;
ieee80211_set_qos_hdr(sdata, skb);
+ ieee80211_set_mesh_ps_flags(sdata, NULL, hdr);
}

/**
@@ -1074,6 +1076,13 @@ int mesh_nexthop_resolve(struct sk_buff *skb,
u8 *target_addr = hdr->addr3;
int err = 0;

+ /*
+ * Nulls are only sent to direct peers for PS and
+ * should already be addressed
+ */
+ if (ieee80211_is_qos_nullfunc(hdr->frame_control))
+ return err;
+
rcu_read_lock();
err = mesh_nexthop_lookup(skb, sdata);
if (!err)
@@ -1145,6 +1154,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
if (next_hop) {
memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
+ ieee80211_set_mesh_ps_flags(sdata, next_hop, hdr);
err = 0;
}

diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index aa74981..a8e4040 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -212,6 +212,7 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
hdr = (struct ieee80211_hdr *) skb->data;
memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN);
+ ieee80211_set_mesh_ps_flags(sta->sdata, sta, hdr);
}

spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
index 14bf65b..0d496fe 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -10,6 +10,60 @@
#include "mesh.h"

/**
+ * ieee80211_mesh_null_get - create pre-addressed QoS Null frame
+ *
+ * Returns the created sk_buff
+ *
+ * @sta: mesh STA
+ */
+static struct sk_buff *ieee80211_mesh_null_get(struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hdr *nullfunc; /* use 4addr header */
+ struct sk_buff *skb;
+ int size = sizeof(*nullfunc);
+ __le16 fc;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2);
+ if (!skb)
+ return NULL;
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ nullfunc = (struct ieee80211_hdr *) skb_put(skb, size);
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
+ ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr,
+ sdata->vif.addr);
+ nullfunc->frame_control = fc;
+ nullfunc->duration_id = 0;
+ /* no address resolution for this frame -> set addr 1 immediately */
+ memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
+ skb_put(skb, 2); /* append QoS control field */
+ ieee80211_set_mesh_ps_flags(sdata, sta, nullfunc);
+
+ return skb;
+}
+
+/**
+ * ieee80211_send_mesh_null - send a QoS Null to peer to indicate power mode
+ *
+ * @sta: mesh STA to inform
+ */
+static void ieee80211_send_mesh_null(struct sta_info *sta)
+{
+ struct sk_buff *skb;
+
+ skb = ieee80211_mesh_null_get(sta);
+ if (!skb)
+ return;
+
+ mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n",
+ sta->sta.addr);
+
+ ieee80211_tx_skb(sta->sdata, skb);
+}
+
+/**
* ieee80211_mesh_local_ps_update - keep track of link-specific PS modes
*
* @sdata: local mesh subif
@@ -107,6 +161,13 @@ void ieee80211_set_sta_mesh_local_ps_mode(struct sta_info *sta,

sta->local_ps_mode = pm;

+ /*
+ * announce peer-specific power mode transition
+ * see IEEE802.11-2012 13.14.3.2 and 13.14.3.3
+ */
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ ieee80211_send_mesh_null(sta);
+
ieee80211_mesh_local_ps_update(sdata);
}

@@ -122,3 +183,53 @@ void ieee80211_sta_mesh_local_ps_mode_timer(unsigned long data)
ieee80211_set_sta_mesh_local_ps_mode(sta, sta->local_ps_mode_delayed,
0);
}
+
+/**
+ * ieee80211_set_mesh_ps_flags - set mesh PS flags in FC (and QoS Control)
+ *
+ * @sdata: local mesh subif
+ * @sta: mesh STA
+ * @hdr: 802.11 frame header
+ *
+ * see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11
+ *
+ * NOTE: sta must be given when an individually-addressed QoS frame header
+ * is handed, for group-addressed and management frames it not used
+ */
+void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ enum nl80211_mesh_power_mode pm;
+ __le16 *qc;
+
+ if (WARN_ON(is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control) &&
+ !sta))
+ return;
+
+ if (is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control) &&
+ sta->plink_state == NL80211_PLINK_ESTAB)
+ pm = sta->local_ps_mode;
+ else
+ pm = sdata->u.mesh.nonpeer_ps_mode;
+
+ if (pm == NL80211_MESH_POWER_ACTIVE)
+ hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM);
+ else
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return;
+
+ qc = (__le16 *) ieee80211_get_qos_ctl(hdr);
+
+ if ((is_unicast_ether_addr(hdr->addr1) &&
+ pm == NL80211_MESH_POWER_DEEP_SLEEP) ||
+ (is_multicast_ether_addr(hdr->addr1) &&
+ sdata->u.mesh.ps_peers_deep_sleep > 0))
+ *qc |= cpu_to_le16(IEEE80211_QOS_CTL_MESH_PS_LEVEL);
+ else
+ *qc &= cpu_to_le16(~IEEE80211_QOS_CTL_MESH_PS_LEVEL);
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 8c1f152..67aa26c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1998,7 +1998,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
if (is_multicast_ether_addr(fwd_hdr->addr1)) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
+ /* update power mode indication when forwarding */
+ ieee80211_set_mesh_ps_flags(sdata, NULL, fwd_hdr);
} else if (!mesh_nexthop_lookup(fwd_skb, sdata)) {
+ /* mesh power mode flags updated in mesh_nexthop_lookup */
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
} else {
/* unable to resolve next hop */
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 6f19818..70aa64f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1472,12 +1472,16 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
hdr = (struct ieee80211_hdr *) skb->data;
info->control.vif = &sdata->vif;

- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- ieee80211_is_data(hdr->frame_control) &&
- !is_multicast_ether_addr(hdr->addr1) &&
- mesh_nexthop_resolve(skb, sdata)) {
- /* skb queued: don't free */
- return;
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ if (ieee80211_is_data(hdr->frame_control) &&
+ is_unicast_ether_addr(hdr->addr1)) {
+ if (mesh_nexthop_resolve(skb, sdata)) {
+ /* skb queued: don't free */
+ return;
+ }
+ } else {
+ ieee80211_set_mesh_ps_flags(sdata, NULL, hdr);
+ }
}

ieee80211_set_qos_hdr(sdata, skb);
@@ -2448,6 +2452,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
eth_broadcast_addr(mgmt->da);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+ ieee80211_set_mesh_ps_flags(sdata, NULL,
+ (struct ieee80211_hdr *) mgmt);
mgmt->u.beacon.beacon_int =
cpu_to_le16(sdata->vif.bss_conf.beacon_int);
mgmt->u.beacon.capab_info |= cpu_to_le16(
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 906f00c..afba19c 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -191,6 +191,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,

/* qos header is 2 bytes */
*p++ = ack_policy | tid;
- *p = ieee80211_vif_is_mesh(&sdata->vif) ?
- (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ /* preserve RSPI and Mesh PS Level bit */
+ *p &= ((IEEE80211_QOS_CTL_RSPI |
+ IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8);
+
+ /* Nulls don't have a mesh header (frame body) */
+ if (!ieee80211_is_qos_nullfunc(hdr->frame_control))
+ *p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8);
+ } else {
+ *p = 0;
+ }
}
--
1.7.9.5


2012-11-23 20:39:00

by Joe Perches

[permalink] [raw]
Subject: Re: [RFCv2 01/13] {nl,cfg,mac}80211: set beacon interval and DTIM period on mesh join

On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> Move the default mesh beacon interval and DTIM period to cfg80211 and
> make it accessible to nl80211. This enables setting both values when joining
> an MBSS.

Trivial notes for a couple of patches

> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
[]
> @@ -937,6 +939,8 @@ struct mesh_setup {
> bool is_authenticated;
> bool is_secure;
> int mcast_rate[IEEE80211_NUM_BANDS];
> + u16 beacon_interval;
> + u8 dtim_period;

It might be better to move the new u8 after the last bool
to avoid unnecessary padding.

bool is_secure;
u8 dtim_period;
u16 beacon_interval;
int mcast_rate[IEEE80211_NUM_BANDS];

and for patch 3/13:

diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
[]
> +void ieee80211_mesh_local_ps_update(struct ieee80211_sub_if_data *sdata)
> +{
[]
> + list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
[]
> + case NL80211_PLINK_ESTAB:
> + if (sta->local_ps_mode ==
> + NL80211_MESH_POWER_LIGHT_SLEEP)
> + light_sleep_cnt++;
> + if (sta->local_ps_mode ==
> + NL80211_MESH_POWER_DEEP_SLEEP)

else if

> + deep_sleep_cnt++;
> + break;



2012-11-23 20:19:25

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 09/13] mac80211: enable frame buffering for PS STA

In case of a neighbor changing its power mode or peering status, call
ieee80211_mesh_sta_ps_update to set or clear the WLAN_STA_PS flag. This
triggers the frame buffering routines for individually-addressed frames in the
tx path used for client mode, which are re-used here.
Also the num_sta_ps counter is updated, which will trigger the buffering
and release of group-addressed frames after DTIM beacons.

Until drivers are adapted for mesh power save, do not inform the driver about
the neighbor STA being in PS mode towards us (as sta_ps_start would do).

Signed-off-by: Marco Porsch <[email protected]>
---
net/mac80211/mesh.h | 1 +
net/mac80211/mesh_plink.c | 3 +++
net/mac80211/mesh_ps.c | 42 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+)

diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 2b6aee7..62ca646 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -250,6 +250,7 @@ void ieee80211_sta_mesh_local_ps_mode_timer(unsigned long data);
void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_hdr *hdr);
+void ieee80211_mesh_sta_ps_update(struct sta_info *sta);
void ieee80211_set_peer_ps_mode(struct sta_info *sta,
struct ieee80211_hdr *hdr);
void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 19994e7..0572346 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -179,6 +179,7 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);

+ ieee80211_mesh_sta_ps_update(sta);
ieee80211_mesh_local_ps_update(sdata);

return changed;
@@ -878,6 +879,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
sta->sta.addr);

+ ieee80211_mesh_sta_ps_update(sta);
ieee80211_set_sta_mesh_local_ps_mode(sta,
mshcfg->power_mode, 100);
break;
@@ -918,6 +920,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_CONFIRM,
sta->sta.addr, llid, plid, 0);
+ ieee80211_mesh_sta_ps_update(sta);
/*
* we need some delay here, otherwise
* the announcement Null would arrive before the CONFIRM
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
index 2d00aea..ee72f3e 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -235,6 +235,44 @@ void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
}

/**
+ * ieee80211_mesh_sta_ps_update - update the buffering status for neighbor STA
+ *
+ * @sta: mesh STA
+ *
+ * called after change of peering status or non-peer/peer-specific power mode
+ */
+void ieee80211_mesh_sta_ps_update(struct sta_info *sta)
+{
+ enum nl80211_mesh_power_mode pm;
+ bool do_buffer;
+
+ /*
+ * use peer-specific power mode if peering is established and
+ * the peer's power mode is known
+ */
+ if (sta->plink_state == NL80211_PLINK_ESTAB &&
+ sta->peer_ps_mode != NL80211_MESH_POWER_UNKNOWN)
+ pm = sta->peer_ps_mode;
+ else
+ pm = sta->nonpeer_ps_mode;
+
+ do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
+
+ /* Don't let the same PS state be set twice */
+ if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
+ return;
+
+ if (do_buffer) {
+ set_sta_flag(sta, WLAN_STA_PS_STA);
+ atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps);
+ mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n",
+ sta->sta.addr);
+ } else {
+ ieee80211_sta_ps_deliver_wakeup(sta);
+ }
+}
+
+/**
* ieee80211_set_peer_ps_mode - track the link-specific power mode of peers
*
* @sta: STA info to update
@@ -275,6 +313,8 @@ void ieee80211_set_peer_ps_mode(struct sta_info *sta,
sta->sta.addr, pm);

sta->peer_ps_mode = pm;
+
+ ieee80211_mesh_sta_ps_update(sta);
}

/**
@@ -300,4 +340,6 @@ void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
sta->sta.addr, pm);

sta->nonpeer_ps_mode = pm;
+
+ ieee80211_mesh_sta_ps_update(sta);
}
--
1.7.9.5


2012-11-23 20:19:19

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 06/13] {cfg,nl}80211: allow userspace to set the local link-specific power mode

The local link-specific power mode defines the mesh power mode in which the
local STA operates towards the peer STA of the corresponding mesh peering.

The idea behind link-specific power modes it that a node in low power mode may
set a single link to active mode to transport data, without having to notify
the surrounding peers (+ following routing updates, ...). This commit allows
userspace to set these local per-link power modes.

Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
---
include/net/cfg80211.h | 2 ++
include/uapi/linux/nl80211.h | 5 +++++
net/mac80211/cfg.c | 4 ++++
net/wireless/nl80211.c | 16 ++++++++++++++++
4 files changed, 27 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index cc61b4b..6eb0a6b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -506,6 +506,7 @@ enum station_parameters_apply_mask {
* @sta_modify_mask: bitmap indicating which parameters changed
* (for those that don't have a natural "no change" value),
* see &enum station_parameters_apply_mask
+ * @local_ps_mode: local link-specific mesh power save mode
*/
struct station_parameters {
u8 *supported_rates;
@@ -521,6 +522,7 @@ struct station_parameters {
struct ieee80211_vht_cap *vht_capa;
u8 uapsd_queues;
u8 max_sp;
+ enum nl80211_mesh_power_mode local_ps_mode;
};

/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d0efa1e..bd54f21 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1281,6 +1281,9 @@ enum nl80211_commands {
*
* @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
*
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ * defined in &enum nl80211_mesh_power_mode.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1544,6 +1547,8 @@ enum nl80211_attrs {

NL80211_ATTR_SCAN_FLAGS,

+ NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 62564e9..2ed111f 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1184,6 +1184,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
mesh_plink_block(sta);
break;
}
+
+ if (params->local_ps_mode)
+ ieee80211_set_sta_mesh_local_ps_mode(sta,
+ params->local_ps_mode, 0);
#endif
}

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 3e2aa63..8749d3c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3100,6 +3100,18 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_state =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);

+ if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
+ enum nl80211_mesh_power_mode pm = nla_get_u8(
+ info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
+
+ if (pm <= NL80211_MESH_POWER_UNKNOWN ||
+ pm >= __NL80211_MESH_POWER_AFTER_LAST)
+ return -EINVAL;
+
+ params.local_ps_mode =
+ nla_get_u8(info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
+ }
+
switch (dev->ieee80211_ptr->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
@@ -3107,6 +3119,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
/* disallow mesh-specific things */
if (params.plink_action)
return -EINVAL;
+ if (params.local_ps_mode)
+ err = -EINVAL;

/* TDLS can't be set, ... */
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
@@ -3149,6 +3163,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
if (params.listen_interval >= 0)
return -EINVAL;
+ if (params.local_ps_mode)
+ err = -EINVAL;
/* reject any changes other than AUTHORIZED */
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
return -EINVAL;
--
1.7.9.5


2012-11-23 20:19:28

by Marco Porsch

[permalink] [raw]
Subject: [RFCv2 11/13] mac80211: add awake window IE to mesh beacons

The awake window value is announced in an IE in beacons if the local STA
is in light or deep sleep mode towards any peer or non-peer. On receipt STA
parse the IE.

Signed-off-by: Marco Porsch <[email protected]>
---
net/mac80211/cfg.c | 5 +++++
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mesh.c | 23 +++++++++++++++++++++++
net/mac80211/mesh.h | 2 ++
net/mac80211/tx.c | 4 +++-
net/mac80211/util.c | 4 ++++
6 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index db27208..e49eb08 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1683,6 +1683,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
conf->power_mode = nconf->power_mode;
ieee80211_mesh_local_ps_update(sdata);
}
+ if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) {
+ conf->dot11MeshAwakeWindowDuration =
+ nconf->dot11MeshAwakeWindowDuration;
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+ }
return 0;
}

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index eec4aac..3d4c43f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1204,6 +1204,7 @@ struct ieee802_11_elems {
struct ieee80211_meshconf_ie *mesh_config;
u8 *mesh_id;
u8 *peering;
+ u8 *awake_window;
u8 *preq;
u8 *prep;
u8 *perr;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index c61bbc0..ad5dca7 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -301,6 +301,29 @@ mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
}

int
+mesh_add_awake_window_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ u8 *pos;
+
+ /* see IEEE802.11-2012 13.14.6 */
+ if (ifmsh->ps_peers_light_sleep == 0 &&
+ ifmsh->ps_peers_deep_sleep == 0 &&
+ ifmsh->nonpeer_ps_mode == NL80211_MESH_POWER_ACTIVE)
+ return 0;
+
+ if (skb_tailroom(skb) < 4)
+ return -ENOMEM;
+
+ pos = skb_put(skb, 2 + 2);
+ *pos++ = WLAN_EID_MESH_AWAKE_WINDOW;
+ *pos++ = 2;
+ put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos);
+
+ return 0;
+}
+
+int
mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 62ca646..f7bf9e3 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -222,6 +222,8 @@ int mesh_add_meshid_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_rsn_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
+int mesh_add_awake_window_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata);
int mesh_add_vendor_ies(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_ds_params_ie(struct sk_buff *skb,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 88994dc..1194020 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2444,7 +2444,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2 + sizeof(struct ieee80211_ht_operation) +
2 + sdata->u.mesh.mesh_id_len +
2 + sizeof(struct ieee80211_meshconf_ie) +
- sdata->u.mesh.ie_len);
+ sdata->u.mesh.ie_len +
+ 2 + sizeof(__le16)); /* awake window */
if (!skb)
goto out;

@@ -2477,6 +2478,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
mesh_add_ht_oper_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata) ||
+ mesh_add_awake_window_ie(skb, sdata) ||
mesh_add_vendor_ies(skb, sdata)) {
pr_err("o11s: couldn't add ies!\n");
goto out;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index acbb8c9..390d1a6 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -795,6 +795,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
elems->peering = pos;
elems->peering_len = elen;
break;
+ case WLAN_EID_MESH_AWAKE_WINDOW:
+ if (elen >= 2)
+ elems->awake_window = pos;
+ break;
case WLAN_EID_PREQ:
elems->preq = pos;
elems->preq_len = elen;
--
1.7.9.5


2012-12-03 11:03:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFCv2 08/13] mac80211: add power save support structure to mesh interface

On Fri, 2012-11-30 at 09:06 -0800, Marco Porsch wrote:
> Thanks again for all your comments!
>
> On 11/30/2012 04:09 AM, Johannes Berg wrote:
> > On Fri, 2012-11-23 at 12:18 -0800, Marco Porsch wrote:
> >
> >> +++ b/net/mac80211/mesh.c
> >> @@ -657,10 +657,16 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
> >> ifmsh->mesh_id_len = 0;
> >> ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
> >>
> >> + /* free all potentially still buffered group-addressed frames */
> >> + local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
> >> + skb_queue_purge(&ifmsh->ps.bc_buf);
> >> +
> >> /* flush STAs and mpaths on this iface */
> >> sta_info_flush(sdata->local, sdata);
> >> mesh_path_flush_by_iface(sdata);
> >
> > the queue purge would seem to be in the wrong spot
>
> Where would you recommend to put it then?
> My idea was that after the (DTIM) beacon is disabled these buffered
> broadcasts are stuck either way, so they can be purged.

But if the stations aren't all deleted yet, new frames might be added.

johannes