[RFC PATCH v4 13/18] mm/swap: drop swap active plist
From: Lian Wang
Date: Sat Nov 14 2026 - 09:52:07 EST
From: Kairui Song <kasong@xxxxxxxxxxx>
Remove swap_active_head and the associated plist_node. Device identity
lookups can use the writable swap_info iterator without ordering.
Keep priority ordering for the block-cgroup swap throttle target by
walking the priority queue under the topology reader. Choose the first
unmasked, writable, non-offlist device with a backing block device.
Check OFFLIST as well as the pointer tag because full transitions can
temporarily leave an unmasked entry, and keep the explicit WRITEOK
check. The disable transition already publishes its mask before
releasing the topology writer.
This read-only lookup does not consume per-CPU allocation quotas or
change retry cursors. It avoids making the throttle disk depend on
swapon's swap-type assignment order when devices have different
priorities.
Signed-off-by: Kairui Song <kasong@xxxxxxxxxxx>
Signed-off-by: Lian Wang <lianux.mm@xxxxxxxxx>
---
include/linux/swap.h | 1 -
mm/swapfile.c | 60 +++++++++++++++++++++++---------------------
2 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/include/linux/swap.h b/include/linux/swap.h
index d31f3f023d36..ce8a800751ca 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -263,7 +263,6 @@ struct swap_info_struct {
struct percpu_ref users; /* indicate and keep swap device valid. */
unsigned long flags; /* SWP_USED etc: see above */
signed short prio; /* swap priority of this type */
- struct plist_node list; /* entry in swap_active_head */
signed char type; /* strange name for an index */
unsigned int max; /* size of this swap device */
struct swap_cluster_info *cluster_info; /* array, one entry per cluster */
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 6b2dbaf33cb6..bd098114ee4b 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -42,7 +42,6 @@
#include <linux/completion.h>
#include <linux/suspend.h>
#include <linux/zswap.h>
-#include <linux/plist.h>
#include <asm/tlbflush.h>
#include <linux/leafops.h>
@@ -91,12 +90,6 @@ bool swap_migration_ad_supported;
static const char Bad_file[] = "Bad swap file entry ";
static const char Bad_offset[] = "Bad swap offset entry ";
-/*
- * all active swap_info_structs
- * protected with swapon_rwsem, and ordered by priority.
- */
-static PLIST_HEAD(swap_active_head);
-
static inline struct swap_info_struct *__swap_iter(int *i, unsigned long flag)
{
lockdep_assert_held(&swapon_rwsem);
@@ -3535,7 +3528,6 @@ static void __swap_device_enable(struct swap_info_struct *si)
spin_unlock(&si->lock);
atomic_long_add(si->pages, &nr_swap_pages);
total_swap_pages += si->pages;
- plist_add(&si->list, &swap_active_head);
add_to_avail_list(si);
}
@@ -3555,9 +3547,8 @@ static int swap_device_disable(struct address_space *mapping,
int err = -EINVAL;
percpu_down_write(&swapon_rwsem);
- plist_for_each_entry(si, &swap_active_head, list) {
- if ((si->flags & SWP_WRITEOK) &&
- si->swap_file->f_mapping == mapping) {
+ for_each_avail_swap(si) {
+ if (si->swap_file->f_mapping == mapping) {
err = 0;
break;
}
@@ -3583,8 +3574,6 @@ static int swap_device_disable(struct address_space *mapping,
spin_lock(&si->lock);
del_from_avail_list(si, true);
spin_unlock(&si->lock);
-
- plist_del(&si->list, &swap_active_head);
total_swap_pages -= si->pages;
atomic_long_sub(si->pages, &nr_swap_pages);
@@ -3913,7 +3902,6 @@ static struct swap_info_struct *alloc_swap_info(void)
*/
}
p->swap_extent_root = RB_ROOT;
- plist_node_init(&p->list, 0);
p->flags = SWP_USED;
percpu_up_write(&swapon_rwsem);
if (defer) {
@@ -4323,12 +4311,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
if (swap_flags & SWAP_FLAG_PREFER)
prio = swap_flags & SWAP_FLAG_PRIO_MASK;
- /*
- * The plist prio is negated because plist ordering is
- * low-to-high, while swap ordering is high-to-low
- */
si->prio = prio;
- si->list.prio = -si->prio;
/*
* Publish swap_file before making the percpu ref live, then add the device
@@ -4454,7 +4437,33 @@ int swap_dup_entry_direct(swp_entry_t entry)
#if defined(CONFIG_MEMCG) && defined(CONFIG_BLK_CGROUP)
static bool __has_usable_swap(void)
{
- return !plist_head_empty(&swap_active_head);
+ return READ_ONCE(total_swap_pages) > 0;
+}
+
+static struct swap_info_struct *swap_queue_first_usable_bdev(void)
+{
+ struct swap_prio_ring *ring;
+ struct swap_info_struct *si;
+ unsigned int ring_idx, dev_idx;
+
+ lockdep_assert_held(&swapon_rwsem);
+ for (ring_idx = 0; ring_idx < swap_queue_len; ring_idx++) {
+ ring = swap_queue[ring_idx];
+ for (dev_idx = 0; dev_idx < ring->size; dev_idx++) {
+ si = READ_ONCE(ring->dev[dev_idx]);
+ if (swap_device_masked(si))
+ continue;
+ si = swap_device_unmask_ptr(si);
+ if (!(READ_ONCE(si->flags) & SWP_WRITEOK))
+ continue;
+ if (atomic_long_read(&si->inuse_pages) &
+ SWAP_USAGE_OFFLIST_BIT)
+ continue;
+ if (si->bdev)
+ return si;
+ }
+ }
+ return NULL;
}
void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp)
@@ -4478,14 +4487,9 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp)
return;
percpu_down_read(&swapon_rwsem);
- plist_for_each_entry(si, &swap_active_head, list) {
- if ((si->flags & SWP_WRITEOK) &&
- !(atomic_long_read(&si->inuse_pages) &
- SWAP_USAGE_OFFLIST_BIT) && si->bdev) {
- blkcg_schedule_throttle(si->bdev->bd_disk, true);
- break;
- }
- }
+ si = swap_queue_first_usable_bdev();
+ if (si)
+ blkcg_schedule_throttle(si->bdev->bd_disk, true);
percpu_up_read(&swapon_rwsem);
}
#endif