Skip to content

Commit 38c8d02

Browse files
benzeaKalle Valo
authored andcommitted
wifi: iwlwifi: correctly lookup DMA address in SG table
The code to lookup the scatter gather table entry assumed that it was possible to use sg_virt() in order to lookup the DMA address in a mapped scatter gather table. However, this assumption is incorrect as the DMA mapping code may merge multiple entries into one. In that case, the DMA address space may have e.g. two consecutive pages which is correctly represented by the scatter gather list entry, however the virtual addresses for these two pages may differ and the relationship cannot be resolved anymore. Avoid this problem entirely by working with the offset into the mapped area instead of using virtual addresses. With that we only use the DMA length and DMA address from the scatter gather list entries. The underlying DMA/IOMMU code is therefore free to merge two entries into one even if the virtual addresses space for the area is not continuous. Fixes: 90db507 ("wifi: iwlwifi: use already mapped data when TXing an AMSDU") Reported-by: Chris Bainbridge <chris.bainbridge@gmail.com> Closes: https://lore.kernel.org/r/ZrNRoEbdkxkKFMBi@debian.local Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Tested-by: Chris Bainbridge <chris.bainbridge@gmail.com> Signed-off-by: Kalle Valo <kvalo@kernel.org> Link: https://patch.msgid.link/20240812110640.460514-1-benjamin@sipsolutions.net
1 parent 479ffee commit 38c8d02

3 files changed

Lines changed: 28 additions & 12 deletions

File tree

drivers/net/wireless/intel/iwlwifi/pcie/internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,8 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
639639
int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq,
640640
int slots_num, bool cmd_queue);
641641

642-
dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, void *addr);
642+
dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
643+
unsigned int len);
643644
struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
644645
struct iwl_cmd_meta *cmd_meta,
645646
u8 **hdr, unsigned int hdr_room);

drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
168168
struct ieee80211_hdr *hdr = (void *)skb->data;
169169
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
170170
unsigned int mss = skb_shinfo(skb)->gso_size;
171+
unsigned int data_offset = 0;
171172
dma_addr_t start_hdr_phys;
172173
u16 length, amsdu_pad;
173174
u8 *start_hdr;
@@ -260,7 +261,8 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
260261
int ret;
261262

262263
tb_len = min_t(unsigned int, tso.size, data_left);
263-
tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, tso.data);
264+
tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, data_offset,
265+
tb_len);
264266
/* Not a real mapping error, use direct comparison */
265267
if (unlikely(tb_phys == DMA_MAPPING_ERROR))
266268
goto out_err;
@@ -272,6 +274,7 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans,
272274
goto out_err;
273275

274276
data_left -= tb_len;
277+
data_offset += tb_len;
275278
tso_build_data(skb, &tso, tb_len);
276279
}
277280
}

drivers/net/wireless/intel/iwlwifi/pcie/tx.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,23 +1814,31 @@ static void *iwl_pcie_get_page_hdr(struct iwl_trans *trans,
18141814
/**
18151815
* iwl_pcie_get_sgt_tb_phys - Find TB address in mapped SG list
18161816
* @sgt: scatter gather table
1817-
* @addr: Virtual address
1817+
* @offset: Offset into the mapped memory (i.e. SKB payload data)
1818+
* @len: Length of the area
18181819
*
1819-
* Find the entry that includes the address for the given address and return
1820-
* correct physical address for the TB entry.
1820+
* Find the DMA address that corresponds to the SKB payload data at the
1821+
* position given by @offset.
18211822
*
18221823
* Returns: Address for TB entry
18231824
*/
1824-
dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, void *addr)
1825+
dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset,
1826+
unsigned int len)
18251827
{
18261828
struct scatterlist *sg;
1829+
unsigned int sg_offset = 0;
18271830
int i;
18281831

1832+
/*
1833+
* Search the mapped DMA areas in the SG for the area that contains the
1834+
* data at offset with the given length.
1835+
*/
18291836
for_each_sgtable_dma_sg(sgt, sg, i) {
1830-
if (addr >= sg_virt(sg) &&
1831-
(u8 *)addr < (u8 *)sg_virt(sg) + sg_dma_len(sg))
1832-
return sg_dma_address(sg) +
1833-
((unsigned long)addr - (unsigned long)sg_virt(sg));
1837+
if (offset >= sg_offset &&
1838+
offset + len <= sg_offset + sg_dma_len(sg))
1839+
return sg_dma_address(sg) + offset - sg_offset;
1840+
1841+
sg_offset += sg_dma_len(sg);
18341842
}
18351843

18361844
WARN_ON_ONCE(1);
@@ -1875,7 +1883,9 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb,
18751883

18761884
sg_init_table(sgt->sgl, skb_shinfo(skb)->nr_frags + 1);
18771885

1878-
sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, 0, skb->len);
1886+
/* Only map the data, not the header (it is copied to the TSO page) */
1887+
sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, skb_headlen(skb),
1888+
skb->data_len);
18791889
if (WARN_ON_ONCE(sgt->orig_nents <= 0))
18801890
return NULL;
18811891

@@ -1900,6 +1910,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
19001910
struct ieee80211_hdr *hdr = (void *)skb->data;
19011911
unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
19021912
unsigned int mss = skb_shinfo(skb)->gso_size;
1913+
unsigned int data_offset = 0;
19031914
u16 length, iv_len, amsdu_pad;
19041915
dma_addr_t start_hdr_phys;
19051916
u8 *start_hdr, *pos_hdr;
@@ -2000,7 +2011,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
20002011
data_left);
20012012
dma_addr_t tb_phys;
20022013

2003-
tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, tso.data);
2014+
tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, data_offset, size);
20042015
/* Not a real mapping error, use direct comparison */
20052016
if (unlikely(tb_phys == DMA_MAPPING_ERROR))
20062017
return -EINVAL;
@@ -2011,6 +2022,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
20112022
tb_phys, size);
20122023

20132024
data_left -= size;
2025+
data_offset += size;
20142026
tso_build_data(skb, &tso, size);
20152027
}
20162028
}

0 commit comments

Comments
 (0)