<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css" style="display:none;"><!-- P {margin-top:0;margin-bottom:0;} --></style>
</head>
<body dir="ltr">
<div id="divtagdefaultwrapper" style="font-size:12pt;color:#000000;font-family:Calibri,Helvetica,sans-serif;" dir="ltr">
<p style="margin-top:0;margin-bottom:0">+ Suravee</p>
</div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> Robin Murphy <robin.murphy@arm.com><br>
<b>Sent:</b> Tuesday, April 17, 2018 2:22:46 PM<br>
<b>To:</b> Koenig, Christian; amd-gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org<br>
<b>Cc:</b> okaya@codeaurora.org; Deucher, Alexander<br>
<b>Subject:</b> Re: [PATCH v2 1/3] drm/prime: Iterate SG DMA addresses separately</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">On 17/04/18 17:29, Christian König wrote:<br>
> Am 17.04.2018 um 17:58 schrieb Robin Murphy:<br>
>> For dma_map_sg(), DMA API implementations are free to merge consecutive<br>
>> segments into a single DMA mapping if conditions are suitable, thus the<br>
>> resulting DMA addresses which drm_prime_sg_to_page_addr_arrays()<br>
>> iterates may be packed into fewer entries than ttm->sg->nents implies.<br>
>><br>
>> The current implementation does not account for this, meaning that its<br>
>> callers either have to reject the 0 < count < nents case or risk getting<br>
>> bogus DMA addresses beyond the first segment. Fortunately this is quite<br>
>> easy to handle without having to rejig structures to also store the<br>
>> mapped count, since the total DMA length should still be equal to the<br>
>> total buffer length. All we need is a second scatterlist cursor to<br>
>> iterate through the DMA addresses independently of the page addresses.<br>
>><br>
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com><br>
> <br>
> Reviewed-by: Christian König <christian.koenig@amd.com> for the whole <br>
> series.<br>
<br>
Thanks Christian.<br>
<br>
FWIW, the following *completely untested* hack should in theory give the <br>
AMD IOMMU similar segment-merging behaviour to the arm64 IOMMU DMA ops, <br>
if that helps widen the scope for testing/investigation (I have neither <br>
an AMD/ATI graphics card nor a PCIe-capable arm64 box to hand just at <br>
the moment).<br>
<br>
Robin.<br>
<br>
----->8-----<br>
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c<br>
index 2a99f0f14795..60b0e495b567 100644<br>
--- a/drivers/iommu/amd_iommu.c<br>
+++ b/drivers/iommu/amd_iommu.c<br>
@@ -2489,11 +2489,11 @@ static int map_sg(struct device *dev, struct <br>
scatterlist *sglist,<br>
                   int nelems, enum dma_data_direction direction,<br>
                   unsigned long attrs)<br>
  {<br>
-       int mapped_pages = 0, npages = 0, prot = 0, i;<br>
+       int mapped_pages = 0, npages = 0, prot = 0, i, count;<br>
         struct protection_domain *domain;<br>
         struct dma_ops_domain *dma_dom;<br>
-       struct scatterlist *s;<br>
-       unsigned long address;<br>
+       struct scatterlist *s, *d;<br>
+       unsigned long address, max_seg;<br>
         u64 dma_mask;<br>
<br>
         domain = get_domain(dev);<br>
@@ -2535,7 +2535,28 @@ static int map_sg(struct device *dev, struct <br>
scatterlist *sglist,<br>
                 s->dma_length   = s->length;<br>
         }<br>
<br>
-       return nelems;<br>
+       d = sglist;<br>
+       max_seg = dma_get_max_seg_size(dev);<br>
+       count = 1;<br>
+       nelems -= 1;<br>
+       for_each_sg(sg_next(sglist), s, nelems, i) {<br>
+               dma_addr_t s_dma_addr = s->dma_address;<br>
+               unsigned int s_dma_len = s->dma_length;<br>
+<br>
+               s->dma_address = 0;<br>
+               s->dma_length = 0;<br>
+               if (s_dma_addr == d->dma_address + d->dma_length &&<br>
+                   d->dma_length + s_dma_len <= max_seg) {<br>
+                       d->dma_length += s_dma_len;<br>
+               } else {<br>
+                       d = sg_next(d);<br>
+                       d->dma_address = s_dma_addr;<br>
+                       d->dma_length = s_dma_len;<br>
+                       count++;<br>
+               }<br>
+       }<br>
+<br>
+       return count;<br>
<br>
  out_unmap:<br>
         pr_err("%s: IOMMU mapping error in map_sg (io-pages: %d)\n",<br>
</div>
</span></font></div>
</body>
</html>