2019-02-19 19:45:26 +02:00
|
|
|
// SPDX-License-Identifier: ISC
|
2017-04-26 12:18:00 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2004-2011 Atheros Communications Inc.
|
|
|
|
* Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc.
|
|
|
|
* Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com>
|
wifi: ath10k: avoid NULL pointer error during sdio remove
When running 'rmmod ath10k', ath10k_sdio_remove() will free sdio
workqueue by destroy_workqueue(). But if CONFIG_INIT_ON_FREE_DEFAULT_ON
is set to yes, kernel panic will happen:
Call trace:
destroy_workqueue+0x1c/0x258
ath10k_sdio_remove+0x84/0x94
sdio_bus_remove+0x50/0x16c
device_release_driver_internal+0x188/0x25c
device_driver_detach+0x20/0x2c
This is because during 'rmmod ath10k', ath10k_sdio_remove() will call
ath10k_core_destroy() before destroy_workqueue(). wiphy_dev_release()
will finally be called in ath10k_core_destroy(). This function will free
struct cfg80211_registered_device *rdev and all its members, including
wiphy, dev and the pointer of sdio workqueue. Then the pointer of sdio
workqueue will be set to NULL due to CONFIG_INIT_ON_FREE_DEFAULT_ON.
After device release, destroy_workqueue() will use NULL pointer then the
kernel panic happen.
Call trace:
ath10k_sdio_remove
->ath10k_core_unregister
……
->ath10k_core_stop
->ath10k_hif_stop
->ath10k_sdio_irq_disable
->ath10k_hif_power_down
->del_timer_sync(&ar_sdio->sleep_timer)
->ath10k_core_destroy
->ath10k_mac_destroy
->ieee80211_free_hw
->wiphy_free
……
->wiphy_dev_release
->destroy_workqueue
Need to call destroy_workqueue() before ath10k_core_destroy(), free
the work queue buffer first and then free pointer of work queue by
ath10k_core_destroy(). This order matches the error path order in
ath10k_sdio_probe().
No work will be queued on sdio workqueue between it is destroyed and
ath10k_core_destroy() is called. Based on the call_stack above, the
reason is:
Only ath10k_sdio_sleep_timer_handler(), ath10k_sdio_hif_tx_sg() and
ath10k_sdio_irq_disable() will queue work on sdio workqueue.
Sleep timer will be deleted before ath10k_core_destroy() in
ath10k_hif_power_down().
ath10k_sdio_irq_disable() only be called in ath10k_hif_stop().
ath10k_core_unregister() will call ath10k_hif_power_down() to stop hif
bus, so ath10k_sdio_hif_tx_sg() won't be called anymore.
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189
Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
Tested-by: David Ruth <druth@chromium.org>
Reviewed-by: David Ruth <druth@chromium.org>
Link: https://patch.msgid.link/20241008022246.1010-1-quic_kangyang@quicinc.com
Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
2024-10-08 10:22:46 +08:00
|
|
|
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
2017-04-26 12:18:00 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mmc/card.h>
|
|
|
|
#include <linux/mmc/mmc.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
|
|
#include <linux/mmc/sdio_func.h>
|
|
|
|
#include <linux/mmc/sdio_ids.h>
|
|
|
|
#include <linux/mmc/sdio.h>
|
|
|
|
#include <linux/mmc/sd.h>
|
|
|
|
#include <linux/bitfield.h>
|
|
|
|
#include "core.h"
|
|
|
|
#include "bmi.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "hif.h"
|
|
|
|
#include "htc.h"
|
ath10k: transmit queued frames after processing rx packets
When running iperf on ath10k SDIO, TX can stop working:
iperf -c 192.168.1.1 -i 1 -t 20 -w 10K
[ 3] 0.0- 1.0 sec 2.00 MBytes 16.8 Mbits/sec
[ 3] 1.0- 2.0 sec 3.12 MBytes 26.2 Mbits/sec
[ 3] 2.0- 3.0 sec 3.25 MBytes 27.3 Mbits/sec
[ 3] 3.0- 4.0 sec 655 KBytes 5.36 Mbits/sec
[ 3] 4.0- 5.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 5.0- 6.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 6.0- 7.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 7.0- 8.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 8.0- 9.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 9.0-10.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 0.0-10.3 sec 9.01 MBytes 7.32 Mbits/sec
There are frames in the ieee80211_txq and there are frames that have
been removed from from this queue, but haven't yet been sent on the wire
(num_pending_tx).
When num_pending_tx reaches max_num_pending_tx, we will stop the queues
by calling ieee80211_stop_queues().
As frames that have previously been sent for transmission
(num_pending_tx) are completed, we will decrease num_pending_tx and wake
the queues by calling ieee80211_wake_queue(). ieee80211_wake_queue()
does not call wake_tx_queue, so we might still have frames in the
queue at this point.
While the queues were stopped, the socket buffer might have filled up,
and in order for user space to write more, we need to free the frames
in the queue, since they are accounted to the socket. In order to free
them, we first need to transmit them.
This problem cannot be reproduced on low-latency devices, e.g. pci,
since they call ath10k_mac_tx_push_pending() from
ath10k_htt_txrx_compl_task(). ath10k_htt_txrx_compl_task() is not called
on high-latency devices.
Fix the problem by calling ath10k_mac_tx_push_pending(), after
processing rx packets, just like for low-latency devices, also in the
SDIO case. Since we are calling ath10k_mac_tx_push_pending() directly,
we also need to export it.
Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2018-06-18 17:00:49 +03:00
|
|
|
#include "mac.h"
|
2017-04-26 12:18:00 +03:00
|
|
|
#include "targaddrs.h"
|
|
|
|
#include "trace.h"
|
|
|
|
#include "sdio.h"
|
2020-08-18 17:12:02 +03:00
|
|
|
#include "coredump.h"
|
|
|
|
|
|
|
|
void ath10k_sdio_fw_crashed_dump(struct ath10k *ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
2019-11-15 09:21:01 +02:00
|
|
|
#define ATH10K_SDIO_VSG_BUF_SIZE (64 * 1024)
|
2019-11-15 09:20:58 +02:00
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
/* inlined helper functions */
|
|
|
|
|
|
|
|
static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
return __ALIGN_MASK((len), ar_sdio->mbox_info.block_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline enum ath10k_htc_ep_id pipe_id_to_eid(u8 pipe_id)
|
|
|
|
{
|
|
|
|
return (enum ath10k_htc_ep_id)pipe_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void ath10k_sdio_mbox_free_rx_pkt(struct ath10k_sdio_rx_data *pkt)
|
|
|
|
{
|
|
|
|
dev_kfree_skb(pkt->skb);
|
|
|
|
pkt->skb = NULL;
|
|
|
|
pkt->alloc_len = 0;
|
|
|
|
pkt->act_len = 0;
|
|
|
|
pkt->trailer_only = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int ath10k_sdio_mbox_alloc_rx_pkt(struct ath10k_sdio_rx_data *pkt,
|
|
|
|
size_t act_len, size_t full_len,
|
|
|
|
bool part_of_bundle,
|
|
|
|
bool last_in_bundle)
|
|
|
|
{
|
|
|
|
pkt->skb = dev_alloc_skb(full_len);
|
|
|
|
if (!pkt->skb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
pkt->act_len = act_len;
|
|
|
|
pkt->alloc_len = full_len;
|
|
|
|
pkt->part_of_bundle = part_of_bundle;
|
|
|
|
pkt->last_in_bundle = last_in_bundle;
|
|
|
|
pkt->trailer_only = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool is_trailer_only_msg(struct ath10k_sdio_rx_data *pkt)
|
|
|
|
{
|
|
|
|
bool trailer_only = false;
|
|
|
|
struct ath10k_htc_hdr *htc_hdr =
|
|
|
|
(struct ath10k_htc_hdr *)pkt->skb->data;
|
|
|
|
u16 len = __le16_to_cpu(htc_hdr->len);
|
|
|
|
|
|
|
|
if (len == htc_hdr->trailer_len)
|
|
|
|
trailer_only = true;
|
|
|
|
|
|
|
|
return trailer_only;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sdio/mmc functions */
|
|
|
|
|
|
|
|
static inline void ath10k_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
|
|
|
|
unsigned int address,
|
|
|
|
unsigned char val)
|
|
|
|
{
|
|
|
|
*arg = FIELD_PREP(BIT(31), write) |
|
|
|
|
FIELD_PREP(BIT(27), raw) |
|
|
|
|
FIELD_PREP(BIT(26), 1) |
|
|
|
|
FIELD_PREP(GENMASK(25, 9), address) |
|
|
|
|
FIELD_PREP(BIT(8), 1) |
|
|
|
|
FIELD_PREP(GENMASK(7, 0), val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
|
|
|
|
unsigned int address,
|
|
|
|
unsigned char byte)
|
|
|
|
{
|
|
|
|
struct mmc_command io_cmd;
|
|
|
|
|
|
|
|
memset(&io_cmd, 0, sizeof(io_cmd));
|
|
|
|
ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
|
|
|
|
io_cmd.opcode = SD_IO_RW_DIRECT;
|
|
|
|
io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
return mmc_wait_for_cmd(card->host, &io_cmd, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_func0_cmd52_rd_byte(struct mmc_card *card,
|
|
|
|
unsigned int address,
|
|
|
|
unsigned char *byte)
|
|
|
|
{
|
|
|
|
struct mmc_command io_cmd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(&io_cmd, 0, sizeof(io_cmd));
|
|
|
|
ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 0, 0, address, 0);
|
|
|
|
io_cmd.opcode = SD_IO_RW_DIRECT;
|
|
|
|
io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
|
|
|
|
|
|
|
|
ret = mmc_wait_for_cmd(card->host, &io_cmd, 0);
|
|
|
|
if (!ret)
|
|
|
|
*byte = io_cmd.resp[0];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_config(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
unsigned char byte, asyncintdelay = 2;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio configuration\n");
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
byte = 0;
|
|
|
|
ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
|
|
|
|
SDIO_CCCR_DRIVE_STRENGTH,
|
|
|
|
&byte);
|
|
|
|
|
|
|
|
byte &= ~ATH10K_SDIO_DRIVE_DTSX_MASK;
|
|
|
|
byte |= FIELD_PREP(ATH10K_SDIO_DRIVE_DTSX_MASK,
|
|
|
|
ATH10K_SDIO_DRIVE_DTSX_TYPE_D);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
|
|
|
|
SDIO_CCCR_DRIVE_STRENGTH,
|
|
|
|
byte);
|
|
|
|
|
|
|
|
byte = 0;
|
|
|
|
ret = ath10k_sdio_func0_cmd52_rd_byte(
|
|
|
|
func->card,
|
|
|
|
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
|
|
|
|
&byte);
|
|
|
|
|
|
|
|
byte |= (CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
|
|
|
|
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
|
|
|
|
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
|
|
|
|
CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
|
|
|
|
byte);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to enable driver strength: %d\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte = 0;
|
|
|
|
ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
|
|
|
|
CCCR_SDIO_IRQ_MODE_REG_SDIO3,
|
|
|
|
&byte);
|
|
|
|
|
|
|
|
byte |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3;
|
|
|
|
|
|
|
|
ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
|
|
|
|
CCCR_SDIO_IRQ_MODE_REG_SDIO3,
|
|
|
|
byte);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to enable 4-bit async irq mode: %d\n",
|
|
|
|
ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte = 0;
|
|
|
|
ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
|
|
|
|
CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
|
|
|
|
&byte);
|
|
|
|
|
|
|
|
byte &= ~CCCR_SDIO_ASYNC_INT_DELAY_MASK;
|
|
|
|
byte |= FIELD_PREP(CCCR_SDIO_ASYNC_INT_DELAY_MASK, asyncintdelay);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
|
|
|
|
CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
|
|
|
|
byte);
|
|
|
|
|
|
|
|
/* give us some time to enable, in ms */
|
|
|
|
func->enable_timeout = 100;
|
|
|
|
|
|
|
|
ret = sdio_set_block_size(func, ar_sdio->mbox_info.block_size);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to set sdio block size to %d: %d\n",
|
|
|
|
ar_sdio->mbox_info.block_size, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_write32(struct ath10k *ar, u32 addr, u32 val)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
sdio_writel(func, val, addr, &ret);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to write 0x%x to address 0x%x: %d\n",
|
|
|
|
val, addr, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio write32 addr 0x%x val 0x%x\n",
|
|
|
|
addr, val);
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_writesb32(struct ath10k *ar, u32 addr, u32 val)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
__le32 *buf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*buf = cpu_to_le32(val);
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
ret = sdio_writesb(func, addr, buf, sizeof(*buf));
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to write value 0x%x to fixed sb address 0x%x: %d\n",
|
|
|
|
val, addr, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio writesb32 addr 0x%x val 0x%x\n",
|
|
|
|
addr, val);
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_read32(struct ath10k *ar, u32 addr, u32 *val)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
*val = sdio_readl(func, addr, &ret);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read from address 0x%x: %d\n",
|
|
|
|
addr, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read32 addr 0x%x val 0x%x\n",
|
|
|
|
addr, *val);
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_read(struct ath10k *ar, u32 addr, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
ret = sdio_memcpy_fromio(func, buf, addr, len);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read from address 0x%x: %d\n",
|
|
|
|
addr, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read addr 0x%x buf 0x%p len %zu\n",
|
|
|
|
addr, buf, len);
|
|
|
|
ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio read ", buf, len);
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_write(struct ath10k *ar, u32 addr, const void *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
/* For some reason toio() doesn't have const for the buffer, need
|
|
|
|
* an ugly hack to workaround that.
|
|
|
|
*/
|
|
|
|
ret = sdio_memcpy_toio(func, addr, (void *)buf, len);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to write to address 0x%x: %d\n",
|
|
|
|
addr, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio write addr 0x%x buf 0x%p len %zu\n",
|
|
|
|
addr, buf, len);
|
|
|
|
ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio write ", buf, len);
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_readsb(struct ath10k *ar, u32 addr, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
len = round_down(len, ar_sdio->mbox_info.block_size);
|
|
|
|
|
|
|
|
ret = sdio_readsb(func, buf, addr, len);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read from fixed (sb) address 0x%x: %d\n",
|
|
|
|
addr, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio readsb addr 0x%x buf 0x%p len %zu\n",
|
|
|
|
addr, buf, len);
|
|
|
|
ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL, "sdio readsb ", buf, len);
|
|
|
|
|
|
|
|
out:
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HIF mbox functions */
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
|
|
|
|
struct ath10k_sdio_rx_data *pkt,
|
|
|
|
u32 *lookaheads,
|
|
|
|
int *n_lookaheads)
|
|
|
|
{
|
|
|
|
struct ath10k_htc *htc = &ar->htc;
|
|
|
|
struct sk_buff *skb = pkt->skb;
|
|
|
|
struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
|
|
|
|
bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
|
|
|
|
enum ath10k_htc_ep_id eid;
|
|
|
|
u8 *trailer;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (trailer_present) {
|
ath10k: adjust skb length in ath10k_sdio_mbox_rx_packet
When the FW bundles multiple packets, pkt->act_len may be incorrect
as it refers to the first packet only (however, the FW will only
bundle packets that fit into the same pkt->alloc_len).
Before this patch, the skb length would be set (incorrectly) to
pkt->act_len in ath10k_sdio_mbox_rx_packet, and then later manually
adjusted in ath10k_sdio_mbox_rx_process_packet.
The first problem is that ath10k_sdio_mbox_rx_process_packet does not
use proper skb_put commands to adjust the length (it directly changes
skb->len), so we end up with a mismatch between skb->head + skb->tail
and skb->data + skb->len. This is quite serious, and causes corruptions
in the TCP stack, as the stack tries to coalesce packets, and relies
on skb->tail being correct (that is, skb_tail_pointer must point to
the first byte_after_ the data).
Instead of re-adjusting the size in ath10k_sdio_mbox_rx_process_packet,
this moves the code to ath10k_sdio_mbox_rx_packet, and also add a
bounds check, as skb_put would crash the kernel if not enough space is
available.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.
Fixes: 8530b4e7b22bc3b ("ath10k: sdio: set skb len for all rx packets")
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-09-10 16:46:17 +03:00
|
|
|
trailer = skb->data + skb->len - htc_hdr->trailer_len;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
eid = pipe_id_to_eid(htc_hdr->eid);
|
|
|
|
|
|
|
|
ret = ath10k_htc_process_trailer(htc,
|
|
|
|
trailer,
|
|
|
|
htc_hdr->trailer_len,
|
|
|
|
eid,
|
|
|
|
lookaheads,
|
|
|
|
n_lookaheads);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (is_trailer_only_msg(pkt))
|
|
|
|
pkt->trailer_only = true;
|
|
|
|
|
|
|
|
skb_trim(skb, skb->len - htc_hdr->trailer_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_pull(skb, sizeof(*htc_hdr));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar,
|
|
|
|
u32 lookaheads[],
|
|
|
|
int *n_lookahead)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_htc *htc = &ar->htc;
|
|
|
|
struct ath10k_sdio_rx_data *pkt;
|
|
|
|
struct ath10k_htc_ep *ep;
|
2019-11-15 09:21:03 +02:00
|
|
|
struct ath10k_skb_rxcb *cb;
|
2017-04-26 12:18:00 +03:00
|
|
|
enum ath10k_htc_ep_id id;
|
|
|
|
int ret, i, *n_lookahead_local;
|
|
|
|
u32 *lookaheads_local;
|
2018-06-29 16:27:56 +03:00
|
|
|
int lookahead_idx = 0;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
|
|
|
|
lookaheads_local = lookaheads;
|
|
|
|
n_lookahead_local = n_lookahead;
|
|
|
|
|
2018-06-29 16:27:56 +03:00
|
|
|
id = ((struct ath10k_htc_hdr *)
|
|
|
|
&lookaheads[lookahead_idx++])->eid;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
if (id >= ATH10K_HTC_EP_COUNT) {
|
|
|
|
ath10k_warn(ar, "invalid endpoint in look-ahead: %d\n",
|
|
|
|
id);
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep = &htc->endpoint[id];
|
|
|
|
|
|
|
|
if (ep->service_id == 0) {
|
|
|
|
ath10k_warn(ar, "ep %d is not connected\n", id);
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt = &ar_sdio->rx_pkts[i];
|
|
|
|
|
|
|
|
if (pkt->part_of_bundle && !pkt->last_in_bundle) {
|
|
|
|
/* Only read lookahead's from RX trailers
|
|
|
|
* for the last packet in a bundle.
|
|
|
|
*/
|
2018-06-29 16:27:56 +03:00
|
|
|
lookahead_idx--;
|
2017-04-26 12:18:00 +03:00
|
|
|
lookaheads_local = NULL;
|
|
|
|
n_lookahead_local = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath10k_sdio_mbox_rx_process_packet(ar,
|
|
|
|
pkt,
|
|
|
|
lookaheads_local,
|
|
|
|
n_lookahead_local);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2019-11-15 09:21:03 +02:00
|
|
|
if (!pkt->trailer_only) {
|
|
|
|
cb = ATH10K_SKB_RXCB(pkt->skb);
|
|
|
|
cb->eid = id;
|
|
|
|
|
|
|
|
skb_queue_tail(&ar_sdio->rx_head, pkt->skb);
|
|
|
|
queue_work(ar->workqueue_aux,
|
|
|
|
&ar_sdio->async_work_rx);
|
|
|
|
} else {
|
2017-04-26 12:18:00 +03:00
|
|
|
kfree_skb(pkt->skb);
|
2019-11-15 09:21:03 +02:00
|
|
|
}
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
/* The RX complete handler now owns the skb...*/
|
|
|
|
pkt->skb = NULL;
|
|
|
|
pkt->alloc_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
/* Free all packets that was not passed on to the RX completion
|
|
|
|
* handler...
|
|
|
|
*/
|
|
|
|
for (; i < ar_sdio->n_rx_pkts; i++)
|
|
|
|
ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar,
|
|
|
|
struct ath10k_sdio_rx_data *rx_pkts,
|
|
|
|
struct ath10k_htc_hdr *htc_hdr,
|
|
|
|
size_t full_len, size_t act_len,
|
|
|
|
size_t *bndl_cnt)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
int ret, i;
|
ath10k: change bundle count for max rx bundle for sdio
For max bundle size 32, the bundle mask is not same with 8/16.
Change it to match the max bundle size of htc. Otherwise it
will not match with firmware, for example, when bundle count
is 17, then flags of ath10k_htc_hdr is 0x4, if without this
patch, it will be considered as non-bundled packet because it
does not have mask 0xF0, then trigger error message later:
payload length 56747 exceeds max htc length: 4088.
htc->max_msgs_per_htc_bundle is the min value of
HTC_HOST_MAX_MSG_PER_RX_BUNDLE and
msg->ready_ext.max_msgs_per_htc_bundle of ath10k_htc_wait_target,
it will be sent to firmware later in ath10k_htc_start, then
firmware will use it as the final max rx bundle count, in
WLAN.RMH.4.4.1-00029, msg->ready_ext.max_msgs_per_htc_bundle
is 32, it is same with HTC_HOST_MAX_MSG_PER_RX_BUNDLE, so the
final max rx bundle count will be set to 32 in firmware.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Fixes: 224776520ead69e ("ath10k: change max RX bundle size from 8 to 32 for sdio")
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:59 +02:00
|
|
|
u8 max_msgs = ar->htc.max_msgs_per_htc_bundle;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
ath10k: change bundle count for max rx bundle for sdio
For max bundle size 32, the bundle mask is not same with 8/16.
Change it to match the max bundle size of htc. Otherwise it
will not match with firmware, for example, when bundle count
is 17, then flags of ath10k_htc_hdr is 0x4, if without this
patch, it will be considered as non-bundled packet because it
does not have mask 0xF0, then trigger error message later:
payload length 56747 exceeds max htc length: 4088.
htc->max_msgs_per_htc_bundle is the min value of
HTC_HOST_MAX_MSG_PER_RX_BUNDLE and
msg->ready_ext.max_msgs_per_htc_bundle of ath10k_htc_wait_target,
it will be sent to firmware later in ath10k_htc_start, then
firmware will use it as the final max rx bundle count, in
WLAN.RMH.4.4.1-00029, msg->ready_ext.max_msgs_per_htc_bundle
is 32, it is same with HTC_HOST_MAX_MSG_PER_RX_BUNDLE, so the
final max rx bundle count will be set to 32 in firmware.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Fixes: 224776520ead69e ("ath10k: change max RX bundle size from 8 to 32 for sdio")
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:59 +02:00
|
|
|
*bndl_cnt = ath10k_htc_get_bundle_count(max_msgs, htc_hdr->flags);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
ath10k: change bundle count for max rx bundle for sdio
For max bundle size 32, the bundle mask is not same with 8/16.
Change it to match the max bundle size of htc. Otherwise it
will not match with firmware, for example, when bundle count
is 17, then flags of ath10k_htc_hdr is 0x4, if without this
patch, it will be considered as non-bundled packet because it
does not have mask 0xF0, then trigger error message later:
payload length 56747 exceeds max htc length: 4088.
htc->max_msgs_per_htc_bundle is the min value of
HTC_HOST_MAX_MSG_PER_RX_BUNDLE and
msg->ready_ext.max_msgs_per_htc_bundle of ath10k_htc_wait_target,
it will be sent to firmware later in ath10k_htc_start, then
firmware will use it as the final max rx bundle count, in
WLAN.RMH.4.4.1-00029, msg->ready_ext.max_msgs_per_htc_bundle
is 32, it is same with HTC_HOST_MAX_MSG_PER_RX_BUNDLE, so the
final max rx bundle count will be set to 32 in firmware.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Fixes: 224776520ead69e ("ath10k: change max RX bundle size from 8 to 32 for sdio")
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:59 +02:00
|
|
|
if (*bndl_cnt > max_msgs) {
|
2017-04-26 12:18:00 +03:00
|
|
|
ath10k_warn(ar,
|
|
|
|
"HTC bundle length %u exceeds maximum %u\n",
|
|
|
|
le16_to_cpu(htc_hdr->len),
|
ath10k: change bundle count for max rx bundle for sdio
For max bundle size 32, the bundle mask is not same with 8/16.
Change it to match the max bundle size of htc. Otherwise it
will not match with firmware, for example, when bundle count
is 17, then flags of ath10k_htc_hdr is 0x4, if without this
patch, it will be considered as non-bundled packet because it
does not have mask 0xF0, then trigger error message later:
payload length 56747 exceeds max htc length: 4088.
htc->max_msgs_per_htc_bundle is the min value of
HTC_HOST_MAX_MSG_PER_RX_BUNDLE and
msg->ready_ext.max_msgs_per_htc_bundle of ath10k_htc_wait_target,
it will be sent to firmware later in ath10k_htc_start, then
firmware will use it as the final max rx bundle count, in
WLAN.RMH.4.4.1-00029, msg->ready_ext.max_msgs_per_htc_bundle
is 32, it is same with HTC_HOST_MAX_MSG_PER_RX_BUNDLE, so the
final max rx bundle count will be set to 32 in firmware.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Fixes: 224776520ead69e ("ath10k: change max RX bundle size from 8 to 32 for sdio")
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:59 +02:00
|
|
|
max_msgs);
|
2017-04-26 12:18:00 +03:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate bndl_cnt extra skb's for the bundle.
|
|
|
|
* The package containing the
|
|
|
|
* ATH10K_HTC_FLAG_BUNDLE_MASK flag is not included
|
|
|
|
* in bndl_cnt. The skb for that packet will be
|
|
|
|
* allocated separately.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < *bndl_cnt; i++) {
|
|
|
|
ret = ath10k_sdio_mbox_alloc_rx_pkt(&rx_pkts[i],
|
|
|
|
act_len,
|
|
|
|
full_len,
|
|
|
|
true,
|
|
|
|
false);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
|
|
|
|
u32 lookaheads[], int n_lookaheads)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_htc_hdr *htc_hdr;
|
|
|
|
size_t full_len, act_len;
|
|
|
|
bool last_in_bundle;
|
|
|
|
int ret, i;
|
2019-11-15 09:20:58 +02:00
|
|
|
int pkt_cnt = 0;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
|
2020-04-12 23:54:35 +05:30
|
|
|
ath10k_warn(ar, "the total number of pkts to be fetched (%u) exceeds maximum %u\n",
|
2019-11-15 09:21:09 +02:00
|
|
|
n_lookaheads, ATH10K_SDIO_MAX_RX_MSGS);
|
2017-04-26 12:18:00 +03:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n_lookaheads; i++) {
|
|
|
|
htc_hdr = (struct ath10k_htc_hdr *)&lookaheads[i];
|
|
|
|
last_in_bundle = false;
|
|
|
|
|
2019-11-15 09:21:09 +02:00
|
|
|
if (le16_to_cpu(htc_hdr->len) > ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) {
|
|
|
|
ath10k_warn(ar, "payload length %d exceeds max htc length: %zu\n",
|
2017-04-26 12:18:00 +03:00
|
|
|
le16_to_cpu(htc_hdr->len),
|
|
|
|
ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH);
|
|
|
|
ret = -ENOMEM;
|
ath10k: start recovery process when payload length exceeds max htc length for sdio
When simulate random transfer fail for sdio write and read, it happened
"payload length exceeds max htc length" and recovery later sometimes.
Test steps:
1. Add config and update kernel:
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
2. Run simulate fail:
cd /sys/kernel/debug/mmc1/fail_mmc_request
echo 10 > probability
echo 10 > times # repeat until hitting issues
3. It happened payload length exceeds max htc length.
[ 199.935506] ath10k_sdio mmc1:0001:1: payload length 57005 exceeds max htc length: 4088
....
[ 264.990191] ath10k_sdio mmc1:0001:1: payload length 57005 exceeds max htc length: 4088
4. after some time, such as 60 seconds, it start recovery which triggered
by wmi command timeout for periodic scan.
[ 269.229232] ieee80211 phy0: Hardware restart was requested
[ 269.734693] ath10k_sdio mmc1:0001:1: device successfully recovered
The simulate fail of sdio is not a real sdio transter fail, it only
set an error status in mmc_should_fail_request after the transfer end,
actually the transfer is success, then sdio_io_rw_ext_helper will
return error status and stop transfer the left data. For example,
the really RX len is 286 bytes, then it will split to 2 blocks in
sdio_io_rw_ext_helper, one is 256 bytes, left is 30 bytes, if the
first 256 bytes get an error status by mmc_should_fail_request,then
the left 30 bytes will not read in this RX operation. Then when the
next RX arrive, the left 30 bytes will be considered as the header
of the read, the top 4 bytes of the 30 bytes will be considered as
lookaheads, but actually the 4 bytes is not the lookaheads, so the len
from this lookaheads is not correct, it exceeds max htc length 4088
sometimes. When happened exceeds, the buffer chain is not matched between
firmware and ath10k, then it need to start recovery ASAP. Recently then
recovery will be started by wmi command timeout, but it will be long time
later, for example, it is 60+ seconds later from the periodic scan, if
it does not have periodic scan, it will be longer.
Start recovery when it happened "payload length exceeds max htc length"
will be reasonable.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200108031957.22308-3-wgong@codeaurora.org
2020-08-14 18:17:08 +03:00
|
|
|
|
2020-09-08 04:13:06 +00:00
|
|
|
ath10k_core_start_recovery(ar);
|
ath10k: start recovery process when payload length exceeds max htc length for sdio
When simulate random transfer fail for sdio write and read, it happened
"payload length exceeds max htc length" and recovery later sometimes.
Test steps:
1. Add config and update kernel:
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
2. Run simulate fail:
cd /sys/kernel/debug/mmc1/fail_mmc_request
echo 10 > probability
echo 10 > times # repeat until hitting issues
3. It happened payload length exceeds max htc length.
[ 199.935506] ath10k_sdio mmc1:0001:1: payload length 57005 exceeds max htc length: 4088
....
[ 264.990191] ath10k_sdio mmc1:0001:1: payload length 57005 exceeds max htc length: 4088
4. after some time, such as 60 seconds, it start recovery which triggered
by wmi command timeout for periodic scan.
[ 269.229232] ieee80211 phy0: Hardware restart was requested
[ 269.734693] ath10k_sdio mmc1:0001:1: device successfully recovered
The simulate fail of sdio is not a real sdio transter fail, it only
set an error status in mmc_should_fail_request after the transfer end,
actually the transfer is success, then sdio_io_rw_ext_helper will
return error status and stop transfer the left data. For example,
the really RX len is 286 bytes, then it will split to 2 blocks in
sdio_io_rw_ext_helper, one is 256 bytes, left is 30 bytes, if the
first 256 bytes get an error status by mmc_should_fail_request,then
the left 30 bytes will not read in this RX operation. Then when the
next RX arrive, the left 30 bytes will be considered as the header
of the read, the top 4 bytes of the 30 bytes will be considered as
lookaheads, but actually the 4 bytes is not the lookaheads, so the len
from this lookaheads is not correct, it exceeds max htc length 4088
sometimes. When happened exceeds, the buffer chain is not matched between
firmware and ath10k, then it need to start recovery ASAP. Recently then
recovery will be started by wmi command timeout, but it will be long time
later, for example, it is 60+ seconds later from the periodic scan, if
it does not have periodic scan, it will be longer.
Start recovery when it happened "payload length exceeds max htc length"
will be reasonable.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200108031957.22308-3-wgong@codeaurora.org
2020-08-14 18:17:08 +03:00
|
|
|
ath10k_warn(ar, "exceeds length, start recovery\n");
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
|
|
|
|
full_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio, act_len);
|
|
|
|
|
|
|
|
if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) {
|
2019-11-15 09:21:09 +02:00
|
|
|
ath10k_warn(ar, "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n",
|
2017-04-26 12:18:00 +03:00
|
|
|
htc_hdr->eid, htc_hdr->flags,
|
|
|
|
le16_to_cpu(htc_hdr->len));
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
ath10k: change bundle count for max rx bundle for sdio
For max bundle size 32, the bundle mask is not same with 8/16.
Change it to match the max bundle size of htc. Otherwise it
will not match with firmware, for example, when bundle count
is 17, then flags of ath10k_htc_hdr is 0x4, if without this
patch, it will be considered as non-bundled packet because it
does not have mask 0xF0, then trigger error message later:
payload length 56747 exceeds max htc length: 4088.
htc->max_msgs_per_htc_bundle is the min value of
HTC_HOST_MAX_MSG_PER_RX_BUNDLE and
msg->ready_ext.max_msgs_per_htc_bundle of ath10k_htc_wait_target,
it will be sent to firmware later in ath10k_htc_start, then
firmware will use it as the final max rx bundle count, in
WLAN.RMH.4.4.1-00029, msg->ready_ext.max_msgs_per_htc_bundle
is 32, it is same with HTC_HOST_MAX_MSG_PER_RX_BUNDLE, so the
final max rx bundle count will be set to 32 in firmware.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Fixes: 224776520ead69e ("ath10k: change max RX bundle size from 8 to 32 for sdio")
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:59 +02:00
|
|
|
if (ath10k_htc_get_bundle_count(
|
|
|
|
ar->htc.max_msgs_per_htc_bundle, htc_hdr->flags)) {
|
2017-04-26 12:18:00 +03:00
|
|
|
/* HTC header indicates that every packet to follow
|
|
|
|
* has the same padded length so that it can be
|
|
|
|
* optimally fetched as a full bundle.
|
|
|
|
*/
|
|
|
|
size_t bndl_cnt;
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
ret = ath10k_sdio_mbox_alloc_bundle(ar,
|
|
|
|
&ar_sdio->rx_pkts[pkt_cnt],
|
|
|
|
htc_hdr,
|
|
|
|
full_len,
|
|
|
|
act_len,
|
|
|
|
&bndl_cnt);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
2019-04-19 10:29:06 +03:00
|
|
|
if (ret) {
|
2019-11-15 09:21:09 +02:00
|
|
|
ath10k_warn(ar, "failed to allocate a bundle: %d\n",
|
|
|
|
ret);
|
2019-04-19 10:29:06 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
pkt_cnt += bndl_cnt;
|
2019-11-15 09:21:09 +02:00
|
|
|
|
|
|
|
/* next buffer will be the last in the bundle */
|
2017-04-26 12:18:00 +03:00
|
|
|
last_in_bundle = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate skb for packet. If the packet had the
|
|
|
|
* ATH10K_HTC_FLAG_BUNDLE_MASK flag set, all bundled
|
|
|
|
* packet skb's have been allocated in the previous step.
|
|
|
|
*/
|
2018-06-29 16:27:58 +03:00
|
|
|
if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK)
|
|
|
|
full_len += ATH10K_HIF_MBOX_BLOCK_SIZE;
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt],
|
2017-04-26 12:18:00 +03:00
|
|
|
act_len,
|
|
|
|
full_len,
|
|
|
|
last_in_bundle,
|
|
|
|
last_in_bundle);
|
2019-05-23 15:15:34 +08:00
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret);
|
|
|
|
goto err;
|
|
|
|
}
|
2019-11-15 09:20:58 +02:00
|
|
|
|
|
|
|
pkt_cnt++;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
ar_sdio->n_rx_pkts = pkt_cnt;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
for (i = 0; i < ATH10K_SDIO_MAX_RX_MSGS; i++) {
|
|
|
|
if (!ar_sdio->rx_pkts[i].alloc_len)
|
|
|
|
break;
|
|
|
|
ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
2019-11-15 09:20:58 +02:00
|
|
|
struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0];
|
2017-04-26 12:18:00 +03:00
|
|
|
struct sk_buff *skb = pkt->skb;
|
ath10k: adjust skb length in ath10k_sdio_mbox_rx_packet
When the FW bundles multiple packets, pkt->act_len may be incorrect
as it refers to the first packet only (however, the FW will only
bundle packets that fit into the same pkt->alloc_len).
Before this patch, the skb length would be set (incorrectly) to
pkt->act_len in ath10k_sdio_mbox_rx_packet, and then later manually
adjusted in ath10k_sdio_mbox_rx_process_packet.
The first problem is that ath10k_sdio_mbox_rx_process_packet does not
use proper skb_put commands to adjust the length (it directly changes
skb->len), so we end up with a mismatch between skb->head + skb->tail
and skb->data + skb->len. This is quite serious, and causes corruptions
in the TCP stack, as the stack tries to coalesce packets, and relies
on skb->tail being correct (that is, skb_tail_pointer must point to
the first byte_after_ the data).
Instead of re-adjusting the size in ath10k_sdio_mbox_rx_process_packet,
this moves the code to ath10k_sdio_mbox_rx_packet, and also add a
bounds check, as skb_put would crash the kernel if not enough space is
available.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.
Fixes: 8530b4e7b22bc3b ("ath10k: sdio: set skb len for all rx packets")
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-09-10 16:46:17 +03:00
|
|
|
struct ath10k_htc_hdr *htc_hdr;
|
2017-04-26 12:18:00 +03:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
|
|
|
|
skb->data, pkt->alloc_len);
|
ath10k: drop RX skb with invalid length for sdio
When simulate random transfer fail for sdio write and read, it crash
sometimes.
Test steps:
1. Add config and update kernel:
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
2. run simulate fail:
cd /sys/kernel/debug/mmc1/fail_mmc_request
echo 10 > probability
echo 10 > times # repeat until hitting issues
3. it crash, the act len of ath10k_htc_hdr is higher than allocate len, it cause panic:
[ 99.723482] skbuff: skb_over_panic: text:00000000caa0f780 len:57013 put:57013 head:000000004116f24a data:0000000019ecb4dc tail:0xdef5 end:0x640 dev:<NULL>
[ 99.737697] ------------[ cut here ]------------
[ 99.742327] kernel BUG at /mnt/host/source/src/third_party/kernel/v4.19/net/core/skbuff.c:104!
[ 99.750937] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 99.831154] Process kworker/0:2 (pid: 151, stack limit = 0x00000000728010bf)
[ 99.838200] CPU: 0 PID: 151 Comm: kworker/0:2 Tainted: G W 4.19.85 #48
[ 99.846022] Hardware name: MediaTek krane sku0 board (DT)
[ 99.851429] Workqueue: events sdio_irq_work
[ 99.855614] pstate: 60000005 (nZCv daif -PAN -UAO)
[ 99.860402] pc : skb_panic+0x64/0x68
[ 99.863974] lr : skb_panic+0x64/0x68
[ 99.867542] sp : ffffff8008833a90
[ 99.870850] x29: ffffff8008833ac0 x28: ffffffe52e337370
[ 99.876159] x27: ffffffe52e328a90 x26: 000000000000e0d0
[ 99.881469] x25: ffffffe52e336b60 x24: 000000000000deb5
[ 99.886779] x23: ffffffe52e340680 x22: ffffffe4efd47e00
[ 99.892088] x21: 000000000000deb5 x20: ffffffa516d85b4c
[ 99.897397] x19: ffffffa526928037 x18: 0000000000000000
[ 99.902706] x17: 000000000000003c x16: ffffffa5265b6c80
[ 99.908015] x15: 0000000000000006 x14: 3a76656420303436
[ 99.913325] x13: 0000000000029bf0 x12: 0000000000000000
[ 99.918634] x11: 0000000000000000 x10: 0000000000000000
[ 99.923943] x9 : a3b907e4b2783000 x8 : a3b907e4b2783000
[ 99.929253] x7 : 0000000000000000 x6 : ffffffa526f66d76
[ 99.934563] x5 : 0000000000000000 x4 : 0000000000000000
[ 99.939872] x3 : 000000000002a5ab x2 : ffffffe53feed918
[ 99.945182] x1 : ffffffe53fee4a08 x0 : 000000000000008e
[ 99.950491] Call trace:
[ 99.952937] skb_panic+0x64/0x68
[ 99.956165] skb_put+0x7c/0x84
[ 99.959224] ath10k_sdio_irq_handler+0x740/0xbb8 [ath10k_sdio]
[ 99.965055] process_sdio_pending_irqs+0x58/0x1a4
[ 99.969758] sdio_run_irqs+0x34/0x60
[ 99.973329] sdio_irq_work+0x1c/0x28
[ 99.974930] cros-ec-spi spi2.0: SPI transfer timed out
[ 99.976904] process_one_work+0x210/0x410
[ 99.976911] worker_thread+0x234/0x3dc
[ 99.976923] kthread+0x120/0x130
[ 99.982090] cros-ec-spi spi2.0: spi transfer failed: -110
[ 99.986054] ret_from_fork+0x10/0x18
[ 99.986063] Code: aa1403e2 2a1503e4 a90023e9 97e37d1a (d4210000)
[ 99.986068] ---[ end trace cb6d948c5a0fd6c7 ]---
[ 100.017250] Kernel panic - not syncing: Fatal exception
[ 100.018879] cros-ec-spi spi2.0: Command xfer error (err:-110)
[ 100.023659] SMP: stopping secondary CPUs
[ 100.023703] Kernel Offset: 0x251dc00000 from 0xffffff8008000000
[ 100.023707] CPU features: 0x0,2188200c
[ 100.023709] Memory Limit: none
The simulate fail of sdio is not a real sdio transter fail, it only
set an error status in mmc_should_fail_request after the transfer end,
actually the transfer is success, then sdio_io_rw_ext_helper will
return error status and stop transfer the left data. For example,
the really RX len is 286 bytes, then it will split to 2 blocks in
sdio_io_rw_ext_helper, one is 256 bytes, left is 30 bytes, if the
first 256 bytes get an error status by mmc_should_fail_request,then
the left 30 bytes will not read in this RX operation. Then when the
next RX arrive, the left 30 bytes will be considered as the header
of the read, the top 8 bytes will be considered as ath10k_htc_hdr,
but actually the 8 bytes is not the ath10k_htc_hdr, so the act_len
from this ath10k_htc_hdr is not correct, if it is a big value, such
as 57013, it will trigger skb_panic.
Drop the skb with invalid length will be reasonable.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-12-31 17:27:06 +08:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
ath10k: adjust skb length in ath10k_sdio_mbox_rx_packet
When the FW bundles multiple packets, pkt->act_len may be incorrect
as it refers to the first packet only (however, the FW will only
bundle packets that fit into the same pkt->alloc_len).
Before this patch, the skb length would be set (incorrectly) to
pkt->act_len in ath10k_sdio_mbox_rx_packet, and then later manually
adjusted in ath10k_sdio_mbox_rx_process_packet.
The first problem is that ath10k_sdio_mbox_rx_process_packet does not
use proper skb_put commands to adjust the length (it directly changes
skb->len), so we end up with a mismatch between skb->head + skb->tail
and skb->data + skb->len. This is quite serious, and causes corruptions
in the TCP stack, as the stack tries to coalesce packets, and relies
on skb->tail being correct (that is, skb_tail_pointer must point to
the first byte_after_ the data).
Instead of re-adjusting the size in ath10k_sdio_mbox_rx_process_packet,
this moves the code to ath10k_sdio_mbox_rx_packet, and also add a
bounds check, as skb_put would crash the kernel if not enough space is
available.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.
Fixes: 8530b4e7b22bc3b ("ath10k: sdio: set skb len for all rx packets")
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-09-10 16:46:17 +03:00
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
htc_hdr = (struct ath10k_htc_hdr *)skb->data;
|
|
|
|
pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
|
ath10k: drop RX skb with invalid length for sdio
When simulate random transfer fail for sdio write and read, it crash
sometimes.
Test steps:
1. Add config and update kernel:
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
2. run simulate fail:
cd /sys/kernel/debug/mmc1/fail_mmc_request
echo 10 > probability
echo 10 > times # repeat until hitting issues
3. it crash, the act len of ath10k_htc_hdr is higher than allocate len, it cause panic:
[ 99.723482] skbuff: skb_over_panic: text:00000000caa0f780 len:57013 put:57013 head:000000004116f24a data:0000000019ecb4dc tail:0xdef5 end:0x640 dev:<NULL>
[ 99.737697] ------------[ cut here ]------------
[ 99.742327] kernel BUG at /mnt/host/source/src/third_party/kernel/v4.19/net/core/skbuff.c:104!
[ 99.750937] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 99.831154] Process kworker/0:2 (pid: 151, stack limit = 0x00000000728010bf)
[ 99.838200] CPU: 0 PID: 151 Comm: kworker/0:2 Tainted: G W 4.19.85 #48
[ 99.846022] Hardware name: MediaTek krane sku0 board (DT)
[ 99.851429] Workqueue: events sdio_irq_work
[ 99.855614] pstate: 60000005 (nZCv daif -PAN -UAO)
[ 99.860402] pc : skb_panic+0x64/0x68
[ 99.863974] lr : skb_panic+0x64/0x68
[ 99.867542] sp : ffffff8008833a90
[ 99.870850] x29: ffffff8008833ac0 x28: ffffffe52e337370
[ 99.876159] x27: ffffffe52e328a90 x26: 000000000000e0d0
[ 99.881469] x25: ffffffe52e336b60 x24: 000000000000deb5
[ 99.886779] x23: ffffffe52e340680 x22: ffffffe4efd47e00
[ 99.892088] x21: 000000000000deb5 x20: ffffffa516d85b4c
[ 99.897397] x19: ffffffa526928037 x18: 0000000000000000
[ 99.902706] x17: 000000000000003c x16: ffffffa5265b6c80
[ 99.908015] x15: 0000000000000006 x14: 3a76656420303436
[ 99.913325] x13: 0000000000029bf0 x12: 0000000000000000
[ 99.918634] x11: 0000000000000000 x10: 0000000000000000
[ 99.923943] x9 : a3b907e4b2783000 x8 : a3b907e4b2783000
[ 99.929253] x7 : 0000000000000000 x6 : ffffffa526f66d76
[ 99.934563] x5 : 0000000000000000 x4 : 0000000000000000
[ 99.939872] x3 : 000000000002a5ab x2 : ffffffe53feed918
[ 99.945182] x1 : ffffffe53fee4a08 x0 : 000000000000008e
[ 99.950491] Call trace:
[ 99.952937] skb_panic+0x64/0x68
[ 99.956165] skb_put+0x7c/0x84
[ 99.959224] ath10k_sdio_irq_handler+0x740/0xbb8 [ath10k_sdio]
[ 99.965055] process_sdio_pending_irqs+0x58/0x1a4
[ 99.969758] sdio_run_irqs+0x34/0x60
[ 99.973329] sdio_irq_work+0x1c/0x28
[ 99.974930] cros-ec-spi spi2.0: SPI transfer timed out
[ 99.976904] process_one_work+0x210/0x410
[ 99.976911] worker_thread+0x234/0x3dc
[ 99.976923] kthread+0x120/0x130
[ 99.982090] cros-ec-spi spi2.0: spi transfer failed: -110
[ 99.986054] ret_from_fork+0x10/0x18
[ 99.986063] Code: aa1403e2 2a1503e4 a90023e9 97e37d1a (d4210000)
[ 99.986068] ---[ end trace cb6d948c5a0fd6c7 ]---
[ 100.017250] Kernel panic - not syncing: Fatal exception
[ 100.018879] cros-ec-spi spi2.0: Command xfer error (err:-110)
[ 100.023659] SMP: stopping secondary CPUs
[ 100.023703] Kernel Offset: 0x251dc00000 from 0xffffff8008000000
[ 100.023707] CPU features: 0x0,2188200c
[ 100.023709] Memory Limit: none
The simulate fail of sdio is not a real sdio transter fail, it only
set an error status in mmc_should_fail_request after the transfer end,
actually the transfer is success, then sdio_io_rw_ext_helper will
return error status and stop transfer the left data. For example,
the really RX len is 286 bytes, then it will split to 2 blocks in
sdio_io_rw_ext_helper, one is 256 bytes, left is 30 bytes, if the
first 256 bytes get an error status by mmc_should_fail_request,then
the left 30 bytes will not read in this RX operation. Then when the
next RX arrive, the left 30 bytes will be considered as the header
of the read, the top 8 bytes will be considered as ath10k_htc_hdr,
but actually the 8 bytes is not the ath10k_htc_hdr, so the act_len
from this ath10k_htc_hdr is not correct, if it is a big value, such
as 57013, it will trigger skb_panic.
Drop the skb with invalid length will be reasonable.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-12-31 17:27:06 +08:00
|
|
|
|
|
|
|
if (pkt->act_len > pkt->alloc_len) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
skb_put(skb, pkt->act_len);
|
ath10k: drop RX skb with invalid length for sdio
When simulate random transfer fail for sdio write and read, it crash
sometimes.
Test steps:
1. Add config and update kernel:
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
2. run simulate fail:
cd /sys/kernel/debug/mmc1/fail_mmc_request
echo 10 > probability
echo 10 > times # repeat until hitting issues
3. it crash, the act len of ath10k_htc_hdr is higher than allocate len, it cause panic:
[ 99.723482] skbuff: skb_over_panic: text:00000000caa0f780 len:57013 put:57013 head:000000004116f24a data:0000000019ecb4dc tail:0xdef5 end:0x640 dev:<NULL>
[ 99.737697] ------------[ cut here ]------------
[ 99.742327] kernel BUG at /mnt/host/source/src/third_party/kernel/v4.19/net/core/skbuff.c:104!
[ 99.750937] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 99.831154] Process kworker/0:2 (pid: 151, stack limit = 0x00000000728010bf)
[ 99.838200] CPU: 0 PID: 151 Comm: kworker/0:2 Tainted: G W 4.19.85 #48
[ 99.846022] Hardware name: MediaTek krane sku0 board (DT)
[ 99.851429] Workqueue: events sdio_irq_work
[ 99.855614] pstate: 60000005 (nZCv daif -PAN -UAO)
[ 99.860402] pc : skb_panic+0x64/0x68
[ 99.863974] lr : skb_panic+0x64/0x68
[ 99.867542] sp : ffffff8008833a90
[ 99.870850] x29: ffffff8008833ac0 x28: ffffffe52e337370
[ 99.876159] x27: ffffffe52e328a90 x26: 000000000000e0d0
[ 99.881469] x25: ffffffe52e336b60 x24: 000000000000deb5
[ 99.886779] x23: ffffffe52e340680 x22: ffffffe4efd47e00
[ 99.892088] x21: 000000000000deb5 x20: ffffffa516d85b4c
[ 99.897397] x19: ffffffa526928037 x18: 0000000000000000
[ 99.902706] x17: 000000000000003c x16: ffffffa5265b6c80
[ 99.908015] x15: 0000000000000006 x14: 3a76656420303436
[ 99.913325] x13: 0000000000029bf0 x12: 0000000000000000
[ 99.918634] x11: 0000000000000000 x10: 0000000000000000
[ 99.923943] x9 : a3b907e4b2783000 x8 : a3b907e4b2783000
[ 99.929253] x7 : 0000000000000000 x6 : ffffffa526f66d76
[ 99.934563] x5 : 0000000000000000 x4 : 0000000000000000
[ 99.939872] x3 : 000000000002a5ab x2 : ffffffe53feed918
[ 99.945182] x1 : ffffffe53fee4a08 x0 : 000000000000008e
[ 99.950491] Call trace:
[ 99.952937] skb_panic+0x64/0x68
[ 99.956165] skb_put+0x7c/0x84
[ 99.959224] ath10k_sdio_irq_handler+0x740/0xbb8 [ath10k_sdio]
[ 99.965055] process_sdio_pending_irqs+0x58/0x1a4
[ 99.969758] sdio_run_irqs+0x34/0x60
[ 99.973329] sdio_irq_work+0x1c/0x28
[ 99.974930] cros-ec-spi spi2.0: SPI transfer timed out
[ 99.976904] process_one_work+0x210/0x410
[ 99.976911] worker_thread+0x234/0x3dc
[ 99.976923] kthread+0x120/0x130
[ 99.982090] cros-ec-spi spi2.0: spi transfer failed: -110
[ 99.986054] ret_from_fork+0x10/0x18
[ 99.986063] Code: aa1403e2 2a1503e4 a90023e9 97e37d1a (d4210000)
[ 99.986068] ---[ end trace cb6d948c5a0fd6c7 ]---
[ 100.017250] Kernel panic - not syncing: Fatal exception
[ 100.018879] cros-ec-spi spi2.0: Command xfer error (err:-110)
[ 100.023659] SMP: stopping secondary CPUs
[ 100.023703] Kernel Offset: 0x251dc00000 from 0xffffff8008000000
[ 100.023707] CPU features: 0x0,2188200c
[ 100.023709] Memory Limit: none
The simulate fail of sdio is not a real sdio transter fail, it only
set an error status in mmc_should_fail_request after the transfer end,
actually the transfer is success, then sdio_io_rw_ext_helper will
return error status and stop transfer the left data. For example,
the really RX len is 286 bytes, then it will split to 2 blocks in
sdio_io_rw_ext_helper, one is 256 bytes, left is 30 bytes, if the
first 256 bytes get an error status by mmc_should_fail_request,then
the left 30 bytes will not read in this RX operation. Then when the
next RX arrive, the left 30 bytes will be considered as the header
of the read, the top 8 bytes will be considered as ath10k_htc_hdr,
but actually the 8 bytes is not the ath10k_htc_hdr, so the act_len
from this ath10k_htc_hdr is not correct, if it is a big value, such
as 57013, it will trigger skb_panic.
Drop the skb with invalid length will be reasonable.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-12-31 17:27:06 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
ar_sdio->n_rx_pkts = 0;
|
|
|
|
ath10k_sdio_mbox_free_rx_pkt(pkt);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
2019-11-15 09:20:58 +02:00
|
|
|
struct ath10k_sdio_rx_data *pkt;
|
|
|
|
struct ath10k_htc_hdr *htc_hdr;
|
2017-04-26 12:18:00 +03:00
|
|
|
int ret, i;
|
2019-11-15 09:20:58 +02:00
|
|
|
u32 pkt_offset, virt_pkt_len;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
virt_pkt_len = 0;
|
|
|
|
for (i = 0; i < ar_sdio->n_rx_pkts; i++)
|
|
|
|
virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len;
|
|
|
|
|
|
|
|
if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) {
|
|
|
|
ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len);
|
|
|
|
ret = -E2BIG;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
|
|
|
|
ar_sdio->vsg_buffer, virt_pkt_len);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read bundle packets: %d", ret);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt_offset = 0;
|
2017-04-26 12:18:00 +03:00
|
|
|
for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
|
2019-11-15 09:20:58 +02:00
|
|
|
pkt = &ar_sdio->rx_pkts[i];
|
|
|
|
htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
|
|
|
|
pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
|
|
|
|
|
2020-02-11 10:59:38 +02:00
|
|
|
if (pkt->act_len > pkt->alloc_len) {
|
ath10k: drop RX skb with invalid length for sdio
When simulate random transfer fail for sdio write and read, it crash
sometimes.
Test steps:
1. Add config and update kernel:
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
2. run simulate fail:
cd /sys/kernel/debug/mmc1/fail_mmc_request
echo 10 > probability
echo 10 > times # repeat until hitting issues
3. it crash, the act len of ath10k_htc_hdr is higher than allocate len, it cause panic:
[ 99.723482] skbuff: skb_over_panic: text:00000000caa0f780 len:57013 put:57013 head:000000004116f24a data:0000000019ecb4dc tail:0xdef5 end:0x640 dev:<NULL>
[ 99.737697] ------------[ cut here ]------------
[ 99.742327] kernel BUG at /mnt/host/source/src/third_party/kernel/v4.19/net/core/skbuff.c:104!
[ 99.750937] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 99.831154] Process kworker/0:2 (pid: 151, stack limit = 0x00000000728010bf)
[ 99.838200] CPU: 0 PID: 151 Comm: kworker/0:2 Tainted: G W 4.19.85 #48
[ 99.846022] Hardware name: MediaTek krane sku0 board (DT)
[ 99.851429] Workqueue: events sdio_irq_work
[ 99.855614] pstate: 60000005 (nZCv daif -PAN -UAO)
[ 99.860402] pc : skb_panic+0x64/0x68
[ 99.863974] lr : skb_panic+0x64/0x68
[ 99.867542] sp : ffffff8008833a90
[ 99.870850] x29: ffffff8008833ac0 x28: ffffffe52e337370
[ 99.876159] x27: ffffffe52e328a90 x26: 000000000000e0d0
[ 99.881469] x25: ffffffe52e336b60 x24: 000000000000deb5
[ 99.886779] x23: ffffffe52e340680 x22: ffffffe4efd47e00
[ 99.892088] x21: 000000000000deb5 x20: ffffffa516d85b4c
[ 99.897397] x19: ffffffa526928037 x18: 0000000000000000
[ 99.902706] x17: 000000000000003c x16: ffffffa5265b6c80
[ 99.908015] x15: 0000000000000006 x14: 3a76656420303436
[ 99.913325] x13: 0000000000029bf0 x12: 0000000000000000
[ 99.918634] x11: 0000000000000000 x10: 0000000000000000
[ 99.923943] x9 : a3b907e4b2783000 x8 : a3b907e4b2783000
[ 99.929253] x7 : 0000000000000000 x6 : ffffffa526f66d76
[ 99.934563] x5 : 0000000000000000 x4 : 0000000000000000
[ 99.939872] x3 : 000000000002a5ab x2 : ffffffe53feed918
[ 99.945182] x1 : ffffffe53fee4a08 x0 : 000000000000008e
[ 99.950491] Call trace:
[ 99.952937] skb_panic+0x64/0x68
[ 99.956165] skb_put+0x7c/0x84
[ 99.959224] ath10k_sdio_irq_handler+0x740/0xbb8 [ath10k_sdio]
[ 99.965055] process_sdio_pending_irqs+0x58/0x1a4
[ 99.969758] sdio_run_irqs+0x34/0x60
[ 99.973329] sdio_irq_work+0x1c/0x28
[ 99.974930] cros-ec-spi spi2.0: SPI transfer timed out
[ 99.976904] process_one_work+0x210/0x410
[ 99.976911] worker_thread+0x234/0x3dc
[ 99.976923] kthread+0x120/0x130
[ 99.982090] cros-ec-spi spi2.0: spi transfer failed: -110
[ 99.986054] ret_from_fork+0x10/0x18
[ 99.986063] Code: aa1403e2 2a1503e4 a90023e9 97e37d1a (d4210000)
[ 99.986068] ---[ end trace cb6d948c5a0fd6c7 ]---
[ 100.017250] Kernel panic - not syncing: Fatal exception
[ 100.018879] cros-ec-spi spi2.0: Command xfer error (err:-110)
[ 100.023659] SMP: stopping secondary CPUs
[ 100.023703] Kernel Offset: 0x251dc00000 from 0xffffff8008000000
[ 100.023707] CPU features: 0x0,2188200c
[ 100.023709] Memory Limit: none
The simulate fail of sdio is not a real sdio transter fail, it only
set an error status in mmc_should_fail_request after the transfer end,
actually the transfer is success, then sdio_io_rw_ext_helper will
return error status and stop transfer the left data. For example,
the really RX len is 286 bytes, then it will split to 2 blocks in
sdio_io_rw_ext_helper, one is 256 bytes, left is 30 bytes, if the
first 256 bytes get an error status by mmc_should_fail_request,then
the left 30 bytes will not read in this RX operation. Then when the
next RX arrive, the left 30 bytes will be considered as the header
of the read, the top 8 bytes will be considered as ath10k_htc_hdr,
but actually the 8 bytes is not the ath10k_htc_hdr, so the act_len
from this ath10k_htc_hdr is not correct, if it is a big value, such
as 57013, it will trigger skb_panic.
Drop the skb with invalid length will be reasonable.
This patch only effect sdio chips.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-12-31 17:27:06 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
skb_put_data(pkt->skb, htc_hdr, pkt->act_len);
|
|
|
|
pkt_offset += pkt->alloc_len;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
/* Free all packets that was not successfully fetched. */
|
2019-11-15 09:20:58 +02:00
|
|
|
for (i = 0; i < ar_sdio->n_rx_pkts; i++)
|
2017-04-26 12:18:00 +03:00
|
|
|
ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
ar_sdio->n_rx_pkts = 0;
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is the timeout for mailbox processing done in the sdio irq
|
|
|
|
* handler. The timeout is deliberately set quite high since SDIO dump logs
|
|
|
|
* over serial port can/will add a substantial overhead to the processing
|
|
|
|
* (if enabled).
|
|
|
|
*/
|
|
|
|
#define SDIO_MBOX_PROCESSING_TIMEOUT_HZ (20 * HZ)
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar,
|
|
|
|
u32 msg_lookahead, bool *done)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
u32 lookaheads[ATH10K_SDIO_MAX_RX_MSGS];
|
|
|
|
int n_lookaheads = 1;
|
|
|
|
unsigned long timeout;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
*done = true;
|
|
|
|
|
|
|
|
/* Copy the lookahead obtained from the HTC register table into our
|
|
|
|
* temp array as a start value.
|
|
|
|
*/
|
|
|
|
lookaheads[0] = msg_lookahead;
|
|
|
|
|
|
|
|
timeout = jiffies + SDIO_MBOX_PROCESSING_TIMEOUT_HZ;
|
2017-07-28 15:15:40 +03:00
|
|
|
do {
|
2017-04-26 12:18:00 +03:00
|
|
|
/* Try to allocate as many HTC RX packets indicated by
|
|
|
|
* n_lookaheads.
|
|
|
|
*/
|
|
|
|
ret = ath10k_sdio_mbox_rx_alloc(ar, lookaheads,
|
|
|
|
n_lookaheads);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (ar_sdio->n_rx_pkts >= 2)
|
|
|
|
/* A recv bundle was detected, force IRQ status
|
|
|
|
* re-check again.
|
|
|
|
*/
|
|
|
|
*done = false;
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
if (ar_sdio->n_rx_pkts > 1)
|
|
|
|
ret = ath10k_sdio_mbox_rx_fetch_bundle(ar);
|
|
|
|
else
|
|
|
|
ret = ath10k_sdio_mbox_rx_fetch(ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
/* Process fetched packets. This will potentially update
|
|
|
|
* n_lookaheads depending on if the packets contain lookahead
|
|
|
|
* reports.
|
|
|
|
*/
|
|
|
|
n_lookaheads = 0;
|
|
|
|
ret = ath10k_sdio_mbox_rx_process_packets(ar,
|
|
|
|
lookaheads,
|
|
|
|
&n_lookaheads);
|
|
|
|
|
|
|
|
if (!n_lookaheads || ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* For SYNCH processing, if we get here, we are running
|
|
|
|
* through the loop again due to updated lookaheads. Set
|
|
|
|
* flag that we should re-check IRQ status registers again
|
|
|
|
* before leaving IRQ processing, this can net better
|
|
|
|
* performance in high throughput situations.
|
|
|
|
*/
|
|
|
|
*done = false;
|
2017-07-28 15:15:40 +03:00
|
|
|
} while (time_before(jiffies, timeout));
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
if (ret && (ret != -ECANCELED))
|
|
|
|
ath10k_warn(ar, "failed to get pending recv messages: %d\n",
|
|
|
|
ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_proc_dbg_intr(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* TODO: Add firmware crash handling */
|
|
|
|
ath10k_warn(ar, "firmware crashed\n");
|
|
|
|
|
|
|
|
/* read counter to clear the interrupt, the debug error interrupt is
|
|
|
|
* counter 0.
|
|
|
|
*/
|
|
|
|
ret = ath10k_sdio_read32(ar, MBOX_COUNT_DEC_ADDRESS, &val);
|
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar, "failed to clear debug interrupt: %d\n", ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_proc_counter_intr(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
u8 counter_int_status;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&irq_data->mtx);
|
|
|
|
counter_int_status = irq_data->irq_proc_reg->counter_int_status &
|
|
|
|
irq_data->irq_en_reg->cntr_int_status_en;
|
|
|
|
|
|
|
|
/* NOTE: other modules like GMBOX may use the counter interrupt for
|
|
|
|
* credit flow control on other counters, we only need to check for
|
|
|
|
* the debug assertion counter interrupt.
|
|
|
|
*/
|
|
|
|
if (counter_int_status & ATH10K_SDIO_TARGET_DEBUG_INTR_MASK)
|
|
|
|
ret = ath10k_sdio_mbox_proc_dbg_intr(ar);
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
mutex_unlock(&irq_data->mtx);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_proc_err_intr(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
u8 error_int_status;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio error interrupt\n");
|
|
|
|
|
|
|
|
error_int_status = irq_data->irq_proc_reg->error_int_status & 0x0F;
|
|
|
|
if (!error_int_status) {
|
|
|
|
ath10k_warn(ar, "invalid error interrupt status: 0x%x\n",
|
|
|
|
error_int_status);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio error_int_status 0x%x\n", error_int_status);
|
|
|
|
|
|
|
|
if (FIELD_GET(MBOX_ERROR_INT_STATUS_WAKEUP_MASK,
|
|
|
|
error_int_status))
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio interrupt error wakeup\n");
|
|
|
|
|
|
|
|
if (FIELD_GET(MBOX_ERROR_INT_STATUS_RX_UNDERFLOW_MASK,
|
|
|
|
error_int_status))
|
|
|
|
ath10k_warn(ar, "rx underflow interrupt error\n");
|
|
|
|
|
|
|
|
if (FIELD_GET(MBOX_ERROR_INT_STATUS_TX_OVERFLOW_MASK,
|
|
|
|
error_int_status))
|
|
|
|
ath10k_warn(ar, "tx overflow interrupt error\n");
|
|
|
|
|
|
|
|
/* Clear the interrupt */
|
|
|
|
irq_data->irq_proc_reg->error_int_status &= ~error_int_status;
|
|
|
|
|
|
|
|
/* set W1C value to clear the interrupt, this hits the register first */
|
|
|
|
ret = ath10k_sdio_writesb32(ar, MBOX_ERROR_INT_STATUS_ADDRESS,
|
|
|
|
error_int_status);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to write to error int status address: %d\n",
|
|
|
|
ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
u8 cpu_int_status;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&irq_data->mtx);
|
|
|
|
cpu_int_status = irq_data->irq_proc_reg->cpu_int_status &
|
|
|
|
irq_data->irq_en_reg->cpu_int_status_en;
|
|
|
|
if (!cpu_int_status) {
|
|
|
|
ath10k_warn(ar, "CPU interrupt status is zero\n");
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the interrupt */
|
|
|
|
irq_data->irq_proc_reg->cpu_int_status &= ~cpu_int_status;
|
|
|
|
|
|
|
|
/* Set up the register transfer buffer to hit the register 4 times,
|
|
|
|
* this is done to make the access 4-byte aligned to mitigate issues
|
|
|
|
* with host bus interconnects that restrict bus transfer lengths to
|
|
|
|
* be a multiple of 4-bytes.
|
|
|
|
*
|
|
|
|
* Set W1C value to clear the interrupt, this hits the register first.
|
|
|
|
*/
|
|
|
|
ret = ath10k_sdio_writesb32(ar, MBOX_CPU_INT_STATUS_ADDRESS,
|
|
|
|
cpu_int_status);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to write to cpu interrupt status address: %d\n",
|
|
|
|
ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&irq_data->mtx);
|
2020-08-18 17:12:02 +03:00
|
|
|
if (cpu_int_status & MBOX_CPU_STATUS_ENABLE_ASSERT_MASK)
|
|
|
|
ath10k_sdio_fw_crashed_dump(ar);
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_read_int_status(struct ath10k *ar,
|
|
|
|
u8 *host_int_status,
|
|
|
|
u32 *lookahead)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
struct ath10k_sdio_irq_proc_regs *irq_proc_reg = irq_data->irq_proc_reg;
|
|
|
|
struct ath10k_sdio_irq_enable_regs *irq_en_reg = irq_data->irq_en_reg;
|
|
|
|
u8 htc_mbox = FIELD_PREP(ATH10K_HTC_MAILBOX_MASK, 1);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&irq_data->mtx);
|
|
|
|
|
|
|
|
*lookahead = 0;
|
|
|
|
*host_int_status = 0;
|
|
|
|
|
|
|
|
/* int_status_en is supposed to be non zero, otherwise interrupts
|
|
|
|
* shouldn't be enabled. There is however a short time frame during
|
|
|
|
* initialization between the irq register and int_status_en init
|
|
|
|
* where this can happen.
|
|
|
|
* We silently ignore this condition.
|
|
|
|
*/
|
|
|
|
if (!irq_en_reg->int_status_en) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the first sizeof(struct ath10k_irq_proc_registers)
|
|
|
|
* bytes of the HTC register table. This
|
|
|
|
* will yield us the value of different int status
|
|
|
|
* registers and the lookahead registers.
|
|
|
|
*/
|
|
|
|
ret = ath10k_sdio_read(ar, MBOX_HOST_INT_STATUS_ADDRESS,
|
|
|
|
irq_proc_reg, sizeof(*irq_proc_reg));
|
2020-02-14 11:40:07 +08:00
|
|
|
if (ret) {
|
2020-09-08 04:13:06 +00:00
|
|
|
ath10k_core_start_recovery(ar);
|
2020-02-14 11:40:07 +08:00
|
|
|
ath10k_warn(ar, "read int status fail, start recovery\n");
|
2017-04-26 12:18:00 +03:00
|
|
|
goto out;
|
2020-02-14 11:40:07 +08:00
|
|
|
}
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
/* Update only those registers that are enabled */
|
|
|
|
*host_int_status = irq_proc_reg->host_int_status &
|
|
|
|
irq_en_reg->int_status_en;
|
|
|
|
|
|
|
|
/* Look at mbox status */
|
|
|
|
if (!(*host_int_status & htc_mbox)) {
|
|
|
|
*lookahead = 0;
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mask out pending mbox value, we use look ahead as
|
|
|
|
* the real flag for mbox processing.
|
|
|
|
*/
|
|
|
|
*host_int_status &= ~htc_mbox;
|
|
|
|
if (irq_proc_reg->rx_lookahead_valid & htc_mbox) {
|
|
|
|
*lookahead = le32_to_cpu(
|
|
|
|
irq_proc_reg->rx_lookahead[ATH10K_HTC_MAILBOX]);
|
|
|
|
if (!*lookahead)
|
|
|
|
ath10k_warn(ar, "sdio mbox lookahead is zero\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&irq_data->mtx);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_mbox_proc_pending_irqs(struct ath10k *ar,
|
|
|
|
bool *done)
|
|
|
|
{
|
|
|
|
u8 host_int_status;
|
|
|
|
u32 lookahead;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* NOTE: HIF implementation guarantees that the context of this
|
|
|
|
* call allows us to perform SYNCHRONOUS I/O, that is we can block,
|
|
|
|
* sleep or call any API that can block or switch thread/task
|
|
|
|
* contexts. This is a fully schedulable context.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret = ath10k_sdio_mbox_read_int_status(ar,
|
|
|
|
&host_int_status,
|
|
|
|
&lookahead);
|
|
|
|
if (ret) {
|
|
|
|
*done = true;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!host_int_status && !lookahead) {
|
|
|
|
ret = 0;
|
|
|
|
*done = true;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lookahead) {
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio pending mailbox msg lookahead 0x%08x\n",
|
|
|
|
lookahead);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_mbox_rxmsg_pending_handler(ar,
|
|
|
|
lookahead,
|
|
|
|
done);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now, handle the rest of the interrupts */
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio host_int_status 0x%x\n", host_int_status);
|
|
|
|
|
|
|
|
if (FIELD_GET(MBOX_HOST_INT_STATUS_CPU_MASK, host_int_status)) {
|
|
|
|
/* CPU Interrupt */
|
|
|
|
ret = ath10k_sdio_mbox_proc_cpu_intr(ar);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FIELD_GET(MBOX_HOST_INT_STATUS_ERROR_MASK, host_int_status)) {
|
|
|
|
/* Error Interrupt */
|
|
|
|
ret = ath10k_sdio_mbox_proc_err_intr(ar);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FIELD_GET(MBOX_HOST_INT_STATUS_COUNTER_MASK, host_int_status))
|
|
|
|
/* Counter Interrupt */
|
|
|
|
ret = ath10k_sdio_mbox_proc_counter_intr(ar);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
/* An optimization to bypass reading the IRQ status registers
|
2022-09-09 07:53:00 -07:00
|
|
|
* unnecessarily which can re-wake the target, if upper layers
|
2017-04-26 12:18:00 +03:00
|
|
|
* determine that we are in a low-throughput mode, we can rely on
|
|
|
|
* taking another interrupt rather than re-checking the status
|
|
|
|
* registers which can re-wake the target.
|
|
|
|
*
|
|
|
|
* NOTE : for host interfaces that makes use of detecting pending
|
|
|
|
* mbox messages at hif can not use this optimization due to
|
|
|
|
* possible side effects, SPI requires the host to drain all
|
|
|
|
* messages from the mailbox before exiting the ISR routine.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio pending irqs done %d status %d",
|
|
|
|
*done, ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
|
|
|
|
u16 device = ar_sdio->func->device, dev_id_base, dev_id_chiprev;
|
|
|
|
|
|
|
|
mbox_info->htc_addr = ATH10K_HIF_MBOX_BASE_ADDR;
|
|
|
|
mbox_info->block_size = ATH10K_HIF_MBOX_BLOCK_SIZE;
|
|
|
|
mbox_info->block_mask = ATH10K_HIF_MBOX_BLOCK_SIZE - 1;
|
|
|
|
mbox_info->gmbox_addr = ATH10K_HIF_GMBOX_BASE_ADDR;
|
|
|
|
mbox_info->gmbox_sz = ATH10K_HIF_GMBOX_WIDTH;
|
|
|
|
|
|
|
|
mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
|
|
|
|
|
2020-05-22 16:44:09 +02:00
|
|
|
dev_id_base = (device & 0x0F00);
|
|
|
|
dev_id_chiprev = (device & 0x00FF);
|
2017-04-26 12:18:00 +03:00
|
|
|
switch (dev_id_base) {
|
2020-05-22 16:44:09 +02:00
|
|
|
case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
|
2017-04-26 12:18:00 +03:00
|
|
|
if (dev_id_chiprev < 4)
|
|
|
|
mbox_info->ext_info[0].htc_ext_sz =
|
|
|
|
ATH10K_HIF_MBOX0_EXT_WIDTH;
|
|
|
|
else
|
|
|
|
/* from QCA6174 2.0(0x504), the width has been extended
|
|
|
|
* to 56K
|
|
|
|
*/
|
|
|
|
mbox_info->ext_info[0].htc_ext_sz =
|
|
|
|
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
|
|
|
break;
|
2020-05-22 16:44:09 +02:00
|
|
|
case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
|
2017-04-26 12:18:00 +03:00
|
|
|
mbox_info->ext_info[0].htc_ext_sz =
|
|
|
|
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mbox_info->ext_info[0].htc_ext_sz =
|
|
|
|
ATH10K_HIF_MBOX0_EXT_WIDTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
mbox_info->ext_info[1].htc_ext_addr =
|
|
|
|
mbox_info->ext_info[0].htc_ext_addr +
|
|
|
|
mbox_info->ext_info[0].htc_ext_sz +
|
|
|
|
ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE;
|
|
|
|
mbox_info->ext_info[1].htc_ext_sz = ATH10K_HIF_MBOX1_EXT_WIDTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BMI functions */
|
|
|
|
|
|
|
|
static int ath10k_sdio_bmi_credits(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
u32 addr, cmd_credits;
|
|
|
|
unsigned long timeout;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Read the counter register to get the command credits */
|
|
|
|
addr = MBOX_COUNT_DEC_ADDRESS + ATH10K_HIF_MBOX_NUM_MAX * 4;
|
|
|
|
timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
|
|
|
|
cmd_credits = 0;
|
|
|
|
|
|
|
|
while (time_before(jiffies, timeout) && !cmd_credits) {
|
|
|
|
/* Hit the credit counter with a 4-byte access, the first byte
|
|
|
|
* read will hit the counter and cause a decrement, while the
|
|
|
|
* remaining 3 bytes has no effect. The rationale behind this
|
|
|
|
* is to make all HIF accesses 4-byte aligned.
|
|
|
|
*/
|
|
|
|
ret = ath10k_sdio_read32(ar, addr, &cmd_credits);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar,
|
|
|
|
"unable to decrement the command credit count register: %d\n",
|
|
|
|
ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The counter is only 8 bits.
|
|
|
|
* Ignore anything in the upper 3 bytes
|
|
|
|
*/
|
|
|
|
cmd_credits &= 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cmd_credits) {
|
|
|
|
ath10k_warn(ar, "bmi communication timeout\n");
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_bmi_get_rx_lookahead(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
unsigned long timeout;
|
|
|
|
u32 rx_word;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
|
|
|
|
rx_word = 0;
|
|
|
|
|
|
|
|
while ((time_before(jiffies, timeout)) && !rx_word) {
|
|
|
|
ret = ath10k_sdio_read32(ar,
|
|
|
|
MBOX_HOST_INT_STATUS_ADDRESS,
|
|
|
|
&rx_word);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to read RX_LOOKAHEAD_VALID: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* all we really want is one bit */
|
|
|
|
rx_word &= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rx_word) {
|
|
|
|
ath10k_warn(ar, "bmi_recv_buf FIFO empty\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_bmi_exchange_msg(struct ath10k *ar,
|
|
|
|
void *req, u32 req_len,
|
|
|
|
void *resp, u32 *resp_len)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
u32 addr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (req) {
|
|
|
|
ret = ath10k_sdio_bmi_credits(ar);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
addr = ar_sdio->mbox_info.htc_addr;
|
|
|
|
|
|
|
|
memcpy(ar_sdio->bmi_buf, req, req_len);
|
|
|
|
ret = ath10k_sdio_write(ar, addr, ar_sdio->bmi_buf, req_len);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar,
|
|
|
|
"unable to send the bmi data to the device: %d\n",
|
|
|
|
ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!resp || !resp_len)
|
|
|
|
/* No response expected */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* During normal bootup, small reads may be required.
|
|
|
|
* Rather than issue an HIF Read and then wait as the Target
|
|
|
|
* adds successive bytes to the FIFO, we wait here until
|
|
|
|
* we know that response data is available.
|
|
|
|
*
|
|
|
|
* This allows us to cleanly timeout on an unexpected
|
|
|
|
* Target failure rather than risk problems at the HIF level.
|
|
|
|
* In particular, this avoids SDIO timeouts and possibly garbage
|
|
|
|
* data on some host controllers. And on an interconnect
|
|
|
|
* such as Compact Flash (as well as some SDIO masters) which
|
|
|
|
* does not provide any indication on data timeout, it avoids
|
|
|
|
* a potential hang or garbage response.
|
|
|
|
*
|
|
|
|
* Synchronization is more difficult for reads larger than the
|
|
|
|
* size of the MBOX FIFO (128B), because the Target is unable
|
|
|
|
* to push the 129th byte of data until AFTER the Host posts an
|
|
|
|
* HIF Read and removes some FIFO data. So for large reads the
|
|
|
|
* Host proceeds to post an HIF Read BEFORE all the data is
|
|
|
|
* actually available to read. Fortunately, large BMI reads do
|
|
|
|
* not occur in practice -- they're supported for debug/development.
|
|
|
|
*
|
|
|
|
* So Host/Target BMI synchronization is divided into these cases:
|
|
|
|
* CASE 1: length < 4
|
|
|
|
* Should not happen
|
|
|
|
*
|
|
|
|
* CASE 2: 4 <= length <= 128
|
|
|
|
* Wait for first 4 bytes to be in FIFO
|
|
|
|
* If CONSERVATIVE_BMI_READ is enabled, also wait for
|
|
|
|
* a BMI command credit, which indicates that the ENTIRE
|
2020-10-27 14:38:07 +02:00
|
|
|
* response is available in the FIFO
|
2017-04-26 12:18:00 +03:00
|
|
|
*
|
|
|
|
* CASE 3: length > 128
|
|
|
|
* Wait for the first 4 bytes to be in FIFO
|
|
|
|
*
|
|
|
|
* For most uses, a small timeout should be sufficient and we will
|
|
|
|
* usually see a response quickly; but there may be some unusual
|
|
|
|
* (debug) cases of BMI_EXECUTE where we want an larger timeout.
|
|
|
|
* For now, we use an unbounded busy loop while waiting for
|
|
|
|
* BMI_EXECUTE.
|
|
|
|
*
|
|
|
|
* If BMI_EXECUTE ever needs to support longer-latency execution,
|
|
|
|
* especially in production, this code needs to be enhanced to sleep
|
|
|
|
* and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently
|
|
|
|
* a function of Host processor speed.
|
|
|
|
*/
|
|
|
|
ret = ath10k_sdio_bmi_get_rx_lookahead(ar);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* We always read from the start of the mbox address */
|
|
|
|
addr = ar_sdio->mbox_info.htc_addr;
|
|
|
|
ret = ath10k_sdio_read(ar, addr, ar_sdio->bmi_buf, *resp_len);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar,
|
|
|
|
"unable to read the bmi data from the device: %d\n",
|
|
|
|
ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(resp, ar_sdio->bmi_buf, *resp_len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sdio async handling functions */
|
|
|
|
|
|
|
|
static struct ath10k_sdio_bus_request
|
|
|
|
*ath10k_sdio_alloc_busreq(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_bus_request *bus_req;
|
|
|
|
|
|
|
|
spin_lock_bh(&ar_sdio->lock);
|
|
|
|
|
|
|
|
if (list_empty(&ar_sdio->bus_req_freeq)) {
|
|
|
|
bus_req = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
|
|
|
|
struct ath10k_sdio_bus_request, list);
|
|
|
|
list_del(&bus_req->list);
|
|
|
|
|
|
|
|
out:
|
|
|
|
spin_unlock_bh(&ar_sdio->lock);
|
|
|
|
return bus_req;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_free_bus_req(struct ath10k *ar,
|
|
|
|
struct ath10k_sdio_bus_request *bus_req)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
|
|
|
|
memset(bus_req, 0, sizeof(*bus_req));
|
|
|
|
|
|
|
|
spin_lock_bh(&ar_sdio->lock);
|
|
|
|
list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
|
|
|
|
spin_unlock_bh(&ar_sdio->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __ath10k_sdio_write_async(struct ath10k *ar,
|
|
|
|
struct ath10k_sdio_bus_request *req)
|
|
|
|
{
|
|
|
|
struct ath10k_htc_ep *ep;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
skb = req->skb;
|
|
|
|
ret = ath10k_sdio_write(ar, req->address, skb->data, skb->len);
|
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar, "failed to write skb to 0x%x asynchronously: %d",
|
|
|
|
req->address, ret);
|
|
|
|
|
|
|
|
if (req->htc_msg) {
|
|
|
|
ep = &ar->htc.endpoint[req->eid];
|
|
|
|
ath10k_htc_notify_tx_completion(ep, skb);
|
|
|
|
} else if (req->comp) {
|
|
|
|
complete(req->comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_sdio_free_bus_req(ar, req);
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:21:03 +02:00
|
|
|
/* To improve throughput use workqueue to deliver packets to HTC layer,
|
|
|
|
* this way SDIO bus is utilised much better.
|
|
|
|
*/
|
|
|
|
static void ath10k_rx_indication_async_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio,
|
|
|
|
async_work_rx);
|
|
|
|
struct ath10k *ar = ar_sdio->ar;
|
|
|
|
struct ath10k_htc_ep *ep;
|
|
|
|
struct ath10k_skb_rxcb *cb;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
skb = skb_dequeue(&ar_sdio->rx_head);
|
|
|
|
if (!skb)
|
|
|
|
break;
|
|
|
|
cb = ATH10K_SKB_RXCB(skb);
|
|
|
|
ep = &ar->htc.endpoint[cb->eid];
|
|
|
|
ep->ep_ops.ep_rx_complete(ar, skb);
|
|
|
|
}
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
|
ath10k: sdio: Add missing BH locking around napi_schdule()
On a i.MX-based board with a QCA9377 Wifi chip, the following errors
are seen after launching the 'hostapd' application:
hostapd /etc/wifi.conf
Configuration file: /etc/wifi.conf
wlan0: interface state UNINITIALIZED->COUNTRY_UPDATE
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
Using interface wlan0 with hwaddr 00:1f:7b:31:04:a0 and ssid "thessid"
IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
wlan0: interface state COUNTRY_UPDATE->ENABLED
wlan0: AP-ENABLED
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
...
Fix this problem by adding the BH locking around napi-schedule(),
in the same way it was done in commit e63052a5dd3c ("mlx5e: add
add missing BH locking around napi_schdule()").
Its commit log provides the following explanation:
"It's not correct to call napi_schedule() in pure process
context. Because we use __raise_softirq_irqoff() we require
callers to be in a context which will eventually lead to
softirq handling (hardirq, bh disabled, etc.).
With code as is users will see:
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
"
Fixes: cfee8793a74d ("ath10k: enable napi on RX path for sdio")
Signed-off-by: Fabio Estevam <festevam@denx.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210824144339.2796122-1-festevam@denx.de
2021-09-28 14:00:47 +03:00
|
|
|
if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) {
|
|
|
|
local_bh_disable();
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
napi_schedule(&ar->napi);
|
ath10k: sdio: Add missing BH locking around napi_schdule()
On a i.MX-based board with a QCA9377 Wifi chip, the following errors
are seen after launching the 'hostapd' application:
hostapd /etc/wifi.conf
Configuration file: /etc/wifi.conf
wlan0: interface state UNINITIALIZED->COUNTRY_UPDATE
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
Using interface wlan0 with hwaddr 00:1f:7b:31:04:a0 and ssid "thessid"
IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
wlan0: interface state COUNTRY_UPDATE->ENABLED
wlan0: AP-ENABLED
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
...
Fix this problem by adding the BH locking around napi-schedule(),
in the same way it was done in commit e63052a5dd3c ("mlx5e: add
add missing BH locking around napi_schdule()").
Its commit log provides the following explanation:
"It's not correct to call napi_schedule() in pure process
context. Because we use __raise_softirq_irqoff() we require
callers to be in a context which will eventually lead to
softirq handling (hardirq, bh disabled, etc.).
With code as is users will see:
NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #08!!!
"
Fixes: cfee8793a74d ("ath10k: enable napi on RX path for sdio")
Signed-off-by: Fabio Estevam <festevam@denx.de>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210824144339.2796122-1-festevam@denx.de
2021-09-28 14:00:47 +03:00
|
|
|
local_bh_enable();
|
|
|
|
}
|
2019-11-15 09:21:03 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 14:50:57 +03:00
|
|
|
static int ath10k_sdio_read_rtc_state(struct ath10k_sdio *ar_sdio, unsigned char *state)
|
|
|
|
{
|
|
|
|
struct ath10k *ar = ar_sdio->ar;
|
|
|
|
unsigned char rtc_state = 0;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
rtc_state = sdio_f0_readb(ar_sdio->func, ATH10K_CIS_RTC_STATE_ADDR, &ret);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read rtc state: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
*state = rtc_state & 0x3;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
static int ath10k_sdio_set_mbox_sleep(struct ath10k *ar, bool enable_sleep)
|
2020-04-16 14:50:57 +03:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
u32 val;
|
|
|
|
int retry = ATH10K_CIS_READ_RETRY, ret = 0;
|
|
|
|
unsigned char rtc_state = 0;
|
|
|
|
|
|
|
|
sdio_claim_host(ar_sdio->func);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_read32(ar, ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL, &val);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read fifo/chip control register: %d\n",
|
|
|
|
ret);
|
|
|
|
goto release;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enable_sleep) {
|
|
|
|
val &= ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF;
|
|
|
|
ar_sdio->mbox_state = SDIO_MBOX_SLEEP_STATE;
|
|
|
|
} else {
|
|
|
|
val |= ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_ON;
|
|
|
|
ar_sdio->mbox_state = SDIO_MBOX_AWAKE_STATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ath10k_sdio_write32(ar, ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL, val);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to write to FIFO_TIMEOUT_AND_CHIP_CONTROL: %d",
|
|
|
|
ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!enable_sleep) {
|
|
|
|
do {
|
|
|
|
udelay(ATH10K_CIS_READ_WAIT_4_RTC_CYCLE_IN_US);
|
|
|
|
ret = ath10k_sdio_read_rtc_state(ar_sdio, &rtc_state);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to disable mbox sleep: %d", ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio read rtc state: %d\n",
|
|
|
|
rtc_state);
|
|
|
|
|
|
|
|
if (rtc_state == ATH10K_CIS_RTC_STATE_ON)
|
|
|
|
break;
|
|
|
|
|
|
|
|
udelay(ATH10K_CIS_XTAL_SETTLE_DURATION_IN_US);
|
|
|
|
retry--;
|
|
|
|
} while (retry > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
release:
|
|
|
|
sdio_release_host(ar_sdio->func);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_sleep_timer_handler(struct timer_list *t)
|
|
|
|
{
|
2025-05-09 07:51:14 +02:00
|
|
|
struct ath10k_sdio *ar_sdio = timer_container_of(ar_sdio, t,
|
|
|
|
sleep_timer);
|
2020-04-16 14:50:57 +03:00
|
|
|
|
|
|
|
ar_sdio->mbox_state = SDIO_MBOX_REQUEST_TO_SLEEP_STATE;
|
|
|
|
queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
|
|
|
|
}
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
static void ath10k_sdio_write_async_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio,
|
|
|
|
wr_async_work);
|
|
|
|
struct ath10k *ar = ar_sdio->ar;
|
|
|
|
struct ath10k_sdio_bus_request *req, *tmp_req;
|
2020-04-16 14:50:57 +03:00
|
|
|
struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
spin_lock_bh(&ar_sdio->wr_async_lock);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
|
|
|
|
list_del(&req->list);
|
|
|
|
spin_unlock_bh(&ar_sdio->wr_async_lock);
|
2020-04-16 14:50:57 +03:00
|
|
|
|
|
|
|
if (req->address >= mbox_info->htc_addr &&
|
|
|
|
ar_sdio->mbox_state == SDIO_MBOX_SLEEP_STATE) {
|
2020-04-16 14:50:58 +03:00
|
|
|
ath10k_sdio_set_mbox_sleep(ar, false);
|
2020-04-16 14:50:57 +03:00
|
|
|
mod_timer(&ar_sdio->sleep_timer, jiffies +
|
|
|
|
msecs_to_jiffies(ATH10K_MIN_SLEEP_INACTIVITY_TIME_MS));
|
|
|
|
}
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
__ath10k_sdio_write_async(ar, req);
|
|
|
|
spin_lock_bh(&ar_sdio->wr_async_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_bh(&ar_sdio->wr_async_lock);
|
2020-04-16 14:50:57 +03:00
|
|
|
|
|
|
|
if (ar_sdio->mbox_state == SDIO_MBOX_REQUEST_TO_SLEEP_STATE)
|
2020-04-16 14:50:58 +03:00
|
|
|
ath10k_sdio_set_mbox_sleep(ar, true);
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_prep_async_req(struct ath10k *ar, u32 addr,
|
|
|
|
struct sk_buff *skb,
|
|
|
|
struct completion *comp,
|
|
|
|
bool htc_msg, enum ath10k_htc_ep_id eid)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_bus_request *bus_req;
|
|
|
|
|
|
|
|
/* Allocate a bus request for the message and queue it on the
|
|
|
|
* SDIO workqueue.
|
|
|
|
*/
|
|
|
|
bus_req = ath10k_sdio_alloc_busreq(ar);
|
|
|
|
if (!bus_req) {
|
|
|
|
ath10k_warn(ar,
|
|
|
|
"unable to allocate bus request for async request\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_req->skb = skb;
|
|
|
|
bus_req->eid = eid;
|
|
|
|
bus_req->address = addr;
|
|
|
|
bus_req->htc_msg = htc_msg;
|
|
|
|
bus_req->comp = comp;
|
|
|
|
|
|
|
|
spin_lock_bh(&ar_sdio->wr_async_lock);
|
|
|
|
list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
|
|
|
|
spin_unlock_bh(&ar_sdio->wr_async_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IRQ handler */
|
|
|
|
|
|
|
|
static void ath10k_sdio_irq_handler(struct sdio_func *func)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
|
|
|
|
struct ath10k *ar = ar_sdio->ar;
|
|
|
|
unsigned long timeout;
|
|
|
|
bool done = false;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Release the host during interrupts so we can pick it back up when
|
|
|
|
* we process commands.
|
|
|
|
*/
|
|
|
|
sdio_release_host(ar_sdio->func);
|
|
|
|
|
|
|
|
timeout = jiffies + ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ;
|
2017-07-28 15:15:40 +03:00
|
|
|
do {
|
2017-04-26 12:18:00 +03:00
|
|
|
ret = ath10k_sdio_mbox_proc_pending_irqs(ar, &done);
|
|
|
|
if (ret)
|
|
|
|
break;
|
2017-07-28 15:15:40 +03:00
|
|
|
} while (time_before(jiffies, timeout) && !done);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
ath10k: transmit queued frames after processing rx packets
When running iperf on ath10k SDIO, TX can stop working:
iperf -c 192.168.1.1 -i 1 -t 20 -w 10K
[ 3] 0.0- 1.0 sec 2.00 MBytes 16.8 Mbits/sec
[ 3] 1.0- 2.0 sec 3.12 MBytes 26.2 Mbits/sec
[ 3] 2.0- 3.0 sec 3.25 MBytes 27.3 Mbits/sec
[ 3] 3.0- 4.0 sec 655 KBytes 5.36 Mbits/sec
[ 3] 4.0- 5.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 5.0- 6.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 6.0- 7.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 7.0- 8.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 8.0- 9.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 9.0-10.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 0.0-10.3 sec 9.01 MBytes 7.32 Mbits/sec
There are frames in the ieee80211_txq and there are frames that have
been removed from from this queue, but haven't yet been sent on the wire
(num_pending_tx).
When num_pending_tx reaches max_num_pending_tx, we will stop the queues
by calling ieee80211_stop_queues().
As frames that have previously been sent for transmission
(num_pending_tx) are completed, we will decrease num_pending_tx and wake
the queues by calling ieee80211_wake_queue(). ieee80211_wake_queue()
does not call wake_tx_queue, so we might still have frames in the
queue at this point.
While the queues were stopped, the socket buffer might have filled up,
and in order for user space to write more, we need to free the frames
in the queue, since they are accounted to the socket. In order to free
them, we first need to transmit them.
This problem cannot be reproduced on low-latency devices, e.g. pci,
since they call ath10k_mac_tx_push_pending() from
ath10k_htt_txrx_compl_task(). ath10k_htt_txrx_compl_task() is not called
on high-latency devices.
Fix the problem by calling ath10k_mac_tx_push_pending(), after
processing rx packets, just like for low-latency devices, also in the
SDIO case. Since we are calling ath10k_mac_tx_push_pending() directly,
we also need to export it.
Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2018-06-18 17:00:49 +03:00
|
|
|
ath10k_mac_tx_push_pending(ar);
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
sdio_claim_host(ar_sdio->func);
|
|
|
|
|
|
|
|
if (ret && ret != -ECANCELED)
|
|
|
|
ath10k_warn(ar, "failed to process pending SDIO interrupts: %d\n",
|
|
|
|
ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sdio HIF functions */
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
static int ath10k_sdio_disable_intrs(struct ath10k *ar)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&irq_data->mtx);
|
|
|
|
|
|
|
|
memset(regs, 0, sizeof(*regs));
|
|
|
|
ret = ath10k_sdio_write(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
|
|
|
|
®s->int_status_en, sizeof(*regs));
|
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar, "unable to disable sdio interrupts: %d\n", ret);
|
|
|
|
|
|
|
|
mutex_unlock(&irq_data->mtx);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-08 15:50:10 +02:00
|
|
|
static int ath10k_sdio_hif_power_up(struct ath10k *ar,
|
|
|
|
enum ath10k_firmware_mode fw_mode)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct sdio_func *func = ar_sdio->func;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!ar_sdio->is_disabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power on\n");
|
|
|
|
|
2019-02-25 11:46:01 +02:00
|
|
|
ret = ath10k_sdio_config(ar);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_err(ar, "failed to config sdio: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
sdio_claim_host(func);
|
|
|
|
|
|
|
|
ret = sdio_enable_func(func);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to enable sdio function: %d)\n", ret);
|
|
|
|
sdio_release_host(func);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
sdio_release_host(func);
|
|
|
|
|
|
|
|
/* Wait for hardware to initialise. It should take a lot less than
|
|
|
|
* 20 ms but let's be conservative here.
|
|
|
|
*/
|
|
|
|
msleep(20);
|
|
|
|
|
|
|
|
ar_sdio->is_disabled = false;
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_disable_intrs(ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_hif_power_down(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (ar_sdio->is_disabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n");
|
|
|
|
|
2025-04-05 10:17:26 +02:00
|
|
|
timer_delete_sync(&ar_sdio->sleep_timer);
|
2020-04-16 14:50:58 +03:00
|
|
|
ath10k_sdio_set_mbox_sleep(ar, true);
|
2020-04-16 14:50:57 +03:00
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
/* Disable the card */
|
|
|
|
sdio_claim_host(ar_sdio->func);
|
2019-02-25 11:46:01 +02:00
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
ret = sdio_disable_func(ar_sdio->func);
|
2019-02-25 11:46:01 +02:00
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to disable sdio function: %d\n", ret);
|
|
|
|
sdio_release_host(ar_sdio->func);
|
|
|
|
return;
|
|
|
|
}
|
2017-04-26 12:18:00 +03:00
|
|
|
|
2022-04-08 10:00:42 +02:00
|
|
|
ret = mmc_hw_reset(ar_sdio->func->card);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret)
|
2019-02-25 11:46:01 +02:00
|
|
|
ath10k_warn(ar, "unable to reset sdio: %d\n", ret);
|
|
|
|
|
|
|
|
sdio_release_host(ar_sdio->func);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
ar_sdio->is_disabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
|
|
|
|
struct ath10k_hif_sg_item *items, int n_items)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
enum ath10k_htc_ep_id eid;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
eid = pipe_id_to_eid(pipe_id);
|
|
|
|
|
|
|
|
for (i = 0; i < n_items; i++) {
|
|
|
|
size_t padded_len;
|
|
|
|
u32 address;
|
|
|
|
|
|
|
|
skb = items[i].transfer_context;
|
|
|
|
padded_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio,
|
|
|
|
skb->len);
|
|
|
|
skb_trim(skb, padded_len);
|
|
|
|
|
|
|
|
/* Write TX data to the end of the mbox address space */
|
|
|
|
address = ar_sdio->mbox_addr[eid] + ar_sdio->mbox_size[eid] -
|
|
|
|
skb->len;
|
|
|
|
ret = ath10k_sdio_prep_async_req(ar, address, skb,
|
|
|
|
NULL, true, eid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
static int ath10k_sdio_enable_intrs(struct ath10k *ar)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(&irq_data->mtx);
|
|
|
|
|
|
|
|
/* Enable all but CPU interrupts */
|
|
|
|
regs->int_status_en = FIELD_PREP(MBOX_INT_STATUS_ENABLE_ERROR_MASK, 1) |
|
|
|
|
FIELD_PREP(MBOX_INT_STATUS_ENABLE_CPU_MASK, 1) |
|
|
|
|
FIELD_PREP(MBOX_INT_STATUS_ENABLE_COUNTER_MASK, 1);
|
|
|
|
|
|
|
|
/* NOTE: There are some cases where HIF can do detection of
|
|
|
|
* pending mbox messages which is disabled now.
|
|
|
|
*/
|
|
|
|
regs->int_status_en |=
|
|
|
|
FIELD_PREP(MBOX_INT_STATUS_ENABLE_MBOX_DATA_MASK, 1);
|
|
|
|
|
2019-05-22 14:32:56 +08:00
|
|
|
/* Set up the CPU Interrupt Status Register, enable CPU sourced interrupt #0
|
|
|
|
* #0 is used for report assertion from target
|
|
|
|
*/
|
|
|
|
regs->cpu_int_status_en = FIELD_PREP(MBOX_CPU_STATUS_ENABLE_ASSERT_MASK, 1);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
/* Set up the Error Interrupt status Register */
|
|
|
|
regs->err_int_status_en =
|
|
|
|
FIELD_PREP(MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK, 1) |
|
|
|
|
FIELD_PREP(MBOX_ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK, 1);
|
|
|
|
|
|
|
|
/* Enable Counter interrupt status register to get fatal errors for
|
|
|
|
* debugging.
|
|
|
|
*/
|
|
|
|
regs->cntr_int_status_en =
|
|
|
|
FIELD_PREP(MBOX_COUNTER_INT_STATUS_ENABLE_BIT_MASK,
|
|
|
|
ATH10K_SDIO_TARGET_DEBUG_INTR_MASK);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_write(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
|
|
|
|
®s->int_status_en, sizeof(*regs));
|
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar,
|
|
|
|
"failed to update mbox interrupt status register : %d\n",
|
|
|
|
ret);
|
|
|
|
|
|
|
|
mutex_unlock(&irq_data->mtx);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HIF diagnostics */
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
|
|
|
|
size_t buf_len)
|
|
|
|
{
|
|
|
|
int ret;
|
ath10k: use kzalloc to read for ath10k_sdio_hif_diag_read
When use command to read values, it crashed.
command:
dd if=/sys/kernel/debug/ieee80211/phy0/ath10k/mem_value count=1 bs=4 skip=$((0x100233))
It will call to ath10k_sdio_hif_diag_read with address = 0x4008cc and buf_len = 4.
Then system crash:
[ 1786.013258] Unable to handle kernel paging request at virtual address ffffffc00bd45000
[ 1786.013273] Mem abort info:
[ 1786.013281] ESR = 0x96000045
[ 1786.013291] Exception class = DABT (current EL), IL = 32 bits
[ 1786.013299] SET = 0, FnV = 0
[ 1786.013307] EA = 0, S1PTW = 0
[ 1786.013314] Data abort info:
[ 1786.013322] ISV = 0, ISS = 0x00000045
[ 1786.013330] CM = 0, WnR = 1
[ 1786.013342] swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000008542a60e
[ 1786.013350] [ffffffc00bd45000] pgd=0000000000000000, pud=0000000000000000
[ 1786.013368] Internal error: Oops: 96000045 [#1] PREEMPT SMP
[ 1786.013609] Process swapper/0 (pid: 0, stack limit = 0x0000000084b153c6)
[ 1786.013623] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.19.86 #137
[ 1786.013631] Hardware name: MediaTek krane sku176 board (DT)
[ 1786.013643] pstate: 80000085 (Nzcv daIf -PAN -UAO)
[ 1786.013662] pc : __memcpy+0x94/0x180
[ 1786.013678] lr : swiotlb_tbl_unmap_single+0x84/0x150
[ 1786.013686] sp : ffffff8008003c60
[ 1786.013694] x29: ffffff8008003c90 x28: ffffffae96411f80
[ 1786.013708] x27: ffffffae960d2018 x26: ffffff8019a4b9a8
[ 1786.013721] x25: 0000000000000000 x24: 0000000000000001
[ 1786.013734] x23: ffffffae96567000 x22: 00000000000051d4
[ 1786.013747] x21: 0000000000000000 x20: 00000000fe6e9000
[ 1786.013760] x19: 0000000000000004 x18: 0000000000000020
[ 1786.013773] x17: 0000000000000001 x16: 0000000000000000
[ 1786.013787] x15: 00000000ffffffff x14: 00000000000044c0
[ 1786.013800] x13: 0000000000365ba4 x12: 0000000000000000
[ 1786.013813] x11: 0000000000000001 x10: 00000037be6e9000
[ 1786.013826] x9 : ffffffc940000000 x8 : 000000000bd45000
[ 1786.013839] x7 : 0000000000000000 x6 : ffffffc00bd45000
[ 1786.013852] x5 : 0000000000000000 x4 : 0000000000000000
[ 1786.013865] x3 : 0000000000000c00 x2 : 0000000000000004
[ 1786.013878] x1 : fffffff7be6e9004 x0 : ffffffc00bd45000
[ 1786.013891] Call trace:
[ 1786.013903] __memcpy+0x94/0x180
[ 1786.013914] unmap_single+0x6c/0x84
[ 1786.013925] swiotlb_unmap_sg_attrs+0x54/0x80
[ 1786.013938] __swiotlb_unmap_sg_attrs+0x8c/0xa4
[ 1786.013952] msdc_unprepare_data+0x6c/0x84
[ 1786.013963] msdc_request_done+0x58/0x84
[ 1786.013974] msdc_data_xfer_done+0x1a0/0x1c8
[ 1786.013985] msdc_irq+0x12c/0x17c
[ 1786.013996] __handle_irq_event_percpu+0xe4/0x250
[ 1786.014006] handle_irq_event_percpu+0x28/0x68
[ 1786.014015] handle_irq_event+0x48/0x78
[ 1786.014026] handle_fasteoi_irq+0xd0/0x1a0
[ 1786.014039] __handle_domain_irq+0x84/0xc4
[ 1786.014050] gic_handle_irq+0x124/0x1a4
[ 1786.014059] el1_irq+0xb0/0x128
[ 1786.014072] cpuidle_enter_state+0x298/0x328
[ 1786.014082] cpuidle_enter+0x30/0x40
[ 1786.014094] do_idle+0x190/0x268
[ 1786.014104] cpu_startup_entry+0x24/0x28
[ 1786.014116] rest_init+0xd4/0xe0
[ 1786.014126] start_kernel+0x30c/0x38c
[ 1786.014139] Code: f8408423 f80084c3 36100062 b8404423 (b80044c3)
[ 1786.014150] ---[ end trace 3b02ddb698ea69ee ]---
[ 1786.015415] Kernel panic - not syncing: Fatal exception in interrupt
[ 1786.015433] SMP: stopping secondary CPUs
[ 1786.015447] Kernel Offset: 0x2e8d200000 from 0xffffff8008000000
[ 1786.015458] CPU features: 0x0,2188200c
[ 1786.015466] Memory Limit: none
For sdio chip, it need the memory which is kmalloc, if it is
vmalloc from ath10k_mem_value_read, then it have a memory error.
kzalloc of ath10k_sdio_hif_diag_read32 is the correct type, so
add kzalloc in ath10k_sdio_hif_diag_read to replace the buffer
which is vmalloc from ath10k_mem_value_read.
This patch only effect sdio chip.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2020-02-14 11:42:18 +08:00
|
|
|
void *mem;
|
|
|
|
|
|
|
|
mem = kzalloc(buf_len, GFP_KERNEL);
|
|
|
|
if (!mem)
|
|
|
|
return -ENOMEM;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
/* set window register to start read cycle */
|
|
|
|
ret = ath10k_sdio_write32(ar, MBOX_WINDOW_READ_ADDR_ADDRESS, address);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to set mbox window read address: %d", ret);
|
ath10k: use kzalloc to read for ath10k_sdio_hif_diag_read
When use command to read values, it crashed.
command:
dd if=/sys/kernel/debug/ieee80211/phy0/ath10k/mem_value count=1 bs=4 skip=$((0x100233))
It will call to ath10k_sdio_hif_diag_read with address = 0x4008cc and buf_len = 4.
Then system crash:
[ 1786.013258] Unable to handle kernel paging request at virtual address ffffffc00bd45000
[ 1786.013273] Mem abort info:
[ 1786.013281] ESR = 0x96000045
[ 1786.013291] Exception class = DABT (current EL), IL = 32 bits
[ 1786.013299] SET = 0, FnV = 0
[ 1786.013307] EA = 0, S1PTW = 0
[ 1786.013314] Data abort info:
[ 1786.013322] ISV = 0, ISS = 0x00000045
[ 1786.013330] CM = 0, WnR = 1
[ 1786.013342] swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000008542a60e
[ 1786.013350] [ffffffc00bd45000] pgd=0000000000000000, pud=0000000000000000
[ 1786.013368] Internal error: Oops: 96000045 [#1] PREEMPT SMP
[ 1786.013609] Process swapper/0 (pid: 0, stack limit = 0x0000000084b153c6)
[ 1786.013623] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.19.86 #137
[ 1786.013631] Hardware name: MediaTek krane sku176 board (DT)
[ 1786.013643] pstate: 80000085 (Nzcv daIf -PAN -UAO)
[ 1786.013662] pc : __memcpy+0x94/0x180
[ 1786.013678] lr : swiotlb_tbl_unmap_single+0x84/0x150
[ 1786.013686] sp : ffffff8008003c60
[ 1786.013694] x29: ffffff8008003c90 x28: ffffffae96411f80
[ 1786.013708] x27: ffffffae960d2018 x26: ffffff8019a4b9a8
[ 1786.013721] x25: 0000000000000000 x24: 0000000000000001
[ 1786.013734] x23: ffffffae96567000 x22: 00000000000051d4
[ 1786.013747] x21: 0000000000000000 x20: 00000000fe6e9000
[ 1786.013760] x19: 0000000000000004 x18: 0000000000000020
[ 1786.013773] x17: 0000000000000001 x16: 0000000000000000
[ 1786.013787] x15: 00000000ffffffff x14: 00000000000044c0
[ 1786.013800] x13: 0000000000365ba4 x12: 0000000000000000
[ 1786.013813] x11: 0000000000000001 x10: 00000037be6e9000
[ 1786.013826] x9 : ffffffc940000000 x8 : 000000000bd45000
[ 1786.013839] x7 : 0000000000000000 x6 : ffffffc00bd45000
[ 1786.013852] x5 : 0000000000000000 x4 : 0000000000000000
[ 1786.013865] x3 : 0000000000000c00 x2 : 0000000000000004
[ 1786.013878] x1 : fffffff7be6e9004 x0 : ffffffc00bd45000
[ 1786.013891] Call trace:
[ 1786.013903] __memcpy+0x94/0x180
[ 1786.013914] unmap_single+0x6c/0x84
[ 1786.013925] swiotlb_unmap_sg_attrs+0x54/0x80
[ 1786.013938] __swiotlb_unmap_sg_attrs+0x8c/0xa4
[ 1786.013952] msdc_unprepare_data+0x6c/0x84
[ 1786.013963] msdc_request_done+0x58/0x84
[ 1786.013974] msdc_data_xfer_done+0x1a0/0x1c8
[ 1786.013985] msdc_irq+0x12c/0x17c
[ 1786.013996] __handle_irq_event_percpu+0xe4/0x250
[ 1786.014006] handle_irq_event_percpu+0x28/0x68
[ 1786.014015] handle_irq_event+0x48/0x78
[ 1786.014026] handle_fasteoi_irq+0xd0/0x1a0
[ 1786.014039] __handle_domain_irq+0x84/0xc4
[ 1786.014050] gic_handle_irq+0x124/0x1a4
[ 1786.014059] el1_irq+0xb0/0x128
[ 1786.014072] cpuidle_enter_state+0x298/0x328
[ 1786.014082] cpuidle_enter+0x30/0x40
[ 1786.014094] do_idle+0x190/0x268
[ 1786.014104] cpu_startup_entry+0x24/0x28
[ 1786.014116] rest_init+0xd4/0xe0
[ 1786.014126] start_kernel+0x30c/0x38c
[ 1786.014139] Code: f8408423 f80084c3 36100062 b8404423 (b80044c3)
[ 1786.014150] ---[ end trace 3b02ddb698ea69ee ]---
[ 1786.015415] Kernel panic - not syncing: Fatal exception in interrupt
[ 1786.015433] SMP: stopping secondary CPUs
[ 1786.015447] Kernel Offset: 0x2e8d200000 from 0xffffff8008000000
[ 1786.015458] CPU features: 0x0,2188200c
[ 1786.015466] Memory Limit: none
For sdio chip, it need the memory which is kmalloc, if it is
vmalloc from ath10k_mem_value_read, then it have a memory error.
kzalloc of ath10k_sdio_hif_diag_read32 is the correct type, so
add kzalloc in ath10k_sdio_hif_diag_read to replace the buffer
which is vmalloc from ath10k_mem_value_read.
This patch only effect sdio chip.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2020-02-14 11:42:18 +08:00
|
|
|
goto out;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* read the data */
|
ath10k: use kzalloc to read for ath10k_sdio_hif_diag_read
When use command to read values, it crashed.
command:
dd if=/sys/kernel/debug/ieee80211/phy0/ath10k/mem_value count=1 bs=4 skip=$((0x100233))
It will call to ath10k_sdio_hif_diag_read with address = 0x4008cc and buf_len = 4.
Then system crash:
[ 1786.013258] Unable to handle kernel paging request at virtual address ffffffc00bd45000
[ 1786.013273] Mem abort info:
[ 1786.013281] ESR = 0x96000045
[ 1786.013291] Exception class = DABT (current EL), IL = 32 bits
[ 1786.013299] SET = 0, FnV = 0
[ 1786.013307] EA = 0, S1PTW = 0
[ 1786.013314] Data abort info:
[ 1786.013322] ISV = 0, ISS = 0x00000045
[ 1786.013330] CM = 0, WnR = 1
[ 1786.013342] swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000008542a60e
[ 1786.013350] [ffffffc00bd45000] pgd=0000000000000000, pud=0000000000000000
[ 1786.013368] Internal error: Oops: 96000045 [#1] PREEMPT SMP
[ 1786.013609] Process swapper/0 (pid: 0, stack limit = 0x0000000084b153c6)
[ 1786.013623] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.19.86 #137
[ 1786.013631] Hardware name: MediaTek krane sku176 board (DT)
[ 1786.013643] pstate: 80000085 (Nzcv daIf -PAN -UAO)
[ 1786.013662] pc : __memcpy+0x94/0x180
[ 1786.013678] lr : swiotlb_tbl_unmap_single+0x84/0x150
[ 1786.013686] sp : ffffff8008003c60
[ 1786.013694] x29: ffffff8008003c90 x28: ffffffae96411f80
[ 1786.013708] x27: ffffffae960d2018 x26: ffffff8019a4b9a8
[ 1786.013721] x25: 0000000000000000 x24: 0000000000000001
[ 1786.013734] x23: ffffffae96567000 x22: 00000000000051d4
[ 1786.013747] x21: 0000000000000000 x20: 00000000fe6e9000
[ 1786.013760] x19: 0000000000000004 x18: 0000000000000020
[ 1786.013773] x17: 0000000000000001 x16: 0000000000000000
[ 1786.013787] x15: 00000000ffffffff x14: 00000000000044c0
[ 1786.013800] x13: 0000000000365ba4 x12: 0000000000000000
[ 1786.013813] x11: 0000000000000001 x10: 00000037be6e9000
[ 1786.013826] x9 : ffffffc940000000 x8 : 000000000bd45000
[ 1786.013839] x7 : 0000000000000000 x6 : ffffffc00bd45000
[ 1786.013852] x5 : 0000000000000000 x4 : 0000000000000000
[ 1786.013865] x3 : 0000000000000c00 x2 : 0000000000000004
[ 1786.013878] x1 : fffffff7be6e9004 x0 : ffffffc00bd45000
[ 1786.013891] Call trace:
[ 1786.013903] __memcpy+0x94/0x180
[ 1786.013914] unmap_single+0x6c/0x84
[ 1786.013925] swiotlb_unmap_sg_attrs+0x54/0x80
[ 1786.013938] __swiotlb_unmap_sg_attrs+0x8c/0xa4
[ 1786.013952] msdc_unprepare_data+0x6c/0x84
[ 1786.013963] msdc_request_done+0x58/0x84
[ 1786.013974] msdc_data_xfer_done+0x1a0/0x1c8
[ 1786.013985] msdc_irq+0x12c/0x17c
[ 1786.013996] __handle_irq_event_percpu+0xe4/0x250
[ 1786.014006] handle_irq_event_percpu+0x28/0x68
[ 1786.014015] handle_irq_event+0x48/0x78
[ 1786.014026] handle_fasteoi_irq+0xd0/0x1a0
[ 1786.014039] __handle_domain_irq+0x84/0xc4
[ 1786.014050] gic_handle_irq+0x124/0x1a4
[ 1786.014059] el1_irq+0xb0/0x128
[ 1786.014072] cpuidle_enter_state+0x298/0x328
[ 1786.014082] cpuidle_enter+0x30/0x40
[ 1786.014094] do_idle+0x190/0x268
[ 1786.014104] cpu_startup_entry+0x24/0x28
[ 1786.014116] rest_init+0xd4/0xe0
[ 1786.014126] start_kernel+0x30c/0x38c
[ 1786.014139] Code: f8408423 f80084c3 36100062 b8404423 (b80044c3)
[ 1786.014150] ---[ end trace 3b02ddb698ea69ee ]---
[ 1786.015415] Kernel panic - not syncing: Fatal exception in interrupt
[ 1786.015433] SMP: stopping secondary CPUs
[ 1786.015447] Kernel Offset: 0x2e8d200000 from 0xffffff8008000000
[ 1786.015458] CPU features: 0x0,2188200c
[ 1786.015466] Memory Limit: none
For sdio chip, it need the memory which is kmalloc, if it is
vmalloc from ath10k_mem_value_read, then it have a memory error.
kzalloc of ath10k_sdio_hif_diag_read32 is the correct type, so
add kzalloc in ath10k_sdio_hif_diag_read to replace the buffer
which is vmalloc from ath10k_mem_value_read.
This patch only effect sdio chip.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2020-02-14 11:42:18 +08:00
|
|
|
ret = ath10k_sdio_read(ar, MBOX_WINDOW_DATA_ADDRESS, mem, buf_len);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret) {
|
2017-06-25 22:29:32 +01:00
|
|
|
ath10k_warn(ar, "failed to read from mbox window data address: %d\n",
|
2017-04-26 12:18:00 +03:00
|
|
|
ret);
|
ath10k: use kzalloc to read for ath10k_sdio_hif_diag_read
When use command to read values, it crashed.
command:
dd if=/sys/kernel/debug/ieee80211/phy0/ath10k/mem_value count=1 bs=4 skip=$((0x100233))
It will call to ath10k_sdio_hif_diag_read with address = 0x4008cc and buf_len = 4.
Then system crash:
[ 1786.013258] Unable to handle kernel paging request at virtual address ffffffc00bd45000
[ 1786.013273] Mem abort info:
[ 1786.013281] ESR = 0x96000045
[ 1786.013291] Exception class = DABT (current EL), IL = 32 bits
[ 1786.013299] SET = 0, FnV = 0
[ 1786.013307] EA = 0, S1PTW = 0
[ 1786.013314] Data abort info:
[ 1786.013322] ISV = 0, ISS = 0x00000045
[ 1786.013330] CM = 0, WnR = 1
[ 1786.013342] swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000008542a60e
[ 1786.013350] [ffffffc00bd45000] pgd=0000000000000000, pud=0000000000000000
[ 1786.013368] Internal error: Oops: 96000045 [#1] PREEMPT SMP
[ 1786.013609] Process swapper/0 (pid: 0, stack limit = 0x0000000084b153c6)
[ 1786.013623] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.19.86 #137
[ 1786.013631] Hardware name: MediaTek krane sku176 board (DT)
[ 1786.013643] pstate: 80000085 (Nzcv daIf -PAN -UAO)
[ 1786.013662] pc : __memcpy+0x94/0x180
[ 1786.013678] lr : swiotlb_tbl_unmap_single+0x84/0x150
[ 1786.013686] sp : ffffff8008003c60
[ 1786.013694] x29: ffffff8008003c90 x28: ffffffae96411f80
[ 1786.013708] x27: ffffffae960d2018 x26: ffffff8019a4b9a8
[ 1786.013721] x25: 0000000000000000 x24: 0000000000000001
[ 1786.013734] x23: ffffffae96567000 x22: 00000000000051d4
[ 1786.013747] x21: 0000000000000000 x20: 00000000fe6e9000
[ 1786.013760] x19: 0000000000000004 x18: 0000000000000020
[ 1786.013773] x17: 0000000000000001 x16: 0000000000000000
[ 1786.013787] x15: 00000000ffffffff x14: 00000000000044c0
[ 1786.013800] x13: 0000000000365ba4 x12: 0000000000000000
[ 1786.013813] x11: 0000000000000001 x10: 00000037be6e9000
[ 1786.013826] x9 : ffffffc940000000 x8 : 000000000bd45000
[ 1786.013839] x7 : 0000000000000000 x6 : ffffffc00bd45000
[ 1786.013852] x5 : 0000000000000000 x4 : 0000000000000000
[ 1786.013865] x3 : 0000000000000c00 x2 : 0000000000000004
[ 1786.013878] x1 : fffffff7be6e9004 x0 : ffffffc00bd45000
[ 1786.013891] Call trace:
[ 1786.013903] __memcpy+0x94/0x180
[ 1786.013914] unmap_single+0x6c/0x84
[ 1786.013925] swiotlb_unmap_sg_attrs+0x54/0x80
[ 1786.013938] __swiotlb_unmap_sg_attrs+0x8c/0xa4
[ 1786.013952] msdc_unprepare_data+0x6c/0x84
[ 1786.013963] msdc_request_done+0x58/0x84
[ 1786.013974] msdc_data_xfer_done+0x1a0/0x1c8
[ 1786.013985] msdc_irq+0x12c/0x17c
[ 1786.013996] __handle_irq_event_percpu+0xe4/0x250
[ 1786.014006] handle_irq_event_percpu+0x28/0x68
[ 1786.014015] handle_irq_event+0x48/0x78
[ 1786.014026] handle_fasteoi_irq+0xd0/0x1a0
[ 1786.014039] __handle_domain_irq+0x84/0xc4
[ 1786.014050] gic_handle_irq+0x124/0x1a4
[ 1786.014059] el1_irq+0xb0/0x128
[ 1786.014072] cpuidle_enter_state+0x298/0x328
[ 1786.014082] cpuidle_enter+0x30/0x40
[ 1786.014094] do_idle+0x190/0x268
[ 1786.014104] cpu_startup_entry+0x24/0x28
[ 1786.014116] rest_init+0xd4/0xe0
[ 1786.014126] start_kernel+0x30c/0x38c
[ 1786.014139] Code: f8408423 f80084c3 36100062 b8404423 (b80044c3)
[ 1786.014150] ---[ end trace 3b02ddb698ea69ee ]---
[ 1786.015415] Kernel panic - not syncing: Fatal exception in interrupt
[ 1786.015433] SMP: stopping secondary CPUs
[ 1786.015447] Kernel Offset: 0x2e8d200000 from 0xffffff8008000000
[ 1786.015458] CPU features: 0x0,2188200c
[ 1786.015466] Memory Limit: none
For sdio chip, it need the memory which is kmalloc, if it is
vmalloc from ath10k_mem_value_read, then it have a memory error.
kzalloc of ath10k_sdio_hif_diag_read32 is the correct type, so
add kzalloc in ath10k_sdio_hif_diag_read to replace the buffer
which is vmalloc from ath10k_mem_value_read.
This patch only effect sdio chip.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2020-02-14 11:42:18 +08:00
|
|
|
goto out;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
ath10k: use kzalloc to read for ath10k_sdio_hif_diag_read
When use command to read values, it crashed.
command:
dd if=/sys/kernel/debug/ieee80211/phy0/ath10k/mem_value count=1 bs=4 skip=$((0x100233))
It will call to ath10k_sdio_hif_diag_read with address = 0x4008cc and buf_len = 4.
Then system crash:
[ 1786.013258] Unable to handle kernel paging request at virtual address ffffffc00bd45000
[ 1786.013273] Mem abort info:
[ 1786.013281] ESR = 0x96000045
[ 1786.013291] Exception class = DABT (current EL), IL = 32 bits
[ 1786.013299] SET = 0, FnV = 0
[ 1786.013307] EA = 0, S1PTW = 0
[ 1786.013314] Data abort info:
[ 1786.013322] ISV = 0, ISS = 0x00000045
[ 1786.013330] CM = 0, WnR = 1
[ 1786.013342] swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000008542a60e
[ 1786.013350] [ffffffc00bd45000] pgd=0000000000000000, pud=0000000000000000
[ 1786.013368] Internal error: Oops: 96000045 [#1] PREEMPT SMP
[ 1786.013609] Process swapper/0 (pid: 0, stack limit = 0x0000000084b153c6)
[ 1786.013623] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.19.86 #137
[ 1786.013631] Hardware name: MediaTek krane sku176 board (DT)
[ 1786.013643] pstate: 80000085 (Nzcv daIf -PAN -UAO)
[ 1786.013662] pc : __memcpy+0x94/0x180
[ 1786.013678] lr : swiotlb_tbl_unmap_single+0x84/0x150
[ 1786.013686] sp : ffffff8008003c60
[ 1786.013694] x29: ffffff8008003c90 x28: ffffffae96411f80
[ 1786.013708] x27: ffffffae960d2018 x26: ffffff8019a4b9a8
[ 1786.013721] x25: 0000000000000000 x24: 0000000000000001
[ 1786.013734] x23: ffffffae96567000 x22: 00000000000051d4
[ 1786.013747] x21: 0000000000000000 x20: 00000000fe6e9000
[ 1786.013760] x19: 0000000000000004 x18: 0000000000000020
[ 1786.013773] x17: 0000000000000001 x16: 0000000000000000
[ 1786.013787] x15: 00000000ffffffff x14: 00000000000044c0
[ 1786.013800] x13: 0000000000365ba4 x12: 0000000000000000
[ 1786.013813] x11: 0000000000000001 x10: 00000037be6e9000
[ 1786.013826] x9 : ffffffc940000000 x8 : 000000000bd45000
[ 1786.013839] x7 : 0000000000000000 x6 : ffffffc00bd45000
[ 1786.013852] x5 : 0000000000000000 x4 : 0000000000000000
[ 1786.013865] x3 : 0000000000000c00 x2 : 0000000000000004
[ 1786.013878] x1 : fffffff7be6e9004 x0 : ffffffc00bd45000
[ 1786.013891] Call trace:
[ 1786.013903] __memcpy+0x94/0x180
[ 1786.013914] unmap_single+0x6c/0x84
[ 1786.013925] swiotlb_unmap_sg_attrs+0x54/0x80
[ 1786.013938] __swiotlb_unmap_sg_attrs+0x8c/0xa4
[ 1786.013952] msdc_unprepare_data+0x6c/0x84
[ 1786.013963] msdc_request_done+0x58/0x84
[ 1786.013974] msdc_data_xfer_done+0x1a0/0x1c8
[ 1786.013985] msdc_irq+0x12c/0x17c
[ 1786.013996] __handle_irq_event_percpu+0xe4/0x250
[ 1786.014006] handle_irq_event_percpu+0x28/0x68
[ 1786.014015] handle_irq_event+0x48/0x78
[ 1786.014026] handle_fasteoi_irq+0xd0/0x1a0
[ 1786.014039] __handle_domain_irq+0x84/0xc4
[ 1786.014050] gic_handle_irq+0x124/0x1a4
[ 1786.014059] el1_irq+0xb0/0x128
[ 1786.014072] cpuidle_enter_state+0x298/0x328
[ 1786.014082] cpuidle_enter+0x30/0x40
[ 1786.014094] do_idle+0x190/0x268
[ 1786.014104] cpu_startup_entry+0x24/0x28
[ 1786.014116] rest_init+0xd4/0xe0
[ 1786.014126] start_kernel+0x30c/0x38c
[ 1786.014139] Code: f8408423 f80084c3 36100062 b8404423 (b80044c3)
[ 1786.014150] ---[ end trace 3b02ddb698ea69ee ]---
[ 1786.015415] Kernel panic - not syncing: Fatal exception in interrupt
[ 1786.015433] SMP: stopping secondary CPUs
[ 1786.015447] Kernel Offset: 0x2e8d200000 from 0xffffff8008000000
[ 1786.015458] CPU features: 0x0,2188200c
[ 1786.015466] Memory Limit: none
For sdio chip, it need the memory which is kmalloc, if it is
vmalloc from ath10k_mem_value_read, then it have a memory error.
kzalloc of ath10k_sdio_hif_diag_read32 is the correct type, so
add kzalloc in ath10k_sdio_hif_diag_read to replace the buffer
which is vmalloc from ath10k_mem_value_read.
This patch only effect sdio chip.
Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2020-02-14 11:42:18 +08:00
|
|
|
memcpy(buf, mem, buf_len);
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(mem);
|
|
|
|
|
|
|
|
return ret;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
static int ath10k_sdio_diag_read32(struct ath10k *ar, u32 address,
|
|
|
|
u32 *value)
|
2017-04-26 12:18:00 +03:00
|
|
|
{
|
|
|
|
__le32 *val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
val = kzalloc(sizeof(*val), GFP_KERNEL);
|
|
|
|
if (!val)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = ath10k_sdio_hif_diag_read(ar, address, val, sizeof(*val));
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
*value = __le32_to_cpu(*val);
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(val);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_diag_write_mem(struct ath10k *ar, u32 address,
|
|
|
|
const void *data, int nbytes)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* set write data */
|
|
|
|
ret = ath10k_sdio_write(ar, MBOX_WINDOW_DATA_ADDRESS, data, nbytes);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar,
|
2017-06-25 22:29:32 +01:00
|
|
|
"failed to write 0x%p to mbox window data address: %d\n",
|
2017-04-26 12:18:00 +03:00
|
|
|
data, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set window register, which starts the write cycle */
|
|
|
|
ret = ath10k_sdio_write32(ar, MBOX_WINDOW_WRITE_ADDR_ADDRESS, address);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to set mbox window write address: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:50:56 +03:00
|
|
|
static int ath10k_sdio_hif_start_post(struct ath10k *ar)
|
2019-01-29 20:03:12 +08:00
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
u32 addr, val;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_diag_read32(ar, addr, &val);
|
2019-01-29 20:03:12 +08:00
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to read hi_acs_flags : %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val & HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK) {
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio mailbox swap service enabled\n");
|
|
|
|
ar_sdio->swap_mbox = true;
|
2019-04-10 12:30:54 +08:00
|
|
|
} else {
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio mailbox swap service disabled\n");
|
|
|
|
ar_sdio->swap_mbox = false;
|
2019-01-29 20:03:12 +08:00
|
|
|
}
|
2019-04-10 12:30:54 +08:00
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ath10k_sdio_set_mbox_sleep(ar, true);
|
2020-04-16 14:50:57 +03:00
|
|
|
|
2019-01-29 20:03:12 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ath10k: disable TX complete indication of htt for sdio
For sdio chip, it is high latency bus, all the TX packet's content will
be tranferred from HOST memory to firmware memory via sdio bus, then it
need much more memory in firmware than low latency bus chip, for low
latency chip, such as PCI-E, it only need to transfer the TX descriptor
via PCI-E bus to firmware memory. For sdio chip, reduce the complexity of
TX logic will help TX efficiency since its memory is limited, and it will
reduce the TX circle's time of each packet and then firmware will have more
memory for TX since TX complete also need memeory.
This patch disable TX complete indication from firmware for htt data
packet, it will not have TX complete indication from firmware to ath10k.
It will cut the cost of bus bandwidth of TX complete and make the TX
logic of firmware simpler, it results in significant performance
improvement on TX path.
Udp TX throughout is 130Mbps without this patch, and it arrives
400Mbps with this patch.
The downside of this patch is the command "iw wlan0 station dump" will
show 0 for "tx retries" and "tx failed" since all tx packet's status
is success.
This patch only effect sdio chip, it will not effect PCI, SNOC etc.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWPZ-1
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200212080415.31265-2-wgong@codeaurora.org
2020-04-07 08:12:30 +03:00
|
|
|
static int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
u32 addr, val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_diag_read32(ar, addr, &val);
|
ath10k: disable TX complete indication of htt for sdio
For sdio chip, it is high latency bus, all the TX packet's content will
be tranferred from HOST memory to firmware memory via sdio bus, then it
need much more memory in firmware than low latency bus chip, for low
latency chip, such as PCI-E, it only need to transfer the TX descriptor
via PCI-E bus to firmware memory. For sdio chip, reduce the complexity of
TX logic will help TX efficiency since its memory is limited, and it will
reduce the TX circle's time of each packet and then firmware will have more
memory for TX since TX complete also need memeory.
This patch disable TX complete indication from firmware for htt data
packet, it will not have TX complete indication from firmware to ath10k.
It will cut the cost of bus bandwidth of TX complete and make the TX
logic of firmware simpler, it results in significant performance
improvement on TX path.
Udp TX throughout is 130Mbps without this patch, and it arrives
400Mbps with this patch.
The downside of this patch is the command "iw wlan0 station dump" will
show 0 for "tx retries" and "tx failed" since all tx packet's status
is success.
This patch only effect sdio chip, it will not effect PCI, SNOC etc.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWPZ-1
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200212080415.31265-2-wgong@codeaurora.org
2020-04-07 08:12:30 +03:00
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar,
|
2025-04-18 12:56:27 +01:00
|
|
|
"unable to read hi_acs_flags for htt tx complete: %d\n", ret);
|
ath10k: disable TX complete indication of htt for sdio
For sdio chip, it is high latency bus, all the TX packet's content will
be tranferred from HOST memory to firmware memory via sdio bus, then it
need much more memory in firmware than low latency bus chip, for low
latency chip, such as PCI-E, it only need to transfer the TX descriptor
via PCI-E bus to firmware memory. For sdio chip, reduce the complexity of
TX logic will help TX efficiency since its memory is limited, and it will
reduce the TX circle's time of each packet and then firmware will have more
memory for TX since TX complete also need memeory.
This patch disable TX complete indication from firmware for htt data
packet, it will not have TX complete indication from firmware to ath10k.
It will cut the cost of bus bandwidth of TX complete and make the TX
logic of firmware simpler, it results in significant performance
improvement on TX path.
Udp TX throughout is 130Mbps without this patch, and it arrives
400Mbps with this patch.
The downside of this patch is the command "iw wlan0 station dump" will
show 0 for "tx retries" and "tx failed" since all tx packet's status
is success.
This patch only effect sdio chip, it will not effect PCI, SNOC etc.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWPZ-1
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200212080415.31265-2-wgong@codeaurora.org
2020-04-07 08:12:30 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = (val & HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK);
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio reduce tx complete fw%sack\n",
|
|
|
|
ret ? " " : " not ");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
/* HIF start/stop */
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_start(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
int ret;
|
|
|
|
|
ath10k: prevent deinitializing NAPI twice
It happened "Kernel panic - not syncing: hung_task: blocked tasks" when
test simulate crash and ifconfig down/rmmod meanwhile.
Test steps:
1.Test commands, either can reproduce the hang for PCIe, SDIO and SNOC.
echo soft > /sys/kernel/debug/ieee80211/phy0/ath10k/simulate_fw_crash;sleep 0.05;ifconfig wlan0 down
echo soft > /sys/kernel/debug/ieee80211/phy0/ath10k/simulate_fw_crash;rmmod ath10k_sdio
echo hw-restart > /sys/kernel/debug/ieee80211/phy0/ath10k/simulate_fw_crash;rmmod ath10k_pci
2. dmesg:
[ 5622.548630] ath10k_sdio mmc1:0001:1: simulating soft firmware crash
[ 5622.655995] ieee80211 phy0: Hardware restart was requested
[ 5776.355164] INFO: task shill:1572 blocked for more than 122 seconds.
[ 5776.355687] INFO: task kworker/1:2:24437 blocked for more than 122 seconds.
[ 5776.359812] Kernel panic - not syncing: hung_task: blocked tasks
[ 5776.359836] CPU: 1 PID: 55 Comm: khungtaskd Tainted: G W 4.19.86 #137
[ 5776.359846] Hardware name: MediaTek krane sku176 board (DT)
[ 5776.359855] Call trace:
[ 5776.359868] dump_backtrace+0x0/0x170
[ 5776.359881] show_stack+0x20/0x2c
[ 5776.359896] dump_stack+0xd4/0x10c
[ 5776.359916] panic+0x12c/0x29c
[ 5776.359937] hung_task_panic+0x0/0x50
[ 5776.359953] kthread+0x120/0x130
[ 5776.359965] ret_from_fork+0x10/0x18
[ 5776.359986] SMP: stopping secondary CPUs
[ 5776.360012] Kernel Offset: 0x141ea00000 from 0xffffff8008000000
[ 5776.360026] CPU features: 0x0,2188200c
[ 5776.360035] Memory Limit: none
command "ifconfig wlan0 down" or "rmmod ath10k_sdio" will be blocked
callstack of ifconfig:
[<0>] __switch_to+0x120/0x13c
[<0>] msleep+0x28/0x38
[<0>] ath10k_sdio_hif_stop+0x24c/0x294 [ath10k_sdio]
[<0>] ath10k_core_stop+0x50/0x78 [ath10k_core]
[<0>] ath10k_halt+0x120/0x178 [ath10k_core]
[<0>] ath10k_stop+0x4c/0x8c [ath10k_core]
[<0>] drv_stop+0xe0/0x1e4 [mac80211]
[<0>] ieee80211_stop_device+0x48/0x54 [mac80211]
[<0>] ieee80211_do_stop+0x678/0x6f8 [mac80211]
[<0>] ieee80211_stop+0x20/0x30 [mac80211]
[<0>] __dev_close_many+0xb8/0x11c
[<0>] __dev_change_flags+0xe0/0x1d0
[<0>] dev_change_flags+0x30/0x6c
[<0>] devinet_ioctl+0x370/0x564
[<0>] inet_ioctl+0xdc/0x304
[<0>] sock_do_ioctl+0x50/0x288
[<0>] compat_sock_ioctl+0x1b4/0x1aac
[<0>] __se_compat_sys_ioctl+0x100/0x26fc
[<0>] __arm64_compat_sys_ioctl+0x20/0x2c
[<0>] el0_svc_common+0xa4/0x154
[<0>] el0_svc_compat_handler+0x2c/0x38
[<0>] el0_svc_compat+0x8/0x18
[<0>] 0xffffffffffffffff
callstack of rmmod:
[<0>] __switch_to+0x120/0x13c
[<0>] msleep+0x28/0x38
[<0>] ath10k_sdio_hif_stop+0x294/0x31c [ath10k_sdio]
[<0>] ath10k_core_stop+0x50/0x78 [ath10k_core]
[<0>] ath10k_halt+0x120/0x178 [ath10k_core]
[<0>] ath10k_stop+0x4c/0x8c [ath10k_core]
[<0>] drv_stop+0xe0/0x1e4 [mac80211]
[<0>] ieee80211_stop_device+0x48/0x54 [mac80211]
[<0>] ieee80211_do_stop+0x678/0x6f8 [mac80211]
[<0>] ieee80211_stop+0x20/0x30 [mac80211]
[<0>] __dev_close_many+0xb8/0x11c
[<0>] dev_close_many+0x70/0x100
[<0>] dev_close+0x4c/0x80
[<0>] cfg80211_shutdown_all_interfaces+0x50/0xcc [cfg80211]
[<0>] ieee80211_remove_interfaces+0x58/0x1a0 [mac80211]
[<0>] ieee80211_unregister_hw+0x40/0x100 [mac80211]
[<0>] ath10k_mac_unregister+0x1c/0x44 [ath10k_core]
[<0>] ath10k_core_unregister+0x38/0x7c [ath10k_core]
[<0>] ath10k_sdio_remove+0x8c/0xd0 [ath10k_sdio]
[<0>] sdio_bus_remove+0x48/0x108
[<0>] device_release_driver_internal+0x138/0x1ec
[<0>] driver_detach+0x6c/0xa8
[<0>] bus_remove_driver+0x78/0xa8
[<0>] driver_unregister+0x30/0x50
[<0>] sdio_unregister_driver+0x28/0x34
[<0>] cleanup_module+0x14/0x6bc [ath10k_sdio]
[<0>] __arm64_sys_delete_module+0x1e0/0x22c
[<0>] el0_svc_common+0xa4/0x154
[<0>] el0_svc_compat_handler+0x2c/0x38
[<0>] el0_svc_compat+0x8/0x18
[<0>] 0xffffffffffffffff
SNOC:
[ 647.156863] Call trace:
[ 647.162166] [<ffffff80080855a4>] __switch_to+0x120/0x13c
[ 647.164512] [<ffffff800899d8b8>] __schedule+0x5ec/0x798
[ 647.170062] [<ffffff800899dad8>] schedule+0x74/0x94
[ 647.175050] [<ffffff80089a0848>] schedule_timeout+0x314/0x42c
[ 647.179874] [<ffffff80089a0a14>] schedule_timeout_uninterruptible+0x34/0x40
[ 647.185780] [<ffffff80082a494>] msleep+0x28/0x38
[ 647.192546] [<ffffff800117ec4c>] ath10k_snoc_hif_stop+0x4c/0x1e0 [ath10k_snoc]
[ 647.197439] [<ffffff80010dfbd8>] ath10k_core_stop+0x50/0x7c [ath10k_core]
[ 647.204652] [<ffffff80010c8f48>] ath10k_halt+0x114/0x16c [ath10k_core]
[ 647.211420] [<ffffff80010cad68>] ath10k_stop+0x4c/0x88 [ath10k_core]
[ 647.217865] [<ffffff8000fdbf54>] drv_stop+0x110/0x244 [mac80211]
[ 647.224367] [<ffffff80010147ac>] ieee80211_stop_device+0x48/0x54 [mac80211]
[ 647.230359] [<ffffff8000ff3eec>] ieee80211_do_stop+0x6a4/0x73c [mac80211]
[ 647.237033] [<ffffff8000ff4500>] ieee80211_stop+0x20/0x30 [mac80211]
[ 647.243942] [<ffffff80087e39b8>] __dev_close_many+0xa0/0xfc
[ 647.250435] [<ffffff80087e3888>] dev_close_many+0x70/0x100
[ 647.255651] [<ffffff80087e3a60>] dev_close+0x4c/0x80
[ 647.261244] [<ffffff8000f1ba54>] cfg80211_shutdown_all_interfaces+0x44/0xcc [cfg80211]
[ 647.266383] [<ffffff8000ff3fdc>] ieee80211_remove_interfaces+0x58/0x1b4 [mac80211]
[ 647.274128] [<ffffff8000fda540>] ieee80211_unregister_hw+0x50/0x120 [mac80211]
[ 647.281659] [<ffffff80010ca314>] ath10k_mac_unregister+0x1c/0x44 [ath10k_core]
[ 647.288839] [<ffffff80010dfc94>] ath10k_core_unregister+0x48/0x90 [ath10k_core]
[ 647.296027] [<ffffff800117e598>] ath10k_snoc_remove+0x5c/0x150 [ath10k_snoc]
[ 647.303229] [<ffffff80085625fc>] platform_drv_remove+0x28/0x50
[ 647.310517] [<ffffff80085601a4>] device_release_driver_internal+0x114/0x1b8
[ 647.316257] [<ffffff80085602e4>] driver_detach+0x6c/0xa8
[ 647.323021] [<ffffff800855e5b8>] bus_remove_driver+0x78/0xa8
[ 647.328571] [<ffffff800856107c>] driver_unregister+0x30/0x50
[ 647.334213] [<ffffff8008562674>] platform_driver_unregister+0x1c/0x28
[ 647.339876] [<ffffff800117fefc>] cleanup_module+0x1c/0x120 [ath10k_snoc]
[ 647.346196] [<ffffff8008143ab8>] SyS_delete_module+0x1dc/0x22c
PCIe:
[ 615.392770] rmmod D 0 3523 3458 0x00000080
[ 615.392777] Call Trace:
[ 615.392784] __schedule+0x617/0x7d3
[ 615.392791] ? __mod_timer+0x263/0x35c
[ 615.392797] schedule+0x62/0x72
[ 615.392803] schedule_timeout+0x8d/0xf3
[ 615.392809] ? run_local_timers+0x6b/0x6b
[ 615.392814] msleep+0x1b/0x22
[ 615.392824] ath10k_pci_hif_stop+0x68/0xd6 [ath10k_pci]
[ 615.392844] ath10k_core_stop+0x44/0x67 [ath10k_core]
[ 615.392859] ath10k_halt+0x102/0x153 [ath10k_core]
[ 615.392873] ath10k_stop+0x38/0x75 [ath10k_core]
[ 615.392893] drv_stop+0x9a/0x13c [mac80211]
[ 615.392915] ieee80211_do_stop+0x772/0x7cd [mac80211]
[ 615.392937] ieee80211_stop+0x1a/0x1e [mac80211]
[ 615.392945] __dev_close_many+0x9e/0xf0
[ 615.392952] dev_close_many+0x62/0xe8
[ 615.392958] dev_close+0x54/0x7d
[ 615.392975] cfg80211_shutdown_all_interfaces+0x6e/0xa5 [cfg80211]
[ 615.393021] ieee80211_remove_interfaces+0x52/0x1aa [mac80211]
[ 615.393049] ieee80211_unregister_hw+0x54/0x136 [mac80211]
[ 615.393068] ath10k_mac_unregister+0x19/0x4a [ath10k_core]
[ 615.393091] ath10k_core_unregister+0x39/0x7e [ath10k_core]
[ 615.393104] ath10k_pci_remove+0x3d/0x7f [ath10k_pci]
[ 615.393117] pci_device_remove+0x41/0xa6
[ 615.393129] device_release_driver_internal+0x123/0x1ec
[ 615.393140] driver_detach+0x60/0x90
[ 615.393152] bus_remove_driver+0x72/0x9f
[ 615.393164] pci_unregister_driver+0x1e/0x87
[ 615.393177] SyS_delete_module+0x1d7/0x277
[ 615.393188] do_syscall_64+0x6b/0xf7
[ 615.393199] entry_SYSCALL_64_after_hwframe+0x41/0xa6
The test command run simulate_fw_crash firstly and it call into
ath10k_sdio_hif_stop from ath10k_core_restart, then napi_disable
is called and bit NAPI_STATE_SCHED is set. After that, function
ath10k_sdio_hif_stop is called again from ath10k_stop by command
"ifconfig wlan0 down" or "rmmod ath10k_sdio", then command blocked.
It is blocked by napi_synchronize, napi_disable will set bit with
NAPI_STATE_SCHED, and then napi_synchronize will enter dead loop
becuase bit NAPI_STATE_SCHED is set by napi_disable.
function of napi_synchronize
static inline void napi_synchronize(const struct napi_struct *n)
{
if (IS_ENABLED(CONFIG_SMP))
while (test_bit(NAPI_STATE_SCHED, &n->state))
msleep(1);
else
barrier();
}
function of napi_disable
void napi_disable(struct napi_struct *n)
{
might_sleep();
set_bit(NAPI_STATE_DISABLE, &n->state);
while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
msleep(1);
while (test_and_set_bit(NAPI_STATE_NPSVC, &n->state))
msleep(1);
hrtimer_cancel(&n->timer);
clear_bit(NAPI_STATE_DISABLE, &n->state);
}
Add flag for it avoid the hang and crash.
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00049
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
Tested-on: WCN3990 hw1.0 SNOC hw1.0 WLAN.HL.3.1-01307.1-QCAHLSWMTPL-2
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1598617348-2325-1-git-send-email-wgong@codeaurora.org
2020-12-15 08:35:04 +02:00
|
|
|
ath10k_core_napi_enable(ar);
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
/* Sleep 20 ms before HIF interrupts are disabled.
|
|
|
|
* This will give target plenty of time to process the BMI done
|
|
|
|
* request before interrupts are disabled.
|
|
|
|
*/
|
|
|
|
msleep(20);
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_disable_intrs(ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* eid 0 always uses the lower part of the extended mailbox address
|
|
|
|
* space (ext_info[0].htc_ext_addr).
|
|
|
|
*/
|
|
|
|
ar_sdio->mbox_addr[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
|
|
|
|
ar_sdio->mbox_size[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
|
|
|
|
|
|
|
|
sdio_claim_host(ar_sdio->func);
|
|
|
|
|
|
|
|
/* Register the isr */
|
|
|
|
ret = sdio_claim_irq(ar_sdio->func, ath10k_sdio_irq_handler);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to claim sdio interrupt: %d\n", ret);
|
|
|
|
sdio_release_host(ar_sdio->func);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
sdio_release_host(ar_sdio->func);
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_enable_intrs(ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar, "failed to enable sdio interrupts: %d\n", ret);
|
|
|
|
|
|
|
|
/* Enable sleep and then disable it again */
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_set_mbox_sleep(ar, true);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Wait for 20ms for the written value to take effect */
|
|
|
|
msleep(20);
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ret = ath10k_sdio_set_mbox_sleep(ar, false);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SDIO_IRQ_DISABLE_TIMEOUT_HZ (3 * HZ)
|
|
|
|
|
|
|
|
static void ath10k_sdio_irq_disable(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
|
|
|
|
struct ath10k_sdio_irq_enable_regs *regs = irq_data->irq_en_reg;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct completion irqs_disabled_comp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
skb = dev_alloc_skb(sizeof(*regs));
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mutex_lock(&irq_data->mtx);
|
|
|
|
|
|
|
|
memset(regs, 0, sizeof(*regs)); /* disable all interrupts */
|
|
|
|
memcpy(skb->data, regs, sizeof(*regs));
|
|
|
|
skb_put(skb, sizeof(*regs));
|
|
|
|
|
|
|
|
mutex_unlock(&irq_data->mtx);
|
|
|
|
|
|
|
|
init_completion(&irqs_disabled_comp);
|
|
|
|
ret = ath10k_sdio_prep_async_req(ar, MBOX_INT_STATUS_ENABLE_ADDRESS,
|
|
|
|
skb, &irqs_disabled_comp, false, 0);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
|
|
|
|
|
|
|
|
/* Wait for the completion of the IRQ disable request.
|
|
|
|
* If there is a timeout we will try to disable irq's anyway.
|
|
|
|
*/
|
|
|
|
ret = wait_for_completion_timeout(&irqs_disabled_comp,
|
|
|
|
SDIO_IRQ_DISABLE_TIMEOUT_HZ);
|
|
|
|
if (!ret)
|
|
|
|
ath10k_warn(ar, "sdio irq disable request timed out\n");
|
|
|
|
|
|
|
|
sdio_claim_host(ar_sdio->func);
|
|
|
|
|
|
|
|
ret = sdio_release_irq(ar_sdio->func);
|
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar, "failed to release sdio interrupt: %d\n", ret);
|
|
|
|
|
|
|
|
sdio_release_host(ar_sdio->func);
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_hif_stop(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio_bus_request *req, *tmp_req;
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
2020-11-05 14:33:56 +08:00
|
|
|
struct sk_buff *skb;
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
ath10k_sdio_irq_disable(ar);
|
|
|
|
|
2020-11-05 14:33:56 +08:00
|
|
|
cancel_work_sync(&ar_sdio->async_work_rx);
|
|
|
|
|
|
|
|
while ((skb = skb_dequeue(&ar_sdio->rx_head)))
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
cancel_work_sync(&ar_sdio->wr_async_work);
|
|
|
|
|
|
|
|
spin_lock_bh(&ar_sdio->wr_async_lock);
|
|
|
|
|
|
|
|
/* Free all bus requests that have not been handled */
|
|
|
|
list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
|
|
|
|
struct ath10k_htc_ep *ep;
|
|
|
|
|
|
|
|
list_del(&req->list);
|
|
|
|
|
|
|
|
if (req->htc_msg) {
|
|
|
|
ep = &ar->htc.endpoint[req->eid];
|
|
|
|
ath10k_htc_notify_tx_completion(ep, req->skb);
|
|
|
|
} else if (req->skb) {
|
|
|
|
kfree_skb(req->skb);
|
|
|
|
}
|
|
|
|
ath10k_sdio_free_bus_req(ar, req);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_bh(&ar_sdio->wr_async_lock);
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
|
ath10k: prevent deinitializing NAPI twice
It happened "Kernel panic - not syncing: hung_task: blocked tasks" when
test simulate crash and ifconfig down/rmmod meanwhile.
Test steps:
1.Test commands, either can reproduce the hang for PCIe, SDIO and SNOC.
echo soft > /sys/kernel/debug/ieee80211/phy0/ath10k/simulate_fw_crash;sleep 0.05;ifconfig wlan0 down
echo soft > /sys/kernel/debug/ieee80211/phy0/ath10k/simulate_fw_crash;rmmod ath10k_sdio
echo hw-restart > /sys/kernel/debug/ieee80211/phy0/ath10k/simulate_fw_crash;rmmod ath10k_pci
2. dmesg:
[ 5622.548630] ath10k_sdio mmc1:0001:1: simulating soft firmware crash
[ 5622.655995] ieee80211 phy0: Hardware restart was requested
[ 5776.355164] INFO: task shill:1572 blocked for more than 122 seconds.
[ 5776.355687] INFO: task kworker/1:2:24437 blocked for more than 122 seconds.
[ 5776.359812] Kernel panic - not syncing: hung_task: blocked tasks
[ 5776.359836] CPU: 1 PID: 55 Comm: khungtaskd Tainted: G W 4.19.86 #137
[ 5776.359846] Hardware name: MediaTek krane sku176 board (DT)
[ 5776.359855] Call trace:
[ 5776.359868] dump_backtrace+0x0/0x170
[ 5776.359881] show_stack+0x20/0x2c
[ 5776.359896] dump_stack+0xd4/0x10c
[ 5776.359916] panic+0x12c/0x29c
[ 5776.359937] hung_task_panic+0x0/0x50
[ 5776.359953] kthread+0x120/0x130
[ 5776.359965] ret_from_fork+0x10/0x18
[ 5776.359986] SMP: stopping secondary CPUs
[ 5776.360012] Kernel Offset: 0x141ea00000 from 0xffffff8008000000
[ 5776.360026] CPU features: 0x0,2188200c
[ 5776.360035] Memory Limit: none
command "ifconfig wlan0 down" or "rmmod ath10k_sdio" will be blocked
callstack of ifconfig:
[<0>] __switch_to+0x120/0x13c
[<0>] msleep+0x28/0x38
[<0>] ath10k_sdio_hif_stop+0x24c/0x294 [ath10k_sdio]
[<0>] ath10k_core_stop+0x50/0x78 [ath10k_core]
[<0>] ath10k_halt+0x120/0x178 [ath10k_core]
[<0>] ath10k_stop+0x4c/0x8c [ath10k_core]
[<0>] drv_stop+0xe0/0x1e4 [mac80211]
[<0>] ieee80211_stop_device+0x48/0x54 [mac80211]
[<0>] ieee80211_do_stop+0x678/0x6f8 [mac80211]
[<0>] ieee80211_stop+0x20/0x30 [mac80211]
[<0>] __dev_close_many+0xb8/0x11c
[<0>] __dev_change_flags+0xe0/0x1d0
[<0>] dev_change_flags+0x30/0x6c
[<0>] devinet_ioctl+0x370/0x564
[<0>] inet_ioctl+0xdc/0x304
[<0>] sock_do_ioctl+0x50/0x288
[<0>] compat_sock_ioctl+0x1b4/0x1aac
[<0>] __se_compat_sys_ioctl+0x100/0x26fc
[<0>] __arm64_compat_sys_ioctl+0x20/0x2c
[<0>] el0_svc_common+0xa4/0x154
[<0>] el0_svc_compat_handler+0x2c/0x38
[<0>] el0_svc_compat+0x8/0x18
[<0>] 0xffffffffffffffff
callstack of rmmod:
[<0>] __switch_to+0x120/0x13c
[<0>] msleep+0x28/0x38
[<0>] ath10k_sdio_hif_stop+0x294/0x31c [ath10k_sdio]
[<0>] ath10k_core_stop+0x50/0x78 [ath10k_core]
[<0>] ath10k_halt+0x120/0x178 [ath10k_core]
[<0>] ath10k_stop+0x4c/0x8c [ath10k_core]
[<0>] drv_stop+0xe0/0x1e4 [mac80211]
[<0>] ieee80211_stop_device+0x48/0x54 [mac80211]
[<0>] ieee80211_do_stop+0x678/0x6f8 [mac80211]
[<0>] ieee80211_stop+0x20/0x30 [mac80211]
[<0>] __dev_close_many+0xb8/0x11c
[<0>] dev_close_many+0x70/0x100
[<0>] dev_close+0x4c/0x80
[<0>] cfg80211_shutdown_all_interfaces+0x50/0xcc [cfg80211]
[<0>] ieee80211_remove_interfaces+0x58/0x1a0 [mac80211]
[<0>] ieee80211_unregister_hw+0x40/0x100 [mac80211]
[<0>] ath10k_mac_unregister+0x1c/0x44 [ath10k_core]
[<0>] ath10k_core_unregister+0x38/0x7c [ath10k_core]
[<0>] ath10k_sdio_remove+0x8c/0xd0 [ath10k_sdio]
[<0>] sdio_bus_remove+0x48/0x108
[<0>] device_release_driver_internal+0x138/0x1ec
[<0>] driver_detach+0x6c/0xa8
[<0>] bus_remove_driver+0x78/0xa8
[<0>] driver_unregister+0x30/0x50
[<0>] sdio_unregister_driver+0x28/0x34
[<0>] cleanup_module+0x14/0x6bc [ath10k_sdio]
[<0>] __arm64_sys_delete_module+0x1e0/0x22c
[<0>] el0_svc_common+0xa4/0x154
[<0>] el0_svc_compat_handler+0x2c/0x38
[<0>] el0_svc_compat+0x8/0x18
[<0>] 0xffffffffffffffff
SNOC:
[ 647.156863] Call trace:
[ 647.162166] [<ffffff80080855a4>] __switch_to+0x120/0x13c
[ 647.164512] [<ffffff800899d8b8>] __schedule+0x5ec/0x798
[ 647.170062] [<ffffff800899dad8>] schedule+0x74/0x94
[ 647.175050] [<ffffff80089a0848>] schedule_timeout+0x314/0x42c
[ 647.179874] [<ffffff80089a0a14>] schedule_timeout_uninterruptible+0x34/0x40
[ 647.185780] [<ffffff80082a494>] msleep+0x28/0x38
[ 647.192546] [<ffffff800117ec4c>] ath10k_snoc_hif_stop+0x4c/0x1e0 [ath10k_snoc]
[ 647.197439] [<ffffff80010dfbd8>] ath10k_core_stop+0x50/0x7c [ath10k_core]
[ 647.204652] [<ffffff80010c8f48>] ath10k_halt+0x114/0x16c [ath10k_core]
[ 647.211420] [<ffffff80010cad68>] ath10k_stop+0x4c/0x88 [ath10k_core]
[ 647.217865] [<ffffff8000fdbf54>] drv_stop+0x110/0x244 [mac80211]
[ 647.224367] [<ffffff80010147ac>] ieee80211_stop_device+0x48/0x54 [mac80211]
[ 647.230359] [<ffffff8000ff3eec>] ieee80211_do_stop+0x6a4/0x73c [mac80211]
[ 647.237033] [<ffffff8000ff4500>] ieee80211_stop+0x20/0x30 [mac80211]
[ 647.243942] [<ffffff80087e39b8>] __dev_close_many+0xa0/0xfc
[ 647.250435] [<ffffff80087e3888>] dev_close_many+0x70/0x100
[ 647.255651] [<ffffff80087e3a60>] dev_close+0x4c/0x80
[ 647.261244] [<ffffff8000f1ba54>] cfg80211_shutdown_all_interfaces+0x44/0xcc [cfg80211]
[ 647.266383] [<ffffff8000ff3fdc>] ieee80211_remove_interfaces+0x58/0x1b4 [mac80211]
[ 647.274128] [<ffffff8000fda540>] ieee80211_unregister_hw+0x50/0x120 [mac80211]
[ 647.281659] [<ffffff80010ca314>] ath10k_mac_unregister+0x1c/0x44 [ath10k_core]
[ 647.288839] [<ffffff80010dfc94>] ath10k_core_unregister+0x48/0x90 [ath10k_core]
[ 647.296027] [<ffffff800117e598>] ath10k_snoc_remove+0x5c/0x150 [ath10k_snoc]
[ 647.303229] [<ffffff80085625fc>] platform_drv_remove+0x28/0x50
[ 647.310517] [<ffffff80085601a4>] device_release_driver_internal+0x114/0x1b8
[ 647.316257] [<ffffff80085602e4>] driver_detach+0x6c/0xa8
[ 647.323021] [<ffffff800855e5b8>] bus_remove_driver+0x78/0xa8
[ 647.328571] [<ffffff800856107c>] driver_unregister+0x30/0x50
[ 647.334213] [<ffffff8008562674>] platform_driver_unregister+0x1c/0x28
[ 647.339876] [<ffffff800117fefc>] cleanup_module+0x1c/0x120 [ath10k_snoc]
[ 647.346196] [<ffffff8008143ab8>] SyS_delete_module+0x1dc/0x22c
PCIe:
[ 615.392770] rmmod D 0 3523 3458 0x00000080
[ 615.392777] Call Trace:
[ 615.392784] __schedule+0x617/0x7d3
[ 615.392791] ? __mod_timer+0x263/0x35c
[ 615.392797] schedule+0x62/0x72
[ 615.392803] schedule_timeout+0x8d/0xf3
[ 615.392809] ? run_local_timers+0x6b/0x6b
[ 615.392814] msleep+0x1b/0x22
[ 615.392824] ath10k_pci_hif_stop+0x68/0xd6 [ath10k_pci]
[ 615.392844] ath10k_core_stop+0x44/0x67 [ath10k_core]
[ 615.392859] ath10k_halt+0x102/0x153 [ath10k_core]
[ 615.392873] ath10k_stop+0x38/0x75 [ath10k_core]
[ 615.392893] drv_stop+0x9a/0x13c [mac80211]
[ 615.392915] ieee80211_do_stop+0x772/0x7cd [mac80211]
[ 615.392937] ieee80211_stop+0x1a/0x1e [mac80211]
[ 615.392945] __dev_close_many+0x9e/0xf0
[ 615.392952] dev_close_many+0x62/0xe8
[ 615.392958] dev_close+0x54/0x7d
[ 615.392975] cfg80211_shutdown_all_interfaces+0x6e/0xa5 [cfg80211]
[ 615.393021] ieee80211_remove_interfaces+0x52/0x1aa [mac80211]
[ 615.393049] ieee80211_unregister_hw+0x54/0x136 [mac80211]
[ 615.393068] ath10k_mac_unregister+0x19/0x4a [ath10k_core]
[ 615.393091] ath10k_core_unregister+0x39/0x7e [ath10k_core]
[ 615.393104] ath10k_pci_remove+0x3d/0x7f [ath10k_pci]
[ 615.393117] pci_device_remove+0x41/0xa6
[ 615.393129] device_release_driver_internal+0x123/0x1ec
[ 615.393140] driver_detach+0x60/0x90
[ 615.393152] bus_remove_driver+0x72/0x9f
[ 615.393164] pci_unregister_driver+0x1e/0x87
[ 615.393177] SyS_delete_module+0x1d7/0x277
[ 615.393188] do_syscall_64+0x6b/0xf7
[ 615.393199] entry_SYSCALL_64_after_hwframe+0x41/0xa6
The test command run simulate_fw_crash firstly and it call into
ath10k_sdio_hif_stop from ath10k_core_restart, then napi_disable
is called and bit NAPI_STATE_SCHED is set. After that, function
ath10k_sdio_hif_stop is called again from ath10k_stop by command
"ifconfig wlan0 down" or "rmmod ath10k_sdio", then command blocked.
It is blocked by napi_synchronize, napi_disable will set bit with
NAPI_STATE_SCHED, and then napi_synchronize will enter dead loop
becuase bit NAPI_STATE_SCHED is set by napi_disable.
function of napi_synchronize
static inline void napi_synchronize(const struct napi_struct *n)
{
if (IS_ENABLED(CONFIG_SMP))
while (test_bit(NAPI_STATE_SCHED, &n->state))
msleep(1);
else
barrier();
}
function of napi_disable
void napi_disable(struct napi_struct *n)
{
might_sleep();
set_bit(NAPI_STATE_DISABLE, &n->state);
while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
msleep(1);
while (test_and_set_bit(NAPI_STATE_NPSVC, &n->state))
msleep(1);
hrtimer_cancel(&n->timer);
clear_bit(NAPI_STATE_DISABLE, &n->state);
}
Add flag for it avoid the hang and crash.
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00049
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
Tested-on: WCN3990 hw1.0 SNOC hw1.0 WLAN.HL.3.1-01307.1-QCAHLSWMTPL-2
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1598617348-2325-1-git-send-email-wgong@codeaurora.org
2020-12-15 08:35:04 +02:00
|
|
|
ath10k_core_napi_sync_disable(ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_suspend(struct ath10k *ar)
|
|
|
|
{
|
2019-11-27 06:04:13 +00:00
|
|
|
return 0;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_resume(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
switch (ar->state) {
|
|
|
|
case ATH10K_STATE_OFF:
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio resume configuring sdio\n");
|
|
|
|
|
|
|
|
/* need to set sdio settings after power is cut from sdio */
|
|
|
|
ath10k_sdio_config(ar);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ATH10K_STATE_ON:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int ath10k_sdio_hif_map_service_to_pipe(struct ath10k *ar,
|
|
|
|
u16 service_id,
|
|
|
|
u8 *ul_pipe, u8 *dl_pipe)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
struct ath10k_htc *htc = &ar->htc;
|
|
|
|
u32 htt_addr, wmi_addr, htt_mbox_size, wmi_mbox_size;
|
|
|
|
enum ath10k_htc_ep_id eid;
|
|
|
|
bool ep_found = false;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* For sdio, we are interested in the mapping between eid
|
|
|
|
* and pipeid rather than service_id to pipe_id.
|
|
|
|
* First we find out which eid has been allocated to the
|
|
|
|
* service...
|
|
|
|
*/
|
|
|
|
for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
|
|
|
|
if (htc->endpoint[i].service_id == service_id) {
|
|
|
|
eid = htc->endpoint[i].eid;
|
|
|
|
ep_found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ep_found)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Then we create the simplest mapping possible between pipeid
|
|
|
|
* and eid
|
|
|
|
*/
|
|
|
|
*ul_pipe = *dl_pipe = (u8)eid;
|
|
|
|
|
|
|
|
/* Normally, HTT will use the upper part of the extended
|
|
|
|
* mailbox address space (ext_info[1].htc_ext_addr) and WMI ctrl
|
|
|
|
* the lower part (ext_info[0].htc_ext_addr).
|
|
|
|
* If fw wants swapping of mailbox addresses, the opposite is true.
|
|
|
|
*/
|
|
|
|
if (ar_sdio->swap_mbox) {
|
|
|
|
htt_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
|
|
|
|
wmi_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
|
|
|
|
htt_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
|
|
|
|
wmi_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
|
|
|
|
} else {
|
|
|
|
htt_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
|
|
|
|
wmi_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
|
|
|
|
htt_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
|
|
|
|
wmi_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (service_id) {
|
|
|
|
case ATH10K_HTC_SVC_ID_RSVD_CTRL:
|
|
|
|
/* HTC ctrl ep mbox address has already been setup in
|
|
|
|
* ath10k_sdio_hif_start
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
case ATH10K_HTC_SVC_ID_WMI_CONTROL:
|
|
|
|
ar_sdio->mbox_addr[eid] = wmi_addr;
|
|
|
|
ar_sdio->mbox_size[eid] = wmi_mbox_size;
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio wmi ctrl mbox_addr 0x%x mbox_size %d\n",
|
|
|
|
ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
|
|
|
|
break;
|
|
|
|
case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
|
|
|
|
ar_sdio->mbox_addr[eid] = htt_addr;
|
|
|
|
ar_sdio->mbox_size[eid] = htt_mbox_size;
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO,
|
|
|
|
"sdio htt data mbox_addr 0x%x mbox_size %d\n",
|
|
|
|
ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ath10k_warn(ar, "unsupported HTC service id: %d\n",
|
|
|
|
service_id);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_hif_get_default_pipe(struct ath10k *ar,
|
|
|
|
u8 *ul_pipe, u8 *dl_pipe)
|
|
|
|
{
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hif get default pipe\n");
|
|
|
|
|
|
|
|
/* HTC ctrl ep (SVC id 1) always has eid (and pipe_id in our
|
|
|
|
* case) == 0
|
|
|
|
*/
|
|
|
|
*ul_pipe = 0;
|
|
|
|
*dl_pipe = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct ath10k_hif_ops ath10k_sdio_hif_ops = {
|
|
|
|
.tx_sg = ath10k_sdio_hif_tx_sg,
|
|
|
|
.diag_read = ath10k_sdio_hif_diag_read,
|
|
|
|
.diag_write = ath10k_sdio_hif_diag_write_mem,
|
|
|
|
.exchange_bmi_msg = ath10k_sdio_bmi_exchange_msg,
|
|
|
|
.start = ath10k_sdio_hif_start,
|
|
|
|
.stop = ath10k_sdio_hif_stop,
|
2020-04-16 14:50:56 +03:00
|
|
|
.start_post = ath10k_sdio_hif_start_post,
|
ath10k: disable TX complete indication of htt for sdio
For sdio chip, it is high latency bus, all the TX packet's content will
be tranferred from HOST memory to firmware memory via sdio bus, then it
need much more memory in firmware than low latency bus chip, for low
latency chip, such as PCI-E, it only need to transfer the TX descriptor
via PCI-E bus to firmware memory. For sdio chip, reduce the complexity of
TX logic will help TX efficiency since its memory is limited, and it will
reduce the TX circle's time of each packet and then firmware will have more
memory for TX since TX complete also need memeory.
This patch disable TX complete indication from firmware for htt data
packet, it will not have TX complete indication from firmware to ath10k.
It will cut the cost of bus bandwidth of TX complete and make the TX
logic of firmware simpler, it results in significant performance
improvement on TX path.
Udp TX throughout is 130Mbps without this patch, and it arrives
400Mbps with this patch.
The downside of this patch is the command "iw wlan0 station dump" will
show 0 for "tx retries" and "tx failed" since all tx packet's status
is success.
This patch only effect sdio chip, it will not effect PCI, SNOC etc.
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWPZ-1
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200212080415.31265-2-wgong@codeaurora.org
2020-04-07 08:12:30 +03:00
|
|
|
.get_htt_tx_complete = ath10k_sdio_get_htt_tx_complete,
|
2017-04-26 12:18:00 +03:00
|
|
|
.map_service_to_pipe = ath10k_sdio_hif_map_service_to_pipe,
|
|
|
|
.get_default_pipe = ath10k_sdio_hif_get_default_pipe,
|
|
|
|
.power_up = ath10k_sdio_hif_power_up,
|
|
|
|
.power_down = ath10k_sdio_hif_power_down,
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.suspend = ath10k_sdio_hif_suspend,
|
|
|
|
.resume = ath10k_sdio_hif_resume,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
|
|
|
|
|
/* Empty handlers so that mmc subsystem doesn't remove us entirely during
|
|
|
|
* suspend. We instead follow cfg80211 suspend/resume handlers.
|
|
|
|
*/
|
|
|
|
static int ath10k_sdio_pm_suspend(struct device *device)
|
|
|
|
{
|
2019-11-27 06:04:13 +00:00
|
|
|
struct sdio_func *func = dev_to_sdio_func(device);
|
|
|
|
struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
|
|
|
|
struct ath10k *ar = ar_sdio->ar;
|
|
|
|
mmc_pm_flag_t pm_flag, pm_caps;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!device_may_wakeup(ar->dev))
|
|
|
|
return 0;
|
|
|
|
|
2020-04-16 14:50:58 +03:00
|
|
|
ath10k_sdio_set_mbox_sleep(ar, true);
|
2020-04-16 14:50:57 +03:00
|
|
|
|
2019-11-27 06:04:13 +00:00
|
|
|
pm_flag = MMC_PM_KEEP_POWER;
|
|
|
|
|
|
|
|
ret = sdio_set_host_pm_flags(func, pm_flag);
|
|
|
|
if (ret) {
|
|
|
|
pm_caps = sdio_get_host_pm_caps(func);
|
|
|
|
ath10k_warn(ar, "failed to set sdio host pm flags (0x%x, 0x%x): %d\n",
|
|
|
|
pm_flag, pm_caps, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_pm_resume(struct device *device)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend,
|
|
|
|
ath10k_sdio_pm_resume);
|
|
|
|
|
|
|
|
#define ATH10K_SDIO_PM_OPS (&ath10k_sdio_pm_ops)
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#define ATH10K_SDIO_PM_OPS NULL
|
|
|
|
|
|
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
|
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
static int ath10k_sdio_napi_poll(struct napi_struct *ctx, int budget)
|
|
|
|
{
|
|
|
|
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
|
|
|
|
int done;
|
|
|
|
|
|
|
|
done = ath10k_htt_rx_hl_indication(ar, budget);
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "napi poll: done: %d, budget:%d\n", done, budget);
|
|
|
|
|
|
|
|
if (done < budget)
|
|
|
|
napi_complete_done(ctx, done);
|
|
|
|
|
|
|
|
return done;
|
|
|
|
}
|
|
|
|
|
2020-08-18 17:12:02 +03:00
|
|
|
static int ath10k_sdio_read_host_interest_value(struct ath10k *ar,
|
|
|
|
u32 item_offset,
|
|
|
|
u32 *val)
|
|
|
|
{
|
|
|
|
u32 addr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
addr = host_interest_item_address(item_offset);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_diag_read32(ar, addr, val);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
ath10k_warn(ar, "unable to read host interest offset %d value\n",
|
|
|
|
item_offset);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_read_mem(struct ath10k *ar, u32 address, void *buf,
|
|
|
|
u32 buf_len)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
for (i = 0; i < buf_len; i += 4) {
|
|
|
|
ret = ath10k_sdio_diag_read32(ar, address + i, &val);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "unable to read mem %d value\n", address + i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
memcpy(buf + i, &val, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ath10k_sdio_is_fast_dump_supported(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
u32 param;
|
|
|
|
|
|
|
|
ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_option_flag2), ¶m);
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hi_option_flag2 %x\n", param);
|
|
|
|
|
2020-12-07 16:15:58 +02:00
|
|
|
return !!(param & HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW);
|
2020-08-18 17:12:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_dump_registers(struct ath10k *ar,
|
|
|
|
struct ath10k_fw_crash_data *crash_data,
|
|
|
|
bool fast_dump)
|
|
|
|
{
|
|
|
|
u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
|
|
|
|
int i, ret;
|
|
|
|
u32 reg_dump_area;
|
|
|
|
|
|
|
|
ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_failure_state),
|
|
|
|
®_dump_area);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read firmware dump area: %d\n", ret);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fast_dump)
|
|
|
|
ret = ath10k_bmi_read_memory(ar, reg_dump_area, reg_dump_values,
|
|
|
|
sizeof(reg_dump_values));
|
|
|
|
else
|
|
|
|
ret = ath10k_sdio_read_mem(ar, reg_dump_area, reg_dump_values,
|
|
|
|
sizeof(reg_dump_values));
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read firmware dump value: %d\n", ret);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ath10k_err(ar, "firmware register dump:\n");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(reg_dump_values); i += 4)
|
|
|
|
ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
|
|
|
|
i,
|
|
|
|
reg_dump_values[i],
|
|
|
|
reg_dump_values[i + 1],
|
|
|
|
reg_dump_values[i + 2],
|
|
|
|
reg_dump_values[i + 3]);
|
|
|
|
|
|
|
|
if (!crash_data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(reg_dump_values); i++)
|
|
|
|
crash_data->registers[i] = __cpu_to_le32(reg_dump_values[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ath10k_sdio_dump_memory_section(struct ath10k *ar,
|
|
|
|
const struct ath10k_mem_region *mem_region,
|
|
|
|
u8 *buf, size_t buf_len)
|
|
|
|
{
|
|
|
|
const struct ath10k_mem_section *cur_section, *next_section;
|
|
|
|
unsigned int count, section_size, skip_size;
|
|
|
|
int ret, i, j;
|
|
|
|
|
|
|
|
if (!mem_region || !buf)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cur_section = &mem_region->section_table.sections[0];
|
|
|
|
|
|
|
|
if (mem_region->start > cur_section->start) {
|
|
|
|
ath10k_warn(ar, "incorrect memdump region 0x%x with section start address 0x%x.\n",
|
|
|
|
mem_region->start, cur_section->start);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_size = cur_section->start - mem_region->start;
|
|
|
|
|
|
|
|
/* fill the gap between the first register section and register
|
|
|
|
* start address
|
|
|
|
*/
|
|
|
|
for (i = 0; i < skip_size; i++) {
|
|
|
|
*buf = ATH10K_MAGIC_NOT_COPIED;
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = 0;
|
2020-10-28 15:54:30 +02:00
|
|
|
i = 0;
|
|
|
|
for (; cur_section; cur_section = next_section) {
|
2020-08-18 17:12:02 +03:00
|
|
|
section_size = cur_section->end - cur_section->start;
|
|
|
|
|
|
|
|
if (section_size <= 0) {
|
|
|
|
ath10k_warn(ar, "incorrect ramdump format with start address 0x%x and stop address 0x%x\n",
|
|
|
|
cur_section->start,
|
|
|
|
cur_section->end);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-28 15:54:30 +02:00
|
|
|
if (++i == mem_region->section_table.size) {
|
2020-08-18 17:12:02 +03:00
|
|
|
/* last section */
|
|
|
|
next_section = NULL;
|
|
|
|
skip_size = 0;
|
|
|
|
} else {
|
|
|
|
next_section = cur_section + 1;
|
|
|
|
|
|
|
|
if (cur_section->end > next_section->start) {
|
|
|
|
ath10k_warn(ar, "next ramdump section 0x%x is smaller than current end address 0x%x\n",
|
|
|
|
next_section->start,
|
|
|
|
cur_section->end);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_size = next_section->start - cur_section->end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf_len < (skip_size + section_size)) {
|
|
|
|
ath10k_warn(ar, "ramdump buffer is too small: %zu\n", buf_len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf_len -= skip_size + section_size;
|
|
|
|
|
|
|
|
/* read section to dest memory */
|
|
|
|
ret = ath10k_sdio_read_mem(ar, cur_section->start,
|
|
|
|
buf, section_size);
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to read ramdump from section 0x%x: %d\n",
|
|
|
|
cur_section->start, ret);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += section_size;
|
|
|
|
count += section_size;
|
|
|
|
|
|
|
|
/* fill in the gap between this section and the next */
|
|
|
|
for (j = 0; j < skip_size; j++) {
|
|
|
|
*buf = ATH10K_MAGIC_NOT_COPIED;
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
|
|
|
|
count += skip_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if an error happened returns < 0, otherwise the length */
|
|
|
|
static int ath10k_sdio_dump_memory_generic(struct ath10k *ar,
|
|
|
|
const struct ath10k_mem_region *current_region,
|
|
|
|
u8 *buf,
|
|
|
|
bool fast_dump)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (current_region->section_table.size > 0)
|
|
|
|
/* Copy each section individually. */
|
|
|
|
return ath10k_sdio_dump_memory_section(ar,
|
|
|
|
current_region,
|
|
|
|
buf,
|
|
|
|
current_region->len);
|
|
|
|
|
2023-08-22 07:50:48 -07:00
|
|
|
/* No individual memory sections defined so we can
|
2020-08-18 17:12:02 +03:00
|
|
|
* copy the entire memory region.
|
|
|
|
*/
|
|
|
|
if (fast_dump)
|
|
|
|
ret = ath10k_bmi_read_memory(ar,
|
|
|
|
current_region->start,
|
|
|
|
buf,
|
|
|
|
current_region->len);
|
|
|
|
else
|
|
|
|
ret = ath10k_sdio_read_mem(ar,
|
|
|
|
current_region->start,
|
|
|
|
buf,
|
|
|
|
current_region->len);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
|
|
|
|
current_region->name, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return current_region->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_dump_memory(struct ath10k *ar,
|
|
|
|
struct ath10k_fw_crash_data *crash_data,
|
|
|
|
bool fast_dump)
|
|
|
|
{
|
|
|
|
const struct ath10k_hw_mem_layout *mem_layout;
|
|
|
|
const struct ath10k_mem_region *current_region;
|
|
|
|
struct ath10k_dump_ram_data_hdr *hdr;
|
|
|
|
u32 count;
|
|
|
|
size_t buf_len;
|
|
|
|
int ret, i;
|
|
|
|
u8 *buf;
|
|
|
|
|
|
|
|
if (!crash_data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mem_layout = ath10k_coredump_get_mem_layout(ar);
|
|
|
|
if (!mem_layout)
|
|
|
|
return;
|
|
|
|
|
|
|
|
current_region = &mem_layout->region_table.regions[0];
|
|
|
|
|
|
|
|
buf = crash_data->ramdump_buf;
|
|
|
|
buf_len = crash_data->ramdump_buf_len;
|
|
|
|
|
|
|
|
memset(buf, 0, buf_len);
|
|
|
|
|
|
|
|
for (i = 0; i < mem_layout->region_table.size; i++) {
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
if (current_region->len > buf_len) {
|
|
|
|
ath10k_warn(ar, "memory region %s size %d is larger that remaining ramdump buffer size %zu\n",
|
|
|
|
current_region->name,
|
|
|
|
current_region->len,
|
|
|
|
buf_len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reserve space for the header. */
|
|
|
|
hdr = (void *)buf;
|
|
|
|
buf += sizeof(*hdr);
|
|
|
|
buf_len -= sizeof(*hdr);
|
|
|
|
|
|
|
|
ret = ath10k_sdio_dump_memory_generic(ar, current_region, buf,
|
|
|
|
fast_dump);
|
|
|
|
if (ret >= 0)
|
|
|
|
count = ret;
|
|
|
|
|
|
|
|
hdr->region_type = cpu_to_le32(current_region->type);
|
|
|
|
hdr->start = cpu_to_le32(current_region->start);
|
|
|
|
hdr->length = cpu_to_le32(count);
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
/* Note: the header remains, just with zero length. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
buf += count;
|
|
|
|
buf_len -= count;
|
|
|
|
|
|
|
|
current_region++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ath10k_sdio_fw_crashed_dump(struct ath10k *ar)
|
|
|
|
{
|
|
|
|
struct ath10k_fw_crash_data *crash_data;
|
|
|
|
char guid[UUID_STRING_LEN + 1];
|
|
|
|
bool fast_dump;
|
|
|
|
|
|
|
|
fast_dump = ath10k_sdio_is_fast_dump_supported(ar);
|
|
|
|
|
|
|
|
if (fast_dump)
|
|
|
|
ath10k_bmi_start(ar);
|
|
|
|
|
|
|
|
ar->stats.fw_crash_counter++;
|
|
|
|
|
|
|
|
ath10k_sdio_disable_intrs(ar);
|
|
|
|
|
|
|
|
crash_data = ath10k_coredump_new(ar);
|
|
|
|
|
|
|
|
if (crash_data)
|
|
|
|
scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid);
|
|
|
|
else
|
|
|
|
scnprintf(guid, sizeof(guid), "n/a");
|
|
|
|
|
|
|
|
ath10k_err(ar, "firmware crashed! (guid %s)\n", guid);
|
|
|
|
ath10k_print_driver_info(ar);
|
|
|
|
ath10k_sdio_dump_registers(ar, crash_data, fast_dump);
|
|
|
|
ath10k_sdio_dump_memory(ar, crash_data, fast_dump);
|
|
|
|
|
|
|
|
ath10k_sdio_enable_intrs(ar);
|
|
|
|
|
2020-09-08 04:13:06 +00:00
|
|
|
ath10k_core_start_recovery(ar);
|
2020-08-18 17:12:02 +03:00
|
|
|
}
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
static int ath10k_sdio_probe(struct sdio_func *func,
|
|
|
|
const struct sdio_device_id *id)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio;
|
|
|
|
struct ath10k *ar;
|
|
|
|
enum ath10k_hw_rev hw_rev;
|
2018-09-04 15:03:19 +03:00
|
|
|
u32 dev_id_base;
|
2019-04-19 10:28:52 +03:00
|
|
|
struct ath10k_bus_params bus_params = {};
|
2017-04-26 12:18:00 +03:00
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
/* Assumption: All SDIO based chipsets (so far) are QCA6174 based.
|
|
|
|
* If there will be newer chipsets that does not use the hw reg
|
|
|
|
* setup as defined in qca6174_regs and qca6174_values, this
|
|
|
|
* assumption is no longer valid and hw_rev must be setup differently
|
|
|
|
* depending on chipset.
|
|
|
|
*/
|
|
|
|
hw_rev = ATH10K_HW_QCA6174;
|
|
|
|
|
|
|
|
ar = ath10k_core_create(sizeof(*ar_sdio), &func->dev, ATH10K_BUS_SDIO,
|
|
|
|
hw_rev, &ath10k_sdio_hif_ops);
|
|
|
|
if (!ar) {
|
|
|
|
dev_err(&func->dev, "failed to allocate core\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2024-04-22 05:39:02 -07:00
|
|
|
netif_napi_add(ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll);
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
ath10k_dbg(ar, ATH10K_DBG_BOOT,
|
|
|
|
"sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
|
|
|
|
func->num, func->vendor, func->device,
|
|
|
|
func->max_blksize, func->cur_blksize);
|
|
|
|
|
|
|
|
ar_sdio = ath10k_sdio_priv(ar);
|
|
|
|
|
|
|
|
ar_sdio->irq_data.irq_proc_reg =
|
2018-03-27 22:31:44 +02:00
|
|
|
devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_proc_regs),
|
|
|
|
GFP_KERNEL);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (!ar_sdio->irq_data.irq_proc_reg) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_core_destroy;
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:20:58 +02:00
|
|
|
ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL);
|
|
|
|
if (!ar_sdio->vsg_buffer) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_core_destroy;
|
|
|
|
}
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
ar_sdio->irq_data.irq_en_reg =
|
2018-03-27 22:31:44 +02:00
|
|
|
devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
|
|
|
|
GFP_KERNEL);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (!ar_sdio->irq_data.irq_en_reg) {
|
|
|
|
ret = -ENOMEM;
|
2018-03-27 22:31:44 +02:00
|
|
|
goto err_core_destroy;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
2019-11-15 09:21:35 +02:00
|
|
|
ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_LARGE_CMDBUF_SIZE, GFP_KERNEL);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (!ar_sdio->bmi_buf) {
|
|
|
|
ret = -ENOMEM;
|
2018-03-27 22:31:44 +02:00
|
|
|
goto err_core_destroy;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ar_sdio->func = func;
|
|
|
|
sdio_set_drvdata(func, ar_sdio);
|
|
|
|
|
|
|
|
ar_sdio->is_disabled = true;
|
|
|
|
ar_sdio->ar = ar;
|
|
|
|
|
|
|
|
spin_lock_init(&ar_sdio->lock);
|
|
|
|
spin_lock_init(&ar_sdio->wr_async_lock);
|
|
|
|
mutex_init(&ar_sdio->irq_data.mtx);
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
|
|
|
|
INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
|
|
|
|
|
|
|
|
INIT_WORK(&ar_sdio->wr_async_work, ath10k_sdio_write_async_work);
|
|
|
|
ar_sdio->workqueue = create_singlethread_workqueue("ath10k_sdio_wq");
|
|
|
|
if (!ar_sdio->workqueue) {
|
|
|
|
ret = -ENOMEM;
|
2018-03-27 22:31:44 +02:00
|
|
|
goto err_core_destroy;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++)
|
|
|
|
ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]);
|
|
|
|
|
2019-11-15 09:21:03 +02:00
|
|
|
skb_queue_head_init(&ar_sdio->rx_head);
|
|
|
|
INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
|
|
|
|
|
2020-05-22 16:44:09 +02:00
|
|
|
dev_id_base = (id->device & 0x0F00);
|
|
|
|
if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
|
|
|
|
dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
|
2017-04-26 12:18:00 +03:00
|
|
|
ret = -ENODEV;
|
|
|
|
ath10k_err(ar, "unsupported device id %u (0x%x)\n",
|
|
|
|
dev_id_base, id->device);
|
2018-04-26 14:35:02 +02:00
|
|
|
goto err_free_wq;
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
2020-05-22 16:44:09 +02:00
|
|
|
ar->dev_id = QCA9377_1_0_DEVICE_ID;
|
2017-04-26 12:18:00 +03:00
|
|
|
ar->id.vendor = id->vendor;
|
|
|
|
ar->id.device = id->device;
|
|
|
|
|
|
|
|
ath10k_sdio_set_mbox_info(ar);
|
|
|
|
|
2018-09-04 15:03:44 +03:00
|
|
|
bus_params.dev_type = ATH10K_DEV_TYPE_HL;
|
2017-04-26 12:18:00 +03:00
|
|
|
/* TODO: don't know yet how to get chip_id with SDIO */
|
2018-09-04 15:03:19 +03:00
|
|
|
bus_params.chip_id = 0;
|
2019-04-19 10:28:54 +03:00
|
|
|
bus_params.hl_msdu_ids = true;
|
|
|
|
|
2019-11-27 02:59:29 +00:00
|
|
|
ar->hw->max_mtu = ETH_DATA_LEN;
|
|
|
|
|
2018-09-04 15:03:19 +03:00
|
|
|
ret = ath10k_core_register(ar, &bus_params);
|
2017-04-26 12:18:00 +03:00
|
|
|
if (ret) {
|
|
|
|
ath10k_err(ar, "failed to register driver core: %d\n", ret);
|
|
|
|
goto err_free_wq;
|
|
|
|
}
|
|
|
|
|
2020-04-16 14:50:57 +03:00
|
|
|
timer_setup(&ar_sdio->sleep_timer, ath10k_sdio_sleep_timer_handler, 0);
|
|
|
|
|
2017-04-26 12:18:00 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_free_wq:
|
|
|
|
destroy_workqueue(ar_sdio->workqueue);
|
|
|
|
err_core_destroy:
|
|
|
|
ath10k_core_destroy(ar);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ath10k_sdio_remove(struct sdio_func *func)
|
|
|
|
{
|
|
|
|
struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func);
|
|
|
|
struct ath10k *ar = ar_sdio->ar;
|
|
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_BOOT,
|
|
|
|
"sdio removed func %d vendor 0x%x device 0x%x\n",
|
|
|
|
func->num, func->vendor, func->device);
|
|
|
|
|
|
|
|
ath10k_core_unregister(ar);
|
ath10k: enable napi on RX path for sdio
For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity
of tcp data from remote, then it will have many small length packets
on TX path of sdio bus, then it reduce the RX packets's bandwidth of
tcp.
This patch enable napi on RX path, then the RX packet of tcp will not
feed to tcp stack immeditely from mac80211 since GRO is enabled by
default, it will feed to tcp stack after napi complete, if rx bundle
is enabled, then it will feed to tcp stack one time for each bundle
of RX. For example, RX bundle size is 32, then tcp stack will receive
one large length packet, its length is neary 1500*32, then tcp stack
will send a tcp ack for this large packet, this will reduce the tcp
acks ratio from 1/2 to 1/32. This results in significant performance
improvement for tcp RX.
Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps
with this patch. The cpu usage has no obvious difference with and
without NAPI.
call stack for each RX packet on GRO path:
(skb length is about 1500 bytes)
skb_gro_receive ([kernel.kallsyms])
tcp4_gro_receive ([kernel.kallsyms])
inet_gro_receive ([kernel.kallsyms])
dev_gro_receive ([kernel.kallsyms])
napi_gro_receive ([kernel.kallsyms])
ieee80211_deliver_skb ([mac80211])
ieee80211_rx_handlers ([mac80211])
ieee80211_prepare_and_rx_handle ([mac80211])
ieee80211_rx_napi ([mac80211])
ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core])
ath10k_htt_rx_pktlog_completion_handler ([ath10k_core])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
call stack for napi complete and send tcp ack from tcp stack:
(skb length is about 1500*32 bytes)
_tcp_ack_snd_check ([kernel.kallsyms])
tcp_v4_do_rcv ([kernel.kallsyms])
tcp_v4_rcv ([kernel.kallsyms])
local_deliver_finish ([kernel.kallsyms])
ip_local_deliver ([kernel.kallsyms])
ip_rcv_finish ([kernel.kallsyms])
ip_rcv ([kernel.kallsyms])
netif_receive_skb_core ([kernel.kallsyms])
netif_receive_skb_one_core([kernel.kallsyms])
netif_receive_skb ([kernel.kallsyms])
netif_receive_skb_internal ([kernel.kallsyms])
napi_gro_complete ([kernel.kallsyms])
napi_gro_flush ([kernel.kallsyms])
napi_complete_done ([kernel.kallsyms])
ath10k_sdio_napi_poll ([ath10k_sdio])
net_rx_action ([kernel.kallsyms])
__softirqentry_text_start ([kernel.kallsyms])
do_softirq ([kernel.kallsyms])
Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-11-29 09:59:24 +02:00
|
|
|
|
|
|
|
netif_napi_del(&ar->napi);
|
|
|
|
|
2019-06-27 21:21:51 +03:00
|
|
|
destroy_workqueue(ar_sdio->workqueue);
|
wifi: ath10k: avoid NULL pointer error during sdio remove
When running 'rmmod ath10k', ath10k_sdio_remove() will free sdio
workqueue by destroy_workqueue(). But if CONFIG_INIT_ON_FREE_DEFAULT_ON
is set to yes, kernel panic will happen:
Call trace:
destroy_workqueue+0x1c/0x258
ath10k_sdio_remove+0x84/0x94
sdio_bus_remove+0x50/0x16c
device_release_driver_internal+0x188/0x25c
device_driver_detach+0x20/0x2c
This is because during 'rmmod ath10k', ath10k_sdio_remove() will call
ath10k_core_destroy() before destroy_workqueue(). wiphy_dev_release()
will finally be called in ath10k_core_destroy(). This function will free
struct cfg80211_registered_device *rdev and all its members, including
wiphy, dev and the pointer of sdio workqueue. Then the pointer of sdio
workqueue will be set to NULL due to CONFIG_INIT_ON_FREE_DEFAULT_ON.
After device release, destroy_workqueue() will use NULL pointer then the
kernel panic happen.
Call trace:
ath10k_sdio_remove
->ath10k_core_unregister
……
->ath10k_core_stop
->ath10k_hif_stop
->ath10k_sdio_irq_disable
->ath10k_hif_power_down
->del_timer_sync(&ar_sdio->sleep_timer)
->ath10k_core_destroy
->ath10k_mac_destroy
->ieee80211_free_hw
->wiphy_free
……
->wiphy_dev_release
->destroy_workqueue
Need to call destroy_workqueue() before ath10k_core_destroy(), free
the work queue buffer first and then free pointer of work queue by
ath10k_core_destroy(). This order matches the error path order in
ath10k_sdio_probe().
No work will be queued on sdio workqueue between it is destroyed and
ath10k_core_destroy() is called. Based on the call_stack above, the
reason is:
Only ath10k_sdio_sleep_timer_handler(), ath10k_sdio_hif_tx_sg() and
ath10k_sdio_irq_disable() will queue work on sdio workqueue.
Sleep timer will be deleted before ath10k_core_destroy() in
ath10k_hif_power_down().
ath10k_sdio_irq_disable() only be called in ath10k_hif_stop().
ath10k_core_unregister() will call ath10k_hif_power_down() to stop hif
bus, so ath10k_sdio_hif_tx_sg() won't be called anymore.
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189
Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
Tested-by: David Ruth <druth@chromium.org>
Reviewed-by: David Ruth <druth@chromium.org>
Link: https://patch.msgid.link/20241008022246.1010-1-quic_kangyang@quicinc.com
Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
2024-10-08 10:22:46 +08:00
|
|
|
|
|
|
|
ath10k_core_destroy(ar);
|
2017-04-26 12:18:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct sdio_device_id ath10k_sdio_devices[] = {
|
2020-05-22 16:44:09 +02:00
|
|
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
|
|
|
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
|
2017-04-26 12:18:00 +03:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(sdio, ath10k_sdio_devices);
|
|
|
|
|
|
|
|
static struct sdio_driver ath10k_sdio_driver = {
|
|
|
|
.name = "ath10k_sdio",
|
|
|
|
.id_table = ath10k_sdio_devices,
|
|
|
|
.probe = ath10k_sdio_probe,
|
|
|
|
.remove = ath10k_sdio_remove,
|
2019-01-29 15:14:48 -08:00
|
|
|
.drv = {
|
|
|
|
.pm = ATH10K_SDIO_PM_OPS,
|
|
|
|
},
|
2017-04-26 12:18:00 +03:00
|
|
|
};
|
2024-04-03 16:16:52 +02:00
|
|
|
module_sdio_driver(ath10k_sdio_driver);
|
2017-04-26 12:18:00 +03:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Qualcomm Atheros");
|
|
|
|
MODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN SDIO devices");
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|