[PATCH v3 00/14] mm, swap: extendable swap devices (xswap)

Baoquan He posted 14 patches 1 week, 1 day ago
include/linux/swap.h |   26 +-
mm/Kconfig           |    9 +
mm/page_io.c         |   19 +
mm/swap_state.c      |    4 +
mm/swapfile.c        | 1240 +++++++++++++++++++++++++++++++++++++-----
mm/zswap.c           |    7 +-
6 files changed, 1174 insertions(+), 131 deletions(-)
[PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Baoquan He 1 week, 1 day ago
xswap is a swap device with no backing storage. Swapped-out pages live                                
in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,                                 
and the area is grown and shrunk on demand as swap usage changes.                                     
                                                                                                      
The problem being solved is the static size of compressed swap. Both   
zram and zswap need the size fixed in advance, and neither gives memory 
back when the workload shrinks. The solution should be a device whose
size can scale up/down as per usage. xswap does that by mapping the
metadata lazily instead of reserving it for the whole range.

Design   
------                           
- si->cluster_info[] stays a plain array. Access is still
  &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no 
  RCU discipline, no tear-down state machine, no NULL return.              
- Only an initial chunk is mapped at creation. The rest of the address     
  space is reserved, not allocated, so an idle device costs nothing.
- Growth is driven by allocation. When no free cluster is left and the       
  address space has room, the next chunk is mapped and added to the free  
  list. No userspace involvement.
- Shrink is driven by frees. The free tail is scanned, and whole chunks
  are unmapped once the mapped range is at most half in use and several
  chunks can go. One chunk is left mapped as slack, so the next                                       
  allocation does not map it straight back. A ceiling lowered below the                               
  mapped range skips the half-in-use rule and is enforced at once.

Size
----
A device starts at 1xRAM, rounded down to the cluster. That costs
nothing, because the mapping is lazy. The underlying address space is
2xRAM. An optional per-device cap,
/sys/kernel/mm/xswap/type<N>/limit, lets an admin lower the ceiling;
the excess is unmapped right away. Grow and shrink both work without
it. Creating a device requires zswap.

Interface
---------
  /sys/kernel/mm/xswap/create           write an optional priority
  /sys/kernel/mm/xswap/destroy          write a swap type
  /sys/kernel/mm/xswap/type<N>/limit    read/write, in pages
The device shows up in /proc/swaps as xswap<N>.

Note
----
Writeback, rmap lookup, etc. are consumers of this base. I have a
writeback prototype on top of this base and will post it as a reference.

Testing
-------
qemu KVM guest, 8G RAM.

Tested create/destroy, raising and lowering the limit (including clamping
when it is written below the pages in use), shrink with live entries, and
2000 create/destroy cycles for leaks; all passed.

The workload is memhog: it faults in N GB of anonymous memory inside a
cgroup with a much smaller memory.max, forcing the pages to swap.
Set MEMHOG_FILL=pattern: the default fill is all-zero pages that zswap
compresses to almost nothing, so the device never fills.

  # echo 1 > /sys/module/zswap/parameters/enabled
  # mkdir -p /sys/fs/cgroup/xswap_limit
  # echo max > /sys/fs/cgroup/xswap_limit/memory.swap.max
  # MEM="MEMHOG_FILL=pattern numactl --cpunodebind=0 --membind=0 ./memhog"

1. Create and destroy

   # echo > /sys/kernel/mm/xswap/create
   # swapon
   NAME    TYPE  SIZE USED PRIO
   xswap0  xswap 7.8G   0B   -1
   # cat /sys/kernel/mm/xswap/type0/limit
   2035199
   # echo 0 > /sys/kernel/mm/xswap/destroy
   # swapon
   (nothing)

   limit is in 4 KiB pages; 2035199 is RAM (2034976 pages) rounded up to a
   whole number of clusters. The device starts at RAM, not twice RAM.

2. The cap holds

   # echo 2147483648 > /sys/fs/cgroup/xswap_limit/memory.max
   # ( echo $$ > /sys/fs/cgroup/xswap_limit/cgroup.procs; eval $MEM 11 300 ) &
   # awk '/SwapTotal|SwapFree/' /proc/meminfo
   SwapTotal:       8140796 kB
   SwapFree:         354012 kB

   The cgroup runs out of room before the device does and the OOM killer
   takes the workload \ufffd\ufffd\ufffd that is the pass signal. SwapFree never exceeds
   SwapTotal, so nr_swap_pages never goes negative.

3. Raising the cap

   # echo 3052543 > /sys/kernel/mm/xswap/type0/limit
   # awk '/SwapTotal/' /proc/meminfo
   SwapTotal:      12210172 kB
   # ( echo $$ > /sys/fs/cgroup/xswap_limit/cgroup.procs; eval $MEM 11 300 ) &

   No OOM this time: 2473705 pages in use against 2034976 pages of RAM, so
   usage goes past RAM.

4. Lowering the cap below the pages in use

   # echo 1000000 > /sys/kernel/mm/xswap/type0/limit
   # cat /sys/kernel/mm/xswap/type0/limit
   2426879
   # awk '/SwapTotal|SwapFree/' /proc/meminfo
   SwapTotal:       9707516 kB
   SwapFree:             860 kB

   The write is clamped up to the clusters covering the pages in use, so
   the free slots in the partially used top cluster stay accounted for.

5. Shrink with live entries, then destroy

   # echo 4069887 > /sys/kernel/mm/xswap/type0/limit
   # sleep 60
   # awk '/SwapFree/' /proc/meminfo
   SwapFree:       16279548 kB

   The shrink unmapped the tail \ufffd\ufffd\ufffd the state find_next_to_unuse() must
   survive. Put live entries back and destroy:

   # ( echo $$ > /sys/fs/cgroup/xswap_limit/cgroup.procs; eval $MEM 3 300 ) &
   # echo max > /sys/fs/cgroup/xswap_limit/memory.max
   # echo 0 > /sys/kernel/mm/xswap/destroy
   # swapon
   (nothing)

   dmesg stays clean across create, shrink, swapoff and destroy.

6. 2000 create/destroy cycles, diffing /proc/slabinfo before and after:
   the largest growth is 142 objects. One object leaked per cycle would
   be 2000.

Performance
-----------
(qemu KVM guest, 8G RAM, zram as the swap device)                                                         
This series should not slow down a kernel that never creates an xswap
device. I measured that overhead by comparing the base tree with this
series. Both were built with the same .config and CONFIG_XSWAP=y, and no
xswap device was created. I ran three 3G MADV_PAGEOUT workloads, three
rounds each, alternating between the two kernels across reboots. I
counted retired instructions per page swapped out with perf stat:

  workload                      base       series     delta
  swapout                       50824.8    50866.2    +0.08%
  swapout and swapin            63770.4    63796.8    +0.04%
  swapout into a full device    67729.9    67707.8    -0.03%

Two runs of the same kernel differ by less than 0.1%, so the differences
above are real, not measurement noise. I cannot use wall clock time for
this comparison, because two runs of the same kernel differ by more than
the two kernels do.

Changelog
=========
v2 -> v3:
- Rebased onto the latest mm-new.

- The grow path now honors the user-set ceiling (si->nr_clusters) instead
  of growing up to nr_clusters_max, and a ceiling below the mapped range
  is unmapped exactly instead of rounded to a chunk (patches 12 and 14).

- The limit write clamps the ceiling up to the clusters covering the pages
  in use, replacing the earlier WARN_ONCE; si->pages becomes mutable at
  runtime (patch 13).

- Minor comment and cleanup changes.

v1->v2:
- Patch 1 (mm: zswap: return -ENOENT when the swap device is gone) is not
  part of this series; it was posted separately.

- There is only one size knob now. The runtime ceiling and the debugfs
  per-device limit are gone. All that is left is the optional per-device
  cap, /sys/kernel/mm/xswap/type<N>/limit. Grow and shrink work without
  it.

- The shrink no longer keeps its own count of the free tail. It scans the
  tail instead, and dropping the counter also removes a call from the
  cluster allocation path.

- The priority is no longer a patch of its own. The create attribute
  takes it:
    echo 100 > /sys/kernel/mm/xswap/create

RFC v3 -> v1
- Add patch 16 to support setting xswap device priority at creation.
  The create sysfs interface (/sys/kernel/mm/xswap/create) previously
  hardcoded every new device's priority to DEF_SWAP_PRIO, it now
  accepts an optional priority:

    echo "<percent> [<prio>]" > /sys/kernel/mm/xswap/create

- Bug fix: xswap_lock init ordering. mutex_init(&si->xswap_lock) was called
  after xswap_map_clusters() (which locks it), i.e. locking an uninitialized
  mutex. Init now before the first xswap_map_clusters() call. Thanks to Klara.

- Bug fix: Fixes a compile error in !CONFIG_XSWAP builds. xswap_debugfs_root
  is declared inside CONFIG_XSWAP ifdeffery scope, so the ungarded use
  caused error when CONFIG_XSWAP is off.

RFC v2-> RFC v3:
- Replace the "header-only swap file + swapon" creation hack with a
  proper file-less device created and destroyed via sysfs
  (/sys/kernel/mm/xswap/{create,destroy}).  This required the
  __swapoff() refactor and the free_swap_cluster_info() signature
  change (patches 4, 6, 14).

- Require zswap: refuse to create an xswap device when zswap is
  unavailable (patch 15).

- Split the unrelated zswap -ENOENT fix out of the series into a
  standalone patch (patch 1).

- Fix nr_free_tail over-counting on concurrent grow, shrink leaking
  detached clusters on early bail-out, a re-init race on cluster
  spinlocks in xswap_map_clusters(), the nr_clusters_mapped update
  ordering, and swapoff accessing the shrinker-unmapped cluster tail.

- Minor cleanups (checkpatch, /proc/swaps alignment, commit messages).

RFC v1-> RFC v2:
- Added __GFP_HIGH | __GFP_NOMEMALLOC to alloc_page() and kmalloc_array()
  in the grow path, plus memalloc_noreclaim_save/restore() wrapping,
  to prevent the grow path from consuming emergency memory reserves
  or recursing into swap under PF_MEMALLOC. This is folded into patch 3.
  This was pointed out by Nhat.

- Folded the mutex serialization fix into the cluster grow patch (patch
  3). This is suggested by Nhat.

- Fixed coding style issues: corrected indentation of declarations in
  xswap_unmap_clusters(), removed unnecessary block scope around the
  err variable in xswap_map_clusters().

- Rebased onto mm-unstable

Baoquan He (13):
  mm, swap: add CONFIG_XSWAP and xswap fields to swap_info_struct
  mm, swap: refactor free_swap_cluster_info to take swap_info_struct
  mm, swap: add xswap cluster grow via VM_SPARSE vmalloc
  mm, swap: add sysfs create interface for xswap
  mm, swap: add xswap grow trigger on cluster allocation
  mm, swap: add xswap_try_shrink and shrink trigger on cluster free
  mm, swap: free backing pages in xswap_unmap_clusters
  mm, swap: defer xswap shrink to workqueue to avoid lock recursion
  mm, swap: refactor swapoff and add xswap_destroy
  mm, swap: require zswap for xswap devices
  mm, swap: cap xswap growth at nr_clusters
  mm, swap: add sysfs per-device size limit for xswap
  mm, swap: shrink xswap to the ceiling when it drops

Chris Li (1):
  mm: xswap support for zswap

 include/linux/swap.h |   26 +-
 mm/Kconfig           |    9 +
 mm/page_io.c         |   19 +
 mm/swap_state.c      |    4 +
 mm/swapfile.c        | 1240 +++++++++++++++++++++++++++++++++++++-----
 mm/zswap.c           |    7 +-
 6 files changed, 1174 insertions(+), 131 deletions(-)


base-commit: baa8de2f3448d1466a888a805c18d01c998fe052
-- 
2.54.0
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Nhat Pham 1 week ago
On Wed, Sep 16, 2026 at 3:19 AM Baoquan He <hebaoquan@kylinos.cn> wrote:
>
> xswap is a swap device with no backing storage. Swapped-out pages live
> in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,
> and the area is grown and shrunk on demand as swap usage changes.
>
> The problem being solved is the static size of compressed swap. Both
> zram and zswap need the size fixed in advance, and neither gives memory
> back when the workload shrinks. The solution should be a device whose
> size can scale up/down as per usage. xswap does that by mapping the
> metadata lazily instead of reserving it for the whole range.
>
> Design
> ------
> - si->cluster_info[] stays a plain array. Access is still
>   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no
>   RCU discipline, no tear-down state machine, no NULL return.
> - Only an initial chunk is mapped at creation. The rest of the address
>   space is reserved, not allocated, so an idle device costs nothing.
> - Growth is driven by allocation. When no free cluster is left and the
>   address space has room, the next chunk is mapped and added to the free
>   list. No userspace involvement.
> - Shrink is driven by frees. The free tail is scanned, and whole chunks
>   are unmapped once the mapped range is at most half in use and several
>   chunks can go. One chunk is left mapped as slack, so the next
>   allocation does not map it straight back. A ceiling lowered below the
>   mapped range skips the half-in-use rule and is enforced at once.
>
> Size
> ----
> A device starts at 1xRAM, rounded down to the cluster. That costs
> nothing, because the mapping is lazy. The underlying address space is
> 2xRAM. An optional per-device cap,
> /sys/kernel/mm/xswap/type<N>/limit, lets an admin lower the ceiling;
> the excess is unmapped right away. Grow and shrink both work without
> it. Creating a device requires zswap.
>
> Interface
> ---------
>   /sys/kernel/mm/xswap/create           write an optional priority
>   /sys/kernel/mm/xswap/destroy          write a swap type
>   /sys/kernel/mm/xswap/type<N>/limit    read/write, in pages
> The device shows up in /proc/swaps as xswap<N>.
>
> Note
> ----
> Writeback, rmap lookup, etc. are consumers of this base. I have a
> writeback prototype on top of this base and will post it as a reference.

Thanks for posting v3.

I spent a while building the other half of what I want out of this on
top of your series, to see how much work is needed if we are to expand
from xswap to cover the vswap use case.

It is actually way more work than I anticipated. And a lot of it is
because of the way you indiscriminately apply the full swap device
model to xswap, without careful consideration of actual use cases.

Johannes has already discussed one piece of that, the sizing knob [1],
so here I would like to expand on other design and interface choices.

First, in terms of priority, an xswap device has to be preferred over
disk swap. This is already zswap's model, as it is the *only* coherent
swap tiering scheme. Exposing priority selection to userspace just
invites misconfiguration - what if a user accidentally sets physical
swap to be preferred over zswap?

Next, regarding the ability to create multiple xswap devices:

With at most one virtual device, there is no selection problem to solve
at all: vswap's entire device-selection policy is reduced to two
branches: if vswap is possible, go for it; otherwise try physical swap
device. No need to think about policy to select which virtual device to
allocate. No need to track virtual vs physical devices to select the
right class for allocation, as you can just put the virtual device in a
pointer, and off the avail list (which is only used for physical
devices). These suddenly matter when you add multiple xswap devices.
And for what purpose? This has never been properly justified to me.

On the flip side, there are cases where your treatment of the xswap
device as "just an ordinary swap device" actually breaks *existing*
deployment, and makes several code paths more complicated.

For instance, at allocation time, cgroups that disable zswap might get
an xswap slot. At swap_writeout() time, it is *stuck* - we already do
the unmapping step, so we cannot reclaim the page. Ironically a
physical swapfile backend could have bailed you out here, but you have
not implemented it yet :)

