Message ID | 20120105091718.3fb378b2a1d390f9dd63c396@nvidia.com |
---|---|
State | Not Applicable, archived |
Headers | show |
On Thu, Jan 05, 2012 at 09:17:18AM +0200, Hiroshi Doyu wrote: > Just for DMA mapping test from MPU side, the following one is used. This patch is buggy. > +static void dmaapi_test_map_page(struct device *dev) > +{ > + struct page *page; > + dma_addr_t dma_addr; > + void *cpu_addr; > + > + page = alloc_page(GFP_KERNEL); > + BUG_ON(!page); > + > + dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); > + BUG_ON(!dma_addr); > + > + cpu_addr = kmap(page); > + BUG_ON(!cpu_addr); > + memset(cpu_addr, 0xa5, PAGE_SIZE); > + kunmap(cpu_addr); The DMA API works like this: - The CPU owns the page or buffer and can access it. - You map the page or buffer. - The device owns the page or buffer; the CPU must explicitly access it. - You unmap the page or buffer. - The CPU again owns the page/buffer and can access it. Please respect the DMA API rules. So. Once dma_map_page() has returned, you must not kmap() or otherwise access the data contained in that page until after you have unmapped it. -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Russell, From: Russell King - ARM Linux <linux@arm.linux.org.uk> Subject: Re: [PATCH v3 0/2] ARM: IOMMU: tegra: Add iommu_ops for GART/SMMU driver Date: Thu, 5 Jan 2012 13:53:26 +0100 Message-ID: <20120105125326.GT11810@n2100.arm.linux.org.uk> > On Thu, Jan 05, 2012 at 09:17:18AM +0200, Hiroshi Doyu wrote: > > Just for DMA mapping test from MPU side, the following one is used. > > This patch is buggy. > > > +static void dmaapi_test_map_page(struct device *dev) > > +{ > > + struct page *page; > > + dma_addr_t dma_addr; > > + void *cpu_addr; > > + > > + page = alloc_page(GFP_KERNEL); > > + BUG_ON(!page); > > + > > + dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); > > + BUG_ON(!dma_addr); > > + > > + cpu_addr = kmap(page); > > + BUG_ON(!cpu_addr); > > + memset(cpu_addr, 0xa5, PAGE_SIZE); > > + kunmap(cpu_addr); > > The DMA API works like this: > > - The CPU owns the page or buffer and can access it. > - You map the page or buffer. > - The device owns the page or buffer; the CPU must explicitly access it. > - You unmap the page or buffer. > - The CPU again owns the page/buffer and can access it. > > Please respect the DMA API rules. Right, I do. > So. Once dma_map_page() has returned, you must not kmap() or otherwise > access the data contained in that page until after you have unmapped it. Ok, the above function order should be as below? page = alloc_page(GFP_KERNEL); cpu_addr = kmap(page); memset(cpu_addr, 0xa5, PAGE_SIZE); dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); < expect GPU does something > dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE); < CPU can access data here again> kunmap(cpu_addr); __free_page(page); -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Jan 05, 2012 at 03:29:30PM +0100, Hiroshi Doyu wrote: > Ok, the above function order should be as below? > > page = alloc_page(GFP_KERNEL); > cpu_addr = kmap(page); > memset(cpu_addr, 0xa5, PAGE_SIZE); > dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); > < expect GPU does something > > dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE); > < CPU can access data here again> > kunmap(cpu_addr); > __free_page(page); That should work, but it's not particularly nice to wrap kmap around the DMA API. Bear in mind that alloc_page(GFP_KERNEL) will always give you a lowmem page. So, you might as well do: page = alloc_page(GFP_KERNEL); cpu_addr = page_address(page); memset() dma_addr = dma_map_single(dev, cpu_addr, PAGE_SIZE, DMA_TO_DEVICE); < GPU access > dma_unmap_single(dev, dma_ddr, PAGE_SIZE, DMA_TO_DEVICE); < CPU access > __free_page(page); and this doesn't raise any issues with kmap vs DMA API. -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Jan 5, 2012 at 9:53 PM, Russell King - ARM Linux <linux@arm.linux.org.uk> wrote: > On Thu, Jan 05, 2012 at 09:17:18AM +0200, Hiroshi Doyu wrote: >> Just for DMA mapping test from MPU side, the following one is used. > > This patch is buggy. > >> +static void dmaapi_test_map_page(struct device *dev) >> +{ >> + struct page *page; >> + dma_addr_t dma_addr; >> + void *cpu_addr; >> + >> + page = alloc_page(GFP_KERNEL); >> + BUG_ON(!page); >> + >> + dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); >> + BUG_ON(!dma_addr); >> + >> + cpu_addr = kmap(page); >> + BUG_ON(!cpu_addr); >> + memset(cpu_addr, 0xa5, PAGE_SIZE); >> + kunmap(cpu_addr); > > The DMA API works like this: > > - The CPU owns the page or buffer and can access it. > - You map the page or buffer. > - The device owns the page or buffer; the CPU must explicitly access it. I don't understand what "CPU must __explicitly__ access it" means. Do you mean that CPU must access the mapped buffer when it __explicitly__ knows that the access does not cause any side effect? > - You unmap the page or buffer. > - The CPU again owns the page/buffer and can access it. > > Please respect the DMA API rules. > > So. Once dma_map_page() has returned, you must not kmap() or otherwise > access the data contained in that page until after you have unmapped it. > _______________________________________________ > iommu mailing list > iommu@lists.linux-foundation.org > https://lists.linuxfoundation.org/mailman/listinfo/iommu -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Jan 09, 2012 at 09:39:48AM +0900, KyongHo Cho wrote: > On Thu, Jan 5, 2012 at 9:53 PM, Russell King - ARM Linux > <linux@arm.linux.org.uk> wrote: > > On Thu, Jan 05, 2012 at 09:17:18AM +0200, Hiroshi Doyu wrote: > >> Just for DMA mapping test from MPU side, the following one is used. > > > > This patch is buggy. > > > >> +static void dmaapi_test_map_page(struct device *dev) > >> +{ > >> + struct page *page; > >> + dma_addr_t dma_addr; > >> + void *cpu_addr; > >> + > >> + page = alloc_page(GFP_KERNEL); > >> + BUG_ON(!page); > >> + > >> + dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); > >> + BUG_ON(!dma_addr); > >> + > >> + cpu_addr = kmap(page); > >> + BUG_ON(!cpu_addr); > >> + memset(cpu_addr, 0xa5, PAGE_SIZE); > >> + kunmap(cpu_addr); > > > > The DMA API works like this: > > > > - The CPU owns the page or buffer and can access it. > > - You map the page or buffer. > > - The device owns the page or buffer; the CPU must explicitly access it. Sorry, that 'must' should have been a 'must not'. > I don't understand what "CPU must __explicitly__ access it" means. > > Do you mean that CPU must access the mapped buffer when > it __explicitly__ knows that the access does not cause any side effect? No - I mean that the program must not attempt to read or write the buffer. -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/iommu/dmaapi-test.c b/drivers/iommu/dmaapi-test.c new file mode 100644 index 0000000..f386249 --- /dev/null +++ b/drivers/iommu/dmaapi-test.c @@ -0,0 +1,193 @@ +/* + * DMA IOMMU mapping API test module + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Author: Krishna Reddy <vdumpa@nvidia.com>, + * Hiroshi DOYU <hdoyu@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt + +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/highmem.h> +#include <linux/pfn.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> + +#include <mach/iomap.h> +#include <mach/smmu.h> + +#include <asm/dma-iommu.h> + +#include "devices.h" + +/* FIXME: 2x and 3x should be supported at once */ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define IOVA_START TEGRA_GART_BASE +#define IOVA_SIZE TEGRA_GART_SIZE +static struct platform_device *tegra_iommu_device = &tegra_gart_device; +#elif CONFIG_ARCH_TEGRA_3x_SOC +#define IOVA_START TEGRA_SMMU_BASE +#define IOVA_SIZE TEGRA_SMMU_SIZE +static struct platform_device *tegra_iommu_device = &tegra_smmu_device; +#else +#error Unsupported device +#endif + +#define NUM_TEST 3 +#define MAP_SIZE (4 * PAGE_SIZE) + +struct dmaapi_test_case { + char *name; + void (*fn)(struct device *); +}; + +static void dmaapi_test_map_page(struct device *dev) +{ + struct page *page; + dma_addr_t dma_addr; + void *cpu_addr; + + page = alloc_page(GFP_KERNEL); + BUG_ON(!page); + + dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE); + BUG_ON(!dma_addr); + + cpu_addr = kmap(page); + BUG_ON(!cpu_addr); + memset(cpu_addr, 0xa5, PAGE_SIZE); + kunmap(cpu_addr); + + pr_debug("pid:%d,%s mapped\t%08x:%08x\n", + current->pid, dev_name(dev), dma_addr, page_to_phys(page)); + + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE); + __free_page(page); +} + +static void dmaapi_test_alloc_coherent(struct device *dev) +{ + dma_addr_t da[NUM_TEST]; + void *va[NUM_TEST]; + int i; + + for (i = 0; i < NUM_TEST; i++) { + void *cpu_addr; + dma_addr_t dma_addr; + + cpu_addr = dma_alloc_coherent(dev, MAP_SIZE, + &dma_addr, GFP_KERNEL); + BUG_ON(!cpu_addr); + memset(cpu_addr, 0xa5, MAP_SIZE); + + pr_debug("pid:%d,%s,[%d] mapped\t%08x:%08x\n", + current->pid, dev_name(dev), i, + dma_addr, virt_to_phys(cpu_addr)); + + da[i] = dma_addr; + va[i] = cpu_addr; + } + + while (--i >= 0) { + pr_debug("pid:%d,%s,[%d] unmapping\t%08x:%08x\n", + current->pid, dev_name(dev), i, + da[i], virt_to_phys(va[i])); + dma_free_coherent(dev, MAP_SIZE, va[i], da[i]); + } +} + +static struct dmaapi_test_case test[] = { + { + .name = "dmaapi/map page", + .fn = dmaapi_test_map_page, + }, + { + .name = "dmaapi/alloc coherent", + .fn = dmaapi_test_alloc_coherent, + }, +}; + +static u32 dummy_hwgrp_map[] = { + HWG_DC | HWG_AFI | HWG_AVPC | HWG_DCB, + HWG_EPP | HWG_HC | HWG_G2 | HWG_MPE | HWG_HDA | HWG_ISP, + HWG_NV | HWG_PPCS | HWG_SATA | HWG_NV2 | HWG_VI | HWG_VDE, +}; + +/* FIXME: Need driver for iommu context? */ +static struct platform_device dmaapi_dummy_device[] = { + { .name = "hwgrp@a", .id = -1, }, + { .name = "hwgrp@b", .id = -1, }, + { .name = "hwgrp@c", .id = -1, }, +}; + +static int dmaapi_test_thread(void *data) +{ + int i; + struct dmaapi_test_case *c = data; + + for (i = 0; true; i++) { + struct device *dev; + int interval[] = {7, 3, 5,}; + int n; + + n = i % ARRAY_SIZE(dmaapi_dummy_device); + ssleep(interval[n]); + dev = &dmaapi_dummy_device[n].dev; + c->fn(dev); + } + return 0; +} + +static int __init dmaapi_test_init(void) +{ + int i; + struct dma_iommu_mapping *map; + + map = arm_iommu_create_mapping(IOVA_START, IOVA_SIZE, 0); + BUG_ON(!map); + pr_debug("Allocate IOVA: %08x-%08x\n", map->base, map->base + IOVA_SIZE); + + for (i = 0; i < ARRAY_SIZE(dmaapi_dummy_device); i++) { + int err; + struct platform_device *pdev = &dmaapi_dummy_device[i]; + + pdev->dev.platform_data = (void *)dummy_hwgrp_map[i]; + pdev->dev.parent = &tegra_iommu_device->dev; + err = platform_device_register(pdev); + BUG_ON(err); + + err = arm_iommu_attach_device(&pdev->dev, map); + BUG_ON(err); + pr_debug("IOMMU API: Attached to %s\n", dev_name(&pdev->dev)); + } + + for (i = 0; i < ARRAY_SIZE(test); i++) + kthread_run(dmaapi_test_thread, &test[i], test[i].name); + + return 0; +} +module_init(dmaapi_test_init); + +MODULE_AUTHOR("Krishna Reddy <vdumpa@nvidia.com>"); +MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); +MODULE_DESCRIPTION("DMA IOMMU mapping API test"); +MODULE_LICENSE("GPL v2");