Re: [RFC PATCH 4/7] mm: zswap: remove page reclaim logic from zsmalloc

From: Domenico Cerasuolo
Date: Tue Jun 06 2023 - 05:47:22 EST


On Tue, Jun 6, 2023 at 1:37 AM Nhat Pham <nphamcs@xxxxxxxxx> wrote:
>
> On Mon, Jun 5, 2023 at 1:54 AM Domenico Cerasuolo
> <cerasuolodomenico@xxxxxxxxx> wrote:
> >
> > With the recent enhancement to zswap enabling direct page writeback, the
> > need for the shrink code in zsmalloc has become obsolete. As a result,
> > this commit removes the page reclaim logic from zsmalloc entirely.
> >
> > Signed-off-by: Domenico Cerasuolo <cerasuolodomenico@xxxxxxxxx>
> > ---
> > mm/zsmalloc.c | 291 +-------------------------------------------------
> > 1 file changed, 2 insertions(+), 289 deletions(-)
> >
> > diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
> > index 02f7f414aade..c87a60514f21 100644
> > --- a/mm/zsmalloc.c
> > +++ b/mm/zsmalloc.c
> > @@ -250,13 +250,6 @@ struct zs_pool {
> > /* Compact classes */
> > struct shrinker shrinker;
> >
> > -#ifdef CONFIG_ZPOOL
> > - /* List tracking the zspages in LRU order by most recently added object */
> > - struct list_head lru;
> > - struct zpool *zpool;
> > - const struct zpool_ops *zpool_ops;
> > -#endif
> > -
> > #ifdef CONFIG_ZSMALLOC_STAT
> > struct dentry *stat_dentry;
> > #endif
> > @@ -279,13 +272,6 @@ struct zspage {
> > unsigned int freeobj;
> > struct page *first_page;
> > struct list_head list; /* fullness list */
> > -
> > -#ifdef CONFIG_ZPOOL
> > - /* links the zspage to the lru list in the pool */
> > - struct list_head lru;
> > - bool under_reclaim;
> > -#endif
> > -
> > struct zs_pool *pool;
> > rwlock_t lock;
> > };
> > @@ -393,14 +379,7 @@ static void *zs_zpool_create(const char *name, gfp_t gfp,
> > * different contexts and its caller must provide a valid
> > * gfp mask.
> > */
> > - struct zs_pool *pool = zs_create_pool(name);
> > -
> > - if (pool) {
> > - pool->zpool = zpool;
> > - pool->zpool_ops = zpool_ops;
> > - }
> > -
> > - return pool;
> > + return zs_create_pool(name);
> > }
> >
> > static void zs_zpool_destroy(void *pool)
> > @@ -422,27 +401,6 @@ static void zs_zpool_free(void *pool, unsigned long handle)
> > zs_free(pool, handle);
> > }
> >
> > -static int zs_reclaim_page(struct zs_pool *pool, unsigned int retries);
> > -
> > -static int zs_zpool_shrink(void *pool, unsigned int pages,
> > - unsigned int *reclaimed)
> > -{
> > - unsigned int total = 0;
> > - int ret = -EINVAL;
> > -
> > - while (total < pages) {
> > - ret = zs_reclaim_page(pool, 8);
> > - if (ret < 0)
> > - break;
> > - total++;
> > - }
> > -
> > - if (reclaimed)
> > - *reclaimed = total;
> > -
> > - return ret;
> > -}
> > -
> > static void *zs_zpool_map(void *pool, unsigned long handle,
> > enum zpool_mapmode mm)
> > {
> > @@ -481,7 +439,7 @@ static struct zpool_driver zs_zpool_driver = {
> > .malloc_support_movable = true,
> > .malloc = zs_zpool_malloc,
> > .free = zs_zpool_free,
> > - .shrink = zs_zpool_shrink,
> > + .shrink = NULL,
> > .map = zs_zpool_map,
> > .unmap = zs_zpool_unmap,
> > .total_size = zs_zpool_total_size,
> > @@ -884,14 +842,6 @@ static inline bool obj_allocated(struct page *page, void *obj, unsigned long *ph
> > return obj_tagged(page, obj, phandle, OBJ_ALLOCATED_TAG);
> > }
> >
> > -#ifdef CONFIG_ZPOOL
> > -static bool obj_stores_deferred_handle(struct page *page, void *obj,
> > - unsigned long *phandle)
> > -{
> > - return obj_tagged(page, obj, phandle, OBJ_DEFERRED_HANDLE_TAG);
> > -}
> > -#endif
> > -
> > static void reset_page(struct page *page)
> > {
> > __ClearPageMovable(page);
> > @@ -1006,9 +956,6 @@ static void free_zspage(struct zs_pool *pool, struct size_class *class,
> > }
> >
> > remove_zspage(class, zspage, ZS_INUSE_RATIO_0);
> > -#ifdef CONFIG_ZPOOL
> > - list_del(&zspage->lru);
> > -#endif
> > __free_zspage(pool, class, zspage);
> > }
> >
> > @@ -1054,11 +1001,6 @@ static void init_zspage(struct size_class *class, struct zspage *zspage)
> > off %= PAGE_SIZE;
> > }
> >
> > -#ifdef CONFIG_ZPOOL
> > - INIT_LIST_HEAD(&zspage->lru);
> > - zspage->under_reclaim = false;
> > -#endif
> > -
> > set_freeobj(zspage, 0);
> > }
> >
> > @@ -1525,13 +1467,6 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp)
> > /* We completely set up zspage so mark them as movable */
> > SetZsPageMovable(pool, zspage);
> > out:
> > -#ifdef CONFIG_ZPOOL
> > - /* Add/move zspage to beginning of LRU */
> > - if (!list_empty(&zspage->lru))
> > - list_del(&zspage->lru);
> > - list_add(&zspage->lru, &pool->lru);
> > -#endif
> > -
> > spin_unlock(&pool->lock);
> >
> > return handle;
> > @@ -1600,20 +1535,6 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
> > class = zspage_class(pool, zspage);
> >
> > class_stat_dec(class, ZS_OBJS_INUSE, 1);
> > -
> > -#ifdef CONFIG_ZPOOL
> > - if (zspage->under_reclaim) {
> > - /*
> > - * Reclaim needs the handles during writeback. It'll free
> > - * them along with the zspage when it's done with them.
> > - *
> > - * Record current deferred handle in the object's header.
> > - */
> > - obj_free(class->size, obj, &handle);
> > - spin_unlock(&pool->lock);
> > - return;
> > - }
> > -#endif
> > obj_free(class->size, obj, NULL);
> >
> > fullness = fix_fullness_group(class, zspage);
> > @@ -1890,23 +1811,6 @@ static void lock_zspage(struct zspage *zspage)
> > }
> > #endif /* defined(CONFIG_ZPOOL) || defined(CONFIG_COMPACTION) */
>
> If I recall correctly, the defined(CONFIG_ZPOOL) condition is
> only needed for the lock_zspage() call in zs_reclaim_page().
>
> You might be able to get away with just
> #ifdef CONFIG_COMPACTION if you're removing writeback.
>
> Do fact-check me of course - try to build with these CONFIGs turned
> off and see. I'm surprised kernel test robot has not complained about
> unused static function lock_zspage() in the case
> CONFIG_ZPOOL && !CONFIG_COMPACTION
>

