diff mbox series

Adding EPCS Support

Message ID BN0P110MB12102BF7BDD2D28E95373DD9D4D3A@BN0P110MB1210.NAMP110.PROD.OUTLOOK.COM
State New
Headers show
Series Adding EPCS Support | expand

Commit Message

Youzwak, Jason A (PERATON LABS) July 2, 2024, 12:45 p.m. UTC
Hello,

We are implementing a Wi-Fi 7 feature called Emergency Preparedness Communication Services (EPCS) that provides priority to Stations (STAs) that are authorized to receive National Security and Emergency Preparedness (NSEP) Communication Services. This feature is part of Wi-Fi 7 certification, detailed in IEEE 802.11be Draft 6.0.

We have started implementation efforts for the Linux operating system (specifically Arch Linux). We have successfully implemented and tested the over-the-air behavior per Wi-Fi Alliance defined test cases by modifying the ‘hostapd’ and ‘wpa_supplicant’. However, we are facing challenges making kernel modifications related to applying EDCA parameters for channel access. Specifically, we need to modify the nl80211, mac80211, and cfg80211 components in the Linux kernel to accommodate the following behavior at the STA per IEEE 802.11be specification (Draft 6.0, Clause 35.16.3.2):

"While EPCS priority access is enabled, the STA affiliated with an EPCS non-AP MLD shall
 
- follow the contention-based channel-access procedures defined in 10.2.3.2 (HCF contention-based channel access (EDCA)) using the EDCA parameter set stored in the dot11EDCATable as described earlier in this subclause, and
- ignore the part of the procedures defined in 10.2.3.2 (HCF contention-based channel access (EDCA)) that concerns the update of the EDCA parameters that are sent by the corresponding AP in its Beacon and Probe Response frames
- if the per-STA profile of the EPCS Priority Access Multi-Link element is present in the EPCS Priority Access Enable Request or the EPCS Priority Access Enable Response frame received by a STA affiliated with the EPCS non-AP MLD and the per-STA profile contains an MU EDCA Parameter Set element:
  - follow the rules defined in 26.2.7 (EDCA operation using MU EDCA parameters) using the MU EDCA parameters stored in the dot11MUEDCATable as described earlier in this subclause, except that
    - if the MUEDCATimer[AC] of the STA reaches 0, either by counting down or due to a reset following the reception of an MU EDCA Reset frame, the STA affiliated with EPCS non-AP MLD shall update CWmin[AC], CWmax[AC], and AIFSN[AC] to the values stored in the dot11EDCATable as described earlier in this subclause
    - ignore the part of the procedures defined in 26.2.7 (EDCA operation using MU EDCA parameters) that concerns the update of the MU EDCA parameters that are sent by the corresponding AP in its Beacon and Probe Response frames 

After the EPCS priority access is torn down, the STA affiliated with an EPCS non-AP MLD
 
- shall update its CWmin[AC], CWmax[AC], AIFSN[AC], and TXOP Limit [AC] state variables following the procedures in 10.2.3.2 (HCF contention-based channel access (EDCA)). 
- shall update the dot11MUEDCATable following the procedures in 26.2.7 (EDCA operation using MU EDCA parameters)."

We took the following approach: 

- Define new nl80211 commands: NL80211_CMD_EPCS_ENABLE and NL80211_CMD_EPCS_TEARDOWN 
- Define new STA flag: WLAN_STA_EPCS_ENABLED 
- In net/mac80211/mlme.c, in ieee80211_mgd_set_link_qos_params(), if the WLAN_STA_EPCS_ENABLED flag is set, ignore the EDCA parameters from the Beacon and apply Priority EDCA parameters.

Despite our efforts, we encountered several challenges: 

1. Sending EPCS Enable/Teardown commands to the kernel (mac80211) from userspace (hostapd) via netlink.  The response was "Operation not supported on transport endpoint (-EOPNOTSUPP) (-95)".
2. Receiving, parsing and propagating the message via nl80211 and cfg80211.  Specifically, we would like guidance on storing parameters received in the NL80211_CMD_EPCS_ENABLE message and retrieving them in the net/mac80211/mlme.c module. 
3. Applying the new EDCA parameters in mac80211 module. 

