diff mbox

[6/6] IB/srp: Fix srp_map_sg_fr()

Message ID 565DE4BA.1040703@sandisk.com (mailing list archive)
State Accepted
Headers show

Commit Message

Bart Van Assche Dec. 1, 2015, 6:19 p.m. UTC
After dma_map_sg() has been called the return value of that function
must be used as the number of elements in the scatterlist instead of
scsi_sg_count().

Fixes: commit f7f7aab1a5c0 ("IB/srp: Convert to new registration API")
Reported-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
Cc: stable <stable@vger.kernel.org> # v4.4+
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Sebastian Parschauer <sebastian.riemer@profitbricks.com>
---
 drivers/infiniband/ulp/srp/ib_srp.c | 19 ++++++++-----------
 drivers/infiniband/ulp/srp/ib_srp.h |  5 +----
 2 files changed, 9 insertions(+), 15 deletions(-)

Comments

Sagi Grimberg Dec. 1, 2015, 6:35 p.m. UTC | #1
> After dma_map_sg() has been called the return value of that function
> must be used as the number of elements in the scatterlist instead of
> scsi_sg_count().

Umm, but ib_map_mr_sg iterates on the sg list. Say you have sg_nents=3
and after mapping you got dma_nents=2 (2 entries are contig) and you
pass that, ib_sg_to_pages will only iterate on 2 elements won't it? am
I missing something?
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bart Van Assche Dec. 1, 2015, 6:39 p.m. UTC | #2
On 12/01/2015 10:35 AM, Sagi Grimberg wrote:
>> After dma_map_sg() has been called the return value of that function
>> must be used as the number of elements in the scatterlist instead of
>> scsi_sg_count().
>
> Umm, but ib_map_mr_sg iterates on the sg list. Say you have sg_nents=3
> and after mapping you got dma_nents=2 (2 entries are contig) and you
> pass that, ib_sg_to_pages will only iterate on 2 elements won't it? am
> I missing something?

Hello Sagi,

 From https://www.kernel.org/doc/Documentation/DMA-API-HOWTO.txt:

<quote>
With scatterlists, you map a region gathered from several regions by:

	int i, count = dma_map_sg(dev, sglist, nents, direction);
	struct scatterlist *sg;

	for_each_sg(sglist, sg, count, i) {
		hw_address[i] = sg_dma_address(sg);
		hw_len[i] = sg_dma_len(sg);
	}

where nents is the number of entries in the sglist.
</quote>

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sagi Grimberg Dec. 2, 2015, 11:59 a.m. UTC | #3
On 01/12/2015 20:39, Bart Van Assche wrote:
> On 12/01/2015 10:35 AM, Sagi Grimberg wrote:
>>> After dma_map_sg() has been called the return value of that function
>>> must be used as the number of elements in the scatterlist instead of
>>> scsi_sg_count().
>>
>> Umm, but ib_map_mr_sg iterates on the sg list. Say you have sg_nents=3
>> and after mapping you got dma_nents=2 (2 entries are contig) and you
>> pass that, ib_sg_to_pages will only iterate on 2 elements won't it? am
>> I missing something?
>
> Hello Sagi,
>
>  From https://www.kernel.org/doc/Documentation/DMA-API-HOWTO.txt:
>
> <quote>
> With scatterlists, you map a region gathered from several regions by:
>
>      int i, count = dma_map_sg(dev, sglist, nents, direction);
>      struct scatterlist *sg;
>
>      for_each_sg(sglist, sg, count, i) {
>          hw_address[i] = sg_dma_address(sg);
>          hw_len[i] = sg_dma_len(sg);
>      }
>
> where nents is the number of entries in the sglist.
> </quote>

 From Documentation/DMA_API.txt

<quote>
         int
         dma_map_sg(struct device *dev, struct scatterlist *sg,
                 int nents, enum dma_data_direction direction)

Returns: the number of DMA address segments mapped (this may be shorter
than <nents> passed in if some elements of the scatter/gather list are
physically or virtually adjacent and an IOMMU maps them with a single
entry).
</quote>
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig Dec. 2, 2015, 12:41 p.m. UTC | #4
On Wed, Dec 02, 2015 at 01:59:38PM +0200, Sagi Grimberg wrote:
> >where nents is the number of entries in the sglist.
> ></quote>
> 
> From Documentation/DMA_API.txt
> 
> <quote>
>         int
>         dma_map_sg(struct device *dev, struct scatterlist *sg,
>                 int nents, enum dma_data_direction direction)
> 
> Returns: the number of DMA address segments mapped (this may be shorter
> than <nents> passed in if some elements of the scatter/gather list are
> physically or virtually adjacent and an IOMMU maps them with a single
> entry).
> </quote>

dma_map_sg returns the actual number of entries to iterate.  At least
historically some IOMMU implementations would do strange tricks like:

  If entries 2 and 3 could be merged dma_len for 2 would span 2 and 3,
  and then entry 3 would actually have the dma addr and len for entry 4.

I'm not sure anyone still does that, but the first spot to check would
be the Parisc IOMMU drivers.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sagi Grimberg Dec. 2, 2015, 12:50 p.m. UTC | #5
> dma_map_sg returns the actual number of entries to iterate.  At least
> historically some IOMMU implementations would do strange tricks like:
>
>    If entries 2 and 3 could be merged dma_len for 2 would span 2 and 3,
>    and then entry 3 would actually have the dma addr and len for entry 4.
>
> I'm not sure anyone still does that, but the first spot to check would
> be the Parisc IOMMU drivers.

