Re: [PATCH 1/2] mm/zsmalloc: adjust order of functions

From: Minchan Kim
Date: Mon Dec 15 2014 - 19:41:58 EST


Hello Ganesh,

On Sat, Dec 13, 2014 at 09:43:23PM +0800, Ganesh Mahendran wrote:
> Currently functions in zsmalloc.c does not arranged in a readable
> and reasonable sequence. With the more and more functions added,
> we may meet below inconvenience. For example:
>
> Current functions:
> void zs_init()
> {
> }
>
> static void get_maxobj_per_zspage()
> {
> }
>
> Then I want to add a func_1() which is called from zs_init(), and this new added
> function func_1() will used get_maxobj_per_zspage() which is defined below zs_init().
>
> void func_1()
> {
> get_maxobj_per_zspage()
> }
>
> void zs_init()
> {
> func_1()
> }
>
> static void get_maxobj_per_zspage()
> {
> }
>
> This will cause compiling issue. So we must add a declaration:
> static void get_maxobj_per_zspage();
> before func_1() if we do not put get_maxobj_per_zspage() before func_1().

Yes, I suffered from that when I made compaction but was not sure
it's it was obviously wrong.
Stupid question:
What's the problem if we should put function declaration on top of
source code?

>
> In addition, puting module_[init|exit] functions at the bottom of the file
> conforms to our habit.

Normally, we do but without any strong reason, I don't want to rub git-blame
by clean up patches.

In summary, I like this patch but don't like to churn git-blame by clean-up
patchset without strong reason so I need something I am sure.