The other way you can get around it is checking if the folio being
reclaimed belongs to a cgroup that allows for disk swap or zswap, and
bypass xswap/vswap if so. But that brings me to my next point: since
you just treat xswap/vswap as a normal swap device and share all the
allocation structures and logic, making such a selection becomes way
more complicated code-wise, and less efficient at runtime too (since
the per-cpu cache of clusters for allocation is shared for all swap
devices in your code).

As of this version, it not only does not work for the writeback use
case. It cannot even work in deployments where some workloads (cgroup)
select zswap, whereas others select disk swap. It can only work if you
intend to have a single class of device - either virtual/xswap or disk
swap - but not both.

There are more, but I think these already illustrate my points.

The root cause of all of these is that xswap reuses the swap device full
machinery and interface, even where it does not make sense, or where it
exposes a policy decision the kernel should be making (priority, size,
number of instances).

vswap's interface came from the other end: what has to be true for
zswap to be an independent AND co-existing tier, and what needs
userspace's input for that to work. The answer was close to nothing, which
is why close to nothing is exposed, and why there is so little to get wrong
inside it. It also simplifies the logic in many places.

Constraint liberates; liberty constrains.

So let me flip it on its head. What if we keep the interface I already
have, and use your data structure instead? I tried that too [2], and it
was significantly easier, because it is just a data structure change,
without having to accomodate for the full interface of swap device.

