From: Changli Gao on
On Thu, May 6, 2010 at 8:30 AM, Changli Gao <xiaosuo(a)gmail.com> wrote:
> kvmalloc() will try to allocate physically contiguous memory first, and try
> vmalloc to allocate virtually contiguous memory when the former allocation
> fails.
>
> kvfree() is used to free the memory allocated by kvmalloc(). It can't be used
> in atomic context. If the callers are in atomic contex, they can use
> kvfree_inatomic() instead.
>
> There is much duplicate code to do such things in kernel, so I generate the
> above APIs.
>
> Thank Eric Dumazet for the "kv" prefix. :)
>
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/mm.h>
> #include <linux/init.h>
> #include <linux/slab.h>
> #include <linux/vmalloc.h>
> #include <linux/interrupt.h>
>
> void *kvmalloc(size_t size)
> {
>        void *ptr;
>
>        if (size < PAGE_SIZE)
>                return kmalloc(PAGE_SIZE, GFP_KERNEL);
>        ptr = alloc_pages_exact(size, GFP_KERNEL | __GFP_NOWARN);
>        if (ptr != NULL)
>                return ptr;
>
>        return vmalloc(size);
> }
> EXPORT_SYMBOL(kvmalloc);
>
> void kvfree(void *ptr, size_t size)
> {
>        if (size < PAGE_SIZE)
>                kfree(ptr);
>        else if (is_vmalloc_addr(ptr))
>                vfree(ptr);
>        else
>                free_pages_exact(ptr, size);
> }
> EXPORT_SYMBOL(kvfree);
>
> struct kvfree_work_struct {
>        struct work_struct      work;
>        void                    *head;
>        void                    **ptail;
> };
>
> DEFINE_PER_CPU(struct kvfree_work_struct, kvfree_work_struct);
>
> static void kvfree_work(struct work_struct *_work)
> {
>        struct kvfree_work_struct *work;
>        void *head, *tmp;
>
>        work = container_of(_work, struct kvfree_work_struct, work);
>        local_bh_disable();
>        head = work->head;
>        work->head = NULL;
>        work->ptail = &work->head;
>        local_bh_enable();

local_bh_disable should be local_irq_disable(), and local_bh_enable()
should be local_irq_enable().

>
>        while (head) {
>                tmp = head;
>                head = *(void **)head;
>                vfree(tmp);
>        }
> }
>
> void kvfree_inatomic(void *ptr, size_t size)
> {
>        if (size < PAGE_SIZE) {
>                kfree(ptr);
>        } else if (is_vmalloc_addr(ptr)) {
>                struct kvfree_work_struct *work;
>
>                *(void **)ptr = NULL;
>                local_irq_disable();
>                work = this_cpu_ptr(&kvfree_work_struct);
>                *(work->ptail) = ptr;
>                work->ptail = (void**)ptr;
>                schedule_work(&work->work);
>                local_irq_enable();
>        } else {
>                free_pages_exact(ptr, size);
>        }
> }
> EXPORT_SYMBOL(kvfree_inatomic);
>
> static int kvfree_work_struct_init(void)
> {
>        int cpu;
>        struct kvfree_work_struct *work;
>
>        for_each_possible_cpu(cpu) {
>                work = per_cpu_ptr(&kvfree_work_struct, cpu);
>                INIT_WORK(&work->work, kvfree_work);
>                work->head = NULL;
>                work->ptail = &work->head;
>        }
>
>        return 0;
> }
> //pure_initcall(kvfree_work_struct_init);
>
> //--------------------
> // for testing
> static int test_init(void)
> {
>        int size;
>        void *ptr;
>
>        kvfree_work_struct_init();
>        for (size = 1; size < (1<<30); size <<= 1) {
>                ptr = kvmalloc(size);
>                if (is_vmalloc_addr(ptr)) {
>                        printk("%d\n", size);
>                        break;
>                }
>                kvfree(ptr, size);
>        }
>
>        return 0;
> }
> module_init(test_init);
>
> static void test_exit(void)
> {
>        int cpu;
>        struct kvfree_work_struct *work;
>
>        for_each_possible_cpu(cpu) {
>                work = per_cpu_ptr(&kvfree_work_struct, cpu);
>                cancel_work_sync(&work->work);
>        }
> }
> module_exit(test_exit);
>
> MODULE_LICENSE("GPL");
>



