mac80211: brcm: backport brcmfmac 5.2 patches
[openwrt/openwrt.git] / package / kernel / mac80211 / patches / brcm / 361-v5.2-0002-brcmfmac-fix-NULL-pointer-derefence-during-USB-disco.patch
diff --git a/package/kernel/mac80211/patches/brcm/361-v5.2-0002-brcmfmac-fix-NULL-pointer-derefence-during-USB-disco.patch b/package/kernel/mac80211/patches/brcm/361-v5.2-0002-brcmfmac-fix-NULL-pointer-derefence-during-USB-disco.patch
new file mode 100644 (file)
index 0000000..e45288b
--- /dev/null
@@ -0,0 +1,217 @@
+From 5cdb0ef6144f47440850553579aa923c20a63f23 Mon Sep 17 00:00:00 2001
+From: Piotr Figiel <p.figiel@camlintechnologies.com>
+Date: Mon, 4 Mar 2019 15:42:52 +0000
+Subject: [PATCH] brcmfmac: fix NULL pointer derefence during USB disconnect
+
+In case USB disconnect happens at the moment transmitting workqueue is in
+progress the underlying interface may be gone causing a NULL pointer
+dereference. Add synchronization of the workqueue destruction with the
+detach implementation in core so that the transmitting workqueue is stopped
+during detach before the interfaces are removed.
+
+Fix following Oops:
+
+Unable to handle kernel NULL pointer dereference at virtual address 00000008
+pgd = 9e6a802d
+[00000008] *pgd=00000000
+Internal error: Oops: 5 [#1] PREEMPT SMP ARM
+Modules linked in: nf_log_ipv4 nf_log_common xt_LOG xt_limit iptable_mangle
+xt_connmark xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4
+iptable_filter ip_tables x_tables usb_f_mass_storage usb_f_rndis u_ether
+usb_serial_simple usbserial cdc_acm brcmfmac brcmutil smsc95xx usbnet
+ci_hdrc_imx ci_hdrc ulpi usbmisc_imx 8250_exar 8250_pci 8250 8250_base
+libcomposite configfs udc_core
+CPU: 0 PID: 7 Comm: kworker/u8:0 Not tainted 4.19.23-00076-g03740aa-dirty #102
+Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
+Workqueue: brcmf_fws_wq brcmf_fws_dequeue_worker [brcmfmac]
+PC is at brcmf_txfinalize+0x34/0x90 [brcmfmac]
+LR is at brcmf_fws_dequeue_worker+0x218/0x33c [brcmfmac]
+pc : [<7f0dee64>]    lr : [<7f0e4140>]    psr: 60010093
+sp : ee8abef0  ip : 00000000  fp : edf38000
+r10: ffffffed  r9 : edf38970  r8 : edf38004
+r7 : edf3e970  r6 : 00000000  r5 : ede69000  r4 : 00000000
+r3 : 00000a97  r2 : 00000000  r1 : 0000888e  r0 : ede69000
+Flags: nZCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment none
+Control: 10c5387d  Table: 7d03c04a  DAC: 00000051
+Process kworker/u8:0 (pid: 7, stack limit = 0x24ec3e04)
+Stack: (0xee8abef0 to 0xee8ac000)
+bee0:                                     ede69000 00000000 ed56c3e0 7f0e4140
+bf00: 00000001 00000000 edf38004 edf3e99c ed56c3e0 80d03d00 edfea43a edf3e970
+bf20: ee809880 ee804200 ee971100 00000000 edf3e974 00000000 ee804200 80135a70
+bf40: 80d03d00 ee804218 ee809880 ee809894 ee804200 80d03d00 ee804218 ee8aa000
+bf60: 00000088 80135d5c 00000000 ee829f00 ee829dc0 00000000 ee809880 80135d30
+bf80: ee829f1c ee873eac 00000000 8013b1a0 ee829dc0 8013b07c 00000000 00000000
+bfa0: 00000000 00000000 00000000 801010e8 00000000 00000000 00000000 00000000
+bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
+[<7f0dee64>] (brcmf_txfinalize [brcmfmac]) from [<7f0e4140>] (brcmf_fws_dequeue_worker+0x218/0x33c [brcmfmac])
+[<7f0e4140>] (brcmf_fws_dequeue_worker [brcmfmac]) from [<80135a70>] (process_one_work+0x138/0x3f8)
+[<80135a70>] (process_one_work) from [<80135d5c>] (worker_thread+0x2c/0x554)
+[<80135d5c>] (worker_thread) from [<8013b1a0>] (kthread+0x124/0x154)
+[<8013b1a0>] (kthread) from [<801010e8>] (ret_from_fork+0x14/0x2c)
+Exception stack(0xee8abfb0 to 0xee8abff8)
+bfa0:                                     00000000 00000000 00000000 00000000
+bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
+Code: e1530001 0a000007 e3560000 e1a00005 (05942008)
+---[ end trace 079239dd31c86e90 ]---
+
+Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+ .../wireless/broadcom/brcm80211/brcmfmac/bcdc.c  | 11 +++++++++--
+ .../wireless/broadcom/brcm80211/brcmfmac/bcdc.h  |  6 ++++--
+ .../wireless/broadcom/brcm80211/brcmfmac/core.c  |  4 +++-
+ .../broadcom/brcm80211/brcmfmac/fwsignal.c       | 16 ++++++++++++----
+ .../broadcom/brcm80211/brcmfmac/fwsignal.h       |  3 ++-
+ .../wireless/broadcom/brcm80211/brcmfmac/proto.c | 10 ++++++++--
+ .../wireless/broadcom/brcm80211/brcmfmac/proto.h |  3 ++-
+ 7 files changed, 40 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
+@@ -490,11 +490,18 @@ fail:
+       return -ENOMEM;
+ }
+-void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
++void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr)
++{
++      struct brcmf_bcdc *bcdc = drvr->proto->pd;
++
++      brcmf_fws_detach_pre_delif(bcdc->fws);
++}
++
++void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr)
+ {
+       struct brcmf_bcdc *bcdc = drvr->proto->pd;
+       drvr->proto->pd = NULL;
+-      brcmf_fws_detach(bcdc->fws);
++      brcmf_fws_detach_post_delif(bcdc->fws);
+       kfree(bcdc);
+ }
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
+@@ -18,14 +18,16 @@
+ #ifdef CPTCFG_BRCMFMAC_PROTO_BCDC
+ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr);
+-void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr);
++void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr);
++void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr);
+ void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state);
+ void brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
+                                bool success);
+ struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr);
+ #else
+ static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; }
+-static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {}
++static void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr) {};
++static inline void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr) {}
+ #endif
+ #endif /* BRCMFMAC_BCDC_H */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -1342,6 +1342,8 @@ void brcmf_detach(struct device *dev)
+       brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
++      brcmf_proto_detach_pre_delif(drvr);
++
+       /* make sure primary interface removed last */
+       for (i = BRCMF_MAX_IFS-1; i > -1; i--)
+               brcmf_remove_interface(drvr->iflist[i], false);
+@@ -1351,7 +1353,7 @@ void brcmf_detach(struct device *dev)
+       brcmf_bus_stop(drvr->bus_if);
+-      brcmf_proto_detach(drvr);
++      brcmf_proto_detach_post_delif(drvr);
+       bus_if->drvr = NULL;
+       wiphy_free(drvr->wiphy);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
+@@ -2443,17 +2443,25 @@ struct brcmf_fws_info *brcmf_fws_attach(
+       return fws;
+ fail:
+-      brcmf_fws_detach(fws);
++      brcmf_fws_detach_pre_delif(fws);
++      brcmf_fws_detach_post_delif(fws);
+       return ERR_PTR(rc);
+ }
+-void brcmf_fws_detach(struct brcmf_fws_info *fws)
++void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws)
+ {
+       if (!fws)
+               return;
+-
+-      if (fws->fws_wq)
++      if (fws->fws_wq) {
+               destroy_workqueue(fws->fws_wq);
++              fws->fws_wq = NULL;
++      }
++}
++
++void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws)
++{
++      if (!fws)
++              return;
+       /* cleanup */
+       brcmf_fws_lock(fws);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
+@@ -19,7 +19,8 @@
+ #define FWSIGNAL_H_
+ struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr);
+-void brcmf_fws_detach(struct brcmf_fws_info *fws);
++void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws);
++void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws);
+ void brcmf_fws_debugfs_create(struct brcmf_pub *drvr);
+ bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws);
+ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
+@@ -67,16 +67,22 @@ fail:
+       return -ENOMEM;
+ }
+-void brcmf_proto_detach(struct brcmf_pub *drvr)
++void brcmf_proto_detach_post_delif(struct brcmf_pub *drvr)
+ {
+       brcmf_dbg(TRACE, "Enter\n");
+       if (drvr->proto) {
+               if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC)
+-                      brcmf_proto_bcdc_detach(drvr);
++                      brcmf_proto_bcdc_detach_post_delif(drvr);
+               else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF)
+                       brcmf_proto_msgbuf_detach(drvr);
+               kfree(drvr->proto);
+               drvr->proto = NULL;
+       }
+ }
++
++void brcmf_proto_detach_pre_delif(struct brcmf_pub *drvr)
++{
++      if (drvr->proto && drvr->bus_if->proto_type == BRCMF_PROTO_BCDC)
++              brcmf_proto_bcdc_detach_pre_delif(drvr);
++}
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
+@@ -54,7 +54,8 @@ struct brcmf_proto {
+ int brcmf_proto_attach(struct brcmf_pub *drvr);
+-void brcmf_proto_detach(struct brcmf_pub *drvr);
++void brcmf_proto_detach_pre_delif(struct brcmf_pub *drvr);
++void brcmf_proto_detach_post_delif(struct brcmf_pub *drvr);
+ static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+                                     struct sk_buff *skb,