| From 29b66a82de4b7c2c2d256b7f0079cb617a3b5bd7 Mon Sep 17 00:00:00 2001 |
| From: Piotr Figiel <p.figiel@camlintechnologies.com> |
| Date: Mon, 4 Mar 2019 15:42:49 +0000 |
| Subject: brcmfmac: fix WARNING during USB disconnect in case of unempty psq |
| |
| [ Upstream commit c80d26e81ef1802f30364b4ad1955c1443a592b9 ] |
| |
| brcmu_pkt_buf_free_skb emits WARNING when attempting to free a sk_buff |
| which is part of any queue. After USB disconnect this may have happened |
| when brcmf_fws_hanger_cleanup() is called as per-interface psq was never |
| cleaned when removing the interface. |
| Change brcmf_fws_macdesc_cleanup() in a way that it removes the |
| corresponding packets from hanger table (to avoid double-free when |
| brcmf_fws_hanger_cleanup() is called) and add a call to clean-up the |
| interface specific packet queue. |
| |
| Below is a WARNING during USB disconnect with Raspberry Pi WiFi dongle |
| running in AP mode. This was reproducible when the interface was |
| transmitting during the disconnect and is fixed with this commit. |
| |
| ------------[ cut here ]------------ |
| WARNING: CPU: 0 PID: 1171 at drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c:49 brcmu_pkt_buf_free_skb+0x3c/0x40 |
| 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 cdc_acm smsc95xx usbnet ci_hdrc_imx ci_hdrc ulpi usbmisc_imx 8250_exar 8250_pci 8250 8250_base libcomposite configfs udc_core |
| CPU: 0 PID: 1171 Comm: kworker/0:0 Not tainted 4.19.23-00075-gde33ed8 #99 |
| Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) |
| Workqueue: usb_hub_wq hub_event |
| [<8010ff84>] (unwind_backtrace) from [<8010bb64>] (show_stack+0x10/0x14) |
| [<8010bb64>] (show_stack) from [<80840278>] (dump_stack+0x88/0x9c) |
| [<80840278>] (dump_stack) from [<8011f5ec>] (__warn+0xfc/0x114) |
| [<8011f5ec>] (__warn) from [<8011f71c>] (warn_slowpath_null+0x40/0x48) |
| [<8011f71c>] (warn_slowpath_null) from [<805a476c>] (brcmu_pkt_buf_free_skb+0x3c/0x40) |
| [<805a476c>] (brcmu_pkt_buf_free_skb) from [<805bb6c4>] (brcmf_fws_cleanup+0x1e4/0x22c) |
| [<805bb6c4>] (brcmf_fws_cleanup) from [<805bc854>] (brcmf_fws_del_interface+0x58/0x68) |
| [<805bc854>] (brcmf_fws_del_interface) from [<805b66ac>] (brcmf_remove_interface+0x40/0x150) |
| [<805b66ac>] (brcmf_remove_interface) from [<805b6870>] (brcmf_detach+0x6c/0xb0) |
| [<805b6870>] (brcmf_detach) from [<805bdbb8>] (brcmf_usb_disconnect+0x30/0x4c) |
| [<805bdbb8>] (brcmf_usb_disconnect) from [<805e5d64>] (usb_unbind_interface+0x5c/0x1e0) |
| [<805e5d64>] (usb_unbind_interface) from [<804aab10>] (device_release_driver_internal+0x154/0x1ec) |
| [<804aab10>] (device_release_driver_internal) from [<804a97f4>] (bus_remove_device+0xcc/0xf8) |
| [<804a97f4>] (bus_remove_device) from [<804a6fc0>] (device_del+0x118/0x308) |
| [<804a6fc0>] (device_del) from [<805e488c>] (usb_disable_device+0xa0/0x1c8) |
| [<805e488c>] (usb_disable_device) from [<805dcf98>] (usb_disconnect+0x70/0x1d8) |
| [<805dcf98>] (usb_disconnect) from [<805ddd84>] (hub_event+0x464/0xf50) |
| [<805ddd84>] (hub_event) 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(0xecf8dfb0 to 0xecf8dff8) |
| dfa0: 00000000 00000000 00000000 00000000 |
| dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 |
| dfe0: 00000000 00000000 00000000 00000000 00000013 00000000 |
| ---[ end trace 38d234018e9e2a90 ]--- |
| ------------[ cut here ]------------ |
| |
| Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> |
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| .../broadcom/brcm80211/brcmfmac/fwsignal.c | 42 +++++++++++-------- |
| 1 file changed, 24 insertions(+), 18 deletions(-) |
| |
| diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c |
| index abeb305492e01..d48b8b2d946fe 100644 |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c |
| @@ -580,24 +580,6 @@ static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) |
| return ifidx == *(int *)arg; |
| } |
| |
| -static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, |
| - int ifidx) |
| -{ |
| - bool (*matchfn)(struct sk_buff *, void *) = NULL; |
| - struct sk_buff *skb; |
| - int prec; |
| - |
| - if (ifidx != -1) |
| - matchfn = brcmf_fws_ifidx_match; |
| - for (prec = 0; prec < q->num_prec; prec++) { |
| - skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); |
| - while (skb) { |
| - brcmu_pkt_buf_free_skb(skb); |
| - skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); |
| - } |
| - } |
| -} |
| - |
| static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) |
| { |
| int i; |
| @@ -669,6 +651,28 @@ static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, |
| return 0; |
| } |
| |
| +static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, |
| + int ifidx) |
| +{ |
| + bool (*matchfn)(struct sk_buff *, void *) = NULL; |
| + struct sk_buff *skb; |
| + int prec; |
| + u32 hslot; |
| + |
| + if (ifidx != -1) |
| + matchfn = brcmf_fws_ifidx_match; |
| + for (prec = 0; prec < q->num_prec; prec++) { |
| + skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); |
| + while (skb) { |
| + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); |
| + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, |
| + true); |
| + brcmu_pkt_buf_free_skb(skb); |
| + skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); |
| + } |
| + } |
| +} |
| + |
| static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, |
| u32 slot_id) |
| { |
| @@ -2200,6 +2204,8 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) |
| brcmf_fws_lock(fws); |
| ifp->fws_desc = NULL; |
| brcmf_dbg(TRACE, "deleting %s\n", entry->name); |
| + brcmf_fws_macdesc_cleanup(fws, &fws->desc.iface[ifp->ifidx], |
| + ifp->ifidx); |
| brcmf_fws_macdesc_deinit(entry); |
| brcmf_fws_cleanup(fws, ifp->ifidx); |
| brcmf_fws_unlock(fws); |
| -- |
| 2.20.1 |
| |