Thanks! It is indeed the case, with CONFIG_ZPOOL && !CONFIG_COMPACTION
lock_zspage becomes unused -> will go with #ifdef CONFIG_COMPACTION alone.

> >
> > -#ifdef CONFIG_ZPOOL
> > -/*
> > - * Unlocks all the pages of the zspage.
> > - *
> > - * pool->lock must be held before this function is called
> > - * to prevent the underlying pages from migrating.
> > - */
> > -static void unlock_zspage(struct zspage *zspage)
> > -{
> > - struct page *page = get_first_page(zspage);
> > -
> > - do {
> > - unlock_page(page);
> > - } while ((page = get_next_page(page)) != NULL);
> > -}
> > -#endif /* CONFIG_ZPOOL */
> > -
> > static void migrate_lock_init(struct zspage *zspage)
> > {
> > rwlock_init(&zspage->lock);
> > @@ -2126,9 +2030,6 @@ static void async_free_zspage(struct work_struct *work)
> > VM_BUG_ON(fullness != ZS_INUSE_RATIO_0);
> > class = pool->size_class[class_idx];
> > spin_lock(&pool->lock);
> > -#ifdef CONFIG_ZPOOL
> > - list_del(&zspage->lru);
> > -#endif
> > __free_zspage(pool, class, zspage);
> > spin_unlock(&pool->lock);
> > }
> > @@ -2474,10 +2375,6 @@ struct zs_pool *zs_create_pool(const char *name)
> > */
> > zs_register_shrinker(pool);
> >
> > -#ifdef CONFIG_ZPOOL
> > - INIT_LIST_HEAD(&pool->lru);
> > -#endif
> > -
> > return pool;
> >
> > err:
> > @@ -2520,190 +2417,6 @@ void zs_destroy_pool(struct zs_pool *pool)
> > }
> > EXPORT_SYMBOL_GPL(zs_destroy_pool);
> >
> > -#ifdef CONFIG_ZPOOL
> > -static void restore_freelist(struct zs_pool *pool, struct size_class *class,
> > - struct zspage *zspage)
> > -{
> > - unsigned int obj_idx = 0;
> > - unsigned long handle, off = 0; /* off is within-page offset */
> > - struct page *page = get_first_page(zspage);
> > - struct link_free *prev_free = NULL;
> > - void *prev_page_vaddr = NULL;
> > -
> > - /* in case no free object found */
> > - set_freeobj(zspage, (unsigned int)(-1UL));
> > -
> > - while (page) {
> > - void *vaddr = kmap_atomic(page);
> > - struct page *next_page;
> > -
> > - while (off < PAGE_SIZE) {
> > - void *obj_addr = vaddr + off;
> > -
> > - /* skip allocated object */
> > - if (obj_allocated(page, obj_addr, &handle)) {
> > - obj_idx++;
> > - off += class->size;
> > - continue;
> > - }
> > -
> > - /* free deferred handle from reclaim attempt */
> > - if (obj_stores_deferred_handle(page, obj_addr, &handle))
> > - cache_free_handle(pool, handle);
> > -
> > - if (prev_free)
> > - prev_free->next = obj_idx << OBJ_TAG_BITS;
> > - else /* first free object found */
> > - set_freeobj(zspage, obj_idx);
> > -
> > - prev_free = (struct link_free *)vaddr + off / sizeof(*prev_free);
> > - /* if last free object in a previous page, need to unmap */
> > - if (prev_page_vaddr) {
> > - kunmap_atomic(prev_page_vaddr);
> > - prev_page_vaddr = NULL;
> > - }
> > -
> > - obj_idx++;
> > - off += class->size;
> > - }
> > -
> > - /*
> > - * Handle the last (full or partial) object on this page.
> > - */
> > - next_page = get_next_page(page);
> > - if (next_page) {
> > - if (!prev_free || prev_page_vaddr) {
> > - /*
> > - * There is no free object in this page, so we can safely
> > - * unmap it.
> > - */
> > - kunmap_atomic(vaddr);
> > - } else {
> > - /* update prev_page_vaddr since prev_free is on this page */
> > - prev_page_vaddr = vaddr;
> > - }
> > - } else { /* this is the last page */
> > - if (prev_free) {
> > - /*
> > - * Reset OBJ_TAG_BITS bit to last link to tell
> > - * whether it's allocated object or not.
> > - */
> > - prev_free->next = -1UL << OBJ_TAG_BITS;
> > - }
> > -
> > - /* unmap previous page (if not done yet) */
> > - if (prev_page_vaddr) {
> > - kunmap_atomic(prev_page_vaddr);
> > - prev_page_vaddr = NULL;
> > - }
> > -
> > - kunmap_atomic(vaddr);
> > - }
> > -
> > - page = next_page;
> > - off %= PAGE_SIZE;
> > - }
> > -}
> > -
> > -static int zs_reclaim_page(struct zs_pool *pool, unsigned int retries)
> > -{
> > - int i, obj_idx, ret = 0;
> > - unsigned long handle;
> > - struct zspage *zspage;
> > - struct page *page;
> > - int fullness;
> > -
> > - /* Lock LRU and fullness list */
> > - spin_lock(&pool->lock);
> > - if (list_empty(&pool->lru)) {
> > - spin_unlock(&pool->lock);
> > - return -EINVAL;
> > - }
> > -
> > - for (i = 0; i < retries; i++) {
> > - struct size_class *class;
> > -
> > - zspage = list_last_entry(&pool->lru, struct zspage, lru);
> > - list_del(&zspage->lru);
> > -
> > - /* zs_free may free objects, but not the zspage and handles */
> > - zspage->under_reclaim = true;
> > -
> > - class = zspage_class(pool, zspage);
> > - fullness = get_fullness_group(class, zspage);
> > -
> > - /* Lock out object allocations and object compaction */
> > - remove_zspage(class, zspage, fullness);
> > -
> > - spin_unlock(&pool->lock);
> > - cond_resched();
> > -
> > - /* Lock backing pages into place */
> > - lock_zspage(zspage);
> > -
> > - obj_idx = 0;
> > - page = get_first_page(zspage);
> > - while (1) {
> > - handle = find_alloced_obj(class, page, &obj_idx);
> > - if (!handle) {
> > - page = get_next_page(page);
> > - if (!page)
> > - break;
> > - obj_idx = 0;
> > - continue;
> > - }
> > -
> > - /*
> > - * This will write the object and call zs_free.
> > - *
> > - * zs_free will free the object, but the
> > - * under_reclaim flag prevents it from freeing
> > - * the zspage altogether. This is necessary so
> > - * that we can continue working with the
> > - * zspage potentially after the last object
> > - * has been freed.
> > - */
> > - ret = pool->zpool_ops->evict(pool->zpool, handle);
> > - if (ret)
> > - goto next;
> > -
> > - obj_idx++;
> > - }
> > -
> > -next:
> > - /* For freeing the zspage, or putting it back in the pool and LRU list. */
> > - spin_lock(&pool->lock);
> > - zspage->under_reclaim = false;
> > -
> > - if (!get_zspage_inuse(zspage)) {
> > - /*
> > - * Fullness went stale as zs_free() won't touch it
> > - * while the page is removed from the pool. Fix it
> > - * up for the check in __free_zspage().
> > - */
> > - zspage->fullness = ZS_INUSE_RATIO_0;
> > -
> > - __free_zspage(pool, class, zspage);
> > - spin_unlock(&pool->lock);
> > - return 0;
> > - }
> > -
> > - /*
> > - * Eviction fails on one of the handles, so we need to restore zspage.
> > - * We need to rebuild its freelist (and free stored deferred handles),
> > - * put it back to the correct size class, and add it to the LRU list.
> > - */
> > - restore_freelist(pool, class, zspage);
> > - putback_zspage(class, zspage);
> > - list_add(&zspage->lru, &pool->lru);
> > - unlock_zspage(zspage);
> > - }
> > -
> > - spin_unlock(&pool->lock);
> > - return -EAGAIN;
> > -}
> > -#endif /* CONFIG_ZPOOL */
> > -
> > static int __init zs_init(void)
> > {
> > int ret;
> > --
> > 2.34.1
> >