Happy to expand on the prototype if necessary.

Thanks,
Nhat

[1] https://lore.kernel.org/all/aqLi6cIjD2wJwk0B@cmpxchg.org/
[2] https://lore.kernel.org/all/20260910232704.3364879-1-nphamcs@gmail.com/
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Baoquan He 3 days, 22 hours ago
On 09/17/26 at 05:13pm, Nhat Pham wrote:
> On Wed, Sep 16, 2026 at 3:19 AM Baoquan He <hebaoquan@kylinos.cn> wrote:
> >
> > xswap is a swap device with no backing storage. Swapped-out pages live
> > in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,
> > and the area is grown and shrunk on demand as swap usage changes.
> >
> > The problem being solved is the static size of compressed swap. Both
> > zram and zswap need the size fixed in advance, and neither gives memory
> > back when the workload shrinks. The solution should be a device whose
> > size can scale up/down as per usage. xswap does that by mapping the
> > metadata lazily instead of reserving it for the whole range.
> >
> > Design
> > ------
> > - si->cluster_info[] stays a plain array. Access is still
> >   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no
> >   RCU discipline, no tear-down state machine, no NULL return.
> > - Only an initial chunk is mapped at creation. The rest of the address
> >   space is reserved, not allocated, so an idle device costs nothing.
> > - Growth is driven by allocation. When no free cluster is left and the
> >   address space has room, the next chunk is mapped and added to the free
> >   list. No userspace involvement.
> > - Shrink is driven by frees. The free tail is scanned, and whole chunks
> >   are unmapped once the mapped range is at most half in use and several
> >   chunks can go. One chunk is left mapped as slack, so the next
> >   allocation does not map it straight back. A ceiling lowered below the
> >   mapped range skips the half-in-use rule and is enforced at once.
> >
> > Size
> > ----
> > A device starts at 1xRAM, rounded down to the cluster. That costs
> > nothing, because the mapping is lazy. The underlying address space is
> > 2xRAM. An optional per-device cap,
> > /sys/kernel/mm/xswap/type<N>/limit, lets an admin lower the ceiling;
> > the excess is unmapped right away. Grow and shrink both work without
> > it. Creating a device requires zswap.
> >
> > Interface
> > ---------
> >   /sys/kernel/mm/xswap/create           write an optional priority
> >   /sys/kernel/mm/xswap/destroy          write a swap type
> >   /sys/kernel/mm/xswap/type<N>/limit    read/write, in pages
> > The device shows up in /proc/swaps as xswap<N>.
> >
> > Note
> > ----
> > Writeback, rmap lookup, etc. are consumers of this base. I have a
> > writeback prototype on top of this base and will post it as a reference.
> 
> Thanks for posting v3.
> 
> I spent a while building the other half of what I want out of this on
> top of your series, to see how much work is needed if we are to expand
> from xswap to cover the vswap use case.
> 
> It is actually way more work than I anticipated. And a lot of it is
> because of the way you indiscriminately apply the full swap device
> model to xswap, without careful consideration of actual use cases.

Don't worry, I have made a RFC to support xswap writeback, rmap, thp,
charging, etc. You can take it over and make it formal to post if you
decide to join to work together.

Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Nhat Pham 6 days, 10 hours ago
On Thu, Sep 17, 2026 at 5:13 PM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Wed, Sep 16, 2026 at 3:19 AM Baoquan He <hebaoquan@kylinos.cn> wrote:
>
> Constraint liberates; liberty constrains.
>
> So let me flip it on its head. What if we keep the interface I already
> have, and use your data structure instead? I tried that too [2], and it
> was significantly easier, because it is just a data structure change,
> without having to accomodate for the full interface of swap device.

Here's an updated version of this proposal:

https://lore.kernel.org/all/20260918180241.3424851-12-nphamcs@gmail.com/

Per your earlier request, I also:

1. Moved the virtual_table inside struct swap_cluster_info (since the
virtual table is the only new field now), and deleted the new dynamic
cluster struct.

2. I skip the shrinking mechanism, but instead I dynamically freed the
virtual table to minimize metadata overhead of free clusters.

I have not run performance tests yet, but it has survived a couple of
simple stress test rounds that I threw at it so far.

If you think this is a good proposal, please pick it up and drive it.
The code is yours :)
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Nhat Pham 1 week ago
On Thu, Sep 17, 2026 at 5:13 PM Nhat Pham <nphamcs@gmail.com> wrote:
>
> The other way you can get around it is checking if the folio being
> reclaimed belongs to a cgroup that allows for disk swap or zswap, and
> bypass xswap/vswap if so. But that brings me to my next point: since

s/if so/if the cgroup does not allow for zswap

(in case it was confusing).
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Baoquan He 1 week ago
On 09/16/26 at 06:19pm, Baoquan He wrote:

For Sashiko complaints: 

============================================================                                            
Subject: Re: [PATCH v3 01/14] mm: xswap support for zswap                                               
                                                                                                        
│ xswap entries are added to the zswap writeback LRU but                                                
│ zswap_writeback_entry() rejects them with -EINVAL, so shrink_memcg_cb()                               
│ retries and zswap_reject_reclaim_fail keeps climbing.                                                 

Right. An xswap entry has no backing store, so it should not be a
writeback candidate. v4 no longer adds it to the zswap writeback LRU;
zswap_lru_del() tolerates that (__list_lru_del() checks list_empty()
first, and entry->lru is initialized before free). The shrinker then
neither scans nor counts these entries. 

│ swap_vma_readahead() does not skip xswap, unlike swap_cluster_readahead().
                                                                                                        
