[RFC PATCH v3 16/18] mm/swap: advance the global queue cursor directly between priority rings

From: Lian Wang

Date: Tue Oct 27 2026 - 12:16:26 EST


The allocation walk previously passed a total retry index to the selector.
Each retry translated it back to a priority ring by scanning from the
beginning of swap_queue. Keep the ring index and the number of attempted
peers in the task-local cursor instead.

Snapshot the starting device only on the first visit to each ring and
advance each peer exactly once, including masked entries. Preserve the
large-folio same-ring boundary and the stale reader-offset correction.
The selector's complete walk is linear in the devices and priority groups;
device cluster scanning and availability updates have their own costs.

Keep the topology read lock and CPU migration protection across the walk.
The global selector uses only existing priorities and adds no per-memcg
device-selection state or ABI. Existing swap charging and limits remain
in folio_alloc_swap(), whose failure path classifies allocation errors.

The walk no longer returns an ignored internal error to that caller.

Signed-off-by: Lian Wang <lianux.mm@xxxxxxxxx>
---
mm/swapfile.c | 49 +++++++++++++++++++++++--------------------------
1 file changed, 23 insertions(+), 26 deletions(-)

diff --git a/mm/swapfile.c b/mm/swapfile.c
index 5b6492b1bdc6..bbd114f043ab 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -194,10 +194,10 @@ struct swap_queue_reader {
};

struct swap_queue_cursor {
- bool valid;
bool ring_only;
unsigned int ring_idx;
unsigned int offset;
+ unsigned int nr_iter;
};

static __percpu struct swap_queue_reader *swap_queue_readers;
@@ -307,28 +307,32 @@ static void swap_queue_install_readers(struct swap_queue_reader __percpu *reader
swap_queue_reset_readers();
}

-static struct swap_info_struct *swap_queue_get_device(long nr_alloc, int nr_iter,
- struct swap_queue_cursor *cursor)
+static struct swap_info_struct *
+swap_queue_get_device(long nr_alloc, struct swap_queue_cursor *cursor)
{
bool rotate = false;
struct swap_info_struct *si;
struct swap_ring_iterator *ri;
struct swap_prio_ring *ring;
- unsigned int dev_idx, queue_idx;
+ unsigned int dev_idx, queue_idx, nr_iter;

- if (!swap_queue_len)
+ queue_idx = cursor->ring_idx;
+ if (queue_idx >= swap_queue_len)
return ERR_PTR(-ENOENT);

- queue_idx = 0;
- while (nr_iter >= swap_queue[queue_idx]->size) {
- nr_iter -= swap_queue[queue_idx]->size;
- if (++queue_idx >= swap_queue_len)
- return ERR_PTR(cursor->ring_only ? -E2BIG : -ENOENT);
+ ring = swap_queue[queue_idx];
+ if (cursor->nr_iter >= ring->size) {
+ if (cursor->ring_only)
+ return ERR_PTR(-E2BIG);
+ queue_idx = ++cursor->ring_idx;
+ if (queue_idx >= swap_queue_len)
+ return ERR_PTR(-ENOENT);
+ cursor->nr_iter = 0;
+ ring = swap_queue[queue_idx];
}
- if (cursor->ring_only && cursor->ring_idx != queue_idx)
- return ERR_PTR(-E2BIG);

- ring = swap_queue[queue_idx];
+ /* Consume masked peers too, so every walk remains bounded. */
+ nr_iter = cursor->nr_iter++;
local_lock(&swap_queue_readers->lock);
ri = this_cpu_ptr(&swap_queue_readers->ri[queue_idx]);
/*
@@ -336,9 +340,7 @@ static struct swap_info_struct *swap_queue_get_device(long nr_alloc, int nr_iter
* iterator can move between retries, but the cursor must visit every
* device in the ring exactly once before falling through.
*/
- if (!cursor->valid || cursor->ring_idx != queue_idx) {
- cursor->valid = true;
- cursor->ring_idx = queue_idx;
+ if (!nr_iter) {
if (ri->rr_counter < nr_alloc)
rotate = true;
else if (ri->offset >= ring->size)
@@ -1794,29 +1796,25 @@ static bool get_swap_device_info(struct swap_info_struct *si)
return true;
}

-static int swap_alloc_entry(struct folio *folio)
+static void swap_alloc_entry(struct folio *folio)
{
struct swap_queue_cursor cursor = {};
long nr_pages = folio_nr_pages(folio);
struct swap_info_struct *si;
- int nr_iter, ret;

percpu_down_read(&swapon_rwsem);
migrate_disable();
- for (nr_iter = 0;; nr_iter++) {
- si = swap_queue_get_device(nr_pages, nr_iter, &cursor);
+ for (;;) {
+ si = swap_queue_get_device(nr_pages, &cursor);
if (IS_ERR(si)) {
- ret = PTR_ERR(si);
- if (ret == -EBUSY)
+ if (PTR_ERR(si) == -EBUSY)
continue;
break;
}
cluster_alloc_swap_entry(si, folio);

- if (folio_test_swapcache(folio)) {
- ret = 0;
+ if (folio_test_swapcache(folio))
break;
- }

/*
* For large allocations, try every device at the same priority,
@@ -1831,7 +1829,6 @@ static int swap_alloc_entry(struct folio *folio)

migrate_enable();
percpu_up_read(&swapon_rwsem);
- return ret;
}

static int swap_extend_table_alloc(struct swap_info_struct *si,