2019-09-08 12:58:14

by Maya Erez

[permalink] [raw]
Subject: [PATCH 00/11] wil6210 patches

The following wil6210 patches include various bug fixes.

Ahmad Masri (1):
wil6210: fix PTK re-key race

Alexei Avshalom Lazar (1):
wil6210: verify cid value is valid

Dedy Lansky (4):
wil6210: add wil_netif_rx() helper function
wil6210: add debugfs to show PMC ring content
wil6210: make sure DR bit is read before rest of the status message
wil6210: properly initialize discovery_expired_work

Lior David (3):
wil6210: use writel_relaxed in wil_debugfs_iomem_x32_set
wil6210: fix RX short frame check
wil6210: ignore reset errors for FW during probe

Maya Erez (2):
wil6210: add support for pci linkdown recovery
wil6210: report boottime_ns in scan results

drivers/net/wireless/ath/wil6210/cfg80211.c | 15 +-
drivers/net/wireless/ath/wil6210/debugfs.c | 16 +-
drivers/net/wireless/ath/wil6210/main.c | 35 +++-
drivers/net/wireless/ath/wil6210/netdev.c | 4 +
drivers/net/wireless/ath/wil6210/pcie_bus.c | 111 ++++++++++-
drivers/net/wireless/ath/wil6210/pm.c | 6 +
drivers/net/wireless/ath/wil6210/pmc.c | 26 +++
drivers/net/wireless/ath/wil6210/pmc.h | 1 +
drivers/net/wireless/ath/wil6210/txrx.c | 244 +++++++++++++++++++++---
drivers/net/wireless/ath/wil6210/txrx.h | 42 ++++
drivers/net/wireless/ath/wil6210/txrx_edma.c | 38 ++--
drivers/net/wireless/ath/wil6210/txrx_edma.h | 6 -
drivers/net/wireless/ath/wil6210/wil6210.h | 21 ++
drivers/net/wireless/ath/wil6210/wil_platform.h | 8 +
drivers/net/wireless/ath/wil6210/wmi.c | 38 +++-
drivers/net/wireless/ath/wil6210/wmi.h | 3 +
16 files changed, 550 insertions(+), 64 deletions(-)

--
1.9.1


2019-09-08 12:58:14

by Maya Erez

[permalink] [raw]
Subject: [PATCH 03/11] wil6210: add debugfs to show PMC ring content

From: Dedy Lansky <[email protected]>

PMC is a hardware debug mechanism which allows capturing real time
debug data and stream it to host memory. The driver allocates memory
buffers and set them inside PMC ring of descriptors.
Add pmcring debugfs that application can use to read the binary
content of descriptors inside the PMC ring (cat pmcring).

Signed-off-by: Dedy Lansky <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/debugfs.c | 13 +++++++++++++
drivers/net/wireless/ath/wil6210/pmc.c | 26 ++++++++++++++++++++++++++
drivers/net/wireless/ath/wil6210/pmc.h | 1 +
3 files changed, 40 insertions(+)

diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index fd3b2b3..50dc30e 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -959,6 +959,18 @@ static ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf,
.llseek = wil_pmc_llseek,
};

+static int wil_pmcring_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_pmcring_read, inode->i_private);
+}
+
+static const struct file_operations fops_pmcring = {
+ .open = wil_pmcring_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
/*---tx_mgmt---*/
/* Write mgmt frame to this file to send it */
static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
@@ -2371,6 +2383,7 @@ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
{"back", 0644, &fops_back},
{"pmccfg", 0644, &fops_pmccfg},
{"pmcdata", 0444, &fops_pmcdata},
+ {"pmcring", 0444, &fops_pmcring},
{"temp", 0444, &temp_fops},
{"freq", 0444, &freq_fops},
{"link", 0444, &link_fops},
diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c
index c49f798..4b7ac14 100644
--- a/drivers/net/wireless/ath/wil6210/pmc.c
+++ b/drivers/net/wireless/ath/wil6210/pmc.c
@@ -18,6 +18,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/seq_file.h>
#include "wmi.h"
#include "wil6210.h"
#include "txrx.h"
@@ -431,3 +432,28 @@ loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence)

return newpos;
}
+
+int wil_pmcring_read(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ struct pmc_ctx *pmc = &wil->pmc;
+ size_t pmc_ring_size =
+ sizeof(struct vring_rx_desc) * pmc->num_descriptors;
+
+ mutex_lock(&pmc->lock);
+
+ if (!wil_is_pmc_allocated(pmc)) {
+ wil_err(wil, "error, pmc is not allocated!\n");
+ pmc->last_cmd_status = -EPERM;
+ mutex_unlock(&pmc->lock);
+ return -EPERM;
+ }
+
+ wil_dbg_misc(wil, "pmcring_read: size %zu\n", pmc_ring_size);
+
+ seq_write(s, pmc->pring_va, pmc_ring_size);
+
+ mutex_unlock(&pmc->lock);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/pmc.h b/drivers/net/wireless/ath/wil6210/pmc.h
index bebc8d5..92b8c4d 100644
--- a/drivers/net/wireless/ath/wil6210/pmc.h
+++ b/drivers/net/wireless/ath/wil6210/pmc.h
@@ -25,3 +25,4 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
int wil_pmc_last_cmd_status(struct wil6210_priv *wil);
ssize_t wil_pmc_read(struct file *, char __user *, size_t, loff_t *);
loff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence);
+int wil_pmcring_read(struct seq_file *s, void *data);
--
1.9.1

2019-09-08 12:58:14

by Maya Erez

[permalink] [raw]
Subject: [PATCH 07/11] wil6210: properly initialize discovery_expired_work

From: Dedy Lansky <[email protected]>

Upon driver rmmod, cancel_work_sync() can be invoked on
p2p.discovery_expired_work before this work struct was initialized.
This causes a WARN_ON with newer kernel version.

Add initialization of discovery_expired_work inside wil_vif_init().

Signed-off-by: Dedy Lansky <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/netdev.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index a2eca54..a87bb84 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -284,6 +284,7 @@ static void wil_vif_init(struct wil6210_vif *vif)

INIT_WORK(&vif->probe_client_worker, wil_probe_client_worker);
INIT_WORK(&vif->disconnect_worker, wil_disconnect_worker);
+ INIT_WORK(&vif->p2p.discovery_expired_work, wil_p2p_listen_expired);
INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
INIT_WORK(&vif->enable_tx_key_worker, wil_enable_tx_key_worker);

--
1.9.1

2019-09-08 12:58:15

by Maya Erez

[permalink] [raw]
Subject: [PATCH 01/11] wil6210: add wil_netif_rx() helper function

From: Dedy Lansky <[email protected]>

Move common part of wil_netif_rx_any into new helper function and add
support for non-gro receive using netif_rx_ni.

Signed-off-by: Dedy Lansky <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/txrx.c | 60 ++++++++++++++++++++-------------
drivers/net/wireless/ath/wil6210/txrx.h | 2 ++
2 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 8b01ef8..b6253fc 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -728,21 +728,19 @@ static void wil_get_netif_rx_params(struct sk_buff *skb, int *cid,
* Pass Rx packet to the netif. Update statistics.
* Called in softirq context (NAPI poll).
*/
-void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
+void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid,
+ struct wil_net_stats *stats, bool gro)
{
gro_result_t rc = GRO_NORMAL;
struct wil6210_vif *vif = ndev_to_vif(ndev);
struct wil6210_priv *wil = ndev_to_wil(ndev);
struct wireless_dev *wdev = vif_to_wdev(vif);
unsigned int len = skb->len;
- int cid;
- int security;
u8 *sa, *da = wil_skb_get_da(skb);
/* here looking for DA, not A1, thus Rxdesc's 'mcast' indication
* is not suitable, need to look at data
*/
int mcast = is_multicast_ether_addr(da);
- struct wil_net_stats *stats;
struct sk_buff *xmit_skb = NULL;
static const char * const gro_res_str[] = {
[GRO_MERGED] = "GRO_MERGED",
@@ -753,25 +751,6 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
[GRO_CONSUMED] = "GRO_CONSUMED",
};

- wil->txrx_ops.get_netif_rx_params(skb, &cid, &security);
-
- stats = &wil->sta[cid].stats;
-
- skb_orphan(skb);
-
- if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) {
- rc = GRO_DROP;
- dev_kfree_skb(skb);
- stats->rx_replay++;
- goto stats;
- }
-
- /* check errors reported by HW and update statistics */
- if (unlikely(wil->txrx_ops.rx_error_check(wil, skb, stats))) {
- dev_kfree_skb(skb);
- return;
- }
-
if (wdev->iftype == NL80211_IFTYPE_STATION) {
sa = wil_skb_get_sa(skb);
if (mcast && ether_addr_equal(sa, ndev->dev_addr)) {
@@ -817,7 +796,10 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
if (skb) { /* deliver to local stack */
skb->protocol = eth_type_trans(skb, ndev);
skb->dev = ndev;
- rc = napi_gro_receive(&wil->napi_rx, skb);
+ if (gro)
+ rc = napi_gro_receive(&wil->napi_rx, skb);
+ else
+ netif_rx_ni(skb);
wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
len, gro_res_str[rc]);
}
@@ -837,6 +819,36 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
}
}

