diff options
| author | Wenchao Hao <haowenchao@xiaomi.com> | 2026-06-26 09:50:00 +0800 |
|---|---|---|
| committer | Andrew Morton <akpm@linux-foundation.org> | 2026-07-28 21:12:02 -0700 |
| commit | 9909b088b1f0b913aa318605544a38bbd25e9abf (patch) | |
| tree | bf437305daaf18a8d07215ee66f498699b8e0a7f /tools/lib/python | |
| parent | f078b0a0727c723cba4e55b331571f9804d459ca (diff) | |
mm/zsmalloc: encode class index in obj value for lockless class lookup
Patch series "mm/zsmalloc: reduce lock contention in zs_free()", v6.
This series reduces lock contention in zs_free(), which dominates the
unmap path under memory pressure on Android (LMK kills) and on x86 servers
running zswap-heavy workloads.
The current zs_free() takes pool->lock (rwlock, read side) just to look up
the size_class for a handle, then takes class->lock and holds it across
__free_zspage() which can call into the buddy allocator and acquire
zone->lock. Two costs follow:
* pool->lock reader-counter cacheline bouncing among concurrent
zs_free() callers.
* class->lock held across folio_put(), so any zone->lock wait
fans out to every other zs_free() on the same class.
The series tackles both:
Patch 1: encode size_class index into obj alongside PFN and obj_idx,
so zs_free() can locate the class without pool->lock.
Patch 2: drop pool->lock from zs_free() on 64-bit; 32-bit unchanged.
Patch 3: move zspage page-freeing out of class->lock.
Patch 4: document the three free_zspage helper variants that result
from the split in patch 3.
Performance results:
Test: each process independently mmap 256MB, write data, madvise
MADV_PAGEOUT to swap out via zram (lzo-rle), then concurrent munmap.
Raspberry Pi 4B (4-core ARM64 Cortex-A72):
mode Base Patched Speedup
single 59.0ms 56.0ms 1.05x
multi 2p 94.6ms 66.7ms 1.42x
multi 4p 202.9ms 110.6ms 1.83x
x86 (20-core Intel i7-12700, 16 concurrent processes):
mode Base Patched Speedup
single 11.7ms 9.8ms 1.19x
multi 2p 24.1ms 17.2ms 1.40x
multi 4p 63.0ms 45.3ms 1.39x
This patch (of 4):
Encode the size_class index (class_idx) into the obj value so that
zs_free() can determine the correct size_class without dereferencing the
handle->obj->PFN->zpdesc->zspage->class chain under pool->lock. class_idx
is invariant across page migration (only PFN is rewritten), so a lockless
read of obj always yields a valid class_idx.
Where obj has more bits below the PFN field than obj_idx alone needs,
split that space into class_idx and obj_idx subfields:
|<-- _PFN_BITS -->|<-- ZS_OBJ_CLASS_BITS -->|<-- ZS_OBJ_IDX_BITS -->|
+-----------------+-------------------------+-----------------------+
| PFN | class_idx | obj_idx |
+-----------------+-------------------------+-----------------------+
MSB ^ LSB
|
+-- ZS_OBJ_PFN_SHIFT
The macro layout changes as follows:
Before After Meaning
---------------- ------------------ ----------------------------
OBJ_INDEX_BITS ZS_OBJ_IDX_BITS width of obj_idx subfield
OBJ_INDEX_MASK ZS_OBJ_IDX_MASK mask of obj_idx subfield
(n/a) ZS_OBJ_CLASS_BITS width of class_idx subfield
(n/a) ZS_OBJ_CLASS_MASK mask of class_idx subfield
(n/a) ZS_OBJ_PFN_SHIFT bit offset of PFN in obj
ZS_OBJ_CLASS_BITS folds to 0 (and the layout collapses to [PFN | obj_idx])
when obj has no spare bits, i.e. on 32-bit or on 64-bit fallback paths
where MAX_POSSIBLE_PHYSMEM_BITS == BITS_PER_LONG (e.g. UML); zs_free()
then falls back to pool->lock.
[akpm@linux-foundation.org: fix obj_to_class_idx() defined but not used, remove duplicated #ifdef]
Link: https://lore.kernel.org/20260626015003.2965881-1-haowenchao22@gmail.com
Link: https://lore.kernel.org/20260626015003.2965881-2-haowenchao22@gmail.com
Signed-off-by: Wenchao Hao <haowenchao@xiaomi.com>
Reviewed-by: Nhat Pham <nphamcs@gmail.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Xueyuan Chen <xueyuan.chen21@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'tools/lib/python')
0 files changed, 0 insertions, 0 deletions