We are seeking help from experts in the community who have worked with these components. Specifically, we would appreciate responses to the following questions: 

- Is the kernel the right place to implement this or are there user-space solutions that we can leverage? 
- If kernel modifications are needed, does our proposed approach make sense, or are there better alternatives we should consider? 
- Is there documented guidance on how to add such features to the kernel? 

Please see our initial work to provide basic support for EPCS messaging in Linux kernel 6.9.5 on Arch Linux in the patch below.

Note: this is a proof of concept and not a full implementation – Priority Parameters are hardcoded in the net/mac/80211/mlme.c module.  A full implementation would accept priority parameters via the “NL80211_CMD_EPCS_ENABLE” command. 

Patch to Kernel 6.9.5
---------------------- 
 
Thanks in advance, 
Jason

Comments

Johannes Berg July 3, 2024, 7:56 a.m. UTC | #1
On Tue, 2024-07-02 at 12:45 +0000, Youzwak, Jason A (PERATON LABS)
wrote:
> 
> We are implementing a Wi-Fi 7 feature called Emergency Preparedness Communication Services (EPCS)

good luck :)

> - Define new nl80211 commands: NL80211_CMD_EPCS_ENABLE and NL80211_CMD_EPCS_TEARDOWN 

seems reasonable so far

> - Define new STA flag: WLAN_STA_EPCS_ENABLED

That may be necessary at some level, but it might not be possible for
drivers to handle it at the STA level - EDCA parameters are usually set
per *link*, not per *station*. Also, a STA now represents a whole peer
MLD, so affects potentially multiple links.

> - In net/mac80211/mlme.c, in ieee80211_mgd_set_link_qos_params(), if the WLAN_STA_EPCS_ENABLED flag is set, ignore the EDCA parameters from the Beacon and apply Priority EDCA parameters.

Except you never even call ieee80211_mgd_set_link_qos_params() so this
cannot work.

> 1. Sending EPCS Enable/Teardown commands to the kernel (mac80211) from userspace (hostapd) via netlink.  The response was "Operation not supported on transport endpoint (-EOPNOTSUPP) (-95)".

This is so basic I don't even know what it could be. There are tools to
debug netlink, even "iw --debug", or you can print in the kernel what
happens ... Or even run an ARCH=um kernel and attach gdb.

> 2. Receiving, parsing and propagating the message via nl80211 and cfg80211.  Specifically, we would like guidance on storing parameters received in the NL80211_CMD_EPCS_ENABLE message and retrieving them in the net/mac80211/mlme.c module. 

There's a whole 20kLOC file called net/wireless/nl80211.c that's full of
such guidance, please read it.

> 3. Applying the new EDCA parameters in mac80211 module. 

Now you're just asking us to do your work ...

> - Is the kernel the right place to implement this or are there user-space solutions that we can leverage? 

On the AP side you can change EDCA parameters from userspace, but on the
client side I think you do need such new commands, at least to override
the automatic settings - looks like otherwise
NL80211_ATTR_WIPHY_TXQ_PARAMS _could_ even be used.

> - If kernel modifications are needed, does our proposed approach make sense, or are there better alternatives we should consider? 
> - Is there documented guidance on how to add such features to the kernel? 
> 
> Please see our initial work to provide basic support for EPCS messaging in Linux kernel 6.9.5 on Arch Linux in the patch below.

Please check out
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
and more generally
https://wireless.wiki.kernel.org/en/developers/documentation

