Message ID | 20240812110640.460514-1-benjamin@sipsolutions.net (mailing list archive) |
---|---|
State | Accepted |
Commit | 38c8d02501c09454e4fbf0f67de03de35e94d384 |
Delegated to: | Kalle Valo |
Headers | show |
Series | [v2] wifi: iwlwifi: correctly lookup DMA address in SG table | expand |
Benjamin Berg <benjamin@sipsolutions.net> writes: > From: Benjamin Berg <benjamin.berg@intel.com> > > 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: 90db50755228 ("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> > > --- > > We also verified the (previous) patch internally and it is working well > there now. > > v2: Add proper bounds checking by verifying the length Excellent, thank you Benjamin for working on this. Miri, can I have your ack? I would like to apply this tomorrow so that it makes it still this week's wireless pull request.
On Mon, 12 Aug 2024 at 12:07, Benjamin Berg <benjamin@sipsolutions.net> wrote: > [...] Works for me. Tested-by: Chris Bainbridge <chris.bainbridge@gmail.com> (btw Benjamin, did you see my other email? Subject "iwlwifi: WRT: Invalid buffer destination".)
Benjamin Berg <benjamin@sipsolutions.net> wrote: > From: Benjamin Berg <benjamin.berg@intel.com> > > 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: 90db50755228 ("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> Patch applied to wireless.git, thanks. 38c8d02501c0 wifi: iwlwifi: correctly lookup DMA address in SG table
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index b59de4f80b4b..27a7e0b5b3d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -639,7 +639,8 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num, bool cmd_queue); -dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, void *addr); +dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, + unsigned int len); struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta, u8 **hdr, unsigned int hdr_room); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 2e780fb2da42..b1846abb99b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -168,6 +168,7 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans, struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int data_offset = 0; dma_addr_t start_hdr_phys; u16 length, amsdu_pad; u8 *start_hdr; @@ -260,7 +261,8 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans, int ret; tb_len = min_t(unsigned int, tso.size, data_left); - tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, tso.data); + tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, data_offset, + tb_len); /* Not a real mapping error, use direct comparison */ if (unlikely(tb_phys == DMA_MAPPING_ERROR)) goto out_err; @@ -272,6 +274,7 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans, goto out_err; data_left -= tb_len; + data_offset += tb_len; tso_build_data(skb, &tso, tb_len); } } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 22d482ae53d9..9fe050f0ddc1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1814,23 +1814,31 @@ static void *iwl_pcie_get_page_hdr(struct iwl_trans *trans, /** * iwl_pcie_get_sgt_tb_phys - Find TB address in mapped SG list * @sgt: scatter gather table - * @addr: Virtual address + * @offset: Offset into the mapped memory (i.e. SKB payload data) + * @len: Length of the area * - * Find the entry that includes the address for the given address and return - * correct physical address for the TB entry. + * Find the DMA address that corresponds to the SKB payload data at the + * position given by @offset. * * Returns: Address for TB entry */ -dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, void *addr) +dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, + unsigned int len) { struct scatterlist *sg; + unsigned int sg_offset = 0; int i; + /* + * Search the mapped DMA areas in the SG for the area that contains the + * data at offset with the given length. + */ for_each_sgtable_dma_sg(sgt, sg, i) { - if (addr >= sg_virt(sg) && - (u8 *)addr < (u8 *)sg_virt(sg) + sg_dma_len(sg)) - return sg_dma_address(sg) + - ((unsigned long)addr - (unsigned long)sg_virt(sg)); + if (offset >= sg_offset && + offset + len <= sg_offset + sg_dma_len(sg)) + return sg_dma_address(sg) + offset - sg_offset; + + sg_offset += sg_dma_len(sg); } WARN_ON_ONCE(1); @@ -1875,7 +1883,9 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, sg_init_table(sgt->sgl, skb_shinfo(skb)->nr_frags + 1); - sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, 0, skb->len); + /* Only map the data, not the header (it is copied to the TSO page) */ + sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, skb_headlen(skb), + skb->data_len); if (WARN_ON_ONCE(sgt->orig_nents <= 0)) return NULL; @@ -1900,6 +1910,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int data_offset = 0; u16 length, iv_len, amsdu_pad; dma_addr_t start_hdr_phys; u8 *start_hdr, *pos_hdr; @@ -2000,7 +2011,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, data_left); dma_addr_t tb_phys; - tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, tso.data); + tb_phys = iwl_pcie_get_sgt_tb_phys(sgt, data_offset, size); /* Not a real mapping error, use direct comparison */ if (unlikely(tb_phys == DMA_MAPPING_ERROR)) return -EINVAL; @@ -2011,6 +2022,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, tb_phys, size); data_left -= size; + data_offset += size; tso_build_data(skb, &tso, size); } }