--
Regards,
Changli Gao(xiaosuo(a)gmail.com)
From: Changli Gao on
On Thu, May 6, 2010 at 8:30 AM, Changli Gao <xiaosuo(a)gmail.com> wrote:
> kvmalloc() will try to allocate physically contiguous memory first, and try
> vmalloc to allocate virtually contiguous memory when the former allocation
> fails.
>
> kvfree() is used to free the memory allocated by kvmalloc(). It can't be used
> in atomic context. If the callers are in atomic contex, they can use
> kvfree_inatomic() instead.
>
> There is much duplicate code to do such things in kernel, so I generate the
> above APIs.
>
> Thank Eric Dumazet for the "kv" prefix. :)
>
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/mm.h>
> #include <linux/init.h>
> #include <linux/slab.h>
> #include <linux/vmalloc.h>
> #include <linux/interrupt.h>
>
> void *kvmalloc(size_t size)
> {
>        void *ptr;
>
>        if (size < PAGE_SIZE)
>                return kmalloc(PAGE_SIZE, GFP_KERNEL);

typo mistake, should be kmalloc(size, GFP_KERNEL), thank Tetsuo Handa
<penguin-kernel(a)i-love.sakura.ne.jp>.



--
Regards,
Changli Gao(xiaosuo(a)gmail.com)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Tetsuo Handa on
Changli Gao wrote:
> struct kvfree_work_struct {
> struct work_struct work;
> void *head;
> void **ptail;
> };

I wonder why "struct kvfree_work_struct" is needed.
According to http://kernel.ubuntu.com/git?p=jj/ubuntu-lucid.git;a=blobdiff;f=security/apparmor/match.c;h=d2cd55419acfcae85cb748c8f837a4384a3a0d29;hp=afc2dd2260edffcf88521ae86458ad03aa8ea12c;hb=f5eba4b0a01cc671affa429ba1512b6de7caeb5b;hpb=abdff9ddaf2644d0f9962490f73e030806ba90d3 ,

static void kvfree_work(struct work_struct *work)
{
vfree(work);
}

void kvfree_inatomic(void *ptr, size_t size)
{
if (size < PAGE_SIZE) {
kfree(ptr);
} else if (is_vmalloc_addr(ptr)) {
/*
* We can embed "struct work_struct" inside *ptr
* because size >= PAGE_SIZE.
*/
struct work_struct *work = ptr;
BUILD_BUG_ON(sizeof(struct work_struct) > PAGE_SIZE);
INIT_WORK(&work, kvfree_work);
schedule_work(&work);
} else {
free_pages_exact(ptr, size);
}
}

should do what you want. (Though, I didn't test it.)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Changli Gao on
2010/5/6 Tetsuo Handa <penguin-kernel(a)i-love.sakura.ne.jp>:
> Changli Gao wrote:
>> struct kvfree_work_struct {
>>       struct work_struct      work;
>>       void                    *head;
>>       void                    **ptail;
>> };
>
> I wonder why "struct kvfree_work_struct" is needed.
> According to http://kernel.ubuntu.com/git?p=jj/ubuntu-lucid.git;a=blobdiff;f=security/apparmor/match.c;h=d2cd55419acfcae85cb748c8f837a4384a3a0d29;hp=afc2dd2260edffcf88521ae86458ad03aa8ea12c;hb=f5eba4b0a01cc671affa429ba1512b6de7caeb5b;hpb=abdff9ddaf2644d0f9962490f73e030806ba90d3 ,
>
> static void kvfree_work(struct work_struct *work)
> {
>        vfree(work);
> }
>
> void kvfree_inatomic(void *ptr, size_t size)
> {
>        if (size < PAGE_SIZE) {
>                kfree(ptr);
>        } else if (is_vmalloc_addr(ptr)) {
>                /*
>                 * We can embed "struct work_struct" inside *ptr
>                 * because size >= PAGE_SIZE.
>                 */
>                struct work_struct *work = ptr;
>                BUILD_BUG_ON(sizeof(struct work_struct) > PAGE_SIZE);
>                INIT_WORK(&work, kvfree_work);
>                schedule_work(&work);

&work should be work. It is a much better idea. thanks very much.

>        } else {
>                free_pages_exact(ptr, size);
>        }
> }
>
> should do what you want. (Though, I didn't test it.)
>

--
Regards,
Changli Gao(xiaosuo(a)gmail.com)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Jamie Lokier on
Changli Gao wrote:
> kvmalloc() will try to allocate physically contiguous memory first, and try
> vmalloc to allocate virtually contiguous memory when the former allocation
> fails.

Note that converting users from vmalloc() to kvmalloc() may increase
fragmentation problems for other parts of the kernel, because it will
tend to use up more of the available large blocks. Especially users
who allocate large blocks and often. That's worth a mention
somewhere.

On the other hand, this API could make it easier to convert some kmalloc()
calls to kvmalloc(), reducing fragmentation problems. :-)

Since the caller is indicating they don't mind which happens, then
anti-fragmentation heuristics (such as checking watermarks) could be
added to kvmalloc() at some future time, if needed.

-- Jamie
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/