Subject: fix swiotlb sync functions to properly deal with DMA_BIDIRECTIONAL This change was left out from a merge somewhere between 2.6.12 and 2.6.16. Signed-off-by: Jan Beulich Cc: Dominic Curran --- a/lib/swiotlb-xen.c +++ b/lib/swiotlb-xen.c @@ -47,6 +47,14 @@ EXPORT_SYMBOL(swiotlb); */ #define IO_TLB_SHIFT 11 +/* + * Enumeration for sync targets + */ +enum dma_sync_target { + SYNC_FOR_CPU = 0, + SYNC_FOR_DEVICE = 1, +}; + int swiotlb_force; static char *iotlb_virt_start; @@ -444,11 +452,26 @@ unmap_single(struct device *hwdev, char } static void -sync_single(struct device *hwdev, char *dma_addr, size_t size, int dir) +sync_single(struct device *hwdev, char *dma_addr, size_t size, + int dir, int target) { struct phys_addr buffer = dma_addr_to_phys_addr(dma_addr); - BUG_ON((dir != DMA_FROM_DEVICE) && (dir != DMA_TO_DEVICE)); - __sync_single(buffer, dma_addr, size, dir); + switch (target) { + case SYNC_FOR_CPU: + if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) + __sync_single(buffer, dma_addr, size, DMA_FROM_DEVICE); + else + BUG_ON(dir != DMA_TO_DEVICE); + break; + case SYNC_FOR_DEVICE: + if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) + __sync_single(buffer, dma_addr, size, DMA_TO_DEVICE); + else + BUG_ON(dir != DMA_FROM_DEVICE); + break; + default: + BUG(); + } } static void @@ -543,22 +566,27 @@ swiotlb_unmap_single(struct device *hwde * address back to the card, you must first perform a * swiotlb_dma_sync_for_device, and then the device again owns the buffer */ +static inline void +swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr, + size_t size, int dir, int target) +{ + BUG_ON(dir == DMA_NONE); + if (in_swiotlb_aperture(dev_addr)) + sync_single(hwdev, bus_to_virt(dev_addr), size, dir, target); +} + void swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr, size_t size, int dir) { - BUG_ON(dir == DMA_NONE); - if (in_swiotlb_aperture(dev_addr)) - sync_single(hwdev, bus_to_virt(dev_addr), size, dir); + swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU); } void swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr, size_t size, int dir) { - BUG_ON(dir == DMA_NONE); - if (in_swiotlb_aperture(dev_addr)) - sync_single(hwdev, bus_to_virt(dev_addr), size, dir); + swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE); } /* @@ -642,9 +670,9 @@ swiotlb_unmap_sg(struct device *hwdev, s * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules * and usage. */ -void -swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, - int nelems, int dir) +static inline void +swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sg, + int nelems, int dir, int target) { int i; @@ -652,24 +680,22 @@ swiotlb_sync_sg_for_cpu(struct device *h for (i = 0; i < nelems; i++, sg++) if (in_swiotlb_aperture(sg->dma_address)) - sync_single(hwdev, - (void *)bus_to_virt(sg->dma_address), - sg->dma_length, dir); + sync_single(hwdev, bus_to_virt(sg->dma_address), + sg->dma_length, dir, target); +} + +void +swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, + int nelems, int dir) +{ + swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU); } void swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, int nelems, int dir) { - int i; - - BUG_ON(dir == DMA_NONE); - - for (i = 0; i < nelems; i++, sg++) - if (in_swiotlb_aperture(sg->dma_address)) - sync_single(hwdev, - (void *)bus_to_virt(sg->dma_address), - sg->dma_length, dir); + swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE); } #ifdef CONFIG_HIGHMEM