Right, I only added the check to the cluster path. v4 adds the same                                    
SWP_XSWAP check to swap_vma_readahead().                                                                
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 04/14] mm, swap: add xswap cluster grow via VM_SPARSE vmalloc                    
                                                                                                        
│ Returning -EBUSY from vm_area_map_pages() skips vm_area_unmap_pages(),                                
│ then the pages are freed while their PTEs are still populated.                                        
                                                                                                        
This path is unreachable. vmap_pages_pte_range() does return -EBUSY, but                               
vmap_pages_pmd_range() and vmap_pages_pud_range() normalize the return to                               
-ENOMEM, so vm_area_map_pages() never returns -EBUSY. A collision takes                                
the -ENOMEM path, which calls vm_area_unmap_pages() before freeing the                                  
pages, so no freed page is left mapped.                                                                 
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 05/14] mm, swap: add sysfs create interface for xswap                            
                                                                                                        
│ maxpages smaller than SWAPFILE_CLUSTER skips the rounddown(), leaving                                 
│ si->max unaligned.                                                                                    
                                                                                                        
That needs 2 * RAM < SWAPFILE_CLUSTER, i.e. RAM below about 1MB. Not                                   
reachable in practice.                                                                                  
                                                                                                        
│ sysfs_create_group() failure leaks xswap_kobj.                                                        
                                                                                                        
Fixed in v4: kobject_put(xswap_kobj) and clear the pointer on that path.                                
                                                                                                        
│ pr_info() after enable_swap_info() races with swapoff reading si.                                     
                                                                                                        
Fixed in v4: the message is printed while swapon_mutex is still held.                                   
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 06/14] mm, swap: add xswap grow trigger on cluster allocation                    
                                                                                                        
│ free_clusters can be populated concurrently after the scans, so the grow                              
│ block is skipped and the allocation fails.                                                            
│ -EAGAIN from xswap_map_clusters() (another grower won) is not retried.                                
                                                                                                        
Fixed in v4: retry alloc_swap_scan_list(free_clusters) once after the grow                              
attempt, which covers both cases.                                                                       
                                                                                                        
│ backing pages are leaked on swapoff.                                                                  
                                                                                                        
That is fixed by the patch that follows ("mm, swap: free backing pages in                               
xswap_unmap_clusters"); patch 06 has no unmap path yet.                                                 
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 08/14] mm, swap: free backing pages in xswap_unmap_clusters                      
                                                                                                        
│ The teardown callers retry the unmap in an unbounded while loop.                                      
│ kmalloc_array() under memalloc_noreclaim_save() is a high-order,                                      
│ non-reclaimable allocation that can fail under fragmentation.                                         
                                                                                                        
Both fixed in v4. The array is now allocated with kvmalloc_array() and                                
GFP_KERNEL. It can fall back to vmalloc and it can reclaim, and the                                   
unmap always runs in process context, so this is fine. If it still                                    
fails, we unmap in small batches using a stack array. So teardown                                     
always makes progress, and no page is lost. The function cannot fail                                  
now, so we removed the while() loops and the shrink rollback. The                                     
counters are unsigned long now, and an overflow gives a warning.
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 10/14] mm, swap: refactor swapoff and add xswap_destroy                          
                                                                                                        
│ sysfs_create_group() failure leaks xswap_kobj.                                                        
                                                                                                        
Same as patch 05; fixed in v4 in xswap_sysfs_init().                                                    
                                                                                                        
│ sys_swapoff() mixes goto-based cleanup with scope-based cleanup.                                      
                                                                                                        
This is pre-existing upstream style in sys_swapoff(): CLASS(filename,                                   
pathname) and out_dput/filp_close(victim) are already there before this                                 
series; the patch only moved them while extracting __swapoff(). Both
pathname and victim are released on every path.  I left it unchanged to                                 
avoid unrelated churn, and there is no standard CLASS for a struct file *                               
from file_open_name() anyway.                                                                           
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 11/14] mm, swap: require zswap for xswap devices                                 
                                                                                                        
│ The fix only checks zswap at create time; runtime disabling of zswap and                              
│ runtime zswap_store() failures are not handled.                                                       

Yes, this is a known issue. The create-time check cannot cover (a) zswap                              
being disabled after creation, or (b) zswap_store() failing at runtime                                
(pool full, allocation failure). In both cases swap_writeout() cannot                                 
write the folio out, so it stays in the swap cache until it is faulted                                
back in.                                                                                              
                                                                                                      
So this is a temporary state. The writeback series on top adds the                                    
fallback (write to disk when zswap refuses an xswap page), and then this                              
case becomes the normal swap IO error path that every swap device has.
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 13/14] mm, swap: add sysfs per-device size limit for xswap                       
                                                                                                        
│ del_from_avail_list()/add_to_avail_list() use try_cmpxchg() without a                                 
│ retry loop.                                                                                           
                                                                                                        
That is the pre-existing pattern in these functions (they already used                                  
atomic_long_try_cmpxchg() and skipped on failure before this series); this                              
patch does not change it.                                                                               
                                                                                                        
│ DIV_ROUND_UP(val, SWAPFILE_CLUSTER) overflows for a huge val.                                         
                                                                                                        
Fixed in v4: clamp against (unsigned long)nr_clusters_max * SWAPFILE_CLUSTER                            
before dividing.                                                                                        
                                                                                                        
│ a limit write that makes the device full leaves it on swap_avail_head.                                
                                                                                                        
Fixed in v4: after updating si->pages, call del_from_avail_list() when the                              
device is full, add_to_avail_list() otherwise.                                                          
                                                                                                        
============================================================                                            
Subject: Re: [PATCH v3 14/14] mm, swap: shrink xswap to the ceiling when it drops                       
                                                                                                        
│ the hardcoded excess can include an in-use cluster, and the whole shrink                              
│ aborts.                                                                                               
                                                                                                        
The limit write clamps the new ceiling up to the clusters covering the                                  
pages in use, so [ceiling, mapped) is free and the validation loop passes.                              
The only remaining window is an allocation racing the shrink, which just                                
defers the shrink to the next trigger.                                                                  
                                                                                                        
============================================================ 
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Johannes Weiner 1 week, 1 day ago
On Wed, Sep 16, 2026 at 06:19:07PM +0800, Baoquan He wrote:
> xswap is a swap device with no backing storage. Swapped-out pages live                                
> in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,                                 
> and the area is grown and shrunk on demand as swap usage changes.                                     
>                                                                                                       
> The problem being solved is the static size of compressed swap. Both   
> zram and zswap need the size fixed in advance, and neither gives memory 
> back when the workload shrinks. The solution should be a device whose
> size can scale up/down as per usage. xswap does that by mapping the
> metadata lazily instead of reserving it for the whole range.
> 
> Design   
> ------                           
> - si->cluster_info[] stays a plain array. Access is still
>   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no 
>   RCU discipline, no tear-down state machine, no NULL return.              
> - Only an initial chunk is mapped at creation. The rest of the address     
>   space is reserved, not allocated, so an idle device costs nothing.
> - Growth is driven by allocation. When no free cluster is left and the       
>   address space has room, the next chunk is mapped and added to the free  
>   list. No userspace involvement.
> - Shrink is driven by frees. The free tail is scanned, and whole chunks
>   are unmapped once the mapped range is at most half in use and several
>   chunks can go. One chunk is left mapped as slack, so the next                                       
>   allocation does not map it straight back. A ceiling lowered below the                               
>   mapped range skips the half-in-use rule and is enforced at once.

