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,29 @@ | ||||
| From 0c7051610c577b60b01b3b5aec14d6765e177b0d Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Thu, 21 Feb 2019 11:33:24 +0100 | ||||
| Subject: [PATCH] brcmfmac: fix size of the struct msgbuf_ring_status | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This updates host struct to match the in-firmawre definition. It's a | ||||
| cosmetic change as it only applies to the reserved struct space. | ||||
|  | ||||
| Fixes: c988b78244df ("brcmfmac: print firmware reported ring status errors") | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -139,7 +139,7 @@ struct msgbuf_ring_status { | ||||
|  	struct msgbuf_common_hdr	msg; | ||||
|  	struct msgbuf_completion_hdr	compl_hdr; | ||||
|  	__le16				write_idx; | ||||
| -	__le32				rsvd0[5]; | ||||
| +	__le16				rsvd0[5]; | ||||
|  }; | ||||
|   | ||||
|  struct msgbuf_rx_event { | ||||
| @@ -0,0 +1,69 @@ | ||||
| From c91377495192cda096e52dc09c266b0d05f16d86 Mon Sep 17 00:00:00 2001 | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Date: Thu, 21 Feb 2019 11:33:25 +0100 | ||||
| Subject: [PATCH] brcmfmac: print firmware reported general status errors | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| Firmware may report general errors using a special message type. Add | ||||
| basic support for it by simply decoding & printing an error number. | ||||
|  | ||||
| A sample situation in which firmware reports a buf error: | ||||
| CONSOLE: 027084.733 no host response IOCTL buffer available..so fail the request | ||||
| will now produce a "Firmware reported general error: 9" on the host. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../broadcom/brcm80211/brcmfmac/msgbuf.c      | 24 +++++++++++++++++++ | ||||
|  1 file changed, 24 insertions(+) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -134,6 +134,14 @@ struct msgbuf_completion_hdr { | ||||
|  	__le16				flow_ring_id; | ||||
|  }; | ||||
|   | ||||
| +/* Data struct for the MSGBUF_TYPE_GEN_STATUS */ | ||||
| +struct msgbuf_gen_status { | ||||
| +	struct msgbuf_common_hdr	msg; | ||||
| +	struct msgbuf_completion_hdr	compl_hdr; | ||||
| +	__le16				write_idx; | ||||
| +	__le32				rsvd0[3]; | ||||
| +}; | ||||
| + | ||||
|  /* Data struct for the MSGBUF_TYPE_RING_STATUS */ | ||||
|  struct msgbuf_ring_status { | ||||
|  	struct msgbuf_common_hdr	msg; | ||||
| @@ -1194,6 +1202,18 @@ brcmf_msgbuf_process_rx_complete(struct | ||||
|  	brcmf_netif_rx(ifp, skb); | ||||
|  } | ||||
|   | ||||
| +static void brcmf_msgbuf_process_gen_status(struct brcmf_msgbuf *msgbuf, | ||||
| +					    void *buf) | ||||
| +{ | ||||
| +	struct msgbuf_gen_status *gen_status = buf; | ||||
| +	struct brcmf_pub *drvr = msgbuf->drvr; | ||||
| +	int err; | ||||
| + | ||||
| +	err = le16_to_cpu(gen_status->compl_hdr.status); | ||||
| +	if (err) | ||||
| +		bphy_err(drvr, "Firmware reported general error: %d\n", err); | ||||
| +} | ||||
| + | ||||
|  static void brcmf_msgbuf_process_ring_status(struct brcmf_msgbuf *msgbuf, | ||||
|  					     void *buf) | ||||
|  { | ||||
| @@ -1273,6 +1293,10 @@ static void brcmf_msgbuf_process_msgtype | ||||
|   | ||||
|  	msg = (struct msgbuf_common_hdr *)buf; | ||||
|  	switch (msg->msgtype) { | ||||
| +	case MSGBUF_TYPE_GEN_STATUS: | ||||
| +		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_GEN_STATUS\n"); | ||||
| +		brcmf_msgbuf_process_gen_status(msgbuf, buf); | ||||
| +		break; | ||||
|  	case MSGBUF_TYPE_RING_STATUS: | ||||
|  		brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RING_STATUS\n"); | ||||
|  		brcmf_msgbuf_process_ring_status(msgbuf, buf); | ||||
| @@ -0,0 +1,84 @@ | ||||
| From db3b9e2e1d58080d0754bdf9293dabf8c6491b67 Mon Sep 17 00:00:00 2001 | ||||
| From: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||
| Date: Fri, 8 Mar 2019 15:25:04 +0000 | ||||
| Subject: [PATCH] brcmfmac: fix race during disconnect when USB completion is | ||||
|  in progress | ||||
|  | ||||
| It was observed that rarely during USB disconnect happening shortly after | ||||
| connect (before full initialization completes) usb_hub_wq would wait | ||||
| forever for the dev_init_lock to be unlocked. dev_init_lock would remain | ||||
| locked though because of infinite wait during usb_kill_urb: | ||||
|  | ||||
| [ 2730.656472] kworker/0:2     D    0   260      2 0x00000000 | ||||
| [ 2730.660700] Workqueue: events request_firmware_work_func | ||||
| [ 2730.664807] [<809dca20>] (__schedule) from [<809dd164>] (schedule+0x4c/0xac) | ||||
| [ 2730.670587] [<809dd164>] (schedule) from [<8069af44>] (usb_kill_urb+0xdc/0x114) | ||||
| [ 2730.676815] [<8069af44>] (usb_kill_urb) from [<7f258b50>] (brcmf_usb_free_q+0x34/0xa8 [brcmfmac]) | ||||
| [ 2730.684833] [<7f258b50>] (brcmf_usb_free_q [brcmfmac]) from [<7f2517d4>] (brcmf_detach+0xa0/0xb8 [brcmfmac]) | ||||
| [ 2730.693557] [<7f2517d4>] (brcmf_detach [brcmfmac]) from [<7f251a34>] (brcmf_attach+0xac/0x3d8 [brcmfmac]) | ||||
| [ 2730.702094] [<7f251a34>] (brcmf_attach [brcmfmac]) from [<7f2587ac>] (brcmf_usb_probe_phase2+0x468/0x4a0 [brcmfmac]) | ||||
| [ 2730.711601] [<7f2587ac>] (brcmf_usb_probe_phase2 [brcmfmac]) from [<7f252888>] (brcmf_fw_request_done+0x194/0x220 [brcmfmac]) | ||||
| [ 2730.721795] [<7f252888>] (brcmf_fw_request_done [brcmfmac]) from [<805748e4>] (request_firmware_work_func+0x4c/0x88) | ||||
| [ 2730.731125] [<805748e4>] (request_firmware_work_func) from [<80141474>] (process_one_work+0x228/0x808) | ||||
| [ 2730.739223] [<80141474>] (process_one_work) from [<80141a80>] (worker_thread+0x2c/0x564) | ||||
| [ 2730.746105] [<80141a80>] (worker_thread) from [<80147bcc>] (kthread+0x13c/0x16c) | ||||
| [ 2730.752227] [<80147bcc>] (kthread) from [<801010b4>] (ret_from_fork+0x14/0x20) | ||||
|  | ||||
| [ 2733.099695] kworker/0:3     D    0  1065      2 0x00000000 | ||||
| [ 2733.103926] Workqueue: usb_hub_wq hub_event | ||||
| [ 2733.106914] [<809dca20>] (__schedule) from [<809dd164>] (schedule+0x4c/0xac) | ||||
| [ 2733.112693] [<809dd164>] (schedule) from [<809e2a8c>] (schedule_timeout+0x214/0x3e4) | ||||
| [ 2733.119621] [<809e2a8c>] (schedule_timeout) from [<809dde2c>] (wait_for_common+0xc4/0x1c0) | ||||
| [ 2733.126810] [<809dde2c>] (wait_for_common) from [<7f258d00>] (brcmf_usb_disconnect+0x1c/0x4c [brcmfmac]) | ||||
| [ 2733.135206] [<7f258d00>] (brcmf_usb_disconnect [brcmfmac]) from [<8069e0c8>] (usb_unbind_interface+0x5c/0x1e4) | ||||
| [ 2733.143943] [<8069e0c8>] (usb_unbind_interface) from [<8056d3e8>] (device_release_driver_internal+0x164/0x1fc) | ||||
| [ 2733.152769] [<8056d3e8>] (device_release_driver_internal) from [<8056c078>] (bus_remove_device+0xd0/0xfc) | ||||
| [ 2733.161138] [<8056c078>] (bus_remove_device) from [<8056977c>] (device_del+0x11c/0x310) | ||||
| [ 2733.167939] [<8056977c>] (device_del) from [<8069cba8>] (usb_disable_device+0xa0/0x1cc) | ||||
| [ 2733.174743] [<8069cba8>] (usb_disable_device) from [<8069507c>] (usb_disconnect+0x74/0x1dc) | ||||
| [ 2733.181823] [<8069507c>] (usb_disconnect) from [<80695e88>] (hub_event+0x478/0xf88) | ||||
| [ 2733.188278] [<80695e88>] (hub_event) from [<80141474>] (process_one_work+0x228/0x808) | ||||
| [ 2733.194905] [<80141474>] (process_one_work) from [<80141a80>] (worker_thread+0x2c/0x564) | ||||
| [ 2733.201724] [<80141a80>] (worker_thread) from [<80147bcc>] (kthread+0x13c/0x16c) | ||||
| [ 2733.207913] [<80147bcc>] (kthread) from [<801010b4>] (ret_from_fork+0x14/0x20) | ||||
|  | ||||
| It was traced down to a case where usb_kill_urb would be called on an URB | ||||
| structure containing more or less random data, including large number in | ||||
| its use_count. During the debugging it appeared that in brcmf_usb_free_q() | ||||
| the traversal over URBs' lists is not synchronized with operations on those | ||||
| lists in brcmf_usb_rx_complete() leading to handling | ||||
| brcmf_usbdev_info structure (holding lists' head) as lists' element and in | ||||
| result causing above problem. | ||||
|  | ||||
| Fix it by walking through all URBs during brcmf_cancel_all_urbs using the | ||||
| arrays of requests instead of linked lists. | ||||
|  | ||||
| Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 10 ++++++++-- | ||||
|  1 file changed, 8 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | ||||
| @@ -684,12 +684,18 @@ static int brcmf_usb_up(struct device *d | ||||
|   | ||||
|  static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) | ||||
|  { | ||||
| +	int i; | ||||
| + | ||||
|  	if (devinfo->ctl_urb) | ||||
|  		usb_kill_urb(devinfo->ctl_urb); | ||||
|  	if (devinfo->bulk_urb) | ||||
|  		usb_kill_urb(devinfo->bulk_urb); | ||||
| -	brcmf_usb_free_q(&devinfo->tx_postq, true); | ||||
| -	brcmf_usb_free_q(&devinfo->rx_postq, true); | ||||
| +	if (devinfo->tx_reqs) | ||||
| +		for (i = 0; i < devinfo->bus_pub.ntxq; i++) | ||||
| +			usb_kill_urb(devinfo->tx_reqs[i].urb); | ||||
| +	if (devinfo->rx_reqs) | ||||
| +		for (i = 0; i < devinfo->bus_pub.nrxq; i++) | ||||
| +			usb_kill_urb(devinfo->rx_reqs[i].urb); | ||||
|  } | ||||
|   | ||||
|  static void brcmf_usb_down(struct device *dev) | ||||
| @@ -0,0 +1,54 @@ | ||||
| From 2b78e5f5223666d403d4fdb30af4ad65c8da3cdb Mon Sep 17 00:00:00 2001 | ||||
| From: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||
| Date: Fri, 8 Mar 2019 15:25:06 +0000 | ||||
| Subject: [PATCH] brcmfmac: remove pending parameter from brcmf_usb_free_q | ||||
|  | ||||
| brcmf_usb_free_q is no longer called with pending=true thus this boolean | ||||
| parameter is no longer needed. | ||||
|  | ||||
| Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  .../wireless/broadcom/brcm80211/brcmfmac/usb.c    | 15 ++++++--------- | ||||
|  1 file changed, 6 insertions(+), 9 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | ||||
| @@ -445,9 +445,10 @@ fail: | ||||
|   | ||||
|  } | ||||
|   | ||||
| -static void brcmf_usb_free_q(struct list_head *q, bool pending) | ||||
| +static void brcmf_usb_free_q(struct list_head *q) | ||||
|  { | ||||
|  	struct brcmf_usbreq *req, *next; | ||||
| + | ||||
|  	int i = 0; | ||||
|  	list_for_each_entry_safe(req, next, q, list) { | ||||
|  		if (!req->urb) { | ||||
| @@ -455,12 +456,8 @@ static void brcmf_usb_free_q(struct list | ||||
|  			break; | ||||
|  		} | ||||
|  		i++; | ||||
| -		if (pending) { | ||||
| -			usb_kill_urb(req->urb); | ||||
| -		} else { | ||||
| -			usb_free_urb(req->urb); | ||||
| -			list_del_init(&req->list); | ||||
| -		} | ||||
| +		usb_free_urb(req->urb); | ||||
| +		list_del_init(&req->list); | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -1031,8 +1028,8 @@ static void brcmf_usb_detach(struct brcm | ||||
|  	brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo); | ||||
|   | ||||
|  	/* free the URBS */ | ||||
| -	brcmf_usb_free_q(&devinfo->rx_freeq, false); | ||||
| -	brcmf_usb_free_q(&devinfo->tx_freeq, false); | ||||
| +	brcmf_usb_free_q(&devinfo->rx_freeq); | ||||
| +	brcmf_usb_free_q(&devinfo->tx_freeq); | ||||
|   | ||||
|  	usb_free_urb(devinfo->ctl_urb); | ||||
|  	usb_free_urb(devinfo->bulk_urb); | ||||
| @@ -0,0 +1,29 @@ | ||||
| From 504f06725d015954a0fcafdf1d90a6795ca8f769 Mon Sep 17 00:00:00 2001 | ||||
| From: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||
| Date: Fri, 8 Mar 2019 15:25:09 +0000 | ||||
| Subject: [PATCH] brcmfmac: remove unused variable i from brcmf_usb_free_q | ||||
|  | ||||
| Variable i is not used so remove it. | ||||
|  | ||||
| Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 2 -- | ||||
|  1 file changed, 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | ||||
| @@ -449,13 +449,11 @@ static void brcmf_usb_free_q(struct list | ||||
|  { | ||||
|  	struct brcmf_usbreq *req, *next; | ||||
|   | ||||
| -	int i = 0; | ||||
|  	list_for_each_entry_safe(req, next, q, list) { | ||||
|  		if (!req->urb) { | ||||
|  			brcmf_err("bad req\n"); | ||||
|  			break; | ||||
|  		} | ||||
| -		i++; | ||||
|  		usb_free_urb(req->urb); | ||||
|  		list_del_init(&req->list); | ||||
|  	} | ||||
| @@ -0,0 +1,70 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> | ||||
| Subject: [PATCH] brcmfmac: get RAM info right before downloading PCIe firmware | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| It's important as brcmf_chip_get_raminfo() also makes sure that memory | ||||
| is properly setup. Without it the firmware could report invalid RAM | ||||
| address like 0x04000001. | ||||
|  | ||||
| During a normal brcmfmac lifetime brcmf_chip_get_raminfo() is called on | ||||
| probe by the brcmf_chip_recognition(). This change allows implementing | ||||
| further improvements like handling errors by resetting a device with | ||||
| the brcmf_pcie_reset_device() and redownloading a firmware afterwards. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> | ||||
| --- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 6 ++++-- | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h | 1 + | ||||
|  drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 ++++++ | ||||
|  3 files changed, 11 insertions(+), 2 deletions(-) | ||||
|  | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | ||||
| @@ -700,8 +700,10 @@ static u32 brcmf_chip_tcm_rambase(struct | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) | ||||
| +int brcmf_chip_get_raminfo(struct brcmf_chip *pub) | ||||
|  { | ||||
| +	struct brcmf_chip_priv *ci = container_of(pub, struct brcmf_chip_priv, | ||||
| +						  pub); | ||||
|  	struct brcmf_core_priv *mem_core; | ||||
|  	struct brcmf_core *mem; | ||||
|   | ||||
| @@ -981,7 +983,7 @@ static int brcmf_chip_recognition(struct | ||||
|  		brcmf_chip_set_passive(&ci->pub); | ||||
|  	} | ||||
|   | ||||
| -	return brcmf_chip_get_raminfo(ci); | ||||
| +	return brcmf_chip_get_raminfo(&ci->pub); | ||||
|  } | ||||
|   | ||||
|  static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h | ||||
| @@ -80,6 +80,7 @@ struct brcmf_buscore_ops { | ||||
|  	void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); | ||||
|  }; | ||||
|   | ||||
| +int brcmf_chip_get_raminfo(struct brcmf_chip *pub); | ||||
|  struct brcmf_chip *brcmf_chip_attach(void *ctx, | ||||
|  				     const struct brcmf_buscore_ops *ops); | ||||
|  void brcmf_chip_detach(struct brcmf_chip *chip); | ||||
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | ||||
| @@ -1727,6 +1727,12 @@ static void brcmf_pcie_setup(struct devi | ||||
|  	nvram_len = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.len; | ||||
|  	kfree(fwreq); | ||||
|   | ||||
| +	ret = brcmf_chip_get_raminfo(devinfo->ci); | ||||
| +	if (ret) { | ||||
| +		brcmf_err(bus, "Failed to get RAM info\n"); | ||||
| +		goto fail; | ||||
| +	} | ||||
| + | ||||
|  	/* Some of the firmwares have the size of the memory of the device | ||||
|  	 * defined inside the firmware. This is because part of the memory in | ||||
|  	 * the device is shared and the devision is determined by FW. Parse | ||||
		Reference in New Issue
	
	Block a user
	 Rafał Miłecki
					Rafał Miłecki