Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 2964

kernel-2.6.18-194.11.1.el5.src.rpm

From: Brad Peters <bpeters@redhat.com>
Date: Wed, 5 Mar 2008 17:13:54 -0500
Subject: [ppc64] iommu DMA alignment fix
Message-id: 20080305221354.GA8084@bpeters-ibm
O-Subject: Re: [RHEL 5.2 PATCH] IOMMU DMA alignment fix
Bugzilla: 426875

RHBZ#:
------
https://bugzilla.redhat.com/show_bug.cgi?id=426875

Description:
------------
The RDMA/IB code assumes that when doing a DMA mapping of a page (64K) it
obtains a DMA address that has the same 64K alignment. This is not the
case. This causes
the HW to incorrectly drop some bits at the bottom of the address and thus
write into the
wrong memory locations.

This patch enforces a 64K alignment when asked to map a 64K
aligned region of at least 64K in size. This will catch the case used by the
IB/RDMA code and possibly others making similar assumptions (though I
haven't
found any other so far) with little impact on anything else.

RHEL Version Found:
------------------
RHEL 5.1

kABI Status:
------------
No symbols were harmed.

Brew:
-----
Built on all platforms.
http://brewweb.devel.redhat.com/brew/taskinfo?taskID=1181544

Tested Kernel binary rpm available at:
-----
http://people.redhat.com/bpeters/kernels/5.2/kernel-2.6.18-83.el5.83.el5.426875.ppc64.rpm

Upstream Status:
----------------
Upstream at:
commit d262c32a4bcc3e5fda0325a64e53c25fe1e999d7
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=d262c32a4bcc3e5fda0325a64e53c25fe1e999d7;hp=031f2dcd7075e218e74dd7f94
2ad015cf82dffab

Test Status:
------------
Tested, confirmed.  2/24/08 <bpeters@redhat.com>

Acked-by: Pete Zaitcev <zaitcev@redhat.com>

diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index ca1118a..f004e7a 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -279,6 +279,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 	unsigned long flags;
 	struct scatterlist *s, *outs, *segstart;
 	int outcount, incount;
+	unsigned int align;
 	unsigned long handle;
 
 	BUG_ON(direction == DMA_NONE);
@@ -310,7 +311,11 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 		/* Allocate iommu entries for that segment */
 		vaddr = (unsigned long)page_address(s->page) + s->offset;
 		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 && (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);
 
@@ -568,7 +573,7 @@ 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);
 
@@ -576,8 +581,13 @@ dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr,
 	npages = iommu_num_pages(uaddr, size);
 
 	if (tbl) {
+		align = 0;
+		if (IOMMU_PAGE_SHIFT < PAGE_SHIFT &&
+		    ((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, "