If the swap maintainers prefer the VM_SPARSE route, I'm happy to defer
to them on that.

However, from the cgroup and zswap camp, two stipulations that I
reasoned out in the other thread[1]:

1. You must not charge compression space as swap space to the cgroup.

2. You must make the compression space large enough to be outside the
   range where users can hit space limits before hitting memory limits.

   That also means not allowing setups where this is possible.

   I'm fine with fixing the zeroed page flood issue separately, as
   Kairui proposed.

So if you're willing to fix the cgroup charging, and if you're willing
to drop the sizing interface for a statically sized space that is
sufficiently large, I think we can find common ground.

[1] https://lore.kernel.org/linux-mm/aqLi6cIjD2wJwk0B@cmpxchg.org/
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Baoquan He 1 week ago
On 09/16/26 at 12:45pm, Johannes Weiner wrote:
> On Wed, Sep 16, 2026 at 06:19:07PM +0800, Baoquan He wrote:
> > xswap is a swap device with no backing storage. Swapped-out pages live                                
> > in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,                                 
> > and the area is grown and shrunk on demand as swap usage changes.                                     
> >                                                                                                       
> > The problem being solved is the static size of compressed swap. Both   
> > zram and zswap need the size fixed in advance, and neither gives memory 
> > back when the workload shrinks. The solution should be a device whose
> > size can scale up/down as per usage. xswap does that by mapping the
> > metadata lazily instead of reserving it for the whole range.
> > 
> > Design   
> > ------                           
> > - si->cluster_info[] stays a plain array. Access is still
> >   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no 
> >   RCU discipline, no tear-down state machine, no NULL return.              
> > - Only an initial chunk is mapped at creation. The rest of the address     
> >   space is reserved, not allocated, so an idle device costs nothing.
> > - Growth is driven by allocation. When no free cluster is left and the       
> >   address space has room, the next chunk is mapped and added to the free  
> >   list. No userspace involvement.
> > - Shrink is driven by frees. The free tail is scanned, and whole chunks
> >   are unmapped once the mapped range is at most half in use and several
> >   chunks can go. One chunk is left mapped as slack, so the next                                       
> >   allocation does not map it straight back. A ceiling lowered below the                               
> >   mapped range skips the half-in-use rule and is enforced at once.
> 
> If the swap maintainers prefer the VM_SPARSE route, I'm happy to defer
> to them on that.
> 
> However, from the cgroup and zswap camp, two stipulations that I
> reasoned out in the other thread[1]:

> 
> 1. You must not charge compression space as swap space to the cgroup.

Hmm, I don't have a stance on this. However, isn't this an issue
zswap/zram have been doing? It feels like an independent issue which
should be done separately?

> 
> 2. You must make the compression space large enough to be outside the
>    range where users can hit space limits before hitting memory limits.

We may need a way to define 'large enough' at first. From my limited
understanding, take zstd (the best compression ratio) as an exmaple,
the ratio is about 30%, 2xRAM as si->max is enough. Unless we want to
swap to the backing disk with huge content which is much much bigger
than RAM when xswap is ful. I am wondering if there is a actual scenario 
and concrete number.

I am not against a large enough si->max size, that's very easy to change
in code, just one line of adjustment. Just a concrete number and reasonable
description is needed. I think this can be done later with a separate
patch with a convincing log if someone can provide?

static int xswap_create(int prio)
{ 
	...
        ram = totalram_pages();
        maxpages = min_t(unsigned long, ram * 2, swapfile_maximum_size);
	...
}

> 
>    That also means not allowing setups where this is possible.

And the limit is only an optional knob. If the admin does not set it,
the device grows to the full address space, so there is no space limit
to hit at all. It already behaves the way you want by default. The knob
is only for admins who want a ceiling, they can use it or not. I hope
this would not be a problem for your use case.                                                  

> 
>    I'm fine with fixing the zeroed page flood issue separately, as
>    Kairui proposed.
> 
> So if you're willing to fix the cgroup charging, and if you're willing
> to drop the sizing interface for a statically sized space that is
> sufficiently large, I think we can find common ground.

Thanks for the input, I am open to discuss either of them further.

> 
> [1] https://lore.kernel.org/linux-mm/aqLi6cIjD2wJwk0B@cmpxchg.org/
>
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Johannes Weiner 1 week ago
On Thu, Sep 17, 2026 at 03:31:23PM +0800, Baoquan He wrote:
> On 09/16/26 at 12:45pm, Johannes Weiner wrote:
> > On Wed, Sep 16, 2026 at 06:19:07PM +0800, Baoquan He wrote:
> > > xswap is a swap device with no backing storage. Swapped-out pages live                                
> > > in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,                                 
> > > and the area is grown and shrunk on demand as swap usage changes.                                     
> > >                                                                                                       
> > > The problem being solved is the static size of compressed swap. Both   
> > > zram and zswap need the size fixed in advance, and neither gives memory 
> > > back when the workload shrinks. The solution should be a device whose
> > > size can scale up/down as per usage. xswap does that by mapping the
> > > metadata lazily instead of reserving it for the whole range.
> > > 
> > > Design   
> > > ------                           
> > > - si->cluster_info[] stays a plain array. Access is still
> > >   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no 
> > >   RCU discipline, no tear-down state machine, no NULL return.              
> > > - Only an initial chunk is mapped at creation. The rest of the address     
> > >   space is reserved, not allocated, so an idle device costs nothing.
> > > - Growth is driven by allocation. When no free cluster is left and the       
> > >   address space has room, the next chunk is mapped and added to the free  
> > >   list. No userspace involvement.
> > > - Shrink is driven by frees. The free tail is scanned, and whole chunks
> > >   are unmapped once the mapped range is at most half in use and several
> > >   chunks can go. One chunk is left mapped as slack, so the next                                       
> > >   allocation does not map it straight back. A ceiling lowered below the                               
> > >   mapped range skips the half-in-use rule and is enforced at once.
> > 
> > If the swap maintainers prefer the VM_SPARSE route, I'm happy to defer
> > to them on that.
> > 
> > However, from the cgroup and zswap camp, two stipulations that I
> > reasoned out in the other thread[1]:
> 
> > 
> > 1. You must not charge compression space as swap space to the cgroup.
> 
> Hmm, I don't have a stance on this. However, isn't this an issue
> zswap/zram have been doing? It feels like an independent issue which
> should be done separately?

If you have 3 containers using compression space, and two of them have
writeback enabled to a shared swapfile, the memory.swap.* controls
need to work to manage fair access to that swapfile. They do not work
if compression space itself is conflated in.

Right now zswap entries actually consume physical swapfile space, even
before writeback. Charging the space is correct. But the whole point
is to decouple compression space from physical swap space.

This is not something that can be done later. It would be a dramatic
user-visible change to how the resource is categorized and managed.

> > 2. You must make the compression space large enough to be outside the
> >    range where users can hit space limits before hitting memory limits.
> 
> We may need a way to define 'large enough' at first.

I've tried to lay this out in the other thread, and highlighted the
usability issues that result from hitting compression space limits
prematurely. It's kind of your call whether you want to seriously
engage with this or not.

But ultimately it's your claim that a static size can be made to work,
so it's on you to make a convincing case.

> >    That also means not allowing setups where this is possible.
> 
> And the limit is only an optional knob. If the admin does not set it,
> the device grows to the full address space, so there is no space limit
> to hit at all. It already behaves the way you want by default. The knob
> is only for admins who want a ceiling, they can use it or not. I hope
> this would not be a problem for your use case.                                                  

No, I've laid this out already as well.

