[RFC PATCH v2 08/10] mm, swap: add adjustable runtime ceiling (nr_clusters) for xswap
Baoquan He <[email protected]>
| Newsgroups | gmane.linux.kernel,gmane.linux.kernel.mm |
|---|---|
| Message-ID | <[email protected]> |
Split the xswap cluster limit into two fields: - nr_clusters_max: immutable hard limit set at swapon from swap header - 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 swapon, nr_clusters starts at nr_clusters_max (full size). Signed-off-by: Baoquan He <[email protected]> --- include/linux/swap.h | 3 ++- mm/swapfile.c | 31 ++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 72b28116ed0f..1159153459a1 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 3f536495b8cf..3037f428f217 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -72,6 +72,7 @@ static void xswap_trim_free_tail(struct swap_info_struct *si, unsigned long idx) static void xswap_update_free_tail(struct swap_info_struct *si, unsigned long freed_idx); static void xswap_try_shrink(struct swap_info_struct *si); + #endif static void swap_range_alloc(struct swap_info_struct *si, @@ -3151,6 +3152,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; @@ -3972,20 +3974,31 @@ 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 */ 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) { nr_unmap = i - start_idx; @@ -4000,7 +4013,7 @@ static void xswap_try_shrink(struct swap_info_struct *si) return; 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 */ @@ -4024,6 +4037,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; @@ -4058,6 +4072,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; @@ -4469,7 +4485,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 @@ -4479,12 +4494,10 @@ 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; #endif /* CONFIG_MIGRATION */ - return 0; } subsys_initcall(swapfile_init); -- 2.54.0