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:
4: maintaining a local power mode for each peer + one non-peer power mode
5: indicate the power mode towards the respective neighbors
6: track neighbors' power modes
10: buffer frames towards power saving neighbors
14: release buffered frames in coordination with neighbor
Marco Porsch (14):
{nl,cfg,mac}80211: add beacon interval and DTIM period to mesh config
{cfg,nl}80211: mesh power mode config parameters
{cfg,nl}80211: set and get default mesh power mode
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: set local link-specific power mode
{cfg,nl}80211: get 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 | 14 +
include/net/cfg80211.h | 24 ++
include/uapi/linux/nl80211.h | 55 ++++
net/mac80211/Kconfig | 11 +
net/mac80211/Makefile | 3 +-
net/mac80211/cfg.c | 33 ++-
net/mac80211/debug.h | 10 +
net/mac80211/debugfs_netdev.c | 9 +
net/mac80211/debugfs_sta.c | 5 +-
net/mac80211/ieee80211_i.h | 7 +
net/mac80211/iface.c | 3 +
net/mac80211/mesh.c | 34 ++-
net/mac80211/mesh.h | 26 +-
net/mac80211/mesh_hwmp.c | 10 +
net/mac80211/mesh_pathtbl.c | 1 +
net/mac80211/mesh_plink.c | 39 +++
net/mac80211/mesh_ps.c | 578 +++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 32 ++-
net/mac80211/sta_info.c | 28 +-
net/mac80211/sta_info.h | 18 ++
net/mac80211/status.c | 5 +
net/mac80211/tx.c | 62 ++++-
net/mac80211/util.c | 4 +
net/mac80211/wme.c | 15 +-
net/wireless/mesh.c | 8 +
net/wireless/nl80211.c | 46 +++-
26 files changed, 1054 insertions(+), 26 deletions(-)
create mode 100644 net/mac80211/mesh_ps.c
--
1.7.9.5
Create a TIM IE similarly to AP mode and add it to mesh beacons.
Use the peer's link ID instead of the access point's association ID.
Since the TIM map only supports announcing buffered frames for
IEEE80211_MAX_AID STA (2007), we mod down our mesh LLID/PLID to fit.
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 a62deaf..bfcda52 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2333,6 +2333,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)
@@ -2440,6 +2465,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) +
@@ -2473,6 +2499,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
On 11/26/2012 02:45 AM, Johannes Berg wrote:
> On Tue, 2012-11-20 at 10:11 -0800, Marco Porsch wrote:
>
>>> This is ... strange? Can a single station really own *two* num_psp
>>> refcounts?
>>
>> Yes it can. A station can be both owner and recipient. And it would just
>> be overhead to distinguish between num_psp_owner and num_psp_recipient,
>> when in the end we only want to know if there is any PSP ongoing at all.
>>
>> I'll change the comment to:
>> /* number of active PSPs (owner and recipient counted independently) */
>> atomic_t num_psp;
>
> Ok.
>
>>>> + nullfunc = (struct ieee80211_hdr *) skb->data;
>>>> + if (!eosp)
>>>> + nullfunc->frame_control |=
>>>> + cpu_to_le16(IEEE80211_FCTL_MOREDATA);
>>>
>>> This seems wrong -- EOSP and moredata are orthogonal (with the
>>> restriction that "!EOSP => moredata") -- but if you just have that in
>>> the code the moredata bit won't always be set correctly.
>>
>> Imho, in the context of PSP trigger frames it does.
>> Sending a trigger frame to a mesh PS STA with no EOSP implies the start
>> of a PSP with the sender as owner -> following data. The other two
>> combinations imply that there is no more data following in that direction.
>
> The EOSP bit in a trigger frame should always be 0 unless the frame is
> also a PSP response, no?
No. That is one weird thing about the standard: the combination
RSPI:EOSP 0:0 is seen as a MPSP trigger frame when sent towards a
station in PS mode towards the sender. So whenever a frame should be
sent in a non-PSP context it has to set the EOSP flag.
Another situation that sets the EOSP flag is a kind of MPSP poll: if one
STA indicates buffered frames, the other STA can poll it with RSPI:EOSP 1:1.
> What you seem to be missing though is the case when there _is_ more
> data, but the service period has to end nonetheless, say because it was
> limited to a few packets? Nothing here seems to indicate that an MPSP
> ends only after all queued packets are transmitted, which would be a
> requirement if this was supposed to be correct.
MPSPs themselves are not defined in length by the standard.
My current mechanism always sends all packets that are buffered at the
start of the MPSP. At the time the MPSP starts, the last
to-be-transmitted frame is the last buffered frame.
Of course, by the time the MPSP ends, new frames may have been buffered
in the meantime, which are not taken into account here. That would
require additional feedback from ieee80211_tx_status which always seems
a bit racy to me.
(In an earlier version I had something like a feedback loop with
ieee80211_tx_status, where the 'last' frame of a MPSP would always
trigger a re-check of the PS buffers. That allowed to have infinite
length MPSPs as long as data is enqueued for transmission. But that
seemed to be overly complex when something like dynamic PS state
switching is possible and advised by the standard.)
But I see one situation where the More Data flag is indeed not set
correctly. That is the case where I only send a single frame to a PS STA
in a non-MPSP context when no peering is established yet. I'll fix that.
> (Btw, maybe it would be worthwhile to call all of this "MPSP" like the
> spec, not just "PSP"?)
You are right, the standard always says MPSP. Ok, I'll adopt that.
>
>> But now that you mention it... is there any interest in having that
>> function used for uAPSD? Because ieee80211_sta_ps_deliver_response sets
>> the EOSP flag during uAPSD, but does not enforce a QoS Data frame to
>> carry it. But maybe uAPSD just permits transmitting anything else than
>> QoS Data frames...
>
> Well, not really, but non-QoS frames won't happen in that case, because
> the peer will have QoS enabled. Similarly here I think, why would there
> ever be a non-QoS frame? But maybe this can happen with forwarding,
> which can't happen in the non-mesh case.
For mesh also the HWMP routing (Management) frames are transmitted in
MPSPs. So a valid PSP would look like that:
1) QoS Null (RSPI:EOSP 0:0)
2) Mgmt frame (e.g. HWMP Path Reply)
3) QoS Null (RSPI:EOSP 0:1)
here the additional trailing Null is needed to end the MPSP. That
scenario happens regularly when no path has been set up previously.
>>>
>>>> + ieee80211_sta_ps_deliver_response(sta, 1, 0,
>>>> + IEEE80211_FRAME_RELEASE_UAPSD);
>>>
>>> uAPSD?
>>>
>>> The standard *explicitly* states that ASPD is *not* supported in mesh.
>>
>> Absolutely correct. The PSP mechanism is just very similar to uAPSD,
>> though. So once the PSP is set up, the mechanisms are the same actually.
>> What do you advise? Renaming the release reason? Creating a different
>> one that is handled equally?
>
> Well so far the more-data bit seems to be handled different, although I
> argue above that you're actually not doing that correctly ;-)
>
> But I think doing different reasons could be helpful, if only to
> understand the code better.
In the last iteration of my RFC I created an own function similar to
ieee80211_sta_ps_deliver_response. That spares adding even more
complexity to ieee80211_sta_ps_deliver_response and allows me to keep
the mesh PA code separated in mesh_ps.c. I don't seem to need the
release reason then.
--Marco
Hi,
> Sorry, I don't get that one. According to IEEE80211-2012 8.4.2.100 Mesh
> Configuration element and 8.4.2.100.8 Mesh Capability, the capability
> field is just u8.
> It is currently written as follows:
>
> mesh.c : mesh_add_meshconf_ie
>
> /* Mesh capability */
> *pos = MESHCONF_CAPAB_FORWARDING;
> *pos |= ifmsh->accepting_plinks ?
> MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
> *pos++ |= ifmsh->adjusting_tbtt ?
> MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
>
> Should I change anything? Just move the enum?
Err, I misinterpreted the code, sorry. I would appreciate if you moved
it to ieee80211.h with all the other protocol definitions.
johannes
Possible values for the mesh power mode are presented in the
nl80211_mesh_power_mode enumeration. These modes are active,
light sleep and deep sleep.
power_mode has been added to the mesh config structure to hold the
user-configured default mesh power mode which will be used for new
peer links.
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 | 29 +++++++++++++++++++++++++++++
net/wireless/mesh.c | 1 +
3 files changed, 33 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 4c6668f..9488782 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -880,6 +880,8 @@ struct bss_parameters {
* a PREQ element for root path confirmation.
* @beacon_interval: beacon interval to use
* @dtim_period: DTIM period to use
+ * @power_mode: The default mesh power save mode which will be the initial
+ * setting for new peer links.
*/
struct mesh_config {
u16 dot11MeshRetryTimeout;
@@ -909,6 +911,7 @@ struct mesh_config {
u16 dot11MeshHWMPconfirmationInterval;
u16 beacon_interval;
u8 dtim_period;
+ enum nl80211_mesh_power_mode power_mode;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 3306e79..ee80ffc 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2203,6 +2203,35 @@ enum nl80211_mntr_flags {
};
/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @__NL80211_MESH_POWER_INVALID: internal use
+ *
+ * @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_INVALID,
+
+ 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
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index e461502..1c7fab6 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -74,6 +74,7 @@ const struct mesh_config default_mesh_config = {
.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
.beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
.dtim_period = MESH_DEFAULT_DTIM_PERIOD,
+ .power_mode = NL80211_MESH_POWER_ACTIVE,
};
const struct mesh_setup default_mesh_setup = {
--
1.7.9.5
On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
> +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);
This is ... strange? Can a single station really own *two* num_psp
refcounts?
> + nullfunc = (struct ieee80211_hdr *) skb->data;
> + if (!eosp)
> + nullfunc->frame_control |=
> + cpu_to_le16(IEEE80211_FCTL_MOREDATA);
This seems wrong -- EOSP and moredata are orthogonal (with the
restriction that "!EOSP => moredata") -- but if you just have that in
the code the moredata bit won't always be set correctly.
> + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
> + drv_allow_buffered_frames(sdata->local, sta, BIT(7), 1,
> + IEEE80211_FRAME_RELEASE_UAPSD, !eosp);
ditto, passing !eosp definitely seems wrong
> +/**
> + * ieee80211_qos_null_append - append QoS Null as PSP trigger (if necessary)
append? where? why not static?
> + ieee80211_sta_ps_deliver_response(sta, 1, 0,
> + IEEE80211_FRAME_RELEASE_UAPSD);
uAPSD?
The standard *explicitly* states that ASPD is *not* supported in mesh.
Ok I don't really get this, need more time I guess .. also it seems
really hacked together.
johannes
On Tue, 2012-11-20 at 15:53 -0800, Marco Porsch wrote:
> > If we know the interface type is AP/AP_VLAN, we can still get the AP
> > sdata from that pointer:
> > container_of(sdata->ps, struct ieee80211_sub_if_sdata, u.ap.ps);
> >
> > This is only in a few places, and might keep all the RX/TX code simpler?
>
> When counting, I found twice as many lines that would need the
> workaround, as the ones that would benefit. Also I don't really have a
> clue on the AP/VLAN code.
Heh ok, fair enough. I did see a number of changes that would need the
other way, but I guess I thought that then the fastpath would be better.
Anyway, we can change that later.
johannes
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.
A mesh STA shall indicate its 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.
| PM | PSL| Mesh Power Mode |
+----+----+-----------------+
| 0 |Rsrv| Active |
| 1 | 0 | Light |
| 1 | 1 | Deep |
A peer-specific mesh power mode transition is indicated by an
individually addressed QoS Null frame to the respective peer.
In frames transmitted to non-peer STA and in management frames the
non-peer mesh power mode is indicated in the Power Management field
in 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 mesh STAs shall indicate whether they
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 | 3 ++
net/mac80211/mesh.c | 3 ++
net/mac80211/mesh.h | 4 ++
net/mac80211/mesh_hwmp.c | 10 ++++
net/mac80211/mesh_pathtbl.c | 1 +
net/mac80211/mesh_ps.c | 110 +++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 3 ++
net/mac80211/tx.c | 18 ++++---
net/mac80211/wme.c | 15 +++++-
9 files changed, 159 insertions(+), 8 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 85764a9..537edf3 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
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 145d9d2..d4f2021 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 405c6b2..a13549e 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -30,6 +30,7 @@ enum mesh_config_capab_flags {
MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
MESHCONF_CAPAB_FORWARDING = BIT(3),
MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
+ MESHCONF_CAPAB_POWER_SAVE_LEVEL = BIT(6),
};
/**
@@ -260,6 +261,9 @@ const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
void ieee80211_local_ps_update(struct ieee80211_sub_if_data *sdata);
void ieee80211_set_local_ps_mode(struct sta_info *sta,
enum nl80211_mesh_power_mode pm, u32 delay);
+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 45a3500..4b7d2ef 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_local_ps_update - keep track of link-specific PS modes
*
* @sdata: local mesh subif
@@ -120,5 +174,61 @@ void ieee80211_set_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_local_ps_update(sdata);
}
+
+/**
+ * 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;
+
+ BUG_ON(is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control) &&
+ !sta);
+
+ 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 de8a59b..5c38532 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);
@@ -2449,6 +2453,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 cea06e9..4938bb1 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -184,7 +184,18 @@ 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)) {
+ *p = 0;
+ return;
+ }
+
+ /* 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);
}
}
--
1.7.9.5
On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
> Create a TIM IE similarly to AP mode and add it to mesh beacons.
> Use the peer's link ID instead of the access point's association ID.
>
> Since the TIM map only supports announcing buffered frames for
> IEEE80211_MAX_AID STA (2007), we mod down our mesh LLID/PLID to fit.
This comment seems misplaced, no such thing is happening in this patch?
johannes
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
> + if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE])
> + params.local_ps_mode =
> + nla_get_u8(info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
As usual, missing range checks.
However I don't get this entire patch? Doesn't the station control its
own link PS mode?
What happens if userspace uses this on a kernel that doesn't support it?
johannes
On Tue, 2012-11-20 at 10:11 -0800, Marco Porsch wrote:
> > This is ... strange? Can a single station really own *two* num_psp
> > refcounts?
>
> Yes it can. A station can be both owner and recipient. And it would just
> be overhead to distinguish between num_psp_owner and num_psp_recipient,
> when in the end we only want to know if there is any PSP ongoing at all.
>
> I'll change the comment to:
> /* number of active PSPs (owner and recipient counted independently) */
> atomic_t num_psp;
Ok.
> >> + nullfunc = (struct ieee80211_hdr *) skb->data;
> >> + if (!eosp)
> >> + nullfunc->frame_control |=
> >> + cpu_to_le16(IEEE80211_FCTL_MOREDATA);
> >
> > This seems wrong -- EOSP and moredata are orthogonal (with the
> > restriction that "!EOSP => moredata") -- but if you just have that in
> > the code the moredata bit won't always be set correctly.
>
> Imho, in the context of PSP trigger frames it does.
> Sending a trigger frame to a mesh PS STA with no EOSP implies the start
> of a PSP with the sender as owner -> following data. The other two
> combinations imply that there is no more data following in that direction.
The EOSP bit in a trigger frame should always be 0 unless the frame is
also a PSP response, no?
What you seem to be missing though is the case when there _is_ more
data, but the service period has to end nonetheless, say because it was
limited to a few packets? Nothing here seems to indicate that an MPSP
ends only after all queued packets are transmitted, which would be a
requirement if this was supposed to be correct.
(Btw, maybe it would be worthwhile to call all of this "MPSP" like the
spec, not just "PSP"?)
> But now that you mention it... is there any interest in having that
> function used for uAPSD? Because ieee80211_sta_ps_deliver_response sets
> the EOSP flag during uAPSD, but does not enforce a QoS Data frame to
> carry it. But maybe uAPSD just permits transmitting anything else than
> QoS Data frames...
Well, not really, but non-QoS frames won't happen in that case, because
the peer will have QoS enabled. Similarly here I think, why would there
ever be a non-QoS frame? But maybe this can happen with forwarding,
which can't happen in the non-mesh case.
> >
> >> + ieee80211_sta_ps_deliver_response(sta, 1, 0,
> >> + IEEE80211_FRAME_RELEASE_UAPSD);
> >
> > uAPSD?
> >
> > The standard *explicitly* states that ASPD is *not* supported in mesh.
>
> Absolutely correct. The PSP mechanism is just very similar to uAPSD,
> though. So once the PSP is set up, the mechanisms are the same actually.
> What do you advise? Renaming the release reason? Creating a different
> one that is handled equally?
Well so far the more-data bit seems to be handled different, although I
argue above that you're actually not doing that correctly ;-)
But I think doing different reasons could be helpful, if only to
understand the code better.
johannes
This and the next patch should be combind, I think.
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
> Possible values for the mesh power mode are presented in the
> nl80211_mesh_power_mode enumeration. These modes are active,
> light sleep and deep sleep.
>
> power_mode has been added to the mesh config structure to hold the
> user-configured default mesh power mode which will be used for new
> peer links.
Please use active voice for commit logs, e.g. "Add power_mode to the
mesh config structure ..."; we typically read the commit log *before*
the change.
> /**
> + * enum nl80211_mesh_power_mode - mesh power save modes
> + *
> + * @__NL80211_MESH_POWER_INVALID: internal use
There's no need for invalid in something that's used as an enum, enum
*values* can be zero.
johannes
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.
The peer_ps_mode field has been added to the sta_info structure to
represent the peer's link-specific power mode towards the local station.
The nonpeer_ps_mode field has been added to represent the peer's power
mode towards all non-peer stations.
The peer's 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 | 79 +++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 18 +++++++++++
net/mac80211/sta_info.h | 4 +++
5 files changed, 114 insertions(+)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 537edf3..1d8e69a 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 int 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 a13549e..0e80ea7 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -264,6 +264,10 @@ void ieee80211_set_local_ps_mode(struct sta_info *sta,
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 4b7d2ef..2d147d2 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -232,3 +232,82 @@ 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 neighbor mesh STA's peer-specific
+ * power mode towards the local STA
+ *
+ * @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);
+ static const char *modes[] = {
+ [NL80211_MESH_POWER_ACTIVE] = "active",
+ [NL80211_MESH_POWER_LIGHT_SLEEP] = "light sleep",
+ [NL80211_MESH_POWER_DEEP_SLEEP] = "deep sleep",
+ };
+
+ 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 %s mode\n",
+ sta->sta.addr, modes[pm]);
+
+ sta->peer_ps_mode = pm;
+}
+
+/**
+ * ieee80211_set_nonpeer_ps_mode - track the neighbor mesh STA's
+ * power mode towards non-peer STA
+ *
+ * @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;
+ static const char *modes[] = {
+ [NL80211_MESH_POWER_ACTIVE] = "active",
+ [NL80211_MESH_POWER_DEEP_SLEEP] = "deep sleep",
+ };
+
+ 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 %s\n",
+ sta->sta.addr, modes[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
In case of a neighbor changing its power mode or peering status,
ieee80211_sta_ps_transition is called, which sets or clears 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.
Signed-off-by: Marco Porsch <[email protected]>
---
net/mac80211/mesh.h | 1 +
net/mac80211/mesh_plink.c | 3 +++
net/mac80211/mesh_ps.c | 34 ++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 6 ++++--
4 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 0e80ea7..a9406b6 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -264,6 +264,7 @@ void ieee80211_set_local_ps_mode(struct sta_info *sta,
void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_hdr *hdr);
+void ieee80211_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 795f8c7..74d4da5 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -186,6 +186,7 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
+ ieee80211_sta_ps_update(sta);
ieee80211_local_ps_update(sdata);
return changed;
@@ -881,6 +882,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_sta_ps_update(sta);
ieee80211_set_local_ps_mode(sta,
default_ps_mode(sdata),
100);
@@ -922,6 +924,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_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 2d147d2..e98d80c 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -234,6 +234,36 @@ void ieee80211_set_mesh_ps_flags(struct ieee80211_sub_if_data *sdata,
}
/**
+ * ieee80211_sta_ps_update - update the buffering status for a neighbor STA
+ * after peering change or non-peer/peer-specific power mode change
+ *
+ * @sta: mesh STA
+ */
+void ieee80211_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_ACTIVE ||
+ sta->peer_ps_mode == NL80211_MESH_POWER_LIGHT_SLEEP ||
+ sta->peer_ps_mode == NL80211_MESH_POWER_DEEP_SLEEP))
+ pm = sta->peer_ps_mode;
+ else
+ pm = sta->nonpeer_ps_mode;
+
+ do_buffer = (pm == NL80211_MESH_POWER_LIGHT_SLEEP ||
+ pm == NL80211_MESH_POWER_DEEP_SLEEP);
+
+ /* enable frame buffering for powersave STA */
+ ieee80211_sta_ps_transition_ni(&sta->sta, do_buffer);
+}
+
+/**
* ieee80211_set_peer_ps_mode - track the neighbor mesh STA's peer-specific
* power mode towards the local STA
*
@@ -280,6 +310,8 @@ void ieee80211_set_peer_ps_mode(struct sta_info *sta,
sta->sta.addr, modes[pm]);
sta->peer_ps_mode = pm;
+
+ ieee80211_sta_ps_update(sta);
}
/**
@@ -310,4 +342,6 @@ void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
sta->sta.addr, modes[pm]);
sta->nonpeer_ps_mode = pm;
+
+ ieee80211_sta_ps_update(sta);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 741810a..d56889f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1164,7 +1164,8 @@ static void sta_ps_start(struct sta_info *sta)
atomic_inc(&ps->num_sta_ps);
set_sta_flag(sta, WLAN_STA_PS_STA);
- if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
+ if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS) &&
+ !ieee80211_vif_is_mesh(&sta->sdata->vif))
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
ps_dbg(sdata, "STA %pM aid %d enters power save mode\n",
sta->sta.addr, sta->sta.aid);
@@ -1189,7 +1190,8 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
struct sta_info *sta_inf = container_of(sta, struct sta_info, sta);
bool in_ps;
- WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS));
+ WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS) &&
+ !ieee80211_vif_is_mesh(&sta_inf->sdata->vif));
/* Don't let the same PS state be set twice */
in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA);
--
1.7.9.5
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/iface.c | 3 +++
net/mac80211/mesh.c | 3 +++
net/mac80211/rx.c | 2 ++
net/mac80211/sta_info.c | 13 +++++++++++--
net/mac80211/tx.c | 10 ++++++++--
6 files changed, 28 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/iface.c b/net/mac80211/iface.c
index bc3e3e1..8396602 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -769,6 +769,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* free all potentially still buffered bcast frames */
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
+ } else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+ local->total_ps_buffered -= skb_queue_len(&sdata->u.mesh.ps.bc_buf);
+ skb_queue_purge(&sdata->u.mesh.ps.bc_buf);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata);
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index d4f2021..86f076e 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -639,6 +639,9 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
ieee80211_mandatory_rates(sdata->local,
ieee80211_get_sdata_band(sdata));
ieee80211_local_ps_update(sdata);
+ /* reset in case we have been in a mesh with PS peers before */
+ atomic_set(&sdata->u.mesh.ps.num_sta_ps, 0);
+ skb_queue_head_init(&ifmsh->ps.bc_buf);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_HT |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 90f2ce5..741810a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1157,6 +1157,8 @@ static void sta_ps_start(struct sta_info *sta)
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;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f8513a7..4c312ed 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;
@@ -562,6 +564,10 @@ void sta_info_recalc_tim(struct sta_info *sta)
ps = &sta->sdata->bss->ps;
id = sta->sta.aid;
+ } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+ ps = &sta->sdata->u.mesh.ps;
+ /* TIM map only for AID <= IEEE80211_MAX_AID */
+ id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
} else {
return;
}
@@ -720,8 +726,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++)
@@ -969,6 +976,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 5c38532..0ef5708 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;
}
@@ -2732,6 +2736,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
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
(again, changelog entry)
> - if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask))
> + if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
> conf->power_mode = nconf->power_mode;
> + ieee80211_local_ps_update(sdata);
> + }
Hmm, ok, so this _chg_mesh_attr call didn't belong into the previous
patch, but only here.
> +/* mesh power save */
> +void ieee80211_local_ps_update(struct ieee80211_sub_if_data *sdata);
> +void ieee80211_set_local_ps_mode(struct sta_info *sta,
> + enum nl80211_mesh_power_mode pm, u32 delay);
Both function should have 'mesh' somewhere in the name, I think?
> +++ b/net/mac80211/mesh_plink.c
> @@ -24,6 +24,7 @@
> #define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
> #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
> #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
> +#define default_ps_mode(s) (s->u.mesh.mshcfg.power_mode)
I wish these macros were just removed, can't you define an "ifmsh" (or
so, like elsewhere) variable and use "ifmsh->power_mode"?
> + list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
Ummm, no. You have to at least check the sdata.
> +static void ieee80211_local_ps_mode_timer(unsigned long data)
"sta" somewhere in the name?
> +/**
> + * ieee80211_set_local_ps_mode - set local PS mode towards a mesh STA
ditto
> + 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;
> +
> + sta->local_ps_mode_timer.data = (unsigned long) sta;
> + sta->local_ps_mode_timer.function =
> + ieee80211_local_ps_mode_timer;
> + mod_timer(&sta->local_ps_mode_timer,
> + jiffies + msecs_to_jiffies(delay));
I think you should set this up earlier (sta init?) and just use
mod_timer here.
johannes
On Fri, 2012-11-16 at 22:48 -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.
> +++ 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;
You don't need to be able to parse it to add it?
johannes
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
> Moves the default mesh beacon interval and DTIM period to cfg80211 and
> makes it accessible to nl80211. This enables modifying both values at
> mesh runtime.
Which doesn't seem to be permitted "just like that", see 13.13.4.3.
johannes
Possibilities of setting and getting mesh default power mode are
added.
Signed-off-by: Marco Porsch <[email protected]>
Signed-off-by: Ivan Bezyazychnyy <[email protected]>
Signed-off-by: Mike Krinkin <[email protected]>
---
include/uapi/linux/nl80211.h | 3 +++
net/mac80211/cfg.c | 2 ++
net/wireless/nl80211.c | 8 +++++++-
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index ee80ffc..a6b2886 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2329,6 +2329,8 @@ enum nl80211_mesh_power_mode {
*
* @NL80211_MESHCONF_DTIM_PERIOD: DTIM period to use
*
+ * @NL80211_MESHCONF_POWER_MODE: default link-specific power save mode
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -2360,6 +2362,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
NL80211_MESHCONF_BEACON_INTERVAL,
NL80211_MESHCONF_DTIM_PERIOD,
+ NL80211_MESHCONF_POWER_MODE,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index aef8445..84d63fc 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1678,6 +1678,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
sdata->vif.bss_conf.dtim_period = nconf->dtim_period;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
}
+ if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask))
+ conf->power_mode = nconf->power_mode;
return 0;
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 636634d..0312d74 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3774,7 +3774,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
nla_put_u16(msg, NL80211_MESHCONF_BEACON_INTERVAL,
cur_params.beacon_interval) ||
nla_put_u8(msg, NL80211_MESHCONF_DTIM_PERIOD,
- cur_params.dtim_period))
+ cur_params.dtim_period) ||
+ 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);
@@ -3815,6 +3817,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_BEACON_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_DTIM_PERIOD] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U8 },
};
static const struct nla_policy
@@ -3939,6 +3942,9 @@ do {\
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dtim_period,
mask, NL80211_MESHCONF_DTIM_PERIOD,
nla_get_u8);
+ 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
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
> --- a/net/mac80211/mesh.h
> +++ b/net/mac80211/mesh.h
> @@ -30,6 +30,7 @@ enum mesh_config_capab_flags {
> MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
> MESHCONF_CAPAB_FORWARDING = BIT(3),
> MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
> + MESHCONF_CAPAB_POWER_SAVE_LEVEL = BIT(6),
Please move this entire enum to ieee80211.h first (in a separate patch).
It should also be u16 values and be put into the frame as a u16, since
the field is a u16 field apparently.
(yes, it's a bit harsh to demand this of you, but since we don't have a
mesh maintainer who's cleaning up that code ...)
> +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;
> +
> + BUG_ON(is_unicast_ether_addr(hdr->addr1) &&
> + ieee80211_is_data_qos(hdr->frame_control) &&
> + !sta);
NACK.
> +++ b/net/mac80211/wme.c
> @@ -184,7 +184,18 @@ 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)) {
> + *p = 0;
> + return;
I'd rather you refactor the function to return early in the case of the
frame not being a Data QoS frame, and then put the mesh stuff all inside
the if.
johannes
Moves the default mesh beacon interval and DTIM period to cfg80211 and
makes it accessible to nl80211. This enables modifying both values at
mesh runtime.
One safety check is added that keeps the DTIM counter within the allowed
limits.
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 ++++
include/uapi/linux/nl80211.h | 6 ++++++
net/mac80211/cfg.c | 12 ++++++++++++
net/mac80211/debugfs_netdev.c | 6 ++++++
net/mac80211/mesh.c | 3 ++-
net/mac80211/mesh.h | 2 --
net/mac80211/tx.c | 3 ++-
net/wireless/mesh.c | 5 +++++
net/wireless/nl80211.c | 14 +++++++++++++-
9 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index c696457..4c6668f 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.
+ * @beacon_interval: beacon interval to use
+ * @dtim_period: DTIM period to use
*/
struct mesh_config {
u16 dot11MeshRetryTimeout;
@@ -905,6 +907,8 @@ struct mesh_config {
u32 dot11MeshHWMPactivePathToRootTimeout;
u16 dot11MeshHWMProotInterval;
u16 dot11MeshHWMPconfirmationInterval;
+ u16 beacon_interval;
+ u8 dtim_period;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 617d0fb..3306e79 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2296,6 +2296,10 @@ 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_BEACON_INTERVAL: beacon interval to use
+ *
+ * @NL80211_MESHCONF_DTIM_PERIOD: DTIM period to use
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -2325,6 +2329,8 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ NL80211_MESHCONF_BEACON_INTERVAL,
+ NL80211_MESHCONF_DTIM_PERIOD,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7669002..aef8445 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1666,6 +1666,18 @@ 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_BEACON_INTERVAL, mask)) {
+ conf->beacon_interval = nconf->beacon_interval;
+ sdata->vif.bss_conf.beacon_int = nconf->beacon_interval;
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_INT |
+ BSS_CHANGED_BEACON_ENABLED);
+ }
+ if (_chg_mesh_attr(NL80211_MESHCONF_DTIM_PERIOD, mask)) {
+ conf->dtim_period = nconf->dtim_period;
+ sdata->vif.bss_conf.dtim_period = nconf->dtim_period;
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+ }
return 0;
}
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 3393ad5..3a51988 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -516,6 +516,10 @@ IEEE80211_IF_FILE(dot11MeshHWMProotInterval,
u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC);
IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC);
+IEEE80211_IF_FILE(beacon_interval,
+ u.mesh.mshcfg.beacon_interval, DEC);
+IEEE80211_IF_FILE(dtim_period,
+ u.mesh.mshcfg.dtim_period, DEC);
#endif
#define DEBUGFS_ADD_MODE(name, mode) \
@@ -620,6 +624,8 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout);
MESHPARAMS_ADD(dot11MeshHWMProotInterval);
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
+ MESHPARAMS_ADD(beacon_interval);
+ MESHPARAMS_ADD(dtim_period);
#undef MESHPARAMS_ADD
}
#endif
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index a350cab..827a7ca 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -630,7 +630,8 @@ 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.beacon_int = ifmsh->mshcfg.beacon_interval;
+ sdata->vif.bss_conf.dtim_period = ifmsh->mshcfg.dtim_period;
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 9285f3f..1fc5672 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -205,8 +205,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/mac80211/tx.c b/net/mac80211/tx.c
index 6f19818..de8a59b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2276,7 +2276,8 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
have_bits = !bitmap_empty((unsigned long*)ps->tim,
IEEE80211_MAX_AID+1);
- if (ps->dtim_count == 0)
+ if (ps->dtim_count == 0 ||
+ ps->dtim_count >= sdata->vif.bss_conf.dtim_period)
ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
else
ps->dtim_count--;
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 966cfc4..e461502 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,
@@ -69,6 +72,8 @@ const struct mesh_config default_mesh_config = {
.dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,
.dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
+ .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
+ .dtim_period = MESH_DEFAULT_DTIM_PERIOD,
};
const struct mesh_setup default_mesh_setup = {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8c08578..636634d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3770,7 +3770,11 @@ 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_u16(msg, NL80211_MESHCONF_BEACON_INTERVAL,
+ cur_params.beacon_interval) ||
+ nla_put_u8(msg, NL80211_MESHCONF_DTIM_PERIOD,
+ cur_params.dtim_period))
goto nla_put_failure;
nla_nest_end(msg, pinfoattr);
genlmsg_end(msg, hdr);
@@ -3809,6 +3813,8 @@ 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_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_DTIM_PERIOD] = { .type = NLA_U8 },
};
static const struct nla_policy
@@ -3927,6 +3933,12 @@ do {\
dot11MeshHWMPconfirmationInterval, mask,
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, beacon_interval,
+ mask, NL80211_MESHCONF_BEACON_INTERVAL,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dtim_period,
+ mask, NL80211_MESHCONF_DTIM_PERIOD,
+ nla_get_u8);
if (mask_out)
*mask_out = mask;
--
1.7.9.5
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.
This commit adds the possibility of setting the local link-specific
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 | 2 ++
include/uapi/linux/nl80211.h | 5 +++++
net/mac80211/cfg.c | 4 ++++
net/wireless/nl80211.c | 8 ++++++++
4 files changed, 19 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9488782..efec888 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 a6b2886..0e7528f 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 4454aa1..d0f936d 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_local_ps_mode(sta,
+ params->local_ps_mode, 0);
#endif
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0312d74..d3ebdc6 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3100,6 +3100,10 @@ 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])
+ 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 +3111,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 +3155,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
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 operation.
Signed-off-by: Marco Porsch <[email protected]>
---
include/net/cfg80211.h | 3 +++
include/uapi/linux/nl80211.h | 3 +++
net/mac80211/cfg.c | 5 +++++
net/mac80211/debugfs_netdev.c | 3 +++
net/wireless/mesh.c | 2 ++
net/wireless/nl80211.c | 8 +++++++-
6 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index ad6fabe..452932b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -896,6 +896,8 @@ struct bss_parameters {
* @dtim_period: DTIM period to use
* @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;
@@ -926,6 +928,7 @@ struct mesh_config {
u16 beacon_interval;
u8 dtim_period;
enum nl80211_mesh_power_mode power_mode;
+ u16 dot11MeshAwakeWindowDuration;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index baac4e5..356af50 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2345,6 +2345,8 @@ enum nl80211_mesh_power_mode {
*
* @NL80211_MESHCONF_POWER_MODE: default link-specific power save mode
*
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -2377,6 +2379,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_BEACON_INTERVAL,
NL80211_MESHCONF_DTIM_PERIOD,
NL80211_MESHCONF_POWER_MODE,
+ NL80211_MESHCONF_AWAKE_WINDOW,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 32a1dbf..4321cd9 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1692,6 +1692,11 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
conf->power_mode = nconf->power_mode;
ieee80211_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/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 3a51988..58e58cb 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -520,6 +520,8 @@ IEEE80211_IF_FILE(beacon_interval,
u.mesh.mshcfg.beacon_interval, DEC);
IEEE80211_IF_FILE(dtim_period,
u.mesh.mshcfg.dtim_period, DEC);
+IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
+ u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
#endif
#define DEBUGFS_ADD_MODE(name, mode) \
@@ -626,6 +628,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
MESHPARAMS_ADD(beacon_interval);
MESHPARAMS_ADD(dtim_period);
+ MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
#undef MESHPARAMS_ADD
}
#endif
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 1c7fab6..8fe70f3 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,
@@ -75,6 +76,7 @@ const struct mesh_config default_mesh_config = {
.beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
.dtim_period = MESH_DEFAULT_DTIM_PERIOD,
.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 dca2aac..9d45f83 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3796,7 +3796,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
nla_put_u8(msg, NL80211_MESHCONF_DTIM_PERIOD,
cur_params.dtim_period) ||
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);
@@ -3838,6 +3840,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_BEACON_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_DTIM_PERIOD] = { .type = NLA_U8 },
[NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
};
static const struct nla_policy
@@ -3965,6 +3968,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
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.
Signed-off-by: Marco Porsch <[email protected]>
---
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 ++++
5 files changed, 33 insertions(+), 1 deletion(-)
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 86f076e..fff017e 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 a9406b6..19c682e 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -237,6 +237,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 0ef5708..a62deaf 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2445,7 +2445,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;
@@ -2478,6 +2479,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
On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
> #ifdef CONFIG_MAC80211_MESH
> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
> index bc3e3e1..8396602 100644
> --- a/net/mac80211/iface.c
> +++ b/net/mac80211/iface.c
> @@ -769,6 +769,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
> /* free all potentially still buffered bcast frames */
> local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
> skb_queue_purge(&sdata->u.ap.ps.bc_buf);
> + } else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
> + local->total_ps_buffered -= skb_queue_len(&sdata->u.mesh.ps.bc_buf);
> + skb_queue_purge(&sdata->u.mesh.ps.bc_buf);
> } else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
> ieee80211_mgd_stop(sdata);
> }
This will not apply, and is in the wrong place anyway.
> + /* reset in case we have been in a mesh with PS peers before */
should amend the comment to say "and leak all the frames that might be
on the queue now.
> + } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
> + ps = &sta->sdata->u.mesh.ps;
> + /* TIM map only for AID <= IEEE80211_MAX_AID */
AID? Seems more like PLID to me:
> + id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
> - /* 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;
Instead of cluttering all the code paths like this, what if we changed
struct ieee80211_if_ap *sdata->bss
to
struct ps_data *sdata->ps
If we know the interface type is AP/AP_VLAN, we can still get the AP
sdata from that pointer:
container_of(sdata->ps, struct ieee80211_sub_if_sdata, u.ap.ps);
This is only in a few places, and might keep all the RX/TX code simpler?
> +++ 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;
This could just be "ps = sdata->ps" etc.
johannes
On 11/17/2012 01:26 AM, Johannes Berg wrote:
> On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
>
>> #ifdef CONFIG_MAC80211_MESH
>> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
>> index bc3e3e1..8396602 100644
>> --- a/net/mac80211/iface.c
>> +++ b/net/mac80211/iface.c
>> @@ -769,6 +769,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
>> /* free all potentially still buffered bcast frames */
>> local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
>> skb_queue_purge(&sdata->u.ap.ps.bc_buf);
>> + } else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
>> + local->total_ps_buffered -= skb_queue_len(&sdata->u.mesh.ps.bc_buf);
>> + skb_queue_purge(&sdata->u.mesh.ps.bc_buf);
>> } else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
>> ieee80211_mgd_stop(sdata);
>> }
>
> This will not apply, and is in the wrong place anyway.
Moved it to mesh_stop. After disabling the beacon, these buffered
broadcast frames are not going to be transmitted anywhere.
>
>> + /* reset in case we have been in a mesh with PS peers before */
>
> should amend the comment to say "and leak all the frames that might be
> on the queue now.
>
>> + } else if (sta->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
>> + ps = &sta->sdata->u.mesh.ps;
>> + /* TIM map only for AID <= IEEE80211_MAX_AID */
>
> AID? Seems more like PLID to me:
>
>> + id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
>
>> - /* 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;
>
> Instead of cluttering all the code paths like this, what if we changed
>
> struct ieee80211_if_ap *sdata->bss
>
> to
>
> struct ps_data *sdata->ps
>
>
> If we know the interface type is AP/AP_VLAN, we can still get the AP
> sdata from that pointer:
> container_of(sdata->ps, struct ieee80211_sub_if_sdata, u.ap.ps);
>
> This is only in a few places, and might keep all the RX/TX code simpler?
When counting, I found twice as many lines that would need the
workaround, as the ones that would benefit. Also I don't really have a
clue on the AP/VLAN code.
>
>> +++ 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;
>
> This could just be "ps = sdata->ps" etc.
--Marco
This commit adds the possibility of getting the local link-specific
power mode and the tracked link-specific and non-peer power mode of
the peer.
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 efec888..ad6fabe 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 0e7528f..baac4e5 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 d0f936d..32a1dbf 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 d3ebdc6..dca2aac 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
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
> 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).
In addition to all the inline comments, the patch series ordering etc.
worries me. What happens if I run an inbetween snapshot? It seems to me
that could result in pretty inconsistent behaviour, in particular wrt.
userspace configuration being accepted but only partially (?) used etc.
johannes
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.
The local_ps_mode field has been added to sta_info structure to represent
the link-specific power mode at this station for the station represented
by this structure.
When a peer link is established, the local link-specific power mode is
set up equal to the user-configured default power mode. To allow
successful peering, 802.11s defines that the non-peer power mode is
temporarily set to active mode during peering.
The status of the local peer-specific power modes is monitored for
mesh power mode indication and for checking if the radio may be set
to doze state. The PS status is also updated 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 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.
A debug define is added for 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/ieee80211_i.h | 4 ++
net/mac80211/mesh.c | 1 +
net/mac80211/mesh.h | 5 ++
net/mac80211/mesh_plink.c | 21 ++++++++
net/mac80211/mesh_ps.c | 124 ++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/sta_info.c | 2 +
net/mac80211/sta_info.h | 6 +++
11 files changed, 189 insertions(+), 2 deletions(-)
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 84d63fc..4454aa1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1678,8 +1678,10 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
sdata->vif.bss_conf.dtim_period = nconf->dtim_period;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
}
- if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask))
+ if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
conf->power_mode = nconf->power_mode;
+ ieee80211_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/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 827a7ca..145d9d2 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -635,6 +635,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_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 1fc5672..405c6b2 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -256,6 +256,11 @@ 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_local_ps_update(struct ieee80211_sub_if_data *sdata);
+void ieee80211_set_local_ps_mode(struct sta_info *sta,
+ enum nl80211_mesh_power_mode pm, u32 delay);
+
/* 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 234fe75..795f8c7 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -24,6 +24,7 @@
#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
+#define default_ps_mode(s) (s->u.mesh.mshcfg.power_mode)
/* We only need a valid sta if user configured a minimum rssi_threshold. */
#define rssi_threshold_check(sta, sdata) \
@@ -185,6 +186,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
+ ieee80211_local_ps_update(sdata);
+
return changed;
}
@@ -549,6 +552,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_local_ps_update(sdata);
+
return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
sta->sta.addr, llid, 0, 0);
}
@@ -778,6 +784,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
get_random_bytes(&llid, 2);
sta->llid = llid;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+
+ /* set the non-peer mode to active during peering */
+ ieee80211_local_ps_update(sdata);
+
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_OPEN,
@@ -870,6 +880,10 @@ 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_local_ps_mode(sta,
+ default_ps_mode(sdata),
+ 100);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -908,6 +922,13 @@ 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_local_ps_mode(sta,
+ default_ps_mode(sdata),
+ 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..45a3500
--- /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_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_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) {
+ 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;
+}
+
+static void ieee80211_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_local_ps_mode(sta, sta->local_ps_mode_delayed, 0);
+}
+
+/**
+ * ieee80211_set_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_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;
+
+ sta->local_ps_mode_timer.data = (unsigned long) sta;
+ sta->local_ps_mode_timer.function =
+ ieee80211_local_ps_mode_timer;
+ 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_local_ps_update(sdata);
+}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index e9d5768..f8513a7 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,7 @@ 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);
+ init_timer(&sta->local_ps_mode_timer);
#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
On Sat, Nov 17, 2012 at 12:59 AM, Johannes Berg
<[email protected]> wrote:
> On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
>> Possibilities of setting and getting mesh default power mode are
>> added.
>
> ditto.
>
>
>> + * @NL80211_MESHCONF_POWER_MODE: default link-specific power save
>> mode
>
> please describe the type of the attribute (u8)
>
>
>> + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
>> + mask, NL80211_MESHCONF_POWER_MODE,
>> + nla_get_u8);
>
> Huh, ok, how did this FILL_IN_MESH_PARAM thing ever get here ... sad I
> missed that, all of those are *completely* missing range checks.
>
> Please add them here, and somebody should also follow up with a patch to
> fix them on the other attributes.
Noted.
Thomas
Hi,
First of all; thanks for all the comments.
On 11/17/2012 01:10 AM, Johannes Berg wrote:
> On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
>
>> --- a/net/mac80211/mesh.h
>> +++ b/net/mac80211/mesh.h
>> @@ -30,6 +30,7 @@ enum mesh_config_capab_flags {
>> MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
>> MESHCONF_CAPAB_FORWARDING = BIT(3),
>> MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
>> + MESHCONF_CAPAB_POWER_SAVE_LEVEL = BIT(6),
>
> Please move this entire enum to ieee80211.h first (in a separate patch).
>
> It should also be u16 values and be put into the frame as a u16, since
> the field is a u16 field apparently.
Sorry, I don't get that one. According to IEEE80211-2012 8.4.2.100 Mesh
Configuration element and 8.4.2.100.8 Mesh Capability, the capability
field is just u8.
It is currently written as follows:
mesh.c : mesh_add_meshconf_ie
/* Mesh capability */
*pos = MESHCONF_CAPAB_FORWARDING;
*pos |= ifmsh->accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
Should I change anything? Just move the enum?
>
> (yes, it's a bit harsh to demand this of you, but since we don't have a
> mesh maintainer who's cleaning up that code ...)
>
>> +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;
>> +
>> + BUG_ON(is_unicast_ether_addr(hdr->addr1) &&
>> + ieee80211_is_data_qos(hdr->frame_control) &&
>> + !sta);
>
> NACK.
>
>> +++ b/net/mac80211/wme.c
>> @@ -184,7 +184,18 @@ 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)) {
>> + *p = 0;
>> + return;
>
> I'd rather you refactor the function to return early in the case of the
> frame not being a Data QoS frame, and then put the mesh stuff all inside
> the if.
>
> johannes
>
>
On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
> @@ -1189,7 +1190,8 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
> struct sta_info *sta_inf = container_of(sta, struct sta_info, sta);
> bool in_ps;
>
> - WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS));
> + WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS) &&
> + !ieee80211_vif_is_mesh(&sta_inf->sdata->vif));
NACK. This is a public function for *driver* use, don't randomly use and
modify it.
johannes
On Fri, 2012-11-16 at 22:47 -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 int ieee80211_has_qos_pm(__le16 qc)
bool
> +/**
> + * ieee80211_set_peer_ps_mode - track the neighbor mesh STA's peer-specific
> + * power mode towards the local STA
not valid kernel-doc.
> +void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
> + struct ieee80211_hdr *hdr)
> +{
> + enum nl80211_mesh_power_mode pm;
> + static const char *modes[] = {
> + [NL80211_MESH_POWER_ACTIVE] = "active",
> + [NL80211_MESH_POWER_DEEP_SLEEP] = "deep sleep",
> + };
> + mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %s\n",
> + sta->sta.addr, modes[pm]);
This will cause warnings if debug is disabled, can't you just print the
numeric value? It's for your own debugging -- hardly seems necessary to
have the strings.
Same in the other function.
And now that I've seen you haven't even compiled the code with the debug
Kconfig turned off, I'll have to ask you to do that.
johannes
On 11/17/2012 01:40 AM, Johannes Berg wrote:
> On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
>
>> +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);
>
> This is ... strange? Can a single station really own *two* num_psp
> refcounts?
Yes it can. A station can be both owner and recipient. And it would just
be overhead to distinguish between num_psp_owner and num_psp_recipient,
when in the end we only want to know if there is any PSP ongoing at all.
I'll change the comment to:
/* number of active PSPs (owner and recipient counted independently) */
atomic_t num_psp;
>
>> + nullfunc = (struct ieee80211_hdr *) skb->data;
>> + if (!eosp)
>> + nullfunc->frame_control |=
>> + cpu_to_le16(IEEE80211_FCTL_MOREDATA);
>
> This seems wrong -- EOSP and moredata are orthogonal (with the
> restriction that "!EOSP => moredata") -- but if you just have that in
> the code the moredata bit won't always be set correctly.
Imho, in the context of PSP trigger frames it does.
Sending a trigger frame to a mesh PS STA with no EOSP implies the start
of a PSP with the sender as owner -> following data. The other two
combinations imply that there is no more data following in that direction.
>
>> + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
>> + drv_allow_buffered_frames(sdata->local, sta, BIT(7), 1,
>> + IEEE80211_FRAME_RELEASE_UAPSD, !eosp);
>
> ditto, passing !eosp definitely seems wrong
>
>> +/**
>> + * ieee80211_qos_null_append - append QoS Null as PSP trigger (if necessary)
>
> append? where? why not static?
Append to the skb queue given to the function. - I'll clarify the
comment for that.
Not static because called from sta_info.c. I can move it there if you
like, but I valued keeping all the mesh PS stuff in one place.
But now that you mention it... is there any interest in having that
function used for uAPSD? Because ieee80211_sta_ps_deliver_response sets
the EOSP flag during uAPSD, but does not enforce a QoS Data frame to
carry it. But maybe uAPSD just permits transmitting anything else than
QoS Data frames...
>
>> + ieee80211_sta_ps_deliver_response(sta, 1, 0,
>> + IEEE80211_FRAME_RELEASE_UAPSD);
>
> uAPSD?
>
> The standard *explicitly* states that ASPD is *not* supported in mesh.
Absolutely correct. The PSP mechanism is just very similar to uAPSD,
though. So once the PSP is set up, the mechanisms are the same actually.
What do you advise? Renaming the release reason? Creating a different
one that is handled equally?
>
> Ok I don't really get this, need more time I guess .. also it seems
> really hacked together.
Of course it is. You should have seen it before numerous cleanup
iterations =)
>
> johannes
>
>
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 of 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 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 | 8 ++
net/mac80211/mesh_plink.c | 15 +++
net/mac80211/mesh_ps.c | 231 ++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 3 +
net/mac80211/sta_info.c | 13 ++-
net/mac80211/sta_info.h | 8 ++
net/mac80211/status.c | 5 +
11 files changed, 288 insertions(+), 4 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 1d8e69a..537a9e1 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..be80196 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; /* number of PSPs local STA is owner/recipient */
};
#ifdef CONFIG_MAC80211_MESH
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index fff017e..3054bd0 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -664,6 +664,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
ieee80211_local_ps_update(sdata);
/* reset in case we have been in a mesh with PS peers before */
atomic_set(&sdata->u.mesh.ps.num_sta_ps, 0);
+ atomic_set(&sdata->u.mesh.num_psp, 0);
skb_queue_head_init(&ifmsh->ps.bc_buf);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED |
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 19c682e..f0cd72d 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -271,6 +271,14 @@ 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_send_psp_trigger(struct sta_info *sta,
+ bool rspi, bool eosp);
+void ieee80211_qos_null_append(struct sta_info *sta,
+ struct sk_buff_head *frames);
+void ieee80211_mesh_ps_deliver(struct sta_info *sta, u16 awake_window,
+ bool buffer_peer);
+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 74d4da5..6b0912d 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -412,6 +412,8 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct sta_info *sta;
+ bool has_buffered = false;
+ u16 awake_window = 0;
rcu_read_lock();
sta = mesh_peer_init(sdata, hw_addr, elems);
@@ -425,6 +427,19 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
rssi_threshold_check(sta, sdata))
mesh_plink_open(sta);
+ /* TIM map only for AID <= IEEE80211_MAX_AID */
+ if (elems->tim && 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(sdata, "%pM indicates buffered frames (LLID %d)\n",
+ hw_addr, le16_to_cpu(sta->llid));
+
+ if (elems->awake_window)
+ awake_window = get_unaligned_le16(elems->awake_window);
+
+ ieee80211_mesh_ps_deliver(sta, awake_window, has_buffered);
out:
rcu_read_unlock();
}
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
index e98d80c..e972680 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -8,6 +8,28 @@
*/
#include "mesh.h"
+#include "wme.h"
+#include "driver-ops.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 +82,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);
}
@@ -261,6 +291,14 @@ void ieee80211_sta_ps_update(struct sta_info *sta)
/* enable frame buffering for powersave STA */
ieee80211_sta_ps_transition_ni(&sta->sta, do_buffer);
+
+ /* 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);
+ }
}
/**
@@ -345,3 +383,196 @@ void ieee80211_set_nonpeer_ps_mode(struct sta_info *sta,
ieee80211_sta_ps_update(sta);
}
+
+/**
+ * ieee80211_send_psp_trigger - send PSP trigger (QoS Null) to init/end PSP
+ *
+ * @sta: peer mesh STA
+ * @rspi: set RSPI flag
+ * @eosp: set EOSP flag
+ */
+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;
+ if (eosp)
+ info->flags |= IEEE80211_TX_STATUS_EOSP;
+
+ mps_dbg(sdata, "sending PSP trigger%s%s to %pM\n",
+ rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr);
+
+ /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
+ drv_allow_buffered_frames(sdata->local, sta, BIT(7), 1,
+ IEEE80211_FRAME_RELEASE_UAPSD, !eosp);
+
+ ieee80211_tx_skb(sdata, skb);
+}
+
+/**
+ * ieee80211_qos_null_append - append QoS Null as PSP trigger (if necessary)
+ *
+ * @sta: peer mesh STA we are sending frames to
+ * @frames: skb queue to append to
+ */
+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);
+
+ /* must be frame 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 - transmit frames during mesh power save
+ *
+ * @sta: STA info to transmit to
+ * @awake_window: peer's awake window duration
+ * @buffer_peer: the neighbor 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.
+ * If the STA is not a peer, only single frames may be sent.
+ */
+void ieee80211_mesh_ps_deliver(struct sta_info *sta, u16 awake_window,
+ bool buffer_peer)
+{
+ int ac, buffer_local = 0;
+
+ /* only 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 (!buffer_peer && !buffer_local)
+ return;
+
+ if (sta->plink_state == NL80211_PLINK_ESTAB) {
+ ieee80211_send_psp_trigger(sta, buffer_peer, !buffer_local);
+ } else {
+ mps_dbg(sta->sdata, "sending single frame (of %d) to %pM\n",
+ buffer_local, sta->sta.addr);
+
+ ieee80211_sta_ps_deliver_response(sta, 1, 0,
+ IEEE80211_FRAME_RELEASE_UAPSD);
+ }
+}
+
+/**
+ * ieee80211_mesh_ps_deliver_psp - transmit frames during a PSP
+ *
+ * @sta: STA info to transmit to
+ */
+static void ieee80211_mesh_ps_deliver_psp(struct sta_info *sta)
+{
+ int ac, buffer_local = 0;
+
+ 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 (buffer_local) {
+ mps_dbg(sta->sdata, "in PSP with %pM -> sending %d frames\n",
+ sta->sta.addr, buffer_local);
+
+ ieee80211_sta_ps_deliver_response(sta, buffer_local,
+ 0, IEEE80211_FRAME_RELEASE_UAPSD);
+ } else {
+ ieee80211_send_psp_trigger(sta, false, true);
+ }
+}
+
+/**
+ * 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_psp(sta);
+ } 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_psp(sta);
+ }
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d56889f..e8a5e45 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1369,6 +1369,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.c b/net/mac80211/sta_info.c
index 4c312ed..913a8ea 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1107,7 +1107,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
}
-static void
+void
ieee80211_sta_ps_deliver_response(struct sta_info *sta,
int n_frames, u8 ignored_acs,
enum ieee80211_frame_release_type reason)
@@ -1201,7 +1201,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* This will evaluate to 1, 3, 5 or 7. */
tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
- ieee80211_send_null_response(sdata, sta, tid, reason);
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ ieee80211_send_psp_trigger(sta, false, true);
+ else
+ ieee80211_send_null_response(sdata, sta, tid, reason);
+
return;
}
@@ -1213,6 +1217,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
skb_queue_head_init(&pending);
+ /* in a PSP make sure the last skb is a QoS Data frame */
+ if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
+ !skb_queue_empty(&frames))
+ ieee80211_qos_null_append(sta, &frames);
+
while ((skb = __skb_dequeue(&frames))) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *) skb->data;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index dbb3a10..bc48a82 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
@@ -567,5 +571,9 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
+void
+ieee80211_sta_ps_deliver_response(struct sta_info *sta,
+ int n_frames, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason);
#endif /* STA_INFO_H */
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
On Fri, 2012-11-16 at 22:47 -0800, Marco Porsch wrote:
> Possibilities of setting and getting mesh default power mode are
> added.
ditto.
> + * @NL80211_MESHCONF_POWER_MODE: default link-specific power save
> mode
please describe the type of the attribute (u8)
> + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
> + mask, NL80211_MESHCONF_POWER_MODE,
> + nla_get_u8);
Huh, ok, how did this FILL_IN_MESH_PARAM thing ever get here ... sad I
missed that, all of those are *completely* missing range checks.
Please add them here, and somebody should also follow up with a patch to
fix them on the other attributes.
johannes
On Mon, 2012-11-26 at 10:27 -0800, Marco Porsch wrote:
> >> Imho, in the context of PSP trigger frames it does.
> >> Sending a trigger frame to a mesh PS STA with no EOSP implies the start
> >> of a PSP with the sender as owner -> following data. The other two
> >> combinations imply that there is no more data following in that direction.
> >
> > The EOSP bit in a trigger frame should always be 0 unless the frame is
> > also a PSP response, no?
>
> No. That is one weird thing about the standard: the combination
> RSPI:EOSP 0:0 is seen as a MPSP trigger frame when sent towards a
> station in PS mode towards the sender. So whenever a frame should be
> sent in a non-PSP context it has to set the EOSP flag.
> Another situation that sets the EOSP flag is a kind of MPSP poll: if one
> STA indicates buffered frames, the other STA can poll it with RSPI:EOSP 1:1.
What's RSPI?
> > What you seem to be missing though is the case when there _is_ more
> > data, but the service period has to end nonetheless, say because it was
> > limited to a few packets? Nothing here seems to indicate that an MPSP
> > ends only after all queued packets are transmitted, which would be a
> > requirement if this was supposed to be correct.
>
> MPSPs themselves are not defined in length by the standard.
> My current mechanism always sends all packets that are buffered at the
> start of the MPSP. At the time the MPSP starts, the last
> to-be-transmitted frame is the last buffered frame.
> Of course, by the time the MPSP ends, new frames may have been buffered
> in the meantime, which are not taken into account here. That would
> require additional feedback from ieee80211_tx_status which always seems
> a bit racy to me.
> (In an earlier version I had something like a feedback loop with
> ieee80211_tx_status, where the 'last' frame of a MPSP would always
> trigger a re-check of the PS buffers. That allowed to have infinite
> length MPSPs as long as data is enqueued for transmission. But that
> seemed to be overly complex when something like dynamic PS state
> switching is possible and advised by the standard.)
>
> But I see one situation where the More Data flag is indeed not set
> correctly. That is the case where I only send a single frame to a PS STA
> in a non-MPSP context when no peering is established yet. I'll fix that.
Hmm, ok. I don't really follow all of that, but hey :)
> > Well, not really, but non-QoS frames won't happen in that case, because
> > the peer will have QoS enabled. Similarly here I think, why would there
> > ever be a non-QoS frame? But maybe this can happen with forwarding,
> > which can't happen in the non-mesh case.
>
> For mesh also the HWMP routing (Management) frames are transmitted in
> MPSPs. So a valid PSP would look like that:
> 1) QoS Null (RSPI:EOSP 0:0)
> 2) Mgmt frame (e.g. HWMP Path Reply)
> 3) QoS Null (RSPI:EOSP 0:1)
> here the additional trailing Null is needed to end the MPSP. That
> scenario happens regularly when no path has been set up previously.
I assume the packet flow is
1 ->
<- 2
<- 3
correct? But in any case, I hadn't considered management frames.
Actually, those can be bufferable too without mesh, so that might be
buggy here right now, not sure they can be released with uAPSD.
> >> Absolutely correct. The PSP mechanism is just very similar to uAPSD,
> >> though. So once the PSP is set up, the mechanisms are the same actually.
> >> What do you advise? Renaming the release reason? Creating a different
> >> one that is handled equally?
> >
> > Well so far the more-data bit seems to be handled different, although I
> > argue above that you're actually not doing that correctly ;-)
> >
> > But I think doing different reasons could be helpful, if only to
> > understand the code better.
>
> In the last iteration of my RFC I created an own function similar to
> ieee80211_sta_ps_deliver_response. That spares adding even more
> complexity to ieee80211_sta_ps_deliver_response and allows me to keep
> the mesh PA code separated in mesh_ps.c. I don't seem to need the
> release reason then.
Ok. Not for the driver maybe, if that has to release buffered frames,
say with aggregation? But I don't know.
johannes
On Fri, 2012-11-16 at 22:48 -0800, Marco Porsch wrote:
> --- a/net/mac80211/cfg.c
*looks at subject*
Oops.
johannes