Commit 1e9d90db authored by Nicolin Chen's avatar Nicolin Chen Committed by Christoph Hellwig
Browse files

dma-mapping: introduce dma_get_seg_boundary_nr_pages()



We found that callers of dma_get_seg_boundary mostly do an ALIGN
with page mask and then do a page shift to get number of pages:
    ALIGN(boundary + 1, 1 << shift) >> shift

However, the boundary might be as large as ULONG_MAX, which means
that a device has no specific boundary limit. So either "+ 1" or
passing it to ALIGN() would potentially overflow.

According to kernel defines:
    #define ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
    #define ALIGN(x, a)	ALIGN_MASK(x, (typeof(x))(a) - 1)

We can simplify the logic here into a helper function doing:
  ALIGN(boundary + 1, 1 << shift) >> shift
= ALIGN_MASK(b + 1, (1 << s) - 1) >> s
= {[b + 1 + (1 << s) - 1] & ~[(1 << s) - 1]} >> s
= [b + 1 + (1 << s) - 1] >> s
= [b + (1 << s)] >> s
= (b >> s) + 1

This patch introduces and applies dma_get_seg_boundary_nr_pages()
as an overflow-free helper for the dma_get_seg_boundary() callers
to get numbers of pages. It also takes care of the NULL dev case
for non-DMA API callers.

Suggested-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarNicolin Chen <nicoleotsuka@gmail.com>
Acked-by: default avatarNiklas Schnelle <schnelle@linux.ibm.com>
Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
parent 2281f797
Loading
Loading
Loading
Loading
+1 −6
Original line number Diff line number Diff line
@@ -141,12 +141,7 @@ iommu_arena_find_pages(struct device *dev, struct pci_iommu_arena *arena,
	unsigned long boundary_size;

	base = arena->dma_base >> PAGE_SHIFT;
	if (dev) {
		boundary_size = dma_get_seg_boundary(dev) + 1;
		boundary_size >>= PAGE_SHIFT;
	} else {
		boundary_size = 1UL << (32 - PAGE_SHIFT);
	}
	boundary_size = dma_get_seg_boundary_nr_pages(dev, PAGE_SHIFT);

	/* Search forward for the first mask-aligned sequence of N free ptes */
	ptes = arena->ptes;
+1 −2
Original line number Diff line number Diff line
@@ -485,8 +485,7 @@ sba_search_bitmap(struct ioc *ioc, struct device *dev,
	ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0);
	ASSERT(res_ptr < res_end);

	boundary_size = (unsigned long long)dma_get_seg_boundary(dev) + 1;
	boundary_size = ALIGN(boundary_size, 1ULL << iovp_shift) >> iovp_shift;
	boundary_size = dma_get_seg_boundary_nr_pages(dev, iovp_shift);

	BUG_ON(ioc->ibase & ~iovp_mask);
	shift = ioc->ibase >> iovp_shift;
+2 −9
Original line number Diff line number Diff line
@@ -172,7 +172,6 @@ static unsigned long iommu_range_alloc(struct device *dev,
	int largealloc = npages > 15;
	int pass = 0;
	unsigned long align_mask;
	unsigned long boundary_size;
	unsigned long flags;
	unsigned int pool_nr;
	struct iommu_pool *pool;
@@ -236,15 +235,9 @@ again:
		}
	}

	if (dev)
		boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
				      1 << tbl->it_page_shift);
	else
		boundary_size = ALIGN(1UL << 32, 1 << tbl->it_page_shift);
	/* 4GB boundary for iseries_hv_alloc and iseries_hv_map */

	n = iommu_area_alloc(tbl->it_map, limit, start, npages, tbl->it_offset,
			     boundary_size >> tbl->it_page_shift, align_mask);
			dma_get_seg_boundary_nr_pages(dev, tbl->it_page_shift),
			align_mask);
	if (n == -1) {
		if (likely(pass == 0)) {
			/* First try the pool from the start */
+2 −4
Original line number Diff line number Diff line
@@ -261,13 +261,11 @@ static unsigned long __dma_alloc_iommu(struct device *dev,
				       unsigned long start, int size)
{
	struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
	unsigned long boundary_size;

	boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
			      PAGE_SIZE) >> PAGE_SHIFT;
	return iommu_area_alloc(zdev->iommu_bitmap, zdev->iommu_pages,
				start, size, zdev->start_dma >> PAGE_SHIFT,
				boundary_size, 0);
				dma_get_seg_boundary_nr_pages(dev, PAGE_SHIFT),
				0);
}

static dma_addr_t dma_alloc_address(struct device *dev, int size)
+3 −7
Original line number Diff line number Diff line
@@ -166,13 +166,6 @@ unsigned long iommu_tbl_range_alloc(struct device *dev,
		}
	}

	if (dev)
		boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
				      1 << iommu->table_shift);
	else
		boundary_size = ALIGN(1ULL << 32, 1 << iommu->table_shift);

	boundary_size = boundary_size >> iommu->table_shift;
	/*
	 * if the skip_span_boundary_check had been set during init, we set
	 * things up so that iommu_is_span_boundary() merely checks if the
@@ -181,6 +174,9 @@ unsigned long iommu_tbl_range_alloc(struct device *dev,
	if ((iommu->flags & IOMMU_NO_SPAN_BOUND) != 0) {
		shift = 0;
		boundary_size = iommu->poolsize * iommu->nr_pools;
	} else {
		boundary_size = dma_get_seg_boundary_nr_pages(dev,
					iommu->table_shift);
	}
	n = iommu_area_alloc(iommu->map, limit, start, npages, shift,
			     boundary_size, align_mask);
Loading