dma-debug cleanup and dynamic allocation
Subsystems (3)
Robin Murphy
reviewerv3 cover dma-debug cleanup and dynamic allocation
Hi all,
Here's some assorted cleanup and improvements to dma-debug which grew out of the problem that certain drivers use very large numbers of DMA mappings, and knowing when to override "dma_debug_entries=..." and what value to override it with can be a less-than-obvious task for users.
The main part is patch #4, wherein we make dma-debug clever enough to allocate more entries dynamically if needed, such that the preallocation value becomes more of a quality-of-life option than a necessity. Patches #5 and #6 do some cruft-removal to allow patch #7 to make the allocation behaviour more efficient in general.
Patches #1, #2 and #4 are some other cleanup and handy features which fell out of the discussion/development.
Robin.
Robin Murphy (7):
dma-debug: Use pr_fmt()
dma-debug: Expose nr_total_entries in debugfs
dma-debug: Dynamically expand the dma_debug_entry pool
dma-debug: Make leak-like behaviour apparent x86/dma/amd-gart: Stop resizing dma_debug_entry pool dma/debug: Remove dma_debug_resize_entries()
dma-debug: Batch dma_debug_entry allocation
Documentation/DMA-API.txt | 20 +- Documentation/x86/x86_64/boot-options.txt | 5 +- arch/x86/kernel/amd_gart_64.c | 23 --- include/linux/dma-debug.h | 7 - kernel/dma/debug.c | 217 ++++++++++------------ 5 files changed, 109 insertions(+), 163 deletions(-)
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 1/7 dma-debug: Use pr_fmt()
Use pr_fmt() to generate the "DMA-API: " prefix consistently. This results in it being added to a couple of pr_*() messages which were missing it before, and for the err_printk() calls moves it to the actual start of the message instead of somewhere in the middle.
---
v3: No change
kernel/dma/debug.c | 74 ++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 36 deletions(-)
kernel/dma/debug.c +38 -36
@@ -17,6 +17,8 @@* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt (fmt) "DMA-API: " fmt +#include <linux/sched/task_stack.h> #include <linux/scatterlist.h> #include <linux/dma-mapping.h>
@@ -234,7 +236,7 @@ static bool driver_filter(struct device *dev) error_count +=1 ; \if (driver_filter (dev) && \ (show_all_errors || show_num_errors >0 )) { \ -WARN (1 ,"%s %s: " format, \ + WARN(1, pr_fmt("%s %s: ") format, \ dev ?dev_driver_string (dev) :"NULL" , \ dev ? dev_name(dev) : "NULL", ## arg); \dump_entry_trace (entry); \
@@ -519,7 +521,7 @@ static void active_cacheline_inc_overlap(phys_addr_t cln)* prematurely. */ WARN_ONCE (overlap >ACTIVE_CACHELINE_MAX_OVERLAP , -"DMA-API: exceeded %d overlapping mappings of cacheline %pa\n" , +pr_fmt ("exceeded %d overlapping mappings of cacheline %pa\n" ),ACTIVE_CACHELINE_MAX_OVERLAP , &cln); }
@@ -614,7 +616,7 @@ void debug_dma_assert_idle(struct page *page) cln =to_cacheline_number (entry);err_printk (entry->dev, entry, -"DMA-API: cpu touching an active dma mapped cacheline [cln=%pa]\n" , +"cpu touching an active dma mapped cacheline [cln=%pa]\n" , &cln); }
@@ -634,7 +636,7 @@ static void add_dma_entry(struct dma_debug_entry *entry) rc =active_cacheline_insert (entry);if (rc == -ENOMEM ) { -pr_err ("DMA-API: cacheline tracking ENOMEM, dma-debug disabled\n" ); +pr_err ("cacheline tracking ENOMEM, dma-debug disabled\n" ); global_disable = true; }
@@ -673,7 +675,7 @@ static struct dma_debug_entry *dma_entry_alloc(void)if (list_empty (&free_entries)) { global_disable = true;spin_unlock_irqrestore (&free_entries_lock, flags); -pr_err ("DMA-API: debugging out of memory - disabling\n" ); +pr_err ("debugging out of memory - disabling\n" );return NULL ; }
@@ -777,7 +779,7 @@ static int prealloc_memory(u32 num_entries) num_free_entries = num_entries; min_free_entries = num_entries; -pr_info ("DMA-API: preallocated %d debug entries\n" , num_entries); +pr_info ("preallocated %d debug entries\n" , num_entries);return 0 ;
@@ -850,7 +852,7 @@ static ssize_t filter_write(struct file *file, const char __user *userbuf,* switched off. */ if (current_driver_name[0 ]) -pr_info ("DMA-API: switching off dma-debug driver filter\n" ); +pr_info ("switching off dma-debug driver filter\n" ); current_driver_name[0 ] =0 ; current_driver =NULL ; goto out_unlock;
@@ -868,7 +870,7 @@ static ssize_t filter_write(struct file *file, const char __user *userbuf, current_driver_name[i] =0 ; current_driver =NULL ; -pr_info ("DMA-API: enable driver filter for driver [%s]\n" , +pr_info ("enable driver filter for driver [%s]\n" , current_driver_name); out_unlock:
@@ -887,7 +889,7 @@ static int dma_debug_fs_init(void) { dma_debug_dent =debugfs_create_dir ("dma-api" ,NULL );if (!dma_debug_dent) { -pr_err ("DMA-API: can not create debugfs directory\n" ); +pr_err ("can not create debugfs directory\n" );return -ENOMEM ; }
@@ -973,7 +975,7 @@ static int dma_debug_device_change(struct notifier_block *nb, unsigned long acti count =device_dma_allocations (dev, &entry);if (count ==0 )break ; -err_printk (dev, entry,"DMA-API: device driver has pending " +err_printk (dev, entry,"device driver has pending " "DMA allocations while released from device " "[count=%d]\n" "One of leaked entries details: "
@@ -1023,14 +1025,14 @@ static int dma_debug_init(void) }if (dma_debug_fs_init () !=0 ) { -pr_err ("DMA-API: error creating debugfs entries - disabling\n" ); +pr_err ("error creating debugfs entries - disabling\n" ); global_disable = true;return 0 ; }if (prealloc_memory (nr_prealloc_entries) !=0 ) { -pr_err ("DMA-API: debugging out of memory error - disabled\n" ); +pr_err ("debugging out of memory error - disabled\n" ); global_disable = true;return 0 ;
@@ -1040,7 +1042,7 @@ static int dma_debug_init(void) dma_debug_initialized = true; -pr_info ("DMA-API: debugging enabled by kernel config\n" ); +pr_info ("debugging enabled by kernel config\n" );return 0 ; }core_initcall (dma_debug_init);
@@ -1051,7 +1053,7 @@ static __init int dma_debug_cmdline(char *str)return -EINVAL ;if (strncmp (str,"off" ,3 ) ==0 ) { -pr_info ("DMA-API: debugging disabled on kernel command line\n" ); +pr_info ("debugging disabled on kernel command line\n" ); global_disable = true; }
@@ -1085,11 +1087,11 @@ static void check_unmap(struct dma_debug_entry *ref)if (dma_mapping_error (ref->dev, ref->dev_addr)) {err_printk (ref->dev,NULL , -"DMA-API: device driver tries to free an " +"device driver tries to free an " "invalid DMA memory address\n" ); }else {err_printk (ref->dev,NULL , -"DMA-API: device driver tries to free DMA " +"device driver tries to free DMA " "memory it has not allocated [device " "address=0x%016llx] [size=%llu bytes]\n" , ref->dev_addr, ref->size);
@@ -1098,7 +1100,7 @@ static void check_unmap(struct dma_debug_entry *ref) }if (ref->size != entry->size) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " + err_printk(ref->dev, entry, "device driver frees " "DMA memory with different size " "[device address=0x%016llx] [map size=%llu bytes] " "[unmap size=%llu bytes]\n",
@@ -1106,7 +1108,7 @@ static void check_unmap(struct dma_debug_entry *ref) }if (ref->type != entry->type) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " + err_printk(ref->dev, entry, "device driver frees " "DMA memory with wrong function " "[device address=0x%016llx] [size=%llu bytes] " "[mapped as %s] [unmapped as %s]\n",
@@ -1114,7 +1116,7 @@ static void check_unmap(struct dma_debug_entry *ref) type2name[entry->type], type2name[ref->type]); }else if ((entry->type == dma_debug_coherent) && (phys_addr (ref) !=phys_addr (entry))) { -err_printk (ref->dev, entry,"DMA-API: device driver frees " +err_printk (ref->dev, entry,"device driver frees " "DMA memory with different CPU address " "[device address=0x%016llx] [size=%llu bytes] " "[cpu alloc address=0x%016llx] "
@@ -1126,7 +1128,7 @@ static void check_unmap(struct dma_debug_entry *ref)if (ref->sg_call_ents && ref->type == dma_debug_sg && ref->sg_call_ents != entry->sg_call_ents) { -err_printk (ref->dev, entry,"DMA-API: device driver frees " +err_printk (ref->dev, entry,"device driver frees " "DMA sg list with different entry count " "[map count=%d] [unmap count=%d]\n" , entry->sg_call_ents, ref->sg_call_ents);
@@ -1137,7 +1139,7 @@ static void check_unmap(struct dma_debug_entry *ref)* DMA API don't handle this properly, so check for it here */ if (ref->direction != entry->direction) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " + err_printk(ref->dev, entry, "device driver frees " "DMA memory with different direction " "[device address=0x%016llx] [size=%llu bytes] " "[mapped with %s] [unmapped with %s]\n",
@@ -1153,7 +1155,7 @@ static void check_unmap(struct dma_debug_entry *ref)*/ if (entry->map_err_type ==MAP_ERR_NOT_CHECKED ) { err_printk(ref->dev, entry, - "DMA-API: device driver failed to check map error" + "device driver failed to check map error" "[device address=0x%016llx] [size=%llu bytes] " "[mapped as %s]", ref->dev_addr, ref->size,
@@ -1178,7 +1180,7 @@ static void check_for_stack(struct device *dev,return ; addr =page_address (page) + offset;if (object_is_on_stack (addr)) -err_printk (dev,NULL ,"DMA-API: device driver maps memory from stack [addr=%p]\n" , addr); +err_printk (dev,NULL ,"device driver maps memory from stack [addr=%p]\n" , addr); }else {/* Stack is vmalloced. */ int i;
@@ -1188,7 +1190,7 @@ static void check_for_stack(struct device *dev,continue ; addr = (u8 *)current->stack + i *PAGE_SIZE + offset; -err_printk (dev,NULL ,"DMA-API: device driver maps memory from stack [probable addr=%p]\n" , addr); +err_printk (dev,NULL ,"device driver maps memory from stack [probable addr=%p]\n" , addr);break ; } }
@@ -1208,7 +1210,7 @@ static void check_for_illegal_area(struct device *dev, void *addr, unsigned long {if (overlap (addr, len, _stext, _etext) ||overlap (addr, len, __start_rodata, __end_rodata)) -err_printk (dev,NULL ,"DMA-API: device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n" , addr, len); +err_printk (dev,NULL ,"device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n" , addr, len); } static void check_sync(struct device *dev,
@@ -1224,7 +1226,7 @@ static void check_sync(struct device *dev, entry =bucket_find_contain (&bucket, ref, &flags);if (!entry) { -err_printk (dev,NULL ,"DMA-API: device driver tries " +err_printk (dev,NULL ,"device driver tries " "to sync DMA memory it has not allocated " "[device address=0x%016llx] [size=%llu bytes]\n" , (unsigned long long )ref->dev_addr, ref->size);
@@ -1232,7 +1234,7 @@ static void check_sync(struct device *dev, }if (ref->size > entry->size) { -err_printk (dev, entry,"DMA-API: device driver syncs" +err_printk (dev, entry,"device driver syncs" " DMA memory outside allocated range " "[device address=0x%016llx] " "[allocation size=%llu bytes] "
@@ -1245,7 +1247,7 @@ static void check_sync(struct device *dev, goto out;if (ref->direction != entry->direction) { - err_printk(dev, entry, "DMA-API: device driver syncs " + err_printk(dev, entry, "device driver syncs " "DMA memory with different direction " "[device address=0x%016llx] [size=%llu bytes] " "[mapped with %s] [synced with %s]\n",
@@ -1256,7 +1258,7 @@ static void check_sync(struct device *dev,if (to_cpu && !(entry->direction ==DMA_FROM_DEVICE ) && !(ref->direction ==DMA_TO_DEVICE )) - err_printk(dev, entry, "DMA-API: device driver syncs " + err_printk(dev, entry, "device driver syncs " "device read-only DMA memory for cpu " "[device address=0x%016llx] [size=%llu bytes] " "[mapped with %s] [synced with %s]\n",
@@ -1266,7 +1268,7 @@ static void check_sync(struct device *dev,if (!to_cpu && !(entry->direction ==DMA_TO_DEVICE ) && !(ref->direction ==DMA_FROM_DEVICE )) - err_printk(dev, entry, "DMA-API: device driver syncs " + err_printk(dev, entry, "device driver syncs " "device write-only DMA memory to device " "[device address=0x%016llx] [size=%llu bytes] " "[mapped with %s] [synced with %s]\n",
@@ -1276,7 +1278,7 @@ static void check_sync(struct device *dev,if (ref->sg_call_ents && ref->type == dma_debug_sg && ref->sg_call_ents != entry->sg_call_ents) { -err_printk (ref->dev, entry,"DMA-API: device driver syncs " +err_printk (ref->dev, entry,"device driver syncs " "DMA sg list with different entry count " "[map count=%d] [sync count=%d]\n" , entry->sg_call_ents, ref->sg_call_ents);
@@ -1297,7 +1299,7 @@ static void check_sg_segment(struct device *dev, struct scatterlist *sg)* whoever generated the list forgot to check them. */ if (sg->length > max_seg) -err_printk (dev,NULL ,"DMA-API: mapping sg segment longer than device claims to support [len=%u] [max=%u]\n" , +err_printk (dev,NULL ,"mapping sg segment longer than device claims to support [len=%u] [max=%u]\n" , sg->length, max_seg);/* * In some cases this could potentially be the DMA API
@@ -1307,7 +1309,7 @@ static void check_sg_segment(struct device *dev, struct scatterlist *sg) start =sg_dma_address (sg); end = start +sg_dma_len (sg) -1 ;if ((start ^ end) & ~boundary) -err_printk (dev,NULL ,"DMA-API: mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n" , +err_printk (dev,NULL ,"mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n" , start, end, boundary);#endif }
@@ -1319,11 +1321,11 @@ void debug_dma_map_single(struct device *dev, const void *addr,return ;if (!virt_addr_valid (addr)) -err_printk (dev,NULL ,"DMA-API: device driver maps memory from invalid area [addr=%p] [len=%lu]\n" , +err_printk (dev,NULL ,"device driver maps memory from invalid area [addr=%p] [len=%lu]\n" , addr, len);if (is_vmalloc_addr (addr)) -err_printk (dev,NULL ,"DMA-API: device driver maps memory from vmalloc area [addr=%p] [len=%lu]\n" , +err_printk (dev,NULL ,"device driver maps memory from vmalloc area [addr=%p] [len=%lu]\n" , addr, len); }EXPORT_SYMBOL (debug_dma_map_single);
@@ -1780,7 +1782,7 @@ static int __init dma_debug_driver_setup(char *str) }if (current_driver_name[0 ]) -pr_info ("DMA-API: enable driver filter for driver [%s]\n" , +pr_info ("enable driver filter for driver [%s]\n" , current_driver_name);
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 2/7 dma-debug: Expose nr_total_entries in debugfs
In reply to Robin MurphyExpose nr_total_entries in debugfs, so that {num,min}_free_entries become even more meaningful to users interested in current/maximum utilisation. This becomes even more relevant once nr_total_entries may change at runtime beyond just the existing AMD GART debug code.
---
v3: Add Christoph's review tag
Documentation/DMA-API.txt | 3 +++ kernel/dma/debug.c | 7 +++++++ 2 files changed, 10 insertions(+)
Documentation/DMA-API.txt +3 -0
@@ -723,6 +723,9 @@ dma-api/min_free_entries This read-only file can be read to get the dma-api/num_free_entries The current number of free dma_debug_entries in the allocator. +dma-api/nr_total_entries The total number of dma_debug_entries in the + allocator, both free and used. + dma-api/driver-filter You can write a name of a driver into this file to limit the debug output to requests from that particular driver. Write an empty string to
kernel/dma/debug.c +7 -0
@@ -144,6 +144,7 @@ static struct dentry *show_all_errors_dent __read_mostly;static struct dentry *show_num_errors_dent__read_mostly ;static struct dentry *num_free_entries_dent__read_mostly ;static struct dentry *min_free_entries_dent__read_mostly ; +static struct dentry *nr_total_entries_dent__read_mostly ;static struct dentry *filter_dent__read_mostly ;/* per-driver filter related state */
@@ -928,6 +929,12 @@ static int dma_debug_fs_init(void)if (!min_free_entries_dent) goto out_err; + nr_total_entries_dent =debugfs_create_u32 ("nr_total_entries" ,0444 , + dma_debug_dent, + &nr_total_entries); +if (!nr_total_entries_dent) + goto out_err; + filter_dent =debugfs_create_file ("driver_filter" ,0644 , dma_debug_dent,NULL , &filter_fops);if (!filter_dent)
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 3/7 dma-debug: Dynamically expand the dma_debug_entry pool
In reply to Robin MurphyCertain drivers such as large multi-queue network adapters can use pools of mapped DMA buffers larger than the default dma_debug_entry pool of 65536 entries, with the result that merely probing such a device can cause DMA debug to disable itself during boot unless explicitly given an appropriate "dma_debug_entries=..." option.
Developers trying to debug some other driver on such a system may not be immediately aware of this, and at worst it can hide bugs if they fail to realise that dma-debug has already disabled itself unexpectedly by the time their code of interest gets to run. Even once they do realise, it can be a bit of a pain to emprirically determine a suitable number of preallocated entries to configure, short of massively over-allocating.
There's really no need for such a static limit, though, since we can quite easily expand the pool at runtime in those rare cases that the preallocated entries are insufficient, which is arguably the least surprising and most useful behaviour. To that end, refactor the prealloc_memory() logic a little bit to generalise it for runtime reallocations as well.
---
v3: Drop refactoring of dma_debug_resize_entries(), misc. cosmetic tweaks
Documentation/DMA-API.txt | 11 +++--- kernel/dma/debug.c | 79 ++++++++++++++++++++------------------- 2 files changed, 46 insertions(+), 44 deletions(-)
Documentation/DMA-API.txt +5 -6
@@ -717,8 +717,8 @@ dma-api/num_errors The number in this file shows how many 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. + down to zero the code will attempt to increase + nr_total_entries to compensate. dma-api/num_free_entries The current number of free dma_debug_entries in the allocator.
@@ -745,10 +745,9 @@ driver filter at boot time. The debug code will only print errors for that driver afterwards. This filter can be disabled or changed later using debugfs. 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. +out of dma_debug_entries and was unable to allocate more on-demand. 65536 +entries are preallocated at boot - if this is too low for you boot with +'dma_debug_entries=<your_desired_number>' to overwrite the default. ::
kernel/dma/debug.c +41 -38
115 lines not on this page · raw message
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 4/7 dma-debug: Make leak-like behaviour apparent
In reply to Robin MurphyNow that we can dynamically allocate DMA debug entries to cope with drivers maintaining excessively large numbers of live mappings, a driver which *does* actually have a bug leaking mappings (and is not unloaded) will no longer trigger the "DMA-API: debugging out of memory - disabling" message until it gets to actual kernel OOM conditions, which means it could go unnoticed for a while. To that end, let's inform the user each time the pool has grown to a multiple of its initial size, which should make it apparent that they either have a leak or might want to increase the preallocation size.
---
v3: Add Christoph's review tag
Documentation/DMA-API.txt | 6 +++++- kernel/dma/debug.c | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-)
Documentation/DMA-API.txt +5 -1
@@ -747,7 +747,11 @@ driver afterwards. This filter can be disabled or changed later using debugfs. When the code disables itself at runtime this is most likely because it ran out of dma_debug_entries and was unable to allocate more on-demand. 65536 entries are preallocated at boot - if this is too low for you boot with -'dma_debug_entries=<your_desired_number>' to overwrite the default. +'dma_debug_entries=<your_desired_number>' to overwrite the default. The +code will print to the kernel log each time it has dynamically allocated +as many entries as were initially preallocated. This is to indicate that a +larger preallocation size may be appropriate, or if it happens continually +that a driver may be leaking mappings. ::
kernel/dma/debug.c +13 -0
@@ -691,6 +691,18 @@ static struct dma_debug_entry *__dma_entry_alloc(void)return entry; } +void __dma_entry_alloc_check_leak (void ) +{ +u32 tmp = nr_total_entries % nr_prealloc_entries; + +/* Shout each time we tick over some multiple of the initial pool */ +if (tmp <DMA_DEBUG_DYNAMIC_ENTRIES ) { +pr_info ("dma_debug_entry pool grown to %u (%u00%%)\n" , + nr_total_entries, + (nr_total_entries / nr_prealloc_entries)); + } +} +/* struct dma_entry allocator * * The next two functions implement the allocator for
@@ -710,6 +722,7 @@ static struct dma_debug_entry *dma_entry_alloc(void)pr_err ("debugging out of memory - disabling\n" );return NULL ; } +__dma_entry_alloc_check_leak (); } entry =__dma_entry_alloc ();
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 5/7 x86/dma/amd-gart: Stop resizing dma_debug_entry pool
In reply to Robin Murphydma-debug is now capable of adding new entries to its pool on-demand if the initial preallocation was insufficient, so the IOMMU_LEAK logic no longer needs to explicitly change the pool size. This does lose it the ability to save a couple of megabytes of RAM by reducing the pool size below its default, but it seems unlikely that that is a realistic concern these days (or indeed that anyone is actively debugging AGP drivers' DMA usage any more). Getting rid of dma_debug_resize_entries() will make room for further streamlining in the dma-debug code itself.
Removing the call reveals quite a lot of cruft which has been useless for nearly a decade since commit 19c1a6f5764d ("x86 gart: reimplement IOMMU_LEAK feature by using DMA_API_DEBUG"), including the entire 'iommu=leak' parameter, which controlled nothing except whether dma_debug_resize_entries() was called or not.
---
v3: Add Christoph's review tag
Documentation/x86/x86_64/boot-options.txt | 5 +---- arch/x86/kernel/amd_gart_64.c | 23 ----------------------- 2 files changed, 1 insertion(+), 27 deletions(-)
Documentation/x86/x86_64/boot-options.txt +1 -4
@@ -209,7 +209,7 @@ IOMMU (input/output memory management unit) mapping with memory protection, etc. Kernel boot message: "PCI-DMA: Using Calgary IOMMU" - iommu=[<size>][,noagp][,off][,force][,noforce][,leak[=<nr_of_leak_pages>] + iommu=[<size>][,noagp][,off][,force][,noforce] [,memaper[=<order>]][,merge][,fullflush][,nomerge] [,noaperture][,calgary]
@@ -228,9 +228,6 @@ IOMMU (input/output memory management unit) allowed Overwrite iommu off workarounds for specific chipsets. fullflush Flush IOMMU on each allocation (default). nofullflush Don't use IOMMU fullflush. - leak Turn on simple iommu leak tracing (only when - CONFIG_IOMMU_LEAK is on). Default number of leak pages - is 20. memaper[=<order>] Allocate an own aperture over RAM with size 32MB<<order. (default: order=1, i.e. 64MB) merge Do scatter-gather (SG) merging. Implies "force"
arch/x86/kernel/amd_gart_64.c +0 -23
@@ -155,9 +155,6 @@ static void flush_gart(void)#ifdef CONFIG_IOMMU_LEAK /* Debugging aid for drivers that don't free their IOMMU tables */ -static int leak_trace; -static int iommu_leak_pages =20 ; -static void dump_leak (void ) {static int dump;
@@ -774,16 +771,6 @@ int __init gart_iommu_init(void)if (!iommu_gart_bitmap)panic ("Cannot allocate iommu bitmap\n" ); -#ifdef CONFIG_IOMMU_LEAK -if (leak_trace) { -int ret; - - ret =dma_debug_resize_entries (iommu_pages); -if (ret) -pr_debug ("PCI-DMA: Cannot trace all the entries\n" ); - } -#endif -/* * Out of IOMMU space handling. * Reserve some invalid pages at the beginning of the GART.
@@ -853,16 +840,6 @@ void __init gart_parse_options(char *p) {int arg; -#ifdef CONFIG_IOMMU_LEAK -if (!strncmp (p,"leak" ,4 )) { - leak_trace =1 ; - p +=4 ; -if (*p =='=' ) - ++p; -if (isdigit (*p) &&get_option (&p, &arg)) - iommu_leak_pages = arg; - } -#endif if (isdigit (*p) &&get_option (&p, &arg)) iommu_size = arg;if (!strncmp (p,"fullflush" ,9 ))
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 6/7 dma/debug: Remove dma_debug_resize_entries()
In reply to Robin MurphyWith the only caller now gone, we can clean up this part of dma-debug's exposed internals and make way to tweak the allocation behaviour.
---
v3: Add Christoph's review tag
include/linux/dma-debug.h | 7 ------ kernel/dma/debug.c | 46 --------------------------------------- 2 files changed, 53 deletions(-)
include/linux/dma-debug.h +0 -7
@@ -30,8 +30,6 @@ struct bus_type;extern void dma_debug_add_bus (struct bus_type *bus); -extern int dma_debug_resize_entries (u32 num_entries); -extern void debug_dma_map_single (struct device *dev,const void *addr,unsigned long len);
@@ -101,11 +99,6 @@ static inline void dma_debug_add_bus(struct bus_type *bus) { } -static inline int dma_debug_resize_entries (u32 num_entries) -{ -return 0 ; -} -static inline void debug_dma_map_single (struct device *dev,const void *addr,unsigned long len) {
kernel/dma/debug.c +0 -46
52 lines not on this page · raw message
Signature · 1 line
2.19.1.dirty
Robin Murphy
reviewerPATCH v3 7/7 dma-debug: Batch dma_debug_entry allocation
In reply to Robin MurphyDMA debug entries are one of those things which aren't that useful individually - we will always want some larger quantity of them - and which we don't really need to manage the exact number of - we only care about having 'enough'. In that regard, the current behaviour of creating them one-by-one leads to a lot of unwarranted function call overhead and memory wasted on alignment padding.
Now that we don't have to worry about freeing anything via dma_debug_resize_entries(), we can optimise the allocation behaviour by grabbing whole pages at once, which will save considerably on the aforementioned overheads, and probably offer a little more cache/TLB locality benefit for traversing the lists under normal operation. This should also give even less reason for an architecture-level override of the preallocation size, so make the definition unconditional - if there is still any desire to change the compile-time value for some platforms it would be better off as a Kconfig option anyway.
Since freeing a whole page of entries at once becomes enough of a challenge that it's not really worth complicating dma_debug_init(), we may as well tweak the preallocation behaviour such that as long as we manage to allocate *some* pages, we can leave debugging enabled on a best-effort basis rather than otherwise wasting them.
---
v3: Clean up #ifdef, misc. cosmetic tweaks, add Christoph's review tag
Documentation/DMA-API.txt | 4 +++- kernel/dma/debug.c | 50 ++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 30 deletions(-)
Documentation/DMA-API.txt +3 -1
@@ -747,7 +747,9 @@ driver afterwards. This filter can be disabled or changed later using debugfs. When the code disables itself at runtime this is most likely because it ran out of dma_debug_entries and was unable to allocate more on-demand. 65536 entries are preallocated at boot - if this is too low for you boot with -'dma_debug_entries=<your_desired_number>' to overwrite the default. The +'dma_debug_entries=<your_desired_number>' to overwrite the default. Note +that the code allocates entries in batches, so the exact number of +preallocated entries may be greater than the actual number requested. The code will print to the kernel log each time it has dynamically allocated as many entries as were initially preallocated. This is to indicate that a larger preallocation size may be appropriate, or if it happens continually
kernel/dma/debug.c +21 -29
95 lines not on this page · raw message
Signature · 1 line
2.19.1.dirty
Quoted 28 lines · Robin Murphy
Hi all,
Here's some assorted cleanup and improvements to dma-debug which grew out of the problem that certain drivers use very large numbers of DMA mappings, and knowing when to override "dma_debug_entries=..." and what value to override it with can be a less-than-obvious task for users.
The main part is patch #4, wherein we make dma-debug clever enough to allocate more entries dynamically if needed, such that the preallocation value becomes more of a quality-of-life option than a necessity. Patches #5 and #6 do some cruft-removal to allow patch #7 to make the allocation behaviour more efficient in general.
Patches #1, #2 and #4 are some other cleanup and handy features which fell out of the discussion/development.
Robin.
Robin Murphy (7):
dma-debug: Usepr_fmt()
dma-debug: Exposenr_total_entriesin debugfs
dma-debug: Dynamically expand thedma_debug_entrypool
dma-debug: Make leak-like behaviour apparent x86/dma/amd-gart: Stop resizingdma_debug_entrypool dma/debug: Removedma_debug_resize_entries()
dma-debug: Batchdma_debug_entryallocation
Quoted 7 lines
Documentation/DMA-API.txt | 20 +- Documentation/x86/x86_64/boot-options.txt | 5 +- arch/x86/kernel/amd_gart_64.c | 23 --- include/linux/dma-debug.h | 7 - kernel/dma/debug.c | 217 ++++++++++------------ 5 files changed, 109 insertions(+), 163 deletions(-)
Re: [PATCH v3 5/7] x86/dma/amd-gart: Stop resizing dma_debug_entry pool
In reply to Robin MurphyQuoted 15 lines · Robin Murphy
dma-debug is now capable of adding new entries to its pool on-demand if the initial preallocation was insufficient, so the
IOMMU_LEAKlogic no longer needs to explicitly change the pool size. This does lose it the ability to save a couple of megabytes of RAM by reducing the pool size below its default, but it seems unlikely that that is a realistic concern these days (or indeed that anyone is actively debugging AGP drivers' DMA usage any more). Getting rid ofdma_debug_resize_entries()will make room for further streamlining in the dma-debug code itself.Removing the call reveals quite a lot of cruft which has been useless for nearly a decade since commit
19c1a6f5764d("x86 gart: reimplementIOMMU_LEAKfeature by usingDMA_API_DEBUG"), including the entire 'iommu=leak' parameter, which controlled nothing except whetherdma_debug_resize_entries()was called or not.
Quoted 7 lines
CC Thomas Gleixner <[email protected]>CC Ingo Molnar <[email protected]>CC Borislav Petkov <[email protected]>CC "H. Peter Anvin" <[email protected]>Reviewed-by Christoph Hellwig <[email protected]>Signed-off-by Robin Murphy <[email protected]>
Christoph Hellwig
AppliedThanks, applied to dma-mapping for-next.