Oh I see, I didn't know that.

Thanks for clarifying this for me. AFAICT this patch is fine.

Reviewed-by: Sagi Grimberg <sagig@mellanox.com>

iser, nfs needs fixing too.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sagi Grimberg Dec. 3, 2015, 8:46 a.m. UTC | #6
Replying to my own email,


>> dma_map_sg returns the actual number of entries to iterate.  At least
>> historically some IOMMU implementations would do strange tricks like:
>>
>>    If entries 2 and 3 could be merged dma_len for 2 would span 2 and 3,
>>    and then entry 3 would actually have the dma addr and len for entry 4.

So what would be in the last entry {dma_addr, dma_len}? zeros?

>> I'm not sure anyone still does that, but the first spot to check would
>> be the Parisc IOMMU drivers.

So how does that sit with the fact that dma_unmap requires the
same sg_nents as in dma_map and not the actual value of dma entries?
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig Dec. 3, 2015, 9:11 a.m. UTC | #7
On Thu, Dec 03, 2015 at 10:46:10AM +0200, Sagi Grimberg wrote:
> >>   If entries 2 and 3 could be merged dma_len for 2 would span 2 and 3,
> >>   and then entry 3 would actually have the dma addr and len for entry 4.
> 
> So what would be in the last entry {dma_addr, dma_len}? zeros?
> 
> >>I'm not sure anyone still does that, but the first spot to check would
> >>be the Parisc IOMMU drivers.
> 
> So how does that sit with the fact that dma_unmap requires the
> same sg_nents as in dma_map and not the actual value of dma entries?

Take a look at drivers/parisc/iommu-helpers.h:iommu_coalesce_chunks()
and drivers/parisc/sba_iommu.c:sba_unmap_sg() for example.

The first fills out the sglist, and zeroes all unused entries past what
it fills in.  The unmap side than simply exits the loop if the entries
are zeroed.
--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index fac1423..3db9a65 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1313,7 +1313,7 @@  reset_state:
 }
 
 static int srp_map_finish_fr(struct srp_map_state *state,
-			     struct srp_rdma_ch *ch)
+			     struct srp_rdma_ch *ch, int sg_nents)
 {
 	struct srp_target_port *target = ch->target;
 	struct srp_device *dev = target->srp_host->srp_dev;
@@ -1328,10 +1328,10 @@  static int srp_map_finish_fr(struct srp_map_state *state,
 
 	WARN_ON_ONCE(!dev->use_fast_reg);
 
-	if (state->sg_nents == 0)
+	if (sg_nents == 0)
 		return 0;
 
-	if (state->sg_nents == 1 && target->global_mr) {
+	if (sg_nents == 1 && target->global_mr) {
 		srp_map_desc(state, sg_dma_address(state->sg),
 			     sg_dma_len(state->sg),
 			     target->global_mr->rkey);
@@ -1345,8 +1345,7 @@  static int srp_map_finish_fr(struct srp_map_state *state,
 	rkey = ib_inc_rkey(desc->mr->rkey);
 	ib_update_fast_reg_key(desc->mr, rkey);
 
-	n = ib_map_mr_sg(desc->mr, state->sg, state->sg_nents,
-			 dev->mr_page_size);
+	n = ib_map_mr_sg(desc->mr, state->sg, sg_nents, dev->mr_page_size);
 	if (unlikely(n < 0))
 		return n;
 
@@ -1452,16 +1451,15 @@  static int srp_map_sg_fr(struct srp_map_state *state, struct srp_rdma_ch *ch,
 	state->fr.next = req->fr_list;
 	state->fr.end = req->fr_list + ch->target->cmd_sg_cnt;
 	state->sg = scat;
-	state->sg_nents = scsi_sg_count(req->scmnd);
 
-	while (state->sg_nents) {
+	while (count) {
 		int i, n;
 
-		n = srp_map_finish_fr(state, ch);
+		n = srp_map_finish_fr(state, ch, count);
 		if (unlikely(n < 0))
 			return n;
 
-		state->sg_nents -= n;
+		count -= n;
 		for (i = 0; i < n; i++)
 			state->sg = sg_next(state->sg);
 	}
@@ -1521,13 +1519,12 @@  static int srp_map_idb(struct srp_rdma_ch *ch, struct srp_request *req,
 
 	if (dev->use_fast_reg) {
 		state.sg = idb_sg;
-		state.sg_nents = 1;
 		sg_set_buf(idb_sg, req->indirect_desc, idb_len);
 		idb_sg->dma_address = req->indirect_dma_addr; /* hack! */
 #ifdef CONFIG_NEED_SG_DMA_LENGTH
 		idb_sg->dma_length = idb_sg->length;	      /* hack^2 */
 #endif
-		ret = srp_map_finish_fr(&state, ch);
+		ret = srp_map_finish_fr(&state, ch, 1);
 		if (ret < 0)
 			return ret;
 	} else if (dev->use_fmr) {
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 87a2a91..f6af531 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -300,10 +300,7 @@  struct srp_map_state {
 	dma_addr_t		base_dma_addr;
 	u32			dma_len;
 	u32			total_len;
-	union {
-		unsigned int	npages;
-		int		sg_nents;
-	};
+	unsigned int		npages;
 	unsigned int		nmdesc;
 	unsigned int		ndesc;
 };