This isn't about "my" usecase. It's about designing a coherent
interface that works well with a large number of usecases, and other
pieces of kernel infrastructure commonly used in conjunction.

The other proposal in the room needs no such interface. The burden of
proof for adding one is on you.

> > [1] https://lore.kernel.org/linux-mm/aqLi6cIjD2wJwk0B@cmpxchg.org/
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Chris Li 3 days, 18 hours ago
On Thu, Sep 17, 2026 at 3:17 AM Johannes Weiner <hannes@cmpxchg.org> wrote:
>
> On Thu, Sep 17, 2026 at 03:31:23PM +0800, Baoquan He wrote:
> > On 09/16/26 at 12:45pm, Johannes Weiner wrote:
> > > On Wed, Sep 16, 2026 at 06:19:07PM +0800, Baoquan He wrote:
> > > > xswap is a swap device with no backing storage. Swapped-out pages live
> > > > in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,
> > > > and the area is grown and shrunk on demand as swap usage changes.
> > > >
> > > > The problem being solved is the static size of compressed swap. Both
> > > > zram and zswap need the size fixed in advance, and neither gives memory
> > > > back when the workload shrinks. The solution should be a device whose
> > > > size can scale up/down as per usage. xswap does that by mapping the
> > > > metadata lazily instead of reserving it for the whole range.
> > > >
> > > > Design
> > > > ------
> > > > - si->cluster_info[] stays a plain array. Access is still
> > > >   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no
> > > >   RCU discipline, no tear-down state machine, no NULL return.
> > > > - Only an initial chunk is mapped at creation. The rest of the address
> > > >   space is reserved, not allocated, so an idle device costs nothing.
> > > > - Growth is driven by allocation. When no free cluster is left and the
> > > >   address space has room, the next chunk is mapped and added to the free
> > > >   list. No userspace involvement.
> > > > - Shrink is driven by frees. The free tail is scanned, and whole chunks
> > > >   are unmapped once the mapped range is at most half in use and several
> > > >   chunks can go. One chunk is left mapped as slack, so the next
> > > >   allocation does not map it straight back. A ceiling lowered below the
> > > >   mapped range skips the half-in-use rule and is enforced at once.
> > >
> > > If the swap maintainers prefer the VM_SPARSE route, I'm happy to defer
> > > to them on that.
> > >
> > > However, from the cgroup and zswap camp, two stipulations that I
> > > reasoned out in the other thread[1]:
> >
> > >
> > > 1. You must not charge compression space as swap space to the cgroup.
> >

Sorry let me push back on that. That is already existing user-visible
behavior. Changing that will break our deployment using zswap. I don't
think we should change that.

See more in my reply in the other email thread.

https://lore.kernel.org/linux-mm/CACePvbVaPDnva8X-Xmz84r7j5HjTuih-w5phpw7cerK2uPnK6w@mail.gmail.com/

Chris

> > Hmm, I don't have a stance on this. However, isn't this an issue
> > zswap/zram have been doing? It feels like an independent issue which
> > should be done separately?
>
> If you have 3 containers using compression space, and two of them have
> writeback enabled to a shared swapfile, the memory.swap.* controls
> need to work to manage fair access to that swapfile. They do not work
> if compression space itself is conflated in.
>
> Right now zswap entries actually consume physical swapfile space, even
> before writeback. Charging the space is correct. But the whole point
> is to decouple compression space from physical swap space.
>
> This is not something that can be done later. It would be a dramatic
> user-visible change to how the resource is categorized and managed.
>
> > > 2. You must make the compression space large enough to be outside the
> > >    range where users can hit space limits before hitting memory limits.
> >
> > We may need a way to define 'large enough' at first.
>
> I've tried to lay this out in the other thread, and highlighted the
> usability issues that result from hitting compression space limits
> prematurely. It's kind of your call whether you want to seriously
> engage with this or not.
>
> But ultimately it's your claim that a static size can be made to work,
> so it's on you to make a convincing case.
>
> > >    That also means not allowing setups where this is possible.
> >
> > And the limit is only an optional knob. If the admin does not set it,
> > the device grows to the full address space, so there is no space limit
> > to hit at all. It already behaves the way you want by default. The knob
> > is only for admins who want a ceiling, they can use it or not. I hope
> > this would not be a problem for your use case.
>
> No, I've laid this out already as well.
>
> This isn't about "my" usecase. It's about designing a coherent
> interface that works well with a large number of usecases, and other
> pieces of kernel infrastructure commonly used in conjunction.
>
> The other proposal in the room needs no such interface. The burden of
> proof for adding one is on you.
>
> > > [1] https://lore.kernel.org/linux-mm/aqLi6cIjD2wJwk0B@cmpxchg.org/
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Baoquan He 3 days, 18 hours ago
On 09/20/26 at 11:52pm, Chris Li wrote:
> On Thu, Sep 17, 2026 at 3:17 AM Johannes Weiner <hannes@cmpxchg.org> wrote:
> >
> > On Thu, Sep 17, 2026 at 03:31:23PM +0800, Baoquan He wrote:
> > > On 09/16/26 at 12:45pm, Johannes Weiner wrote:
> > > > On Wed, Sep 16, 2026 at 06:19:07PM +0800, Baoquan He wrote:
> > > > > xswap is a swap device with no backing storage. Swapped-out pages live
> > > > > in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,
> > > > > and the area is grown and shrunk on demand as swap usage changes.
> > > > >
> > > > > The problem being solved is the static size of compressed swap. Both
> > > > > zram and zswap need the size fixed in advance, and neither gives memory
> > > > > back when the workload shrinks. The solution should be a device whose
> > > > > size can scale up/down as per usage. xswap does that by mapping the
> > > > > metadata lazily instead of reserving it for the whole range.
> > > > >
> > > > > Design
> > > > > ------
> > > > > - si->cluster_info[] stays a plain array. Access is still
> > > > >   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no
> > > > >   RCU discipline, no tear-down state machine, no NULL return.
> > > > > - Only an initial chunk is mapped at creation. The rest of the address
> > > > >   space is reserved, not allocated, so an idle device costs nothing.
> > > > > - Growth is driven by allocation. When no free cluster is left and the
> > > > >   address space has room, the next chunk is mapped and added to the free
> > > > >   list. No userspace involvement.
> > > > > - Shrink is driven by frees. The free tail is scanned, and whole chunks
> > > > >   are unmapped once the mapped range is at most half in use and several
> > > > >   chunks can go. One chunk is left mapped as slack, so the next
> > > > >   allocation does not map it straight back. A ceiling lowered below the
> > > > >   mapped range skips the half-in-use rule and is enforced at once.
> > > >
> > > > If the swap maintainers prefer the VM_SPARSE route, I'm happy to defer
> > > > to them on that.
> > > >
> > > > However, from the cgroup and zswap camp, two stipulations that I
> > > > reasoned out in the other thread[1]:
> > >
> > > >
> > > > 1. You must not charge compression space as swap space to the cgroup.
> > >
> 
> Sorry let me push back on that. That is already existing user-visible
> behavior. Changing that will break our deployment using zswap. I don't
> think we should change that.
> 
> See more in my reply in the other email thread.
> 
> https://lore.kernel.org/linux-mm/CACePvbVaPDnva8X-Xmz84r7j5HjTuih-w5phpw7cerK2uPnK6w@mail.gmail.com/

Thank both for valuable input. I am thinking if we can add an counter
like memory.swap.disk.* or memory.swap.backing.*, then we won't break
the existing behaviour, and also cover the use case Johannes mentioned
where different cgroup have different swapout target setting on xswap.

