[PATCH v2] MM: CMA: add a simple kernel module as the helper to test CMA
Barry Song
21cnbao at gmail.com
Tue Mar 6 21:18:31 EST 2012
hi Michal,
2012/3/5 Michal Nazarewicz <mina86 at mina86.com>:
> 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;
ok. make sense. "Identifier removed"
>
> here and then removing one indention level later:
>
agree.
>
>> + 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?
>
ok. anyway it is trivial.
>
>> + 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)
>
ok.
> (“<<” 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;
>
in original codes, "kmalloc(sizeof *alloc, GFP_KERNEL);" is not freed.
better we define a temp size_t size
kstrtoul_from_user(buf, count, 0, &size);
...
if (!size)
return -EINVAL;
if (size > ~(size_t)0 / SZ_1K)
return -EOVERFLOW;
alloc = kmalloc(sizeof *alloc, GFP_KERNEL);
if (!alloc)
return -ENOMEM;
alloc->size = size;
>
>> + 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.
ok.
>
> --
> 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--
>
-barry
More information about the linux-arm-kernel
mailing list