> +++ b/include/net/cfg80211.h 
> @@ -4855,6 +4855,10 @@ struct cfg80211_ops { 
> int	(*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev, 
>      u8 tsid, const u8 *peer); 


this is for much later I suppose, but this patch is completely
whitespace damaged ...


> +    /* JY: PLABS ADDED */ 

???

> /* add new commands above here */ 
> +    /* JY: PLABS ADDED */ 


???
"above"?

> +static int ieee80211_epcs_enable(struct wiphy *wiphy, struct wireless_dev *wdev) 
> +{ 
> +    struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 
> +    struct sta_info *sta; 
> + 
> +    sta = sta_info_get_bss(sdata, sdata->deflink.u.mgd.bssid); 
> +    set_sta_flag(sta, WLAN_STA_EPCS_ENABLED); 

sta could be NULL, then you crash. deflink is wrong for MLO and yet you
say you want to implement a WiFi7 feature. Setting a STA flag has no
immediate effect, so nothing will actually happen here.

> +/* JY: PLABS ADDED */           
> +static int nl80211_epcs_enable(struct sk_buff *skb, struct genl_info *info) 
> +{ 
> +    struct cfg80211_registered_device *rdev = info->user_ptr[0]; 
> +    struct net_device *dev = info->user_ptr[1]; 
> +    struct wireless_dev *wdev = dev->ieee80211_ptr; 
> +       
> +    rdev_epcs_enable(rdev, wdev);    
> +                                                                                                                                         
> +    return 1;                   

what's with all the whitespace?
Also, "return 1"??

> +    /* JY: PLABS ADDED */ 
> +    { 
> +        .cmd = NL80211_CMD_EPCS_ENABLE, 
> +        // PLABS: If we encounter issues, remove validate 
> +        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 

Eh, no, always remove it.

> +        .doit = nl80211_epcs_enable, 
> +        .flags = GENL_UNS_ADMIN_PERM, 
> +        .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_MLO_UNSUPPORTED), 

So ... a wifi7 feature that doesn't support MLO?

> .small_ops = nl80211_small_ops, 
> .n_small_ops = ARRAY_SIZE(nl80211_small_ops), 
> -	.resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1, 
> +    // PLABS Changed 
> +	//.resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1, 
> +	.resv_start_op = NL80211_CMD_MAX + 1, 

No.


I don't think that patch was helpful, but since I'm not going to
implement it for you, you're going to have to do a lot more homework in
understanding the code base, how this feature should work, how to submit
changes, etc.

Good luck!

johannes
Youzwak, Jason A (PERATON LABS) July 11, 2024, 3:02 p.m. UTC | #2
Johannes,

Thank you for your feedback.  Our intention is to implement the changes ourselves.  I apologize if it appeared otherwise; we are not seeking someone else to implement the changes, but rather making sure that we are on the right track.

Thanks for confirming that kernel module changes will likely be necessary to apply EDCA parameters from userspace.  I intend to build a user-mode Linux kernel for further debugging and add checks for NULL, following your advice.  Additionally, I have been utilizing the "nlmon" interface for debugging purposes (https://jvns.ca/blog/2017/09/03/debugging-netlink-requests/) .

The code snippet I gave was just a primary attempt.  When calling the NL80211_CMD_EPCS_ENABLE, we will need to include the link_id and EDCA settings as parameters.  We plan to use NL80211_ATTR_WIPHY_TXQ_PARAMS per your suggestion.

Thanks again,
Jason
diff mbox series

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h 
index 1e09329ac..33c123b1d 100644 
--- a/include/net/cfg80211.h 
+++ b/include/net/cfg80211.h 
@@ -4855,6 +4855,10 @@  struct cfg80211_ops { 
int	(*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev, 
     u8 tsid, const u8 *peer); 
  
+    /* JY: PLABS ADDED */ 
+    int (*epcs_enable)(struct wiphy *wiphy, struct wireless_dev *wdev); 
+    int (*epcs_teardown)(struct wiphy *wiphy, struct wireless_dev *wdev); 

int	(*tdls_channel_switch)(struct wiphy *wiphy, 
       struct net_device *dev, 
       const u8 *addr, u8 oper_class, 
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h 
index f23ecbdd8..69ddd2092 100644 
--- a/include/uapi/linux/nl80211.h 
+++ b/include/uapi/linux/nl80211.h 
@@ -1592,6 +1592,9 @@  enum nl80211_commands { 
NL80211_CMD_SET_TID_TO_LINK_MAPPING, 
  
/* add new commands above here */ 
+    /* JY: PLABS ADDED */ 
+    NL80211_CMD_EPCS_ENABLE, 
+    NL80211_CMD_EPCS_TEARDOWN, 
  
/* used to define NL80211_CMD_MAX below */ 
__NL80211_CMD_AFTER_LAST, 
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c 
index 07abaf782..a3d979909 100644 
--- a/net/mac80211/cfg.c 
+++ b/net/mac80211/cfg.c 
@@ -4347,6 +4347,32 @@  static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, 
return ret; 
} 
  
+/* JY: PLABS BEGIN */ 

+static int ieee80211_epcs_enable(struct wiphy *wiphy, struct wireless_dev *wdev) 
+{ 
+    struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 
+    struct sta_info *sta; 

+    sta = sta_info_get_bss(sdata, sdata->deflink.u.mgd.bssid); 
+    set_sta_flag(sta, WLAN_STA_EPCS_ENABLED); 

+    return 0; 
+} 

+static int ieee80211_epcs_teardown(struct wiphy *wiphy, struct wireless_dev *wdev) 
+{ 
+    struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 
+    struct sta_info *sta; 

+    sta = sta_info_get_bss(sdata, sdata->deflink.u.mgd.bssid); 
+    clear_sta_flag(sta, WLAN_STA_EPCS_ENABLED); 

+    return 0; 
+} 

+/* JY: PLABS END */ 

static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev, 
       u8 tsid, const u8 *peer, u8 up, 
       u16 admitted_time) 
@@ -5165,4 +5191,7 @@  const struct cfg80211_ops mac80211_config_ops = { 
.del_link_station = ieee80211_del_link_station, 
.set_hw_timestamp = ieee80211_set_hw_timestamp, 
.set_ttlm = ieee80211_set_ttlm, 
+    /* JY: PLABS ADDED */ 
+    .epcs_enable = ieee80211_epcs_enable, 
+    .epcs_teardown = ieee80211_epcs_teardown, 
}; 
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c 
index 1e9389c49..100e26729 100644 
--- a/net/mac80211/debugfs_sta.c 
+++ b/net/mac80211/debugfs_sta.c 
@@ -80,6 +80,8 @@  static const char * const sta_flag_names[] = { 
FLAG(PS_DELIVER), 
FLAG(USES_ENCRYPTION), 
FLAG(DECAP_OFFLOAD), 
+    /* JY: PLABS ADDED */ 
+    FLAG(EPCS_ENABLED), 
#undef FLAG 
}; 
  
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c 
index 497677e3d..aba87d1d4 100644 
--- a/net/mac80211/mlme.c 
+++ b/net/mac80211/mlme.c 
@@ -2720,7 +2720,30 @@  void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link) 
struct ieee80211_tx_queue_params *params = link->tx_conf; 
u8 ac; 
  
+    /* PLABS JY: Changes BEGIN */ 
+    bool epcs_enabled = false; 
+    struct sta_info *sta = NULL;     
+       
+    /* New flag lives in the Station Info */ 
+    sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); 

+    /* Get result 0 or 1 */ 
+    epcs_enabled = test_sta_flag(sta, WLAN_STA_EPCS_ENABLED); 
+   
+    mlme_dbg(sdata, "JY: PLABS mac80211 module changes\n"); 
+    mlme_dbg(sdata, "EPCS Enabled=%s\n", epcs_enabled ? "true" : "false"); 

for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 
+        if (epcs_enabled) { 
+            mlme_dbg(sdata, "JY: PLABS We are entering priority mode!\n"); 
+            // Setting EDCA parameter to Voice 
+            params[ac].aifs = 3; 
+            params[ac].cw_min = 4; 
+            params[ac].cw_max = 5; 
+            params[ac].txop = 65; 
+            params[ac].acm = 0; 
+          } 

mlme_dbg(sdata, 
 "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", 
 ac, params[ac].acm, 
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h 
index a52fb7638..ac2a39d57 100644 
--- a/net/mac80211/sta_info.h 
+++ b/net/mac80211/sta_info.h 
@@ -104,6 +104,8 @@  enum ieee80211_sta_info_flags { 
WLAN_STA_PS_DELIVER, 
WLAN_STA_USES_ENCRYPTION, 
WLAN_STA_DECAP_OFFLOAD, 
+    /* JY: PLABS ADDED */ 
+    WLAN_STA_EPCS_ENABLED, 
  
NUM_WLAN_STA_FLAGS, 
}; 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c 
index 65c416e8d..8be612b49 100644 
--- a/net/wireless/nl80211.c 
+++ b/net/wireless/nl80211.c 
@@ -2641,6 +2641,8 @@  static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, 
if (rdev->wiphy.features & 
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) 
CMD(add_tx_ts, ADD_TX_TS); 
+            // PLABS Added 
+            CMD(epcs_enable, EPCS_ENABLE); 
CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST); 
CMD(update_connect_params, UPDATE_CONNECT_PARAMS); 
CMD(update_ft_ies, UPDATE_FT_IES); 
@@ -15299,6 +15301,30 @@  static int nl80211_set_qos_map(struct sk_buff *skb, 
return ret; 
} 
  
+/* JY: PLABS ADDED */           
+static int nl80211_epcs_enable(struct sk_buff *skb, struct genl_info *info) 
+{ 
+    struct cfg80211_registered_device *rdev = info->user_ptr[0]; 
+    struct net_device *dev = info->user_ptr[1]; 
+    struct wireless_dev *wdev = dev->ieee80211_ptr; 
+       
+    rdev_epcs_enable(rdev, wdev);    
+                                                                                                                                         
+    return 1;                   
+}  
+       
+/* JY: PLABS ADDED */           
+static int nl80211_epcs_teardown(struct sk_buff *skb, struct genl_info *info) 
+{      
+    struct cfg80211_registered_device *rdev = info->user_ptr[0]; 
+    struct net_device *dev = info->user_ptr[1]; 
+    struct wireless_dev *wdev = dev->ieee80211_ptr; 

+    rdev_epcs_teardown(rdev, wdev);  
+                                                                                                                                         
+    return 1; 
+} 

static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) 
{ 
struct cfg80211_registered_device *rdev = info->user_ptr[0]; 
@@ -17488,6 +17514,24 @@  static const struct genl_small_ops nl80211_small_ops[] = { 
.flags = GENL_UNS_ADMIN_PERM, 
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), 
}, 
+    /* JY: PLABS ADDED */ 
+    { 
+        .cmd = NL80211_CMD_EPCS_ENABLE, 
+        // PLABS: If we encounter issues, remove validate 
+        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 
+        .doit = nl80211_epcs_enable, 
+        .flags = GENL_UNS_ADMIN_PERM, 
+        .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_MLO_UNSUPPORTED), 
+    }, 
+    /* JY: PLABS ADDED */ 
+    { 
+        .cmd = NL80211_CMD_EPCS_TEARDOWN, 
+        // PLABS: If we encounter issues, remove validate 
+        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 
+        .doit = nl80211_epcs_teardown, 
+        .flags = GENL_UNS_ADMIN_PERM, 
+        .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_MLO_UNSUPPORTED), 
+    }, 
}; 
  
