# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID c5db6fd54e36fb4475257172c3f1c8fa77b54745
# Parent 57a5441b323b747b90db089c303e8e79ae7a36b0
Fix dma_map_single to work correctly with multi-page buffers.
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
diff -r 57a5441b323b -r c5db6fd54e36
linux-2.6-xen-sparse/include/asm-xen/asm-i386/dma-mapping.h
--- a/linux-2.6-xen-sparse/include/asm-xen/asm-i386/dma-mapping.h Tue Jul
12 10:16:33 2005
+++ b/linux-2.6-xen-sparse/include/asm-xen/asm-i386/dma-mapping.h Tue Jul
12 10:29:46 2005
@@ -16,21 +16,13 @@
void dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle);
-static inline dma_addr_t
+extern dma_addr_t
dma_map_single(struct device *dev, void *ptr, size_t size,
- enum dma_data_direction direction)
-{
- BUG_ON(direction == DMA_NONE);
- flush_write_buffers();
- return virt_to_bus(ptr);
-}
+ enum dma_data_direction direction);
-static inline void
+extern void
dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
- enum dma_data_direction direction)
-{
- BUG_ON(direction == DMA_NONE);
-}
+ enum dma_data_direction direction);
static inline int
dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
@@ -73,24 +65,20 @@
BUG_ON(direction == DMA_NONE);
}
-static inline void
+extern void
dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
- enum dma_data_direction direction)
-{
-}
+ enum dma_data_direction direction);
-static inline void
+extern void
dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t
size,
- enum dma_data_direction direction)
-{
- flush_write_buffers();
-}
+ enum dma_data_direction direction);
static inline void
dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
unsigned long offset, size_t size,
enum dma_data_direction direction)
{
+ dma_sync_single_for_cpu(dev, dma_handle+offset, size, direction);
}
static inline void
@@ -98,7 +86,7 @@
unsigned long offset, size_t size,
enum dma_data_direction direction)
{
- flush_write_buffers();
+ dma_sync_single_for_device(dev, dma_handle+offset, size, direction);
}
static inline void
diff -r 57a5441b323b -r c5db6fd54e36
linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/dma-mapping.h
--- a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/dma-mapping.h Tue Jul
12 10:16:33 2005
+++ b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/dma-mapping.h Tue Jul
12 10:29:46 2005
@@ -21,68 +21,21 @@
void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle);
-#ifdef CONFIG_GART_IOMMU
-
extern dma_addr_t dma_map_single(struct device *hwdev, void *ptr, size_t size,
int direction);
extern void dma_unmap_single(struct device *dev, dma_addr_t addr,size_t size,
int direction);
-#else
-
-/* No IOMMU */
-
-static inline dma_addr_t dma_map_single(struct device *hwdev, void *ptr,
- size_t size, int direction)
-{
- dma_addr_t addr;
-
- if (direction == DMA_NONE)
- out_of_line_bug();
- addr = virt_to_machine(ptr);
-
- if ((addr+size) & ~*hwdev->dma_mask)
- out_of_line_bug();
- return addr;
-}
-
-static inline void dma_unmap_single(struct device *hwdev, dma_addr_t dma_addr,
- size_t size, int direction)
-{
- if (direction == DMA_NONE)
- out_of_line_bug();
- /* Nothing to do */
-}
-#endif
-
#define dma_map_page(dev,page,offset,size,dir) \
dma_map_single((dev), page_address(page)+(offset), (size), (dir))
-static inline void dma_sync_single_for_cpu(struct device *hwdev,
- dma_addr_t dma_handle,
- size_t size, int direction)
-{
- if (direction == DMA_NONE)
- out_of_line_bug();
+extern void
+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
+ int direction);
- if (swiotlb)
- return
swiotlb_sync_single_for_cpu(hwdev,dma_handle,size,direction);
-
- flush_write_buffers();
-}
-
-static inline void dma_sync_single_for_device(struct device *hwdev,
- dma_addr_t dma_handle,
- size_t size, int direction)
-{
- if (direction == DMA_NONE)
- out_of_line_bug();
-
- if (swiotlb)
- return
swiotlb_sync_single_for_device(hwdev,dma_handle,size,direction);
-
- flush_write_buffers();
-}
+extern void
+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t
size,
+ int direction);
static inline void dma_sync_sg_for_cpu(struct device *hwdev,
struct scatterlist *sg,
diff -r 57a5441b323b -r c5db6fd54e36
linux-2.6-xen-sparse/arch/xen/i386/kernel/pci-dma.c
--- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/pci-dma.c Tue Jul 12
10:16:33 2005
+++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/pci-dma.c Tue Jul 12
10:29:46 2005
@@ -152,3 +152,131 @@
return mem->virt_base + (pos << PAGE_SHIFT);
}
EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
+
+static LIST_HEAD(dma_map_head);
+static DEFINE_SPINLOCK(dma_map_lock);
+struct dma_map_entry {
+ struct list_head list;
+ dma_addr_t dma;
+ char *bounce, *host;
+ size_t size;
+};
+#define DMA_MAP_MATCHES(e,d) (((e)->dma<=(d)) && (((e)->dma+(e)->size)>(d)))
+
+dma_addr_t
+dma_map_single(struct device *dev, void *ptr, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ void *bnc;
+ dma_addr_t dma;
+ unsigned long flags;
+
+ BUG_ON(direction == DMA_NONE);
+
+ /*
+ * Even if size is sub-page, the buffer may still straddle a page
+ * boundary. Take into account buffer start offset. All other calls are
+ * conservative and always search the dma_map list if it's non-empty.
+ */
+ if ((((unsigned int)ptr & ~PAGE_MASK) + size) <= PAGE_SIZE) {
+ dma = virt_to_bus(ptr);
+ } else {
+ BUG_ON((bnc = dma_alloc_coherent(dev, size, &dma, 0)) == NULL);
+ BUG_ON((ent = kmalloc(sizeof(*ent), GFP_KERNEL)) == NULL);
+ if (direction != DMA_FROM_DEVICE)
+ memcpy(bnc, ptr, size);
+ ent->dma = dma;
+ ent->bounce = bnc;
+ ent->host = ptr;
+ ent->size = size;
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_add(&ent->list, &dma_map_head);
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ }
+
+ flush_write_buffers();
+ return dma;
+}
+EXPORT_SYMBOL(dma_map_single);
+
+void
+dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ unsigned long flags;
+
+ BUG_ON(direction == DMA_NONE);
+
+ /* Fast-path check: are there any multi-page DMA mappings? */
+ if (!list_empty(&dma_map_head)) {
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_for_each_entry ( ent, &dma_map_head, list ) {
+ if (DMA_MAP_MATCHES(ent, dma_addr)) {
+ list_del(&ent->list);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ if (&ent->list != &dma_map_head) {
+ BUG_ON(dma_addr != ent->dma);
+ BUG_ON(size != ent->size);
+ if (direction != DMA_TO_DEVICE)
+ memcpy(ent->host, ent->bounce, size);
+ dma_free_coherent(dev, size, ent->bounce, ent->dma);
+ kfree(ent);
+ }
+ }
+}
+EXPORT_SYMBOL(dma_unmap_single);
+
+void
+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ unsigned long flags, off;
+
+ /* Fast-path check: are there any multi-page DMA mappings? */
+ if (!list_empty(&dma_map_head)) {
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_for_each_entry ( ent, &dma_map_head, list )
+ if (DMA_MAP_MATCHES(ent, dma_handle))
+ break;
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ if (&ent->list != &dma_map_head) {
+ off = dma_handle - ent->dma;
+ BUG_ON((off + size) > ent->size);
+ if (direction != DMA_TO_DEVICE)
+ memcpy(ent->host+off, ent->bounce+off, size);
+ }
+ }
+}
+EXPORT_SYMBOL(dma_sync_single_for_cpu);
+
+void
+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t
size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ unsigned long flags, off;
+
+ /* Fast-path check: are there any multi-page DMA mappings? */
+ if (!list_empty(&dma_map_head)) {
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_for_each_entry ( ent, &dma_map_head, list )
+ if (DMA_MAP_MATCHES(ent, dma_handle))
+ break;
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ if (&ent->list != &dma_map_head) {
+ off = dma_handle - ent->dma;
+ BUG_ON((off + size) > ent->size);
+ if (direction != DMA_FROM_DEVICE)
+ memcpy(ent->bounce+off, ent->host+off, size);
+ }
+ }
+
+ flush_write_buffers();
+}
+EXPORT_SYMBOL(dma_sync_single_for_device);
diff -r 57a5441b323b -r c5db6fd54e36
linux-2.6-xen-sparse/arch/xen/x86_64/kernel/pci-dma.c
--- a/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/pci-dma.c Tue Jul 12
10:16:33 2005
+++ b/linux-2.6-xen-sparse/arch/xen/x86_64/kernel/pci-dma.c Tue Jul 12
10:29:46 2005
@@ -203,3 +203,134 @@
}
EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
#endif
+
+static LIST_HEAD(dma_map_head);
+static DEFINE_SPINLOCK(dma_map_lock);
+struct dma_map_entry {
+ struct list_head list;
+ dma_addr_t dma;
+ char *bounce, *host;
+ size_t size;
+};
+#define DMA_MAP_MATCHES(e,d) (((e)->dma<=(d)) && (((e)->dma+(e)->size)>(d)))
+
+dma_addr_t
+dma_map_single(struct device *dev, void *ptr, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ void *bnc;
+ dma_addr_t dma;
+ unsigned long flags;
+
+ if (direction == DMA_NONE)
+ out_of_line_bug();
+
+ /*
+ * Even if size is sub-page, the buffer may still straddle a page
+ * boundary. Take into account buffer start offset. All other calls are
+ * conservative and always search the dma_map list if it's non-empty.
+ */
+ if ((((unsigned int)ptr & ~PAGE_MASK) + size) <= PAGE_SIZE) {
+ dma = virt_to_bus(ptr);
+ } else {
+ BUG_ON((bnc = dma_alloc_coherent(dev, size, &dma, 0)) == NULL);
+ BUG_ON((ent = kmalloc(sizeof(*ent), GFP_KERNEL)) == NULL);
+ if (direction != DMA_FROM_DEVICE)
+ memcpy(bnc, ptr, size);
+ ent->dma = dma;
+ ent->bounce = bnc;
+ ent->host = ptr;
+ ent->size = size;
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_add(&ent->list, &dma_map_head);
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ }
+
+ if ((dma+size) & ~*hwdev->dma_mask)
+ out_of_line_bug();
+ return dma;
+}
+EXPORT_SYMBOL(dma_map_single);
+
+void
+dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ unsigned long flags;
+
+ if (direction == DMA_NONE)
+ out_of_line_bug();
+
+ /* Fast-path check: are there any multi-page DMA mappings? */
+ if (!list_empty(&dma_map_head)) {
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_for_each_entry ( ent, &dma_map_head, list ) {
+ if (DMA_MAP_MATCHES(ent, dma_addr)) {
+ list_del(&ent->list);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ if (&ent->list != &dma_map_head) {
+ BUG_ON(dma_addr != ent->dma);
+ BUG_ON(size != ent->size);
+ if (direction != DMA_TO_DEVICE)
+ memcpy(ent->host, ent->bounce, size);
+ dma_free_coherent(dev, size, ent->bounce, ent->dma);
+ kfree(ent);
+ }
+ }
+}
+EXPORT_SYMBOL(dma_unmap_single);
+
+void
+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ unsigned long flags, off;
+
+ /* Fast-path check: are there any multi-page DMA mappings? */
+ if (!list_empty(&dma_map_head)) {
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_for_each_entry ( ent, &dma_map_head, list )
+ if (DMA_MAP_MATCHES(ent, dma_handle))
+ break;
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ if (&ent->list != &dma_map_head) {
+ off = dma_handle - ent->dma;
+ BUG_ON((off + size) > ent->size);
+ if (direction != DMA_TO_DEVICE)
+ memcpy(ent->host+off, ent->bounce+off, size);
+ }
+ }
+}
+EXPORT_SYMBOL(dma_sync_single_for_cpu);
+
+void
+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t
size,
+ enum dma_data_direction direction)
+{
+ struct dma_map_entry *ent;
+ unsigned long flags, off;
+
+ /* Fast-path check: are there any multi-page DMA mappings? */
+ if (!list_empty(&dma_map_head)) {
+ spin_lock_irqsave(&dma_map_lock, flags);
+ list_for_each_entry ( ent, &dma_map_head, list )
+ if (DMA_MAP_MATCHES(ent, dma_handle))
+ break;
+ spin_unlock_irqrestore(&dma_map_lock, flags);
+ if (&ent->list != &dma_map_head) {
+ off = dma_handle - ent->dma;
+ BUG_ON((off + size) > ent->size);
+ if (direction != DMA_FROM_DEVICE)
+ memcpy(ent->bounce+off, ent->host+off, size);
+ }
+ }
+
+ flush_write_buffers();
+}
+EXPORT_SYMBOL(dma_sync_single_for_device);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|