Skip to content

Commit 3805578

Browse files
Baochen Qiangkvalo
authored andcommitted
wifi: ath12k: use 128 bytes aligned iova in transmit path for WCN7850
In transmit path, it is likely that the iova is not aligned to PCIe TLP max payload size, which is 128 for WCN7850. Normally in such cases hardware is expected to split the packet into several parts in a manner such that they, other than the first one, have aligned iova. However due to hardware limitations, WCN7850 does not behave like that properly with some specific unaligned iova in transmit path. This easily results in target hang in a KPI transmit test: packet send/receive failure, WMI command send timeout etc. Also fatal error seen in PCIe level: ... Capabilities: ... ... DevSta: ... FatalErr+ ... ... ... Work around this by manually moving/reallocating payload buffer such that we can map it to a 128 bytes aligned iova. The moving requires sufficient head room or tail room in skb: for the former we can do ourselves a favor by asking some extra bytes when registering with mac80211, while for the latter we can do nothing. Moving/reallocating buffer consumes additional CPU cycles, but the good news is that an aligned iova increases PCIe efficiency. In my tests on some X86 platforms the KPI results are almost consistent. Since this is seen only with WCN7850, add a new hardware parameter to differentiate from others. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Baochen Qiang <[email protected]> Cc: <[email protected]> Tested-by: Mark Pearson <[email protected]> Signed-off-by: Kalle Valo <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent de9c2c6 commit 3805578

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

drivers/net/wireless/ath/ath12k/dp_tx.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,60 @@ static int ath12k_dp_prepare_htt_metadata(struct sk_buff *skb)
162162
return 0;
163163
}
164164

165+
static void ath12k_dp_tx_move_payload(struct sk_buff *skb,
166+
unsigned long delta,
167+
bool head)
168+
{
169+
unsigned long len = skb->len;
170+
171+
if (head) {
172+
skb_push(skb, delta);
173+
memmove(skb->data, skb->data + delta, len);
174+
skb_trim(skb, len);
175+
} else {
176+
skb_put(skb, delta);
177+
memmove(skb->data + delta, skb->data, len);
178+
skb_pull(skb, delta);
179+
}
180+
}
181+
182+
static int ath12k_dp_tx_align_payload(struct ath12k_base *ab,
183+
struct sk_buff **pskb)
184+
{
185+
u32 iova_mask = ab->hw_params->iova_mask;
186+
unsigned long offset, delta1, delta2;
187+
struct sk_buff *skb2, *skb = *pskb;
188+
unsigned int headroom = skb_headroom(skb);
189+
int tailroom = skb_tailroom(skb);
190+
int ret = 0;
191+
192+
offset = (unsigned long)skb->data & iova_mask;
193+
delta1 = offset;
194+
delta2 = iova_mask - offset + 1;
195+
196+
if (headroom >= delta1) {
197+
ath12k_dp_tx_move_payload(skb, delta1, true);
198+
} else if (tailroom >= delta2) {
199+
ath12k_dp_tx_move_payload(skb, delta2, false);
200+
} else {
201+
skb2 = skb_realloc_headroom(skb, iova_mask);
202+
if (!skb2) {
203+
ret = -ENOMEM;
204+
goto out;
205+
}
206+
207+
dev_kfree_skb_any(skb);
208+
209+
offset = (unsigned long)skb2->data & iova_mask;
210+
if (offset)
211+
ath12k_dp_tx_move_payload(skb2, offset, true);
212+
*pskb = skb2;
213+
}
214+
215+
out:
216+
return ret;
217+
}
218+
165219
int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif,
166220
struct sk_buff *skb)
167221
{
@@ -184,6 +238,7 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif,
184238
bool tcl_ring_retry;
185239
bool msdu_ext_desc = false;
186240
bool add_htt_metadata = false;
241+
u32 iova_mask = ab->hw_params->iova_mask;
187242

188243
if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
189244
return -ESHUTDOWN;
@@ -279,6 +334,23 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif,
279334
goto fail_remove_tx_buf;
280335
}
281336

337+
if (iova_mask &&
338+
(unsigned long)skb->data & iova_mask) {
339+
ret = ath12k_dp_tx_align_payload(ab, &skb);
340+
if (ret) {
341+
ath12k_warn(ab, "failed to align TX buffer %d\n", ret);
342+
/* don't bail out, give original buffer
343+
* a chance even unaligned.
344+
*/
345+
goto map;
346+
}
347+
348+
/* hdr is pointing to a wrong place after alignment,
349+
* so refresh it for later use.
350+
*/
351+
hdr = (void *)skb->data;
352+
}
353+
map:
282354
ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE);
283355
if (dma_mapping_error(ab->dev, ti.paddr)) {
284356
atomic_inc(&ab->soc_stats.tx_err.misc_fail);

drivers/net/wireless/ath/ath12k/hw.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
924924

925925
.acpi_guid = NULL,
926926
.supports_dynamic_smps_6ghz = true,
927+
928+
.iova_mask = 0,
927929
},
928930
{
929931
.name = "wcn7850 hw2.0",
@@ -1000,6 +1002,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
10001002

10011003
.acpi_guid = &wcn7850_uuid,
10021004
.supports_dynamic_smps_6ghz = false,
1005+
1006+
.iova_mask = ATH12K_PCIE_MAX_PAYLOAD_SIZE - 1,
10031007
},
10041008
{
10051009
.name = "qcn9274 hw2.0",
@@ -1072,6 +1076,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
10721076

10731077
.acpi_guid = NULL,
10741078
.supports_dynamic_smps_6ghz = true,
1079+
1080+
.iova_mask = 0,
10751081
},
10761082
};
10771083

drivers/net/wireless/ath/ath12k/hw.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@
9696
#define ATH12K_M3_FILE "m3.bin"
9797
#define ATH12K_REGDB_FILE_NAME "regdb.bin"
9898

99+
#define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128
100+
99101
enum ath12k_hw_rate_cck {
100102
ATH12K_HW_RATE_CCK_LP_11M = 0,
101103
ATH12K_HW_RATE_CCK_LP_5_5M,
@@ -215,6 +217,8 @@ struct ath12k_hw_params {
215217

216218
const guid_t *acpi_guid;
217219
bool supports_dynamic_smps_6ghz;
220+
221+
u32 iova_mask;
218222
};
219223

220224
struct ath12k_hw_ops {

drivers/net/wireless/ath/ath12k/mac.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9193,6 +9193,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
91939193

91949194
hw->vif_data_size = sizeof(struct ath12k_vif);
91959195
hw->sta_data_size = sizeof(struct ath12k_sta);
9196+
hw->extra_tx_headroom = ab->hw_params->iova_mask;
91969197

91979198
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
91989199
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);

0 commit comments

Comments
 (0)