Hello,
This is a third version of my proposal for device tree integration for
Contiguous Memory Allocator. Over 2 month has passed from the time I've
posted the second version, but now some things about using /chosen
device tree node has been clarified, so I expect to get this code merged
soon this time.
Just a few words for those who see this code for the first time:
The proposed bindings allows to define contiguous memory regions of
specified base address and size. Then, the defined regions can be
assigned to the given device(s) by adding a property with a phanle to
the defined contiguous memory region. From the device tree perspective
that's all. Once the bindings are added, all the memory allocations from
dma-mapping subsystem will be served from the defined contiguous memory
regions.
Contiguous Memory Allocator is a framework, which lets to provide a
large contiguous memory buffers for (usually a multimedia) devices. The
contiguous memory is reserved during early boot and then shared with
kernel, which is allowed to allocate it for movable pages. Then, when
device driver requests a contigouous buffer, the framework migrates
movable pages out of contiguous region and gives it to the driver. When
device driver frees the buffer, it is added to kernel memory pool again.
For more information, please refer to commit c64be2bb1c6eb43c838b2c6d57
("drivers: add Contiguous Memory Allocator") and d484864dd96e1830e76895
(CMA merge commit).
Why we need device tree bindings for CMA at all?
Older ARM kernels used so called board-based initialization. Those board
files contained a definition of all hardware blocks available on the
target system and particular kernel and driver software configuration
selected by the board maintainer.
In the new approach the board files will be removed completely and
Device Tree approach is used to describe all hardware blocks available
on the target system. By definition, the bindings should be software
independent, so at least in theory it should be possible to use those
bindings with other operating systems than Linux kernel.
However we also need to pass somehow the information about kernel and
device driver software-only configuration data, which were earlier
encoded in the board file. For such data I decided to use /chosen node,
where kernel command line has been already stored. Future bootloaders
will allow to modify or replace particular nodes and one will be able to
use custom /chosen node to configure his system. The proposed patches
introduce /chosen/contiguous-memory node and related bindings, to avoid
complicated encoding of CMA related configuration to kernel command
line.
Some rationale for using /chosen/ node for kernel configuration entities
has been already suggested by Grant Likely in the following thread:
http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/155296/focus=34363
Best regards
Marek Szyprowski
Samsung R&D Institute Poland
Changelog:
v3:
- fixed issues pointed by Laura and updated documentation
v2: http://thread.gmane.org/gmane.linux.drivers.devicetree/34075
- moved contiguous-memory bindings from /memory to /chosen/contiguous-memory/
node to avoid spreading Linux specific parameters over the whole device
tree definitions
- added support for autoconfigured regions (use zero base)
- fixes minor bugs
v1: http://thread.gmane.org/gmane.linux.drivers.devicetree/30111/
- initial proposal
Patch summary:
Marek Szyprowski (2):
drivers: dma-contiguous: clean source code and prepare for device
tree
drivers: dma-contiguous: add initialization from device tree
.../devicetree/bindings/contiguous-memory.txt | 94 ++++++
arch/arm/boot/dts/skeleton.dtsi | 7 +-
drivers/base/dma-contiguous.c | 325 +++++++++++++-------
include/asm-generic/dma-contiguous.h | 2 -
include/linux/dma-contiguous.h | 32 +-
5 files changed, 352 insertions(+), 108 deletions(-)
create mode 100644 Documentation/devicetree/bindings/contiguous-memory.txt
--
1.7.9.5
In Tegra SoC, IOMMU can set some mapping attribute for each page, for
exmaple, READable, and WRITEable. We'd like to use this feature
*newly* for robustness, where read-only pages can be protected from
being overwritten by wrong operation. We have been using IOMMU via DMA
mapping API(ARM). DMA mapping API currently doesn't use "prot"
parameter when calling IOMMU API. So this series tries to pass "struct
dma_attrs *attrs" via "int prot" in IOMMU API. I'm not so sure if this
implementation is right or not because:
- Casting (struct dma_attrs *) to (int) in DMA API doesn't look nice.
- IOMMU layer needs to cast (int) back to (struct dma_attrs *) again,
which can be considered as violation of layers.
If you have any implementations/suggestions, it would be really helpful.
This series isn't applied cleanly but this is posted to request for
comments.
Hiroshi Doyu (3):
common: DMA-mapping: add DMA_ATTR_READ_ONLY attribute
ARM: dma-mapping: Pass DMA attrs as IOMMU prot
iommu/tegra: smmu: Support read-only mapping
arch/arm/mm/dma-mapping.c | 34 +++++++++++++++++++++-------------
drivers/iommu/tegra-smmu.c | 41 +++++++++++++++++++++++++++++------------
include/linux/dma-attrs.h | 1 +
3 files changed, 51 insertions(+), 25 deletions(-)
--
1.8.1.5
In Tegra SoC, IOMMU can set some mapping attribute for each page, for
exmaple, READable, and WRITEable. We'd like to use this feature
*newly* for robustness, where read-only pages can be protected from
being overwritten by wrong operation. We have been using IOMMU via DMA
mapping API(ARM). DMA mapping API currently doesn't use "prot"
parameter when calling IOMMU API. So this series tries to pass "struct
dma_attrs *attrs" via "int prot" in IOMMU API. I'm not so sure if this
implementation is right or not because:
- Casting (struct dma_attrs *) to (int) in DMA API doesn't look nice.
- IOMMU layer needs to cast (int) back to (struct dma_attrs *) again,
which can be considered as violation of layers.
If you have any implementations/suggestions, it would be really helpful.
This series isn't applied cleanly but this is posted to request for
comments.
Hiroshi Doyu (3):
common: DMA-mapping: add DMA_ATTR_READ_ONLY attribute
ARM: dma-mapping: Pass DMA attrs as IOMMU prot
iommu/tegra: smmu: Support read-only mapping
arch/arm/mm/dma-mapping.c | 34 +++++++++++++++++++++-------------
drivers/iommu/tegra-smmu.c | 41 +++++++++++++++++++++++++++++------------
include/linux/dma-attrs.h | 1 +
3 files changed, 51 insertions(+), 25 deletions(-)
--
1.8.1.5
Earlier version of dma-contig allocator in user ptr mode assumed that in
all cases DMA address equals physical address. This was just a special case.
Commit e15dab752d4c588544ccabdbe020a7cc092e23c8 introduced correct support
for converting userpage to dma address, but unfortunately it broke the
support for simple dma address = physical address for the case, when given
physical frame has no struct page associated with it (this happens if one
use for example dma_declare_coherent api or other reserved memory approach).
This commit restores support for such cases.
Signed-off-by: Marek Szyprowski <m.szyprowski(a)samsung.com>
---
drivers/media/v4l2-core/videobuf2-dma-contig.c | 87 ++++++++++++++++++++++--
1 file changed, 82 insertions(+), 5 deletions(-)
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index fd56f25..1382749 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -423,6 +423,39 @@ static inline int vma_is_io(struct vm_area_struct *vma)
return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
}
+static int vb2_dc_get_user_pfn(unsigned long start, int n_pages,
+ struct vm_area_struct *vma, unsigned long *res)
+{
+ unsigned long pfn, start_pfn, prev_pfn;
+ unsigned int i;
+ int ret;
+
+ if (!vma_is_io(vma))
+ return -EFAULT;
+
+ ret = follow_pfn(vma, start, &pfn);
+ if (ret)
+ return ret;
+
+ start_pfn = pfn;
+ start += PAGE_SIZE;
+
+ for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) {
+ prev_pfn = pfn;
+ ret = follow_pfn(vma, start, &pfn);
+
+ if (ret) {
+ pr_err("no page for address %lu\n", start);
+ return ret;
+ }
+ if (pfn != prev_pfn + 1)
+ return -EINVAL;
+ }
+
+ *res = start_pfn;
+ return 0;
+}
+
static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
int n_pages, struct vm_area_struct *vma, int write)
{
@@ -433,6 +466,9 @@ static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
unsigned long pfn;
int ret = follow_pfn(vma, start, &pfn);
+ if (!pfn_valid(pfn))
+ return -EINVAL;
+
if (ret) {
pr_err("no page for address %lu\n", start);
return ret;
@@ -468,16 +504,49 @@ static void vb2_dc_put_userptr(void *buf_priv)
struct vb2_dc_buf *buf = buf_priv;
struct sg_table *sgt = buf->dma_sgt;
- dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
- if (!vma_is_io(buf->vma))
- vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
+ if (sgt) {
+ dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+ if (!vma_is_io(buf->vma))
+ vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
- sg_free_table(sgt);
- kfree(sgt);
+ sg_free_table(sgt);
+ kfree(sgt);
+ }
vb2_put_vma(buf->vma);
kfree(buf);
}
+/*
+ * For some kind of reserved memory there might be no struct page available,
+ * so all that can be done to support such 'pages' is to try to convert
+ * pfn to dma address or at the last resort just assume that
+ * dma address == physical address (like it has been assumed in earlier version
+ * of videobuf2-dma-contig
+ */
+
+#ifdef __arch_pfn_to_dma
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+ return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
+}
+#elsif defined(__pfn_to_bus)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+ return (dma_addr_t)__pfn_to_bus(pfn);
+}
+#elsif defined(__pfn_to_phys)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+ return (dma_addr_t)__pfn_to_phys(pfn);
+}
+#else
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+{
+ /* really, we cannot do anything better at this point */
+ return (dma_addr_t)(pfn) << PAGE_SHIFT;
+}
+#endif
+
static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
{
@@ -548,6 +617,14 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
/* extract page list from userspace mapping */
ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write);
if (ret) {
+ unsigned long pfn;
+ if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
+ buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn);
+ buf->size = size;
+ kfree(pages);
+ return buf;
+ }
+
pr_err("failed to get user pages\n");
goto fail_vma;
}
--
1.7.9.5
Hi,
Currently ARM64 supports swiotlb only as below[1]. AFAIU, swiotlb
provides some kind of bounce buffer for some restricted H/Ws, and it
cannot replace IOMMU H/W completely. So I'm wondering that we would
need the IOMMU versions of dma_map_ops as Marek did for ARM32. If my
understanding is correct, do you guys have any idea how it's going to
be implemented? Can we reuse the current version of "iommu_ops" in
"arch/arm/mm/dma-mapping.c" for ARM64 as well? Or do we need to
rewrite 64bit version of iommu_ops completely in the same file as one
with swiotlb, "arch/arm64/mm/dma-mapping.c"?
Any feedback would be really appreciated.
[1]:
commit 09b55412469dfe6797244dc5836c17ed0c2f191b
Author: Catalin Marinas <catalin.marinas(a)arm.com>
Date: Mon Mar 5 11:49:30 2012 +0000
arm64: DMA mapping API
This patch adds support for the DMA mapping API. It uses dma_map_ops for
flexibility and it currently supports swiotlb. This patch could be
simplified further if the DMA accesses are coherent (not mandated by the
architecture) or if corresponding hooks are placed in the generic
swiotlb code to deal with cache maintenance.