ia64/xen-unstable

changeset 8410:b92ca87a2403

Work aroudn swiotlb issue where a read-only host buffer is
mapped for DMA_BIDIRECTIONAL streaming access by certain
low-level drivers. This causes an unnecessary copy to the
host buffer that previously caused a fatal kernel page fault.

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Sat Dec 17 00:43:00 2005 +0100 (2005-12-17)
parents 1509521c824e
children 101ae33a8341
files linux-2.6-xen-sparse/arch/xen/i386/kernel/swiotlb.c
line diff
     1.1 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/swiotlb.c	Sat Dec 17 00:28:27 2005 +0100
     1.2 +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/swiotlb.c	Sat Dec 17 00:43:00 2005 +0100
     1.3 @@ -24,6 +24,7 @@
     1.4  #include <asm/io.h>
     1.5  #include <asm/pci.h>
     1.6  #include <asm/dma.h>
     1.7 +#include <asm/uaccess.h>
     1.8  #include <asm-xen/xen-public/memory.h>
     1.9  
    1.10  #define OFFSET(val,align) ((unsigned long)((val) & ( (align) - 1)))
    1.11 @@ -201,6 +202,12 @@ swiotlb_init(void)
    1.12  		printk(KERN_INFO "Software IO TLB disabled\n");
    1.13  }
    1.14  
    1.15 +/*
    1.16 + * We use __copy_to_user to transfer to the host buffer because the buffer
    1.17 + * may be mapped read-only (e.g, in blkback driver) but lower-level
    1.18 + * drivers map the buffer for DMA_BIDIRECTIONAL access. This causes an
    1.19 + * unnecessary copy from the aperture to the host buffer, and a page fault.
    1.20 + */
    1.21  static void
    1.22  __sync_single(struct phys_addr buffer, char *dma_addr, size_t size, int dir)
    1.23  {
    1.24 @@ -214,9 +221,11 @@ static void
    1.25  			kmp  = kmap_atomic(buffer.page, KM_SWIOTLB);
    1.26  			dev  = dma_addr + size - len;
    1.27  			host = kmp + buffer.offset;
    1.28 -			memcpy((dir == DMA_FROM_DEVICE) ? host : dev,
    1.29 -			       (dir == DMA_FROM_DEVICE) ? dev : host,
    1.30 -			       bytes);
    1.31 +			if (dir == DMA_FROM_DEVICE) {
    1.32 +				if (__copy_to_user(host, dev, bytes))
    1.33 +					return; /* inaccessible */
    1.34 +			} else
    1.35 +				memcpy(dev, host, bytes);
    1.36  			kunmap_atomic(kmp, KM_SWIOTLB);
    1.37  			len -= bytes;
    1.38  			buffer.page++;
    1.39 @@ -225,9 +234,10 @@ static void
    1.40  	} else {
    1.41  		char *host = (char *)phys_to_virt(
    1.42  			page_to_pseudophys(buffer.page)) + buffer.offset;
    1.43 -		if (dir == DMA_FROM_DEVICE)
    1.44 -			memcpy(host, dma_addr, size);
    1.45 -		else if (dir == DMA_TO_DEVICE)
    1.46 +		if (dir == DMA_FROM_DEVICE) {
    1.47 +			if (__copy_to_user(host, dma_addr, size))
    1.48 +				return; /* inaccessible */
    1.49 +		} else if (dir == DMA_TO_DEVICE)
    1.50  			memcpy(dma_addr, host, size);
    1.51  	}
    1.52  }