[PATCH v2] MM: CMA: add a simple kernel module as the helper to test CMA
Michal Nazarewicz
mina86 at mina86.com
Mon Mar 5 04:40:09 EST 2012
On Mon, 05 Mar 2012 08:13:46 +0100, Barry Song <Barry.Song at csr.com> wrote:
> Any write request to /dev/cma_test will let the module to allocate memory from
> CMA, for example:
>
> 1st time
> $ echo 1024 > /dev/cma_test
> will require cma_test to request 1MB(1024KB)
> 2nd time
> $ echo 2048 > /dev/cma_test
> will require cma_test to request 2MB(2048KB)
>
> Any read request to /dev/cma_test will let the module to free the 1st valid
> memory from CMA, for example:
>
> 1st time
> $ cat /dev/cma_test
> will require cma_test to free the 1MB allocated in the first write request
> 2nd time
> $ cat /dev/cma_test
> will require cma_test to free the 2MB allocated in the second write request
>
> Signed-off-by: Barry Song <Baohua.Song at csr.com>
> diff --git a/tools/cma/cma_test.c b/tools/cma/cma_test.c
> new file mode 100644
> index 0000000..46af250
> --- /dev/null
> +++ b/tools/cma/cma_test.c
> @@ -0,0 +1,140 @@
> +/*
> + * kernel module helper for testing CMA
> + *
> + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
It's 2012.
> + *
> + * Licensed under GPLv2 or later.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/fs.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +struct cma_allocation {
> + struct list_head list;
> + unsigned long size;
> + dma_addr_t dma;
> + void *virt;
> +};
> +
> +static struct device *cma_dev;
> +static LIST_HEAD(cma_allocations);
> +static DEFINE_SPINLOCK(cma_lock);
> +
> +/*
> + * any read request will free the 1st allocated coherent memory, eg.
> + * cat /dev/cma_test
> + */
> +static ssize_t
> +cma_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
> +{
> + struct cma_allocation *alloc = NULL;
> +
> + spin_lock(&cma_lock);
> + if (!list_empty(&cma_allocations)) {
> + alloc = list_first_entry(&cma_allocations,
> + struct cma_allocation, list);
> + list_del(&alloc->list);
> + }
> + spin_unlock(&cma_lock);
> +
Come to think of it, how about putting:
if (!alloc)
return -EIDRM;
here and then removing one indention level later:
> + if (alloc) {
> + dma_free_coherent(cma_dev, alloc->size, alloc->virt,
> + alloc->dma);
> +
> + _dev_info(cma_dev, "free CM at virtual address: 0x%p dma address: 0x%p size:%luKiB\n",
This message seem overly long and “0x” seem redundant. How about something like:
_dev_info(cma_dev, "free: virt:%p dma:%p size:%luK\n",
which has all the same information but is shorter?
> + alloc->virt, (void *)alloc->dma, alloc->size / SZ_1K);
> + kfree(alloc);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * any write request will alloc a new coherent memory, eg.
> + * echo 1024 > /dev/cma_test
> + * will request 1024KiB by CMA
> + */
> +static ssize_t
> +cma_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
> +{
> + struct cma_allocation *alloc;
> + int ret;
> +
> + alloc = kmalloc(sizeof *alloc, GFP_KERNEL);
> + if (!alloc)
> + return -ENOMEM;
> +
> + ret = kstrtoul_from_user(buf, count, 0, &alloc->size);
> + if (ret)
> + return ret;
> +
> + if (!alloc->size)
> + return -EINVAL;
> +
> + if (alloc->size > (ULONG_MAX << PAGE_SHIFT))
You've changed units from pages to KiBs and, I've just realised that, since
dma_alloc_coherent accepts “size_t” as argument, this should read:
if (alloc->size > ~(size_t)0 / SZ_1K)
(“<<” was in fact a bug in my code from the beginning.)
> + return -EOVERFLOW;
Most importantly, there was a memory leak in my original suggestion, and
it got carried over to this patch. The above code should look
something like that:
ret = kstrtoul_from_user(buf, count, 0, &alloc->size);
if (ret)
return ret;
if (!alloc->size)
return -EINVAL;
if (alloc->size > ~(size_t)0 / SZ_1K)
return -EOVERFLOW;
alloc = kmalloc(sizeof *alloc, GFP_KERNEL);
if (!alloc)
return -ENOMEM;
> + alloc->size *= SZ_1K;
> + alloc->virt = dma_alloc_coherent(cma_dev, alloc->size,
> + &alloc->dma, GFP_KERNEL);
> +
> + if (alloc->virt) {
> + _dev_info(cma_dev, "allocate CM at virtual address: 0x%p"
> + "address: 0x%p size:%luKiB\n", alloc->virt,
Similarly to message earlier, how about:
_dev_info(cma_dev, "alloc: virt:%p dma:%p size:%luK\n",
> + (void *)alloc->dma, alloc->size / SZ_1K);
> +
> + spin_lock(&cma_lock);
> + list_add_tail(&alloc->list, &cma_allocations);
> + spin_unlock(&cma_lock);
> +
> + return count;
> + } else {
> + dev_err(cma_dev, "no mem in CMA area\n");
> + kfree(alloc);
> + return -ENOSPC;
> + }
> +}
> +
> +static const struct file_operations cma_test_fops = {
> + .owner = THIS_MODULE,
> + .read = cma_test_read,
> + .write = cma_test_write,
> +};
> +
> +static struct miscdevice cma_test_misc = {
> + .name = "cma_test",
> + .fops = &cma_test_fops,
> +};
> +
> +static int __init cma_test_init(void)
> +{
> + int ret = 0;
Drop “= 0”, or better yet, combain this declaration with the next line.
> +
> + ret = misc_register(&cma_test_misc);
> + if (unlikely(ret)) {
> + pr_err("failed to register cma test misc device!\n");
> + return ret;
> + }
> + cma_dev = cma_test_misc.this_device;
> + cma_dev->coherent_dma_mask = ~0;
> + _dev_info(cma_dev, "registered.\n");
> +
> + return ret;
return 0;
> +}
> +module_init(cma_test_init);
> +
> +static void __exit cma_test_exit(void)
> +{
> + misc_deregister(&cma_test_misc);
> +}
> +module_exit(cma_test_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Barry Song <Baohua.Song at csr.com>");
> +MODULE_DESCRIPTION("kernel module to help the test of CMA");
> +MODULE_ALIAS("CMA test");
Can module alias contain spaces? I don't think this declaration even
adds anything useful. “cma_test” as module name should be good enough.
--
Best regards, _ _
.o. | Liege of Serenely Enlightened Majesty of o' \,=./ `o
..o | Computer Science, Michał “mina86” Nazarewicz (o o)
ooo +----<email/xmpp: mpn at google.com>--------------ooO--(_)--Ooo--
More information about the linux-arm-kernel
mailing list