[RFC v3 11/15] mm, swap: add adjustable runtime ceiling (nr_clusters) for xswap
Baoquan He <[email protected]>
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Split the xswap cluster limit into two fields: - nr_clusters_max: immutable hard limit set at device creation - nr_clusters: current growth ceiling, adjustable at runtime (≤ nr_clusters_max) The grow path already uses nr_clusters as the ceiling. Shrink now also respects it: when nr_clusters drops below nr_clusters_mapped, shrinking fires on free until the mapped count reaches the ceiling. When nr_clusters == nr_clusters_max (default), shrink is effectively disabled — all growth and no shrink. At creation, nr_clusters starts at nr_clusters_max (full size). Signed-off-by: Baoquan He <[email protected]> --- include/linux/swap.h | 3 ++- mm/swapfile.c | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index e8e6ac5680ff..93d7771f2427 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -250,7 +250,8 @@ struct swap_info_struct { struct swap_cluster_info *cluster_info; /* cluster info. Only for SSD */ #ifdef CONFIG_XSWAP struct vm_struct *cluster_vm; /* VM_SPARSE area for xswap dynamic cluster_info */ - unsigned long nr_clusters; /* total cluster count for xswap */ + unsigned long nr_clusters_max;/* upper limit from swap header */ + unsigned long nr_clusters; /* current growth ceiling (≤ nr_clusters_max) */ unsigned long nr_clusters_mapped; /* currently mapped cluster count */ unsigned long nr_free_tail; /* contiguous free clusters at tail */ struct mutex xswap_lock; /* serialize map/unmap operations */ diff --git a/mm/swapfile.c b/mm/swapfile.c index fa76b8c9bd2d..be56114a6691 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -3228,6 +3228,7 @@ static void free_swap_cluster_info(struct swap_info_struct *si) xswap_unmap_clusters(si, 0, si->nr_clusters_mapped); free_vm_area(si->cluster_vm); si->cluster_vm = NULL; + si->nr_clusters_max = 0; si->nr_clusters = 0; si->nr_clusters_mapped = 0; return; @@ -3998,16 +3999,27 @@ static void xswap_trim_free_tail(struct swap_info_struct *si, unsigned long idx) */ static void xswap_try_shrink(struct swap_info_struct *si) { - unsigned long start_idx, nr_unmap, i; + unsigned long nr_mapped, nr_ceiling, nr_tail, nr_unmap; + unsigned long start_idx, i; struct swap_cluster_info *ci; if (!(si->flags & SWP_XSWAP)) return; - if (si->nr_free_tail < XSWAP_GROW_CLUSTERS) + + nr_mapped = READ_ONCE(si->nr_clusters_mapped); + nr_ceiling = READ_ONCE(si->nr_clusters); + nr_tail = READ_ONCE(si->nr_free_tail); + + if (nr_mapped <= nr_ceiling) + return; + if (nr_tail < XSWAP_GROW_CLUSTERS) return; - nr_unmap = round_down(si->nr_free_tail, XSWAP_GROW_CLUSTERS); - start_idx = si->nr_clusters_mapped - nr_unmap; + nr_unmap = min(round_down(nr_tail, XSWAP_GROW_CLUSTERS), + nr_mapped - nr_ceiling); + if (nr_unmap < XSWAP_GROW_CLUSTERS) + return; + start_idx = nr_mapped - nr_unmap; /* * Verify the tail clusters are still free before unmapping. Count the @@ -4016,7 +4028,7 @@ static void xswap_try_shrink(struct swap_info_struct *si) * leave a partial run off the free list if it proved too short to unmap. */ spin_lock(&si->lock); - for (i = start_idx; i < si->nr_clusters_mapped; i++) { + for (i = start_idx; i < nr_mapped; i++) { ci = &si->cluster_info[i]; if (ci->flags != CLUSTER_FLAG_FREE) break; @@ -4035,7 +4047,7 @@ static void xswap_try_shrink(struct swap_info_struct *si) spin_unlock(&si->lock); xswap_unmap_clusters(si, start_idx, nr_unmap); - si->nr_free_tail -= nr_unmap; + WRITE_ONCE(si->nr_free_tail, nr_tail - nr_unmap); } #endif /* CONFIG_XSWAP */ @@ -4059,6 +4071,7 @@ static int setup_swap_clusters_info(struct swap_info_struct *si, cluster_info = vm->addr; si->cluster_vm = vm; + si->nr_clusters_max = nr_clusters; si->nr_clusters = nr_clusters; si->cluster_info = cluster_info; @@ -4093,6 +4106,8 @@ static int setup_swap_clusters_info(struct swap_info_struct *si, } } + /* All mapped clusters except cluster 0 are free at the tail */ + si->nr_free_tail = si->nr_clusters_mapped - 1; mutex_init(&si->xswap_lock); return 0; @@ -4586,7 +4601,6 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) static int __init swapfile_init(void) { swapfile_maximum_size = arch_max_swapfile_size(); - /* * Once a cluster is freed, it's swap table content is read * only, and all swap cache readers (swap_cache_*) verifies @@ -4596,7 +4610,6 @@ static int __init swapfile_init(void) swap_table_cachep = kmem_cache_create("swap_table", sizeof(struct swap_table), 0, SLAB_PANIC | SLAB_TYPESAFE_BY_RCU, NULL); - #ifdef CONFIG_MIGRATION if (swapfile_maximum_size >= (1UL << SWP_MIG_TOTAL_BITS)) swap_migration_ad_supported = true; -- 2.54.0