mac80211: brcmfmac: really add early fw crash recovery
Previous commit backported USB fixes instead of firmware crash recovery
patches.
Fixes: 02aed76968 ("mac80211: brcmfmac: early work on FullMAC firmware crash recovery")
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
			
			
This commit is contained in:
		| @@ -0,0 +1,32 @@ | |||||||
|  | From c9692820710f57c826b2e43a6fb1e4cd307508b0 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Tue, 26 Feb 2019 14:11:16 +0100 | ||||||
|  | Subject: [PATCH] brcmfmac: support repeated brcmf_fw_alloc_request() calls | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | During a normal brcmfmac lifetime brcmf_fw_alloc_request() is called | ||||||
|  | once only during the probe. It's safe to assume provided array is clear. | ||||||
|  |  | ||||||
|  | Further brcmfmac improvements may require calling it multiple times | ||||||
|  | though. This patch allows it by fixing invalid firmware paths like: | ||||||
|  | brcm/brcmfmac4366c-pcie.binbrcm/brcmfmac4366c-pcie.bin | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||||
|  | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||||
|  | --- | ||||||
|  |  drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | 1 + | ||||||
|  |  1 file changed, 1 insertion(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | ||||||
|  | @@ -743,6 +743,7 @@ brcmf_fw_alloc_request(u32 chip, u32 chi | ||||||
|  |   | ||||||
|  |  	for (j = 0; j < n_fwnames; j++) { | ||||||
|  |  		fwreq->items[j].path = fwnames[j].path; | ||||||
|  | +		fwnames[j].path[0] = '\0'; | ||||||
|  |  		/* check if firmware path is provided by module parameter */ | ||||||
|  |  		if (brcmf_mp_global.firmware_path[0] != '\0') { | ||||||
|  |  			strlcpy(fwnames[j].path, mp_path, | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | From a2ec87ddbf1637f854ffcfff9d12d392fa30758b Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Tue, 26 Feb 2019 14:11:18 +0100 | ||||||
|  | Subject: [PATCH] brcmfmac: add a function designated for handling firmware | ||||||
|  |  fails | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | This improves handling PCIe firmware halts by printing a clear error | ||||||
|  | message and replaces a similar code in the SDIO bus support. | ||||||
|  |  | ||||||
|  | It will also allow further improvements like trying to recover from a | ||||||
|  | firmware crash. | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||||
|  | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||||
|  | --- | ||||||
|  |  drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h |  2 ++ | ||||||
|  |  .../net/wireless/broadcom/brcm80211/brcmfmac/core.c    | 10 ++++++++++ | ||||||
|  |  .../net/wireless/broadcom/brcm80211/brcmfmac/pcie.c    |  2 +- | ||||||
|  |  .../net/wireless/broadcom/brcm80211/brcmfmac/sdio.c    |  4 ++-- | ||||||
|  |  4 files changed, 15 insertions(+), 3 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | ||||||
|  | @@ -262,6 +262,8 @@ void brcmf_detach(struct device *dev); | ||||||
|  |  void brcmf_dev_reset(struct device *dev); | ||||||
|  |  /* Request from bus module to initiate a coredump */ | ||||||
|  |  void brcmf_dev_coredump(struct device *dev); | ||||||
|  | +/* Indication that firmware has halted or crashed */ | ||||||
|  | +void brcmf_fw_crashed(struct device *dev); | ||||||
|  |   | ||||||
|  |  /* Configure the "global" bus state used by upper layers */ | ||||||
|  |  void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | ||||||
|  | @@ -1294,6 +1294,16 @@ void brcmf_dev_coredump(struct device *d | ||||||
|  |  		brcmf_dbg(TRACE, "failed to create coredump\n"); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +void brcmf_fw_crashed(struct device *dev) | ||||||
|  | +{ | ||||||
|  | +	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | ||||||
|  | +	struct brcmf_pub *drvr = bus_if->drvr; | ||||||
|  | + | ||||||
|  | +	bphy_err(drvr, "Firmware has halted or crashed\n"); | ||||||
|  | + | ||||||
|  | +	brcmf_dev_coredump(dev); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  void brcmf_detach(struct device *dev) | ||||||
|  |  { | ||||||
|  |  	s32 i; | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||||
|  | @@ -730,7 +730,7 @@ static void brcmf_pcie_handle_mb_data(st | ||||||
|  |  	} | ||||||
|  |  	if (dtoh_mb_data & BRCMF_D2H_DEV_FWHALT) { | ||||||
|  |  		brcmf_dbg(PCIE, "D2H_MB_DATA: FW HALT\n"); | ||||||
|  | -		brcmf_dev_coredump(&devinfo->pdev->dev); | ||||||
|  | +		brcmf_fw_crashed(&devinfo->pdev->dev); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | ||||||
|  | @@ -1090,8 +1090,8 @@ static u32 brcmf_sdio_hostmail(struct br | ||||||
|  |   | ||||||
|  |  	/* dongle indicates the firmware has halted/crashed */ | ||||||
|  |  	if (hmb_data & HMB_DATA_FWHALT) { | ||||||
|  | -		brcmf_err("mailbox indicates firmware halted\n"); | ||||||
|  | -		brcmf_dev_coredump(&sdiod->func1->dev); | ||||||
|  | +		brcmf_dbg(SDIO, "mailbox indicates firmware halted\n"); | ||||||
|  | +		brcmf_fw_crashed(&sdiod->func1->dev); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	/* Dongle recomposed rx frames, accept them again */ | ||||||
| @@ -0,0 +1,153 @@ | |||||||
|  | From 4684997d9eea29380000e062755aa6d368d789a3 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||||
|  | Date: Tue, 26 Feb 2019 14:11:19 +0100 | ||||||
|  | Subject: [PATCH] brcmfmac: reset PCIe bus on a firmware crash | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | This includes bus reset & reloading a firmware. It should be sufficient | ||||||
|  | for a user space to (setup and) use a wireless device again. | ||||||
|  |  | ||||||
|  | Support for reset on USB & SDIO can be added later. | ||||||
|  |  | ||||||
|  | Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||||
|  | Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||||
|  | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||||
|  | --- | ||||||
|  |  .../broadcom/brcm80211/brcmfmac/bus.h         | 10 ++++++ | ||||||
|  |  .../broadcom/brcm80211/brcmfmac/core.c        | 12 +++++++ | ||||||
|  |  .../broadcom/brcm80211/brcmfmac/core.h        |  2 ++ | ||||||
|  |  .../broadcom/brcm80211/brcmfmac/pcie.c        | 35 +++++++++++++++++++ | ||||||
|  |  4 files changed, 59 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | ||||||
|  | @@ -90,6 +90,7 @@ struct brcmf_bus_ops { | ||||||
|  |  	int (*get_memdump)(struct device *dev, void *data, size_t len); | ||||||
|  |  	int (*get_fwname)(struct device *dev, const char *ext, | ||||||
|  |  			  unsigned char *fw_name); | ||||||
|  | +	int (*reset)(struct device *dev); | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |   | ||||||
|  | @@ -235,6 +236,15 @@ int brcmf_bus_get_fwname(struct brcmf_bu | ||||||
|  |  	return bus->ops->get_fwname(bus->dev, ext, fw_name); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static inline | ||||||
|  | +int brcmf_bus_reset(struct brcmf_bus *bus) | ||||||
|  | +{ | ||||||
|  | +	if (!bus->ops->reset) | ||||||
|  | +		return -EOPNOTSUPP; | ||||||
|  | + | ||||||
|  | +	return bus->ops->reset(bus->dev); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  /* | ||||||
|  |   * interface functions from common layer | ||||||
|  |   */ | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | ||||||
|  | @@ -1104,6 +1104,14 @@ static int brcmf_revinfo_read(struct seq | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static void brcmf_core_bus_reset(struct work_struct *work) | ||||||
|  | +{ | ||||||
|  | +	struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, | ||||||
|  | +					      bus_reset); | ||||||
|  | + | ||||||
|  | +	brcmf_bus_reset(drvr->bus_if); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops) | ||||||
|  |  { | ||||||
|  |  	int ret = -1; | ||||||
|  | @@ -1175,6 +1183,8 @@ static int brcmf_bus_started(struct brcm | ||||||
|  |  #endif | ||||||
|  |  #endif /* CONFIG_INET */ | ||||||
|  |   | ||||||
|  | +	INIT_WORK(&drvr->bus_reset, brcmf_core_bus_reset); | ||||||
|  | + | ||||||
|  |  	/* populate debugfs */ | ||||||
|  |  	brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read); | ||||||
|  |  	brcmf_feat_debugfs_create(drvr); | ||||||
|  | @@ -1300,6 +1310,8 @@ void brcmf_fw_crashed(struct device *dev | ||||||
|  |  	bphy_err(drvr, "Firmware has halted or crashed\n"); | ||||||
|  |   | ||||||
|  |  	brcmf_dev_coredump(dev); | ||||||
|  | + | ||||||
|  | +	schedule_work(&drvr->bus_reset); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void brcmf_detach(struct device *dev) | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h | ||||||
|  | @@ -143,6 +143,8 @@ struct brcmf_pub { | ||||||
|  |  	struct notifier_block inet6addr_notifier; | ||||||
|  |  	struct brcmf_mp_device *settings; | ||||||
|  |   | ||||||
|  | +	struct work_struct bus_reset; | ||||||
|  | + | ||||||
|  |  	u8 clmver[BRCMF_DCMD_SMLEN]; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||||
|  | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||||
|  | @@ -345,6 +345,10 @@ static const u32 brcmf_ring_itemsize[BRC | ||||||
|  |  	BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +static void brcmf_pcie_setup(struct device *dev, int ret, | ||||||
|  | +			     struct brcmf_fw_request *fwreq); | ||||||
|  | +static struct brcmf_fw_request * | ||||||
|  | +brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo); | ||||||
|  |   | ||||||
|  |  static u32 | ||||||
|  |  brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) | ||||||
|  | @@ -1409,6 +1413,36 @@ int brcmf_pcie_get_fwname(struct device | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static int brcmf_pcie_reset(struct device *dev) | ||||||
|  | +{ | ||||||
|  | +	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | ||||||
|  | +	struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; | ||||||
|  | +	struct brcmf_pciedev_info *devinfo = buspub->devinfo; | ||||||
|  | +	struct brcmf_fw_request *fwreq; | ||||||
|  | +	int err; | ||||||
|  | + | ||||||
|  | +	brcmf_detach(dev); | ||||||
|  | + | ||||||
|  | +	brcmf_pcie_release_irq(devinfo); | ||||||
|  | +	brcmf_pcie_release_scratchbuffers(devinfo); | ||||||
|  | +	brcmf_pcie_release_ringbuffers(devinfo); | ||||||
|  | +	brcmf_pcie_reset_device(devinfo); | ||||||
|  | + | ||||||
|  | +	fwreq = brcmf_pcie_prepare_fw_request(devinfo); | ||||||
|  | +	if (!fwreq) { | ||||||
|  | +		dev_err(dev, "Failed to prepare FW request\n"); | ||||||
|  | +		return -ENOMEM; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	err = brcmf_fw_get_firmwares(dev, fwreq, brcmf_pcie_setup); | ||||||
|  | +	if (err) { | ||||||
|  | +		dev_err(dev, "Failed to prepare FW request\n"); | ||||||
|  | +		kfree(fwreq); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return err; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { | ||||||
|  |  	.txdata = brcmf_pcie_tx, | ||||||
|  |  	.stop = brcmf_pcie_down, | ||||||
|  | @@ -1418,6 +1452,7 @@ static const struct brcmf_bus_ops brcmf_ | ||||||
|  |  	.get_ramsize = brcmf_pcie_get_ramsize, | ||||||
|  |  	.get_memdump = brcmf_pcie_get_memdump, | ||||||
|  |  	.get_fwname = brcmf_pcie_get_fwname, | ||||||
|  | +	.reset = brcmf_pcie_reset, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |   | ||||||
| @@ -0,0 +1,124 @@ | |||||||
|  | From c80d26e81ef1802f30364b4ad1955c1443a592b9 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||||
|  | Date: Mon, 4 Mar 2019 15:42:49 +0000 | ||||||
|  | Subject: [PATCH] brcmfmac: fix WARNING during USB disconnect in case of | ||||||
|  |  unempty psq | ||||||
|  |  | ||||||
|  | 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> | ||||||
|  | --- | ||||||
|  |  .../broadcom/brcm80211/brcmfmac/fwsignal.c    | 42 +++++++++++-------- | ||||||
|  |  1 file changed, 24 insertions(+), 18 deletions(-) | ||||||
|  |  | ||||||
|  | --- 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 | ||||||
|  |  	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_poppk | ||||||
|  |  	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 brcm | ||||||
|  |  	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); | ||||||
| @@ -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, | ||||||
		Reference in New Issue
	
	Block a user
	 Rafał Miłecki
					Rafał Miłecki