From: Russell King <linux@armlinux.org.uk> Date: 2021-06-03 11:53:27
Hi,
(I'm assuming this is the correct way to submit for
wireless-drivers-next.)
The following series fixes a number of issues with the firmware logging
on TI wireless devices, noticed while looking at AP mode issues on the
WL18xx family of devices.
Patch 1 tidies up the use of "fw_log.actual_buff_size" removing
multiple unnecessary endian conversions by caching the CPU endian value
in a local variable "actual_size".
Patch 2 makes the buffer calculations more obvious and adds comments
to describe what is going on. In particular, the addition of "addr_ptr"
eliminates several "addr + internal_fw_addrbase" calculations in the
code.
Patch 3 fixes an error in the calculation of "clean_addr" when it hits
the end of the buffer, which was causing the kernel console to spew
"errors" when firmware tracing is in effect.
Patch 4 removes the error message fixed in patch 3, which can still
occur when we race with the firmware reading the log structure. The
write pointer is where the firmware is writing its next message to,
whereas "clean_ptr" is the point that we've read the firmware log
to. It is fine that these may not match.
As this is a debug facility, I don't see any urgent need to push these
patches into -rc nor stable kernels.
drivers/net/wireless/ti/wlcore/event.c | 67 +++++++++++++++++++---------------
1 file changed, 38 insertions(+), 29 deletions(-)
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
From: Russell King <hidden> Date: 2021-06-03 11:54:19
Tidy up the use of fw_log.actual_buff_size - rather than reading it
multiple times and applying the endian conversion, read it once into
actual_len and use that instead.
Signed-off-by: Russell King <redacted>
---
drivers/net/wireless/ti/wlcore/event.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
@@ -40,7 +40,7 @@ int wlcore_event_fw_logger(struct wl1271 *wl)buffer=kzalloc(WL18XX_LOGGER_SDIO_BUFF_MAX,GFP_KERNEL);if(!buffer){wl1271_error("Fail to allocate fw logger memory");-fw_log.actual_buff_size=cpu_to_le32(0);+actual_len=0;gotoout;}
@@ -49,30 +49,30 @@ int wlcore_event_fw_logger(struct wl1271 *wl)if(ret<0){wl1271_error("Fail to read logger buffer, error_id = %d",ret);-fw_log.actual_buff_size=cpu_to_le32(0);+actual_len=0;gotofree_out;}memcpy(&fw_log,buffer,sizeof(fw_log));-if(le32_to_cpu(fw_log.actual_buff_size)==0)+actual_len=le32_to_cpu(fw_log.actual_buff_size);+if(actual_len==0)gotofree_out;-actual_len=le32_to_cpu(fw_log.actual_buff_size);start_loc=(le32_to_cpu(fw_log.buff_read_ptr)-internal_fw_addrbase)-addr;end_buff_addr+=le32_to_cpu(fw_log.max_buff_size);available_len=end_buff_addr-(le32_to_cpu(fw_log.buff_read_ptr)-internal_fw_addrbase);-actual_len=min(actual_len,available_len);-len=actual_len;+/* Copy initial part from end of ring buffer */+len=min(actual_len,available_len);wl12xx_copy_fwlog(wl,&buffer[start_loc],len);-clear_addr=addr+start_loc+le32_to_cpu(fw_log.actual_buff_size)+-internal_fw_addrbase;+clear_addr=addr+start_loc+actual_len+internal_fw_addrbase;-len=le32_to_cpu(fw_log.actual_buff_size)-len;+/* Copy any remaining part from beginning of ring buffer */+len=actual_len-len;if(len){wl12xx_copy_fwlog(wl,&buffer[WL18XX_LOGGER_BUFF_OFFSET],
@@ -93,7 +93,7 @@ int wlcore_event_fw_logger(struct wl1271 *wl)free_out:kfree(buffer);out:-returnle32_to_cpu(fw_log.actual_buff_size);+returnactual_len;}EXPORT_SYMBOL_GPL(wlcore_event_fw_logger);
From: Russell King <hidden> Date: 2021-06-03 11:54:24
Make some of the fwlog calculations more obvious by calculating bits
that get used and documenting what they are. Validate the read pointer
while we're at it to ensure we do not overflow the data block we have
allocated and read.
Signed-off-by: Russell King <redacted>
---
drivers/net/wireless/ti/wlcore/event.c | 43 +++++++++++++++++---------
1 file changed, 28 insertions(+), 15 deletions(-)
@@ -29,11 +29,13 @@ int wlcore_event_fw_logger(struct wl1271 *wl)u8*buffer;u32internal_fw_addrbase=WL18XX_DATA_RAM_BASE_ADDRESS;u32addr=WL18XX_LOGGER_SDIO_BUFF_ADDR;-u32end_buff_addr=WL18XX_LOGGER_SDIO_BUFF_ADDR+-WL18XX_LOGGER_BUFF_OFFSET;+u32addr_ptr;+u32buff_start_ptr;+u32buff_read_ptr;+u32buff_end_ptr;u32available_len;u32actual_len;-u32clear_addr;+u32clear_ptr;size_tlen;u32start_loc;
@@ -59,17 +61,29 @@ int wlcore_event_fw_logger(struct wl1271 *wl)if(actual_len==0)gotofree_out;-start_loc=(le32_to_cpu(fw_log.buff_read_ptr)--internal_fw_addrbase)-addr;-end_buff_addr+=le32_to_cpu(fw_log.max_buff_size);-available_len=end_buff_addr--(le32_to_cpu(fw_log.buff_read_ptr)--internal_fw_addrbase);+/* Calculate the internal pointer to the fwlog structure */+addr_ptr=internal_fw_addrbase+addr;-/* Copy initial part from end of ring buffer */+/* Calculate the internal pointers to the start and end of log buffer */+buff_start_ptr=addr_ptr+WL18XX_LOGGER_BUFF_OFFSET;+buff_end_ptr=buff_start_ptr+le32_to_cpu(fw_log.max_buff_size);++/* Read the read pointer and validate it */+buff_read_ptr=le32_to_cpu(fw_log.buff_read_ptr);+if(buff_read_ptr<buff_start_ptr||+buff_read_ptr>=buff_end_ptr){+wl1271_error("buffer read pointer out of bounds: %x not in (%x-%x)\n",+buff_read_ptr,buff_start_ptr,buff_end_ptr);+gotofree_out;+}++start_loc=buff_read_ptr-addr_ptr;+available_len=buff_end_ptr-buff_read_ptr;++/* Copy initial part up to the end of ring buffer */len=min(actual_len,available_len);wl12xx_copy_fwlog(wl,&buffer[start_loc],len);-clear_addr=addr+start_loc+actual_len+internal_fw_addrbase;+clear_ptr=addr_ptr+start_loc+actual_len;/* Copy any remaining part from beginning of ring buffer */len=actual_len-len;
@@ -77,14 +91,13 @@ int wlcore_event_fw_logger(struct wl1271 *wl)wl12xx_copy_fwlog(wl,&buffer[WL18XX_LOGGER_BUFF_OFFSET],len);-clear_addr=addr+WL18XX_LOGGER_BUFF_OFFSET+len+-internal_fw_addrbase;+clear_ptr=addr_ptr+WL18XX_LOGGER_BUFF_OFFSET+len;}/* double check that clear address and write pointer are the same */-if(clear_addr!=le32_to_cpu(fw_log.buff_write_ptr)){+if(clear_ptr!=le32_to_cpu(fw_log.buff_write_ptr)){wl1271_error("Calculate of clear addr Clear = %x, write = %x",-clear_addr,le32_to_cpu(fw_log.buff_write_ptr));+clear_ptr,le32_to_cpu(fw_log.buff_write_ptr));}/* indicate FW about Clear buffer */
From: Russell King <hidden> Date: 2021-06-03 11:54:29
With logging enabled, it has been observed that the driver spews
messages such as:
wlcore: ERROR Calculate of clear addr Clear = 204025b0, write = 204015b0
The problem occurs because 204025b0 is the end of the buffer, and
204015b0 is the beginning, and the calculation for "clear"ing the
buffer does not take into account that if we read to the very end
of the ring buffer, we are actually at the beginning of the buffer.
Fix this.
Signed-off-by: Russell King <redacted>
---
drivers/net/wireless/ti/wlcore/event.c | 2 ++
1 file changed, 2 insertions(+)
@@ -84,6 +84,8 @@ int wlcore_event_fw_logger(struct wl1271 *wl)len=min(actual_len,available_len);wl12xx_copy_fwlog(wl,&buffer[start_loc],len);clear_ptr=addr_ptr+start_loc+actual_len;+if(clear_ptr==buff_end_ptr)+clear_ptr=buff_start_ptr;/* Copy any remaining part from beginning of ring buffer */len=actual_len-len;
From: Russell King <hidden> Date: 2021-06-03 11:54:35
When reading the fw_log structure from the device's memory, we could
race with the firmware updating the actual_buff_size and buff_write_ptr
members of this structure. This would lead to bytes being dropped from
the log.
Fix this by writing back the actual - now fixed - clear_ptr which
reflects where we read up to in the buffer.
This also means that we must not check that the clear_ptr matches the
current write pointer, so remove that check.
Signed-off-by: Russell King <redacted>
---
drivers/net/wireless/ti/wlcore/event.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
@@ -96,15 +96,9 @@ int wlcore_event_fw_logger(struct wl1271 *wl)clear_ptr=addr_ptr+WL18XX_LOGGER_BUFF_OFFSET+len;}-/* double check that clear address and write pointer are the same */-if(clear_ptr!=le32_to_cpu(fw_log.buff_write_ptr)){-wl1271_error("Calculate of clear addr Clear = %x, write = %x",-clear_ptr,le32_to_cpu(fw_log.buff_write_ptr));-}--/* indicate FW about Clear buffer */+/* Update the read pointer */ret=wlcore_write32(wl,addr+WL18XX_LOGGER_READ_POINT_OFFSET,-fw_log.buff_write_ptr);+clear_ptr);free_out:kfree(buffer);out:
From: Kalle Valo <hidden> Date: 2021-06-14 15:50:26
Russell King [off-list ref] wrote:
Tidy up the use of fw_log.actual_buff_size - rather than reading it
multiple times and applying the endian conversion, read it once into
actual_len and use that instead.
Signed-off-by: Russell King <redacted>