[RFC PATCH 2/3] mm/zsmalloc: remove pool->lock from zs_free on 64-bit systems
Barry Song
baohua at kernel.org
Mon May 11 18:15:27 PDT 2026
On Fri, May 8, 2026 at 2:19 PM Wenchao Hao <haowenchao22 at gmail.com> wrote:
>
> With class_idx now encoded in the obj value (ZS_OBJ_CLASS_IDX),
> zs_free() no longer needs pool->lock to locate the size class on
> 64-bit systems.
>
> The class_idx is invariant across page migration (only PFN changes),
> and 64-bit aligned reads are atomic, so a lockless read of the handle
> always yields a valid class_idx. After acquiring class->lock (which
> blocks concurrent migration), the handle is re-read to obtain a stable
> PFN for the actual free operation.
>
> This eliminates rwlock read-side contention between zs_free() and page
> migration/compaction, improving zs_free() scalability on multi-core
> systems.
>
> On 32-bit systems (ZS_OBJ_CLASS_IDX not defined), the original
> pool->lock path is preserved.
>
> Signed-off-by: Wenchao Hao <haowenchao at xiaomi.com>
> ---
> mm/zsmalloc.c | 23 +++++++++++++++++++++--
> 1 file changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
> index bccadf0a27f2..47ec0414ce9e 100644
> --- a/mm/zsmalloc.c
> +++ b/mm/zsmalloc.c
> @@ -21,6 +21,10 @@
> * pool->lock
> * class->lock
> * zspage->lock
> + *
> + * On 64-bit systems with ZS_OBJ_CLASS_IDX enabled, zs_free() does not
> + * take pool->lock; it extracts class_idx from the obj encoding with a
> + * lockless read, then re-reads obj under class->lock.
> */
>
> #include <linux/module.h>
> @@ -1467,10 +1471,24 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
> if (IS_ERR_OR_NULL((void *)handle))
> return;
>
> +#ifdef ZS_OBJ_CLASS_IDX
> + /*
> + * The class_idx encoded in obj is invariant across migration
> + * (only PFN changes), and the read of *(unsigned long *)handle
> + * is atomic on 64-bit, so we can determine the correct class
> + * without holding pool->lock.
> + */
> + obj = handle_to_obj(handle);
> + class = pool->size_class[obj_to_class_idx(obj)];
> + spin_lock(&class->lock);
> /*
> - * The pool->lock protects the race with zpage's migration
> - * so it's safe to get the page from handle.
> + * Re-read under class->lock: migration also acquires class->lock,
> + * so the obj value is now stable and the PFN is valid.
> */
> + obj = handle_to_obj(handle);
> + obj_to_zpdesc(obj, &f_zpdesc);
> + zspage = get_zspage(f_zpdesc);
> +#else
> read_lock(&pool->lock);
> obj = handle_to_obj(handle);
> obj_to_zpdesc(obj, &f_zpdesc);
> @@ -1478,6 +1496,7 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
> class = zspage_class(pool, zspage);
> spin_lock(&class->lock);
> read_unlock(&pool->lock);
> +#endif
This looks a bit inelegant. Can we extract a helper like
obj_handle_class_lock(), or something similar? It could have
different implementations for 32-bit and 64-bit builds.
>
> class_stat_sub(class, ZS_OBJS_INUSE, 1);
> obj_free(class->size, obj);
> --
> 2.34.1
>
More information about the linux-riscv
mailing list