mm/swap: ghost swapfile with backend switching via Redirect entries
Subsystems (3)
Baoquan He
reviewercover mm/swap: ghost swapfile with backend switching via Redirect entries
This series introduces a virtual "ghost" swapfile that can be backed by multiple physical swap devices, switching between them at runtime using a new "Redirect" swap table entry type.
Motivation
----------
Physical swap devices have fixed, large sizes but a machine may only need a fraction at any given time. Ghost swap decouples the logical swap capacity from physical swap allocation: it presents a large virtual swap device to userspace while lazily mapping slots to physical devices only when data must leave zswap (writeback). This enables overcommit of swap capacity without wasting physical disk space, and allows the ghost device to grow dynamically at runtime.
Ghost swapfile characteristics
--------------------------------
Ghost swap decouples logical swap capacity from physical swap allocation. Key properties:
- No backing store required. A ghost swap device can operate purely as a zswap container — compressed pages stay in the zswap pool without ever touching a physical swap device. No physical swap device needs to be attached.
- Optional physical backing. One or more physical swap devices can be swapon'd alongside the ghost device, acting as writeback targets when the zswap pool needs to evict cold pages. The Redirect entry in the ghost swap table links each evicted slot to its physical destination.
- Dynamic size growth. The ghost device can be extended at runtime via sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for cluster_info[] structures are allocated only on demand as the device grows. The cluster_info pointer never changes, so all existing accessors (__swap_offset_to_cluster, cluster_index) work unchanged.
- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — backing device name
max (RW) — current max pages; writing a larger value triggers
swap_ghost_extend_max()
Design overview
---------------
A swap table slot can now hold a Redirect entry:
Redirect: |----- physical swp_entry_t -----|101|
This stores a plain swp_entry_t pointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.
The lifecycle is:
1. swapon a ghost device — reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
2. zswap writeback from ghost: when the zswap pool needs to evict, zswap_writeback_ghost() allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.
3. Swap-in from ghost: swap_read_folio() detects the ghost device (SWP_GHOST), resolves the Redirect to the physical swp_entry_t, and forwards the read I/O to the physical device.
4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.
5. When the physical slot is freed, the ghost slot is also freed, cascading through swap_range_free().
Note:
------
Ghost swapfile is a proof of concept. Compared with the other patchset:
[RFC PATCH v2 0/7] mm, swap: Virtual Swap Space (Swap Table Edition), it explores a different approach to implementing virtual swap on top of the existing swap table infrastructure, with a simpler implementation. It builds on Chris Li's earlier ghost swapfile work and is currently tentatively named "ghost swapfile" — alternative naming suggestions are welcome. Ghost swapfile setup can also be integrated through modifications to the swapon API and the swapon command in util-linux, for example:
swapon --ghost ghost_1:2G -p 5.
Baoquan He (9):
mm/swap_table: add Redirect entry encoding for ghost swap backend
switching
mm/swap: add redirect_xa field and ghost redirect helper declarations mm/swapfile: implement ghost redirect helpers and free-path cascade mm/swap_state: restore Redirect entry when swap cache folio is removed mm/zswap: implement ghost-to-physical writeback for backend switching mm/page_io: forward ghost swap reads to physical device via Redirect mm/swapfile: manage ghost cluster_info via lazy vmalloc mm/swapfile: implement swap_ghost_extend_max() for dynamic growth mm/swapfile: add sysfs interface for ghost swap extension
Chris Li (1):
mm: ghost swapfile support for zswap
include/linux/swap.h | 10 + mm/page_io.c | 51 +++- mm/swap.h | 16 +- mm/swap_state.c | 28 +++ mm/swap_table.h | 38 +++ mm/swapfile.c | 579 ++++++++++++++++++++++++++++++++++++++++--- mm/zswap.c | 98 +++++++- 7 files changed, 772 insertions(+), 48 deletions(-)
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 1/10 mm: ghost swapfile support for zswap
From: Chris Li <[email protected]>
The current zswap requires a backing swapfile. The swap slot used by zswap is not able to be used by the swapfile. That waste swapfile space.
The ghost swapfile is a swapfile that only contains the swapfile header for zswap. The swapfile header indicate the size of the swapfile. There is no swap data section in the ghost swapfile, therefore, no waste of swapfile space. As such, any write to a ghost swapfile will fail. To prevents accidental read or write of ghost swapfile, bdev of swap_info_struct is set to NULL. Ghost swapfile will also set the SSD flag because there is no rotation disk access when using zswap.
The zswap write back has been disabled if all swapfiles in the system are ghost swap files.
Quote Chris's setup steps here:
---------------------------
touch swap.1G
truncate -s 1G swap.1G
mkswap swap.1G
dd if=swap.1G of=ghost.1G bs=4096 count=1
Then you will have one 4K size ghost.1G.
you should be able to do:
swapon ghost.1G
---------------------------
--- include/linux/swap.h | 2 ++ mm/page_io.c | 18 +++++++++++++++--- mm/swap.h | 2 +- mm/swap_state.c | 7 +++++++ mm/swapfile.c | 41 +++++++++++++++++++++++++++++++++++++---- mm/zswap.c | 12 +++++++++--- 6 files changed, 71 insertions(+), 11 deletions(-)
include/linux/swap.h +2 -0
@@ -208,6 +208,7 @@ enum {SWP_STABLE_WRITES = (1 <<11 ),/* no overwrite PG_writeback pages */ SWP_SYNCHRONOUS_IO = (1 <<12 ),/* synchronous IO is efficient */ SWP_HIBERNATION = (1 <<13 ),/* pinned for hibernation */ + SWP_GHOST = (1 << 14), /* not backed by anything */ /* add others here before... */ };
@@ -412,6 +413,7 @@ void free_folio_and_swap_cache(struct folio *folio);void free_pages_and_swap_cache (struct encoded_page **,int );/* linux/mm/swapfile.c */ extern atomic_long_t nr_swap_pages; +extern atomic_t nr_real_swapfiles;extern long total_swap_pages;extern atomic_t nr_rotate_swap;
mm/page_io.c +15 -3
@@ -295,8 +295,7 @@ int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug) }rcu_read_unlock (); -__swap_writepage (folio, swap_plug); -return 0 ; +return __swap_writepage (folio, swap_plug); out_unlock:folio_unlock (folio);return ret;
@@ -463,11 +462,18 @@ static void swap_writepage_bdev_async(struct folio *folio,submit_bio (bio); } -void __swap_writepage (struct folio *folio,struct swap_iocb **swap_plug) +int __swap_writepage (struct folio *folio,struct swap_iocb **swap_plug) {struct swap_info_struct *sis =__swap_entry_to_info (folio->swap);VM_BUG_ON_FOLIO (!folio_test_swapcache (folio), folio); + +if (sis->flags &SWP_GHOST ) { +/* Prevent the page from getting reclaimed. */ +folio_set_dirty (folio); +return AOP_WRITEPAGE_ACTIVATE ; + } +/* * ->flags can be updated non-atomically, * but that will never affect SWP_FS_OPS, so the data_race
@@ -484,6 +490,7 @@ void __swap_writepage(struct folio *folio, struct swap_iocb **swap_plug)swap_writepage_bdev_sync (folio, sis); elseswap_writepage_bdev_async (folio ,sis ); +return 0 ; }void swap_write_unplug (struct swap_iocb *sio)
@@ -689,6 +696,11 @@ void swap_read_folio(struct folio *folio, struct swap_iocb **plug, void *zentry)if (zswap_load (folio, zentry) != -ENOENT ) goto finish; +if (unlikely (sis->flags &SWP_GHOST )) { +folio_unlock (folio); + goto finish; + } +/* We have to read from slower devices. Increase zswap protection. */ zswap_folio_swapin (folio);
mm/swap.h +1 -1
@@ -285,7 +285,7 @@ static inline void swap_read_unplug(struct swap_iocb *plug) }void swap_write_unplug (struct swap_iocb *sio);int swap_writeout (struct folio *folio,struct swap_iocb **swap_plug); -void __swap_writepage (struct folio *folio,struct swap_iocb **swap_plug); +int __swap_writepage (struct folio *folio,struct swap_iocb **swap_plug);/* linux/mm/swap_state.c */ extern struct address_space swap_space__read_mostly ;
mm/swap_state.c +7 -0
@@ -883,6 +883,13 @@ struct folio *swap_cluster_readahead(swp_entry_t entry, gfp_t gfp_mask,struct swap_iocb *splug =NULL ;swp_entry_t ra_entry; +/* +* The entry may have been freed by another task. Avoid swap_info_get() +* which will print error message if the race happens. +*/ +if (si->flags &SWP_GHOST ) + goto skip; + mask =swapin_nr_pages (offset) -1 ;if (!mask) goto skip;
mm/swapfile.c +37 -4
95 lines not on this page · raw message
mm/zswap.c +9 -3
@@ -992,7 +992,12 @@ static int zswap_writeback_entry(struct zswap_entry *entry,/* try to allocate swap cache folio */ si =get_swap_device (swpentry);if (!si) -return -EEXIST ; +return -ENOENT ; + +if (si->flags &SWP_GHOST ) { +put_swap_device (si); +return -EINVAL ; + }/* * Verify the swap table Pointer entry still references this
@@ -1048,7 +1053,8 @@ static int zswap_writeback_entry(struct zswap_entry *entry,folio_set_reclaim (folio);/* start writeback */ -__swap_writepage (folio,NULL ); + ret =__swap_writepage (folio,NULL ); +WARN_ON_ONCE (ret); out:if (ret) {
@@ -1538,7 +1544,7 @@ bool zswap_store(struct folio *folio)zswap_pool_put (pool); put_objcg:obj_cgroup_put (objcg); -if (!ret && zswap_pool_reached_full) +if (!ret && zswap_pool_reached_full &&atomic_read (&nr_real_swapfiles))queue_work (shrink_wq, &zswap_shrink_work); check_old:/*
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 2/10 mm/swap_table: add Redirect entry encoding for ghost swap backend switching
In reply to Baoquan HeIntroduce SWP_TB_REDIRECT_MARK (0b101) as a new swap table entry type that stores a physical swp_entry_t directly. This allows a ghost swap slot to redirect to a physical swap slot after zswap writeback.
The swp_entry_t has its low 12 bits clear (page-aligned offset), so the 3-bit mark is ORed directly into the value without shifting and without losing any type or offset bits.
Add inline helpers:
- swp_tb_is_redirect(): type check
- swp_entry_to_redirect(): encode a swp_entry_t as a Redirect entry
- redirect_to_swp_entry(): decode a Redirect entry back to swp_entry_t
Update the swap table entry type documentation to include the new Redirect type.
--- mm/swap_table.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
mm/swap_table.h +38 -0
@@ -29,6 +29,7 @@ struct swap_memcg_table {* Shadow: |SWAP_COUNT|Z|---- SHADOW_VAL ---|1| - Swapped out slot * PFN: |SWAP_COUNT|Z|------ PFN -------|10| - Cached slot * Pointer: |----------- Pointer ----------|100| - Zswap compressed entry +* Redirect: |------- swp_entry_t.val ------|101| - Redirect to phys swap * Bad: |------------- 1 -------------|1000| - Bad slot * * COUNT is `SWP_TB_COUNT_BITS` long, Z is the `SWP_TB_ZERO_FLAG` bit,
@@ -55,6 +56,11 @@ struct swap_memcg_table {* all upper bits. `0b100` is used as the marker because zswap_entry * pointers are at least 8-byte aligned, leaving the lower three bits free. * +* - Redirect: Stores a physical swp_entry_t directly, ending with `0b101`. +* Used by ghost swapfiles to redirect a virtual slot to a physical swap +* slot after zswap writeback. swp_entry_t has bits 0-11 clear (page-aligned +* offset), so the 3-bit mark is ORed directly into the value without shifting. +* * - Bad: Swap slot is reserved, protects swap header or holes on swap devices. */
@@ -83,6 +89,15 @@ struct swap_memcg_table {/* Pointer: zswap_entry pointer stored directly, ends with 0b100 */ #define SWP_TB_POINTER_MARK 0b100UL +/* +* Redirect: stores a physical swp_entry_t directly, ends with 0b101. +* Used by ghost swapfiles to redirect a virtual slot to a physical +* swap slot after writeback. swp_entry_t has its low 12 bits clear +* (page-aligned offset), so we can OR the 3-bit mark directly into +* the entry value without shifting and without losing type bits. +*/ +#define SWP_TB_REDIRECT_MARK 0b101UL +/* Bad slot: ends with 0b1000 and rests of bits are all 1 */ #define SWP_TB_BAD ((~0UL) << 3)
@@ -176,6 +191,11 @@ static inline bool swp_tb_is_pointer(unsigned long swp_tb)return (swp_tb & (BIT (3 ) -1 )) ==SWP_TB_POINTER_MARK ; } +static inline bool swp_tb_is_redirect (unsigned long swp_tb) +{ +return (swp_tb & (BIT (3 ) -1 )) ==SWP_TB_REDIRECT_MARK ; +} +static inline bool swp_tb_is_countable (unsigned long swp_tb) { return (swp_tb_is_shadow(swp_tb) || swp_tb_is_folio(swp_tb) ||
@@ -215,6 +235,24 @@ static inline void *swp_tb_to_pointer(unsigned long swp_tb)return (void *)(swp_tb & ~((unsigned long )(BIT (3 ) -1 ))); } +/* +* Redirect encoding: swp_entry_t is stored directly, with the 3-bit +* mark ORed into the low bits. swp_entry_t.val has bits 0-11 clear +* (page-aligned offset), so no shifting is needed and no type bits +* are lost. +*/ +static inline unsigned long swp_entry_to_redirect (swp_entry_t entry) +{ +VM_WARN_ON (entry.val & (BIT (3 ) -1 )); +return entry.val |SWP_TB_REDIRECT_MARK ; +} + +static inline swp_entry_t redirect_to_swp_entry (unsigned long swp_tb) +{ +VM_WARN_ON (!swp_tb_is_redirect (swp_tb)); +return (swp_entry_t ){ .val = swp_tb & ~((unsigned long )(BIT (3 ) -1 )) }; +} +static inline unsigned char __swp_tb_get_count (unsigned long swp_tb) {VM_WARN_ON (!swp_tb_is_countable (swp_tb));
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 3/10 mm/swap: add redirect_xa field and ghost redirect helper declarations
In reply to Baoquan HeAdd a 'struct xarray redirect_xa' to swap_info_struct (guarded by CONFIG_ZSWAP) to persist ghost_offset -> phys_swp_entry_t mappings across swap-cache PFN cycles.
Declare the ghost redirect helper functions in mm/swap.h. All are no-ops when CONFIG_ZSWAP=n.
--- include/linux/swap.h | 3 +++ mm/swap.h | 14 ++++++++++++++ 2 files changed, 17 insertions(+)
include/linux/swap.h +3 -0
@@ -277,6 +277,9 @@ struct swap_info_struct {struct work_struct reclaim_work;/* reclaim worker */ struct list_head discard_clusters;/* discard clusters list */ struct plist_node avail_list;/* entry in swap_avail_head */ +#ifdef CONFIG_ZSWAP +struct xarray redirect_xa;/* ghost offset → phys swp_entry_t */ +#endif };static inline swp_entry_t page_swap_entry (struct page *page)
mm/swap.h +14 -0
@@ -287,6 +287,20 @@ void swap_write_unplug(struct swap_iocb *sio);int swap_writeout (struct folio *folio,struct swap_iocb **swap_plug);int __swap_writepage (struct folio *folio,struct swap_iocb **swap_plug); +/* Ghost swapfile redirect helpers (mm/swapfile.c) */ +#ifdef CONFIG_ZSWAP +swp_entry_t ghost_redirect_to_phys (struct swap_info_struct *si, +pgoff_t offset); +swp_entry_t alloc_phys_swap_slot (void ); +void free_phys_swap_slot (swp_entry_t phys_entry); +#else +static inline swp_entry_t ghost_redirect_to_phys (struct swap_info_struct *si, +pgoff_t offset) +{return (swp_entry_t ){}; } +static inline swp_entry_t alloc_phys_swap_slot (void ) {return (swp_entry_t ){}; } +static inline void free_phys_swap_slot (swp_entry_t phys_entry) {} +#endif +/* linux/mm/swap_state.c */ extern struct address_space swap_space__read_mostly ;static inline struct address_space *swap_address_space (swp_entry_t entry)
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 4/10 mm/swapfile: implement ghost redirect helpers and free-path cascade
In reply to Baoquan HeAdd the core infrastructure for ghost swapfile backend switching:
- alloc_phys_swap_slot()
- free_phys_swap_slot()
- ghost_redirect_to_phys()
Initialize redirect_xa in read_swap_header() when a ghost swapfile is detected (file size == PAGE_SIZE).
Handle Redirect entries in __swap_cluster_free_entries(): when a ghost entry with a Redirect is freed (swap count reaches zero), erase the xarray mapping and cascade the free to the physical swap slot. Also skip zero-flag clearing for Redirect entries (they carry no zero flag).
--- mm/swapfile.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-)
mm/swapfile.c +105 -2
@@ -15,6 +15,7 @@#include <linux/slab.h> #include <linux/kernel_stat.h> #include <linux/swap.h> +#include <linux/xarray.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/namei.h>
@@ -1975,7 +1976,14 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,* means zswap has a compressed copy; the xarray still * holds the entry and zswap_invalidate will free it. */ -if (swp_tb_is_pointer (old_tb)) { +if (swp_tb_is_redirect (old_tb)) { +swp_entry_t phys_entry; + + phys_entry =redirect_to_swp_entry (old_tb); +xa_erase (&si->redirect_xa, + ci_off +cluster_offset (si, ci)); +free_phys_swap_slot (phys_entry); + }else if (swp_tb_is_pointer (old_tb)) {VM_WARN_ON (zswap_swp_tb_get_count (old_tb) >1 );/* * Free the zswap entry now under ci->lock while
@@ -1990,7 +1998,8 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,/* Resetting the slot to NULL also clears the inline flags. */ __swap_table_set (ci, ci_off,null_to_swp_tb ()); -if (!SWAP_TABLE_HAS_ZEROFLAG && !swp_tb_is_pointer (old_tb)) +if (!SWAP_TABLE_HAS_ZEROFLAG && !swp_tb_is_pointer (old_tb) && + !swp_tb_is_redirect (old_tb))__swap_table_clear_zero (ci, ci_off);/*
@@ -3593,6 +3602,7 @@ static unsigned long read_swap_header(struct swap_info_struct *si,/* Ghost swapfile */ si->bdev =NULL ; si->flags |=SWP_GHOST |SWP_SOLIDSTATE ; +xa_init (&si->redirect_xa);return maxpages; }
@@ -4006,6 +4016,99 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) }#endif +#ifdef CONFIG_ZSWAP +/* +* Ghost swapfile redirect: physical swap slot allocation for writeback. +* +* Allocates a single order-0 swap slot from any real (non-ghost, writable) +* swap device. Used by zswap writeback to obtain a physical destination +* for ghost-backed entries. +*/ +swp_entry_t alloc_phys_swap_slot (void ) +{ +struct swap_info_struct *si; +unsigned long offset; +swp_entry_t entry = {}; + +spin_lock (&swap_avail_lock); +plist_for_each_entry (si, &swap_avail_head, avail_list) { +/* Skip ghost or non-writable devices */ +if (si->flags &SWP_GHOST ) +continue ; +if (!(si->flags &SWP_WRITEOK )) +continue ; +if (!get_swap_device_info (si)) +continue ; + +/* Try to allocate one slot */ + offset =cluster_alloc_swap_entry (si,NULL ); +put_swap_device (si); +if (offset && offset !=SWAP_ENTRY_INVALID ) { + entry =swp_entry (si->type, offset); +break ; + } + } +spin_unlock (&swap_avail_lock); + +return entry; +} + +void free_phys_swap_slot (swp_entry_t phys_entry) +{ +struct swap_info_struct *si; +unsigned long offset; +struct swap_cluster_info *ci; + + si =get_swap_device (phys_entry); +if (!si) +return ; + offset =swp_offset (phys_entry); + ci =swap_cluster_lock (si, offset); +if (ci) { +/* +* Clear the physical slot. The swap count should be 0 at +* this point (the ghost entry's count has been transferred +* or freed). +*/ +__swap_table_set (ci, offset %SWAPFILE_CLUSTER , +null_to_swp_tb ()); +swap_cluster_unlock (ci); +swap_range_free (si, offset,1 ); + } +put_swap_device (si); +} + +/* +* Resolve a ghost swap offset to its physical backing entry. +* Checks the swap table first (for Redirect entries), then falls +* back to the xarray (for entries that have a swap-cache folio). +*/ +swp_entry_t ghost_redirect_to_phys (struct swap_info_struct *si,pgoff_t offset) +{ +struct swap_cluster_info *ci; +unsigned long swp_tb; +void *val; + +/* Fast path: check the swap table for a Redirect entry */ + ci =__swap_offset_to_cluster (si, offset); +spin_lock (&ci->lock); + swp_tb =__swap_table_get (ci, offset %SWAPFILE_CLUSTER ); +if (swp_tb_is_redirect (swp_tb)) { +swp_entry_t phys =redirect_to_swp_entry (swp_tb); +spin_unlock (&ci->lock); +return phys; + } +spin_unlock (&ci->lock); + +/* Slow path: look up the persistent xarray mapping */ + val =xa_load (&si->redirect_xa, offset); +if (val && !xa_is_err (val)) +return (swp_entry_t ){ .val =xa_to_value (val) }; + +return (swp_entry_t ){}; +} +#endif /* CONFIG_ZSWAP */ +static int __init swapfile_init (void ) { swapfile_maximum_size =arch_max_swapfile_size ();
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 5/10 mm/swap_state: restore Redirect entry when swap cache folio is removed
In reply to Baoquan HeWhen a swap cache folio is removed from a ghost swap device, __swap_cache_do_del_folio() normally restores a Shadow entry with the preserved swap count. For ghost devices, the entry may have originated from a Redirect (zswap writeback to a physical device).
Check the redirect_xa before restoring a Shadow: if a redirect mapping exists, restore the Redirect entry instead. This ensures future swapins are correctly forwarded to the physical device.
The redirect mapping is stored in the xarray during zswap writeback (see zswap_writeback_ghost()) and persists across the swap-cache PFN lifecycle.
--- mm/swap_state.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
mm/swap_state.c +21 -0
@@ -12,6 +12,7 @@#include <linux/kernel_stat.h> #include <linux/mempolicy.h> #include <linux/swap.h> +#include <linux/xarray.h> #include <linux/leafops.h> #include <linux/init.h> #include <linux/pagemap.h>
@@ -329,6 +330,26 @@ static void __swap_cache_do_del_folio(struct swap_cluster_info *ci, folio_swapped = true; else need_free = true; +/* +* For ghost swap devices with a redirect mapping, +* restore the Redirect entry so future swapins +* are forwarded to the physical device. +*/ +if (si->flags &SWP_GHOST ) { +swp_entry_t phys_entry; +void *val; + + val =xa_load (&si->redirect_xa, + (swp_offset (entry) - +swp_cluster_offset (entry)) + + ci_off); +if (val && !xa_is_err (val)) { + phys_entry.val =xa_to_value (val); +__swap_table_set (ci, ci_off, +swp_entry_to_redirect (phys_entry)); +continue ; + } + }/* If shadow is NULL, we set an empty shadow. */ __swap_table_set (ci, ci_off,shadow_to_swp_tb (shadow,__swp_tb_get_flags (old_tb)));
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 6/10 mm/zswap: implement ghost-to-physical writeback for backend switching
In reply to Baoquan HeAdd zswap_writeback_ghost() to handle zswap writeback when the backing swap device is a ghost swapfile (SWP_GHOST). Previously, writeback for ghost entries was rejected with -EINVAL.
The new writeback path:
1. Allocates a physical swap slot from any real swap device via alloc_phys_swap_slot().
2. Creates a swap-cache folio attached to the ghost swap entry.
3. Decompresses the zswap entry into the folio.
4. Stores the ghost->physical mapping in the ghost's redirect_xa.
5. Temporarily redirects folio->swap to the physical entry and calls __swap_writepage() to write to the physical device.
6. On success, the xarray mapping persists. When the swap-cache folio is later removed, __swap_cache_do_del_folio() restores the Redirect entry from the xarray.
This enables the backend switch from zswap (compressed in memory) to physical swap (on disk) without changing the PTE — the PTE continues to point to the ghost swap entry, and the swapin path forwards the I/O via the Redirect entry.
Modify zswap_writeback_entry() to call zswap_writeback_ghost() for ghost-backed entries instead of returning -EINVAL.
--- mm/zswap.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 5 deletions(-)
mm/zswap.c +91 -5
@@ -978,6 +978,90 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio)* the swap cache, the compressed version stored by zswap can be * freed. */ +/* +* Ghost swapfile writeback: allocate a physical swap slot, decompress +* the zswap entry into a swap-cache folio, write it to the physical +* device via __swap_writepage, and set up a Redirect entry on the +* ghost swap table so future swapins are forwarded to the physical +* device. +*/ +static int zswap_writeback_ghost (struct zswap_entry *entry, +swp_entry_t swpentry, +struct swap_info_struct *ghost_si, +pgoff_t offset) +{ +swp_entry_t phys_entry; +struct folio *folio; +struct mempolicy *mpol; +swp_entry_t saved_entry; +int ret =0 ; + +/* Allocate a physical swap slot from a real device */ + phys_entry =alloc_phys_swap_slot (); +if (!phys_entry.val) +return -ENOMEM ; + +/* Create a swap-cache folio attached to the ghost swap entry */ + mpol =get_task_policy (current); + folio =swap_cache_alloc_folio (swpentry,GFP_KERNEL ,BIT (0 ),NULL , mpol, +NO_INTERLEAVE_INDEX ,NULL ); +if (IS_ERR (folio)) { + ret =PTR_ERR (folio); + goto free_phys; + } + +/* Decompress zswap data into the folio */ +if (!zswap_decompress (entry, folio)) { + ret = -EIO ; + goto del_folio; + } + +count_vm_event (ZSWPWB ); +if (entry->objcg) +count_objcg_events (entry->objcg,ZSWPWB ,1 ); + +zswap_entry_free (entry); + +folio_mark_uptodate (folio); +folio_set_reclaim (folio); + +/* +* Store the redirect mapping in the xarray so it persists +* across swap-cache PFN cycles. The swap table currently +* holds a PFN (the swap cache folio); when the folio is +* eventually removed from swap cache, the Redirect entry +* will be restored from the xarray. +*/ +xa_store (&ghost_si->redirect_xa, offset, +xa_mk_value (phys_entry.val),GFP_ATOMIC ); + +/* +* Write the folio to the physical swap device. Temporarily +* redirect folio->swap to the physical entry so +* __swap_writepage uses the correct bdev and sector. +*/ + saved_entry = folio->swap; + folio->swap = phys_entry; + ret =__swap_writepage (folio,NULL ); + folio->swap = saved_entry; + +if (ret) { +xa_erase (&ghost_si->redirect_xa, offset); + goto del_folio; + } + +folio_put (folio); +return 0 ; + +del_folio: +swap_cache_del_folio (folio); +folio_unlock (folio); +folio_put (folio); +free_phys: +free_phys_swap_slot (phys_entry); +return ret; +} +static int zswap_writeback_entry (struct zswap_entry *entry,swp_entry_t swpentry) {
@@ -994,11 +1078,6 @@ static int zswap_writeback_entry(struct zswap_entry *entry,if (!si)return -ENOENT ; -if (si->flags &SWP_GHOST ) { -put_swap_device (si); -return -EINVAL ; - } -/* * Verify the swap table Pointer entry still references this * zswap_entry. If the entry was invalidated or replaced by a
@@ -1015,6 +1094,13 @@ static int zswap_writeback_entry(struct zswap_entry *entry, }spin_unlock (&ci->lock); +/* Ghost swapfile: write back to a physical swap device */ +if (si->flags &SWP_GHOST ) { + ret =zswap_writeback_ghost (entry, swpentry, si, offset); +put_swap_device (si); +return ret; + } + mpol =get_task_policy (current); folio =swap_cache_alloc_folio (swpentry,GFP_KERNEL ,BIT (0 ),NULL , mpol,NO_INTERLEAVE_INDEX ,NULL );
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 7/10 mm/page_io: forward ghost swap reads to physical device via Redirect
In reply to Baoquan HeWhen swap_read_folio() encounters a ghost swap entry (SWP_GHOST), check if there is a Redirect mapping to a physical swap slot via ghost_redirect_to_phys(). If a physical backing exists, forward the I/O to that device:
1. Query the redirect_xa / swap table for the physical swp_entry_t.
2. Temporarily redirect folio->swap to the physical entry so the existing swap_read_folio_bdev_{sync,async} / swap_read_folio_fs path computes the correct sector from the physical offset.
3. Restore folio->swap to the original ghost entry after I/O.
This completes the backend-switching round trip:
- Swapout: PTE -> ghost entry -> zswap_store -> Pointer(zswap_entry)
- Writeback: Pointer -> decompress -> write to phys -> Redirect(phys)
- Swapin: PTE -> ghost entry -> Redirect -> read from phys device
--- mm/page_io.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
mm/page_io.c +33 -0
@@ -697,6 +697,39 @@ void swap_read_folio(struct folio *folio, struct swap_iocb **plug, void *zentry) goto finish;if (unlikely (sis->flags &SWP_GHOST )) { +swp_entry_t phys_entry; +struct swap_info_struct *phys_si; + +/* +* Ghost swap entry: check if there's a Redirect to a +* physical swap slot. If so, read from the physical +* device instead. +*/ + phys_entry =ghost_redirect_to_phys (sis,swp_offset (folio->swap)); +if (phys_entry.val) { + phys_si =__swap_entry_to_info (phys_entry); +if (phys_si && phys_si->bdev) { +swp_entry_t saved = folio->swap; + +/* +* Temporarily point the folio to the +* physical entry so the bdev read path +* computes the correct sector. +*/ + folio->swap = phys_entry; +zswap_folio_swapin (folio); +if (data_race (phys_si->flags &SWP_FS_OPS )) +swap_read_folio_fs (folio, plug); +else if (phys_si->flags &SWP_SYNCHRONOUS_IO ) +swap_read_folio_bdev_sync (folio, + phys_si); +else +swap_read_folio_bdev_async (folio, + phys_si); + folio->swap = saved; + goto finish; + } + }folio_unlock (folio); goto finish; }
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 8/10 mm/swapfile: manage ghost cluster_info via lazy vmalloc
In reply to Baoquan HeReplace the kvzalloc cluster_info allocation for ghost swap with a sparse vmalloc area (VM_SPARSE). At swapon time, reserve up to 64MB of virtual address space (supporting ~1TB ghost swap) but only allocate physical pages for the initial size. The cluster_info pointer never changes — __swap_offset_to_cluster() and cluster_index() are unchanged.
Add cluster_vm and ghost_name fields to struct swap_info_struct. In setup_swap_clusters_info(), allocate zeroed physical pages and map them into the reserved VA via vm_area_map_pages(). Handle teardown in both swapoff and the swapon error path.
--- include/linux/swap.h | 4 + mm/swapfile.c | 210 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 179 insertions(+), 35 deletions(-)
include/linux/swap.h +4 -0
@@ -16,6 +16,8 @@#include <uapi/linux/mempolicy.h> #include <asm/page.h> +struct vm_struct ; +#define SWAP_FLAG_PREFER 0x8000/* set if swap priority specified */ #define SWAP_FLAG_PRIO_MASK 0x7fff#define SWAP_FLAG_DISCARD 0x10000/* enable discard for swap */
@@ -279,6 +281,8 @@ struct swap_info_struct {struct plist_node avail_list;/* entry in swap_avail_head */ #ifdef CONFIG_ZSWAP struct xarray redirect_xa;/* ghost offset → phys swp_entry_t */ +struct vm_struct *cluster_vm;/* ghost: sparse vm_area for cluster_info */ +char *ghost_name;/* ghost: dentry name for sysfs */ #endif };
mm/swapfile.c +175 -35
273 lines not on this page · raw message
Signature · 1 line
2.54.0
Baoquan He
reviewerRFC PATCH 9/10 mm/swapfile: implement swap_ghost_extend_max() for dynamic growth
In reply to Baoquan HeAllocate additional physical pages and map them at the end of the already-reserved sparse vmalloc area via vm_area_map_pages(). The flat cluster_info pointer never moves — new clusters appear contiguously at higher indices. New clusters are fully initialized (spinlocks, list heads, trailing BAD mask) before si->max is published via WRITE_ONCE.
Page-pointer tracking in cluster_vm->pages is reallocated to accommodate additional entries. Returns -E2BIG if the requested size exceeds the reserved VA range. Extension is serialized by swapon_mutex.
--- mm/swapfile.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)
mm/swapfile.c +110 -0
@@ -58,6 +58,8 @@ static void move_cluster(struct swap_info_struct *si, enum swap_cluster_flags new_flags);#ifdef CONFIG_ZSWAP #define GHOST_MAX_CLUSTER_BYTES (64UL * 1024 * 1024) +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new_maxpages);#endif /*
@@ -4247,6 +4249,114 @@ swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset) return (swp_entry_t){}; } + +/* +* swap_ghost_extend_max — Extend an active ghost swap device. +* @si: ghost device (SWP_GHOST set) +* @new: new max pages (> si->max) +* +* Allocates physical pages and maps them at the end of the reserved +* sparse vmalloc area. si->cluster_info never moves. New clusters +* are fully initialized before si->max is published via WRITE_ONCE. +* +* Returns 0, -ENOMEM, -EINVAL, or -E2BIG. Caller holds swapon_mutex. +*/ +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new) +{ +unsigned long old_max = si->max, old_nr, new_nr; +unsigned long old_bytes, new_bytes, nr_new_pg; +struct page **new_pglist; +unsigned long i; + +if (!(si->flags &SWP_GHOST ) || + !(si->flags & (SWP_USED |SWP_WRITEOK )) || + new <= si->max) +return -EINVAL ; + + old_nr =DIV_ROUND_UP (si->max,SWAPFILE_CLUSTER ); + new_nr =DIV_ROUND_UP (new,SWAPFILE_CLUSTER ); + old_bytes =ALIGN (old_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + new_bytes =ALIGN (new_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + nr_new_pg = (new_bytes - old_bytes) /PAGE_SIZE ; + +if (old_bytes + nr_new_pg *PAGE_SIZE > si->cluster_vm->size) { +pr_warn ("ghost: extend to %u exceeds reserved VA (%lu bytes)\n" , + new, si->cluster_vm->size); +return -E2BIG ; + } + + new_pglist =kvmalloc_array (si->cluster_vm->nr_pages + nr_new_pg, +sizeof (struct page *),GFP_KERNEL ); +if (!new_pglist) +return -ENOMEM ; +if (si->cluster_vm->pages) { +memcpy (new_pglist, si->cluster_vm->pages, + si->cluster_vm->nr_pages *sizeof (struct page *)); +kvfree (si->cluster_vm->pages); + } + si->cluster_vm->pages = (void *)new_pglist; + +for (i =0 ; i < nr_new_pg; i++) { +struct page *page =alloc_page (GFP_KERNEL | __GFP_ZERO); +if (!page) { +while (i-- >0 ) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + new_pglist[si->cluster_vm->nr_pages + i] = page; + } + +if (vm_area_map_pages (si->cluster_vm, + (unsigned long )si->cluster_vm->addr + old_bytes, + (unsigned long )si->cluster_vm->addr + new_bytes, + &new_pglist[si->cluster_vm->nr_pages])) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + si->cluster_vm->nr_pages += nr_new_pg; + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; +spin_lock_init (&ci->lock); +INIT_LIST_HEAD (&ci->list); + } + +if (new %SWAPFILE_CLUSTER ) { +unsigned long base = (new -1 ) /SWAPFILE_CLUSTER * +SWAPFILE_CLUSTER ; +unsigned long idx = base /SWAPFILE_CLUSTER ; +struct swap_cluster_info *ci = &si->cluster_info[idx]; + +if (!ci->count &&swap_cluster_alloc_table (ci,GFP_KERNEL )) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } +spin_lock (&ci->lock); +for (i = new %SWAPFILE_CLUSTER ; i <SWAPFILE_CLUSTER ; i++) +__swap_table_set (ci, i,SWP_TB_BAD ); +spin_unlock (&ci->lock); + } + +WRITE_ONCE (si->max, new); + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; + ci->flags =CLUSTER_FLAG_FREE ; +spin_lock (&si->lock); +list_add_tail (&ci->list, &si->free_clusters); +spin_unlock (&si->lock); + } + + si->pages += new - old_max; +atomic_long_add (new - old_max, &nr_swap_pages); +spin_lock (&swap_lock); + total_swap_pages += new - old_max; +spin_unlock (&swap_lock); +return 0 ; +}#endif /* CONFIG_ZSWAP */ static int __init swapfile_init (void )
Signature · 1 line
2.54.0
Nhat Pham
maintainerRe: [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth
Quoted 132 lines · Baoquan He
Allocate additional physical pages and map them at the end of the already-reserved sparse vmalloc area via
vm_area_map_pages(). The flatcluster_infopointer never moves — new clusters appear contiguously at higher indices. New clusters are fully initialized (spinlocks, list heads, trailing BAD mask) beforesi->maxis published viaWRITE_ONCE.Page-pointer tracking in
cluster_vm->pagesis reallocated to accommodate additional entries. Returns-E2BIGif the requested size exceeds the reserved VA range. Extension is serialized byswapon_mutex.Signed-off-by Baoquan He <[email protected]>--- mm/swapfile.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)mm/swapfile.c +101
-0@@ -58,6 +58,8 @@ static void move_cluster(struct swap_info_struct *si, enum swap_cluster_flags new_flags);#ifdef CONFIG_ZSWAP #define GHOST_MAX_CLUSTER_BYTES (64UL * 1024 * 1024) +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new_maxpages);#endif /* @@ -4247,6 +4249,114 @@ swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset)return (swp_entry_t ){}; } + +/* +* swap_ghost_extend_max — Extend an active ghost swap device. +* @si: ghost device (SWP_GHOST set) +* @new: new max pages (> si->max) +* +* Allocates physical pages and maps them at the end of the reserved +* sparse vmalloc area. si->cluster_info never moves. New clusters +* are fully initialized before si->max is published via WRITE_ONCE. +* +* Returns 0, -ENOMEM, -EINVAL, or -E2BIG. Caller holds swapon_mutex. +*/ +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new) +{ +unsigned long old_max = si->max, old_nr, new_nr; +unsigned long old_bytes, new_bytes, nr_new_pg; +struct page **new_pglist; +unsigned long i; + +if (!(si->flags &SWP_GHOST ) || + !(si->flags & (SWP_USED |SWP_WRITEOK )) || + new <= si->max) +return -EINVAL ; + + old_nr =DIV_ROUND_UP (si->max,SWAPFILE_CLUSTER ); + new_nr =DIV_ROUND_UP (new,SWAPFILE_CLUSTER ); + old_bytes =ALIGN (old_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + new_bytes =ALIGN (new_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + nr_new_pg = (new_bytes - old_bytes) /PAGE_SIZE ; + +if (old_bytes + nr_new_pg *PAGE_SIZE > si->cluster_vm->size) { +pr_warn ("ghost: extend to %u exceeds reserved VA (%lu bytes)\n" , + new, si->cluster_vm->size); +return -E2BIG ; + } + + new_pglist =kvmalloc_array (si->cluster_vm->nr_pages + nr_new_pg, +sizeof (struct page *),GFP_KERNEL ); +if (!new_pglist) +return -ENOMEM ; +if (si->cluster_vm->pages) { +memcpy (new_pglist, si->cluster_vm->pages, + si->cluster_vm->nr_pages *sizeof (struct page *)); +kvfree (si->cluster_vm->pages); + } + si->cluster_vm->pages = (void *)new_pglist; + +for (i =0 ; i < nr_new_pg; i++) { +struct page *page =alloc_page (GFP_KERNEL | __GFP_ZERO); +if (!page) { +while (i-- >0 ) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + new_pglist[si->cluster_vm->nr_pages + i] = page; + } + +if (vm_area_map_pages (si->cluster_vm, + (unsigned long )si->cluster_vm->addr + old_bytes, + (unsigned long )si->cluster_vm->addr + new_bytes, + &new_pglist[si->cluster_vm->nr_pages])) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + si->cluster_vm->nr_pages += nr_new_pg; + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; +spin_lock_init (&ci->lock); +INIT_LIST_HEAD (&ci->list); + } + +if (new %SWAPFILE_CLUSTER ) { +unsigned long base = (new -1 ) /SWAPFILE_CLUSTER * +SWAPFILE_CLUSTER ; +unsigned long idx = base /SWAPFILE_CLUSTER ; +struct swap_cluster_info *ci = &si->cluster_info[idx]; + +if (!ci->count &&swap_cluster_alloc_table (ci,GFP_KERNEL )) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } +spin_lock (&ci->lock); +for (i = new %SWAPFILE_CLUSTER ; i <SWAPFILE_CLUSTER ; i++) +__swap_table_set (ci, i,SWP_TB_BAD ); +spin_unlock (&ci->lock); + } + +WRITE_ONCE (si->max, new); + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; + ci->flags =CLUSTER_FLAG_FREE ; +spin_lock (&si->lock); +list_add_tail (&ci->list, &si->free_clusters); +spin_unlock (&si->lock);
mm/swapfile.c swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset) · line 4360
Quoted 10 lines
mm/swapfile.c +9
-0+ } + + si->pages += new - old_max; +atomic_long_add (new - old_max, &nr_swap_pages); +spin_lock (&swap_lock); + total_swap_pages += new - old_max; +spin_unlock (&swap_lock); +return 0 ; +}#endif /* CONFIG_ZSWAP */
Does this only grow? Say I have a huge but temporary spike in swap usage, which triggers the extension of the the ghost swapfile address space. Do these metadata space live forever?
If that's the case, might as well just swapon...?
Baoquan He
reviewerRe: [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth
Quoted 139 lines · Nhat Pham
Quoted 138 lines · Baoquan He
Allocate additional physical pages and map them at the end of the already-reserved sparse vmalloc area via
vm_area_map_pages(). The flatcluster_infopointer never moves — new clusters appear contiguously at higher indices. New clusters are fully initialized (spinlocks, list heads, trailing BAD mask) beforesi->maxis published viaWRITE_ONCE.Page-pointer tracking in
cluster_vm->pagesis reallocated to accommodate additional entries. Returns-E2BIGif the requested size exceeds the reserved VA range. Extension is serialized byswapon_mutex.Signed-off-by Baoquan He <[email protected]>--- mm/swapfile.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)mm/swapfile.c +107
-0@@ -58,6 +58,8 @@ static void move_cluster(struct swap_info_struct *si, enum swap_cluster_flags new_flags);#ifdef CONFIG_ZSWAP #define GHOST_MAX_CLUSTER_BYTES (64UL * 1024 * 1024) +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new_maxpages);#endif /* @@ -4247,6 +4249,114 @@ swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset)return (swp_entry_t ){}; } + +/* +* swap_ghost_extend_max — Extend an active ghost swap device. +* @si: ghost device (SWP_GHOST set) +* @new: new max pages (> si->max) +* +* Allocates physical pages and maps them at the end of the reserved +* sparse vmalloc area. si->cluster_info never moves. New clusters +* are fully initialized before si->max is published via WRITE_ONCE. +* +* Returns 0, -ENOMEM, -EINVAL, or -E2BIG. Caller holds swapon_mutex. +*/ +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new) +{ +unsigned long old_max = si->max, old_nr, new_nr; +unsigned long old_bytes, new_bytes, nr_new_pg; +struct page **new_pglist; +unsigned long i; + +if (!(si->flags &SWP_GHOST ) || + !(si->flags & (SWP_USED |SWP_WRITEOK )) || + new <= si->max) +return -EINVAL ; + + old_nr =DIV_ROUND_UP (si->max,SWAPFILE_CLUSTER ); + new_nr =DIV_ROUND_UP (new,SWAPFILE_CLUSTER ); + old_bytes =ALIGN (old_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + new_bytes =ALIGN (new_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + nr_new_pg = (new_bytes - old_bytes) /PAGE_SIZE ; + +if (old_bytes + nr_new_pg *PAGE_SIZE > si->cluster_vm->size) { +pr_warn ("ghost: extend to %u exceeds reserved VA (%lu bytes)\n" , + new, si->cluster_vm->size); +return -E2BIG ; + } + + new_pglist =kvmalloc_array (si->cluster_vm->nr_pages + nr_new_pg, +sizeof (struct page *),GFP_KERNEL ); +if (!new_pglist) +return -ENOMEM ; +if (si->cluster_vm->pages) { +memcpy (new_pglist, si->cluster_vm->pages, + si->cluster_vm->nr_pages *sizeof (struct page *)); +kvfree (si->cluster_vm->pages); + } + si->cluster_vm->pages = (void *)new_pglist; + +for (i =0 ; i < nr_new_pg; i++) { +struct page *page =alloc_page (GFP_KERNEL | __GFP_ZERO); +if (!page) { +while (i-- >0 ) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + new_pglist[si->cluster_vm->nr_pages + i] = page; + } + +if (vm_area_map_pages (si->cluster_vm, + (unsigned long )si->cluster_vm->addr + old_bytes, + (unsigned long )si->cluster_vm->addr + new_bytes, + &new_pglist[si->cluster_vm->nr_pages])) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + si->cluster_vm->nr_pages += nr_new_pg; + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; +spin_lock_init (&ci->lock); +INIT_LIST_HEAD (&ci->list); + } + +if (new %SWAPFILE_CLUSTER ) { +unsigned long base = (new -1 ) /SWAPFILE_CLUSTER * +SWAPFILE_CLUSTER ; +unsigned long idx = base /SWAPFILE_CLUSTER ; +struct swap_cluster_info *ci = &si->cluster_info[idx]; + +if (!ci->count &&swap_cluster_alloc_table (ci,GFP_KERNEL )) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } +spin_lock (&ci->lock); +for (i = new %SWAPFILE_CLUSTER ; i <SWAPFILE_CLUSTER ; i++) +__swap_table_set (ci, i,SWP_TB_BAD ); +spin_unlock (&ci->lock); + } + +WRITE_ONCE (si->max, new); + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; + ci->flags =CLUSTER_FLAG_FREE ; +spin_lock (&si->lock); +list_add_tail (&ci->list, &si->free_clusters); +spin_unlock (&si->lock); + } + + si->pages += new - old_max; +atomic_long_add (new - old_max, &nr_swap_pages); +spin_lock (&swap_lock); + total_swap_pages += new - old_max;
Quoted 8 lines
mm/swapfile.c swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset) · line 4360
Quoted 4 lines
mm/swapfile.c +3
-0+spin_unlock (&swap_lock); + return 0; +}#endif /* CONFIG_ZSWAP */ Does this only grow? Say I have a huge but temporary spike in swap usage, which triggers the extension of the the ghost swapfile address space. Do these metadata space live forever?
Yes, it is grow-only for now. The metadata overhead of an extended ghost device is very small: cluster_info[] costs ~32 MB per TB of capacity. For a 64 GB machine, 1 TB of ghost swap space costs about 0.05% of RAM in metadata. This is not a resource that needs urgent reclamation after a spike subsides.
If it does need to be reclaimed — for instance, after a one-time spike pushed capacity far beyond normal needs — ghost swap supports swapoff. The admin can swapoff the ghost device and swapon a new one at a smaller size. No swap type in the kernel today supports online shrinking (not block devices, not swapfiles, not zram), so this is the standard mechanism.
Shrinking would require migrating active swap entries out of the evacuated range — swap entry migration infrastructure that does not exist in the kernel today. Implementing it solely to reclaim a few MB of metadata after a capacity spike does not seem like a good cost/benefit trade-off. If there is a concrete use case that requires online shrinking, we can discuss adding it as follow-up work.
Thanks
Baoquan
Quoted 2 lines
If that's the case, might as well just swapon...?
Nhat Pham
maintainerRe: [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth
Quoted 9 lines · Baoquan He
On 07/07/26 at 03:36pm, Nhat Pham wrote:
Yes, it is grow-only for now. The metadata overhead of an extended ghost device is very small: cluster_info[] costs ~32 MB per TB of capacity. For a 64 GB machine, 1 TB of ghost swap space costs about 0.05% of RAM in metadata. This is not a resource that needs urgent reclamation after a spike subsides.
Quoted 6 lines
If it does need to be reclaimed — for instance, after a one-time spike pushed capacity far beyond normal needs — ghost swap supports swapoff. The admin can swapoff the ghost device and swapon a new one at a smaller size. No swap type in the kernel today supports online shrinking (not block devices, not swapfiles, not zram), so this is the standard mechanism.
Except for my virtual swap design, which does support dynamic growth AND shrinking of capacity on demand ;)
If it cannot grow (and furthermore, if it requires userspace operation to trigger swapfile growth), why do we need this at all? Might as well create a new swapfile with swapon? That was my point.
Chris Li
maintainerRe: [RFC PATCH 09/10] mm/swapfile: implement swap_ghost_extend_max() for dynamic growth
In reply to Baoquan HeQuoted 68 lines · Baoquan He
Allocate additional physical pages and map them at the end of the already-reserved sparse vmalloc area via
vm_area_map_pages(). The flatcluster_infopointer never moves — new clusters appear contiguously at higher indices. New clusters are fully initialized (spinlocks, list heads, trailing BAD mask) beforesi->maxis published viaWRITE_ONCE.Page-pointer tracking in
cluster_vm->pagesis reallocated to accommodate additional entries. Returns-E2BIGif the requested size exceeds the reserved VA range. Extension is serialized byswapon_mutex.Signed-off-by Baoquan He <[email protected]>--- mm/swapfile.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+)mm/swapfile.c +37
-0@@ -58,6 +58,8 @@ static void move_cluster(struct swap_info_struct *si, enum swap_cluster_flags new_flags);#ifdef CONFIG_ZSWAP #define GHOST_MAX_CLUSTER_BYTES (64UL * 1024 * 1024) +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new_maxpages);#endif /* @@ -4247,6 +4249,114 @@ swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset)return (swp_entry_t ){}; } + +/* +* swap_ghost_extend_max — Extend an active ghost swap device. +* @si: ghost device (SWP_GHOST set) +* @new: new max pages (> si->max) +* +* Allocates physical pages and maps them at the end of the reserved +* sparse vmalloc area. si->cluster_info never moves. New clusters +* are fully initialized before si->max is published via WRITE_ONCE. +* +* Returns 0, -ENOMEM, -EINVAL, or -E2BIG. Caller holds swapon_mutex. +*/ +static int swap_ghost_extend_max (struct swap_info_struct *si, +unsigned int new) +{ +unsigned long old_max = si->max, old_nr, new_nr; +unsigned long old_bytes, new_bytes, nr_new_pg; +struct page **new_pglist; +unsigned long i; + +if (!(si->flags &SWP_GHOST ) || + !(si->flags & (SWP_USED |SWP_WRITEOK )) || + new <= si->max) +return -EINVAL ; + + old_nr =DIV_ROUND_UP (si->max,SWAPFILE_CLUSTER ); + new_nr =DIV_ROUND_UP (new,SWAPFILE_CLUSTER ); + old_bytes =ALIGN (old_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + new_bytes =ALIGN (new_nr *sizeof (struct swap_cluster_info ),PAGE_SIZE ); + nr_new_pg = (new_bytes - old_bytes) /PAGE_SIZE ; + +if (old_bytes + nr_new_pg *PAGE_SIZE > si->cluster_vm->size) { +pr_warn ("ghost: extend to %u exceeds reserved VA (%lu bytes)\n" , + new, si->cluster_vm->size); +return -E2BIG ;
mm/swapfile.c swp_entry_t ghost_redirect_to_phys(struct swap_info_struct *si, pgoff_t offset) · line 4296
Quoted 10 lines
mm/swapfile.c +10
-0+ } + + new_pglist =kvmalloc_array (si->cluster_vm->nr_pages + nr_new_pg, +sizeof (struct page *),GFP_KERNEL ); +if (!new_pglist) +return -ENOMEM ; +if (si->cluster_vm->pages) { +memcpy (new_pglist, si->cluster_vm->pages, + si->cluster_vm->nr_pages *sizeof (struct page *)); +kvfree (si->cluster_vm->pages);
Can you use kvrealloc() here? I was hoping that could get rid of the memcpy on your side.
I was considering another approach: allowing kvmalloc internally to take two arguments for size. One is the initial size, and the other is the vma size. That way you can specify the vma->pages with the max size it can support but with a small inti size. Then it can grow the backing pages incrementally to the maximum size without reallocating the vma. I did some experiments; I might send out an RFC if there is interest.
Chris
Quoted 69 lines
mm/swapfile.c +63
-1+ } + si->cluster_vm->pages = (void *)new_pglist; + +for (i =0 ; i < nr_new_pg; i++) { +struct page *page =alloc_page (GFP_KERNEL | __GFP_ZERO); +if (!page) { +while (i-- >0 ) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + new_pglist[si->cluster_vm->nr_pages + i] = page; + } + +if (vm_area_map_pages (si->cluster_vm, + (unsigned long )si->cluster_vm->addr + old_bytes, + (unsigned long )si->cluster_vm->addr + new_bytes, + &new_pglist[si->cluster_vm->nr_pages])) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } + si->cluster_vm->nr_pages += nr_new_pg; + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; +spin_lock_init (&ci->lock); +INIT_LIST_HEAD (&ci->list); + } + +if (new %SWAPFILE_CLUSTER ) { +unsigned long base = (new -1 ) /SWAPFILE_CLUSTER * +SWAPFILE_CLUSTER ; +unsigned long idx = base /SWAPFILE_CLUSTER ; +struct swap_cluster_info *ci = &si->cluster_info[idx]; + +if (!ci->count &&swap_cluster_alloc_table (ci,GFP_KERNEL )) { +for (i =0 ; i < nr_new_pg; i++) +__free_page (new_pglist[si->cluster_vm->nr_pages + i]); +return -ENOMEM ; + } +spin_lock (&ci->lock); +for (i = new %SWAPFILE_CLUSTER ; i <SWAPFILE_CLUSTER ; i++) +__swap_table_set (ci, i,SWP_TB_BAD ); +spin_unlock (&ci->lock); + } + +WRITE_ONCE (si->max, new); + +for (i = old_nr; i < new_nr; i++) { +struct swap_cluster_info *ci = &si->cluster_info[i]; + ci->flags =CLUSTER_FLAG_FREE ; +spin_lock (&si->lock); +list_add_tail (&ci->list, &si->free_clusters); +spin_unlock (&si->lock); + } + + si->pages += new - old_max; +atomic_long_add (new - old_max, &nr_swap_pages); +spin_lock (&swap_lock); + total_swap_pages += new - old_max; +spin_unlock (&swap_lock); +return 0 ; +}#endif /* CONFIG_ZSWAP */ static int __init swapfile_init (void ) --2.54.0
Baoquan He
reviewerRFC PATCH 10/10 mm/swapfile: add sysfs interface for ghost swap extension
In reply to Baoquan HeAdd per-device attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — dentry basename
max (RW) — current max pages; write to trigger swap_ghost_extend_max()
The ghost_kobj field ties each ghost device to its sysfs directory, created at swapon and removed at swapoff.
--- include/linux/swap.h | 1 + mm/swapfile.c | 111 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+)
include/linux/swap.h +1 -0
@@ -283,6 +283,7 @@ struct swap_info_struct {struct xarray redirect_xa;/* ghost offset → phys swp_entry_t */ struct vm_struct *cluster_vm;/* ghost: sparse vm_area for cluster_info */ char *ghost_name;/* ghost: dentry name for sysfs */ +struct kobject *ghost_kobj;/* ghost: sysfs per-device directory */ #endif };
mm/swapfile.c +111 -0
141 lines not on this page · raw message
Signature · 1 line
2.54.0
Baoquan He
reviewerQuoted 3 lines · Baoquan He
This series introduces a virtual "ghost" swapfile that can be backed by multiple physical swap devices, switching between them at runtime using a new "Redirect" swap table entry type.
Forgot mentioning this patchset is based on below patchset:
[RFC PATCH 0/7] mm: store zswap entries in swap table (Pointer entry)
https://lore.kernel.org/all/[email protected]/T/#u
and they sit on top of akpm/mm-unstale commit cfb8731f5396 ("mm: fix CONFIG_STACK_GROWSUP typo in tools/testing/vma/include/dup.h")
Quoted 112 lines
Motivation
----------
Physical swap devices have fixed, large sizes but a machine may only need a fraction at any given time. Ghost swap decouples the logical swap capacity from physical swap allocation: it presents a large virtual swap device to userspace while lazily mapping slots to physical devices only when data must leave zswap (writeback). This enables overcommit of swap capacity without wasting physical disk space, and allows the ghost device to grow dynamically at runtime.Ghost swapfile characteristics
--------------------------------
Ghost swap decouples logical swap capacity from physical swap allocation. Key properties:- No backing store required. A ghost swap device can operate purely as a zswap container ??? compressed pages stay in the zswap pool without ever touching a physical swap device. No physical swap device needs to be attached.
- Optional physical backing. One or more physical swap devices can be swapon'd alongside the ghost device, acting as writeback targets when the zswap pool needs to evict cold pages. The Redirect entry in the ghost swap table links each evicted slot to its physical destination.
- Dynamic size growth. The ghost device can be extended at runtime via sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for cluster_info[] structures are allocated only on demand as the device grows. The
cluster_infopointer never changes, so all existing accessors (__swap_offset_to_cluster,cluster_index) work unchanged.- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) ??? backing device name
max (RW) ??? current max pages; writing a larger value triggers
swap_ghost_extend_max()Design overview
---------------
A swap table slot can now hold a Redirect entry:Redirect: |----- physical
swp_entry_t-----|101|This stores a plain
swp_entry_tpointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.The lifecycle is:
1. swapon a ghost device ??? reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
2. zswap writeback from ghost: when the zswap pool needs to evict,
zswap_writeback_ghost()allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.3. Swap-in from ghost:
swap_read_folio()detects the ghost device (SWP_GHOST), resolves the Redirect to the physicalswp_entry_t, and forwards the read I/O to the physical device.4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (
redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.5. When the physical slot is freed, the ghost slot is also freed, cascading through
swap_range_free().Note:
------
Ghost swapfile is a proof of concept. Compared with the other patchset:
[RFC PATCH v2 0/7] mm, swap: Virtual Swap Space (Swap Table Edition), it explores a different approach to implementing virtual swap on top of the existing swap table infrastructure, with a simpler implementation. It builds on Chris Li's earlier ghost swapfile work and is currently tentatively named "ghost swapfile" ??? alternative naming suggestions are welcome. Ghost swapfile setup can also be integrated through modifications to the swapon API and the swapon command in util-linux, for example:
swapon --ghost ghost_1:2G -p 5.Baoquan He (9):
mm/swap_table: add Redirect entry encoding for ghost swap backend
switching
mm/swap: addredirect_xafield and ghost redirect helper declarations mm/swapfile: implement ghost redirect helpers and free-path cascademm/swap_state: restore Redirect entry when swap cache folio is removed mm/zswap: implement ghost-to-physical writeback for backend switchingmm/page_io: forward ghost swap reads to physical device via Redirect mm/swapfile: manage ghostcluster_infovia lazy vmalloc mm/swapfile: implementswap_ghost_extend_max()for dynamic growth mm/swapfile: add sysfs interface for ghost swap extensionChris Li (1):
mm: ghost swapfile support for zswapinclude/linux/swap.h | 10 + mm/page_io.c | 51 +++- mm/swap.h | 16 +- mm/swap_state.c | 28 +++ mm/swap_table.h | 38 +++ mm/swapfile.c | 579 ++++++++++++++++++++++++++++++++++++++++--- mm/zswap.c | 98 +++++++- 7 files changed, 772 insertions(+), 48 deletions(-)Signature · 1 line
2.54.0
Nhat Pham
maintainerQuoted 25 lines · Baoquan He
This series introduces a virtual "ghost" swapfile that can be backed by multiple physical swap devices, switching between them at runtime using a new "Redirect" swap table entry type.
Motivation
----------
Physical swap devices have fixed, large sizes but a machine may only need a fraction at any given time. Ghost swap decouples the logical swap capacity from physical swap allocation: it presents a large virtual swap device to userspace while lazily mapping slots to physical devices only when data must leave zswap (writeback). This enables overcommit of swap capacity without wasting physical disk space, and allows the ghost device to grow dynamically at runtime.Ghost swapfile characteristics
--------------------------------
Ghost swap decouples logical swap capacity from physical swap allocation. Key properties:- No backing store required. A ghost swap device can operate purely as a zswap container — compressed pages stay in the zswap pool without ever touching a physical swap device. No physical swap device needs to be attached.
Quoted 6 lines
- Optional physical backing. One or more physical swap devices can be swapon'd alongside the ghost device, acting as writeback targets when the zswap pool needs to evict cold pages. The Redirect entry in the ghost swap table links each evicted slot to its physical destination.
- Dynamic size growth. The ghost device can be extended at runtime via
We don't want to put additional operational burden on users. Besides, expecting userspace to react to swap space being filled, especially under memory pressure (at which point userspace processes might be stalling already), is tricky.
This is especially bad when redirection entry still eats up space in the upper level (ghost zswap) swapfile. You will run out of zswap space really fast under concurrent zswap store and zswap writeback.
Quoted 3 lines
sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for
This range seems a bit small, especially from something essentially fixed.
Quoted 20 lines
cluster_info[] structures are allocated only on demand as the device grows. The
cluster_infopointer never changes, so all existing accessors (__swap_offset_to_cluster,cluster_index) work unchanged.- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — backing device name
max (RW) — current max pages; writing a larger value triggers
swap_ghost_extend_max()Design overview
---------------
A swap table slot can now hold a Redirect entry:Redirect: |----- physical
swp_entry_t-----|101|This stores a plain
swp_entry_tpointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.
Quoted 6 lines
The lifecycle is:
1. swapon a ghost device — reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
If you think this data structure is superior to the xarray for dynamic swap address, it's a fairly simple change on top of what I had in the virtual swap RFC, no?
It can be grafted on top of what I had in a subsequent patch, or squashed into my first patch. If you have concerns with me implementing your idea here, I'd take your code if it proves to be superior.
Quoted 5 lines
2. zswap writeback from ghost: when the zswap pool needs to evict,
zswap_writeback_ghost()allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.
What if user swapoff the physical swapfile, whose slot is backing a redirection entry of a ghost swapfile, rather than a PTE. Has this been handled yet?
Similarly, say the physical swapfile is full, and you attempt to reclaim a physical swap slot that is swap-cache-only (swap count = 0). Say this physical swap slot was allocated from a previous zswap writeback attempt, i.e it has a redirection entry in the ghost zswap swapfile associated with it. Did we handle it somehow?
My apologies if I missed it somehow.
Quoted 11 lines
3. Swap-in from ghost:
swap_read_folio()detects the ghost device (SWP_GHOST), resolves the Redirect to the physicalswp_entry_t, and forwards the read I/O to the physical device.4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (
redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.5. When the physical slot is freed, the ghost slot is also freed, cascading through
swap_range_free().
I don't get how *any* of this is simpler from what I had. If anything, it's more complicated. With a vswap device, you just allocate a physical swap slot (which you also did), change the backend, submit the IO (which you also did), and that's it. No need to juggle this redirect_xa entry. What does this redirect_xa buy us?
Not to mention now it entagles generic swap logic into backend-specific code, unnecessarily. And we have more state in the state machine to consider: With a generic vswap device, vswap -> disk swap directly is the same state as vswap -> written back zswap entry. With this zswap ghost swapfile design, you have to treat the latter separately, as there is a redirection entry involved, which means concurrent swap operations interact with it.
So it's more complicated, and achieve less - this is specific to zswap, whereas what I provide is something generic to swap infrastructure, which can be extended for future backends. I would understand if it resulted in less overhead (which was the argument for my pre-swap-table vswap design), but this seems like it's just virtual swap, with similar space overhead (technically a bit worse since you added redirect_xa xarray to physical swap cluster too), and with more complications?
Quoted 7 lines
Note:
------
Ghost swapfile is a proof of concept. Compared with the other patchset:
[RFC PATCH v2 0/7] mm, swap: Virtual Swap Space (Swap Table Edition), it explores a different approach to implementing virtual swap on top of the existing swap table infrastructure, with a simpler implementation.
You neglected to mention that this "simpler" implementation lacks a lot of design choices. Just on top of my head:
1. Certain optimizations for cases when vswap points to disk swap.
2. No reverse mapping, or at least some mechanisms to handle the case where you have to go from physical swap slots to the upper layer.
3. Charging behavior difference, and deduplication of metadata when we have multiple swap levels. (In fact, you seem to be double charging a writtenback entry here towards the swap counter, not just duplicating the metadata space)
4. Infrastructure to make sure allocation from this swap device and allocation from the backing swap device does not invalidate each other's per-cpu allocation cache. You seem to just go for the slow path with alloc_phys_swap_slot() for zswap writeback?
It's fine to disagree with any or all of these design choices of mine
- I'm receptive to discussion. But it's misleading to say you have a simpler design here, IMHO. My design choices resulted from multiple thoughtful iterations (a lot of them could be found in my pre-swap-table version of vswap) and real production pains.
Nhat Pham
maintainerQuoted 11 lines · Nhat Pham
On Tue, Jul 7, 2026 at 1:26 AM Baoquan He <[email protected]> wrote:
So it's more complicated, and achieve less - this is specific to zswap, whereas what I provide is something generic to swap infrastructure, which can be extended for future backends. I would understand if it resulted in less overhead (which was the argument for my pre-swap-table vswap design), but this seems like it's just virtual swap, with similar space overhead (technically a bit worse since you added
redirect_xaxarray to physical swap cluster too), and with more complications?
Actually no - I just realized redirect_xa is global per-swapfile :) It's not even cluster-partitioned.
Baoquan He
reviewerQuoted 26 lines · Nhat Pham
Quoted 25 lines · Baoquan He
This series introduces a virtual "ghost" swapfile that can be backed by multiple physical swap devices, switching between them at runtime using a new "Redirect" swap table entry type.
Motivation
----------
Physical swap devices have fixed, large sizes but a machine may only need a fraction at any given time. Ghost swap decouples the logical swap capacity from physical swap allocation: it presents a large virtual swap device to userspace while lazily mapping slots to physical devices only when data must leave zswap (writeback). This enables overcommit of swap capacity without wasting physical disk space, and allows the ghost device to grow dynamically at runtime.Ghost swapfile characteristics
--------------------------------
Ghost swap decouples logical swap capacity from physical swap allocation. Key properties:- No backing store required. A ghost swap device can operate purely as a zswap container — compressed pages stay in the zswap pool without ever touching a physical swap device. No physical swap device needs to be attached.
Quoted 6 lines
Quoted 6 lines
- Optional physical backing. One or more physical swap devices can be swapon'd alongside the ghost device, acting as writeback targets when the zswap pool needs to evict cold pages. The Redirect entry in the ghost swap table links each evicted slot to its physical destination.
- Dynamic size growth. The ghost device can be extended at runtime via
Thanks for the thorough review, Nhat.
Quoted 5 lines
We don't want to put additional operational burden on users. Besides, expecting userspace to react to swap space being filled, especially under memory pressure (at which point userspace processes might be stalling already), is tricky.
The manual sysfs extension is the minimal viable interface for the RFC, not the final choice. But even in its current form, it is a dramatic improvement over the status quo.
Today, to use zswap you MUST provision a physical swap device of the same size as the swap capacity wanted — even though most of that disk space will never be touched. With ghost swap, you swapon a ghost device at whatever size you expect to need (e.g. 64 GB), and if your workload grows, you grow it via sysfs. That is already a step change in operational simplicity: no disk pre-provisioning, no wasted storage, and a one-line resize when needed.
Automatic growth can be considered later if it's really demanded.
Quoted 4 lines
This is especially bad when redirection entry still eats up space in the upper level (ghost zswap) swapfile. You will run out of zswap space really fast under concurrent zswap store and zswap writeback.
I think there is a misunderstanding here. A Redirect entry does not consume an *additional* ghost slot. Before writeback, the ghost slot held a Pointer entry (zswap). After writeback, that same slot holds a Redirect entry (pointing to the physical device). It's a state transition — one slot, one entry — not an extra allocation. The ghost device's total capacity is unchanged by writeback activity.
Quoted 6 lines
Quoted 4 lines
sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for
This range seems a bit small, especially from something essentially fixed.
The 1TB limit comes from a conservative 64 MB sparse vmalloc reservation in RFC. It certainly can be redefined as a bigger one which is more reasonable in reality. For most real-world deployments today, 1 TB per ghost device — with support for multiple ghost devices — is more than sufficient.
Quoted 23 lines
Quoted 23 lines
cluster_info[] structures are allocated only on demand as the device grows. The
cluster_infopointer never changes, so all existing accessors (__swap_offset_to_cluster,cluster_index) work unchanged.- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — backing device name
max (RW) — current max pages; writing a larger value triggers
swap_ghost_extend_max()Design overview
---------------
A swap table slot can now hold a Redirect entry:Redirect: |----- physical
swp_entry_t-----|101|This stores a plain
swp_entry_tpointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.The lifecycle is:
Quoted 8 lines
Quoted 4 lines
1. swapon a ghost device — reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
If you think this data structure is superior to the xarray for dynamic swap address, it's a fairly simple change on top of what I had in the virtual swap RFC, no?
I appreciate the suggestion. However, the difference between the two approaches goes beyond the cluster allocation strategy — it's the overall architecture:
- Virtual swap introduces a mandatory, always-on vswap device (CONFIG_VSWAP). with per-cluster virtual_table arrays, xarray-backed dynamic clusters. per-CPU allocation caching.
- Ghost swap extends the existing swap device model with one new entry type (Redirect, 0b101) and one new flag (SWP_GHOST). It reuses swapon/swapoff, the existing allocator, and the swap cache essentially unchanged. No Kconfig option is needed.
These are two explorations of the same problem, while virtual swap goes far beyond the problem itself, the decoupling of physical swap and virtal zswap. We can discuss and choose one approach.
Quoted 7 lines
It can be grafted on top of what I had in a subsequent patch, or squashed into my first patch. If you have concerns with me implementing your idea here, I'd take your code if it proves to be superior.
Quoted 1 line
Quoted 8 lines
Quoted 4 lines
2. zswap writeback from ghost: when the zswap pool needs to evict,
zswap_writeback_ghost()allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.What if user swapoff the physical swapfile, whose slot is backing a redirection entry of a ghost swapfile, rather than a PTE. Has this been handled yet?
This is a gap in the current RFC. Both swapoff and physical-slot reclaim would need a reverse mappingfrom the physical slot back to the ghost entry to be handled robustly. This can be added in a future version if there is consensus that writeback-to-physical is a necessary part of the design.
I think the primary motivation for ghost swap is to enable zswap usage *without* a backing physical swap device at all — the "No backing
store required" mode described in the cover letter. In that mode,
writeback never happens (there is no physical device to write to), so the swapoff and reclaim questions do not arise. Ghost writeback to a physical device is an optional extension for users who want the safety net of disk-backed swap. Surely we can discuss and decide if we need add it or not.
Quoted 18 lines
Similarly, say the physical swapfile is full, and you attempt to reclaim a physical swap slot that is swap-cache-only (swap count = 0). Say this physical swap slot was allocated from a previous zswap writeback attempt, i.e it has a redirection entry in the ghost zswap swapfile associated with it. Did we handle it somehow?
My apologies if I missed it somehow.
Quoted 9 lines
3. Swap-in from ghost:
swap_read_folio()detects the ghost device (SWP_GHOST), resolves the Redirect to the physicalswp_entry_t, and forwards the read I/O to the physical device.4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (
redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.
Quoted 8 lines
Quoted 2 lines
5. When the physical slot is freed, the ghost slot is also freed, cascading through
swap_range_free().I don't get how *any* of this is simpler from what I had. If anything, it's more complicated. With a vswap device, you just allocate a physical swap slot (which you also did), change the backend, submit the IO (which you also did), and that's it. No need to juggle this
redirect_xaentry. What does thisredirect_xabuy us?
The redirect_xa persists the ghost→physical mapping across swap-cache folio teardown.
Ghost: redirect_xa[offset] = phys (only allocated when Redirect
is active) + swap_table[slot] = Redirect(phys) once the swap cache folio is removed
Quoted 8 lines
Not to mention now it entagles generic swap logic into backend-specific code, unnecessarily. And we have more state in the state machine to consider: With a generic vswap device, vswap -> disk swap directly is the same state as vswap -> written back zswap entry. With this zswap ghost swapfile design, you have to treat the latter separately, as there is a redirection entry involved, which means concurrent swap operations interact with it.
I'd argue the opposite. Ghost swap touches the generic swap paths in very few places:
- swap_read_folio: one SWP_GHOST branch → resolve Redirect
- __swap_cluster_free_entries: one Redirect check → cascade free
- __swap_cache_do_del_folio: one redirect_xa lookup → restore
- zswap_writeback_entry: one SWP_GHOST branch → ghost writeback
Ghost swap only affects behavior when a ghost device has been explicitly swapon'd, and even then only on the paths listed above.
This is different than virtual swap which is more ambitious. As I said, ghost swapfile focuses on the problem itself, and based on the swap table.
Quoted 18 lines
So it's more complicated, and achieve less - this is specific to zswap, whereas what I provide is something generic to swap infrastructure, which can be extended for future backends. I would understand if it resulted in less overhead (which was the argument for my pre-swap-table vswap design), but this seems like it's just virtual swap, with similar space overhead (technically a bit worse since you added
redirect_xaxarray to physical swap cluster too), and with more complications?Quoted 7 lines
Note:
------
Ghost swapfile is a proof of concept. Compared with the other patchset:
[RFC PATCH v2 0/7] mm, swap: Virtual Swap Space (Swap Table Edition), it explores a different approach to implementing virtual swap on top of the existing swap table infrastructure, with a simpler implementation.
Quoted 4 lines
You neglected to mention that this "simpler" implementation lacks a lot of design choices. Just on top of my head:
1. Certain optimizations for cases when vswap points to disk swap.
Ghost swap's Redirect entry directly encodes the target swp_entry_t, so the swapin path resolves it with a single cluster lookup — no extra indirection through a virtual_table. For the hot swapin path, this is an optimization, not a missing feature.
Quoted 3 lines
2. No reverse mapping, or at least some mechanisms to handle the case where you have to go from physical swap slots to the upper layer.
Acknowledged — a reverse mapping can be added in a future version if writeback-to-physical becomes a priority.
Quoted 5 lines
3. Charging behavior difference, and deduplication of metadata when we have multiple swap levels. (In fact, you seem to be double charging a writtenback entry here towards the swap counter, not just duplicating the metadata space)
There is no double-charging. A ghost slot is charged to nr_swap_pages when the ghost device is swapon'd. When zswap writeback allocates a physical slot via alloc_phys_swap_slot(), the physical device's own inuse_pages is incremented — but this is the physical device's independent accounting, not a double-charge to the ghost device. The two devices have separate si->inuse_pages. That said, we agree the swap counter semantics need to be clearly documented in the cover letter and will do so.
Quoted 5 lines
4. Infrastructure to make sure allocation from this swap device and allocation from the backing swap device does not invalidate each other's per-cpu allocation cache. You seem to just go for the slow path with
alloc_phys_swap_slot()for zswap writeback?
Yes, alloc_phys_swap_slot() uses the slow-path allocator (cluster_alloc_swap_entry), which is correct for this low-frequency operation. zswap writeback is not a hot allocation path — it runs from the zswap shrinker under memory pressure, typically hundreds to thousands of operations per second at most. The fast-path per-CPU caches remain dedicated to folio_alloc_swap(), which handles the high-frequency swapout path. There is no cache invalidation problem because the two paths use different allocation mechanisms on different devices.
Quoted 6 lines
It's fine to disagree with any or all of these design choices of mine
- I'm receptive to discussion. But it's misleading to say you have a simpler design here, IMHO. My design choices resulted from multiple thoughtful iterations (a lot of them could be found in my pre-swap-table version of vswap) and real production pains.
You're right that "simpler" needs qualification, and I'll update the cover letter accordingly. What I meant — and what I should have stated explicitly — is that ghost swap is less invasive to the existing swap infrastructure:
- It does not require a Kconfig option.
- It does not create a boot-time device that lives for the kernel's lifetime.
- It does not introduce a new per-cluster data structure (swap_cluster_info_dynamic / virtual_table).
- It does not add a separate per-CPU allocation cache.
- It does not split the swap entry namespace into "vswap" vs "physical" — a ghost device is just another swap device with one extra flag and one extra swap-table entry type.
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
Thanks
Baoquan
Nhat Pham
maintainerQuoted 13 lines · Baoquan He
Thanks for the thorough review, Nhat.
Quoted 5 lines
We don't want to put additional operational burden on users. Besides, expecting userspace to react to swap space being filled, especially under memory pressure (at which point userspace processes might be stalling already), is tricky.
The manual sysfs extension is the minimal viable interface for the RFC, not the final choice. But even in its current form, it is a dramatic improvement over the status quo.
Quoted 7 lines
Today, to use zswap you MUST provision a physical swap device of the same size as the swap capacity wanted — even though most of that disk space will never be touched. With ghost swap, you swapon a ghost device at whatever size you expect to need (e.g. 64 GB), and if your workload grows, you grow it via sysfs. That is already a step change in operational simplicity: no disk pre-provisioning, no wasted storage, and a one-line resize when needed.
If these are the only three requirements you have, in fact, you can already achieve something equivalent to a ghost swapfile: just put zswap on top of any of the in-memory swapfile (and perhaps disable writeback, although with careful limit setting it's almost impossible to trigger writeback with zswap in newer kernels anyway). I know this works, because I even run tests with zswap-on-top of zram :) You can swapon new swapfiles to support your "userspace-triggered" growth strategy.
Quoted 2 lines
Automatic growth can be considered later if it's really demanded.
It is really, really necessary. Please read my older virtual swap space patch series for why it's necessary (see [5]).
Quoted 6 lines
Quoted 5 lines
This is especially bad when redirection entry still eats up space in the upper level (ghost zswap) swapfile. You will run out of zswap space really fast under concurrent zswap store and zswap writeback.
Quoted 6 lines
I think there is a misunderstanding here. A Redirect entry does not consume an *additional* ghost slot. Before writeback, the ghost slot held a Pointer entry (zswap). After writeback, that same slot holds a Redirect entry (pointing to the physical device). It's a state transition — one slot, one entry — not an extra allocation. The ghost device's total capacity is unchanged by writeback activity.
No, what I meant is, after writeback, you still hold on to the old slot on the ghost device, AND a new slot on the physical device, correct?
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for
Quoted 7 lines
Quoted 1 line
This range seems a bit small, especially from something essentially fixed.
The 1TB limit comes from a conservative 64 MB sparse vmalloc reservation in RFC. It certainly can be redefined as a bigger one which is more reasonable in reality. For most real-world deployments today, 1 TB per ghost device — with support for multiple ghost devices — is more than sufficient.
I beg to differ. We already have multi-terrabyte scale machines in production in multiple places, and more and more overcommitting. At least make this configurable.
Quoted 41 lines
Quoted 24 lines
Quoted 24 lines
cluster_info[] structures are allocated only on demand as the device grows. The
cluster_infopointer never changes, so all existing accessors (__swap_offset_to_cluster,cluster_index) work unchanged.- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — backing device name
max (RW) — current max pages; writing a larger value triggers
swap_ghost_extend_max()Design overview
---------------
A swap table slot can now hold a Redirect entry:Redirect: |----- physical
swp_entry_t-----|101|This stores a plain
swp_entry_tpointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.The lifecycle is:
Quoted 8 lines
Quoted 4 lines
1. swapon a ghost device — reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
If you think this data structure is superior to the xarray for dynamic swap address, it's a fairly simple change on top of what I had in the virtual swap RFC, no?
I appreciate the suggestion. However, the difference between the two approaches goes beyond the cluster allocation strategy — it's the overall architecture:
- Virtual swap introduces a mandatory, always-on vswap device (
CONFIG_VSWAP). with per-clustervirtual_tablearrays, xarray-backed dynamic clusters. per-CPU allocation caching.
Quoted 8 lines
- Ghost swap extends the existing swap device model with one new entry type (Redirect, 0b101) and one new flag (
SWP_GHOST). It reuses swapon/swapoff, the existing allocator, and the swap cache essentially unchanged. NoKconfigoption is needed.These are two explorations of the same problem, while virtual swap goes far beyond the problem itself, the decoupling of physical swap and virtal zswap. We can discuss and choose one approach.
If my problems are real, why do we have to choose? If we go with my approach, all of our problems are solved. If we go with your approach, then your problem is solved, with a promise that my problems will be tackled in the future.
Quoted 13 lines
Quoted 13 lines
It can be grafted on top of what I had in a subsequent patch, or squashed into my first patch. If you have concerns with me implementing your idea here, I'd take your code if it proves to be superior.
Quoted 5 lines
2. zswap writeback from ghost: when the zswap pool needs to evict,
zswap_writeback_ghost()allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.
Quoted 8 lines
Quoted 3 lines
What if user swapoff the physical swapfile, whose slot is backing a redirection entry of a ghost swapfile, rather than a PTE. Has this been handled yet?
This is a gap in the current RFC. Both swapoff and physical-slot reclaim would need a reverse mappingfrom the physical slot back to the ghost entry to be handled robustly. This can be added in a future version
To be clear, this is not a feature gap. In the current form of your RFC, it is an actual bug: it breaks writeback *completely*. Swapoff (and reclaim of swap-cache-only physical slots) leaks ghost zswap swapfile slots that are still backed by these reclaimed slots.
I understand that this is just an RFC, or rather a proof-of-concept of what you want to propose. But you can't claim it both way. It is only simpler if you're dropping writeback altogether.
Quoted 2 lines
if there is consensus that writeback-to-physical is a necessary part of the design.
I'll not spend my time argue for the necessity of writeback. I've already written essays about it, but the fact that we're implementing new mechanism to trigger writeback (see [4]), optimization on the writeback path (see [1]), and the fact that even ZRAM has some writeback mechanism, mean multiple parties are interested. We need to make it better, not digging ourselves into a hole that prevent it from moving forwards.
Quoted 36 lines
I think the primary motivation for ghost swap is to enable zswap usage *without* a backing physical swap device at all — the "No backing
store required" mode described in the cover letter. In that mode,
writeback never happens (there is no physical device to write to), so the swapoff and reclaim questions do not arise. Ghost writeback to a physical device is an optional extension for users who want the safety net of disk-backed swap. Surely we can discuss and decide if we need add it or not.Quoted 18 lines
Similarly, say the physical swapfile is full, and you attempt to reclaim a physical swap slot that is swap-cache-only (swap count = 0). Say this physical swap slot was allocated from a previous zswap writeback attempt, i.e it has a redirection entry in the ghost zswap swapfile associated with it. Did we handle it somehow?
My apologies if I missed it somehow.
Quoted 9 lines
3. Swap-in from ghost:
swap_read_folio()detects the ghost device (SWP_GHOST), resolves the Redirect to the physicalswp_entry_t, and forwards the read I/O to the physical device.4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (
redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.Quoted 8 lines
Quoted 2 lines
5. When the physical slot is freed, the ghost slot is also freed, cascading through
swap_range_free().I don't get how *any* of this is simpler from what I had. If anything, it's more complicated. With a vswap device, you just allocate a physical swap slot (which you also did), change the backend, submit the IO (which you also did), and that's it. No need to juggle this
redirect_xaentry. What does thisredirect_xabuy us?
Quoted 6 lines
The
redirect_xapersists the ghost→physical mapping across swap-cache folio teardown.Ghost: redirect_xa[offset] = phys (only allocated when Redirect
is active) + swap_table[slot] = Redirect(phys) once the swap cache folio is removed
So it's basically the same thing as vswap's vtable, which houses the redirection, except:
1. It's not cluster-partitioned, so more lock contention.
2. It only holds the redirection temporarily. Which complicates the logic, because now you have to add more logic to handle the transition of the redirection entry to the ghost swapfile's swap table?
So it's potentially less efficient, and logic-wise, just as if not more complicated?
Quoted 10 lines
Quoted 9 lines
Not to mention now it entagles generic swap logic into backend-specific code, unnecessarily. And we have more state in the state machine to consider: With a generic vswap device, vswap -> disk swap directly is the same state as vswap -> written back zswap entry. With this zswap ghost swapfile design, you have to treat the latter separately, as there is a redirection entry involved, which means concurrent swap operations interact with it.
Quoted 7 lines
I'd argue the opposite. Ghost swap touches the generic swap paths in very few places:
-
swap_read_folio: oneSWP_GHOSTbranch → resolve Redirect
-__swap_cluster_free_entries: one Redirect check → cascade free
-__swap_cache_do_del_folio: oneredirect_xalookup → restore
-zswap_writeback_entry: oneSWP_GHOSTbranch → ghost writeback
That's because you just don't handle all the cases required to make it correct (rmap) and efficient (per-device per-CPU allocation caching) :)
Quoted 7 lines
Ghost swap only affects behavior when a ghost device has been explicitly swapon'd, and even then only on the paths listed above.
This is different than virtual swap which is more ambitious. As I said, ghost swapfile focuses on the problem itself, and based on the swap table.
I'm considering adding a sysctl knob to enable/disable vswap at runtime in the next version of my patch series. This is trivial, as even with CONFIG_VSWAP, both the new and the old code paths already co-exist (as I still keep the old code path as a potential fallback for the cases when vswap slot allocation fails for whatever reason).
That means that you can get functionally the same thing here if you want - the old behavior, even with CONFIG_VSWAP.
Quoted 7 lines
Quoted 4 lines
2. No reverse mapping, or at least some mechanisms to handle the case where you have to go from physical swap slots to the upper layer.
Acknowledged — a reverse mapping can be added in a future version if writeback-to-physical becomes a priority.
More like, if you want writeback-to-physical *correct* at all.
Quoted 12 lines
Quoted 11 lines
3. Charging behavior difference, and deduplication of metadata when we have multiple swap levels. (In fact, you seem to be double charging a writtenback entry here towards the swap counter, not just duplicating the metadata space)
4. Infrastructure to make sure allocation from this swap device and allocation from the backing swap device does not invalidate each other's per-cpu allocation cache. You seem to just go for the slow path with
alloc_phys_swap_slot()for zswap writeback?
Quoted 9 lines
Yes,
alloc_phys_swap_slot()uses the slow-path allocator (cluster_alloc_swap_entry), which is correct for this low-frequency operation. zswap writeback is not a hot allocation path — it runs from the zswap shrinker under memory pressure, typically hundreds to thousands of operations per second at most. The fast-path per-CPU caches remain dedicated tofolio_alloc_swap(), which handles the high-frequency swapout path. There is no cache invalidation problem because the two paths use different allocation mechanisms on different devices.
I'm nooot entirely sure about this. Zswap writeback can be quite a hot path - there is work on optimizing it (see [1] for a recent attempt to limit the expensive rstat flushing here - and this flushing is *per shrinker iteration*, whereas the physical device allocation will be *per writeback*, which is much, much more frequent).
Quoted 7 lines
Quoted 6 lines
It's fine to disagree with any or all of these design choices of mine
- I'm receptive to discussion. But it's misleading to say you have a simpler design here, IMHO. My design choices resulted from multiple thoughtful iterations (a lot of them could be found in my pre-swap-table version of vswap) and real production pains.
Quoted 8 lines
You're right that "simpler" needs qualification, and I'll update the cover letter accordingly. What I meant — and what I should have stated explicitly — is that ghost swap is less invasive to the existing swap infrastructure:
- It does not require a
Kconfigoption.
- It does not create a boot-time device that lives for the kernel's lifetime.
I don't think these 2 are significant.
Quoted 2 lines
- It does not introduce a new per-cluster data structure (
swap_cluster_info_dynamic/virtual_table).
Instead, you add a redirect_xa xarray, which, as I noted above, plays a *very* similar role (store the redirection), except a bit worse performance-wise and more complicated.
Again, unless you're willing to drop writeback :)
Quoted 1 line
- It does not add a separate per-CPU allocation cache.
This is *by design*, because I want to avoid writeback invalidating the main allocation path.
If the argument is that it is not a problem, then I'm happy to drop it. But I don't think that's the case - in fact, as Kairui have pointed out, what we're moving towards is *every* swap device having its own per-CPU allocation cache (see [2]). It's really necessary, not just for vswap, but also for swap tiering (see [3]). So i don't think the argument (that we don't need separate caching) here is correct, and it's also immaterial soon enough (once we have generic per-device per-CPU allocation cache I'll drop the vswap-specific one).
Quoted 3 lines
- It does not split the swap entry namespace into "vswap" vs "physical" — a ghost device is just another swap device with one extra flag and one extra swap-table entry type.
Actually, if we're to take in your full design, it's *two* entry types. It's the redirection entry (if you want to support writeback) + the Pointer entry (for your zswap "optimization").
Quoted 8 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
Quoted 4 lines
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/all/CAMgjq7BhOn48xEyC=2j837R7qddfjeBVHMiRqdx8no4ZEBpBLg@mail.gmail.com/
[3]: https://lore.kernel.org/linux-mm/ah0BfRFv285gWph8@yjaykim-PowerEdge-T330/
[4]: https://lore.kernel.org/all/[email protected]/
[5]: https://lore.kernel.org/all/[email protected]/
Chris Li
maintainerQuoted 408 lines · Nhat Pham
Quoted 13 lines · Baoquan He
Thanks for the thorough review, Nhat.
Quoted 5 lines
We don't want to put additional operational burden on users. Besides, expecting userspace to react to swap space being filled, especially under memory pressure (at which point userspace processes might be stalling already), is tricky.
The manual sysfs extension is the minimal viable interface for the RFC, not the final choice. But even in its current form, it is a dramatic improvement over the status quo.
Quoted 7 lines
Today, to use zswap you MUST provision a physical swap device of the same size as the swap capacity wanted — even though most of that disk space will never be touched. With ghost swap, you swapon a ghost device at whatever size you expect to need (e.g. 64 GB), and if your workload grows, you grow it via sysfs. That is already a step change in operational simplicity: no disk pre-provisioning, no wasted storage, and a one-line resize when needed.
If these are the only three requirements you have, in fact, you can already achieve something equivalent to a ghost swapfile: just put zswap on top of any of the in-memory swapfile (and perhaps disable writeback, although with careful limit setting it's almost impossible to trigger writeback with zswap in newer kernels anyway). I know this works, because I even run tests with zswap-on-top of zram :) You can swapon new swapfiles to support your "userspace-triggered" growth strategy.
Quoted 2 lines
Automatic growth can be considered later if it's really demanded.
It is really, really necessary. Please read my older virtual swap space patch series for why it's necessary (see [5]).
Quoted 6 lines
Quoted 5 lines
This is especially bad when redirection entry still eats up space in the upper level (ghost zswap) swapfile. You will run out of zswap space really fast under concurrent zswap store and zswap writeback.
Quoted 6 lines
I think there is a misunderstanding here. A Redirect entry does not consume an *additional* ghost slot. Before writeback, the ghost slot held a Pointer entry (zswap). After writeback, that same slot holds a Redirect entry (pointing to the physical device). It's a state transition — one slot, one entry — not an extra allocation. The ghost device's total capacity is unchanged by writeback activity.
No, what I meant is, after writeback, you still hold on to the old slot on the ghost device, AND a new slot on the physical device, correct?
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for
Quoted 7 lines
Quoted 1 line
This range seems a bit small, especially from something essentially fixed.
The 1TB limit comes from a conservative 64 MB sparse vmalloc reservation in RFC. It certainly can be redefined as a bigger one which is more reasonable in reality. For most real-world deployments today, 1 TB per ghost device — with support for multiple ghost devices — is more than sufficient.
I beg to differ. We already have multi-terrabyte scale machines in production in multiple places, and more and more overcommitting. At least make this configurable.
Quoted 41 lines
Quoted 24 lines
Quoted 24 lines
cluster_info[] structures are allocated only on demand as the device grows. The
cluster_infopointer never changes, so all existing accessors (__swap_offset_to_cluster,cluster_index) work unchanged.- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
path (RO) — backing device name
max (RW) — current max pages; writing a larger value triggers
swap_ghost_extend_max()Design overview
---------------
A swap table slot can now hold a Redirect entry:Redirect: |----- physical
swp_entry_t-----|101|This stores a plain
swp_entry_tpointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.The lifecycle is:
Quoted 8 lines
Quoted 4 lines
1. swapon a ghost device — reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
If you think this data structure is superior to the xarray for dynamic swap address, it's a fairly simple change on top of what I had in the virtual swap RFC, no?
I appreciate the suggestion. However, the difference between the two approaches goes beyond the cluster allocation strategy — it's the overall architecture:
- Virtual swap introduces a mandatory, always-on vswap device (
CONFIG_VSWAP). with per-clustervirtual_tablearrays, xarray-backed dynamic clusters. per-CPU allocation caching.Quoted 8 lines
- Ghost swap extends the existing swap device model with one new entry type (Redirect, 0b101) and one new flag (
SWP_GHOST). It reuses swapon/swapoff, the existing allocator, and the swap cache essentially unchanged. NoKconfigoption is needed.These are two explorations of the same problem, while virtual swap goes far beyond the problem itself, the decoupling of physical swap and virtal zswap. We can discuss and choose one approach.
If my problems are real, why do we have to choose? If we go with my approach, all of our problems are solved. If we go with your approach, then your problem is solved, with a promise that my problems will be tackled in the future.
Quoted 13 lines
Quoted 13 lines
It can be grafted on top of what I had in a subsequent patch, or squashed into my first patch. If you have concerns with me implementing your idea here, I'd take your code if it proves to be superior.
Quoted 5 lines
2. zswap writeback from ghost: when the zswap pool needs to evict,
zswap_writeback_ghost()allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.Quoted 8 lines
Quoted 3 lines
What if user swapoff the physical swapfile, whose slot is backing a redirection entry of a ghost swapfile, rather than a PTE. Has this been handled yet?
This is a gap in the current RFC. Both swapoff and physical-slot reclaim would need a reverse mappingfrom the physical slot back to the ghost entry to be handled robustly. This can be added in a future version
To be clear, this is not a feature gap. In the current form of your RFC, it is an actual bug: it breaks writeback *completely*. Swapoff (and reclaim of swap-cache-only physical slots) leaks ghost zswap swapfile slots that are still backed by these reclaimed slots.
I understand that this is just an RFC, or rather a proof-of-concept of what you want to propose. But you can't claim it both way. It is only simpler if you're dropping writeback altogether.
Quoted 2 lines
if there is consensus that writeback-to-physical is a necessary part of the design.
I'll not spend my time argue for the necessity of writeback. I've already written essays about it, but the fact that we're implementing new mechanism to trigger writeback (see [4]), optimization on the writeback path (see [1]), and the fact that even ZRAM has some writeback mechanism, mean multiple parties are interested. We need to make it better, not digging ourselves into a hole that prevent it from moving forwards.
Quoted 36 lines
I think the primary motivation for ghost swap is to enable zswap usage *without* a backing physical swap device at all — the "No backing
store required" mode described in the cover letter. In that mode,
writeback never happens (there is no physical device to write to), so the swapoff and reclaim questions do not arise. Ghost writeback to a physical device is an optional extension for users who want the safety net of disk-backed swap. Surely we can discuss and decide if we need add it or not.Quoted 18 lines
Similarly, say the physical swapfile is full, and you attempt to reclaim a physical swap slot that is swap-cache-only (swap count = 0). Say this physical swap slot was allocated from a previous zswap writeback attempt, i.e it has a redirection entry in the ghost zswap swapfile associated with it. Did we handle it somehow?
My apologies if I missed it somehow.
Quoted 9 lines
3. Swap-in from ghost:
swap_read_folio()detects the ghost device (SWP_GHOST), resolves the Redirect to the physicalswp_entry_t, and forwards the read I/O to the physical device.4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (
redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.Quoted 8 lines
Quoted 2 lines
5. When the physical slot is freed, the ghost slot is also freed, cascading through
swap_range_free().I don't get how *any* of this is simpler from what I had. If anything, it's more complicated. With a vswap device, you just allocate a physical swap slot (which you also did), change the backend, submit the IO (which you also did), and that's it. No need to juggle this
redirect_xaentry. What does thisredirect_xabuy us?Quoted 6 lines
The
redirect_xapersists the ghost→physical mapping across swap-cache folio teardown.Ghost: redirect_xa[offset] = phys (only allocated when Redirect
is active) + swap_table[slot] = Redirect(phys) once the swap cache folio is removedSo it's basically the same thing as vswap's vtable, which houses the redirection, except:
1. It's not cluster-partitioned, so more lock contention.
2. It only holds the redirection temporarily. Which complicates the logic, because now you have to add more logic to handle the transition of the redirection entry to the ghost swapfile's swap table?
So it's potentially less efficient, and logic-wise, just as if not more complicated?
Quoted 10 lines
Quoted 9 lines
Not to mention now it entagles generic swap logic into backend-specific code, unnecessarily. And we have more state in the state machine to consider: With a generic vswap device, vswap -> disk swap directly is the same state as vswap -> written back zswap entry. With this zswap ghost swapfile design, you have to treat the latter separately, as there is a redirection entry involved, which means concurrent swap operations interact with it.
Quoted 7 lines
I'd argue the opposite. Ghost swap touches the generic swap paths in very few places:
-
swap_read_folio: oneSWP_GHOSTbranch → resolve Redirect
-__swap_cluster_free_entries: one Redirect check → cascade free
-__swap_cache_do_del_folio: oneredirect_xalookup → restore
-zswap_writeback_entry: oneSWP_GHOSTbranch → ghost writebackThat's because you just don't handle all the cases required to make it correct (rmap) and efficient (per-device per-CPU allocation caching) :)
Quoted 7 lines
Ghost swap only affects behavior when a ghost device has been explicitly swapon'd, and even then only on the paths listed above.
This is different than virtual swap which is more ambitious. As I said, ghost swapfile focuses on the problem itself, and based on the swap table.
I'm considering adding a sysctl knob to enable/disable vswap at runtime in the next version of my patch series. This is trivial, as even with
CONFIG_VSWAP, both the new and the old code paths already co-exist (as I still keep the old code path as a potential fallback for the cases when vswap slot allocation fails for whatever reason).That means that you can get functionally the same thing here if you want - the old behavior, even with
CONFIG_VSWAP.Quoted 7 lines
Quoted 4 lines
2. No reverse mapping, or at least some mechanisms to handle the case where you have to go from physical swap slots to the upper layer.
Acknowledged — a reverse mapping can be added in a future version if writeback-to-physical becomes a priority.
More like, if you want writeback-to-physical *correct* at all.
Quoted 12 lines
Quoted 11 lines
3. Charging behavior difference, and deduplication of metadata when we have multiple swap levels. (In fact, you seem to be double charging a writtenback entry here towards the swap counter, not just duplicating the metadata space)
4. Infrastructure to make sure allocation from this swap device and allocation from the backing swap device does not invalidate each other's per-cpu allocation cache. You seem to just go for the slow path with
alloc_phys_swap_slot()for zswap writeback?Quoted 9 lines
Yes,
alloc_phys_swap_slot()uses the slow-path allocator (cluster_alloc_swap_entry), which is correct for this low-frequency operation. zswap writeback is not a hot allocation path — it runs from the zswap shrinker under memory pressure, typically hundreds to thousands of operations per second at most. The fast-path per-CPU caches remain dedicated tofolio_alloc_swap(), which handles the high-frequency swapout path. There is no cache invalidation problem because the two paths use different allocation mechanisms on different devices.I'm nooot entirely sure about this. Zswap writeback can be quite a hot path - there is work on optimizing it (see [1] for a recent attempt to limit the expensive rstat flushing here - and this flushing is *per shrinker iteration*, whereas the physical device allocation will be *per writeback*, which is much, much more frequent).
Quoted 7 lines
Quoted 6 lines
It's fine to disagree with any or all of these design choices of mine
- I'm receptive to discussion. But it's misleading to say you have a simpler design here, IMHO. My design choices resulted from multiple thoughtful iterations (a lot of them could be found in my pre-swap-table version of vswap) and real production pains.Quoted 8 lines
You're right that "simpler" needs qualification, and I'll update the cover letter accordingly. What I meant — and what I should have stated explicitly — is that ghost swap is less invasive to the existing swap infrastructure:
- It does not require a
Kconfigoption.
- It does not create a boot-time device that lives for the kernel's lifetime.I don't think these 2 are significant.
Quoted 2 lines
- It does not introduce a new per-cluster data structure (
swap_cluster_info_dynamic/virtual_table).Instead, you add a
redirect_xaxarray, which, as I noted above, plays a *very* similar role (store the redirection), except a bit worse performance-wise and more complicated.Again, unless you're willing to drop writeback :)
Quoted 1 line
- It does not add a separate per-CPU allocation cache.
This is *by design*, because I want to avoid writeback invalidating the main allocation path.
If the argument is that it is not a problem, then I'm happy to drop it. But I don't think that's the case - in fact, as Kairui have pointed out, what we're moving towards is *every* swap device having its own per-CPU allocation cache (see [2]). It's really necessary, not just for vswap, but also for swap tiering (see [3]). So i don't think the argument (that we don't need separate caching) here is correct, and it's also immaterial soon enough (once we have generic per-device per-CPU allocation cache I'll drop the vswap-specific one).
Quoted 3 lines
- It does not split the swap entry namespace into "vswap" vs "physical" — a ghost device is just another swap device with one extra flag and one extra swap-table entry type.
Actually, if we're to take in your full design, it's *two* entry types. It's the redirection entry (if you want to support writeback) + the Pointer entry (for your zswap "optimization").
Quoted 8 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
Quoted 4 lines
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Hi Nhat,
Please read my above email. I agree with you that the ghost swapfile writeback feature seems a bit premature. I suggested dropping the ghost swapfile writeback from this series and focusing on generic swapfile growing, because the swap device can actually grow outside of the virtual context as well.
Growing a swap device does not mean it has to have another xarray or indirection sitting in front of it. That is what I want to see happening. It is actually more efficient without it.
As to your VS series, I have already made my comments on earlier series and I am not going to repeat myself too much. I suggest taking a more incremental approach rather than trying to solve all the problems at once. Another thing is that, is there any reason the VS can't be done on a per swap file using the swap ops approach? Assume you can modify the swap ops to fit your need. That way you don't force zram to take your VS layer as an additional penalty.
Given the choice between two approaches, if everything else is equal, one forces an extra layer while the other does not. I would pick the later one any time. Again if one extra layer is compile time, the other one extra layer is run time. If everything else is equal, I would prefer the runtime layer.
Thanks
Chris
Baoquan He
reviewerHi Nhat,
Quoted 1 line · Nhat Pham
On Thu, Jul 9, 2026 at 4:39 AM Baoquan He <[email protected]> wrote:
...snip...
Sorry, I won't reply to each comment individually. I'll just leave a summary reply at the end.
Quoted 56 lines
Quoted 11 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Before I get into the technical discussion, I want to address something you raised at the end of your reply — the concern that I'm working against you rather than with you. Our paths in MM simply haven't crossed much until now. I can't think of reason why I would want to target you or your work.
I respect the work you've put into VSS. I did check it. Honestly I am not fan of it. In your v2: 2244 insertions, 250 deletions, 15 files touched, a new 455-line header (vswap.h). While swap subsystem has just absorbed the swap table series. Then another 2200 lines of architectural restructuring on top is added. Not sure if anybody raise concern about your solution, or anybody suggests other directions.
I posted ghost swap because I saw a concrete, narrow problem that I believed could be solved with a small change. It makes decoupling and dynamic growth with the minimum possible mechanism. Writeback is explicitly deferred
— not because it's unimportant, but because it's a separate problem that
shouldn't block the common case. With my shallow knowledge, it's optimal solution to the encountered problem. If you agree, you can change to Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Thanks
Baoquan
Baoquan He
reviewerQuoted 81 lines · Baoquan He
Hi Nhat,
Quoted 1 line · Nhat Pham
On Thu, Jul 9, 2026 at 4:39 AM Baoquan He <[email protected]> wrote:
...snip...
Sorry, I won't reply to each comment individually. I'll just leave a summary reply at the end.
Quoted 56 lines
Quoted 11 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Before I get into the technical discussion, I want to address something you raised at the end of your reply — the concern that I'm working against you rather than with you. Our paths in MM simply haven't crossed much until now. I can't think of reason why I would want to target you or your work.
I respect the work you've put into VSS. I did check it. Honestly I am not fan of it. In your v2: 2244 insertions, 250 deletions, 15 files touched, a new 455-line header (
vswap.h). While swap subsystem has just absorbed the swap table series. Then another 2200 lines of architectural restructuring on top is added. Not sure if anybody raise concern about your solution, or anybody suggests other directions.
Quoted 6 lines
I posted ghost swap because I saw a concrete, narrow problem that I believed could be solved with a small change. It makes decoupling and dynamic growth with the minimum possible mechanism. Writeback is explicitly deferred
— not because it's unimportant, but because it's a separate problem that
shouldn't block the common case. With my shallow knowledge, it's optimal solution to the encountered problem. If you agree, you can change to
Part of above sentence is missing. I meant if you agree on the ghost solution, you can change to take its way and continue, just add me to CC. Or we can work together.
Quoted 5 lines
Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Thanks
Baoquan
Nhat Pham
maintainerQuoted 76 lines · Baoquan He
Quoted 74 lines · Baoquan He
Hi Nhat,
Quoted 1 line · Nhat Pham
On Thu, Jul 9, 2026 at 4:39 AM Baoquan He <[email protected]> wrote:
...snip...
Sorry, I won't reply to each comment individually. I'll just leave a summary reply at the end.
Quoted 56 lines
Quoted 11 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Before I get into the technical discussion, I want to address something you raised at the end of your reply — the concern that I'm working against you rather than with you. Our paths in MM simply haven't crossed much until now. I can't think of reason why I would want to target you or your work.
Quoted 6 lines
Quoted 6 lines
I respect the work you've put into VSS. I did check it. Honestly I am not fan of it. In your v2: 2244 insertions, 250 deletions, 15 files touched, a new 455-line header (
vswap.h). While swap subsystem has just absorbed the swap table series. Then another 2200 lines of architectural restructuring on top is added. Not sure if anybody raise concern about your solution, or anybody suggests other directions.
Yes, it's a big patch series. But as you pointed out earlier, it solves a larger problem with a single coherent design :)
Quoted 7 lines
Quoted 7 lines
I posted ghost swap because I saw a concrete, narrow problem that I believed could be solved with a small change. It makes decoupling and dynamic growth with the minimum possible mechanism. Writeback is explicitly deferred
— not because it's unimportant, but because it's a separate problem that
shouldn't block the common case. With my shallow knowledge, it's optimal solution to the encountered problem. If you agree, you can change to
The problem is not the lack of writeback support per se. I understand that sometimes pieces of a design can be deferred to a follow-up patch series.
I'm more concerned that the ghost direction so far does not seriously take it into account in the design at all, which might make it either impossible or very hard to redesign around down the line. I don't want to rip a huge chunk of it out down the line later - we just avoided it with the swap table with Kairui's proposal.
Quoted 4 lines
Part of above sentence is missing. I meant if you agree on the ghost solution, you can change to take its way and continue, just add me to CC. Or we can work together.
How about this - let's solve one piece of the puzzle together, one at a time.
First, your optimization patch series. Have you taken a look at my proposal ([1])? I believe that design should be cleaner, while still allowing you to put it all you want to get (elimination of xarray being the chief benefit), without requiring you to modify swap cache and swap count operations (since you don't have to move the metadata in and out of struct zswap_entry). It does require a bit more code to dynamically allocated the backend array, but should be trivial IMHO.
[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Next, if you just want pure ghost swapfile without writeback support, maybe something similar to the first 2 patches in my RFC? i.e up to this patch:
https://lore.kernel.org/all/[email protected]/
I'm sure you could find issues/bugs in it too with some extensive testing (a lot of my performance testing is done on the entire series as a whole), but from a design point of view it should achieve what us both want, no? It's literally just a ghost swap device - most of the other design elements of vswap (the new charging behavior, rmap, support for writeback) comes in latter patches. I think the difference is:
1. It's transparently dynamic, whereas in your RFC it's driven by a userspace knob. Do you think there are inherent values in a userspace knob?
2. The address space can also shrink when swap usage drops at runtime.
3. The dynamic address space is backed by an xarray. Note that it's structured differently than zswap's one (it's an xarray pointing to clusters). If you have rationales or numbers to show that vmalloc array is superior (while still support the kernel-driven expansion and shrink), please let me know.
If you have no faith in me and do not like my code, you can take the code as a starting point, or just the idea, and drive it yourself. Or let me know if you're unhappy with any piece of it - we can discuss further.
Quoted 3 lines
Quoted 3 lines
Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Look man, I care about the problems being solved and I'm a sucker for good ideas, but I'm not married to anything, be it my code or my ideas.
I have literally dropped my old code and adopted Kairui's suggestions after playing with it and liking it, to make sure every party's concern is met.
If you have concerns, please let me know, or keep sending code if you believe you can get it done better than I do :) All I'm asking is you take my concerns into account in your design.
Chris Li
maintainerQuoted 121 lines · Nhat Pham
Quoted 76 lines · Baoquan He
Quoted 74 lines · Baoquan He
Hi Nhat,
Quoted 1 line · Nhat Pham
On Thu, Jul 9, 2026 at 4:39 AM Baoquan He <[email protected]> wrote:
...snip...
Sorry, I won't reply to each comment individually. I'll just leave a summary reply at the end.
Quoted 56 lines
Quoted 11 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Before I get into the technical discussion, I want to address something you raised at the end of your reply — the concern that I'm working against you rather than with you. Our paths in MM simply haven't crossed much until now. I can't think of reason why I would want to target you or your work.
Quoted 6 lines
Quoted 6 lines
I respect the work you've put into VSS. I did check it. Honestly I am not fan of it. In your v2: 2244 insertions, 250 deletions, 15 files touched, a new 455-line header (
vswap.h). While swap subsystem has just absorbed the swap table series. Then another 2200 lines of architectural restructuring on top is added. Not sure if anybody raise concern about your solution, or anybody suggests other directions.Yes, it's a big patch series. But as you pointed out earlier, it solves a larger problem with a single coherent design :)
Quoted 7 lines
Quoted 7 lines
I posted ghost swap because I saw a concrete, narrow problem that I believed could be solved with a small change. It makes decoupling and dynamic growth with the minimum possible mechanism. Writeback is explicitly deferred
— not because it's unimportant, but because it's a separate problem that
shouldn't block the common case. With my shallow knowledge, it's optimal solution to the encountered problem. If you agree, you can change toThe problem is not the lack of writeback support per se. I understand that sometimes pieces of a design can be deferred to a follow-up patch series.
I'm more concerned that the ghost direction so far does not seriously take it into account in the design at all, which might make it either impossible or very hard to redesign around down the line. I don't want to rip a huge chunk of it out down the line later - we just avoided it with the swap table with Kairui's proposal.
Quoted 4 lines
Part of above sentence is missing. I meant if you agree on the ghost solution, you can change to take its way and continue, just add me to CC. Or we can work together.
How about this - let's solve one piece of the puzzle together, one at a time.
First, your optimization patch series. Have you taken a look at my proposal ([1])? I believe that design should be cleaner, while still allowing you to put it all you want to get (elimination of xarray being the chief benefit), without requiring you to modify swap cache and swap count operations (since you don't have to move the metadata in and out of struct
zswap_entry). It does require a bit more code to dynamically allocated the backend array, but should be trivial IMHO.
Quoted 1 line
[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Hi Nhat,
Sorry for the late reply. I've been busy and just saw this. First impression looks promising, it is better than any previous version of VS. Thanks for incorporating Kairui's feedback. Please give me some time to sleep on it. I will get back to you on that.
Quoted 39 lines
Next, if you just want pure ghost swapfile without writeback support, maybe something similar to the first 2 patches in my RFC? i.e up to this patch:
https://lore.kernel.org/all/[email protected]/
I'm sure you could find issues/bugs in it too with some extensive testing (a lot of my performance testing is done on the entire series as a whole), but from a design point of view it should achieve what us both want, no? It's literally just a ghost swap device - most of the other design elements of vswap (the new charging behavior, rmap, support for writeback) comes in latter patches. I think the difference is:
1. It's transparently dynamic, whereas in your RFC it's driven by a userspace knob. Do you think there are inherent values in a userspace knob?
2. The address space can also shrink when swap usage drops at runtime.
3. The dynamic address space is backed by an xarray. Note that it's structured differently than zswap's one (it's an xarray pointing to clusters). If you have rationales or numbers to show that vmalloc array is superior (while still support the kernel-driven expansion and shrink), please let me know.
If you have no faith in me and do not like my code, you can take the code as a starting point, or just the idea, and drive it yourself. Or let me know if you're unhappy with any piece of it - we can discuss further.
Quoted 3 lines
Quoted 3 lines
Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Look man, I care about the problems being solved and I'm a sucker for good ideas, but I'm not married to anything, be it my code or my ideas.
Quoted 5 lines
I have literally dropped my old code and adopted Kairui's suggestions after playing with it and liking it, to make sure every party's concern is met.
If you have concerns, please let me know, or keep sending code if you
May I add one wishlist item related to ghost swapfile here? I will reply to your VS thread for other feedback.
For the ghost swapfile usage case (no writeback). Can VS support this usage case while maintaining per-swap-slot metadata usage comparable to the ghost swapfile implementation? If we have to put a number to it, let's say the increase in metadata is less than 1 byte per swap slot compared to the ghost swapfile implementation.
We have large deployments of ghost swapfile in the fleet. I want an upstream aligned solution that does not increase the existing memory overhead compared to the current ghost swapfile deployment, for that usage. As you know, memory pricing and optimization pressure are high. The ghost swapfile patch itself is relatively simple. If VS can address this usage case, it will remove a lot of incentive to explore ghost swapfile-like solutions.
Thanks
Chris
Nhat Pham
maintainerQuoted 123 lines · Chris Li
Quoted 121 lines · Nhat Pham
Quoted 76 lines · Baoquan He
Quoted 74 lines · Baoquan He
Hi Nhat,
Quoted 1 line · Nhat Pham
On Thu, Jul 9, 2026 at 4:39 AM Baoquan He <[email protected]> wrote:
...snip...
Sorry, I won't reply to each comment individually. I'll just leave a summary reply at the end.
Quoted 56 lines
Quoted 11 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Before I get into the technical discussion, I want to address something you raised at the end of your reply — the concern that I'm working against you rather than with you. Our paths in MM simply haven't crossed much until now. I can't think of reason why I would want to target you or your work.
Quoted 6 lines
Quoted 6 lines
I respect the work you've put into VSS. I did check it. Honestly I am not fan of it. In your v2: 2244 insertions, 250 deletions, 15 files touched, a new 455-line header (
vswap.h). While swap subsystem has just absorbed the swap table series. Then another 2200 lines of architectural restructuring on top is added. Not sure if anybody raise concern about your solution, or anybody suggests other directions.Yes, it's a big patch series. But as you pointed out earlier, it solves a larger problem with a single coherent design :)
Quoted 7 lines
Quoted 7 lines
I posted ghost swap because I saw a concrete, narrow problem that I believed could be solved with a small change. It makes decoupling and dynamic growth with the minimum possible mechanism. Writeback is explicitly deferred
— not because it's unimportant, but because it's a separate problem that
shouldn't block the common case. With my shallow knowledge, it's optimal solution to the encountered problem. If you agree, you can change toThe problem is not the lack of writeback support per se. I understand that sometimes pieces of a design can be deferred to a follow-up patch series.
I'm more concerned that the ghost direction so far does not seriously take it into account in the design at all, which might make it either impossible or very hard to redesign around down the line. I don't want to rip a huge chunk of it out down the line later - we just avoided it with the swap table with Kairui's proposal.
Quoted 4 lines
Part of above sentence is missing. I meant if you agree on the ghost solution, you can change to take its way and continue, just add me to CC. Or we can work together.
How about this - let's solve one piece of the puzzle together, one at a time.
First, your optimization patch series. Have you taken a look at my proposal ([1])? I believe that design should be cleaner, while still allowing you to put it all you want to get (elimination of xarray being the chief benefit), without requiring you to modify swap cache and swap count operations (since you don't have to move the metadata in and out of struct
zswap_entry). It does require a bit more code to dynamically allocated the backend array, but should be trivial IMHO.
Quoted 8 lines
Quoted 1 line
[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Hi Nhat,
Sorry for the late reply. I've been busy and just saw this. First impression looks promising, it is better than any previous version of VS. Thanks for incorporating Kairui's feedback. Please give me some time to sleep on it. I will get back to you on that.
Please take a look. I was waiting for your response as well :)
The patch series is large, yes, but it's an RFC meant to show case the final end design.
Quoted 55 lines
Quoted 40 lines
Next, if you just want pure ghost swapfile without writeback support, maybe something similar to the first 2 patches in my RFC? i.e up to this patch:
https://lore.kernel.org/all/[email protected]/
I'm sure you could find issues/bugs in it too with some extensive testing (a lot of my performance testing is done on the entire series as a whole), but from a design point of view it should achieve what us both want, no? It's literally just a ghost swap device - most of the other design elements of vswap (the new charging behavior, rmap, support for writeback) comes in latter patches. I think the difference is:
1. It's transparently dynamic, whereas in your RFC it's driven by a userspace knob. Do you think there are inherent values in a userspace knob?
2. The address space can also shrink when swap usage drops at runtime.
3. The dynamic address space is backed by an xarray. Note that it's structured differently than zswap's one (it's an xarray pointing to clusters). If you have rationales or numbers to show that vmalloc array is superior (while still support the kernel-driven expansion and shrink), please let me know.
If you have no faith in me and do not like my code, you can take the code as a starting point, or just the idea, and drive it yourself. Or let me know if you're unhappy with any piece of it - we can discuss further.
Quoted 3 lines
Quoted 3 lines
Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Look man, I care about the problems being solved and I'm a sucker for good ideas, but I'm not married to anything, be it my code or my ideas.
Quoted 5 lines
I have literally dropped my old code and adopted Kairui's suggestions after playing with it and liking it, to make sure every party's concern is met.
If you have concerns, please let me know, or keep sending code if you
May I add one wishlist item related to ghost swapfile here? I will reply to your VS thread for other feedback.
For the ghost swapfile usage case (no writeback). Can VS support this usage case while maintaining per-swap-slot metadata usage comparable to the ghost swapfile implementation? If we have to put a number to it, let's say the increase in metadata is less than 1 byte per swap slot compared to the ghost swapfile implementation.
Quoted 7 lines
We have large deployments of ghost swapfile in the fleet. I want an upstream aligned solution that does not increase the existing memory overhead compared to the current ghost swapfile deployment, for that usage. As you know, memory pricing and optimization pressure are high. The ghost swapfile patch itself is relatively simple. If VS can address this usage case, it will remove a lot of incentive to explore ghost swapfile-like solutions.
Hi Chris. I have some thoughts on this - let me know what you think.
I understand the memory overhead discussion here. In the case where vswap slot is backed by a real swapfile, I have not figured out a way to make the overhead disappear yet. The damn rmap is very annoying - I have some strategies to get around it for certain operations, but none of it can be done without a full fledged patch series of its own.
However, with the ghost zswap swapfile backend specifically, I think the overhead is going to be negligible compared to the status quo upstream (things might be different internally at Google - please let me know!). This is because the main remaining per-slot overhead comes in the form of one per-cluster array to store the "backend", here it's going to store struct zswap_entry pointers. This is not extra overhead though, because it basically replaces the xarray (that you added all those years ago)!
The rest is small stuff here and there that is not O(slot) but rather O(cluster). For example one struct rcu_head per cluster, xarray overhead for the dynamicization - note that this xarray manages cluster, not slot. I think that's all, unless I miss something.
So if you don't writeback, i.e no physical swapfile, then I think there is negligible overhead (no rmap and all that jazz).
Does this sound acceptable to you?
Baoquan, what do you think?
Quoted 4 lines
Thanks
Chris
Nhat Pham
maintainerQuoted 5 lines · Nhat Pham
On Wed, Jul 15, 2026 at 4:04 PM Chris Li <[email protected]> wrote:
Hi Chris. I have some thoughts on this - let me know what you think.
Quoted 7 lines
I understand the memory overhead discussion here. In the case where vswap slot is backed by a real swapfile, I have not figured out a way to make the overhead disappear yet. The damn rmap is very annoying - I have some strategies to get around it for certain operations, but none of it can be done without a full fledged patch series of its own.
However, with the ghost zswap swapfile backend specifically, I think
I think I this is a bit confusing on my part. Let me clarify - I did not mean having vswap backed by ghost zswap swapfile backend here. What I meant here was, "in order to support the use case similar to ghost zswap swapfile", i.e zswap-without-writeback.
Vswap extended cluster struct is just the normal swap cluster struct, with an extra array to hold the backend, which we can use to store reference to struct zswap entry. If you don't do any writeback, that's sufficient - there will be no rmap etc. extra overhead. The rest of swap metadata follows Kairui's swap table design (and any advances he made in optimizing the overhead will be applied immediately, since I pretty much did not change the swap cache and swap count operations at all).
Baoquan He
reviewerQuoted 223 lines · Nhat Pham
Quoted 123 lines · Chris Li
Quoted 121 lines · Nhat Pham
Quoted 76 lines · Baoquan He
Quoted 74 lines · Baoquan He
Hi Nhat,
Quoted 1 line · Nhat Pham
On Thu, Jul 9, 2026 at 4:39 AM Baoquan He <[email protected]> wrote:
...snip...
Sorry, I won't reply to each comment individually. I'll just leave a summary reply at the end.
Quoted 56 lines
Quoted 11 lines
I think VSS goes beyond solving the "zswap without backing store" problem — it is fundamentally an architectural restructuring of the swap subsystem, with virtual swap as a first-class abstraction layered above physical devices. That is both its strength (comprehensive, future-proof) and its trade-off (more invasive to existing paths). I respect that.
My goal with ghost swap is different: address the immediate pain point with the smallest possible change to the existing infrastructure. Both approaches have value, and I hope the community discussion will help identify the right balance.
That's because I believe "zswap without backing store" just by itself is a bit narrow of a problem. It's certainly nice to fix, but it's more of a nuisance - you already have userspace hacks for it (as I mentioned above).
The problem I'm trying to solve is to support:
1. Writeback.
2. Dynamicity.
3. Decoupled backends.
all of which are motivated by real production issues, not some theoretical problems. I'm concerned that if we only focus on the third goal, we'll dig ourself into a hole that prevents us from solving 1 and 2 efficiently down the line.
Quoted 4 lines
Thank you again for the detailed review — it has already clarified which parts of the design need more thought and documentation, and will make the next version stronger.
I really don't want to be antagonistic, but I hope you'd take the real production pains that we've had (and have tried to communicate in multiple mailing threads, across a timeline of almost 2 years at this point) seriously.
Collaborations should go both ways. I've gone out of my way to try to address the concerns of various parties, from spending *multiple weeks* testing and investigating performance regression on zram backend (which my company does not use), to a rewrite/re-design of virtual swap to accommodate parties who wished to opt out of virtual swap for now.
I hope you can extend the same good will to our needs :) I've included you (and other swap folks such as Chris and Kairui) in my cc-lists. If you have concerns, you could have commented. Instead, you decided to send a patch series, which is basically just the ghost swapfile, with a bit of afterthought to handle writeback and dynamicity, rather inefficiently (no dedicated per-CPU caching), and not even correctly (the lack of rmap means swapoff / swap-cache-only physical swap slot reclaim is broken).
Quoted 2 lines
I respect you and Chris very much to assume bad faith, but please work with me rather than against me.
Before I get into the technical discussion, I want to address something you raised at the end of your reply — the concern that I'm working against you rather than with you. Our paths in MM simply haven't crossed much until now. I can't think of reason why I would want to target you or your work.
Quoted 6 lines
Quoted 6 lines
I respect the work you've put into VSS. I did check it. Honestly I am not fan of it. In your v2: 2244 insertions, 250 deletions, 15 files touched, a new 455-line header (
vswap.h). While swap subsystem has just absorbed the swap table series. Then another 2200 lines of architectural restructuring on top is added. Not sure if anybody raise concern about your solution, or anybody suggests other directions.Yes, it's a big patch series. But as you pointed out earlier, it solves a larger problem with a single coherent design :)
Quoted 7 lines
Quoted 7 lines
I posted ghost swap because I saw a concrete, narrow problem that I believed could be solved with a small change. It makes decoupling and dynamic growth with the minimum possible mechanism. Writeback is explicitly deferred
— not because it's unimportant, but because it's a separate problem that
shouldn't block the common case. With my shallow knowledge, it's optimal solution to the encountered problem. If you agree, you can change toThe problem is not the lack of writeback support per se. I understand that sometimes pieces of a design can be deferred to a follow-up patch series.
I'm more concerned that the ghost direction so far does not seriously take it into account in the design at all, which might make it either impossible or very hard to redesign around down the line. I don't want to rip a huge chunk of it out down the line later - we just avoided it with the swap table with Kairui's proposal.
Quoted 4 lines
Part of above sentence is missing. I meant if you agree on the ghost solution, you can change to take its way and continue, just add me to CC. Or we can work together.
How about this - let's solve one piece of the puzzle together, one at a time.
First, your optimization patch series. Have you taken a look at my proposal ([1])? I believe that design should be cleaner, while still allowing you to put it all you want to get (elimination of xarray being the chief benefit), without requiring you to modify swap cache and swap count operations (since you don't have to move the metadata in and out of struct
zswap_entry). It does require a bit more code to dynamically allocated the backend array, but should be trivial IMHO.Quoted 8 lines
Quoted 1 line
[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Hi Nhat,
Sorry for the late reply. I've been busy and just saw this. First impression looks promising, it is better than any previous version of VS. Thanks for incorporating Kairui's feedback. Please give me some time to sleep on it. I will get back to you on that.
Please take a look. I was waiting for your response as well :)
The patch series is large, yes, but it's an RFC meant to show case the final end design.
Quoted 55 lines
Quoted 40 lines
Next, if you just want pure ghost swapfile without writeback support, maybe something similar to the first 2 patches in my RFC? i.e up to this patch:
https://lore.kernel.org/all/[email protected]/
I'm sure you could find issues/bugs in it too with some extensive testing (a lot of my performance testing is done on the entire series as a whole), but from a design point of view it should achieve what us both want, no? It's literally just a ghost swap device - most of the other design elements of vswap (the new charging behavior, rmap, support for writeback) comes in latter patches. I think the difference is:
1. It's transparently dynamic, whereas in your RFC it's driven by a userspace knob. Do you think there are inherent values in a userspace knob?
2. The address space can also shrink when swap usage drops at runtime.
3. The dynamic address space is backed by an xarray. Note that it's structured differently than zswap's one (it's an xarray pointing to clusters). If you have rationales or numbers to show that vmalloc array is superior (while still support the kernel-driven expansion and shrink), please let me know.
If you have no faith in me and do not like my code, you can take the code as a starting point, or just the idea, and drive it yourself. Or let me know if you're unhappy with any piece of it - we can discuss further.
Quoted 3 lines
Quoted 3 lines
Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Look man, I care about the problems being solved and I'm a sucker for good ideas, but I'm not married to anything, be it my code or my ideas.
Quoted 5 lines
I have literally dropped my old code and adopted Kairui's suggestions after playing with it and liking it, to make sure every party's concern is met.
If you have concerns, please let me know, or keep sending code if you
May I add one wishlist item related to ghost swapfile here? I will reply to your VS thread for other feedback.
For the ghost swapfile usage case (no writeback). Can VS support this usage case while maintaining per-swap-slot metadata usage comparable to the ghost swapfile implementation? If we have to put a number to it, let's say the increase in metadata is less than 1 byte per swap slot compared to the ghost swapfile implementation.
Quoted 7 lines
We have large deployments of ghost swapfile in the fleet. I want an upstream aligned solution that does not increase the existing memory overhead compared to the current ghost swapfile deployment, for that usage. As you know, memory pricing and optimization pressure are high. The ghost swapfile patch itself is relatively simple. If VS can address this usage case, it will remove a lot of incentive to explore ghost swapfile-like solutions.
Hi Chris. I have some thoughts on this - let me know what you think.
I understand the memory overhead discussion here. In the case where vswap slot is backed by a real swapfile, I have not figured out a way to make the overhead disappear yet. The damn rmap is very annoying - I have some strategies to get around it for certain operations, but none of it can be done without a full fledged patch series of its own.
However, with the ghost zswap swapfile backend specifically, I think the overhead is going to be negligible compared to the status quo upstream (things might be different internally at Google - please let me know!). This is because the main remaining per-slot overhead comes in the form of one per-cluster array to store the "backend", here it's going to store struct
zswap_entrypointers. This is not extra overhead though, because it basically replaces the xarray (that you added all those years ago)!The rest is small stuff here and there that is not O(slot) but rather O(cluster). For example one struct
rcu_headper cluster, xarray overhead for the dynamicization - note that this xarray manages cluster, not slot. I think that's all, unless I miss something.
Quoted 6 lines
So if you don't writeback, i.e no physical swapfile, then I think there is negligible overhead (no rmap and all that jazz).
Does this sound acceptable to you?
Baoquan, what do you think?
Thanks for the thoughtful reply and the very constructive proposal. Your suggestion about moving forward in small, incremental steps in last mail is really appealing — I think that's the right way to make progress on this. I need a little time to think it through more carefully before giving you a proper response. Will get back to you soon.
Thanks
Baoquan
Nhat Pham
maintainerQuoted 9 lines · Baoquan He
On 07/16/26 at 09:49am, Nhat Pham wrote:
Thanks for the thoughtful reply and the very constructive proposal. Your suggestion about moving forward in small, incremental steps in last mail is really appealing — I think that's the right way to make progress on this. I need a little time to think it through more carefully before giving you a proper response. Will get back to you soon.
Yeah let me know what you think. I'm planning to send out v3 with some more cleanups and fixes, but I'm happy to hear what you and Chris wants to make it as useful as possible for everyone.
Best,
Nhat
Quoted 3 lines
Thanks
Baoquan
Baoquan He
reviewerQuoted 11 lines · Nhat Pham
Quoted 9 lines · Baoquan He
On 07/16/26 at 09:49am, Nhat Pham wrote:
Thanks for the thoughtful reply and the very constructive proposal. Your suggestion about moving forward in small, incremental steps in last mail is really appealing — I think that's the right way to make progress on this. I need a little time to think it through more carefully before giving you a proper response. Will get back to you soon.
Quoted 3 lines
Yeah let me know what you think. I'm planning to send out v3 with some more cleanups and fixes, but I'm happy to hear what you and Chris wants to make it as useful as possible for everyone.
Hi Nhat,
I've thought about it more carefully. Here's what I think the path forward could look like. I'll try to lay it out from the bottom up.
1. Data structure
Instead of introducing a separate swap_cluster_info_dynamic wrapper, I'd prefer to add a per-slot backend pointer array directly into struct swap_cluster_info:
struct swap_cluster_info {
...
#ifdef CONFIG_ZSWAP
atomic_long_t *vs_pointer; /* or xs_pointer — per-slot backend pointers */
#endif
struct list_head list;
};This is allocated together with or alongside the swap table when the cluster is first used. No wrapper struct, no new ->cluster_info pointer type change — just one extra field that is NULL for clusters that don't need it.
The name is bike-sheddable: vs_pointer (virtual-space pointer) or xs_pointer (cross-space pointer), xs_table (xswap extension table), aux_table (auxiliary table), priv_table (private data table), slot_priv (per-slot private data) or s_ext (slot extension). The point is that it stores a pointer to the backend owner of each slot — initially just struct zswap_entry *, eventually extensible to other backend types. I prefer to name it ext_table, but we have had extend_table. My second favorite option is aux_table. What's your choice?
2. Eliminate the zswap xarray
Once vs_pointer is in place, zswap can store and load entries through vs_pointer[ci_off] instead of xa_store/xa_load on the per-device zswap xarray. The cluster lock protects the access. This is exactly the same idea as your virtual_table, just placed directly in swap_cluster_info rather than in a wrapper.
The benefit: zswap_swapon and zswap_swapoff become no-ops (no global xarray to allocate or tear down). The per-cluster allocation is lazy — only clusters that actually see a zswap store allocate the array.
3. First step for ghost swap / virtual swap: dynamic growth and shrink, no writeback yet
I think we agree that the first landing should be narrow in scope:
- A virtual swap device that decouples PTE swap entries from physical backing.
- Backed only by zswap (and zero pages) initially.
- No writeback to physical swap.
- Dynamic growth and shrink of the cluster space.
For the dynamic cluster management, I'd like to try a lazy vmalloc
approach: allocate the cluster_info array as a vmalloc region, and use vm_area_map_pages() to map newly populated clusters on demand. Growth is automatic via page faults; shrink means reclaiming the physical pages backing freed clusters.
The vmalloc approach has O(1) lookup (simple array index), which matters on the swapout hot path. The trade-off is that shrink is coarser than xarray-based management (we can't easily unmap an individual cluster from the middle of the vmalloc region). But for the initial landing where swap usage tends to be ratchet-like, I think this is a reasonable trade-off. We can always switch to xarray later if shrink granularity becomes a real issue.
4. Writeback and rmap as follow-up
Once the basic virtual swap device is upstream, we add writeback support:
- When zswap store fails, fall back to allocating a physical swap slot and writing out.
- The vs_pointer entry is updated to point to the physical slot (tagged to distinguish from a zswap_entry pointer).
- rmap for physical -> virtual reverse lookup, needed for swapoff and physical slot reclaim.
This is a natural extension: the vs_pointer is already per-slot, so adding a new backend type is a tag-bit change, not a rewrite.
5. Longer-term: swap tiering
Looking further ahead, the per-slot pointer can also serve as the basis for a swap tiering model. Imagine a memcg with multiple
tiers: ghost/virtual swap at the top, then pmem, then SSD, then HDD. Swapout goes to the top tier first; when that fills, data cascades down to the next tier. Each swap device only needs to know about the device directly below it — a simple "push down" interface.
This is a longer-term vision, but having a uniform per-slot backend pointer from the start keeps the door open without painting us into a corner.
6. Naming
We have three names floating around: "ghost swap", "virtual swap", and Chris suggested "xswap". We're going to be talking about this thing a lot, and having a single agreed-upon name would help avoid confusion in discussions, commit messages, and documentation. I don't have strong feelings about which one — but I'd like us to settle on something everyone can live with.
7. How to swapon a ghost / virtual swap device
There are several approaches on the table right now:
a) A sparse file created via truncate (Chris's approach): a swap file that looks large (e.g. 1GiB) but only has one real backing page. Works with the existing swapon syscall, but the sparse file has to live somewhere and the filesystem interaction is a bit of a hack.
b) A boot-time, always-present device (your VSS approach): the vswap device is created unconditionally at late_initcall when CONFIG_VSWAP=y. No userspace action needed, but no runtime control either — you can't create or destroy it, and you can't have per-memcg or per-workload virtual swap spaces.
c) A swapon flag: something like SWAP_FLAG_VIRTUAL or SWAP_FLAG_GHOST passed to swapon(2), which tells the kernel to treat the swap device as a ghost device with no backing storage.
d) A dedicated sysfs / debugfs knob: create and size the ghost swap device at runtime via a file under /sys/kernel/mm/swap/.
e) Something I'm missing?
Each has trade-offs between flexibility, simplicity, and the existing swap management model. I'd like to hear what you and Chris think — especially whether the boot-time-only nature of the current VSS design is a deliberate simplification or something you'd be open to revisiting.
Curious to hear what you think — especially if you see any structural issues with putting vs_pointer directly in swap_cluster_info rather than in a wrapper. If the wrapper is important for your v3 design, I'd like to understand the rationale better.
Thanks,
Baoquan
Chris Li
maintainerQuoted 19 lines · Baoquan He
Quoted 11 lines · Nhat Pham
Quoted 9 lines · Baoquan He
On 07/16/26 at 09:49am, Nhat Pham wrote:
Thanks for the thoughtful reply and the very constructive proposal. Your suggestion about moving forward in small, incremental steps in last mail is really appealing — I think that's the right way to make progress on this. I need a little time to think it through more carefully before giving you a proper response. Will get back to you soon.
Quoted 3 lines
Yeah let me know what you think. I'm planning to send out v3 with some more cleanups and fixes, but I'm happy to hear what you and Chris wants to make it as useful as possible for everyone.
Hi Nhat,
Quoted 8 lines
I've thought about it more carefully. Here's what I think the path forward could look like. I'll try to lay it out from the bottom up.
1. Data structure
Instead of introducing a separate
swap_cluster_info_dynamicwrapper, I'd prefer to add a per-slot backend pointer array directly into structswap_cluster_info:
I agree with that. The current VS patch is a great improvement over the previous version of VS. However it places the cluster info and swap table in different locations when accessing swap table-related information. The code path requires different locks as well, the xarray needs to take the xa lock. This kind of dual-personality issue will make reasoning harder. Keeping the swap table in the same place is simpler in my mind.
The other issue I want to add is that, using xarray as a swap allocator is unverified. It has no size limit which is one of its claimed advantages, but also one of its drawbacks. Because it does not have swap_full() concept. It will never do the normal if swap_full() fail to allocate, let's do swap cache related. That code path cannot trigger because the xarray has no size limit.
There are other considerations as well. It works fine if VS only wants to allocate 4K swap entries. If VS later wants to go into the mthp swap entry allocation. I don't think xarray has similar considerations as the current swap allocator regarding fragment lists, etc. The xarray isn't designed to be used that way, at least not in the common usage situation. You are likely running into your xarray allocation specific issue.
Therefore, I think we should keep the current swap entry allocator and the swap table data structure storing locations. Just extend the per-swap-entry to allow custom implementation for VS to store the zswap or even the write back info. Grow the swap device size if needed after the normal swap cache reclaim path. That is likely a better way to proceed.
Quoted 9 lines
struct swap_cluster_info {
...
#ifdef CONFIG_ZSWAP
atomic_long_t *vs_pointer; /* or xs_pointer — per-slot backend pointers */
#endif
struct list_head list;
};Quoted 4 lines
This is allocated together with or alongside the swap table when the cluster is first used. No wrapper struct, no new ->cluster_info pointer type change — just one extra field that is NULL for clusters that don't need it.
I think there are other ways to allocate and grow that array. Maybe we could unify this with other per swap-ops devices by having their own extnesion on swap ops private pointer. On the other hand, we can do the simplest thing to unblock and move forward. This data structure is internal, we can always change it later.
Quoted 11 lines
The name is bike-sheddable:
vs_pointer(virtual-space pointer) orxs_pointer(cross-space pointer),xs_table(xswap extension table),aux_table(auxiliary table),priv_table(private data table),slot_priv(per-slot private data) ors_ext(slot extension). The point is that it stores a pointer to the backend owner of each slot — initially just structzswap_entry*, eventually extensible to other backend types. I prefer to name itext_table, but we have hadextend_table. My second favorite option isaux_table. What's your choice?
Quoted 7 lines
2. Eliminate the zswap xarray
Once
vs_pointeris in place, zswap can store and load entries through vs_pointer[ci_off] instead of xa_store/xa_load on the per-device zswap xarray. The cluster lock protects the access. This is exactly the same idea as yourvirtual_table, just placed directly inswap_cluster_inforather than in a wrapper.
Yes. I would like to get rid of the xarray from zswap or VS.
Quoted 7 lines
The benefit:
zswap_swaponandzswap_swapoffbecome no-ops (no global xarray to allocate or tear down). The per-cluster allocation is lazy — only clusters that actually see a zswap store allocate the array.3. First step for ghost swap / virtual swap: dynamic growth and shrink,
You mean xswap :-)
Quoted 15 lines
no writeback yet
I think we agree that the first landing should be narrow in scope:
- A virtual swap device that decouples PTE swap entries from physical backing.
- Backed only by zswap (and zero pages) initially.
- No writeback to physical swap.
- Dynamic growth and shrink of the cluster space.For the dynamic cluster management, I'd like to try a lazy vmalloc
approach: allocate thecluster_infoarray as a vmalloc region, and usevm_area_map_pages()to map newly populated clusters on demand. Growth is automatic via page faults; shrink means reclaiming the physical pages backing freed clusters.
Quoted 7 lines
The vmalloc approach has O(1) lookup (simple array index), which matters on the swapout hot path. The trade-off is that shrink is coarser than xarray-based management (we can't easily unmap an individual cluster from the middle of the vmalloc region). But for the initial landing where swap usage tends to be ratchet-like, I think this is a reasonable trade-off. We can always switch to xarray later if shrink granularity becomes a real issue.
The xarray allocator also has a fragmentation issue where not all memory released from the xarray returns to the system as free pages. Using kvmalloc grow it will wait some unused cluster info. But let's put it into perspective: cluster info is shared by 512 swap entries. Each swap entry is 4K. So the memory used by swap cluster is pretty small. Much smaller than 1 byte per swap entry. We can also modify the cluster info array, making it an array of pointers. Then the waste will be even smaller (if unused). At the cost of one more pointer per swap_cluster_info. We can consider all this later, not at this current stage.
Quoted 1 line
4. Writeback and rmap as follow-up
I agree that adding cluster info growth as the first step. I recall Kairui has an RFC patch series to implement virtual entry lookup (still using the xarray). I think the combination of growth and indirection lookup should provide most of what VS depends on from the core swap point of view.
Quoted 14 lines
Once the basic virtual swap device is upstream, we add writeback support:
- When zswap store fails, fall back to allocating a physical swap slot and writing out.
- Thevs_pointerentry is updated to point to the physical slot (tagged to distinguish from azswap_entrypointer).
- rmap for physical -> virtual reverse lookup, needed for swapoff and physical slot reclaim.This is a natural extension: the
vs_pointeris already per-slot, so adding a new backend type is a tag-bit change, not a rewrite.5. Longer-term: swap tiering
Quoted 7 lines
Looking further ahead, the per-slot pointer can also serve as the basis for a swap tiering model. Imagine a memcg with multiple
tiers: ghost/virtual swap at the top, then pmem, then SSD, then HDD. Swapout goes to the top tier first; when that fills, data cascades down to the next tier. Each swap device only needs to know about the device directly below it — a simple "push down" interface.
I like that a lot.
Quoted 5 lines
This is a longer-term vision, but having a uniform per-slot backend pointer from the start keeps the door open without painting us into a corner.
Quoted 8 lines
6. Naming
We have three names floating around: "ghost swap", "virtual swap", and Chris suggested "xswap". We're going to be talking about this thing a lot, and having a single agreed-upon name would help avoid confusion in discussions, commit messages, and documentation. I don't have strong feelings about which one — but I'd like us to settle on something everyone can live with.
I recall Rik suggested taking VS and ghost swap and creating something new that is better than each. I agree with that collaboration suggestion. I totally welcome a name that follows that spirit.
Quoted 10 lines
7. How to swapon a ghost / virtual swap device
There are several approaches on the table right now:
a) A sparse file created via truncate (Chris's approach): a swap file that looks large (e.g. 1GiB) but only has one real backing page. Works with the existing swapon syscall, but the sparse file has to live somewhere and the filesystem interaction is a bit of a hack.
I give up on that truncated hack, but what if we make the swap file really small, e.g. 4K and then dynamically grow it at runtime. Then it is not a hack anymore. No more truncated file. We can find many ways to improve it to get rid of the hack. That was the original reason to send out the ghost swapfile RFC: a starting point for discussions.
Quoted 6 lines
b) A boot-time, always-present device (your VSS approach): the vswap device is created unconditionally at
late_initcallwhenCONFIG_VSWAP=y. No userspace action needed, but no runtime control either — you can't create or destroy it, and you can't have per-memcg or per-workload virtual swap spaces.
I don't think we should have swap without swap on. I prefer keeping the swap on/off interface even for memory based swap.
Quoted 4 lines
c) A swapon flag: something like
SWAP_FLAG_VIRTUALorSWAP_FLAG_GHOSTpassed to swapon(2), which tells the kernel to treat the swap device as a ghost device with no backing storage.
You mean SWAP_FLAG_XSWAP :-)
Quoted 3 lines
d) A dedicated sysfs / debugfs knob: create and size the ghost swap device at runtime via a file under
/sys/kernel/mm/swap/.
For the most part we should allow the swap device to grow automatically for the virtual entry internally without userspace knobs. We might still want a knob for the max limit of the virtual entries. I don't think it should be unlimited. I am open to suggesting.
Chris
Quoted 17 lines
e) Something I'm missing?
Each has trade-offs between flexibility, simplicity, and the existing swap management model. I'd like to hear what you and Chris think — especially whether the boot-time-only nature of the current VSS design is a deliberate simplification or something you'd be open to revisiting.
Curious to hear what you think — especially if you see any structural issues with putting
vs_pointerdirectly inswap_cluster_inforather than in a wrapper. If the wrapper is important for your v3 design, I'd like to understand the rationale better.Thanks,
Baoquan
Nhat Pham
maintainerOn Tue, Jul 21, 2026 at 11:03 AM Chris Li <[email protected]> wrote:
I'm writing a full response to Baoquan, but there are a couple points I'd like to point out first, to save everyone's from the confusion going forward :)
Quoted 26 lines · Baoquan He
Quoted 13 lines · Nhat Pham
Quoted 11 lines · Baoquan He
Quoted 9 lines · Nhat Pham
Thanks for the thoughtful reply and the very constructive proposal. Your suggestion about moving forward in small, incremental steps in last mail is really appealing — I think that's the right way to make progress on this. I need a little time to think it through more carefully before giving you a proper response. Will get back to you soon.
Quoted 4 lines
Quoted 4 lines
Yeah let me know what you think. I'm planning to send out v3 with some more cleanups and fixes, but I'm happy to hear what you and Chris wants to make it as useful as possible for everyone.
I agree with that. The current VS patch is a great improvement overthe previous version of VS. However it places the cluster info and swap table in different locations when accessing swap table-related information. The code path requires different locks as well, the xarray needs to take the xa lock. This kind of dual-personality issue will make reasoning harder. Keeping the swap table in the same place is simpler in my mind.
Quoted 6 lines
The other issue I want to add is that, using xarray as a swap allocator is unverified. It has no size limit which is one of its claimed advantages, but also one of its drawbacks. Because it does not have
swap_full()concept. It will never do the normal ifswap_full()fail to allocate, let's do swap cache related. That code path cannot trigger because the xarray has no size limit.
1. If the page is reclaimable, reclaim path will take care of swap cache too.
2. For non-physical swap users, we actually eagerly attempt to get rid of the swap space (and swap cache) once the final reference is dropped. Zswap load invalidates the compressed copy, so there's not a lot of value in caching. I'm not sure if you missed this or not.
Quoted 7 lines
There are other considerations as well. It works fine if VS only wants to allocate 4K swap entries. If VS later wants to go into the mthp swap entry allocation. I don't think xarray has similar considerations as the current swap allocator regarding fragment lists, etc. The xarray isn't designed to be used that way, at least not in the common usage situation. You are likely running into your xarray allocation specific issue.
I don't understand what you're asking here. mTHP swap allocation already works with my RFC.
The xarray maps to 512 entry cluster, not individual swap slots. Once you get a cluster (either from a partial cluster or a new cluster from the xarray), allocation of mTHP works exactly the same as physical swap allocator.
Quoted 11 lines
The xarray allocator also has a fragmentation issue where not all memory released from the xarray returns to the system as free pages. Using kvmalloc grow it will wait some unused cluster info. But let's put it into perspective: cluster info is shared by 512 swap entries. Each swap entry is 4K. So the memory used by swap cluster is pretty small. Much smaller than 1 byte per swap entry. We can also modify the cluster info array, making it an array of pointers. Then the waste will be even smaller (if unused). At the cost of one more pointer per
swap_cluster_info. We can consider all this later, not at this current stage.
The xarray also maps each index to a cluster too.
Quoted 8 lines
Quoted 2 lines
4. Writeback and rmap as follow-up
I agree that adding cluster info growth as the first step. I recall Kairui has an RFC patch series to implement virtual entry lookup (still using the xarray). I think the combination of growth and indirection lookup should provide most of what VS depends on from the core swap point of view.
That is *exactly* what my RFC is using. Did you see the first patch? I put Co-developed-by Kairui for a reason.
Nhat Pham
maintainerQuoted 8 lines · Nhat Pham
1. If the page is reclaimable, reclaim path will take care of swap cache too.
2. For non-physical swap users, we actually eagerly attempt to get rid of the swap space (and swap cache) once the final reference is dropped. Zswap load invalidates the compressed copy, so there's not a lot of value in caching. I'm not sure if you missed this or not.
What I meant here was, in should_try_to_free_swap(), I added a check:
if the vswap slot is not backed physical swap slots, try to free it (folio_free_swap() etc. does check for swap count).
I have tested my code and yes, I did encounter the case you pointed out. That's why I added that, with comments to justify why etc. I also had a version of it in the older virtual swap already :)
Chris Li
maintainerQuoted 6 lines · Nhat Pham
On Tue, Jul 21, 2026 at 11:03 AM Chris Li <[email protected]> wrote:
I'm writing a full response to Baoquan, but there are a couple points I'd like to point out first, to save everyone's from the confusion going forward :)
Thanks
Quoted 32 lines
Quoted 25 lines · Baoquan He
Quoted 13 lines · Nhat Pham
Quoted 11 lines · Baoquan He
Quoted 9 lines · Nhat Pham
Thanks for the thoughtful reply and the very constructive proposal. Your suggestion about moving forward in small, incremental steps in last mail is really appealing — I think that's the right way to make progress on this. I need a little time to think it through more carefully before giving you a proper response. Will get back to you soon.
Quoted 4 lines
Quoted 4 lines
Yeah let me know what you think. I'm planning to send out v3 with some more cleanups and fixes, but I'm happy to hear what you and Chris wants to make it as useful as possible for everyone.
I agree with that. The current VS patch is a great improvement overthe previous version of VS. However it places the cluster info and swap table in different locations when accessing swap table-related information. The code path requires different locks as well, the xarray needs to take the xa lock. This kind of dual-personality issue will make reasoning harder. Keeping the swap table in the same place is simpler in my mind.
Quoted 6 lines
The other issue I want to add is that, using xarray as a swap allocator is unverified. It has no size limit which is one of its claimed advantages, but also one of its drawbacks. Because it does not have
swap_full()concept. It will never do the normal ifswap_full()fail to allocate, let's do swap cache related. That code path cannot trigger because the xarray has no size limit.
Quoted 6 lines
1. If the page is reclaimable, reclaim path will take care of swap cache too.
2. For non-physical swap users, we actually eagerly attempt to get rid of the swap space (and swap cache) once the final reference is dropped. Zswap load invalidates the compressed copy, so there's not a lot of value in caching. I'm not sure if you missed this or not.
I am aware of that. However my brain is a bit fuzzy on the exact behavior in zswap because our internal implementation has some minor differences in how it handles this.
Anyway, I am not sure 1) and 2) is enough to get rid of the swap_full() and reclaim swap cache test for zswap. If you think that is sufficient, we should be able to make it as a separate patch to remove the swap full in this situation and test it on the upstream kernel before the VS series.
Quoted 8 lines
Quoted 7 lines
There are other considerations as well. It works fine if VS only wants to allocate 4K swap entries. If VS later wants to go into the mthp swap entry allocation. I don't think xarray has similar considerations as the current swap allocator regarding fragment lists, etc. The xarray isn't designed to be used that way, at least not in the common usage situation. You are likely running into your xarray allocation specific issue.
Quoted 7 lines
I don't understand what you're asking here. mTHP swap allocation already works with my RFC.
The xarray maps to 512 entry cluster, not individual swap slots. Once you get a cluster (either from a partial cluster or a new cluster from the xarray), allocation of mTHP works exactly the same as physical swap allocator.
Thanks for the clarification. I haven't looked at the patch that closely. I was mostly thinking about the data structure layout and the relationship of the newly proposed data structure.
A follow up question. If the free_cluster list able to satisfy the allocation requirement, there is no extra xarray allocation of the cluster in your patch series, right? I just want to make sure I understand it correctly.
Quoted 11 lines
Quoted 10 lines
The xarray allocator also has a fragmentation issue where not all memory released from the xarray returns to the system as free pages. Using kvmalloc grow it will wait some unused cluster info. But let's put it into perspective: cluster info is shared by 512 swap entries. Each swap entry is 4K. So the memory used by swap cluster is pretty small. Much smaller than 1 byte per swap entry. We can also modify the cluster info array, making it an array of pointers. Then the waste will be even smaller (if unused). At the cost of one more pointer per
swap_cluster_info. We can consider all this later, not at this current stage.
Quoted 1 line
The xarray also maps each index to a cluster too.
It the cluster slot offset take care of the lower 9 bits (512) entry and the upper bits of the index is for the xarray look up?
Quoted 10 lines
Quoted 7 lines
Quoted 1 line
4. Writeback and rmap as follow-up
I agree that adding cluster info growth as the first step. I recall Kairui has an RFC patch series to implement virtual entry lookup (still using the xarray). I think the combination of growth and indirection lookup should provide most of what VS depends on from the core swap point of view.
That is *exactly* what my RFC is using. Did you see the first patch? I put Co-developed-by Kairui for a reason.
I saw that. What I mean is that using the xswap (ghostswap) file was the original purpose of Kairui's patch series. Could you use Kairui's original patches series with an xswap/ghostswap file and build on top of that? Now with Baoquan's kvmalloc growing the array, I think VS can use that instead of the xarray mapping of the cluster. That is what I mean.
Let me know if you have further questions.
Chris
Chris Li
maintainerQuoted 9 lines · Nhat Pham
Quoted 8 lines
Quoted 1 line
[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Hi Nhat,
Sorry for the late reply. I've been busy and just saw this. First impression looks promising, it is better than any previous version of VS. Thanks for incorporating Kairui's feedback. Please give me some time to sleep on it. I will get back to you on that.
Quoted 4 lines
Please take a look. I was waiting for your response as well :)
The patch series is large, yes, but it's an RFC meant to show case the final end design.
Understand.
Quoted 19 lines
Quoted 9 lines
May I add one wishlist item related to ghost swapfile here? I will reply to your VS thread for other feedback.
For the ghost swapfile usage case (no writeback). Can VS support this usage case while maintaining per-swap-slot metadata usage comparable to the ghost swapfile implementation? If we have to put a number to it, let's say the increase in metadata is less than 1 byte per swap slot compared to the ghost swapfile implementation.
Quoted 7 lines
We have large deployments of ghost swapfile in the fleet. I want an upstream aligned solution that does not increase the existing memory overhead compared to the current ghost swapfile deployment, for that usage. As you know, memory pricing and optimization pressure are high. The ghost swapfile patch itself is relatively simple. If VS can address this usage case, it will remove a lot of incentive to explore ghost swapfile-like solutions.
Hi Chris. I have some thoughts on this - let me know what you think.
Quoted 8 lines
I understand the memory overhead discussion here. In the case where vswap slot is backed by a real swapfile, I have not figured out a way to make the overhead disappear yet. The damn rmap is very annoying - I have some strategies to get around it for certain operations, but none of it can be done without a full fledged patch series of its own.
However, with the ghost zswap swapfile backend specifically, I think the overhead is going to be negligible compared to the status quo
We might need a clearer definition of negligible. e.g. in my mind less than 1 byte per 4K size swap slot.
Quoted 6 lines
upstream (things might be different internally at Google - please let me know!). This is because the main remaining per-slot overhead comes in the form of one per-cluster array to store the "backend", here it's going to store struct
zswap_entrypointers. This is not extra overhead though, because it basically replaces the xarray (that you added all those years ago)!
I hope you are not clinging to that xarray because I want to get rid of that as well.
Quoted 4 lines
The rest is small stuff here and there that is not O(slot) but rather O(cluster). For example one struct
rcu_headper cluster, xarray overhead for the dynamicization - note that this xarray manages cluster, not slot. I think that's all, unless I miss something.
I also mean the normal path that accesses the swap table will take an xarray lookup with different locking requirements.
I prefer to keep the swap table and cluster info where they are now to avoid that extra complexity in the mental model.
It seems allowing the cluster array's virtual entry to grow should mostly answer your VS need?
Quoted 4 lines
So if you don't writeback, i.e no physical swapfile, then I think there is negligible overhead (no rmap and all that jazz).
Does this sound acceptable to you?
Regarding the memory size, yes, if the overhead is less than 1 byte per swap slot compared to the ghost swapfile. The location and lookup path for the swap table and cluster info are not ideal because VS will force a different location and lookup code path. I prefer growing the cluster info array to gain more swap entries and keep the current swap cluster and swap table data structures where they are.
Chris
Nhat Pham
maintainerQuoted 44 lines · Chris Li
Quoted 9 lines · Nhat Pham
Quoted 8 lines
Quoted 1 line
[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Hi Nhat,
Sorry for the late reply. I've been busy and just saw this. First impression looks promising, it is better than any previous version of VS. Thanks for incorporating Kairui's feedback. Please give me some time to sleep on it. I will get back to you on that.
Quoted 4 lines
Please take a look. I was waiting for your response as well :)
The patch series is large, yes, but it's an RFC meant to show case the final end design.
Understand.
Quoted 25 lines
Quoted 9 lines
May I add one wishlist item related to ghost swapfile here? I will reply to your VS thread for other feedback.
For the ghost swapfile usage case (no writeback). Can VS support this usage case while maintaining per-swap-slot metadata usage comparable to the ghost swapfile implementation? If we have to put a number to it, let's say the increase in metadata is less than 1 byte per swap slot compared to the ghost swapfile implementation.
Quoted 7 lines
We have large deployments of ghost swapfile in the fleet. I want an upstream aligned solution that does not increase the existing memory overhead compared to the current ghost swapfile deployment, for that usage. As you know, memory pricing and optimization pressure are high. The ghost swapfile patch itself is relatively simple. If VS can address this usage case, it will remove a lot of incentive to explore ghost swapfile-like solutions.
Hi Chris. I have some thoughts on this - let me know what you think.
I understand the memory overhead discussion here. In the case where vswap slot is backed by a real swapfile, I have not figured out a way to make the overhead disappear yet. The damn rmap is very annoying - I have some strategies to get around it for certain operations, but none of it can be done without a full fledged patch series of its own.
Quoted 5 lines
Quoted 2 lines
However, with the ghost zswap swapfile backend specifically, I think the overhead is going to be negligible compared to the status quo
We might need a clearer definition of negligible. e.g. in my mind less than 1 byte per 4K size swap slot.
I laid out the overhead below already :)
Quoted 10 lines
Quoted 7 lines
upstream (things might be different internally at Google - please let me know!). This is because the main remaining per-slot overhead comes in the form of one per-cluster array to store the "backend", here it's going to store struct
zswap_entrypointers. This is not extra overhead though, because it basically replaces the xarray (that you added all those years ago)!I hope you are not clinging to that xarray because I want to get rid of that as well.
That's what I meant though. The virtual table replaces it for vswap users.
Quoted 6 lines
Quoted 5 lines
The rest is small stuff here and there that is not O(slot) but rather O(cluster). For example one struct
rcu_headper cluster, xarray overhead for the dynamicization - note that this xarray manages cluster, not slot. I think that's all, unless I miss something.
Quoted 7 lines
I also mean the normal path that accesses the swap table will take an xarray lookup with different locking requirements.
I prefer to keep the swap table and cluster info where they are now to avoid that extra complexity in the mental model.It seems allowing the cluster array's virtual entry to grow should mostly answer your VS need?
I don't get it. What do you mean here?
Quoted 8 lines
Quoted 5 lines
So if you don't writeback, i.e no physical swapfile, then I think there is negligible overhead (no rmap and all that jazz).
Does this sound acceptable to you?
Regarding the memory size, yes, if the overhead is less than 1 byte per swap slot compared to the ghost swapfile. The location and lookup
If you see where I miss, point out. Otherwise I'll take that as a yes? ;)
Quoted 4 lines
path for the swap table and cluster info are not ideal because VS will force a different location and lookup code path. I prefer growing the cluster info array to gain more swap entries and keep the current swap cluster and swap table data structures where they are.
The current swap cluster and swap table data structure are *exactly* where they are. In fact my proposal keep it closer to the status quo - once you lock the cluster, swap cache and swap count operations are *exactly* the same, whereas Baoquan's approach requires taking swap table information and shove it into the struct zswap entry.
Can you clarify what you meant here? :)
Chris Li
maintainerQuoted 4 lines · Nhat Pham
Quoted 2 lines
I hope you are not clinging to that xarray because I want to get rid of that as well.
That's what I meant though. The virtual table replaces it for vswap users.
But you still have one xarray for lookup swap entry to cluster right?
I want to get rid of xarray in both zswap and VS. The ultimate solution is to not have xarray in the zswap/VS lookup of swap entries at all. I believe achieving that would be a better win. I can already see that happening by building on top of Baoquan's xswap kvmalloc growth series.
Quoted 12 lines
Quoted 12 lines
Quoted 6 lines
The rest is small stuff here and there that is not O(slot) but rather O(cluster). For example one struct
rcu_headper cluster, xarray overhead for the dynamicization - note that this xarray manages cluster, not slot. I think that's all, unless I miss something.I also mean the normal path that accesses the swap table will take an xarray lookup with different locking requirements.
I prefer to keep the swap table and cluster info where they are now to avoid that extra complexity in the mental model.
Quoted 4 lines
Quoted 2 lines
It seems allowing the cluster array's virtual entry to grow should mostly answer your VS need?
I don't get it. What do you mean here?
I mean, here is my observation, please correct me if I am wrong, VS patche series needs two things from the swap core function:
1) able to allocate more swap entry beyond the current si->max. Baoqun's kvmalloc grow patches should be able to answer that.
2) able to store private data structures per swap slot. e.g. a zswap entry pointer, or a redirection pointer or both. We can modify the swap core to provide this extra context per swap entry for each swap device. In Baoquan's new purposal that is the vs_table array. We can use the same kvmalloc grow trick to grow this pointer array as well.
That way, it does not need to have the VS xarray lookup in front of cluster_info. You just keep growing the cluster info array and vs_table array as needed.
Quoted 9 lines
Quoted 7 lines
Quoted 4 lines
So if you don't writeback, i.e no physical swapfile, then I think there is negligible overhead (no rmap and all that jazz).
Does this sound acceptable to you?
Regarding the memory size, yes, if the overhead is less than 1 byte per swap slot compared to the ghost swapfile. The location and lookup
If you see where I miss, point out. Otherwise I'll take that as a yes? ;)
Pending my deeper look into your patch series. I have been bitten by your patch series' claim about metadata overhead before so I am a bit more cautious about concluding anything regarding your patch's meta data usage now :-)
Quoted 7 lines
Quoted 4 lines
path for the swap table and cluster info are not ideal because VS will force a different location and lookup code path. I prefer growing the cluster info array to gain more swap entries and keep the current swap cluster and swap table data structures where they are.
The current swap cluster and swap table data structure are *exactly* where they are. In fact my proposal keep it closer to the status quo -
It is not the case in my mind because the swap entry will require an extra xarray lookup to get to the cluster info. No?
I do not want that extra xarray lookup. I prefer an array lookup if possible. With Baoquan's kvmalloc grow, that should be possible.
Quoted 3 lines
once you lock the cluster, swap cache and swap count operations are *exactly* the same, whereas Baoquan's approach requires taking swap table information and shove it into the struct zswap entry.
Yes, my concern is the xarray lookup for the swap cluster info.
Quoted 1 line
Can you clarify what you meant here? :)
It still has the xarray lookup in the code path (looking up the swap entry index to get swap cluster info), that xarray is what I want to get rid of.
Chris
Baoquan He
reviewerHi Nhat,
Quoted 1 line · Nhat Pham
On Wed, Jul 15, 2026 at 7:19 AM Baoquan He <[email protected]> wrote:
...snip...
Quoted 5 lines
Quoted 3 lines
Part of above sentence is missing. I meant if you agree on the ghost solution, you can change to take its way and continue, just add me to CC. Or we can work together.
How about this - let's solve one piece of the puzzle together, one at a time.
I took your suggestion to heart. I've been working on the first piece:
dynamic cluster management for xswap (vswap) devices — the VM_SPARSE vmalloc approach for growing and shrinking the cluster_info array on demand.
The series is 10 patches (plus Chris's xswap base patch) and I just posted it:
https://lore.kernel.org/all/[email protected]/
The idea is simple: instead of allocating the full cluster_info[] at swapon, we back it with a VM_SPARSE vmalloc area that maps physical pages lazily as clusters are allocated, and unmaps them from the tail when enough contiguous free clusters accumulate. Lookup stays O(1) via array indexing — no extra xarray in the cluster access path.
Once the dynamic cluster management lands, you'd add the per-slot backend pointer, writeback, and rmap on top — without changing the cluster_info lookup path.
The shrink is coarser than what xarray could do (full pages at a time, not individual clusters), but for the initial landing where swap usage tends to be ratchet-like, I think this is a reasonable trade-off. If the need arises, we can always revisit the shrink granularity later.
Curious to hear what you think — especially whether this provides the cluster management your VS series needs.
Thanks
Baoquan
Quoted 57 lines
First, your optimization patch series. Have you taken a look at my proposal ([1])? I believe that design should be cleaner, while still allowing you to put it all you want to get (elimination of xarray being the chief benefit), without requiring you to modify swap cache and swap count operations (since you don't have to move the metadata in and out of struct
zswap_entry). It does require a bit more code to dynamically allocated the backend array, but should be trivial IMHO.[1]: https://lore.kernel.org/all/CAKEwX=OvR7GbU_9f2h_MtU4m0g6s-esHmNQKYNhJz610M0P3Sw@mail.gmail.com/
Next, if you just want pure ghost swapfile without writeback support, maybe something similar to the first 2 patches in my RFC? i.e up to this patch:
https://lore.kernel.org/all/[email protected]/
I'm sure you could find issues/bugs in it too with some extensive testing (a lot of my performance testing is done on the entire series as a whole), but from a design point of view it should achieve what us both want, no? It's literally just a ghost swap device - most of the other design elements of vswap (the new charging behavior, rmap, support for writeback) comes in latter patches. I think the difference is:
1. It's transparently dynamic, whereas in your RFC it's driven by a userspace knob. Do you think there are inherent values in a userspace knob?
2. The address space can also shrink when swap usage drops at runtime.
3. The dynamic address space is backed by an xarray. Note that it's structured differently than zswap's one (it's an xarray pointing to clusters). If you have rationales or numbers to show that vmalloc array is superior (while still support the kernel-driven expansion and shrink), please let me know.
If you have no faith in me and do not like my code, you can take the code as a starting point, or just the idea, and drive it yourself. Or let me know if you're unhappy with any piece of it - we can discuss further.
Quoted 3 lines
Quoted 3 lines
Otherwise, please continue to push virtual swap forward. I'll stop and wait.
Look man, I care about the problems being solved and I'm a sucker for good ideas, but I'm not married to anything, be it my code or my ideas.
I have literally dropped my old code and adopted Kairui's suggestions after playing with it and liking it, to make sure every party's concern is met.
If you have concerns, please let me know, or keep sending code if you believe you can get it done better than I do :) All I'm asking is you take my concerns into account in your design.
Chris Li
maintainerHi Baoquan,
Thanks for the patches series. That is very interesting take.
Some high level feedback follows:
Quoted 30 lines · Baoquan He
This series introduces a virtual "ghost" swapfile that can be backed by multiple physical swap devices, switching between them at runtime using a new "Redirect" swap table entry type.
Motivation
----------
Physical swap devices have fixed, large sizes but a machine may only need a fraction at any given time. Ghost swap decouples the logical swap capacity from physical swap allocation: it presents a large virtual swap device to userspace while lazily mapping slots to physical devices only when data must leave zswap (writeback). This enables overcommit of swap capacity without wasting physical disk space, and allows the ghost device to grow dynamically at runtime.Ghost swapfile characteristics
--------------------------------
Ghost swap decouples logical swap capacity from physical swap allocation. Key properties:- No backing store required. A ghost swap device can operate purely as a zswap container — compressed pages stay in the zswap pool without ever touching a physical swap device. No physical swap device needs to be attached.
- Optional physical backing. One or more physical swap devices can be swapon'd alongside the ghost device, acting as writeback targets when the zswap pool needs to evict cold pages. The Redirect entry in the ghost swap table links each evicted slot to its physical destination.
Quoted 7 lines
- Dynamic size growth. The ghost device can be extended at runtime via sysfs, up to a current maximum of 1TB. This is implemented through lazy vmalloc: a sparse 64MB virtual address space is reserved at swapon time to cover the full 1TB range, but physical pages for cluster_info[] structures are allocated only on demand as the device grows. The
cluster_infopointer never changes, so all existing accessors (__swap_offset_to_cluster,cluster_index) work unchanged.
I think supporting swap file growth is useful at the swap infrastructure level. I mean generic runtime size growth for swap files. That does not need to tight to ghost swapfile. For example. If your swapfile is backed by an LVM logical volume. You can grow the swapfile size by growing the swapfile's logical volume.
I suggest focus on that allow increase of si->max as the baby step and keep the optinal physical backing for later phase. That should make your series much smaller and easier to merge.
Quoted 3 lines
- sysfs control. Each ghost device exposes attributes under /sys/kernel/mm/ghost_swap/ghost_<N>/:
BTW, I want to rename the ghost swap file to remove the ghost part. I inherited that ghost name but I feel ghost is a bit too spooky for my taste. I am pretty bad at naming stuff myself. I was thinking something like "VX swapfile" (virtual extendable swapfile) for example.
Quoted 1 line
path (RO) — backing device name
Let's focus on the extendable part for now and worry about the physical backing device later.
Quoted 2 lines
max (RW) — current max pages; writing a larger value triggers
swap_ghost_extend_max()
We can use a debugfs entry before we settle on the final implementation. It is useful for demo and testing purposes if we have some way to trigger the growth. Having the ability to grow is more important than how it is triggered. We can also let it auto grow as needed based on config.
BTW, I notice your patch series doesn't apply cleanly on mm-unstable. I have to resolve some conflicts myself.
Quoted 11 lines
Design overview
---------------
A swap table slot can now hold a Redirect entry:Redirect: |----- physical
swp_entry_t-----|101|This stores a plain
swp_entry_tpointing to a slot on a real (physical) swap device. The low 3-bit mark (0b101) distinguishes it from Pointer (0b100), PFN (0b10), Shadow (0b1), and NULL (0) entries.
Quoted 6 lines
The lifecycle is:
1. swapon a ghost device — reserves sparse vmalloc area (64MB virtual) and physically backs only the initial range. The device starts with no physical backing store: pages swapped out go to zswap, and zswap pool evictions go to any concurrently attached physical device.
Again, let's make it more generic also works on physical device change size as well.
Quoted 5 lines
2. zswap writeback from ghost: when the zswap pool needs to evict,
zswap_writeback_ghost()allocates a physical slot from any real swap device, writes the data there, and plants a Redirect entry in the ghost's swap table pointing to the physical slot.
Don't worry about zswap write back for now. Able to grow swap device size is a useful phase by itself.
Quoted 12 lines
3. Swap-in from ghost:
swap_read_folio()detects the ghost device (SWP_GHOST), resolves the Redirect to the physicalswp_entry_t, and forwards the read I/O to the physical device.4. When the swap cache folio is removed, the Redirect entry is restored from an xarray (
redirect_xa) that persists the mapping, so subsequent reads still find the physical backing.5. When the physical slot is freed, the ghost slot is also freed, cascading through
swap_range_free().
Quoted 9 lines
Note:
------
Ghost swapfile is a proof of concept. Compared with the other patchset:
[RFC PATCH v2 0/7] mm, swap: Virtual Swap Space (Swap Table Edition), it explores a different approach to implementing virtual swap on top of the existing swap table infrastructure, with a simpler implementation. It builds on Chris Li's earlier ghost swapfile work and is currently tentatively named "ghost swapfile" — alternative naming suggestions are welcome.
VXSwap or XSwap
Quoted 3 lines
Ghost swapfile setup can also be integrated through modifications to the swapon API and the swapon command in util-linux, for example:
swapon --ghost ghost_1:2G -p 5.
For the ghost swapfile, the eventual goal is that its size can automatically grow as needed because there is no phyiscal backing device.
Chris
Quoted 29 lines
Baoquan He (9):
mm/swap_table: add Redirect entry encoding for ghost swap backend
switching
mm/swap: addredirect_xafield and ghost redirect helper declarations mm/swapfile: implement ghost redirect helpers and free-path cascademm/swap_state: restore Redirect entry when swap cache folio is removed mm/zswap: implement ghost-to-physical writeback for backend switchingmm/page_io: forward ghost swap reads to physical device via Redirect mm/swapfile: manage ghostcluster_infovia lazy vmalloc mm/swapfile: implementswap_ghost_extend_max()for dynamic growth mm/swapfile: add sysfs interface for ghost swap extensionChris Li (1):
mm: ghost swapfile support for zswapinclude/linux/swap.h | 10 + mm/page_io.c | 51 +++- mm/swap.h | 16 +- mm/swap_state.c | 28 +++ mm/swap_table.h | 38 +++ mm/swapfile.c | 579 ++++++++++++++++++++++++++++++++++++++++--- mm/zswap.c | 98 +++++++- 7 files changed, 772 insertions(+), 48 deletions(-)--
2.54.0