+void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
+{
+ int cid, security;
+ struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct wil_net_stats *stats;
+
+ wil->txrx_ops.get_netif_rx_params(skb, &cid, &security);
+
+ stats = &wil->sta[cid].stats;
+
+ skb_orphan(skb);
+
+ if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) {
+ dev_kfree_skb(skb);
+ ndev->stats.rx_dropped++;
+ stats->rx_replay++;
+ stats->rx_dropped++;
+ wil_dbg_txrx(wil, "Rx drop %d bytes\n", skb->len);
+ return;
+ }
+
+ /* check errors reported by HW and update statistics */
+ if (unlikely(wil->txrx_ops.rx_error_check(wil, skb, stats))) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ wil_netif_rx(skb, ndev, cid, stats, true);
+}
+
/**
* Proceed all completed skb's from Rx VRING
*
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index c0da134..fceb251 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -646,6 +646,8 @@ static inline void wil_skb_set_cid(struct sk_buff *skb, u8 cid)
}

void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
+void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid,
+ struct wil_net_stats *stats, bool gro);
void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif,
u8 cid, u8 tid, u16 seq);
--
1.9.1

2019-09-08 12:58:16

by Maya Erez

[permalink] [raw]
Subject: [PATCH 06/11] wil6210: verify cid value is valid

From: Alexei Avshalom Lazar <[email protected]>

cid value is not being verified in wmi_evt_delba(),
verification is added.

Signed-off-by: Alexei Avshalom Lazar <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/wmi.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 73fe9bf..88d9e5a 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1332,6 +1332,12 @@ static void wmi_evt_delba(struct wil6210_vif *vif, int id, void *d, int len)
cid = evt->cid;
tid = evt->tid;
}
+
+ if (!wil_cid_valid(wil, cid)) {
+ wil_err(wil, "DELBA: Invalid CID %d\n", cid);
+ return;
+ }
+
wil_dbg_wmi(wil, "DELBA MID %d CID %d TID %d from %s reason %d\n",
vif->mid, cid, tid,
evt->from_initiator ? "originator" : "recipient",
--
1.9.1

2019-09-08 12:58:16

by Maya Erez

[permalink] [raw]
Subject: [PATCH 04/11] wil6210: fix PTK re-key race

From: Ahmad Masri <[email protected]>

Fix a race between cfg80211 add_key call and transmitting of 4/4 EAP
packet. In case the transmit is delayed until after the add key takes
place, message 4/4 will be encrypted with the new key, and the
receiver side (AP) will drop it due to MIC error.

Wil6210 will monitor and look for the transmitted packet 4/4 eap key.
In case add_key takes place before the transmission completed, then
wil6210 will let the FW store the key and wil6210 will notify the FW
to use the PTK key only after 4/4 eap packet transmission was
completed.

Signed-off-by: Ahmad Masri <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/cfg80211.c | 15 ++-
drivers/net/wireless/ath/wil6210/main.c | 4 +
drivers/net/wireless/ath/wil6210/netdev.c | 3 +
drivers/net/wireless/ath/wil6210/txrx.c | 184 +++++++++++++++++++++++++++
drivers/net/wireless/ath/wil6210/txrx.h | 40 ++++++
drivers/net/wireless/ath/wil6210/txrx_edma.c | 4 +
drivers/net/wireless/ath/wil6210/wil6210.h | 15 +++
drivers/net/wireless/ath/wil6210/wmi.c | 11 +-
drivers/net/wireless/ath/wil6210/wmi.h | 3 +
9 files changed, 276 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 1883690..c70854e 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -331,6 +331,8 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
[WMI_KEY_USE_PAIRWISE] = "PTK",
[WMI_KEY_USE_RX_GROUP] = "RX_GTK",
[WMI_KEY_USE_TX_GROUP] = "TX_GTK",
+ [WMI_KEY_USE_STORE_PTK] = "STORE_PTK",
+ [WMI_KEY_USE_APPLY_PTK] = "APPLY_PTK",
};

int wil_iftype_nl2wmi(enum nl80211_iftype type)
@@ -542,7 +544,7 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy,
/*
* Find @idx-th active STA for specific MID for station dump.
*/
-static int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
+int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
{
int i;

@@ -1554,6 +1556,7 @@ void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
return;

switch (key_usage) {
+ case WMI_KEY_USE_STORE_PTK:
case WMI_KEY_USE_PAIRWISE:
for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
cc = &cs->tid_crypto_rx[tid].key_id[key_index];
@@ -1651,6 +1654,16 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
return -EINVAL;
}

+ spin_lock_bh(&wil->eap_lock);
+ if (pairwise && wdev->iftype == NL80211_IFTYPE_STATION &&
+ (vif->ptk_rekey_state == WIL_REKEY_M3_RECEIVED ||
+ vif->ptk_rekey_state == WIL_REKEY_WAIT_M4_SENT)) {
+ key_usage = WMI_KEY_USE_STORE_PTK;
+ vif->ptk_rekey_state = WIL_REKEY_WAIT_M4_SENT;
+ wil_dbg_misc(wil, "Store EAPOL key\n");
+ }
+ spin_unlock_bh(&wil->eap_lock);
+
rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
params->key, key_usage);
if (!rc && !IS_ERR(cs)) {
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 0f270b8..2c3a192 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -373,6 +373,7 @@ static void _wil6210_disconnect_complete(struct wil6210_vif *vif,
}
clear_bit(wil_vif_fwconnecting, vif->status);
clear_bit(wil_vif_ft_roam, vif->status);
+ vif->ptk_rekey_state = WIL_REKEY_IDLE;

break;
case NL80211_IFTYPE_AP:
@@ -736,6 +737,8 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
spin_lock_init(&wil->net_queue_lock);
+ spin_lock_init(&wil->eap_lock);
+
init_waitqueue_head(&wil->wq);
init_rwsem(&wil->mem_lock);

@@ -1667,6 +1670,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
cancel_work_sync(&vif->disconnect_worker);
wil6210_disconnect(vif, NULL,
WLAN_REASON_DEAUTH_LEAVING);
+ vif->ptk_rekey_state = WIL_REKEY_IDLE;
}
}
wil_bcast_fini_all(wil);
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 59f041d..a2eca54 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -218,6 +218,7 @@ static void wil_vif_deinit(struct wil6210_vif *vif)
cancel_work_sync(&vif->p2p.delayed_listen_work);
wil_probe_client_flush(vif);
cancel_work_sync(&vif->probe_client_worker);
+ cancel_work_sync(&vif->enable_tx_key_worker);
}

