From: jbeulich@suse.com
Subject: swiotlb: don't assume PA 0 is invalid
Patch-mainline: native counterpart submitted for 3.16
References: bnc#865882

In 2.6.29 io_tlb_orig_addr[] got converted from storing virtual addresses
to storing physical ones. While checking virtual addresses against NULL
is a legitimate thing to catch invalid entries, checking physical ones
against zero isn't: There's no guarantee that PFN 0 is reserved on a
particular platform.

Since it is unclear whether the check in swiotlb_tbl_unmap_single() is
actually needed, retain it but check against a guaranteed invalid physical
address. This requires setting up the array in a suitable fashion. And
since the original code failed to invalidate array entries when regions
get unmapped, this is being fixed at once along with adding a similar
check to swiotlb_tbl_sync_single().

Obviously the less intrusive change would be to simply drop the check in
swiotlb_tbl_unmap_single().

This really should be an autogenerated counterpart of a patch to
lib/swiotlb.c.

--- sle11sp3.orig/lib/swiotlb-xen.c	2013-02-19 15:47:41.000000000 +0100
+++ sle11sp3/lib/swiotlb-xen.c	2014-05-15 17:46:06.000000000 +0200
@@ -71,6 +71,7 @@ static unsigned int io_tlb_index;
  * We need to save away the original address corresponding to a mapped entry
  * for the sync operations.
  */
+#define INVALID_PHYS_ADDR (~(phys_addr_t)0)
 static phys_addr_t *io_tlb_orig_addr;
 
 /*
@@ -181,10 +182,12 @@ void __init swiotlb_init_with_tbl(char *
 	 * to find contiguous free memory regions of size up to IO_TLB_SEGSIZE.
 	 */
 	io_tlb_list = alloc_bootmem_pages(PAGE_ALIGN(io_tlb_nslabs * sizeof(int)));
-	for (i = 0; i < io_tlb_nslabs; i++)
+	io_tlb_orig_addr = alloc_bootmem_pages(PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)));
+	for (i = 0; i < io_tlb_nslabs; i++) {
  		io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE);
+ 		io_tlb_orig_addr[i] = INVALID_PHYS_ADDR;
+	}
 	io_tlb_index = 0;
-	io_tlb_orig_addr = alloc_bootmem_pages(PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)));
 
 	/*
 	 * Get the overflow emergency buffer
@@ -448,7 +451,8 @@ swiotlb_tbl_unmap_single(struct device *
 	/*
 	 * First, sync the memory before unmapping the entry
 	 */
-	if (phys && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
+	if (phys != INVALID_PHYS_ADDR &&
+	    ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
 		swiotlb_bounce(phys, dma_addr, size, DMA_FROM_DEVICE);
 
 	/*
@@ -465,8 +469,10 @@ swiotlb_tbl_unmap_single(struct device *
 		 * Step 1: return the slots to the free list, merging the
 		 * slots with superceeding slots
 		 */
-		for (i = index + nslots - 1; i >= index; i--)
+		for (i = index + nslots - 1; i >= index; i--) {
 			io_tlb_list[i] = ++count;
+			io_tlb_orig_addr[i] = INVALID_PHYS_ADDR;
+		}
 		/*
 		 * Step 2: merge the returned slots with the preceding slots,
 		 * if available (non zero)
@@ -489,6 +495,8 @@ swiotlb_tbl_sync_single(struct device *h
 	int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT;
 	phys_addr_t phys = io_tlb_orig_addr[index];
 
+	if (phys == INVALID_PHYS_ADDR)
+		return;
 	phys += ((unsigned long)dma_addr & ((1 << IO_TLB_SHIFT) - 1));
 
 	switch (target) {