> 
> > > Hmm, I don't have a stance on this. However, isn't this an issue
> > > zswap/zram have been doing? It feels like an independent issue which
> > > should be done separately?
> >
> > If you have 3 containers using compression space, and two of them have
> > writeback enabled to a shared swapfile, the memory.swap.* controls
> > need to work to manage fair access to that swapfile. They do not work
> > if compression space itself is conflated in.
> >
> > Right now zswap entries actually consume physical swapfile space, even
> > before writeback. Charging the space is correct. But the whole point
> > is to decouple compression space from physical swap space.
> >
> > This is not something that can be done later. It would be a dramatic
> > user-visible change to how the resource is categorized and managed.
> >
> > > > 2. You must make the compression space large enough to be outside the
> > > >    range where users can hit space limits before hitting memory limits.
> > >
> > > We may need a way to define 'large enough' at first.
> >
> > I've tried to lay this out in the other thread, and highlighted the
> > usability issues that result from hitting compression space limits
> > prematurely. It's kind of your call whether you want to seriously
> > engage with this or not.
> >
> > But ultimately it's your claim that a static size can be made to work,
> > so it's on you to make a convincing case.
> >
> > > >    That also means not allowing setups where this is possible.
> > >
> > > And the limit is only an optional knob. If the admin does not set it,
> > > the device grows to the full address space, so there is no space limit
> > > to hit at all. It already behaves the way you want by default. The knob
> > > is only for admins who want a ceiling, they can use it or not. I hope
> > > this would not be a problem for your use case.
> >
> > No, I've laid this out already as well.
> >
> > This isn't about "my" usecase. It's about designing a coherent
> > interface that works well with a large number of usecases, and other
> > pieces of kernel infrastructure commonly used in conjunction.
> >
> > The other proposal in the room needs no such interface. The burden of
> > proof for adding one is on you.
> >
> > > > [1] https://lore.kernel.org/linux-mm/aqLi6cIjD2wJwk0B@cmpxchg.org/
Re: [PATCH v3 00/14] mm, swap: extendable swap devices (xswap)
Posted by Klara Modin 18 hours ago
On 2026-09-16 18:19:07 +0800, Baoquan He wrote:
> xswap is a swap device with no backing storage. Swapped-out pages live                                
> in zswap. Its cluster_info[] array lives in a VM_SPARSE vmalloc area,                                 
> and the area is grown and shrunk on demand as swap usage changes.                                     
>                                                                                                       
> The problem being solved is the static size of compressed swap. Both   
> zram and zswap need the size fixed in advance, and neither gives memory 
> back when the workload shrinks. The solution should be a device whose
> size can scale up/down as per usage. xswap does that by mapping the
> metadata lazily instead of reserving it for the whole range.
> 
> Design   
> ------                           
> - si->cluster_info[] stays a plain array. Access is still
>   &si->cluster_info[offset / SWAPFILE_CLUSTER]: no per-access branch, no 
>   RCU discipline, no tear-down state machine, no NULL return.              
> - Only an initial chunk is mapped at creation. The rest of the address     
>   space is reserved, not allocated, so an idle device costs nothing.
> - Growth is driven by allocation. When no free cluster is left and the       
>   address space has room, the next chunk is mapped and added to the free  
>   list. No userspace involvement.
> - Shrink is driven by frees. The free tail is scanned, and whole chunks
>   are unmapped once the mapped range is at most half in use and several
>   chunks can go. One chunk is left mapped as slack, so the next                                       
>   allocation does not map it straight back. A ceiling lowered below the                               
>   mapped range skips the half-in-use rule and is enforced at once.
> 

> Size
> ----
> A device starts at 1xRAM, rounded down to the cluster. That costs
> nothing, because the mapping is lazy. The underlying address space is
> 2xRAM. An optional per-device cap,
> /sys/kernel/mm/xswap/type<N>/limit, lets an admin lower the ceiling;
> the excess is unmapped right away. Grow and shrink both work without
> it. Creating a device requires zswap.

So I can't set an xswap device to more than twice the RAM? I suppose I
could create multiple xswap devices, but it would get tedious fast on
systems which have a different amount of memory. Is there a particular
reason for this limit? I think I could create an arbitrarily large xswap
device with your previous version which needed the specially crafted
swapfile (with only the header).

As I wrote in the other thread, I would rather not have to set a limit
at all, or at least have a limit I'm sure I won't reach.