void wil_vif_free(struct wil6210_vif *vif)
@@ -284,6 +285,7 @@ static void wil_vif_init(struct wil6210_vif *vif)
INIT_WORK(&vif->probe_client_worker, wil_probe_client_worker);
INIT_WORK(&vif->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
+ INIT_WORK(&vif->enable_tx_key_worker, wil_enable_tx_key_worker);

INIT_LIST_HEAD(&vif->probe_client_pending);

@@ -540,6 +542,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
cancel_work_sync(&vif->disconnect_worker);
wil_probe_client_flush(vif);
cancel_work_sync(&vif->probe_client_worker);
+ cancel_work_sync(&vif->enable_tx_key_worker);
/* for VIFs, ndev will be freed by destructor after RTNL is unlocked.
* the main interface will be freed in wil_if_free, we need to keep it
* a bit longer so logging macros will work.
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index b6253fc..cb13652 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -725,6 +725,182 @@ static void wil_get_netif_rx_params(struct sk_buff *skb, int *cid,
}

/*
+ * Check if skb is ptk eapol key message
+ *
+ * returns a pointer to the start of the eapol key structure, NULL
+ * if frame is not PTK eapol key
+ */
+static struct wil_eapol_key *wil_is_ptk_eapol_key(struct wil6210_priv *wil,
+ struct sk_buff *skb)
+{
+ u8 *buf;
+ const struct wil_1x_hdr *hdr;
+ struct wil_eapol_key *key;
+ u16 key_info;
+ int len = skb->len;
+
+ if (!skb_mac_header_was_set(skb)) {
+ wil_err(wil, "mac header was not set\n");
+ return NULL;
+ }
+
+ len -= skb_mac_offset(skb);
+
+ if (len < sizeof(struct ethhdr) + sizeof(struct wil_1x_hdr) +
+ sizeof(struct wil_eapol_key))
+ return NULL;
+
+ buf = skb_mac_header(skb) + sizeof(struct ethhdr);
+
+ hdr = (const struct wil_1x_hdr *)buf;
+ if (hdr->type != WIL_1X_TYPE_EAPOL_KEY)
+ return NULL;
+
+ key = (struct wil_eapol_key *)(buf + sizeof(struct wil_1x_hdr));
+ if (key->type != WIL_EAPOL_KEY_TYPE_WPA &&
+ key->type != WIL_EAPOL_KEY_TYPE_RSN)
+ return NULL;
+
+ key_info = be16_to_cpu(key->key_info);
+ if (!(key_info & WIL_KEY_INFO_KEY_TYPE)) /* check if pairwise */
+ return NULL;
+
+ return key;
+}
+
+static bool wil_skb_is_eap_3(struct wil6210_priv *wil, struct sk_buff *skb)
+{
+ struct wil_eapol_key *key;
+ u16 key_info;
+
+ key = wil_is_ptk_eapol_key(wil, skb);
+ if (!key)
+ return false;
+
+ key_info = be16_to_cpu(key->key_info);
+ if (key_info & (WIL_KEY_INFO_MIC |
+ WIL_KEY_INFO_ENCR_KEY_DATA)) {
+ /* 3/4 of 4-Way Handshake */
+ wil_dbg_misc(wil, "EAPOL key message 3\n");
+ return true;
+ }
+ /* 1/4 of 4-Way Handshake */
+ wil_dbg_misc(wil, "EAPOL key message 1\n");
+
+ return false;
+}
+
+static bool wil_skb_is_eap_4(struct wil6210_priv *wil, struct sk_buff *skb)
+{
+ struct wil_eapol_key *key;
+ u32 *nonce, i;
+
+ key = wil_is_ptk_eapol_key(wil, skb);
+ if (!key)
+ return false;
+
+ nonce = (u32 *)key->key_nonce;
+ for (i = 0; i < WIL_EAP_NONCE_LEN / sizeof(u32); i++, nonce++) {
+ if (*nonce != 0) {
+ /* message 2/4 */
+ wil_dbg_misc(wil, "EAPOL key message 2\n");
+ return false;
+ }
+ }
+ wil_dbg_misc(wil, "EAPOL key message 4\n");
+
+ return true;
+}
+
+void wil_enable_tx_key_worker(struct work_struct *work)
+{
+ struct wil6210_vif *vif = container_of(work,
+ struct wil6210_vif, enable_tx_key_worker);
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ int rc, cid;
+
+ rtnl_lock();
+ if (vif->ptk_rekey_state != WIL_REKEY_WAIT_M4_SENT) {
+ wil_dbg_misc(wil, "Invalid rekey state = %d\n",
+ vif->ptk_rekey_state);
+ rtnl_unlock();
+ return;
+ }
+
+ cid = wil_find_cid_by_idx(wil, vif->mid, 0);
+ if (!wil_cid_valid(wil, cid)) {
+ wil_err(wil, "Invalid cid = %d\n", cid);
+ rtnl_unlock();
+ return;
+ }
+
+ wil_dbg_misc(wil, "Apply PTK key after eapol was sent out\n");
+ rc = wmi_add_cipher_key(vif, 0, wil->sta[cid].addr, 0, NULL,
+ WMI_KEY_USE_APPLY_PTK);
+
+ vif->ptk_rekey_state = WIL_REKEY_IDLE;
+ rtnl_unlock();
+
+ if (rc)
+ wil_err(wil, "Apply PTK key failed %d\n", rc);
+}
+
+void wil_tx_complete_handle_eapol(struct wil6210_vif *vif, struct sk_buff *skb)
+{
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+ bool q = false;
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION ||
+ !test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities))
+ return;
+
+ /* check if skb is an EAP message 4/4 */
+ if (!wil_skb_is_eap_4(wil, skb))
+ return;
+
+ spin_lock_bh(&wil->eap_lock);
+ switch (vif->ptk_rekey_state) {
+ case WIL_REKEY_IDLE:
+ /* ignore idle state, can happen due to M4 retransmission */
+ break;
+ case WIL_REKEY_M3_RECEIVED:
+ vif->ptk_rekey_state = WIL_REKEY_IDLE;
+ break;
+ case WIL_REKEY_WAIT_M4_SENT:
+ q = true;
+ break;
+ default:
+ wil_err(wil, "Unknown rekey state = %d",
+ vif->ptk_rekey_state);
+ }
+ spin_unlock_bh(&wil->eap_lock);
+
+ if (q) {
+ q = queue_work(wil->wmi_wq, &vif->enable_tx_key_worker);
+ wil_dbg_misc(wil, "queue_work of enable_tx_key_worker -> %d\n",
+ q);
+ }
+}
+
+static void wil_rx_handle_eapol(struct wil6210_vif *vif, struct sk_buff *skb)
+{
+ struct wil6210_priv *wil = vif_to_wil(vif);
+ struct wireless_dev *wdev = vif_to_wdev(vif);
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION ||
+ !test_bit(WMI_FW_CAPABILITY_SPLIT_REKEY, wil->fw_capabilities))
+ return;
+
+ /* check if skb is a EAP message 3/4 */
+ if (!wil_skb_is_eap_3(wil, skb))
+ return;
+
+ if (vif->ptk_rekey_state == WIL_REKEY_IDLE)
+ vif->ptk_rekey_state = WIL_REKEY_M3_RECEIVED;
+}
+
+/*
* Pass Rx packet to the netif. Update statistics.
* Called in softirq context (NAPI poll).
*/
@@ -796,6 +972,10 @@ void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid,
if (skb) { /* deliver to local stack */
skb->protocol = eth_type_trans(skb, ndev);
skb->dev = ndev;
+
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE))
+ wil_rx_handle_eapol(vif, skb);
+
if (gro)
rc = napi_gro_receive(&wil->napi_rx, skb);
else
@@ -2332,6 +2512,10 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid)
if (stats)
stats->tx_errors++;
}
+
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE))
+ wil_tx_complete_handle_eapol(vif, skb);
+
wil_consume_skb(skb, d->dma.error == 0);
}
memset(ctx, 0, sizeof(*ctx));
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index fceb251..5120475b 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -423,6 +423,46 @@ struct vring_rx_mac {
#define RX_DMA_STATUS_PHY_INFO BIT(6)
#define RX_DMA_STATUS_FFM BIT(7) /* EtherType Flex Filter Match */

+/* IEEE 802.11, 8.5.2 EAPOL-Key frames */
+#define WIL_KEY_INFO_KEY_TYPE BIT(3) /* val of 1 = Pairwise, 0 = Group key */
+
+#define WIL_KEY_INFO_MIC BIT(8)
+#define WIL_KEY_INFO_ENCR_KEY_DATA BIT(12) /* for rsn only */
+
+#define WIL_EAP_NONCE_LEN 32
+#define WIL_EAP_KEY_RSC_LEN 8
+#define WIL_EAP_REPLAY_COUNTER_LEN 8
+#define WIL_EAP_KEY_IV_LEN 16
+#define WIL_EAP_KEY_ID_LEN 8
+
+enum {
+ WIL_1X_TYPE_EAP_PACKET = 0,
+ WIL_1X_TYPE_EAPOL_START = 1,
+ WIL_1X_TYPE_EAPOL_LOGOFF = 2,
+ WIL_1X_TYPE_EAPOL_KEY = 3,
+};
+
+#define WIL_EAPOL_KEY_TYPE_RSN 2
+#define WIL_EAPOL_KEY_TYPE_WPA 254
+
+struct wil_1x_hdr {
+ u8 version;
+ u8 type;
+ __be16 length;
+ /* followed by data */
+} __packed;
+
+struct wil_eapol_key {
+ u8 type;
+ __be16 key_info;
+ __be16 key_length;
+ u8 replay_counter[WIL_EAP_REPLAY_COUNTER_LEN];
+ u8 key_nonce[WIL_EAP_NONCE_LEN];
+ u8 key_iv[WIL_EAP_KEY_IV_LEN];
+ u8 key_rsc[WIL_EAP_KEY_RSC_LEN];
+ u8 key_id[WIL_EAP_KEY_ID_LEN];
+} __packed;
+
struct vring_rx_dma {
u32 d0;
struct wil_ring_dma_addr addr;
diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c
index 29a9a24..f313041 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.c
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c
@@ -1257,6 +1257,10 @@ int wil_tx_sring_handler(struct wil6210_priv *wil,
if (stats)
stats->tx_errors++;
}
+
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE))
+ wil_tx_complete_handle_eapol(vif, skb);
+
wil_consume_skb(skb, msg.status == 0);
}
memset(ctx, 0, sizeof(*ctx));
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 6463310..dd0c87a 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -732,6 +732,12 @@ enum wil_sta_status {
wil_sta_connected = 2,
};

