Loading Documentation/DMA-API.txt +106 −0 Original line number Diff line number Diff line Loading @@ -609,3 +609,109 @@ size is the size (and should be a page-sized multiple). The return value will be either a pointer to the processor virtual address of the memory, or an error (via PTR_ERR()) if any part of the region is occupied. Part III - Debug drivers use of the DMA-API ------------------------------------------- The DMA-API as described above as some constraints. DMA addresses must be released with the corresponding function with the same size for example. With the advent of hardware IOMMUs it becomes more and more important that drivers do not violate those constraints. In the worst case such a violation can result in data corruption up to destroyed filesystems. To debug drivers and find bugs in the usage of the DMA-API checking code can be compiled into the kernel which will tell the developer about those violations. If your architecture supports it you can select the "Enable debugging of DMA-API usage" option in your kernel configuration. Enabling this option has a performance impact. Do not enable it in production kernels. If you boot the resulting kernel will contain code which does some bookkeeping about what DMA memory was allocated for which device. If this code detects an error it prints a warning message with some details into your kernel log. An example warning message may look like this: ------------[ cut here ]------------ WARNING: at /data2/repos/linux-2.6-iommu/lib/dma-debug.c:448 check_unmap+0x203/0x490() Hardware name: forcedeth 0000:00:08.0: DMA-API: device driver frees DMA memory with wrong function [device address=0x00000000640444be] [size=66 bytes] [mapped as single] [unmapped as page] Modules linked in: nfsd exportfs bridge stp llc r8169 Pid: 0, comm: swapper Tainted: G W 2.6.28-dmatest-09289-g8bb99c0 #1 Call Trace: <IRQ> [<ffffffff80240b22>] warn_slowpath+0xf2/0x130 [<ffffffff80647b70>] _spin_unlock+0x10/0x30 [<ffffffff80537e75>] usb_hcd_link_urb_to_ep+0x75/0xc0 [<ffffffff80647c22>] _spin_unlock_irqrestore+0x12/0x40 [<ffffffff8055347f>] ohci_urb_enqueue+0x19f/0x7c0 [<ffffffff80252f96>] queue_work+0x56/0x60 [<ffffffff80237e10>] enqueue_task_fair+0x20/0x50 [<ffffffff80539279>] usb_hcd_submit_urb+0x379/0xbc0 [<ffffffff803b78c3>] cpumask_next_and+0x23/0x40 [<ffffffff80235177>] find_busiest_group+0x207/0x8a0 [<ffffffff8064784f>] _spin_lock_irqsave+0x1f/0x50 [<ffffffff803c7ea3>] check_unmap+0x203/0x490 [<ffffffff803c8259>] debug_dma_unmap_page+0x49/0x50 [<ffffffff80485f26>] nv_tx_done_optimized+0xc6/0x2c0 [<ffffffff80486c13>] nv_nic_irq_optimized+0x73/0x2b0 [<ffffffff8026df84>] handle_IRQ_event+0x34/0x70 [<ffffffff8026ffe9>] handle_edge_irq+0xc9/0x150 [<ffffffff8020e3ab>] do_IRQ+0xcb/0x1c0 [<ffffffff8020c093>] ret_from_intr+0x0/0xa <EOI> <4>---[ end trace f6435a98e2a38c0e ]--- The driver developer can find the driver and the device including a stacktrace of the DMA-API call which caused this warning. Per default only the first error will result in a warning message. All other errors will only silently counted. This limitation exist to prevent the code from flooding your kernel log. To support debugging a device driver this can be disabled via debugfs. See the debugfs interface documentation below for details. The debugfs directory for the DMA-API debugging code is called dma-api/. In this directory the following files can currently be found: dma-api/all_errors This file contains a numeric value. If this value is not equal to zero the debugging code will print a warning for every error it finds into the kernel log. Be carefull with this option. It can easily flood your logs. dma-api/disabled This read-only file contains the character 'Y' if the debugging code is disabled. This can happen when it runs out of memory or if it was disabled at boot time dma-api/error_count This file is read-only and shows the total numbers of errors found. dma-api/num_errors The number in this file shows how many warnings will be printed to the kernel log before it stops. This number is initialized to one at system boot and be set by writing into this file dma-api/min_free_entries This read-only file can be read to get the minimum number of free dma_debug_entries the allocator has ever seen. If this value goes down to zero the code will disable itself because it is not longer reliable. dma-api/num_free_entries The current number of free dma_debug_entries in the allocator. If you have this code compiled into your kernel it will be enabled by default. If you want to boot without the bookkeeping anyway you can provide 'dma_debug=off' as a boot parameter. This will disable DMA-API debugging. Notice that you can not enable it again at runtime. You have to reboot to do so. When the code disables itself at runtime this is most likely because it ran out of dma_debug_entries. These entries are preallocated at boot. The number of preallocated entries is defined per architecture. If it is too low for you boot with 'dma_debug_entries=<your_desired_number>' to overwrite the architectural default. Documentation/kernel-parameters.txt +10 −0 Original line number Diff line number Diff line Loading @@ -491,6 +491,16 @@ and is between 256 and 4096 characters. It is defined in the file Range: 0 - 8192 Default: 64 dma_debug=off If the kernel is compiled with DMA_API_DEBUG support this option disables the debugging code at boot. dma_debug_entries=<number> This option allows to tune the number of preallocated entries for DMA-API debugging code. One entry is required per DMA-API allocation. Use this if the DMA-API debugging code disables itself because the architectural default is too low. hpet= [X86-32,HPET] option to control HPET usage Format: { enable (default) | disable | force } disable: disable HPET and use PIT instead Loading arch/Kconfig +2 −0 Original line number Diff line number Diff line Loading @@ -106,3 +106,5 @@ config HAVE_CLK The <linux/clk.h> calls support software clock gating and thus are a key power management tool on many systems. config HAVE_DMA_API_DEBUG bool arch/x86/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ config X86 select HAVE_GENERIC_DMA_COHERENT if X86_32 select HAVE_EFFICIENT_UNALIGNED_ACCESS select USER_STACKTRACE_SUPPORT select HAVE_DMA_API_DEBUG config ARCH_DEFCONFIG string Loading arch/x86/include/asm/dma-mapping.h +39 −6 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ */ #include <linux/scatterlist.h> #include <linux/dma-debug.h> #include <linux/dma-attrs.h> #include <asm/io.h> #include <asm/swiotlb.h> Loading Loading @@ -56,11 +57,16 @@ dma_map_single(struct device *hwdev, void *ptr, size_t size, enum dma_data_direction dir) { struct dma_map_ops *ops = get_dma_ops(hwdev); dma_addr_t addr; BUG_ON(!valid_dma_direction(dir)); return ops->map_page(hwdev, virt_to_page(ptr), addr = ops->map_page(hwdev, virt_to_page(ptr), (unsigned long)ptr & ~PAGE_MASK, size, dir, NULL); debug_dma_map_page(hwdev, virt_to_page(ptr), (unsigned long)ptr & ~PAGE_MASK, size, dir, addr, true); return addr; } static inline void Loading @@ -72,6 +78,7 @@ dma_unmap_single(struct device *dev, dma_addr_t addr, size_t size, BUG_ON(!valid_dma_direction(dir)); if (ops->unmap_page) ops->unmap_page(dev, addr, size, dir, NULL); debug_dma_unmap_page(dev, addr, size, dir, true); } static inline int Loading @@ -79,9 +86,13 @@ dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, enum dma_data_direction dir) { struct dma_map_ops *ops = get_dma_ops(hwdev); int ents; BUG_ON(!valid_dma_direction(dir)); return ops->map_sg(hwdev, sg, nents, dir, NULL); ents = ops->map_sg(hwdev, sg, nents, dir, NULL); debug_dma_map_sg(hwdev, sg, nents, ents, dir); return ents; } static inline void Loading @@ -91,6 +102,7 @@ dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, struct dma_map_ops *ops = get_dma_ops(hwdev); BUG_ON(!valid_dma_direction(dir)); debug_dma_unmap_sg(hwdev, sg, nents, dir); if (ops->unmap_sg) ops->unmap_sg(hwdev, sg, nents, dir, NULL); } Loading @@ -104,6 +116,7 @@ dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t dma_handle, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_single_for_cpu) ops->sync_single_for_cpu(hwdev, dma_handle, size, dir); debug_dma_sync_single_for_cpu(hwdev, dma_handle, size, dir); flush_write_buffers(); } Loading @@ -116,6 +129,7 @@ dma_sync_single_for_device(struct device *hwdev, dma_addr_t dma_handle, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_single_for_device) ops->sync_single_for_device(hwdev, dma_handle, size, dir); debug_dma_sync_single_for_device(hwdev, dma_handle, size, dir); flush_write_buffers(); } Loading @@ -130,6 +144,8 @@ dma_sync_single_range_for_cpu(struct device *hwdev, dma_addr_t dma_handle, if (ops->sync_single_range_for_cpu) ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, size, dir); debug_dma_sync_single_range_for_cpu(hwdev, dma_handle, offset, size, dir); flush_write_buffers(); } Loading @@ -144,6 +160,8 @@ dma_sync_single_range_for_device(struct device *hwdev, dma_addr_t dma_handle, if (ops->sync_single_range_for_device) ops->sync_single_range_for_device(hwdev, dma_handle, offset, size, dir); debug_dma_sync_single_range_for_device(hwdev, dma_handle, offset, size, dir); flush_write_buffers(); } Loading @@ -156,6 +174,7 @@ dma_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_sg_for_cpu) ops->sync_sg_for_cpu(hwdev, sg, nelems, dir); debug_dma_sync_sg_for_cpu(hwdev, sg, nelems, dir); flush_write_buffers(); } Loading @@ -168,6 +187,7 @@ dma_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_sg_for_device) ops->sync_sg_for_device(hwdev, sg, nelems, dir); debug_dma_sync_sg_for_device(hwdev, sg, nelems, dir); flush_write_buffers(); } Loading @@ -177,15 +197,24 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, enum dma_data_direction dir) { struct dma_map_ops *ops = get_dma_ops(dev); dma_addr_t addr; BUG_ON(!valid_dma_direction(dir)); return ops->map_page(dev, page, offset, size, dir, NULL); addr = ops->map_page(dev, page, offset, size, dir, NULL); debug_dma_map_page(dev, page, offset, size, dir, addr, false); return addr; } static inline void dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir) { dma_unmap_single(dev, addr, size, dir); struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); if (ops->unmap_page) ops->unmap_page(dev, addr, size, dir, NULL); debug_dma_unmap_page(dev, addr, size, dir, false); } static inline void Loading Loading @@ -250,8 +279,11 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, if (!ops->alloc_coherent) return NULL; return ops->alloc_coherent(dev, size, dma_handle, memory = ops->alloc_coherent(dev, size, dma_handle, dma_alloc_coherent_gfp_flags(dev, gfp)); debug_dma_alloc_coherent(dev, size, *dma_handle, memory); return memory; } static inline void dma_free_coherent(struct device *dev, size_t size, Loading @@ -264,6 +296,7 @@ static inline void dma_free_coherent(struct device *dev, size_t size, if (dma_release_from_coherent(dev, get_order(size), vaddr)) return; debug_dma_free_coherent(dev, size, vaddr, bus); if (ops->free_coherent) ops->free_coherent(dev, size, vaddr, bus); } Loading Loading
Documentation/DMA-API.txt +106 −0 Original line number Diff line number Diff line Loading @@ -609,3 +609,109 @@ size is the size (and should be a page-sized multiple). The return value will be either a pointer to the processor virtual address of the memory, or an error (via PTR_ERR()) if any part of the region is occupied. Part III - Debug drivers use of the DMA-API ------------------------------------------- The DMA-API as described above as some constraints. DMA addresses must be released with the corresponding function with the same size for example. With the advent of hardware IOMMUs it becomes more and more important that drivers do not violate those constraints. In the worst case such a violation can result in data corruption up to destroyed filesystems. To debug drivers and find bugs in the usage of the DMA-API checking code can be compiled into the kernel which will tell the developer about those violations. If your architecture supports it you can select the "Enable debugging of DMA-API usage" option in your kernel configuration. Enabling this option has a performance impact. Do not enable it in production kernels. If you boot the resulting kernel will contain code which does some bookkeeping about what DMA memory was allocated for which device. If this code detects an error it prints a warning message with some details into your kernel log. An example warning message may look like this: ------------[ cut here ]------------ WARNING: at /data2/repos/linux-2.6-iommu/lib/dma-debug.c:448 check_unmap+0x203/0x490() Hardware name: forcedeth 0000:00:08.0: DMA-API: device driver frees DMA memory with wrong function [device address=0x00000000640444be] [size=66 bytes] [mapped as single] [unmapped as page] Modules linked in: nfsd exportfs bridge stp llc r8169 Pid: 0, comm: swapper Tainted: G W 2.6.28-dmatest-09289-g8bb99c0 #1 Call Trace: <IRQ> [<ffffffff80240b22>] warn_slowpath+0xf2/0x130 [<ffffffff80647b70>] _spin_unlock+0x10/0x30 [<ffffffff80537e75>] usb_hcd_link_urb_to_ep+0x75/0xc0 [<ffffffff80647c22>] _spin_unlock_irqrestore+0x12/0x40 [<ffffffff8055347f>] ohci_urb_enqueue+0x19f/0x7c0 [<ffffffff80252f96>] queue_work+0x56/0x60 [<ffffffff80237e10>] enqueue_task_fair+0x20/0x50 [<ffffffff80539279>] usb_hcd_submit_urb+0x379/0xbc0 [<ffffffff803b78c3>] cpumask_next_and+0x23/0x40 [<ffffffff80235177>] find_busiest_group+0x207/0x8a0 [<ffffffff8064784f>] _spin_lock_irqsave+0x1f/0x50 [<ffffffff803c7ea3>] check_unmap+0x203/0x490 [<ffffffff803c8259>] debug_dma_unmap_page+0x49/0x50 [<ffffffff80485f26>] nv_tx_done_optimized+0xc6/0x2c0 [<ffffffff80486c13>] nv_nic_irq_optimized+0x73/0x2b0 [<ffffffff8026df84>] handle_IRQ_event+0x34/0x70 [<ffffffff8026ffe9>] handle_edge_irq+0xc9/0x150 [<ffffffff8020e3ab>] do_IRQ+0xcb/0x1c0 [<ffffffff8020c093>] ret_from_intr+0x0/0xa <EOI> <4>---[ end trace f6435a98e2a38c0e ]--- The driver developer can find the driver and the device including a stacktrace of the DMA-API call which caused this warning. Per default only the first error will result in a warning message. All other errors will only silently counted. This limitation exist to prevent the code from flooding your kernel log. To support debugging a device driver this can be disabled via debugfs. See the debugfs interface documentation below for details. The debugfs directory for the DMA-API debugging code is called dma-api/. In this directory the following files can currently be found: dma-api/all_errors This file contains a numeric value. If this value is not equal to zero the debugging code will print a warning for every error it finds into the kernel log. Be carefull with this option. It can easily flood your logs. dma-api/disabled This read-only file contains the character 'Y' if the debugging code is disabled. This can happen when it runs out of memory or if it was disabled at boot time dma-api/error_count This file is read-only and shows the total numbers of errors found. dma-api/num_errors The number in this file shows how many warnings will be printed to the kernel log before it stops. This number is initialized to one at system boot and be set by writing into this file dma-api/min_free_entries This read-only file can be read to get the minimum number of free dma_debug_entries the allocator has ever seen. If this value goes down to zero the code will disable itself because it is not longer reliable. dma-api/num_free_entries The current number of free dma_debug_entries in the allocator. If you have this code compiled into your kernel it will be enabled by default. If you want to boot without the bookkeeping anyway you can provide 'dma_debug=off' as a boot parameter. This will disable DMA-API debugging. Notice that you can not enable it again at runtime. You have to reboot to do so. When the code disables itself at runtime this is most likely because it ran out of dma_debug_entries. These entries are preallocated at boot. The number of preallocated entries is defined per architecture. If it is too low for you boot with 'dma_debug_entries=<your_desired_number>' to overwrite the architectural default.
Documentation/kernel-parameters.txt +10 −0 Original line number Diff line number Diff line Loading @@ -491,6 +491,16 @@ and is between 256 and 4096 characters. It is defined in the file Range: 0 - 8192 Default: 64 dma_debug=off If the kernel is compiled with DMA_API_DEBUG support this option disables the debugging code at boot. dma_debug_entries=<number> This option allows to tune the number of preallocated entries for DMA-API debugging code. One entry is required per DMA-API allocation. Use this if the DMA-API debugging code disables itself because the architectural default is too low. hpet= [X86-32,HPET] option to control HPET usage Format: { enable (default) | disable | force } disable: disable HPET and use PIT instead Loading
arch/Kconfig +2 −0 Original line number Diff line number Diff line Loading @@ -106,3 +106,5 @@ config HAVE_CLK The <linux/clk.h> calls support software clock gating and thus are a key power management tool on many systems. config HAVE_DMA_API_DEBUG bool
arch/x86/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ config X86 select HAVE_GENERIC_DMA_COHERENT if X86_32 select HAVE_EFFICIENT_UNALIGNED_ACCESS select USER_STACKTRACE_SUPPORT select HAVE_DMA_API_DEBUG config ARCH_DEFCONFIG string Loading
arch/x86/include/asm/dma-mapping.h +39 −6 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ */ #include <linux/scatterlist.h> #include <linux/dma-debug.h> #include <linux/dma-attrs.h> #include <asm/io.h> #include <asm/swiotlb.h> Loading Loading @@ -56,11 +57,16 @@ dma_map_single(struct device *hwdev, void *ptr, size_t size, enum dma_data_direction dir) { struct dma_map_ops *ops = get_dma_ops(hwdev); dma_addr_t addr; BUG_ON(!valid_dma_direction(dir)); return ops->map_page(hwdev, virt_to_page(ptr), addr = ops->map_page(hwdev, virt_to_page(ptr), (unsigned long)ptr & ~PAGE_MASK, size, dir, NULL); debug_dma_map_page(hwdev, virt_to_page(ptr), (unsigned long)ptr & ~PAGE_MASK, size, dir, addr, true); return addr; } static inline void Loading @@ -72,6 +78,7 @@ dma_unmap_single(struct device *dev, dma_addr_t addr, size_t size, BUG_ON(!valid_dma_direction(dir)); if (ops->unmap_page) ops->unmap_page(dev, addr, size, dir, NULL); debug_dma_unmap_page(dev, addr, size, dir, true); } static inline int Loading @@ -79,9 +86,13 @@ dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, enum dma_data_direction dir) { struct dma_map_ops *ops = get_dma_ops(hwdev); int ents; BUG_ON(!valid_dma_direction(dir)); return ops->map_sg(hwdev, sg, nents, dir, NULL); ents = ops->map_sg(hwdev, sg, nents, dir, NULL); debug_dma_map_sg(hwdev, sg, nents, ents, dir); return ents; } static inline void Loading @@ -91,6 +102,7 @@ dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, struct dma_map_ops *ops = get_dma_ops(hwdev); BUG_ON(!valid_dma_direction(dir)); debug_dma_unmap_sg(hwdev, sg, nents, dir); if (ops->unmap_sg) ops->unmap_sg(hwdev, sg, nents, dir, NULL); } Loading @@ -104,6 +116,7 @@ dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t dma_handle, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_single_for_cpu) ops->sync_single_for_cpu(hwdev, dma_handle, size, dir); debug_dma_sync_single_for_cpu(hwdev, dma_handle, size, dir); flush_write_buffers(); } Loading @@ -116,6 +129,7 @@ dma_sync_single_for_device(struct device *hwdev, dma_addr_t dma_handle, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_single_for_device) ops->sync_single_for_device(hwdev, dma_handle, size, dir); debug_dma_sync_single_for_device(hwdev, dma_handle, size, dir); flush_write_buffers(); } Loading @@ -130,6 +144,8 @@ dma_sync_single_range_for_cpu(struct device *hwdev, dma_addr_t dma_handle, if (ops->sync_single_range_for_cpu) ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, size, dir); debug_dma_sync_single_range_for_cpu(hwdev, dma_handle, offset, size, dir); flush_write_buffers(); } Loading @@ -144,6 +160,8 @@ dma_sync_single_range_for_device(struct device *hwdev, dma_addr_t dma_handle, if (ops->sync_single_range_for_device) ops->sync_single_range_for_device(hwdev, dma_handle, offset, size, dir); debug_dma_sync_single_range_for_device(hwdev, dma_handle, offset, size, dir); flush_write_buffers(); } Loading @@ -156,6 +174,7 @@ dma_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_sg_for_cpu) ops->sync_sg_for_cpu(hwdev, sg, nelems, dir); debug_dma_sync_sg_for_cpu(hwdev, sg, nelems, dir); flush_write_buffers(); } Loading @@ -168,6 +187,7 @@ dma_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, BUG_ON(!valid_dma_direction(dir)); if (ops->sync_sg_for_device) ops->sync_sg_for_device(hwdev, sg, nelems, dir); debug_dma_sync_sg_for_device(hwdev, sg, nelems, dir); flush_write_buffers(); } Loading @@ -177,15 +197,24 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, enum dma_data_direction dir) { struct dma_map_ops *ops = get_dma_ops(dev); dma_addr_t addr; BUG_ON(!valid_dma_direction(dir)); return ops->map_page(dev, page, offset, size, dir, NULL); addr = ops->map_page(dev, page, offset, size, dir, NULL); debug_dma_map_page(dev, page, offset, size, dir, addr, false); return addr; } static inline void dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir) { dma_unmap_single(dev, addr, size, dir); struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); if (ops->unmap_page) ops->unmap_page(dev, addr, size, dir, NULL); debug_dma_unmap_page(dev, addr, size, dir, false); } static inline void Loading Loading @@ -250,8 +279,11 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, if (!ops->alloc_coherent) return NULL; return ops->alloc_coherent(dev, size, dma_handle, memory = ops->alloc_coherent(dev, size, dma_handle, dma_alloc_coherent_gfp_flags(dev, gfp)); debug_dma_alloc_coherent(dev, size, *dma_handle, memory); return memory; } static inline void dma_free_coherent(struct device *dev, size_t size, Loading @@ -264,6 +296,7 @@ static inline void dma_free_coherent(struct device *dev, size_t size, if (dma_release_from_coherent(dev, get_order(size), vaddr)) return; debug_dma_free_coherent(dev, size, vaddr, bus); if (ops->free_coherent) ops->free_coherent(dev, size, vaddr, bus); } Loading