Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to work with reserved CMA memory?

I would like to allocate piece of physically contiguous reserved memory (in predefined physical addresses) for my device with DMA support. As I see CMA has three options: 1. To reserve memory via kernel config file. 2. To reserve memory via kernel cmdline. 3. To reserve memory via device-tree memory node. In the first case: size and number of areas could be reserved.

CONFIG_DMA_CMA=y
CONFIG_CMA_AREAS=7
CONFIG_CMA_SIZE_MBYTES=8

So I could use:

start_cma_virt = dma_alloc_coherent(dev->cmadev, (size_t)size_cma, &start_cma_dma, GFP_KERNEL);

in my driver to allocate contiguous memory. I could use it max 7 times and it will be possible allocate up to 8M. But unfortunately

dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));

from arch/arm/mm/init.c:

void __init arm_memblock_init(struct meminfo *mi,const struct machine_desc *mdesc)

it is impossible to set predefined physical addresses for contiguous allocation. Of Course I could use kernel cmdline:

mem=cma=cmadevlabel=8M@32M cma_map=mydevname=cmadevlabel
//struct device *dev = cmadev->dev; /*dev->name is mydevname*/

After that dma_alloc_coherent() should alloc memory in physical memory area from 32M + 8M (0x2000000 + 0x800000) up to 0x27FFFFF. But unfortunately I have problem with this solution. Maybe my cmdline has error? Next one try was device tree implementation.

cmadev_region: mycma {
    /*no-map;*/ /*DMA coherent memory*/
    /*reusable;*/
    reg = <0x02000000 0x00100000>;      
};

And phandle in some node:

memory-region = <&cmadev_region>;

As I saw in kernel usual it should be used like:

of_find_node_by_name(); //find needed node
of_parse_phandle(); //resolve a phandle property to a device_node pointer
of_get_address(); //get DT __be32 physical addresses
of_translate_address(); //DT represent local (bus, device) addresses so translate it to CPU physical addresses 
request_mem_region(); //reserve IOMAP memory (cat /proc/iomem)
ioremap(); //alloc entry in page table for reserved memory and return kernel logical addresses.

But I want use DMA via (as I know only one external API function dma_alloc_coherent) dma_alloc_coherent() instead IO-MAP ioremap(). But how call

start_cma_virt = dma_alloc_coherent(dev->cmadev, (size_t)size_cma, &start_cma_dma, GFP_KERNEL);

associate memory from device-tree (reg = <0x02000000 0x00100000>;) to dev->cmadev ? In case with cmdline it is clear it has device name and addresses region. Does reserved memory after call of_parse_phandle() automatically should be booked for your special driver (which parse DT). And next call dma_alloc_coherent will allocate dma area inside memory from cmadev_region: mycma?

like image 979
cosinus0 Avatar asked Feb 22 '16 14:02

cosinus0


People also ask

How do you allocate CMA memory?

To allocate CMA memory one uses: struct page *dma_alloc_from_contiguous(struct device *dev, int count, unsigned int align); Its first argument is a device that the allocation is performed on behalf of. The second specifies the number of pages (not bytes or order) to allocate.

What is CMA memory allocation?

CMA is a memory allocator within the kernel which allows allocating large chunks of memory with contiguous physical memory addresses.

What is reserved memory in Linux?

The Reserved-memory mechanism allows reserving memory regions in the kernel. This mechanism is used by drivers to allocate buffers in specific memory regions (such as internal SRAM) or to get a dedicated memory pool that will not be managed by Linux® conventionnal memory allocator (in DDR).

What is Dma_alloc_coherent?

The flag parameter (dma_alloc_coherent() only) allows the caller to specify the ``GFP_`` flags (see kmalloc()) for the allocation (the implementation may choose to ignore flags that affect the location of the returned memory, like GFP_DMA).


1 Answers

To use dma_alloc_coherent() on reserved memory node, you need to declare that area as dma_coherent. You can do some thing like:

In dt:

cmadev_region: mycma {
    compatible = "compatible-name"
    no-map;
    reg = <0x02000000 0x00100000>;      
};

In your driver:

struct device *cma_dev;

static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
{
    int ret;

    if (!mem) {
        ret = dma_declare_coherent_memory(cma_dev, rmem->base, rmem->base,
                           rmem->size, DMA_MEMORY_EXCLUSIVE);
        if (ret) {
            pr_err("Error");
            return ret;
        }
    }
    return 0;
}

static void rmem_dma_device_release(struct reserved_mem *rmem,
                struct device *dev)
{
    if (dev)
        dev->dma_mem = NULL;
}

static const struct reserved_mem_ops rmem_dma_ops = {
    .device_init    = rmem_dma_device_init,
    .device_release = rmem_dma_device_release,
};

int __init cma_setup(struct reserved_mem *rmem)
{
    rmem->ops = &rmem_dma_ops;
    return 0;
}
RESERVEDMEM_OF_DECLARE(some-name, "compatible-name", cma_setup);

Now on this cma_dev you can perform dma_alloc_coherent and get memory.

like image 85
Ashish Mhetre Avatar answered Sep 20 '22 04:09

Ashish Mhetre