Skip to content

Commit

Permalink
[POWERPC] Workaround for iommu page alignment
Browse files Browse the repository at this point in the history
Commit 5d2efba changed our iommu code
so that it always uses an iommu page size of 4kB.  That means with our
current code, drivers may do a dma_map_sg() of a 64kB page and obtain
a dma_addr_t that is only 4k aligned.

This works fine in most cases except for some infiniband HW it seems,
where they tell the HW about the page size and it ignores the low bits
of the DMA address.

This works around it by making our IOMMU code enforce a PAGE_SIZE alignment
for mappings of objects that are page aligned in the first place and whose
size is larger or equal to a page.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Benjamin Herrenschmidt authored and Paul Mackerras committed Jan 15, 2008
1 parent 031f2dc commit d262c32
Showing 1 changed file with 14 additions and 3 deletions.
17 changes: 14 additions & 3 deletions arch/powerpc/kernel/iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ int iommu_map_sg(struct iommu_table *tbl, struct scatterlist *sglist,
unsigned long flags;
struct scatterlist *s, *outs, *segstart;
int outcount, incount, i;
unsigned int align;
unsigned long handle;

BUG_ON(direction == DMA_NONE);
Expand Down Expand Up @@ -309,7 +310,12 @@ int iommu_map_sg(struct iommu_table *tbl, struct scatterlist *sglist,
/* Allocate iommu entries for that segment */
vaddr = (unsigned long) sg_virt(s);
npages = iommu_num_pages(vaddr, slen);
entry = iommu_range_alloc(tbl, npages, &handle, mask >> IOMMU_PAGE_SHIFT, 0);
align = 0;
if (IOMMU_PAGE_SHIFT < PAGE_SHIFT && slen >= PAGE_SIZE &&
(vaddr & ~PAGE_MASK) == 0)
align = PAGE_SHIFT - IOMMU_PAGE_SHIFT;
entry = iommu_range_alloc(tbl, npages, &handle,
mask >> IOMMU_PAGE_SHIFT, align);

DBG(" - vaddr: %lx, size: %lx\n", vaddr, slen);

Expand Down Expand Up @@ -572,16 +578,21 @@ dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr,
{
dma_addr_t dma_handle = DMA_ERROR_CODE;
unsigned long uaddr;
unsigned int npages;
unsigned int npages, align;

BUG_ON(direction == DMA_NONE);

uaddr = (unsigned long)vaddr;
npages = iommu_num_pages(uaddr, size);

if (tbl) {
align = 0;
if (IOMMU_PAGE_SHIFT < PAGE_SHIFT && size >= PAGE_SIZE &&
((unsigned long)vaddr & ~PAGE_MASK) == 0)
align = PAGE_SHIFT - IOMMU_PAGE_SHIFT;

dma_handle = iommu_alloc(tbl, vaddr, npages, direction,
mask >> IOMMU_PAGE_SHIFT, 0);
mask >> IOMMU_PAGE_SHIFT, align);
if (dma_handle == DMA_ERROR_CODE) {
if (printk_ratelimit()) {
printk(KERN_INFO "iommu_alloc failed, "
Expand Down

0 comments on commit d262c32

Please sign in to comment.