+enum wil_rekey_state {
+ WIL_REKEY_IDLE = 0,
+ WIL_REKEY_M3_RECEIVED = 1,
+ WIL_REKEY_WAIT_M4_SENT = 2,
+};
+
/**
* struct wil_sta_info - data for peer
*
@@ -880,6 +886,10 @@ struct wil6210_vif {
int net_queue_stopped; /* netif_tx_stop_all_queues invoked */
bool fw_stats_ready; /* per-cid statistics are ready inside sta_info */
u64 fw_stats_tsf; /* measurement timestamp */
+
+ /* PTK rekey race prevention, this is relevant to station mode only */
+ enum wil_rekey_state ptk_rekey_state;
+ struct work_struct enable_tx_key_worker;
};

/**
@@ -980,6 +990,7 @@ struct wil6210_priv {
*/
spinlock_t wmi_ev_lock;
spinlock_t net_queue_lock; /* guarding stop/wake netif queue */
+ spinlock_t eap_lock; /* guarding access to eap rekey fields */
struct napi_struct napi_rx;
struct napi_struct napi_tx;
struct net_device napi_ndev; /* dummy net_device serving all VIFs */
@@ -1229,6 +1240,7 @@ int wil_ps_update(struct wil6210_priv *wil,
void wil_refresh_fw_capabilities(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, u8 mid, const u8 *mac);
+int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx);
void wil_set_ethtoolops(struct net_device *ndev);

struct fw_map *wil_find_fw_mapping(const char *section);
@@ -1354,6 +1366,7 @@ void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
void wil_probe_client_flush(struct wil6210_vif *vif);
void wil_probe_client_worker(struct work_struct *work);
void wil_disconnect_worker(struct work_struct *work);
+void wil_enable_tx_key_worker(struct work_struct *work);

void wil_init_txrx_ops(struct wil6210_priv *wil);

@@ -1373,6 +1386,8 @@ void wil_update_net_queues_bh(struct wil6210_priv *wil, struct wil6210_vif *vif,
struct wil_ring *ring, bool check_stop);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
int wil_tx_complete(struct wil6210_vif *vif, int ringid);
+void wil_tx_complete_handle_eapol(struct wil6210_vif *vif,
+ struct sk_buff *skb);
void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil);

diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 5760d14..73fe9bf 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -2438,10 +2438,17 @@ int wmi_add_cipher_key(struct wil6210_vif *vif, u8 key_index,
.key_len = key_len,
};

- if (!key || (key_len > sizeof(cmd.key)))
+ if (key_len > sizeof(cmd.key))
return -EINVAL;

- memcpy(cmd.key, key, key_len);
+ /* key len = 0 is allowed only for usage of WMI_KEY_USE_APPLY */
+ if ((key_len == 0 || !key) &&
+ key_usage != WMI_KEY_USE_APPLY_PTK)
+ return -EINVAL;
+
+ if (key)
+ memcpy(cmd.key, key, key_len);
+
if (mac_addr)
memcpy(cmd.mac, mac_addr, WMI_MAC_LEN);

diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index d75022b..a2f7034 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -109,6 +109,7 @@ enum wmi_fw_capability {
WMI_FW_CAPABILITY_CHANNEL_4 = 26,
WMI_FW_CAPABILITY_IPA = 27,
WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF = 30,
+ WMI_FW_CAPABILITY_SPLIT_REKEY = 31,
WMI_FW_CAPABILITY_MAX,
};

@@ -421,6 +422,8 @@ enum wmi_key_usage {
WMI_KEY_USE_PAIRWISE = 0x00,
WMI_KEY_USE_RX_GROUP = 0x01,
WMI_KEY_USE_TX_GROUP = 0x02,
+ WMI_KEY_USE_STORE_PTK = 0x03,
+ WMI_KEY_USE_APPLY_PTK = 0x04,
};

struct wmi_add_cipher_key_cmd {
--
1.9.1

2019-09-08 12:58:17

by Maya Erez

[permalink] [raw]
Subject: [PATCH 05/11] wil6210: make sure DR bit is read before rest of the status message

From: Dedy Lansky <[email protected]>

Due to compiler optimization, it's possible that dr_bit (descriptor
ready) is read last from the status message.
Due to race condition between HW writing the status message and
driver reading it, other fields that were read earlier (before dr_bit)
could have invalid values.

Fix this by explicitly reading the dr_bit first and then using rmb
before reading the rest of the status message.

Signed-off-by: Dedy Lansky <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/txrx_edma.c | 30 +++++++++++++++++-----------
drivers/net/wireless/ath/wil6210/txrx_edma.h | 6 ------
2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c
index f313041..f21b2fa 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.c
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c
@@ -221,10 +221,17 @@ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil,
}

static inline
-void wil_get_next_rx_status_msg(struct wil_status_ring *sring, void *msg)
+void wil_get_next_rx_status_msg(struct wil_status_ring *sring, u8 *dr_bit,
+ void *msg)
{
- memcpy(msg, (void *)(sring->va + (sring->elem_size * sring->swhead)),
- sring->elem_size);
+ struct wil_rx_status_compressed *_msg;
+
+ _msg = (struct wil_rx_status_compressed *)
+ (sring->va + (sring->elem_size * sring->swhead));
+ *dr_bit = WIL_GET_BITS(_msg->d0, 31, 31);
+ /* make sure dr_bit is read before the rest of status msg */
+ rmb();
+ memcpy(msg, (void *)_msg, sring->elem_size);
}

static inline void wil_sring_advance_swhead(struct wil_status_ring *sring)
@@ -587,8 +594,7 @@ static bool wil_is_rx_idle_edma(struct wil6210_priv *wil)
if (!sring->va)
continue;

- wil_get_next_rx_status_msg(sring, msg);
- dr_bit = wil_rx_status_get_desc_rdy_bit(msg);
+ wil_get_next_rx_status_msg(sring, &dr_bit, msg);

/* Check if there are unhandled RX status messages */
if (dr_bit == sring->desc_rdy_pol)
@@ -878,8 +884,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb));

again:
- wil_get_next_rx_status_msg(sring, msg);
- dr_bit = wil_rx_status_get_desc_rdy_bit(msg);
+ wil_get_next_rx_status_msg(sring, &dr_bit, msg);

/* Completed handling all the ready status messages */
if (dr_bit != sring->desc_rdy_pol)
@@ -1135,12 +1140,15 @@ static int wil_tx_desc_map_edma(union wil_tx_desc *desc,
}

