# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID b92ca87a2403d465e4d1087f8a7a43223b21bed8
# Parent 1509521c824efbae25bb953a2e2a49ab3f7fe7f4
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@xxxxxxxxxxxxx>
diff -r 1509521c824e -r b92ca87a2403
linux-2.6-xen-sparse/arch/xen/i386/kernel/swiotlb.c
--- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/swiotlb.c Fri Dec 16
23:28:27 2005
+++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/swiotlb.c Fri Dec 16
23:43:00 2005
@@ -24,6 +24,7 @@
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/dma.h>
+#include <asm/uaccess.h>
#include <asm-xen/xen-public/memory.h>
#define OFFSET(val,align) ((unsigned long)((val) & ( (align) - 1)))
@@ -201,6 +202,12 @@
printk(KERN_INFO "Software IO TLB disabled\n");
}
+/*
+ * We use __copy_to_user to transfer to the host buffer because the buffer
+ * may be mapped read-only (e.g, in blkback driver) but lower-level
+ * drivers map the buffer for DMA_BIDIRECTIONAL access. This causes an
+ * unnecessary copy from the aperture to the host buffer, and a page fault.
+ */
static void
__sync_single(struct phys_addr buffer, char *dma_addr, size_t size, int dir)
{
@@ -214,9 +221,11 @@
kmp = kmap_atomic(buffer.page, KM_SWIOTLB);
dev = dma_addr + size - len;
host = kmp + buffer.offset;
- memcpy((dir == DMA_FROM_DEVICE) ? host : dev,
- (dir == DMA_FROM_DEVICE) ? dev : host,
- bytes);
+ if (dir == DMA_FROM_DEVICE) {
+ if (__copy_to_user(host, dev, bytes))
+ return; /* inaccessible */
+ } else
+ memcpy(dev, host, bytes);
kunmap_atomic(kmp, KM_SWIOTLB);
len -= bytes;
buffer.page++;
@@ -225,9 +234,10 @@
} else {
char *host = (char *)phys_to_virt(
page_to_pseudophys(buffer.page)) + buffer.offset;
- if (dir == DMA_FROM_DEVICE)
- memcpy(host, dma_addr, size);
- else if (dir == DMA_TO_DEVICE)
+ if (dir == DMA_FROM_DEVICE) {
+ if (__copy_to_user(host, dma_addr, size))
+ return; /* inaccessible */
+ } else if (dir == DMA_TO_DEVICE)
memcpy(dma_addr, host, size);
}
}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|