]> xenbits.xensource.com Git - xentesttools/bootstrap.git/commitdiff
drivers: Add dma_test
authorKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Mon, 29 Oct 2012 15:58:31 +0000 (11:58 -0400)
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Mon, 29 Oct 2012 15:58:31 +0000 (11:58 -0400)
to check SWIOTLB for contingous allocations of compound pages.

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
root_image/drivers/Makefile
root_image/drivers/dma_test/Makefile [new file with mode: 0644]
root_image/drivers/dma_test/dma_test.c [new file with mode: 0644]

index 6dbdb758c1cace35b5d62f9a4ec3370e7a82fad5..782655fbf0947c74bca3ac223225966635a9fa3c 100644 (file)
@@ -1,4 +1,4 @@
-obj-m += dump/
+obj-m += dma_test/
 obj-m += wb_to_wc/
 
 .PHONY:        distclean
diff --git a/root_image/drivers/dma_test/Makefile b/root_image/drivers/dma_test/Makefile
new file mode 100644 (file)
index 0000000..e931fac
--- /dev/null
@@ -0,0 +1,37 @@
+# Comment/uncomment the following line to disable/enable debugging
+#DEBUG = y
+
+# Add your debugging flag (or not) to CFLAGS
+ifeq ($(DEBUG),y)
+  DEBFLAGS = -O -g # "-O" is needed to expand inlines
+else
+  DEBFLAGS = -O2
+endif
+
+EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR)
+
+ifneq ($(KERNELRELEASE),)
+# call from kernel build system
+
+obj-m  := dma_test.o
+
+else
+
+KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+PWD       := $(shell pwd)
+
+default:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINCDIR=$(PWD)/../include modules
+
+endif
+
+clean:
+       rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
+
+depend .depend dep:
+       $(CC) $(CFLAGS) -M *.c > .depend
+
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/root_image/drivers/dma_test/dma_test.c b/root_image/drivers/dma_test/dma_test.c
new file mode 100644 (file)
index 0000000..0c5743f
--- /dev/null
@@ -0,0 +1,225 @@
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <xen/page.h>
+
+#define DMA_TEST  "0.1"
+
+MODULE_AUTHOR("Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>");
+MODULE_DESCRIPTION("dma_test");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DMA_TEST);
+
+static struct bus_type fallback_bus_type = {
+       .name = "fallback_bus:",
+};
+
+static void fake_release(struct device *dev)
+{
+       /* No kfree as the device was allocated on stack. */
+}
+
+struct args {
+       int len;
+       enum dma_data_direction dir;
+};
+
+#define MAGIC_DEVICE 0xffffffdd
+#define MAGIC_CPU 0xffffffcc
+
+static int dma_test_thread(void *arg)
+{
+       struct page *page;
+       dma_addr_t dma_addr = 0;
+       struct device fake = {
+               .coherent_dma_mask = DMA_BIT_MASK(32),
+               .bus = &fallback_bus_type,
+               .release = fake_release,
+       };
+       gfp_t gfp = __GFP_COMP | __GFP_NOWARN | GFP_ATOMIC;
+       int ret;
+       int i;
+       void *addr;
+       struct page *p;
+
+       struct args *args = (struct args *)arg;
+       int dir = args->dir;
+       int len = args->len;
+
+       dev_set_name(&fake, "%s", dir == DMA_TO_DEVICE ? "to_dev" : "to_cpu");
+       fake.dma_mask = &fake.coherent_dma_mask;
+       ret = device_register(&fake);
+       if (ret)
+               goto out;
+
+       do {
+               unsigned long prev_mfn = 0;
+               bool bus_and_dma_same;
+
+               page = alloc_pages(gfp, get_order(len));
+               p = page;
+               /* Check that the bus addresses are contingous. */
+               for (i = 0; i < len / PAGE_SIZE; i++, p++) {
+                       unsigned long pfn, mfn;
+
+                       addr = page_address(p);
+                       pfn = PFN_DOWN(virt_to_phys(addr));
+                       if (xen_domain())
+                               mfn = pfn_to_mfn(pfn);
+                       else
+                               mfn = pfn;
+                       if (i != 0) {
+                               if (prev_mfn + 1 != mfn)
+                                       dev_warn(&fake, "va: %lx (pfn:%lx, mfn:%lx) w.r.t prev mfn: %lx!\n",
+                                                (unsigned long)addr, pfn, mfn, prev_mfn);
+                       }
+                       prev_mfn = mfn;
+               }
+               dma_addr = dma_map_page(&fake, page, 0 /* no offset */, len, dir);
+               /* Note, dma_addr is the physical address ! */
+               if (dma_mapping_error(&fake, dma_addr)) {
+                       dev_warn(&fake, "DMA %lx for %lx is not right\n", (unsigned long)dma_addr,
+                               (unsigned long)page_address(page));
+                       __free_pages(page, get_order(len));
+                       page = NULL;
+               }
+               bus_and_dma_same = false;
+               if (page) {
+                       unsigned long phys;
+                       unsigned long pfn, mfn, bus_addr_mfn;
+                       unsigned long bus_addr = 0;
+
+                       p = page;
+                       for (i = 0; i < len / PAGE_SIZE; i++, p++) {
+                               void *bus_va;
+
+                               addr = page_address(p);
+                               phys = virt_to_phys(addr);
+                               pfn = PFN_DOWN(phys);
+
+                               bus_va = (void *)(dma_addr + (i * PAGE_SIZE));
+
+                               if (xen_domain()) {
+                                       void * tmp;
+
+                                       /* Find the bus frame for the physical frame*/
+                                       mfn = pfn_to_mfn(pfn);
+                                       /* and .. voodoo time! */
+                                       bus_addr_mfn = PFN_DOWN(dma_addr + (i * PAGE_SIZE));
+                                       bus_addr = PFN_PHYS(mfn_to_pfn(bus_addr_mfn));
+                                       tmp = __va(bus_addr);
+                                       bus_va = mfn_to_virt(bus_addr_mfn);
+                                       WARN(bus_va != tmp, "Expected %lx (%lx+%d*PAGE_SIZE), got: %lx (pfn: %lx, mfn: %lx)!\n",
+                                               (unsigned long)bus_va, (unsigned long)dma_addr, i, (unsigned long)tmp, PFN_DOWN(bus_addr), bus_addr_mfn);
+                               } else {
+                                       mfn = pfn;
+                                       bus_addr = (unsigned long)bus_va;
+                                       /* Assume DMA addr == physical addr */
+                                       bus_addr_mfn = PFN_DOWN(bus_addr);
+                                       bus_va = __va(PFN_PHYS(bus_addr_mfn));
+                               }
+
+                               dev_info(&fake, "%lx (pfn:%lx, bus frame: %lx) %s %lx (addr: %lx, frame: %lx)\n",
+                                       (unsigned long)addr, pfn, mfn,
+                                       dir == DMA_TO_DEVICE ? "=>" : "<=",
+                                       (unsigned long)bus_va, bus_addr, bus_addr_mfn);
+
+                               if (!virt_addr_valid(bus_va))
+                                       break;
+                               if (!virt_addr_valid(addr))
+                                       break;
+
+                               /* CPU */
+                               memset(addr, 0xCC, PAGE_SIZE);
+
+                               /* Device */
+                               memset(bus_va, 0xDD, PAGE_SIZE);
+                               if (addr == bus_va)
+                                       bus_and_dma_same = true;
+                       }
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout_interruptible(5*HZ);
+
+               if (!page)
+                       continue;
+               p = page;
+
+               for (i = 0; i < len / PAGE_SIZE; i++, p++) {
+                       if (bus_and_dma_same)
+                               continue;
+                       addr = page_address(p);
+                       if (((char *)addr)[0] != MAGIC_CPU)
+                               dev_warn(&fake, "%lx with DMA (%lx) has %x (expected %lx)\n",
+                                       (unsigned long)addr, (unsigned long)(dma_addr + (i * PAGE_SIZE)),
+                                       ((char *)addr)[0], (unsigned long)MAGIC_CPU);
+               }
+               /* sync the page */
+               dma_sync_single_for_cpu(&fake, dma_addr, len, dir);
+               p = page;
+               for (i = 0; i < len / PAGE_SIZE; i++, p++) {
+                       unsigned long check_val = MAGIC_DEVICE;
+
+                       addr = page_address(p);
+                       if (dir == DMA_TO_DEVICE)
+                               check_val = MAGIC_CPU;
+                       if (dir == DMA_FROM_DEVICE)
+                               check_val = MAGIC_DEVICE;
+
+                       dev_info(&fake, "%lx with DMA (%lx) has %x (expected %lx)\n",
+                               (unsigned long)addr, (unsigned long)(dma_addr + (i * PAGE_SIZE)),
+                               ((char *)addr)[0], check_val);
+               }
+               dma_unmap_page(&fake, dma_addr, len, dir);
+               dma_addr = 0;
+               __free_pages(page, get_order(len));
+               page = NULL;
+       } while (!kthread_should_stop());
+
+       if (dma_addr)
+               dma_unmap_page(&fake, dma_addr, len, dir);
+       if (page)
+               __free_pages(page, get_order(len));
+       put_device(&fake);
+       device_unregister(&fake);
+out:
+       return 0;
+}
+static struct task_struct *t[2];
+static struct args a[2];
+
+static int __init dma_test_init(void)
+{
+       int ret;
+
+       /* No point doing this without SWIOTLB */
+       if (!swiotlb_nr_tbl())
+               return -ENODEV;
+
+       ret = bus_register(&fallback_bus_type);
+       if (ret)
+               return ret;
+
+       a[0].dir = DMA_TO_DEVICE;
+       a[0].len = 32768;
+       t[0] = kthread_run(dma_test_thread, &a[0], "dma_test_dev");
+
+       a[1].len = 16384;
+       a[1].dir = DMA_FROM_DEVICE;
+       t[1] = kthread_run(dma_test_thread, &a[1], "dma_test_cpu");
+       return 0;
+}
+static void __exit dma_test_exit(void)
+{
+       if (t[0])
+               kthread_stop(t[0]);
+       if (t[1])
+               kthread_stop(t[1]);
+
+       bus_unregister(&fallback_bus_type);
+}
+module_init(dma_test_init);
+module_exit(dma_test_exit);