static inline void
-wil_get_next_tx_status_msg(struct wil_status_ring *sring,
+wil_get_next_tx_status_msg(struct wil_status_ring *sring, u8 *dr_bit,
struct wil_ring_tx_status *msg)
{
struct wil_ring_tx_status *_msg = (struct wil_ring_tx_status *)
(sring->va + (sring->elem_size * sring->swhead));

+ *dr_bit = _msg->desc_ready >> TX_STATUS_DESC_READY_POS;
+ /* make sure dr_bit is read before the rest of status msg */
+ rmb();
*msg = *_msg;
}

@@ -1169,8 +1177,7 @@ int wil_tx_sring_handler(struct wil6210_priv *wil,
int used_before_complete;
int used_new;

- wil_get_next_tx_status_msg(sring, &msg);
- dr_bit = msg.desc_ready >> TX_STATUS_DESC_READY_POS;
+ wil_get_next_tx_status_msg(sring, &dr_bit, &msg);

/* Process completion messages while DR bit has the expected polarity */
while (dr_bit == sring->desc_rdy_pol) {
@@ -1293,8 +1300,7 @@ int wil_tx_sring_handler(struct wil6210_priv *wil,

wil_sring_advance_swhead(sring);

- wil_get_next_tx_status_msg(sring, &msg);
- dr_bit = msg.desc_ready >> TX_STATUS_DESC_READY_POS;
+ wil_get_next_tx_status_msg(sring, &dr_bit, &msg);
}

/* shall we wake net queues? */
diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h
index 724d223..136c51c 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.h
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h
@@ -421,12 +421,6 @@ static inline u8 wil_rx_status_get_tid(void *msg)
return val & WIL_RX_EDMA_DLPF_LU_MISS_CID_TID_MASK;
}

-static inline int wil_rx_status_get_desc_rdy_bit(void *msg)
-{
- return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0,
- 31, 31);
-}
-
static inline int wil_rx_status_get_eop(void *msg) /* EoP = End of Packet */
{
return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0,
--
1.9.1

2019-09-08 12:58:18

by Maya Erez

[permalink] [raw]
Subject: [PATCH 08/11] wil6210: report boottime_ns in scan results

Call cfg80211_inform_bss_frame_data to report cfg80211 on the
boottime_ns in order to prevent the scan results filtering due to
aging.

Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/wmi.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 88d9e5a..153b844 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -878,6 +878,12 @@ static void wmi_evt_rx_mgmt(struct wil6210_vif *vif, int id, void *d, int len)

if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
struct cfg80211_bss *bss;
+ struct cfg80211_inform_bss bss_data = {
+ .chan = channel,
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .signal = signal,
+ .boottime_ns = ktime_to_ns(ktime_get_boottime()),
+ };
u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
@@ -892,8 +898,9 @@ static void wmi_evt_rx_mgmt(struct wil6210_vif *vif, int id, void *d, int len)

wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);

- bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
- d_len, signal, GFP_KERNEL);
+ bss = cfg80211_inform_bss_frame_data(wiphy, &bss_data,
+ rx_mgmt_frame,
+ d_len, GFP_KERNEL);
if (bss) {
wil_dbg_wmi(wil, "Added BSS %pM\n",
rx_mgmt_frame->bssid);
@@ -1391,6 +1398,10 @@ static void wmi_evt_delba(struct wil6210_vif *vif, int id, void *d, int len)
__le16 fc;
u32 d_len;
struct cfg80211_bss *bss;
+ struct cfg80211_inform_bss bss_data = {
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .boottime_ns = ktime_to_ns(ktime_get_boottime()),
+ };

if (flen < 0) {
wil_err(wil, "sched scan result event too short, len %d\n",
@@ -1433,8 +1444,10 @@ static void wmi_evt_delba(struct wil6210_vif *vif, int id, void *d, int len)
return;
}

- bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
- d_len, signal, GFP_KERNEL);
+ bss_data.signal = signal;
+ bss_data.chan = channel;
+ bss = cfg80211_inform_bss_frame_data(wiphy, &bss_data, rx_mgmt_frame,
+ d_len, GFP_KERNEL);
if (bss) {
wil_dbg_wmi(wil, "Added BSS %pM\n", rx_mgmt_frame->bssid);
cfg80211_put_bss(wiphy, bss);
--
1.9.1

2019-09-08 12:58:19

by Maya Erez

[permalink] [raw]
Subject: [PATCH 09/11] wil6210: use writel_relaxed in wil_debugfs_iomem_x32_set

From: Lior David <[email protected]>

writel_relaxed can be used in wil_debugfs_iomem_x32_set
since there is a wmb call immediately after.

Signed-off-by: Lior David <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/debugfs.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 50dc30e..304b4d4 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -393,7 +393,8 @@ static int wil_debugfs_iomem_x32_set(void *data, u64 val)
if (ret < 0)
return ret;

- writel(val, (void __iomem *)d->offset);
+ writel_relaxed(val, (void __iomem *)d->offset);
+
wmb(); /* make sure write propagated to HW */

wil_pm_runtime_put(wil);
--
1.9.1

2019-09-08 12:58:20

by Maya Erez

[permalink] [raw]
Subject: [PATCH 10/11] wil6210: fix RX short frame check

From: Lior David <[email protected]>

The short frame check in wil_sring_reap_rx_edma uses
skb->len which store the maximum frame length. Fix
this to use dmalen which is the actual length of
the received frame.

Signed-off-by: Lior David <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/txrx_edma.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c
index f21b2fa..04d576d 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.c
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c
@@ -964,8 +964,8 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
}
stats = &wil->sta[cid].stats;

- if (unlikely(skb->len < ETH_HLEN)) {
- wil_dbg_txrx(wil, "Short frame, len = %d\n", skb->len);
+ if (unlikely(dmalen < ETH_HLEN)) {
+ wil_dbg_txrx(wil, "Short frame, len = %d\n", dmalen);
stats->rx_short_frame++;
rxdata->skipping = true;
goto skipping;
--
1.9.1

2019-09-08 12:59:16

by Maya Erez

[permalink] [raw]
Subject: [PATCH 11/11] wil6210: ignore reset errors for FW during probe

From: Lior David <[email protected]>

There are special kinds of FW such as WMI only which
are used for testing, diagnostics and other specific
scenario.
Such FW is loaded during driver probe and the driver
disallows enabling any network interface, to avoid
operational issues.
In many cases it is used to debug early versions
of FW with new features, which sometimes fail
on startup.
Currently when such FW fails to load (for example,
because of init failure), the driver probe would fail
and shutdown the device making it difficult to debug
the early failure.
To fix this, ignore load failures in WMI only FW and
allow driver probe to succeed, making it possible to
continue and debug the FW load failure.

Signed-off-by: Lior David <[email protected]>
Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/pcie_bus.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index cd417fa..904dcfa 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -537,7 +537,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mutex_unlock(&wil->mutex);
if (rc) {
wil_err(wil, "failed to load WMI only FW\n");
- goto if_remove;
+ /* ignore the error to allow debugging */
}
}

@@ -557,8 +557,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)

return 0;

-if_remove:
- wil_if_remove(wil);
bus_disable:
wil_if_pcie_disable(wil);
err_iounmap:
--
1.9.1

2019-09-08 12:59:56

by Maya Erez

[permalink] [raw]
Subject: [PATCH 02/11] wil6210: add support for pci linkdown recovery

Some platforms can notify on pci linkdown events.
Add pci linkdown recovery flow, invoked upon pci linkdown
notification.
A new wil6210 status flag is added, wil_status_pci_linkdown, to
prevent pci suspend / resume until the recovery is completed.
After the pci link is recovered in the platform pci recovery
callback the device is being reset and the interface and AP
mode states are restored.

Signed-off-by: Maya Erez <[email protected]>
---
drivers/net/wireless/ath/wil6210/main.c | 31 +++++--
drivers/net/wireless/ath/wil6210/pcie_bus.c | 107 ++++++++++++++++++++++++
drivers/net/wireless/ath/wil6210/pm.c | 6 ++
drivers/net/wireless/ath/wil6210/wil6210.h | 6 ++
drivers/net/wireless/ath/wil6210/wil_platform.h | 8 ++
5 files changed, 149 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 173561f..0f270b8 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -533,22 +533,16 @@ bool wil_is_recovery_blocked(struct wil6210_priv *wil)
return no_fw_recovery && (wil->recovery_state == fw_recovery_pending);
}

-static void wil_fw_error_worker(struct work_struct *work)
+void wil_fw_recovery(struct wil6210_priv *wil)
{
- struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
- fw_error_worker);
struct net_device *ndev = wil->main_ndev;
struct wireless_dev *wdev;

- wil_dbg_misc(wil, "fw error worker\n");
+ wil_dbg_misc(wil, "fw recovery\n");

- if (!ndev || !(ndev->flags & IFF_UP)) {
- wil_info(wil, "No recovery - interface is down\n");
- return;
- }
wdev = ndev->ieee80211_ptr;

- /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
+ /* increment @recovery_count if less than WIL6210_FW_RECOVERY_TO
* passed since last recovery attempt
*/
if (time_is_after_jiffies(wil->last_fw_recovery +
@@ -608,6 +602,22 @@ static void wil_fw_error_worker(struct work_struct *work)
rtnl_unlock();
}

+static void wil_fw_error_worker(struct work_struct *work)
+{
+ struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+ fw_error_worker);
+ struct net_device *ndev = wil->main_ndev;
+
+ wil_dbg_misc(wil, "fw error worker\n");
+
+ if (!ndev || !(ndev->flags & IFF_UP)) {
+ wil_info(wil, "No recovery - interface is down\n");
+ return;
+ }
+
+ wil_fw_recovery(wil);
+}
+
static int wil_find_free_ring(struct wil6210_priv *wil)
{
int i;
@@ -720,6 +730,8 @@ int wil_priv_init(struct wil6210_priv *wil)

INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
+ INIT_WORK(&wil->pci_linkdown_recovery_worker,
+ wil_pci_linkdown_recovery_worker);

INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
@@ -836,6 +848,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)

wil_set_recovery_state(wil, fw_recovery_idle);
cancel_work_sync(&wil->fw_error_worker);
+ cancel_work_sync(&wil->pci_linkdown_recovery_worker);
wmi_event_flush(wil);
destroy_workqueue(wil->wq_service);
destroy_workqueue(wil->wmi_wq);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 9f5a914..cd417fa 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -296,6 +296,107 @@ static int wil_platform_rop_fw_recovery(void *wil_handle)
return 0;
}