> 
> Interface
> ---------
>   /sys/kernel/mm/xswap/create           write an optional priority
>   /sys/kernel/mm/xswap/destroy          write a swap type
>   /sys/kernel/mm/xswap/type<N>/limit    read/write, in pages
> The device shows up in /proc/swaps as xswap<N>.
> 
> Note
> ----
> Writeback, rmap lookup, etc. are consumers of this base. I have a
> writeback prototype on top of this base and will post it as a reference.
> 
> Testing
> -------
> qemu KVM guest, 8G RAM.
> 
> Tested create/destroy, raising and lowering the limit (including clamping
> when it is written below the pages in use), shrink with live entries, and
> 2000 create/destroy cycles for leaks; all passed.
> 
> The workload is memhog: it faults in N GB of anonymous memory inside a
> cgroup with a much smaller memory.max, forcing the pages to swap.
> Set MEMHOG_FILL=pattern: the default fill is all-zero pages that zswap
> compresses to almost nothing, so the device never fills.
> 
>   # echo 1 > /sys/module/zswap/parameters/enabled
>   # mkdir -p /sys/fs/cgroup/xswap_limit
>   # echo max > /sys/fs/cgroup/xswap_limit/memory.swap.max
>   # MEM="MEMHOG_FILL=pattern numactl --cpunodebind=0 --membind=0 ./memhog"
> 
> 1. Create and destroy
> 
>    # echo > /sys/kernel/mm/xswap/create
>    # swapon
>    NAME    TYPE  SIZE USED PRIO
>    xswap0  xswap 7.8G   0B   -1
>    # cat /sys/kernel/mm/xswap/type0/limit
>    2035199
>    # echo 0 > /sys/kernel/mm/xswap/destroy
>    # swapon
>    (nothing)
> 
>    limit is in 4 KiB pages; 2035199 is RAM (2034976 pages) rounded up to a
>    whole number of clusters. The device starts at RAM, not twice RAM.
> 
> 2. The cap holds
> 
>    # echo 2147483648 > /sys/fs/cgroup/xswap_limit/memory.max
>    # ( echo $$ > /sys/fs/cgroup/xswap_limit/cgroup.procs; eval $MEM 11 300 ) &
>    # awk '/SwapTotal|SwapFree/' /proc/meminfo
>    SwapTotal:       8140796 kB
>    SwapFree:         354012 kB
> 
>    The cgroup runs out of room before the device does and the OOM killer
>    takes the workload ??? that is the pass signal. SwapFree never exceeds
>    SwapTotal, so nr_swap_pages never goes negative.
> 
> 3. Raising the cap
> 
>    # echo 3052543 > /sys/kernel/mm/xswap/type0/limit
>    # awk '/SwapTotal/' /proc/meminfo
>    SwapTotal:      12210172 kB
>    # ( echo $$ > /sys/fs/cgroup/xswap_limit/cgroup.procs; eval $MEM 11 300 ) &
> 
>    No OOM this time: 2473705 pages in use against 2034976 pages of RAM, so
>    usage goes past RAM.
> 
> 4. Lowering the cap below the pages in use
> 
>    # echo 1000000 > /sys/kernel/mm/xswap/type0/limit
>    # cat /sys/kernel/mm/xswap/type0/limit
>    2426879
>    # awk '/SwapTotal|SwapFree/' /proc/meminfo
>    SwapTotal:       9707516 kB
>    SwapFree:             860 kB
> 
>    The write is clamped up to the clusters covering the pages in use, so
>    the free slots in the partially used top cluster stay accounted for.
> 
> 5. Shrink with live entries, then destroy
> 
>    # echo 4069887 > /sys/kernel/mm/xswap/type0/limit
>    # sleep 60
>    # awk '/SwapFree/' /proc/meminfo
>    SwapFree:       16279548 kB
> 
>    The shrink unmapped the tail ??? the state find_next_to_unuse() must
>    survive. Put live entries back and destroy:
> 
>    # ( echo $$ > /sys/fs/cgroup/xswap_limit/cgroup.procs; eval $MEM 3 300 ) &
>    # echo max > /sys/fs/cgroup/xswap_limit/memory.max
>    # echo 0 > /sys/kernel/mm/xswap/destroy
>    # swapon
>    (nothing)
> 
>    dmesg stays clean across create, shrink, swapoff and destroy.
> 
> 6. 2000 create/destroy cycles, diffing /proc/slabinfo before and after:
>    the largest growth is 142 objects. One object leaked per cycle would
>    be 2000.
> 
> Performance
> -----------
> (qemu KVM guest, 8G RAM, zram as the swap device)                                                         
> This series should not slow down a kernel that never creates an xswap
> device. I measured that overhead by comparing the base tree with this
> series. Both were built with the same .config and CONFIG_XSWAP=y, and no
> xswap device was created. I ran three 3G MADV_PAGEOUT workloads, three
> rounds each, alternating between the two kernels across reboots. I
> counted retired instructions per page swapped out with perf stat:
> 
>   workload                      base       series     delta
>   swapout                       50824.8    50866.2    +0.08%
>   swapout and swapin            63770.4    63796.8    +0.04%
>   swapout into a full device    67729.9    67707.8    -0.03%
> 
> Two runs of the same kernel differ by less than 0.1%, so the differences
> above are real, not measurement noise. I cannot use wall clock time for
> this comparison, because two runs of the same kernel differ by more than
> the two kernels do.
> 
> Changelog
> =========
> v2 -> v3:
> - Rebased onto the latest mm-new.
> 
> - The grow path now honors the user-set ceiling (si->nr_clusters) instead
>   of growing up to nr_clusters_max, and a ceiling below the mapped range
>   is unmapped exactly instead of rounded to a chunk (patches 12 and 14).
> 
> - The limit write clamps the ceiling up to the clusters covering the pages
>   in use, replacing the earlier WARN_ONCE; si->pages becomes mutable at
>   runtime (patch 13).
> 
> - Minor comment and cleanup changes.
> 
> v1->v2:
> - Patch 1 (mm: zswap: return -ENOENT when the swap device is gone) is not
>   part of this series; it was posted separately.
> 
> - There is only one size knob now. The runtime ceiling and the debugfs
>   per-device limit are gone. All that is left is the optional per-device
>   cap, /sys/kernel/mm/xswap/type<N>/limit. Grow and shrink work without
>   it.
> 
> - The shrink no longer keeps its own count of the free tail. It scans the
>   tail instead, and dropping the counter also removes a call from the
>   cluster allocation path.
> 
> - The priority is no longer a patch of its own. The create attribute
>   takes it:
>     echo 100 > /sys/kernel/mm/xswap/create
> 
> RFC v3 -> v1
> - Add patch 16 to support setting xswap device priority at creation.
>   The create sysfs interface (/sys/kernel/mm/xswap/create) previously
>   hardcoded every new device's priority to DEF_SWAP_PRIO, it now
>   accepts an optional priority:
> 
>     echo "<percent> [<prio>]" > /sys/kernel/mm/xswap/create
> 
> - Bug fix: xswap_lock init ordering. mutex_init(&si->xswap_lock) was called
>   after xswap_map_clusters() (which locks it), i.e. locking an uninitialized
>   mutex. Init now before the first xswap_map_clusters() call. Thanks to Klara.
> 
> - Bug fix: Fixes a compile error in !CONFIG_XSWAP builds. xswap_debugfs_root
>   is declared inside CONFIG_XSWAP ifdeffery scope, so the ungarded use
>   caused error when CONFIG_XSWAP is off.
> 
> RFC v2-> RFC v3:
> - Replace the "header-only swap file + swapon" creation hack with a
>   proper file-less device created and destroyed via sysfs
>   (/sys/kernel/mm/xswap/{create,destroy}).  This required the
>   __swapoff() refactor and the free_swap_cluster_info() signature
>   change (patches 4, 6, 14).
> 
> - Require zswap: refuse to create an xswap device when zswap is
>   unavailable (patch 15).
> 
> - Split the unrelated zswap -ENOENT fix out of the series into a
>   standalone patch (patch 1).
> 
> - Fix nr_free_tail over-counting on concurrent grow, shrink leaking
>   detached clusters on early bail-out, a re-init race on cluster
>   spinlocks in xswap_map_clusters(), the nr_clusters_mapped update
>   ordering, and swapoff accessing the shrinker-unmapped cluster tail.
> 
> - Minor cleanups (checkpatch, /proc/swaps alignment, commit messages).
> 
> RFC v1-> RFC v2:
> - Added __GFP_HIGH | __GFP_NOMEMALLOC to alloc_page() and kmalloc_array()
>   in the grow path, plus memalloc_noreclaim_save/restore() wrapping,
>   to prevent the grow path from consuming emergency memory reserves
>   or recursing into swap under PF_MEMALLOC. This is folded into patch 3.
>   This was pointed out by Nhat.
> 
> - Folded the mutex serialization fix into the cluster grow patch (patch
>   3). This is suggested by Nhat.
> 
> - Fixed coding style issues: corrected indentation of declarations in
>   xswap_unmap_clusters(), removed unnecessary block scope around the
>   err variable in xswap_map_clusters().
> 
> - Rebased onto mm-unstable
> 
> Baoquan He (13):
>   mm, swap: add CONFIG_XSWAP and xswap fields to swap_info_struct
>   mm, swap: refactor free_swap_cluster_info to take swap_info_struct
>   mm, swap: add xswap cluster grow via VM_SPARSE vmalloc
>   mm, swap: add sysfs create interface for xswap
>   mm, swap: add xswap grow trigger on cluster allocation
>   mm, swap: add xswap_try_shrink and shrink trigger on cluster free
>   mm, swap: free backing pages in xswap_unmap_clusters
>   mm, swap: defer xswap shrink to workqueue to avoid lock recursion
>   mm, swap: refactor swapoff and add xswap_destroy
>   mm, swap: require zswap for xswap devices
>   mm, swap: cap xswap growth at nr_clusters
>   mm, swap: add sysfs per-device size limit for xswap
>   mm, swap: shrink xswap to the ceiling when it drops
> 
> Chris Li (1):
>   mm: xswap support for zswap
> 
>  include/linux/swap.h |   26 +-
>  mm/Kconfig           |    9 +
>  mm/page_io.c         |   19 +
>  mm/swap_state.c      |    4 +
>  mm/swapfile.c        | 1240 +++++++++++++++++++++++++++++++++++++-----
>  mm/zswap.c           |    7 +-
>  6 files changed, 1174 insertions(+), 131 deletions(-)
> 
> 
> base-commit: baa8de2f3448d1466a888a805c18d01c998fe052
> -- 
> 2.54.0
>