diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index a59638b..d513c8d 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -105,4 +105,10 @@ config SWIOTLB_XEN depends on PCI select SWIOTLB +config SWIOTLB_DEBUG + tristate "swiotlb debug facility" + default m + help + Do not enable it unless you know what you are doing. + endmenu diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index bbc1825..1dea490 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_XENFS) += xenfs/ obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o obj-$(CONFIG_XEN_PLATFORM_PCI) += xen-platform-pci.o obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o +obj-$(CONFIG_SWIOTLB_DEBUG) += dump_swiotlb.o obj-$(CONFIG_XEN_DOM0) += pci.o xen-evtchn-y := evtchn.o diff --git a/drivers/xen/dump_swiotlb.c b/drivers/xen/dump_swiotlb.c new file mode 100644 index 0000000..e0e4a0b --- /dev/null +++ b/drivers/xen/dump_swiotlb.c @@ -0,0 +1,72 @@ +/* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#define DUMP_SWIOTLB_FUN "0.1" + +MODULE_AUTHOR("Konrad Rzeszutek Wilk "); +MODULE_DESCRIPTION("dump swiotlb"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DUMP_SWIOTLB_FUN); + +extern int xen_swiotlb_start_thread(void); +extern void xen_swiotlb_stop_thread(void); +static int __init dump_swiotlb_init(void) +{ + printk(KERN_INFO "Starting SWIOTLB debug thread.\n"); + swiotlb_start_thread(); + xen_swiotlb_start_thread(); + return 0; +} + +static void __exit dump_swiotlb_exit(void) +{ + swiotlb_stop_thread(); + xen_swiotlb_stop_thread(); +} + +module_init(dump_swiotlb_init); +module_exit(dump_swiotlb_exit); diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 6e8c15a..c833501 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -143,6 +143,63 @@ xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs) return 0; } +#include +struct xen_swiotlb_debug { + unsigned long alloc; + unsigned long dealloc; + char dev_name[64]; +}; +static DEFINE_PER_CPU(struct xen_swiotlb_debug, xen_tlb_debug); +#include +static int xen_swiotlb_debug_thread(void *arg) +{ + int cpu; + do { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout_interruptible(HZ*10); + + for_each_online_cpu(cpu) { + struct xen_swiotlb_debug *d; + + d = &per_cpu(xen_tlb_debug, cpu); + /* Can't really happend.*/ + if (!d) + continue; + + if (d->dev_name[0] == 0) + continue; + + printk(KERN_INFO "%u %s alloc coherent: %ld, free: %ld\n", + cpu, + d->dev_name ? d->dev_name : "?", + d->alloc, d->dealloc); + + memset(d, 0, sizeof(struct xen_swiotlb_debug)); + } + + } while (!kthread_should_stop()); + return 0; +} +static struct task_struct *xen_debug_thread = NULL; + +int xen_swiotlb_start_thread(void) { + + if (xen_debug_thread) + return -EINVAL; + printk(KERN_INFO "%s: Go!\n",__func__); + xen_debug_thread = kthread_run(xen_swiotlb_debug_thread, NULL, "xen_swiotlb_debug"); + return 0; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_start_thread); +void xen_swiotlb_stop_thread(void) { + + printk(KERN_INFO "%s: Stop!\n",__func__); + if (xen_debug_thread) + kthread_stop(xen_debug_thread); + xen_debug_thread = NULL; +} +EXPORT_SYMBOL_GPL(xen_swiotlb_stop_thread); + void __init xen_swiotlb_init(int verbose) { unsigned long bytes; @@ -194,7 +251,14 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, int order = get_order(size); u64 dma_mask = DMA_BIT_MASK(32); unsigned long vstart; - + struct xen_swiotlb_debug *d; + + preempt_disable(); + d = &__get_cpu_var(xen_tlb_debug); + preempt_enable(); + d->alloc++; + snprintf(d->dev_name, sizeof(d->dev_name), "%s %s", + dev_driver_string(hwdev), dev_name(hwdev)); /* * Ignore region specifiers - the kernel's ideas of * pseudo-phys memory layout has nothing to do with the @@ -230,6 +294,14 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dev_addr) { int order = get_order(size); + struct xen_swiotlb_debug *d; + + preempt_disable(); + d = &__get_cpu_var(xen_tlb_debug); + preempt_enable(); + d->dealloc++; + snprintf(d->dev_name, sizeof(d->dev_name), "%s %s", + dev_driver_string(hwdev), dev_name(hwdev)); if (dma_release_from_coherent(hwdev, order, vaddr)) return; diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h index 445702c..0d2e049 100644 --- a/include/linux/swiotlb.h +++ b/include/linux/swiotlb.h @@ -26,6 +26,8 @@ extern void swiotlb_init(int verbose); extern void swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose); extern unsigned long swioltb_nr_tbl(void); +extern int swiotlb_start_thread(void); +extern void swiotlb_stop_thread(void); /* * Enumeration for sync targets */ diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 99093b3..5446076 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -92,6 +92,74 @@ static DEFINE_SPINLOCK(io_tlb_lock); static int late_alloc; +#include +struct swiotlb_debug { + unsigned long bounce_to; + unsigned long bounce_from; + unsigned long bounce_slow; + unsigned long map; + unsigned long unmap; + unsigned long sync; + char dev_name[64]; +}; +static DEFINE_PER_CPU(struct swiotlb_debug, tlb_debug); +#include +static int swiotlb_debug_thread(void *arg) +{ + int cpu; + int size = io_tlb_nslabs; + do { + int i; + unsigned long filled = 0; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout_interruptible(HZ*5); + + for_each_online_cpu(cpu) { + struct swiotlb_debug *d = &per_cpu(tlb_debug, cpu); + /* Can't really happend.*/ + if (!d) + continue; + if (d->dev_name[0] == 0) + continue; + + printk(KERN_INFO "%d [%s] bounce: from:%ld(slow:%ld)to:%ld map:%ld unmap:%ld sync:%ld\n", + cpu, + d->dev_name ? d->dev_name : "?", + d->bounce_from, + d->bounce_slow, + d->bounce_to, + d->map, d->unmap, d->sync); + memset(d, 0, sizeof(struct swiotlb_debug)); + } + /* Very crude calculation. */ + for (i = 0; i < size; i++) { + if (io_tlb_list[i] == 0) + filled++; + } + printk(KERN_INFO "SWIOTLB is %ld%% full\n", (filled * 100) / size); + + } while (!kthread_should_stop()); + return 0; +} +static struct task_struct *debug_thread = NULL; + +int swiotlb_start_thread(void) { + + if (debug_thread) + return -EINVAL; + printk(KERN_INFO "%s: Go!\n",__func__); + debug_thread = kthread_run(swiotlb_debug_thread, NULL, "swiotlb_debug"); +} +EXPORT_SYMBOL_GPL(swiotlb_start_thread); +void swiotlb_stop_thread(void) { + + printk(KERN_INFO "%s: Stop!\n",__func__); + if (debug_thread) + kthread_stop(debug_thread); + debug_thread = NULL; +} +EXPORT_SYMBOL_GPL(swiotlb_stop_thread); + static int __init setup_io_tlb_npages(char *str) { @@ -166,6 +234,7 @@ void __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose) panic("Cannot allocate SWIOTLB overflow buffer!\n"); if (verbose) swiotlb_print_info(); + } /* @@ -336,6 +405,7 @@ void swiotlb_bounce(phys_addr_t phys, char *dma_addr, size_t size, enum dma_data_direction dir) { unsigned long pfn = PFN_DOWN(phys); + struct swiotlb_debug *d = &__get_cpu_var(tlb_debug); if (PageHighMem(pfn_to_page(pfn))) { /* The buffer does not have a mapping. Map it in and copy */ @@ -362,11 +432,16 @@ void swiotlb_bounce(phys_addr_t phys, char *dma_addr, size_t size, dma_addr += sz; offset = 0; } + d->bounce_slow++; } else { - if (dir == DMA_TO_DEVICE) + if (dir == DMA_TO_DEVICE) { memcpy(dma_addr, phys_to_virt(phys), size); - else + d->bounce_to++; + } + else { memcpy(phys_to_virt(phys), dma_addr, size); + d->bounce_from++; + } } } EXPORT_SYMBOL_GPL(swiotlb_bounce); @@ -471,7 +546,15 @@ found: io_tlb_orig_addr[index+i] = phys + (i << IO_TLB_SHIFT); if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) swiotlb_bounce(phys, dma_addr, size, DMA_TO_DEVICE); - + { + struct swiotlb_debug *d; + preempt_disable(); + d = &__get_cpu_var(tlb_debug); + preempt_enable(); + d->map++; + snprintf(d->dev_name, sizeof(d->dev_name), "%s %s", + dev_driver_string(hwdev), dev_name(hwdev)); + } return dma_addr; } EXPORT_SYMBOL_GPL(swiotlb_tbl_map_single); @@ -531,6 +614,15 @@ swiotlb_tbl_unmap_single(struct device *hwdev, char *dma_addr, size_t size, io_tlb_list[i] = ++count; } spin_unlock_irqrestore(&io_tlb_lock, flags); + { + struct swiotlb_debug *d; + preempt_disable(); + d = &__get_cpu_var(tlb_debug); + preempt_enable(); + d->unmap++; + snprintf(d->dev_name, sizeof(d->dev_name), "%s %s", + dev_driver_string(hwdev), dev_name(hwdev)); + } } EXPORT_SYMBOL_GPL(swiotlb_tbl_unmap_single); @@ -541,7 +633,13 @@ swiotlb_tbl_sync_single(struct device *hwdev, char *dma_addr, size_t size, { int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT; phys_addr_t phys = io_tlb_orig_addr[index]; - + struct swiotlb_debug *d; + preempt_disable(); + d = &__get_cpu_var(tlb_debug); + preempt_enable(); + d->sync++; + snprintf(d->dev_name, sizeof(d->dev_name), "%s %s", + dev_driver_string(hwdev), dev_name(hwdev)); phys += ((unsigned long)dma_addr & ((1 << IO_TLB_SHIFT) - 1)); switch (target) {