zsmalloc: compaction support
Minchan Kim
maintainerv2 cover zsmalloc: compaction support
Recently, we started to use zram heavily and some of issues popped.
1) external fragmentation
I got a report from Juneho Choi that fork failed although there are plenty of free pages in the system. His investigation revealed zram is one of the culprit to make heavy fragmentation so there was no more contiguous 16K page for pgd to fork in the ARM.
2) non-movable pages
Other problem of zram now is that inherently, user want to use zram as swap in small memory system so they use zRAM with CMA to use memory efficiently. However, unfortunately, it doesn't work well because zRAM cannot use CMA's movable pages unless it doesn't support compaction. I got several reports about that OOM happened with zram although there are lots of swap space and free space in CMA area.
3) internal fragmentation
zRAM has started support memory limitation feature to limit memory usage and I sent a patchset(https://lkml.org/lkml/2014/9/21/148) for VM to be harmonized with zram-swap to stop anonymous page reclaim if zram consumed memory up to the limit although there are free space on the swap. One problem for that direction is zram has no way to know any hole in memory space zsmalloc allocated by internal fragmentation so zram would regard swap is full although there are free space in zsmalloc. For solving the issue, zram want to trigger compaction of zsmalloc before it decides full or not.
This patchset is first step to support above issues. For that, it adds indirect layer between handle and object location and supports manual compaction to solve 3th problem first of all.
After this patchset got merged, next step is to make VM aware of zsmalloc compaction so that generic compaction will move zsmalloced-pages automatically in runtime.
In my imaginary experiment(ie, high compress ratio data with heavy swap in/out on 8G zram-swap), data is as follows,
Before =
zram allocated object : 60212066 bytes zram total used: 140103680 bytes ratio: 42.98 percent MemFree: 840192 kB
Compaction
After =
frag ratio after compaction
zram allocated object : 60212066 bytes zram total used: 76185600 bytes ratio: 79.03 percent MemFree: 901932 kB
Juneho reported below in his real platform with small aging. So, I think the benefit would be bigger in real aging system for a long time.
- frag_ratio increased 3% (ie, higher is better)
- memfree increased about 6MB
- In buddy info, Normal 2^3: 4, 2^2: 1: 2^1 increased, Highmem: 2^1 21 increased
frag ratio after swap fragment
used : 156677 kbytes total: 166092 kbytes frag_ratio : 94 meminfo before compaction MemFree: 83724 kB Node 0, zone Normal 13642 1364 57 10 61 17 9 5 4 0 0 Node 0, zone HighMem 425 29 1 0 0 0 0 0 0 0 0
num_migrated : 23630
compaction done
frag ratio after compaction
used : 156673 kbytes total: 160564 kbytes frag_ratio : 97 meminfo after compaction MemFree: 89060 kB Node 0, zone Normal 14076 1544 67 14 61 17 9 5 4 0 0 Node 0, zone HighMem 863 50 1 0 0 0 0 0 0 0 0
This patchset adds more logics(about 480 lines) in zsmalloc but when I tested heavy swapin/out program, the regression for swapin/out speed is marginal because most of overheads were caused by compress/decompress and other MM reclaim stuff.
* from v1
* remove rcu - suggested by Joonsoo
* iterating biggest size class - Seth
* add experiment data in description - Juneho
Minchan Kim (7):
zsmalloc: decouple handle and object
zsmalloc: factor out obj_[malloc|free]
zsmalloc: support compaction
zsmalloc: adjust ZS_ALMOST_FULL
zram: support compaction
zsmalloc: record handle in page->private for huge object
zsmalloc: add fullness into stat
Documentation/ABI/testing/sysfs-block-zram | 15 + drivers/block/zram/zram_drv.c | 25 + drivers/block/zram/zram_drv.h | 1 + include/linux/zsmalloc.h | 1 + mm/zsmalloc.c | 991 +++++++++++++++++++++-------- 5 files changed, 777 insertions(+), 256 deletions(-)
Signature · 1 line
1.9.1
Minchan Kim
maintainerPATCH v2 1/7 zsmalloc: decouple handle and object
Currently, handle of zsmalloc encodes object's location directly so it makes support of migration hard.
This patch decouples handle and object via adding indirect layer. For that, it allocates handle dynamically and returns it to user. The handle is the address allocated by slab allocation so it's unique and we could keep object's location in the memory space allocated for handle.
With it, we can change object's position without changing handle itself.
--- mm/zsmalloc.c | 128 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 30 deletions(-)
mm/zsmalloc.c +98 -30
@@ -110,6 +110,8 @@#define ZS_MAX_ZSPAGE_ORDER 2#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) +#define ZS_HANDLE_SIZE (sizeof(unsigned long)) +/* * Object location (<PFN>, <obj_idx>) is encoded as * as single (unsigned long) handle value.
@@ -140,7 +142,8 @@/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ #define ZS_MIN_ALLOC_SIZE \ MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) -#define ZS_MAX_ALLOC_SIZE PAGE_SIZE +/* each chunk includes extra space to keep handle */ +#define ZS_MAX_ALLOC_SIZE (PAGE_SIZE + ZS_HANDLE_SIZE)/* * On systems with 4K page size, this gives 255 size classes! There is a
@@ -233,14 +236,24 @@ struct size_class {* This must be power of 2 and less than or equal to ZS_ALIGN */ struct link_free { -/* Handle of next free chunk (encodes <PFN, obj_idx>) */ -void *next; +union { +/* +* Position of next free chunk (encodes <PFN, obj_idx>) +* It's valid for non-allocated object +*/ +void *next; +/* +* Handle of allocated object. +*/ +unsigned long handle; + }; };struct zs_pool {char *name;struct size_class **size_class; +struct kmem_cache *handle_cachep;gfp_t flags;/* allocation flags used when growing pool */ atomic_long_t pages_allocated;
@@ -269,6 +282,34 @@ struct mapping_area {enum zs_mapmode vm_mm;/* mapping mode */ }; +static int create_handle_cache (struct zs_pool *pool) +{ + pool->handle_cachep =kmem_cache_create ("zs_handle" ,ZS_HANDLE_SIZE , +0 ,0 ,NULL ); +return pool->handle_cachep ?0 :1 ; +} + +static void destroy_handle_cache (struct zs_pool *pool) +{ +kmem_cache_destroy (pool->handle_cachep); +} + +static unsigned long alloc_handle (struct zs_pool *pool) +{ +return (unsigned long )kmem_cache_alloc (pool->handle_cachep, + pool->flags & ~__GFP_HIGHMEM); +} + +static void free_handle (struct zs_pool *pool,unsigned long handle) +{ +kmem_cache_free (pool->handle_cachep, (void *)handle); +} + +static void record_obj (unsigned long handle,unsigned long obj) +{ + *(unsigned long *)handle = obj; +} +/* zpool driver */ #ifdef CONFIG_ZPOOL
@@ -595,13 +636,18 @@ static void *obj_location_to_handle(struct page *page, unsigned long obj_idx)* decoded obj_idx back to its original value since it was adjusted in * obj_location_to_handle(). */ -static void obj_handle_to_location (unsigned long handle,struct page **page, +static void obj_to_location (unsigned long handle,struct page **page,unsigned long *obj_idx) { *page =pfn_to_page (handle >>OBJ_INDEX_BITS ); *obj_idx = (handle &OBJ_INDEX_MASK ) -1 ; } +static unsigned long handle_to_obj (unsigned long handle) +{ +return *(unsigned long *)handle; +} +static unsigned long obj_idx_to_offset (struct page *page,unsigned long obj_idx,int class_size) {
@@ -860,12 +906,16 @@ static void __zs_unmap_object(struct mapping_area *area, {int sizes[2 ];void *addr; -char *buf = area->vm_buf; +char *buf;/* no write fastpath */ if (area->vm_mm ==ZS_MM_RO ) goto out; + buf = area->vm_buf +ZS_HANDLE_SIZE ; + size -=ZS_HANDLE_SIZE ; + off +=ZS_HANDLE_SIZE ; + sizes[0 ] =PAGE_SIZE - off; sizes[1 ] = size - sizes[0 ];
@@ -1153,13 +1203,14 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, enum zs_mapmode mm) {struct page *page; -unsigned long obj_idx, off; +unsigned long obj, obj_idx, off;unsigned int class_idx;enum fullness_group fg;struct size_class *class;struct mapping_area *area;struct page *pages[2 ]; +void *ret;BUG_ON (!handle);
@@ -1170,7 +1221,8 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,*/ BUG_ON (in_interrupt ()); -obj_handle_to_location (handle, &page, &obj_idx); + obj =handle_to_obj (handle); +obj_to_location (obj, &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);
@@ -1180,7 +1232,8 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,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; + ret = area->vm_addr + off; + goto out; }/* this object spans two pages */
@@ -1188,14 +1241,16 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, pages[1 ] =get_next_page (page);BUG_ON (!pages[1 ]); -return __zs_map_object (area, pages, off, class->size); + ret =__zs_map_object (area, pages, off, class->size); +out: +return ret +ZS_HANDLE_SIZE ; }EXPORT_SYMBOL_GPL (zs_map_object);void zs_unmap_object (struct zs_pool *pool,unsigned long handle) {struct page *page; -unsigned long obj_idx, off; +unsigned long obj, obj_idx, off;unsigned int class_idx;enum fullness_group fg;
@@ -1204,7 +1259,8 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)BUG_ON (!handle); -obj_handle_to_location (handle, &page, &obj_idx); + obj =handle_to_obj (handle); +obj_to_location (obj, &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);
@@ -1236,7 +1292,7 @@ EXPORT_SYMBOL_GPL(zs_unmap_object);*/ unsigned long zs_malloc (struct zs_pool *pool,size_t size) { -unsigned long obj; +unsigned long handle, obj;struct link_free *link;struct size_class *class;void *vaddr;
@@ -1244,9 +1300,15 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)struct page *first_page, *m_page;unsigned long m_objidx, m_offset; -if (unlikely (!size || size >ZS_MAX_ALLOC_SIZE )) +if (unlikely (!size || (size +ZS_HANDLE_SIZE ) >ZS_MAX_ALLOC_SIZE ))return 0 ; + handle =alloc_handle (pool); +if (!handle) +return 0 ; + +/* extra space in chunk to keep the handle */ + size +=ZS_HANDLE_SIZE ; class = pool->size_class[get_size_class_index (size)];spin_lock (&class->lock);
@@ -1255,8 +1317,10 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)if (!first_page) {spin_unlock (&class->lock); first_page =alloc_zspage (class, pool->flags); -if (unlikely (!first_page)) +if (unlikely (!first_page)) { +free_handle (pool, handle);return 0 ; + }set_zspage_mapping (first_page, class->index,ZS_EMPTY ); atomic_long_add(class->pages_per_zspage,
@@ -1268,40 +1332,45 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) } obj = (unsigned long )first_page->freelist; -obj_handle_to_location (obj, &m_page, &m_objidx); +obj_to_location (obj, &m_page, &m_objidx); m_offset =obj_idx_to_offset (m_page, m_objidx, class->size); vaddr =kmap_atomic (m_page); link = (struct link_free *)vaddr + m_offset /sizeof (*link); first_page->freelist = link->next; -memset (link,POISON_INUSE ,sizeof (*link)); + +/* record handle in the header of allocated chunk */ + link->handle = handle;kunmap_atomic (vaddr); first_page->inuse++;zs_stat_inc (class,OBJ_USED ,1 );/* Now move the zspage to another fullness group, if required */ fix_fullness_group (pool, first_page); +record_obj (handle, obj);spin_unlock (&class->lock); -return obj; +return handle; }EXPORT_SYMBOL_GPL (zs_malloc); -void zs_free (struct zs_pool *pool,unsigned long obj) +void zs_free (struct zs_pool *pool,unsigned long handle) {struct link_free *link;struct page *first_page, *f_page; -unsigned long f_objidx, f_offset; +unsigned long obj, f_objidx, f_offset;void *vaddr;int class_idx;struct size_class *class;enum fullness_group fullness; -if (unlikely (!obj)) +if (unlikely (!handle))return ; -obj_handle_to_location (obj, &f_page, &f_objidx); + obj =handle_to_obj (handle); +free_handle (pool, handle); +obj_to_location (obj, &f_page, &f_objidx); first_page =get_first_page (f_page);get_zspage_mapping (first_page, &class_idx, &fullness);
@@ -1356,18 +1425,16 @@ struct zs_pool *zs_create_pool(char *name, gfp_t flags)return NULL ; pool->name =kstrdup (name,GFP_KERNEL ); -if (!pool->name) { -kfree (pool); -return NULL ; - } +if (!pool->name) + goto err; + +if (create_handle_cache (pool)) + goto err; pool->size_class =kcalloc (zs_size_classes,sizeof (struct size_class *),GFP_KERNEL ); -if (!pool->size_class) { -kfree (pool->name); -kfree (pool); -return NULL ; - } +if (!pool->size_class) + goto err;/* * Iterate reversly, because, size of size_class that we want to use
@@ -1450,6 +1517,7 @@ void zs_destroy_pool(struct zs_pool *pool)kfree (class); } +destroy_handle_cache (pool);kfree (pool->size_class);kfree (pool->name);kfree (pool);
Signature · 1 line
1.9.1
Minchan Kim
maintainerRe: [PATCH v2 1/7] zsmalloc: decouple handle and object
Oops. I sent old version which had a bug Ganesh pointed out. Sorry for that.
Please review this instead.
Minchan Kim
maintainerPATCH v2 2/7 zsmalloc: factor out obj_[malloc|free]
In reply to Minchan KimIn later patch, migration needs some part of functions in zs_malloc and zs_free so this patch factor out them.
--- mm/zsmalloc.c | 99 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 38 deletions(-)
mm/zsmalloc.c +61 -38
@@ -525,11 +525,10 @@ static void remove_zspage(struct page *page, struct size_class *class,* page from the freelist of the old fullness group to that of the new * fullness group. */ -static enum fullness_group fix_fullness_group (struct zs_pool *pool, +static enum fullness_group fix_fullness_group (struct size_class *class,struct page *page) {int class_idx; -struct size_class *class;enum fullness_group currfg, newfg;BUG_ON (!is_first_page (page));
@@ -539,7 +538,6 @@ static enum fullness_group fix_fullness_group(struct zs_pool *pool,if (newfg == currfg) goto out; - class = pool->size_class[class_idx];remove_zspage (page, class, currfg);insert_zspage (page, class, newfg);set_zspage_mapping (page, class_idx, newfg);
@@ -1281,6 +1279,33 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle) }EXPORT_SYMBOL_GPL (zs_unmap_object); +static unsigned long obj_malloc (struct page *first_page, +struct size_class *class,unsigned long handle) +{ +unsigned long obj; +struct link_free *link; + +struct page *m_page; +unsigned long m_objidx, m_offset; +void *vaddr; + + obj = (unsigned long )first_page->freelist; +obj_to_location (obj, &m_page, &m_objidx); + m_offset =obj_idx_to_offset (m_page, m_objidx, class->size); + + vaddr =kmap_atomic (m_page); + link = (struct link_free *)vaddr + m_offset /sizeof (*link); + first_page->freelist = link->next; +/* record handle in the header of allocated chunk */ + link->handle = handle; +kunmap_atomic (vaddr); + first_page->inuse++; +zs_stat_inc (class,OBJ_USED ,1 ); + +return obj; +} + +/** * zs_malloc - Allocate block of given size from pool. * @pool: pool to allocate from
@@ -1293,12 +1318,8 @@ EXPORT_SYMBOL_GPL(zs_unmap_object);unsigned long zs_malloc (struct zs_pool *pool,size_t size) {unsigned long handle, obj; -struct link_free *link;struct size_class *class; -void *vaddr; - -struct page *first_page, *m_page; -unsigned long m_objidx, m_offset; +struct page *first_page;if (unlikely (!size || (size +ZS_HANDLE_SIZE ) >ZS_MAX_ALLOC_SIZE ))return 0 ;
@@ -1331,22 +1352,9 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) class->size, class->pages_per_zspage)); } - obj = (unsigned long )first_page->freelist; -obj_to_location (obj, &m_page, &m_objidx); - m_offset =obj_idx_to_offset (m_page, m_objidx, class->size); - - vaddr =kmap_atomic (m_page); - link = (struct link_free *)vaddr + m_offset /sizeof (*link); - first_page->freelist = link->next; - -/* record handle in the header of allocated chunk */ - link->handle = handle; -kunmap_atomic (vaddr); - - first_page->inuse++; -zs_stat_inc (class,OBJ_USED ,1 ); + obj =obj_malloc (first_page, class, handle);/* Now move the zspage to another fullness group, if required */ -fix_fullness_group (pool, first_page); +fix_fullness_group (class, first_page);record_obj (handle, obj);spin_unlock (&class->lock);
@@ -1354,46 +1362,61 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) }EXPORT_SYMBOL_GPL (zs_malloc); -void zs_free (struct zs_pool *pool,unsigned long handle) +static void obj_free (struct zs_pool *pool,struct size_class *class, +unsigned long obj) {struct link_free *link;struct page *first_page, *f_page; -unsigned long obj, f_objidx, f_offset; +unsigned long f_objidx, f_offset;void *vaddr; -int class_idx; -struct size_class *class;enum fullness_group fullness; -if (unlikely (!handle)) -return ; +BUG_ON (!obj); - obj =handle_to_obj (handle); -free_handle (pool, handle);obj_to_location (obj, &f_page, &f_objidx); first_page =get_first_page (f_page);get_zspage_mapping (first_page, &class_idx, &fullness); - class = pool->size_class[class_idx]; f_offset =obj_idx_to_offset (f_page, f_objidx, class->size); -spin_lock (&class->lock); + vaddr =kmap_atomic (f_page);/* Insert this object in containing zspage's freelist */ - vaddr =kmap_atomic (f_page); link = (struct link_free *)(vaddr + f_offset); link->next = first_page->freelist;kunmap_atomic (vaddr); first_page->freelist = (void *)obj; - first_page->inuse--; - fullness =fix_fullness_group (pool, first_page); -zs_stat_dec (class,OBJ_USED ,1 ); +} + +void zs_free (struct zs_pool *pool,unsigned long handle) +{ +struct page *first_page, *f_page; +unsigned long obj, f_objidx; + +int class_idx; +struct size_class *class; +enum fullness_group fullness; + +if (unlikely (!handle)) +return ; + + obj =handle_to_obj (handle); +free_handle (pool, handle); +obj_to_location (obj, &f_page, &f_objidx); + first_page =get_first_page (f_page); + +get_zspage_mapping (first_page, &class_idx, &fullness); + class = pool->size_class[class_idx]; + +spin_lock (&class->lock); +obj_free (pool, class, obj); + fullness =fix_fullness_group (class, first_page);if (fullness ==ZS_EMPTY )zs_stat_dec (class,OBJ_ALLOCATED ,get_maxobj_per_zspage ( class->size, class->pages_per_zspage)); -spin_unlock (&class->lock);if (fullness ==ZS_EMPTY ) {
Signature · 7 lines
1.9.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerPATCH v2 3/7 zsmalloc: support compaction
In reply to Minchan KimThis patch provides core functions for migration of zsmalloc. Migraion policy is simple as follows.
for each size class {
while {
src_page = get zs_page from ZS_ALMOST_EMPTY
if (!src_page)
break;
dst_page = get zs_page from ZS_ALMOST_FULL
if (!dst_page)
dst_page = get zs_page from ZS_ALMOST_EMPTY
if (!dst_page)
break;
migrate(from src_page, to dst_page);
}
}For migration, we need to identify which objects in zspage are allocated to migrate them out. We could know it by iterating of freed objects in a zspage because first_page of zspage keeps free objects singly-linked list but it's not efficient. Instead, this patch adds a tag(ie, OBJ_ALLOCATED_TAG) in header of each object(ie, handle) so we could check whether the object is allocated easily.
This patch adds another status bit in handle to synchronize between user access through zs_map_object and migration. During migration, we cannot move objects user are using due to data coherency between old object and new object.
--- include/linux/zsmalloc.h | 1 + mm/zsmalloc.c | 377 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 359 insertions(+), 19 deletions(-)
include/linux/zsmalloc.h +1 -0
@@ -47,5 +47,6 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,void zs_unmap_object (struct zs_pool *pool,unsigned long handle);unsigned long zs_get_total_pages (struct zs_pool *pool); +unsigned long zs_compact (struct zs_pool *pool);#endif
mm/zsmalloc.c +358 -19
489 lines not on this page · raw message
Signature · 7 lines
1.9.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v2 3/7] zsmalloc: support compaction
Hello Minchan,
Nice work!
mm/zsmalloc.c void zs_free(struct zs_pool *pool, unsigned long handle) · line 1671
Quoted 11 lines · Minchan Kim
mm/zsmalloc.c +11
-0+static void putback_zspage (struct zs_pool *pool,struct size_class *class, +struct page *first_page) +{ +int class_idx; +enum fullness_group fullness; + +BUG_ON (!is_first_page (first_page)); + +get_zspage_mapping (first_page, &class_idx, &fullness); +insert_zspage (first_page, class, fullness); + fullness =fix_fullness_group (class, first_page);
Removal and re-insertion of zspage above can be eliminated, like this:
fullness = get_fullness_group(first_page); insert_zspage(first_page, class, fullness); set_zspage_mapping(first_page, class->index, fullness);
regards,
heesub
mm/zsmalloc.c void zs_free(struct zs_pool *pool, unsigned long handle) · line 1681
Quoted 11 lines
mm/zsmalloc.c +4
-1if (fullness ==ZS_EMPTY ) { +zs_stat_dec (class,OBJ_ALLOCATED ,get_maxobj_per_zspage ( + class->size, class->pages_per_zspage));atomic_long_sub (class->pages_per_zspage, &pool->pages_allocated); +free_zspage (first_page); } } -EXPORT_SYMBOL_GPL (zs_free); +
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerRe: [PATCH v2 3/7] zsmalloc: support compaction
Hello Heesub,
Quoted 3 lines · Heesub Shin
Hello Minchan,
Nice work!
Thanks. :)
Quoted 16 lines · Minchan Kim
mm/zsmalloc.c void zs_free(struct zs_pool *pool, unsigned long handle) · line 1671
Quoted 11 lines
mm/zsmalloc.c +11
-0+static void putback_zspage (struct zs_pool *pool,struct size_class *class, +struct page *first_page) +{ +int class_idx; +enum fullness_group fullness; + +BUG_ON (!is_first_page (first_page)); + +get_zspage_mapping (first_page, &class_idx, &fullness); +insert_zspage (first_page, class, fullness); + fullness =fix_fullness_group (class, first_page);Removal and re-insertion of zspage above can be eliminated, like this:
Quoted 3 lines
fullness = get_fullness_group(first_page); insert_zspage(first_page, class, fullness); set_zspage_mapping(first_page, class->index, fullness);
True.
Thanks for the review!
Signature · 8 lines
Kind regards, Minchan Kim -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerPATCH v2 4/7 zsmalloc: adjust ZS_ALMOST_FULL
In reply to Minchan KimCurretly, zsmalloc regards a zspage as ZS_ALMOST_EMPTY if the zspage has under 1/4 used objects(ie, fullness_threshold_frac). It could make result in loose packing since zsmalloc migrates only ZS_ALMOST_EMPTY zspage out.
This patch changes the rule so that zsmalloc makes zspage which has above 3/4 used object ZS_ALMOST_FULL so it could make tight packing.
--- mm/zsmalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/zsmalloc.c +1 -1
@@ -479,7 +479,7 @@ static enum fullness_group get_fullness_group(struct page *page) fg =ZS_EMPTY ; else if (inuse == max_objects) fg =ZS_FULL ; - else if (inuse <= max_objects / fullness_threshold_frac) + else if (inuse <= 3 * max_objects / fullness_threshold_frac) fg =ZS_ALMOST_EMPTY ; else fg =ZS_ALMOST_FULL ;
Signature · 7 lines
1.9.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerPATCH v2 5/7 zram: support compaction
In reply to Minchan KimNow that zsmalloc supports compaction, zram can use it. For the first step, this patch exports compact knob via sysfs so user can do compaction via "echo 1 > /sys/block/zram0/compact".
--- Documentation/ABI/testing/sysfs-block-zram | 15 +++++++++++++++ drivers/block/zram/zram_drv.c | 25 +++++++++++++++++++++++++ drivers/block/zram/zram_drv.h | 1 + 3 files changed, 41 insertions(+)
Documentation/ABI/testing/sysfs-block-zram +15 -0
@@ -141,3 +141,18 @@ Description: amount of memory ZRAM can use to store the compressed data. The limit could be changed in run time and "0" means disable the limit. No limit is the initial state. Unit: bytes + +What: /sys/block/zram<id>/compact +Date: August 2015 +Contact: Minchan Kim <[email protected]> +Description: + The compact file is write-only and trigger compaction for + allocator zrm uses. The allocator moves some objects so that + it could free fragment space. + +What: /sys/block/zram<id>/num_migrated +Date: August 2015 +Contact: Minchan Kim <[email protected]> +Description: + The compact file is read-only and shows how many object + migrated by compaction.
drivers/block/zram/zram_drv.c +25 -0
@@ -246,6 +246,27 @@ static ssize_t comp_algorithm_store(struct device *dev,return len; } +static ssize_t compact_store (struct device *dev, +struct device_attribute *attr,const char *buf,size_t len) +{ +unsigned long nr_migrated; +struct zram *zram =dev_to_zram (dev); +struct zram_meta *meta; + +down_read (&zram->init_lock); +if (!init_done (zram)) { +up_read (&zram->init_lock); +return -EINVAL ; + } + + meta = zram->meta; + nr_migrated =zs_compact (meta->mem_pool); +atomic64_add (nr_migrated, &zram->stats.num_migrated); +up_read (&zram->init_lock); + +return len; +} +/* flag operations needs meta->tb_lock */ static int zram_test_flag (struct zram_meta *meta,u32 index,enum zram_pageflags flag)
@@ -1017,6 +1038,7 @@ static const struct block_device_operations zram_devops = { .owner = THIS_MODULE }; +static DEVICE_ATTR_WO (compact);static DEVICE_ATTR_RW (disksize);static DEVICE_ATTR_RO (initstate);static DEVICE_ATTR_WO (reset);
@@ -1035,6 +1057,7 @@ ZRAM_ATTR_RO(invalid_io);ZRAM_ATTR_RO (notify_free);ZRAM_ATTR_RO (zero_pages);ZRAM_ATTR_RO (compr_data_size); +ZRAM_ATTR_RO (num_migrated);static struct attribute *zram_disk_attrs[] = { &dev_attr_disksize.attr,
@@ -1044,6 +1067,8 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_num_writes.attr, &dev_attr_failed_reads.attr, &dev_attr_failed_writes.attr, + &dev_attr_num_migrated.attr, + &dev_attr_compact.attr, &dev_attr_invalid_io.attr, &dev_attr_notify_free.attr, &dev_attr_zero_pages.attr,
drivers/block/zram/zram_drv.h +1 -0
@@ -84,6 +84,7 @@ struct zram_stats {atomic64_t compr_data_size;/* compressed size of pages stored */ atomic64_t num_reads;/* failed + successful */ atomic64_t num_writes;/* --do-- */ +atomic64_t num_migrated;/* no. of migrated object */ atomic64_t failed_reads;/* can happen when memory is too low */ atomic64_t failed_writes;/* can happen when memory is too low */ atomic64_t invalid_io;/* non-page-aligned I/O requests */
Signature · 7 lines
1.9.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerPATCH v2 6/7 zsmalloc: record handle in page->private for huge object
In reply to Minchan KimWe stores handle on header of each allocated object so it increases the size of each object by sizeof(unsigned long).
If zram stores 4096 bytes to zsmalloc(ie, bad compression), zsmalloc needs 4104B-class to add handle.
However, 4104B-class has 1-pages_per_zspage so wasted size by internal fragment is 8192 - 4104, which is terrible.
So this patch records the handle in page->private on such huge object(ie, pages_per_zspage == 1 && maxobj_per_zspage == 1) instead of header of each object so we could use 4096B-class, not 4104B-class.
--- mm/zsmalloc.c | 54 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 12 deletions(-)
mm/zsmalloc.c +42 -12
@@ -57,6 +57,8 @@* * page->private (union with page->first_page): refers to the * component page after the first page +* If the page is first_page for huge object, it stores handle. +* Look at size_class->huge. * page->freelist: points to the first free object in zspage. * Free objects are linked together using in-place * metadata.
@@ -162,7 +164,7 @@#define ZS_MIN_ALLOC_SIZE \ MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS))/* each chunk includes extra space to keep handle */ -#define ZS_MAX_ALLOC_SIZE (PAGE_SIZE + ZS_HANDLE_SIZE) +#define ZS_MAX_ALLOC_SIZE PAGE_SIZE/* * On systems with 4K page size, this gives 255 size classes! There is a
@@ -238,6 +240,8 @@ struct size_class {/* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */ int pages_per_zspage; +/* huge object: pages_per_zspage == 1 && maxobj_per_zspage == 1 */ +bool huge;#ifdef CONFIG_ZSMALLOC_STAT struct zs_size_stat stats;
@@ -299,6 +303,7 @@ struct mapping_area {#endif char *vm_addr;/* address of kmap_atomic()'ed pages */ enum zs_mapmode vm_mm;/* mapping mode */ +bool huge; };static int create_handle_cache (struct zs_pool *pool)
@@ -456,7 +461,7 @@ static int get_size_class_index(int size) idx =DIV_ROUND_UP (size -ZS_MIN_ALLOC_SIZE ,ZS_SIZE_CLASS_DELTA ); -return idx; +return min (zs_size_classes -1 , idx); }/*
@@ -665,9 +670,14 @@ static unsigned long handle_to_obj(unsigned long handle)return *(unsigned long *)handle; } -unsigned long obj_to_head (void *obj) +static unsigned long obj_to_head (struct size_class *class,struct page *page, +void *obj) { -return *(unsigned long *)obj; +if (class->huge) { +VM_BUG_ON (!is_first_page (page)); +return *(unsigned long *)page_private (page); + }else +return *(unsigned long *)obj; } static unsigned long obj_idx_to_offset(struct page *page,
@@ -953,9 +963,12 @@ static void __zs_unmap_object(struct mapping_area *area,if (area->vm_mm ==ZS_MM_RO ) goto out; - buf = area->vm_buf +ZS_HANDLE_SIZE ; - size -=ZS_HANDLE_SIZE ; - off +=ZS_HANDLE_SIZE ; + buf = area->vm_buf; +if (!area->huge) { + buf = buf +ZS_HANDLE_SIZE ; + size -=ZS_HANDLE_SIZE ; + off +=ZS_HANDLE_SIZE ; + } sizes[0 ] =PAGE_SIZE - off; sizes[1 ] = size - sizes[0 ];
@@ -1294,7 +1307,10 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, ret =__zs_map_object (area, pages, off, class->size); out: -return ret +ZS_HANDLE_SIZE ; +if (!class->huge) + ret +=ZS_HANDLE_SIZE ; + +return ret; }EXPORT_SYMBOL_GPL (zs_map_object);
@@ -1351,8 +1367,12 @@ static unsigned long obj_malloc(struct page *first_page, vaddr =kmap_atomic (m_page); link = (struct link_free *)vaddr + m_offset /sizeof (*link); first_page->freelist = link->next; -/* record handle in the header of allocated chunk */ - link->handle = handle; +if (!class->huge) +/* record handle in the header of allocated chunk */ + link->handle = handle; +else +/* record handle in first_page->private */ +set_page_private (first_page, handle);kunmap_atomic (vaddr); first_page->inuse++;zs_stat_inc (class,OBJ_USED ,1 );
@@ -1376,7 +1396,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)struct size_class *class;struct page *first_page; -if (unlikely (!size || (size +ZS_HANDLE_SIZE ) >ZS_MAX_ALLOC_SIZE )) +if (unlikely (!size || size >ZS_MAX_ALLOC_SIZE ))return 0 ; handle =alloc_handle (pool);
@@ -1386,6 +1406,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)/* extra space in chunk to keep the handle */ size +=ZS_HANDLE_SIZE ; class = pool->size_class[get_size_class_index (size)]; +/* In huge class size, we store the handle into first_page->private */ +if (class->huge) { + size -=ZS_HANDLE_SIZE ; + class = pool->size_class[get_size_class_index (size)]; + }spin_lock (&class->lock); first_page =find_get_zspage (class);
@@ -1441,6 +1466,8 @@ static void obj_free(struct zs_pool *pool, struct size_class *class,/* Insert this object in containing zspage's freelist */ link = (struct link_free *)(vaddr + f_offset); link->next = first_page->freelist; +if (class->huge) +set_page_private (first_page,0 );kunmap_atomic (vaddr); first_page->freelist = (void *)obj; first_page->inuse--;
@@ -1567,7 +1594,7 @@ static unsigned long find_alloced_obj(struct page *page, int index, offset += class->size * index;while (offset <PAGE_SIZE ) { - head =obj_to_head (addr + offset); + head =obj_to_head (class, page, addr + offset);if (head &OBJ_ALLOCATED_TAG ) { handle = head & ~OBJ_ALLOCATED_TAG ;if (trypin_tag (handle))
@@ -1835,6 +1862,9 @@ struct zs_pool *zs_create_pool(char *name, gfp_t flags) class->size = size; class->index = i; class->pages_per_zspage = pages_per_zspage; +if (pages_per_zspage ==1 && +get_maxobj_per_zspage (size, pages_per_zspage) ==1 ) + class->huge = true;spin_lock_init (&class->lock); pool->size_class[i] = class;
Signature · 1 line
1.9.1
Minchan Kim
maintainerPATCH v2 7/7 zsmalloc: add fullness into stat
In reply to Minchan KimDuring investigating compaction, fullness information of each class is helpful for investigating how the compaction works well. With that, we could know how compaction works well more clear on each size class.
--- mm/zsmalloc.c | 349 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 184 insertions(+), 165 deletions(-)
mm/zsmalloc.c +184 -165
@@ -196,6 +196,8 @@ enum fullness_group {enum zs_stat_type {OBJ_ALLOCATED ,OBJ_USED , +CLASS_ALMOST_FULL , +CLASS_ALMOST_EMPTY ,NR_ZS_STAT_TYPE , };
@@ -411,6 +413,11 @@ static struct zpool_driver zs_zpool_driver = {MODULE_ALIAS ("zpool-zsmalloc" );#endif /* CONFIG_ZPOOL */ +static unsigned int get_maxobj_per_zspage (int size,int pages_per_zspage) +{ +return pages_per_zspage *PAGE_SIZE / size; +} +/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ static DEFINE_PER_CPU(struct mapping_area, zs_map_area);
@@ -464,6 +471,179 @@ static int get_size_class_index(int size)return min (zs_size_classes -1 , idx); } +#ifdef CONFIG_ZSMALLOC_STAT + +static inline void zs_stat_inc (struct size_class *class, +enum zs_stat_type type,unsigned long cnt) +{ + class->stats.objs[type] += cnt; +} + +static inline void zs_stat_dec (struct size_class *class, +enum zs_stat_type type,unsigned long cnt) +{ + class->stats.objs[type] -= cnt; +} + +static inline unsigned long zs_stat_get (struct size_class *class, +enum zs_stat_type type) +{ +return class->stats.objs[type]; +} + +static int __init zs_stat_init (void ) +{ +if (!debugfs_initialized ()) +return -ENODEV ; + + zs_stat_root =debugfs_create_dir ("zsmalloc" ,NULL ); +if (!zs_stat_root) +return -ENOMEM ; + +return 0 ; +} + +static void __exit zs_stat_exit(void) +{ +debugfs_remove_recursive (zs_stat_root); +} + +static int zs_stats_size_show (struct seq_file *s,void *v) +{ +int i; +struct zs_pool *pool = s->private; +struct size_class *class; +int objs_per_zspage; +unsigned long class_almost_full, class_almost_empty; +unsigned long obj_allocated, obj_used, pages_used; +unsigned long total_class_almost_full =0 , total_class_almost_empty =0 ; +unsigned long total_objs =0 , total_used_objs =0 , total_pages =0 ; + +seq_printf (s," %5s %5s %11s %12s %13s %10s %10s %16s\n" , +"class" ,"size" ,"almost_full" ,"almost_empty" , +"obj_allocated" ,"obj_used" ,"pages_used" , +"pages_per_zspage" ); + +for (i =0 ; i < zs_size_classes; i++) { + class = pool->size_class[i]; + +if (class->index != i) +continue ; + +spin_lock (&class->lock); + class_almost_full =zs_stat_get (class,CLASS_ALMOST_FULL ); + class_almost_empty =zs_stat_get (class,CLASS_ALMOST_EMPTY ); + obj_allocated =zs_stat_get (class,OBJ_ALLOCATED ); + obj_used =zs_stat_get (class,OBJ_USED ); +spin_unlock (&class->lock); + + objs_per_zspage =get_maxobj_per_zspage (class->size, + class->pages_per_zspage); + pages_used = obj_allocated / objs_per_zspage * + class->pages_per_zspage; + +seq_printf (s," %5u %5u %11lu %12lu %13lu %10lu %10lu %16d\n" , + i, class->size, class_almost_full, class_almost_empty, + obj_allocated, obj_used, pages_used, + class->pages_per_zspage); + + total_class_almost_full += class_almost_full; + total_class_almost_empty += class_almost_empty; + total_objs += obj_allocated; + total_used_objs += obj_used; + total_pages += pages_used; + } + +seq_puts (s,"\n" ); +seq_printf (s," %5s %5s %11lu %12lu %13lu %10lu %10lu\n" , +"Total" ,"" , total_class_almost_full, + total_class_almost_empty, total_objs, + total_used_objs, total_pages); + +return 0 ; +} + +static int zs_stats_size_open (struct inode *inode,struct file *file) +{ +return single_open (file, zs_stats_size_show, inode->i_private); +} + +static const struct file_operations zs_stat_size_ops = { + .open = zs_stats_size_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int zs_pool_stat_create (char *name,struct zs_pool *pool) +{ +struct dentry *entry; + +if (!zs_stat_root) +return -ENODEV ; + + entry =debugfs_create_dir (name, zs_stat_root); +if (!entry) { +pr_warn ("debugfs dir <%s> creation failed\n" , name); +return -ENOMEM ; + } + pool->stat_dentry = entry; + + entry =debugfs_create_file ("classes" ,S_IFREG |S_IRUGO , + pool->stat_dentry, pool, &zs_stat_size_ops); +if (!entry) { +pr_warn ("%s: debugfs file entry <%s> creation failed\n" , + name,"classes" ); +return -ENOMEM ; + } + +return 0 ; +} + +static void zs_pool_stat_destroy (struct zs_pool *pool) +{ +debugfs_remove_recursive (pool->stat_dentry); +} + +#else /* CONFIG_ZSMALLOC_STAT */ + +static inline void zs_stat_inc (struct size_class *class, +enum zs_stat_type type,unsigned long cnt) +{ +} + +static inline void zs_stat_dec (struct size_class *class, +enum zs_stat_type type,unsigned long cnt) +{ +} + +static inline unsigned long zs_stat_get (struct size_class *class, +enum zs_stat_type type) +{ +return 0 ; +} + +static int __init zs_stat_init (void ) +{ +return 0 ; +} + +static void __exit zs_stat_exit(void) +{ +} + +static inline int zs_pool_stat_create (char *name,struct zs_pool *pool) +{ +return 0 ; +} + +static inline void zs_pool_stat_destroy (struct zs_pool *pool) +{ +} + +#endif + +/* * For each size class, zspages are divided into different groups * depending on how "full" they are. This was done so that we could
@@ -513,6 +693,8 @@ static void insert_zspage(struct page *page, struct size_class *class,list_add_tail (&page->lru, &(*head)->lru); *head = page; +zs_stat_inc (class, fullness ==ZS_ALMOST_EMPTY ? +CLASS_ALMOST_EMPTY :CLASS_ALMOST_FULL ,1 ); }/*
@@ -538,6 +720,8 @@ static void remove_zspage(struct page *page, struct size_class *class, struct page, lru);list_del_init (&page->lru); +zs_stat_dec (class, fullness ==ZS_ALMOST_EMPTY ? +CLASS_ALMOST_EMPTY :CLASS_ALMOST_FULL ,1 ); }/*
@@ -1056,11 +1240,6 @@ static void init_zs_size_classes(void) zs_size_classes = nr; } -static unsigned int get_maxobj_per_zspage (int size,int pages_per_zspage) -{ -return pages_per_zspage *PAGE_SIZE / size; -} -static bool can_merge (struct size_class *prev,int size,int pages_per_zspage) {if (prev->pages_per_zspage != pages_per_zspage)
@@ -1080,166 +1259,6 @@ static bool zspage_full(struct page *page)return page->inuse == page->objects; } -#ifdef CONFIG_ZSMALLOC_STAT - -static inline void zs_stat_inc (struct size_class *class, -enum zs_stat_type type,unsigned long cnt) -{ - class->stats.objs[type] += cnt; -} - -static inline void zs_stat_dec (struct size_class *class, -enum zs_stat_type type,unsigned long cnt) -{ - class->stats.objs[type] -= cnt; -} - -static inline unsigned long zs_stat_get (struct size_class *class, -enum zs_stat_type type) -{ -return class->stats.objs[type]; -} - -static int __init zs_stat_init (void ) -{ -if (!debugfs_initialized ()) -return -ENODEV ; - - zs_stat_root =debugfs_create_dir ("zsmalloc" ,NULL ); -if (!zs_stat_root) -return -ENOMEM ; - -return 0 ; -} - -static void __exit zs_stat_exit(void) -{ -debugfs_remove_recursive (zs_stat_root); -} - -static int zs_stats_size_show (struct seq_file *s,void *v) -{ -int i; -struct zs_pool *pool = s->private; -struct size_class *class; -int objs_per_zspage; -unsigned long obj_allocated, obj_used, pages_used; -unsigned long total_objs =0 , total_used_objs =0 , total_pages =0 ; - -seq_printf (s," %5s %5s %13s %10s %10s\n" ,"class" ,"size" , -"obj_allocated" ,"obj_used" ,"pages_used" ); - -for (i =0 ; i < zs_size_classes; i++) { - class = pool->size_class[i]; - -if (class->index != i) -continue ; - -spin_lock (&class->lock); - obj_allocated =zs_stat_get (class,OBJ_ALLOCATED ); - obj_used =zs_stat_get (class,OBJ_USED ); -spin_unlock (&class->lock); - - objs_per_zspage =get_maxobj_per_zspage (class->size, - class->pages_per_zspage); - pages_used = obj_allocated / objs_per_zspage * - class->pages_per_zspage; - -seq_printf (s," %5u %5u %10lu %10lu %10lu\n" , i, - class->size, obj_allocated, obj_used, pages_used); - - total_objs += obj_allocated; - total_used_objs += obj_used; - total_pages += pages_used; - } - -seq_puts (s,"\n" ); -seq_printf (s," %5s %5s %10lu %10lu %10lu\n" ,"Total" ,"" , - total_objs, total_used_objs, total_pages); - -return 0 ; -} - -static int zs_stats_size_open (struct inode *inode,struct file *file) -{ -return single_open (file, zs_stats_size_show, inode->i_private); -} - -static const struct file_operations zs_stat_size_ops = { - .open = zs_stats_size_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int zs_pool_stat_create (char *name,struct zs_pool *pool) -{ -struct dentry *entry; - -if (!zs_stat_root) -return -ENODEV ; - - entry =debugfs_create_dir (name, zs_stat_root); -if (!entry) { -pr_warn ("debugfs dir <%s> creation failed\n" , name); -return -ENOMEM ; - } - pool->stat_dentry = entry; - - entry =debugfs_create_file ("obj_in_classes" ,S_IFREG |S_IRUGO , - pool->stat_dentry, pool, &zs_stat_size_ops); -if (!entry) { -pr_warn ("%s: debugfs file entry <%s> creation failed\n" , - name,"obj_in_classes" ); -return -ENOMEM ; - } - -return 0 ; -} - -static void zs_pool_stat_destroy (struct zs_pool *pool) -{ -debugfs_remove_recursive (pool->stat_dentry); -} - -#else /* CONFIG_ZSMALLOC_STAT */ - -static inline void zs_stat_inc (struct size_class *class, -enum zs_stat_type type,unsigned long cnt) -{ -} - -static inline void zs_stat_dec (struct size_class *class, -enum zs_stat_type type,unsigned long cnt) -{ -} - -static inline unsigned long zs_stat_get (struct size_class *class, -enum zs_stat_type type) -{ -return 0 ; -} - -static int __init zs_stat_init (void ) -{ -return 0 ; -} - -static void __exit zs_stat_exit(void) -{ -} - -static inline int zs_pool_stat_create (char *name,struct zs_pool *pool) -{ -return 0 ; -} - -static inline void zs_pool_stat_destroy (struct zs_pool *pool) -{ -} - -#endif -unsigned long zs_get_total_pages (struct zs_pool *pool) {return atomic_long_read (&pool->pages_allocated);
Signature · 1 line
1.9.1
Andrew Morton
maintainerRe: [PATCH v2 7/7] zsmalloc: add fullness into stat
Quoted 5 lines · On Wed, 4 Mar 2015 14:01:32 +0900 Minchan Kim <[email protected]> wrote:
mm/zsmalloc.c +5
-0+static int zs_stats_size_show (struct seq_file *s,void *v) +{ +int i; +struct zs_pool *pool = s->private; +struct size_class *class;
mm/zsmalloc.c static int get_size_class_index(int size) · line 525
Quoted 10 lines
mm/zsmalloc.c +10
-0+int objs_per_zspage; +unsigned long class_almost_full, class_almost_empty; +unsigned long obj_allocated, obj_used, pages_used; +unsigned long total_class_almost_full =0 , total_class_almost_empty =0 ; +unsigned long total_objs =0 , total_used_objs =0 , total_pages =0 ; + +seq_printf (s," %5s %5s %11s %12s %13s %10s %10s %16s\n" , +"class" ,"size" ,"almost_full" ,"almost_empty" , +"obj_allocated" ,"obj_used" ,"pages_used" , +"pages_per_zspage" );
Documentation?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerRe: [PATCH v2 7/7] zsmalloc: add fullness into stat
Hello Andrew,
Quoted 11 lines · Andrew Morton
Quoted 9 lines · On Wed, 4 Mar 2015 14:01:32 +0900 Minchan Kim <[email protected]> wrote:
mm/zsmalloc.c +9
-0+static int zs_stats_size_show (struct seq_file *s,void *v) +{ +int i; +struct zs_pool *pool = s->private; +struct size_class *class; +int objs_per_zspage; +unsigned long class_almost_full, class_almost_empty; +unsigned long obj_allocated, obj_used, pages_used; +unsigned long total_class_almost_full =0 , total_class_almost_empty =0 ;
Quoted 8 lines
mm/zsmalloc.c static int get_size_class_index(int size) · line 525
Quoted 6 lines
mm/zsmalloc.c +6
-0+unsigned long total_objs =0 , total_used_objs =0 , total_pages =0 ; + +seq_printf (s," %5s %5s %11s %12s %13s %10s %10s %16s\n" , +"class" ,"size" ,"almost_full" ,"almost_empty" , +"obj_allocated" ,"obj_used" ,"pages_used" , +"pages_per_zspage" );Documentation?
It should been since [0f050d9, mm/zsmalloc: add statistics support]. Anyway, I will try it.
Where is right place to put only this statistics in Documentation?
Documentation/zsmalloc.txt?Documentation/vm/zsmalloc.txt?Documentation/blockdev/zram.txt?Documentation/ABI/testing/sysfs-block-zram?
I hope you suggest.
Signature · 8 lines
Kind regards, Minchan Kim -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerRe: [PATCH v2 7/7] zsmalloc: add fullness into stat
Quoted 23 lines · On Thu, 5 Mar 2015 09:43:31 +0900 Minchan Kim <[email protected]> wrote:
Hello Andrew,
Quoted 11 lines · Andrew Morton
Quoted 9 lines · On Wed, 4 Mar 2015 14:01:32 +0900 Minchan Kim <[email protected]> wrote:
mm/zsmalloc.c +9
-0+static int zs_stats_size_show (struct seq_file *s,void *v) +{ +int i; +struct zs_pool *pool = s->private; +struct size_class *class; +int objs_per_zspage; +unsigned long class_almost_full, class_almost_empty; +unsigned long obj_allocated, obj_used, pages_used; +unsigned long total_class_almost_full =0 , total_class_almost_empty =0 ;Quoted 8 lines
mm/zsmalloc.c static int get_size_class_index(int size) · line 525
Quoted 6 lines
mm/zsmalloc.c +6
-0+unsigned long total_objs =0 , total_used_objs =0 , total_pages =0 ; + +seq_printf (s," %5s %5s %11s %12s %13s %10s %10s %16s\n" , +"class" ,"size" ,"almost_full" ,"almost_empty" , +"obj_allocated" ,"obj_used" ,"pages_used" , +"pages_per_zspage" );Documentation?
Quoted 8 lines
It should been since [0f050d9, mm/zsmalloc: add statistics support]. Anyway, I will try it.
Where is right place to put only this statistics in Documentation?
Documentation/zsmalloc.txt?Documentation/vm/zsmalloc.txt?Documentation/blockdev/zram.txt?Documentation/ABI/testing/sysfs-block-zram?
hm, this is debugfs so Documentation/ABI/testing/sysfs-block-zram isn't the right place.
akpm3:/usr/src/25> grep -rli zsmalloc Documentation akpm3:/usr/src/25>
lol.
Documentation/vm/zsmalloc.txt looks good.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Minchan Kim
maintainerQuoted 89 lines · Minchan Kim
Recently, we started to use zram heavily and some of issues popped.
1) external fragmentation
I got a report from Juneho Choi that fork failed although there are plenty of free pages in the system. His investigation revealed zram is one of the culprit to make heavy fragmentation so there was no more contiguous 16K page for pgd to fork in the ARM.2) non-movable pages
Other problem of zram now is that inherently, user want to use zram as swap in small memory system so they use zRAM with CMA to use memory efficiently. However, unfortunately, it doesn't work well because zRAM cannot use CMA's movable pages unless it doesn't support compaction. I got several reports about that OOM happened with zram although there are lots of swap space and free space in CMA area.3) internal fragmentation
zRAM has started support memory limitation feature to limit memory usage and I sent a patchset(https://lkml.org/lkml/2014/9/21/148) for VM to be harmonized with zram-swap to stop anonymous page reclaim if zram consumed memory up to the limit although there are free space on the swap. One problem for that direction is zram has no way to know any hole in memory space zsmalloc allocated by internal fragmentation so zram would regard swap is full although there are free space in zsmalloc. For solving the issue, zram want to trigger compaction of zsmalloc before it decides full or not.This patchset is first step to support above issues. For that, it adds indirect layer between handle and object location and supports manual compaction to solve 3th problem first of all.
After this patchset got merged, next step is to make VM aware of zsmalloc compaction so that generic compaction will move zsmalloced-pages automatically in runtime.
In my imaginary
experiment(ie, high compress ratio data with heavy swap in/out on 8G zram-swap), data is as follows,Before =
zram allocated object : 60212066 bytes zram total used: 140103680 bytes ratio: 42.98 percent MemFree: 840192 kBCompaction
After =
frag ratio after compactionzram allocated object : 60212066 bytes zram total used: 76185600 bytes ratio: 79.03 percent MemFree: 901932 kBJuneho reported below in his real platform with small aging. So, I think the benefit would be bigger in real aging system for a long time.
-
frag_ratioincreased 3% (ie, higher is better)
- memfree increased about 6MB
- In buddy info, Normal 2^3: 4, 2^2: 1: 2^1 increased, Highmem: 2^1 21 increasedfrag ratio after swap fragment
used : 156677 kbytes total: 166092 kbytes frag_ratio : 94 meminfo before compaction MemFree: 83724 kB Node 0, zone Normal 13642 1364 57 10 61 17 9 5 4 0 0 Node 0, zone HighMem 425 29 1 0 0 0 0 0 0 0 0
num_migrated: 23630
compaction donefrag ratio after compaction
used : 156673 kbytes total: 160564 kbytes frag_ratio : 97 meminfo after compaction MemFree: 89060 kB Node 0, zone Normal 14076 1544 67 14 61 17 9 5 4 0 0 Node 0, zone HighMem 863 50 1 0 0 0 0 0 0 0 0This patchset adds more logics(about 480 lines) in zsmalloc but when I tested heavy swapin/out program, the regression for swapin/out speed is marginal because most of overheads were caused by compress/decompress and other MM reclaim stuff.
Quoted 4 lines
* from v1
* remove rcu - suggested by Joonsoo
* iterating biggest size class - Seth
* add experiment data in description - Juneho
* fix null refrence bug in zs_destory_pool - Ganesh
Signature · 8 lines
Kind regards, Minchan Kim -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>