static struct genl_family nl80211_fam __ro_after_init = { 
@@ -17504,7 +17548,9 @@  static struct genl_family nl80211_fam __ro_after_init = { 
.n_ops = ARRAY_SIZE(nl80211_ops), 
.small_ops = nl80211_small_ops, 
.n_small_ops = ARRAY_SIZE(nl80211_small_ops), 
-	.resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1, 
+    // PLABS Changed 
+	//.resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1, 
+	.resv_start_op = NL80211_CMD_MAX + 1, 
.mcgrps = nl80211_mcgrps, 
.n_mcgrps = ARRAY_SIZE(nl80211_mcgrps), 
.parallel_ops = true, 
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h 
index 43897a526..8aec66206 100644 
--- a/net/wireless/rdev-ops.h 
+++ b/net/wireless/rdev-ops.h 
@@ -637,6 +637,17 @@  static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) 
trace_rdev_return_void(&rdev->wiphy); 
} 
  
+/* JY: PLABS ADDED */           
+static inline void rdev_epcs_enable(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) 
+{ 
+    rdev->ops->epcs_enable(&rdev->wiphy, wdev); 
+} 
+  
+/* JY: PLABS ADDED */ 
+static inline void rdev_epcs_teardown(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) 
+{ 
+    rdev->ops->epcs_teardown(&rdev->wiphy, wdev); 
+} 
  
#ifdef CONFIG_NL80211_TESTMODE 
static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,