+void wil_pci_linkdown_recovery_worker(struct work_struct *work)
+{
+ struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+ pci_linkdown_recovery_worker);
+ int rc, i;
+ struct wil6210_vif *vif;
+ struct net_device *ndev = wil->main_ndev;
+
+ wil_dbg_misc(wil, "starting pci_linkdown recovery\n");
+
+ rtnl_lock();
+ mutex_lock(&wil->mutex);
+ down_write(&wil->mem_lock);
+ clear_bit(wil_status_fwready, wil->status);
+ set_bit(wil_status_pci_linkdown, wil->status);
+ set_bit(wil_status_resetting, wil->status);
+ up_write(&wil->mem_lock);
+
+ if (test_and_clear_bit(wil_status_napi_en, wil->status)) {
+ napi_disable(&wil->napi_rx);
+ napi_disable(&wil->napi_tx);
+ }
+
+ mutex_unlock(&wil->mutex);
+ rtnl_unlock();
+
+ mutex_lock(&wil->mutex);
+
+ mutex_lock(&wil->vif_mutex);
+ wil_p2p_stop_radio_operations(wil);
+ wil_abort_scan_all_vifs(wil, false);
+ mutex_unlock(&wil->vif_mutex);
+
+ for (i = 0; i < wil->max_vifs; i++) {
+ vif = wil->vifs[i];
+ if (vif) {
+ cancel_work_sync(&vif->disconnect_worker);
+ wil6210_disconnect(vif, NULL,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+ }
+
+ wmi_event_flush(wil);
+ flush_workqueue(wil->wq_service);
+ flush_workqueue(wil->wmi_wq);
+
+ /* Recover PCIe */
+ if (wil->platform_ops.pci_linkdown_recovery) {
+ rc = wil->platform_ops.pci_linkdown_recovery(
+ wil->platform_handle);
+ if (rc) {
+ wil_err(wil,
+ "platform device failed to recover from pci linkdown (%d)\n",
+ rc);
+ goto out;
+ }
+ } else {
+ wil_err(wil,
+ "platform device doesn't support pci_linkdown recovery\n");
+ goto out;
+ }
+
+ if (!ndev || !(ndev->flags & IFF_UP)) {
+ wil_reset(wil, false);
+ mutex_unlock(&wil->mutex);
+ } else {
+ mutex_unlock(&wil->mutex);
+ wil->recovery_state = fw_recovery_pending;
+ wil_fw_recovery(wil);
+ }
+
+ return;
+
+out:
+ mutex_unlock(&wil->mutex);
+}
+
+static int wil_platform_rop_notify(void *wil_handle,
+ enum wil_platform_notif notif)
+{
+ struct wil6210_priv *wil = wil_handle;
+
+ if (!wil)
+ return -EINVAL;
+
+ switch (notif) {
+ case WIL_PLATFORM_NOTIF_PCI_LINKDOWN:
+ wil_info(wil, "received WIL_PLATFORM_NOTIF_PCI_LINKDOWN\n");
+ clear_bit(wil_status_fwready, wil->status);
+ set_bit(wil_status_resetting, wil->status);
+ set_bit(wil_status_pci_linkdown, wil->status);
+
+ schedule_work(&wil->pci_linkdown_recovery_worker);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void wil_platform_ops_uninit(struct wil6210_priv *wil)
{
if (wil->platform_ops.uninit)
@@ -311,6 +412,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
const struct wil_platform_rops rops = {
.ramdump = wil_platform_rop_ramdump,
.fw_recovery = wil_platform_rop_fw_recovery,
+ .notify = wil_platform_rop_notify,
};
u32 bar_size = pci_resource_len(pdev, 0);
int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */
@@ -548,6 +650,11 @@ static int wil6210_resume(struct device *dev, bool is_runtime)
struct wil6210_priv *wil = pci_get_drvdata(pdev);
bool keep_radio_on, active_ifaces;

+ if (test_bit(wil_status_pci_linkdown, wil->status)) {
+ wil_dbg_pm(wil, "ignore resume during pci linkdown\n");
+ return 0;
+ }
+
wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");

mutex_lock(&wil->vif_mutex);
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index 56143e7..bc642d0 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -101,6 +101,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
goto out;
}

+ if (test_bit(wil_status_pci_linkdown, wil->status)) {
+ wil_dbg_pm(wil, "Delay suspend during pci linkdown\n");
+ rc = -EBUSY;
+ goto out;
+ }
+
mutex_lock(&wil->vif_mutex);
active_ifaces = wil_has_active_ifaces(wil, true, false);
mutex_unlock(&wil->vif_mutex);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index ecda3ca..6463310 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -661,6 +661,7 @@ enum { /* for wil6210_priv.status */
wil_status_suspending, /* suspend in progress */
wil_status_suspended, /* suspend completed, device is suspended */
wil_status_resuming, /* resume in progress */
+ wil_status_pci_linkdown, /* pci linkdown occurred */
wil_status_last /* keep last */
};

@@ -1059,6 +1060,8 @@ struct wil6210_priv {

u32 max_agg_wsize;
u32 max_ampdu_size;
+
+ struct work_struct pci_linkdown_recovery_worker;
};

#define wil_to_wiphy(i) (i->wiphy)
@@ -1354,6 +1357,9 @@ void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,

void wil_init_txrx_ops(struct wil6210_priv *wil);

+void wil_fw_recovery(struct wil6210_priv *wil);
+void wil_pci_linkdown_recovery_worker(struct work_struct *work);
+
/* TX API */
int wil_ring_init_tx(struct wil6210_vif *vif, int cid);
int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size);
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
index bca0906..20f69e8 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.h
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -27,6 +27,10 @@ enum wil_platform_event {
WIL_PLATFORM_EVT_POST_SUSPEND = 4,
};

+enum wil_platform_notif {
+ WIL_PLATFORM_NOTIF_PCI_LINKDOWN = 0,
+};
+
enum wil_platform_features {
WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0,
WIL_PLATFORM_FEATURE_TRIPLE_MSI = 1,
@@ -52,6 +56,7 @@ struct wil_platform_ops {
int (*notify)(void *handle, enum wil_platform_event evt);
int (*get_capa)(void *handle);
void (*set_features)(void *handle, int features);
+ int (*pci_linkdown_recovery)(void *handle);
};

/**
@@ -63,10 +68,13 @@ struct wil_platform_ops {
* @fw_recovery: start a firmware recovery process. Called as
* part of a crash recovery process which may include other
* related platform subsystems.
+ * @notify: get notifications from the Platform driver, such as
+ * pci linkdown
*/
struct wil_platform_rops {
int (*ramdump)(void *wil_handle, void *buf, uint32_t size);
int (*fw_recovery)(void *wil_handle);
+ int (*notify)(void *wil_handle, enum wil_platform_notif notif);
};

/**
--
1.9.1

2019-09-10 13:59:50

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Maya Erez <[email protected]> wrote:

> Fix a race between cfg80211 add_key call and transmitting of 4/4 EAP
> packet. In case the transmit is delayed until after the add key takes
> place, message 4/4 will be encrypted with the new key, and the
> receiver side (AP) will drop it due to MIC error.
>
> Wil6210 will monitor and look for the transmitted packet 4/4 eap key.
> In case add_key takes place before the transmission completed, then
> wil6210 will let the FW store the key and wil6210 will notify the FW
> to use the PTK key only after 4/4 eap packet transmission was
> completed.

This is rather ugly but I guess still ok. Or what do people think?

But for a proper fix you should look at:

[PATCH v2] wpa_supplicant: Send EAPoL-Key frames over NL80211 where available

http://lists.infradead.org/pipermail/hostap/2019-September/040516.html

--
https://patchwork.kernel.org/patch/11136851/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2019-09-11 07:52:24

by Arend Van Spriel

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race



On 9/10/2019 3:23 PM, Kalle Valo wrote:
> Maya Erez <[email protected]> wrote:
>
>> Fix a race between cfg80211 add_key call and transmitting of 4/4 EAP
>> packet. In case the transmit is delayed until after the add key takes
>> place, message 4/4 will be encrypted with the new key, and the
>> receiver side (AP) will drop it due to MIC error.
>>
>> Wil6210 will monitor and look for the transmitted packet 4/4 eap key.
>> In case add_key takes place before the transmission completed, then
>> wil6210 will let the FW store the key and wil6210 will notify the FW
>> to use the PTK key only after 4/4 eap packet transmission was
>> completed.
>
> This is rather ugly but I guess still ok. Or what do people think?

The idea is similar to what we have in brcmfmac although it looks like a
lot more code. So there seems precedent for the approach.

> But for a proper fix you should look at:
>
> [PATCH v2] wpa_supplicant: Send EAPoL-Key frames over NL80211 where available
>
> http://lists.infradead.org/pipermail/hostap/2019-September/040516.html

However, I agree that Denis did a better job with this and we should aim
to use it. It is on my largish TODO list for brcmfmac.

Regards,
Arend

2019-09-11 18:41:05

by Alexander Wetzel

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Am 10.09.19 um 15:23 schrieb Kalle Valo:
> Maya Erez <[email protected]> wrote:
>
>> Fix a race between cfg80211 add_key call and transmitting of 4/4 EAP
>> packet. In case the transmit is delayed until after the add key takes
>> place, message 4/4 will be encrypted with the new key, and the
>> receiver side (AP) will drop it due to MIC error.
>>
>> Wil6210 will monitor and look for the transmitted packet 4/4 eap key.
>> In case add_key takes place before the transmission completed, then
>> wil6210 will let the FW store the key and wil6210 will notify the FW
>> to use the PTK key only after 4/4 eap packet transmission was
>> completed.
>
> This is rather ugly but I guess still ok. Or what do people think?
>

I don't know anything about the driver here but in mac80211 the idea to
avoid the race is to simply flush the queues prior deleting the outgoing
key.

Now wpa_supplicant is not yet bypassing qdisks, but adding the socket
parameter PACKET_QDISC_BYPASS is basically a one-liner in wpa_supplicant
and should allow a generic way for drivers to avoid the race with a
simple queue flush...

Alexander

2019-09-12 15:11:39

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 01/11] wil6210: add wil_netif_rx() helper function

Maya Erez <[email protected]> wrote:

> Move common part of wil_netif_rx_any into new helper function and add
> support for non-gro receive using netif_rx_ni.
>
> Signed-off-by: Dedy Lansky <[email protected]>
> Signed-off-by: Maya Erez <[email protected]>
> Signed-off-by: Kalle Valo <[email protected]>

10 patches applied to ath-next branch of ath.git, thanks.

f99fe49ff372 wil6210: add wil_netif_rx() helper function
977c45ab5f41 wil6210: add debugfs to show PMC ring content
42fe1e519e9f wil6210: fix PTK re-key race
f4519fd9375d wil6210: make sure DR bit is read before rest of the status message
e78975fcdae4 wil6210: verify cid value is valid
068f359aac40 wil6210: properly initialize discovery_expired_work
058b3f112419 wil6210: report boottime_ns in scan results
0e698cd0b94c wil6210: use writel_relaxed in wil_debugfs_iomem_x32_set
055c8a71eb5b wil6210: fix RX short frame check
50e107ff2213 wil6210: ignore reset errors for FW during probe

--
https://patchwork.kernel.org/patch/11136849/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2019-09-12 15:24:48

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 02/11] wil6210: add support for pci linkdown recovery

Maya Erez <[email protected]> wrote:

> Some platforms can notify on pci linkdown events.
> Add pci linkdown recovery flow, invoked upon pci linkdown
> notification.
> A new wil6210 status flag is added, wil_status_pci_linkdown, to
> prevent pci suspend / resume until the recovery is completed.
> After the pci link is recovered in the platform pci recovery
> callback the device is being reset and the interface and AP
> mode states are restored.
>
> Signed-off-by: Maya Erez <[email protected]>

Dropping per Maya's request

Patch set to Changes Requested.

--
https://patchwork.kernel.org/patch/11136843/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2019-09-12 19:49:14

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Hi Alexander,

> I don't know anything about the driver here but in mac80211 the idea to
> avoid the race is to simply flush the queues prior deleting the outgoing
> key.

Maybe a silly question, but what does flushing the queue mean in this
context? Is it waiting for all the packets to be sent or dropping them
on the floor?

>
> Now wpa_supplicant is not yet bypassing qdisks, but adding the socket
> parameter PACKET_QDISC_BYPASS is basically a one-liner in wpa_supplicant
> and should allow a generic way for drivers to avoid the race with a
> simple queue flush...

Can you expand on this actually? What would the sequence of events be?

Also, how would this be made to work with CONTROL_PORT over NL80211 ?

Regards,
-Denis

2019-09-12 22:32:30

by Alexander Wetzel

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Hi Denis,

>> I don't know anything about the driver here but in mac80211 the idea
>> to avoid the race is to simply flush the queues prior deleting the
>> outgoing key.
>
> Maybe a silly question, but what does flushing the queue mean in this
> context?  Is it waiting for all the packets to be sent or dropping them
> on the floor?
>

It's stopping them to make sure nothing can be added and then sends out
all MPDUs in the queues.

>>
>> Now wpa_supplicant is not yet bypassing qdisks, but adding the socket
>> parameter PACKET_QDISC_BYPASS is basically a one-liner in
>> wpa_supplicant and should allow a generic way for drivers to avoid the
>> race with a simple queue flush...
>
> Can you expand on this actually?  What would the sequence of events be?
>

1) wpa_supplicant hands over eapol #4 to the kernel.
When bypassing the QDISC the frame is directly added to a driver
queue or directly send out. When the send call returns the driver
has eapol 4 either in the queuem already send it or the send command
has failed.

2) wpa_supplicant deletes the old key (NL80211_CMD_DEL_KEY)

3) The driver stops all hw queues and sends out all MPDUs queued up to
that time

4) Driver makes sure no traffic can be send with no/wrong key or PN to
STA

5) the driver really removes the key from the HW/installs the new and
resumes normal operation

I've just posted my hostpad patch to use PACKET_QDISC_BYPASS for eapol
frames; It's probably too optimistic and need more code to retry a
transmit to compensate for the missing QDISC buffers.

> Also, how would this be made to work with CONTROL_PORT over NL80211 ?
>

Control port is an optional feature drivers can provide. wpa_supplicant
should just use it when available or fall back to the "traditional" path
when not. Now the driver don't has to flush all queues when using
control port, as long as it makes sure the control port frame will be
send out prior to deleting the key.

But then the driver must know that eapol frames will really be handed
over via control port; So I guess flushing all queues is still the
simpler solution. So I guess it will change next to nothing...

Alexander

2019-09-13 08:05:23

by Arend Van Spriel

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race



On 9/12/2019 11:04 PM, Alexander Wetzel wrote:
> Hi Denis,
>
>>> I don't know anything about the driver here but in mac80211 the idea
>>> to avoid the race is to simply flush the queues prior deleting the
>>> outgoing key.
>>
>> Maybe a silly question, but what does flushing the queue mean in this
>> context?  Is it waiting for all the packets to be sent or dropping
>> them on the floor?
>>
>
> It's stopping them to make sure nothing can be added and then sends out
> all MPDUs in the queues.
>
>>>
>>> Now wpa_supplicant is not yet bypassing qdisks, but adding the socket
>>> parameter PACKET_QDISC_BYPASS is basically a one-liner in
>>> wpa_supplicant and should allow a generic way for drivers to avoid
>>> the race with a simple queue flush...
>>
>> Can you expand on this actually?  What would the sequence of events be?
>>
>
> 1) wpa_supplicant hands over eapol #4 to the kernel.
>    When bypassing the QDISC the frame is directly added to a driver
>    queue or directly send out. When the send call returns the driver
>    has eapol 4 either in the queuem already send it or the send command
>    has failed.
>
> 2) wpa_supplicant deletes the old key (NL80211_CMD_DEL_KEY)
>
> 3) The driver stops all hw queues and sends out all MPDUs queued up to
>    that time
>
> 4) Driver makes sure no traffic can be send with no/wrong key or PN to
>    STA
>
> 5) the driver really removes the key from the HW/installs the new and
>    resumes normal operation
>
> I've just posted my hostpad patch to use PACKET_QDISC_BYPASS for eapol
> frames; It's probably too optimistic and need more code to retry a
> transmit to compensate for the missing QDISC buffers.
>
>> Also, how would this be made to work with CONTROL_PORT over NL80211 ?
>>
>
> Control port is an optional feature drivers can provide. wpa_supplicant
> should just use it when available or fall back to the "traditional" path
> when not. Now the driver don't has to flush all queues when using
> control port, as long as it makes sure the control port frame will be
> send out prior to deleting the key.
>
> But then the driver must know that eapol frames will really be handed
> over via control port; So I guess flushing all queues is still the
> simpler solution. So I guess it will change next to nothing...

Well, in the steps you describe (maybe its just how you describe it) it
relies on how the driver is handling it all. I mean step 4) seems more
the goal of the whole approach.

Basically, we now have two bypass methods dealing with the same/similar
issue:

1) bypass the QDISC.
2) bypass network stack entirely with CONTROL_PORT.

How does option 1) work for drivers that skip the QDISC for all traffic
and rely on mac80211 to schedule the packets? Guess mac80211 can control
that, right?

Regards,
Arend

2019-09-13 16:40:06

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Hi Arend, Alexander,

> Basically, we now have two bypass methods dealing with the same/similar
> issue:
>
> 1) bypass the QDISC.
> 2) bypass network stack entirely with CONTROL_PORT.

It also raises the question in my mind as to why we have two ways of
doing the same thing? From the discussion so far it also sounds like
each requires somewhat different / special handling in the driver.
Wouldn't it make sense to deprecate one and encourage drivers to
implement the other?

CONTROL_PORT was added specifically to take care of the re-keying races
and can be extended with additional attributes over time, as needed
(perhaps for extended key id, etc). Also note that in our testing
CONTROL_PORT is _way_ faster than PAE socket...

Regards,
-Denis

2019-09-13 22:06:59

by Alexander Wetzel

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Am 13.09.19 um 16:33 schrieb Denis Kenzior:
> Hi Arend, Alexander,
>
>> Basically, we now have two bypass methods dealing with the
>> same/similar issue:
>>
>> 1) bypass the QDISC.
>> 2) bypass network stack entirely with CONTROL_PORT.
>
> It also raises the question in my mind as to why we have two ways of
> doing the same thing?  From the discussion so far it also sounds like
> each requires somewhat different / special handling in the driver.
> Wouldn't it make sense to deprecate one and encourage drivers to
> implement the other?
>

My understanding is, that any control port frame must bypass any queues
and just send out the frame directly, correct? Any packets send via it
is directly jumping to the very front and is immediately send out.
And that the intend of it is to replace the "old" path.

So the best way forward here would be to

1) implement the patch here to work around the problem without
control_port or the theoretical QDISC bypass
2) start implementing control port for the future.

correct?


> CONTROL_PORT was added specifically to take care of the re-keying races
> and can be extended with additional attributes over time, as needed
> (perhaps for extended key id, etc).  Also note that in our testing
> CONTROL_PORT is _way_ faster than PAE socket...
>

Extended Key ID is pretty robust when rekeying and the driver/card only
has to take care to not mix KeyIDs within one A-MPDU. It's no problem
encrypting eapol#4 with the new key. You can even encrypt it at the
initial connect and it will work. Basically all races the "classical"
rekey has to work around go away.

For "normal" rekeys it's working pretty well with ath9k and iwlwifi even
without control_port and just learned some weeks ago that QDISC could
still cause issues...

Alexander



2019-09-14 14:41:17

by Alexander Wetzel

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

>> Hi Denis,
>>
>>>> I don't know anything about the driver here but in mac80211 the idea
>>>> to avoid the race is to simply flush the queues prior deleting the
>>>> outgoing key.
>>>
>>> Maybe a silly question, but what does flushing the queue mean in this
>>> context?  Is it waiting for all the packets to be sent or dropping
>>> them on the floor?
>>>
>>
>> It's stopping them to make sure nothing can be added and then sends
>> out all MPDUs in the queues.
>>
>>>>
>>>> Now wpa_supplicant is not yet bypassing qdisks, but adding the
>>>> socket parameter PACKET_QDISC_BYPASS is basically a one-liner in
>>>> wpa_supplicant and should allow a generic way for drivers to avoid
>>>> the race with a simple queue flush...
>>>
>>> Can you expand on this actually?  What would the sequence of events be?
>>>
>>
>> 1) wpa_supplicant hands over eapol #4 to the kernel.
>>     When bypassing the QDISC the frame is directly added to a driver
>>     queue or directly send out. When the send call returns the driver
>>     has eapol 4 either in the queuem already send it or the send command
>>     has failed.
>>
>> 2) wpa_supplicant deletes the old key (NL80211_CMD_DEL_KEY)
>>
>> 3) The driver stops all hw queues and sends out all MPDUs queued up to
>>     that time
>>
>> 4) Driver makes sure no traffic can be send with no/wrong key or PN to
>>     STA
>>
>> 5) the driver really removes the key from the HW/installs the new and
>>     resumes normal operation
>>
>> I've just posted my hostpad patch to use PACKET_QDISC_BYPASS for eapol
>> frames; It's probably too optimistic and need more code to retry a
>> transmit to compensate for the missing QDISC buffers.
>>
>>> Also, how would this be made to work with CONTROL_PORT over NL80211 ?
>>>
>>
>> Control port is an optional feature drivers can provide.
>> wpa_supplicant should just use it when available or fall back to the
>> "traditional" path when not. Now the driver don't has to flush all
>> queues when using control port, as long as it makes sure the control
>> port frame will be send out prior to deleting the key.
>>
>> But then the driver must know that eapol frames will really be handed
>> over via control port; So I guess flushing all queues is still the
>> simpler solution. So I guess it will change next to nothing...
>
> Well, in the steps you describe (maybe its just how you describe it) it
> relies on how the driver is handling it all. I mean step 4) seems more
> the goal of the whole approach.
>
Well, if you do not take care there are plenty of pitfalls a driver can
fall into when trying to rekey, especially when having ongoing traffic.
Most drivers will need some code to make sure they can safely delete the
old key for a STA and install a new one without a full disassociation.
Just what exactly is driver/hw depended. (I've detailed knowledge for
iwlfifi and ath9k and good guess how ath10 is handling it. All other
cards: No idea...)

I've tested around ten different cards (Android, iPhone, notebooks, usb
dongles) and found only two handling it correctly. The chances that
someone has both an AP and a device handling that correctly is therefore
not very good, but then my sample is still too small to be representative.
Known broken devices are e.g. Samsung galaxy S5, Nexus 5x, HTC 10, my
Samsumg Smart TV, iwlwifi cards (both windows and linux, just different)
and for sure any device using ath9k driver with a kernel < 4.19.

The only "good" devices I found were an iPhone (forgot the model) and a
Microsoft Surface Pro (also forgot the exact model)

I was focusing on cards I'm using: iwlwifi, ath9k and ath10k. Of those
cards ath10k was ok, iwlwif was working around 50% of the rekeys and
ath9k 100% broken (pretty sure it compromised even the security by
sending out the some frames two times: With encryption and without.

The details of that are best documented here, which fixed it for many -
but probably not all - mac80211 cards:
https://patchwork.kernel.org/patch/10583675/

The core idea here is, to tell hostpd/wpa_supplicant when a driver
believes it can rekey correctly and without that confirmation refuses to
reky but disconnect/reconects fast. But that is work in progress,
delayed by first implementing the "ideal" rekey solution added in IEEE
802.11 - 2102 "Extended key ID".

Problem is of course, that all card/drivers are handling things a bit
different and what works for one may well be broken for another. ath10k
is a good sample for that: Doing basically everything in HW it worked
quite well, bypassing the pitfalls.

The generic risks are:
- PN out of sync with the key (ath9k's main fault)
Especially risky are drivers using HW crypto but generating the PNs in
SW.

- A-MPDU sessions across rekeys. (Holding back MPDUs till all belonging
to the session are received. And then bump the PN for the new key to
the value the old key used. And then dropping all MPDUs for the new
key as "replay")

- Not stopping/blocking Tx depending on the outgoing key

- repeating lost frames originally send with the old with the new key


> Basically, we now have two bypass methods dealing with the same/similar
> issue:
>
> 1) bypass the QDISC.
> 2) bypass network stack entirely with CONTROL_PORT.
>
> How does option 1) work for drivers that skip the QDISC for all traffic

Which drivers skip QDISC, and how? I'm not aware of a way "normal"
network traffic can do that.
A "normal" linux wlan driver will register as a network card and short
of setting PACKET_QDISC_BYPASS on the socket or providing a non-standard
API all network traffic will pass trough QDISC. (But that's mostly new
area for me, just stitched the path together end2end some days ago.)

Now assuming you start a load generator using PACKET_QDISC_BYPASS and
try to rekey the connection: the NIC driver still will have the eapol#4
in one of it's queue. So stopping to add new skb's for the queues and
send out everything which is in the driver queues would send out tons of
other MPDUs, but one of them would be the eapol #4 one.

The only "trick" here is, that the the sendto() call from wpa_supplicant
up to the *driver* queues is atomic. With PACKET_QDISC_BYPASS set it -
at least for my understanding after investigating the issue some hours.

Once the sendto call returns and code execution in wpa_supplicant
continues - heading for the key deletion - the eapol #4 MPDU is
accessible for the driver and can be send out.

> and rely on mac80211 to schedule the packets? Guess mac80211 can control
> that, right?
>

Not sure i understand that part.. mac80211 is like the top half of a
wlan driver: It handles some parts but the full driver consists of
(mac80211 + low level driver).

Alexander

2019-09-17 18:14:09

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 04/11] wil6210: fix PTK re-key race

Hi Alexander,

> And that the intend of it is to replace the "old" path.
Correct.

>
> So the best way forward here would be to
>
> 1) implement the patch here to work around the problem without
> control_port or the theoretical QDISC bypass
> 2) start implementing control port for the future.
>
> correct?
>

I don't know what the right answer is, but it seems strange to me that
we developed a 'better way', upstreamed it several years ago, but are
still trying to kludge around adding special flags to what is now
considered a legacy approach. Also disconcerting that not a single
fullmac driver has added support for this 'better way' yet.

>
>> CONTROL_PORT was added specifically to take care of the re-keying
>> races and can be extended with additional attributes over time, as
>> needed (perhaps for extended key id, etc).  Also note that in our
>> testing CONTROL_PORT is _way_ faster than PAE socket...
>>
>
> Extended Key ID is pretty robust when rekeying and the driver/card only
> has to take care to not mix KeyIDs within one A-MPDU. It's no problem
> encrypting eapol#4 with the new key. You can even encrypt it at the
> initial connect and it will work. Basically all races the "classical"
> rekey has to work around go away.
>
> For "normal" rekeys it's working pretty well with ath9k and iwlwifi even
> without control_port and just learned some weeks ago that QDISC could
> still cause issues...

Okay, if control port doesn't need to handle extended keys then even better.

By the way, thanks for your earlier explanation (upthread).

Regards,
-Denis