>
> So, this patch ajusts function sequence as:
> /* helper functions */
> ...
> obj_location_to_handle()
> ...
>
> /* Some exported functions */
> ...
>
> zs_map_object()
> zs_unmap_object()
>
> zs_malloc()
> zs_free()
>
> zs_init()
> zs_exit()
>
> Signed-off-by: Ganesh Mahendran <opensource.ganesh@xxxxxxxxx>
> ---
> mm/zsmalloc.c | 374 ++++++++++++++++++++++++++++-----------------------------
> 1 file changed, 187 insertions(+), 187 deletions(-)
>
> diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
> index 4d0a063..b724039 100644
> --- a/mm/zsmalloc.c
> +++ b/mm/zsmalloc.c
> @@ -884,19 +884,6 @@ static struct notifier_block zs_cpu_nb = {
> .notifier_call = zs_cpu_notifier
> };
>
> -static void zs_unregister_cpu_notifier(void)
> -{
> - int cpu;
> -
> - cpu_notifier_register_begin();
> -
> - for_each_online_cpu(cpu)
> - zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
> - __unregister_cpu_notifier(&zs_cpu_nb);
> -
> - cpu_notifier_register_done();
> -}
> -
> static int zs_register_cpu_notifier(void)
> {
> int cpu, uninitialized_var(ret);
> @@ -914,40 +901,28 @@ static int zs_register_cpu_notifier(void)
> return notifier_to_errno(ret);
> }
>
> -static void init_zs_size_classes(void)
> +static void zs_unregister_cpu_notifier(void)
> {
> - int nr;
> + int cpu;
>
> - nr = (ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / ZS_SIZE_CLASS_DELTA + 1;
> - if ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) % ZS_SIZE_CLASS_DELTA)
> - nr += 1;
> + cpu_notifier_register_begin();
>
> - zs_size_classes = nr;
> -}
> + for_each_online_cpu(cpu)
> + zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
> + __unregister_cpu_notifier(&zs_cpu_nb);
>
> -static void __exit zs_exit(void)
> -{
> -#ifdef CONFIG_ZPOOL
> - zpool_unregister_driver(&zs_zpool_driver);
> -#endif
> - zs_unregister_cpu_notifier();
> + cpu_notifier_register_done();
> }
>
> -static int __init zs_init(void)
> +static void init_zs_size_classes(void)
> {
> - int ret = zs_register_cpu_notifier();
> -
> - if (ret) {
> - zs_unregister_cpu_notifier();
> - return ret;
> - }
> + int nr;
>
> - init_zs_size_classes();
> + nr = (ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / ZS_SIZE_CLASS_DELTA + 1;
> + if ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) % ZS_SIZE_CLASS_DELTA)
> + nr += 1;
>
> -#ifdef CONFIG_ZPOOL
> - zpool_register_driver(&zs_zpool_driver);
> -#endif
> - return 0;
> + zs_size_classes = nr;
> }
>
> static unsigned int get_maxobj_per_zspage(int size, int pages_per_zspage)
> @@ -967,113 +942,101 @@ static bool can_merge(struct size_class *prev, int size, int pages_per_zspage)
> return true;
> }
>
> +unsigned long zs_get_total_pages(struct zs_pool *pool)
> +{
> + return atomic_long_read(&pool->pages_allocated);
> +}
> +EXPORT_SYMBOL_GPL(zs_get_total_pages);
> +
> /**
> - * zs_create_pool - Creates an allocation pool to work from.
> - * @flags: allocation flags used to allocate pool metadata
> + * zs_map_object - get address of allocated object from handle.
> + * @pool: pool from which the object was allocated
> + * @handle: handle returned from zs_malloc
> *
> - * This function must be called before anything when using
> - * the zsmalloc allocator.
> + * Before using an object allocated from zs_malloc, it must be mapped using
> + * this function. When done with the object, it must be unmapped using
> + * zs_unmap_object.
> *
> - * On success, a pointer to the newly created pool is returned,
> - * otherwise NULL.
> + * Only one object can be mapped per cpu at a time. There is no protection
> + * against nested mappings.
> + *
> + * This function returns with preemption and page faults disabled.
> */
> -struct zs_pool *zs_create_pool(gfp_t flags)
> +void *zs_map_object(struct zs_pool *pool, unsigned long handle,
> + enum zs_mapmode mm)
> {
> - int i;
> - struct zs_pool *pool;
> - struct size_class *prev_class = NULL;
> + struct page *page;
> + unsigned long obj_idx, off;
>
> - pool = kzalloc(sizeof(*pool), GFP_KERNEL);
> - if (!pool)
> - return NULL;
> + unsigned int class_idx;
> + enum fullness_group fg;
> + struct size_class *class;
> + struct mapping_area *area;
> + struct page *pages[2];
>
> - pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *),
> - GFP_KERNEL);
> - if (!pool->size_class) {
> - kfree(pool);
> - return NULL;
> - }
> + BUG_ON(!handle);
>
> /*
> - * Iterate reversly, because, size of size_class that we want to use
> - * for merging should be larger or equal to current size.
> + * Because we use per-cpu mapping areas shared among the
> + * pools/users, we can't allow mapping in interrupt context
> + * because it can corrupt another users mappings.
> */
> - for (i = zs_size_classes - 1; i >= 0; i--) {
> - int size;
> - int pages_per_zspage;
> - struct size_class *class;
> -
> - size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA;
> - if (size > ZS_MAX_ALLOC_SIZE)
> - size = ZS_MAX_ALLOC_SIZE;
> - pages_per_zspage = get_pages_per_zspage(size);
> -
> - /*
> - * size_class is used for normal zsmalloc operation such
> - * as alloc/free for that size. Although it is natural that we
> - * have one size_class for each size, there is a chance that we
> - * can get more memory utilization if we use one size_class for
> - * many different sizes whose size_class have same
> - * characteristics. So, we makes size_class point to
> - * previous size_class if possible.
> - */
> - if (prev_class) {
> - if (can_merge(prev_class, size, pages_per_zspage)) {
> - pool->size_class[i] = prev_class;
> - continue;
> - }
> - }
> -
> - class = kzalloc(sizeof(struct size_class), GFP_KERNEL);
> - if (!class)
> - goto err;
> + BUG_ON(in_interrupt());
>
> - class->size = size;
> - class->index = i;
> - class->pages_per_zspage = pages_per_zspage;
> - spin_lock_init(&class->lock);
> - pool->size_class[i] = class;
> + obj_handle_to_location(handle, &page, &obj_idx);
> + get_zspage_mapping(get_first_page(page), &class_idx, &fg);
> + class = pool->size_class[class_idx];
> + off = obj_idx_to_offset(page, obj_idx, class->size);
>
> - prev_class = class;
> + area = &get_cpu_var(zs_map_area);
> + area->vm_mm = mm;
> + if (off + class->size <= PAGE_SIZE) {
> + /* this object is contained entirely within a page */
> + area->vm_addr = kmap_atomic(page);
> + return area->vm_addr + off;
> }
>
> - pool->flags = flags;
> -
> - return pool;
> + /* this object spans two pages */
> + pages[0] = page;
> + pages[1] = get_next_page(page);
> + BUG_ON(!pages[1]);
>
> -err:
> - zs_destroy_pool(pool);
> - return NULL;
> + return __zs_map_object(area, pages, off, class->size);
> }
> -EXPORT_SYMBOL_GPL(zs_create_pool);
> +EXPORT_SYMBOL_GPL(zs_map_object);
>
> -void zs_destroy_pool(struct zs_pool *pool)
> +void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
> {
> - int i;
> + struct page *page;
> + unsigned long obj_idx, off;
>
> - for (i = 0; i < zs_size_classes; i++) {
> - int fg;
> - struct size_class *class = pool->size_class[i];
> + unsigned int class_idx;
> + enum fullness_group fg;
> + struct size_class *class;
> + struct mapping_area *area;
>
> - if (!class)
> - continue;
> + BUG_ON(!handle);
>
> - if (class->index != i)
> - continue;
> + obj_handle_to_location(handle, &page, &obj_idx);
> + get_zspage_mapping(get_first_page(page), &class_idx, &fg);
> + class = pool->size_class[class_idx];
> + off = obj_idx_to_offset(page, obj_idx, class->size);
>
> - for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) {
> - if (class->fullness_list[fg]) {
> - pr_info("Freeing non-empty class with size %db, fullness group %d\n",
> - class->size, fg);
> - }
> - }
> - kfree(class);
> - }
> + area = this_cpu_ptr(&zs_map_area);
> + if (off + class->size <= PAGE_SIZE)
> + kunmap_atomic(area->vm_addr);
> + else {
> + struct page *pages[2];
>
> - kfree(pool->size_class);
> - kfree(pool);
> + pages[0] = page;
> + pages[1] = get_next_page(page);
> + BUG_ON(!pages[1]);
> +
> + __zs_unmap_object(area, pages, off, class->size);
> + }
> + put_cpu_var(zs_map_area);
> }
> -EXPORT_SYMBOL_GPL(zs_destroy_pool);
> +EXPORT_SYMBOL_GPL(zs_unmap_object);
>
> /**
> * zs_malloc - Allocate block of given size from pool.
> @@ -1176,100 +1139,137 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
> EXPORT_SYMBOL_GPL(zs_free);
>
> /**
> - * zs_map_object - get address of allocated object from handle.
> - * @pool: pool from which the object was allocated
> - * @handle: handle returned from zs_malloc
> - *
> - * Before using an object allocated from zs_malloc, it must be mapped using
> - * this function. When done with the object, it must be unmapped using
> - * zs_unmap_object.
> + * zs_create_pool - Creates an allocation pool to work from.
> + * @flags: allocation flags used to allocate pool metadata
> *
> - * Only one object can be mapped per cpu at a time. There is no protection
> - * against nested mappings.
> + * This function must be called before anything when using
> + * the zsmalloc allocator.
> *
> - * This function returns with preemption and page faults disabled.
> + * On success, a pointer to the newly created pool is returned,
> + * otherwise NULL.
> */
> -void *zs_map_object(struct zs_pool *pool, unsigned long handle,
> - enum zs_mapmode mm)
> +struct zs_pool *zs_create_pool(gfp_t flags)
> {
> - struct page *page;
> - unsigned long obj_idx, off;
> + int i;
> + struct zs_pool *pool;
> + struct size_class *prev_class = NULL;
>
> - unsigned int class_idx;
> - enum fullness_group fg;
> - struct size_class *class;
> - struct mapping_area *area;
> - struct page *pages[2];
> + pool = kzalloc(sizeof(*pool), GFP_KERNEL);
> + if (!pool)
> + return NULL;
>
> - BUG_ON(!handle);
> + pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *),
> + GFP_KERNEL);
> + if (!pool->size_class) {
> + kfree(pool);
> + return NULL;
> + }
>
> /*
> - * Because we use per-cpu mapping areas shared among the
> - * pools/users, we can't allow mapping in interrupt context
> - * because it can corrupt another users mappings.
> + * Iterate reversly, because, size of size_class that we want to use
> + * for merging should be larger or equal to current size.
> */
> - BUG_ON(in_interrupt());
> + for (i = zs_size_classes - 1; i >= 0; i--) {
> + int size;
> + int pages_per_zspage;
> + struct size_class *class;
>
> - obj_handle_to_location(handle, &page, &obj_idx);
> - get_zspage_mapping(get_first_page(page), &class_idx, &fg);
> - class = pool->size_class[class_idx];
> - off = obj_idx_to_offset(page, obj_idx, class->size);
> + size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA;
> + if (size > ZS_MAX_ALLOC_SIZE)
> + size = ZS_MAX_ALLOC_SIZE;
> + pages_per_zspage = get_pages_per_zspage(size);
>
> - area = &get_cpu_var(zs_map_area);
> - area->vm_mm = mm;
> - if (off + class->size <= PAGE_SIZE) {
> - /* this object is contained entirely within a page */
> - area->vm_addr = kmap_atomic(page);
> - return area->vm_addr + off;
> + /*
> + * size_class is used for normal zsmalloc operation such
> + * as alloc/free for that size. Although it is natural that we
> + * have one size_class for each size, there is a chance that we
> + * can get more memory utilization if we use one size_class for
> + * many different sizes whose size_class have same
> + * characteristics. So, we makes size_class point to
> + * previous size_class if possible.
> + */
> + if (prev_class) {
> + if (can_merge(prev_class, size, pages_per_zspage)) {
> + pool->size_class[i] = prev_class;
> + continue;
> + }
> + }
> +
> + class = kzalloc(sizeof(struct size_class), GFP_KERNEL);
> + if (!class)
> + goto err;
> +
> + class->size = size;
> + class->index = i;
> + class->pages_per_zspage = pages_per_zspage;
> + spin_lock_init(&class->lock);
> + pool->size_class[i] = class;
> +
> + prev_class = class;
> }
>
> - /* this object spans two pages */
> - pages[0] = page;
> - pages[1] = get_next_page(page);
> - BUG_ON(!pages[1]);
> + pool->flags = flags;
>
> - return __zs_map_object(area, pages, off, class->size);
> + return pool;
> +
> +err:
> + zs_destroy_pool(pool);
> + return NULL;
> }
> -EXPORT_SYMBOL_GPL(zs_map_object);
> +EXPORT_SYMBOL_GPL(zs_create_pool);
>
> -void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
> +void zs_destroy_pool(struct zs_pool *pool)
> {
> - struct page *page;
> - unsigned long obj_idx, off;
> + int i;
>
> - unsigned int class_idx;
> - enum fullness_group fg;
> - struct size_class *class;
> - struct mapping_area *area;
> + for (i = 0; i < zs_size_classes; i++) {
> + int fg;
> + struct size_class *class = pool->size_class[i];
>
> - BUG_ON(!handle);
> + if (!class)
> + continue;
>
> - obj_handle_to_location(handle, &page, &obj_idx);
> - get_zspage_mapping(get_first_page(page), &class_idx, &fg);
> - class = pool->size_class[class_idx];
> - off = obj_idx_to_offset(page, obj_idx, class->size);
> + if (class->index != i)
> + continue;
>
> - area = this_cpu_ptr(&zs_map_area);
> - if (off + class->size <= PAGE_SIZE)
> - kunmap_atomic(area->vm_addr);
> - else {
> - struct page *pages[2];
> + for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) {
> + if (class->fullness_list[fg]) {
> + pr_info("Freeing non-empty class with size %db, fullness group %d\n",
> + class->size, fg);
> + }
> + }
> + kfree(class);
> + }
>
> - pages[0] = page;
> - pages[1] = get_next_page(page);
> - BUG_ON(!pages[1]);
> + kfree(pool->size_class);
> + kfree(pool);
> +}
> +EXPORT_SYMBOL_GPL(zs_destroy_pool);
>
> - __zs_unmap_object(area, pages, off, class->size);
> +static int __init zs_init(void)
> +{
> + int ret = zs_register_cpu_notifier();
> +
> + if (ret) {
> + zs_unregister_cpu_notifier();
> + return ret;
> }
> - put_cpu_var(zs_map_area);
> +
> + init_zs_size_classes();
> +
> +#ifdef CONFIG_ZPOOL
> + zpool_register_driver(&zs_zpool_driver);
> +#endif
> + return 0;
> }
> -EXPORT_SYMBOL_GPL(zs_unmap_object);
>
> -unsigned long zs_get_total_pages(struct zs_pool *pool)
> +static void __exit zs_exit(void)
> {
> - return atomic_long_read(&pool->pages_allocated);
> +#ifdef CONFIG_ZPOOL
> + zpool_unregister_driver(&zs_zpool_driver);
> +#endif
> + zs_unregister_cpu_notifier();
> }
> -EXPORT_SYMBOL_GPL(zs_get_total_pages);
>
> module_init(zs_init);
> module_exit(zs_exit);
> --
> 1.7.9.5
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/