* [PATCH v2 0/2] mm/swapfile: fix and cleanup swap list iterations
@ 2025-11-27 10:03 Youngjun Park
2025-11-27 10:03 ` [PATCH v2 1/2] mm/swapfile: fix list iteration when next node is removed during discard Youngjun Park
2025-11-27 10:03 ` [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate Youngjun Park
0 siblings, 2 replies; 7+ messages in thread
From: Youngjun Park @ 2025-11-27 10:03 UTC (permalink / raw)
To: akpm
Cc: chrisl, kasong, shikemeng, nphamcs, bhe, baohua, linux-mm, youngjun.park
This series fixes a potential list iteration issue in swap_sync_discard()
when devices are removed, and includes a cleanup for
__folio_throttle_swaprate().
v2 -> v1:
- Keep the original return value style. (Thank you Kairui)
- Clarified commit message based on discussion. (Thank you Baoquan He)
- Collect tags.
- Updated only patch [1/2], base commit unchanged. (minor fix only).
Link v1: https://lore.kernel.org/linux-mm/aSfkrpV7T7g5mhpI@yjaykim-PowerEdge-T330/T/#m101dc552904259e26d6595f91d9cbee37b6da7ac
Youngjun Park (2):
mm/swapfile: fix list iteration when next node is removed during
discard
mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate
mm/swapfile.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
--
base-commit: 624c4d7b8fac3fdf9894a5f89f84709771db4dbb
2.34.1
Best regards,
--
Youngjun Park
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 1/2] mm/swapfile: fix list iteration when next node is removed during discard 2025-11-27 10:03 [PATCH v2 0/2] mm/swapfile: fix and cleanup swap list iterations Youngjun Park @ 2025-11-27 10:03 ` Youngjun Park 2025-11-27 10:48 ` Baoquan He 2025-11-27 10:03 ` [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate Youngjun Park 1 sibling, 1 reply; 7+ messages in thread From: Youngjun Park @ 2025-11-27 10:03 UTC (permalink / raw) To: akpm Cc: chrisl, kasong, shikemeng, nphamcs, bhe, baohua, linux-mm, youngjun.park When the next node is removed from the plist (e.g. by swapoff), plist_del() makes the node point to itself, causing the iteration to loop on the same entry indefinitely. Add a plist_node_empty() check to detect this case and restart iteration, allowing swap_sync_discard() to continue processing remaining swap devices that still have pending discard entries. Additionally, switch from swap_avail_lock/swap_avail_head to swap_lock/swap_active_head so that iteration is only affected by swapoff operations rather than frequent availability changes, reducing exceptional condition checks and lock contention. Fixes: 686ea517f471 ("mm, swap: do not perform synchronous discard during allocation") Suggested-by: Kairui Song <kasong@tencent.com> Acked-by: Kairui Song <kasong@tencent.com> Signed-off-by: Youngjun Park <youngjun.park@lge.com> --- mm/swapfile.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index d12332423a06..8116f36e440b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1387,9 +1387,10 @@ static bool swap_sync_discard(void) bool ret = false; struct swap_info_struct *si, *next; - spin_lock(&swap_avail_lock); - plist_for_each_entry_safe(si, next, &swap_avail_head, avail_list) { - spin_unlock(&swap_avail_lock); + spin_lock(&swap_lock); +start_over: + plist_for_each_entry_safe(si, next, &swap_active_head, list) { + spin_unlock(&swap_lock); if (get_swap_device_info(si)) { if (si->flags & SWP_PAGE_DISCARD) ret = swap_do_scheduled_discard(si); @@ -1397,9 +1398,12 @@ static bool swap_sync_discard(void) } if (ret) return true; - spin_lock(&swap_avail_lock); + + spin_lock(&swap_lock); + if (plist_node_empty(&next->list)) + goto start_over; } - spin_unlock(&swap_avail_lock); + spin_unlock(&swap_lock); return false; } -- 2.34.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] mm/swapfile: fix list iteration when next node is removed during discard 2025-11-27 10:03 ` [PATCH v2 1/2] mm/swapfile: fix list iteration when next node is removed during discard Youngjun Park @ 2025-11-27 10:48 ` Baoquan He 0 siblings, 0 replies; 7+ messages in thread From: Baoquan He @ 2025-11-27 10:48 UTC (permalink / raw) To: Youngjun Park; +Cc: akpm, chrisl, kasong, shikemeng, nphamcs, baohua, linux-mm On 11/27/25 at 07:03pm, Youngjun Park wrote: > When the next node is removed from the plist (e.g. by swapoff), > plist_del() makes the node point to itself, causing the iteration to > loop on the same entry indefinitely. > > Add a plist_node_empty() check to detect this case and restart > iteration, allowing swap_sync_discard() to continue processing > remaining swap devices that still have pending discard entries. > > Additionally, switch from swap_avail_lock/swap_avail_head to > swap_lock/swap_active_head so that iteration is only affected by > swapoff operations rather than frequent availability changes, > reducing exceptional condition checks and lock contention. > > Fixes: 686ea517f471 ("mm, swap: do not perform synchronous discard during allocation") > Suggested-by: Kairui Song <kasong@tencent.com> > Acked-by: Kairui Song <kasong@tencent.com> > Signed-off-by: Youngjun Park <youngjun.park@lge.com> > --- > mm/swapfile.c | 14 +++++++++----- > 1 file changed, 9 insertions(+), 5 deletions(-) Reviewed-by: Baoquan He <bhe@redhat.com> > > diff --git a/mm/swapfile.c b/mm/swapfile.c > index d12332423a06..8116f36e440b 100644 > --- a/mm/swapfile.c > +++ b/mm/swapfile.c > @@ -1387,9 +1387,10 @@ static bool swap_sync_discard(void) > bool ret = false; > struct swap_info_struct *si, *next; > > - spin_lock(&swap_avail_lock); > - plist_for_each_entry_safe(si, next, &swap_avail_head, avail_list) { > - spin_unlock(&swap_avail_lock); > + spin_lock(&swap_lock); > +start_over: > + plist_for_each_entry_safe(si, next, &swap_active_head, list) { > + spin_unlock(&swap_lock); > if (get_swap_device_info(si)) { > if (si->flags & SWP_PAGE_DISCARD) > ret = swap_do_scheduled_discard(si); > @@ -1397,9 +1398,12 @@ static bool swap_sync_discard(void) > } > if (ret) > return true; > - spin_lock(&swap_avail_lock); > + > + spin_lock(&swap_lock); > + if (plist_node_empty(&next->list)) > + goto start_over; > } > - spin_unlock(&swap_avail_lock); > + spin_unlock(&swap_lock); > > return false; > } > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate 2025-11-27 10:03 [PATCH v2 0/2] mm/swapfile: fix and cleanup swap list iterations Youngjun Park 2025-11-27 10:03 ` [PATCH v2 1/2] mm/swapfile: fix list iteration when next node is removed during discard Youngjun Park @ 2025-11-27 10:03 ` Youngjun Park 2025-11-27 10:51 ` Baoquan He ` (2 more replies) 1 sibling, 3 replies; 7+ messages in thread From: Youngjun Park @ 2025-11-27 10:03 UTC (permalink / raw) To: akpm Cc: chrisl, kasong, shikemeng, nphamcs, bhe, baohua, linux-mm, youngjun.park The loop breaks immediately after finding the first swap device and never modifies the list. Replace plist_for_each_entry_safe() with plist_for_each_entry() and remove the unused next variable. Signed-off-by: Youngjun Park <youngjun.park@lge.com> --- mm/swapfile.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 8116f36e440b..46d2008e4b99 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -4023,7 +4023,7 @@ static bool __has_usable_swap(void) void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) { - struct swap_info_struct *si, *next; + struct swap_info_struct *si; if (!(gfp & __GFP_IO)) return; @@ -4042,8 +4042,7 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) return; spin_lock(&swap_avail_lock); - plist_for_each_entry_safe(si, next, &swap_avail_head, - avail_list) { + plist_for_each_entry(si, &swap_avail_head, avail_list) { if (si->bdev) { blkcg_schedule_throttle(si->bdev->bd_disk, true); break; -- 2.34.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate 2025-11-27 10:03 ` [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate Youngjun Park @ 2025-11-27 10:51 ` Baoquan He 2025-11-28 2:45 ` Kairui Song 2025-11-28 15:02 ` Chris Li 2 siblings, 0 replies; 7+ messages in thread From: Baoquan He @ 2025-11-27 10:51 UTC (permalink / raw) To: Youngjun Park; +Cc: akpm, chrisl, kasong, shikemeng, nphamcs, baohua, linux-mm On 11/27/25 at 07:03pm, Youngjun Park wrote: > The loop breaks immediately after finding the first swap device and > never modifies the list. Replace plist_for_each_entry_safe() with > plist_for_each_entry() and remove the unused next variable. Yeah, and the lock is always held. So this looks good to me. Reviewed-by: Baoquan He <bhe@redhat.com> > > Signed-off-by: Youngjun Park <youngjun.park@lge.com> > --- > mm/swapfile.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/mm/swapfile.c b/mm/swapfile.c > index 8116f36e440b..46d2008e4b99 100644 > --- a/mm/swapfile.c > +++ b/mm/swapfile.c > @@ -4023,7 +4023,7 @@ static bool __has_usable_swap(void) > > void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) > { > - struct swap_info_struct *si, *next; > + struct swap_info_struct *si; > > if (!(gfp & __GFP_IO)) > return; > @@ -4042,8 +4042,7 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) > return; > > spin_lock(&swap_avail_lock); > - plist_for_each_entry_safe(si, next, &swap_avail_head, > - avail_list) { > + plist_for_each_entry(si, &swap_avail_head, avail_list) { > if (si->bdev) { > blkcg_schedule_throttle(si->bdev->bd_disk, true); > break; > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate 2025-11-27 10:03 ` [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate Youngjun Park 2025-11-27 10:51 ` Baoquan He @ 2025-11-28 2:45 ` Kairui Song 2025-11-28 15:02 ` Chris Li 2 siblings, 0 replies; 7+ messages in thread From: Kairui Song @ 2025-11-28 2:45 UTC (permalink / raw) To: Youngjun Park; +Cc: akpm, chrisl, shikemeng, nphamcs, bhe, baohua, linux-mm On Thu, Nov 27, 2025 at 6:06 PM Youngjun Park <youngjun.park@lge.com> wrote: > > The loop breaks immediately after finding the first swap device and > never modifies the list. Replace plist_for_each_entry_safe() with > plist_for_each_entry() and remove the unused next variable. > > Signed-off-by: Youngjun Park <youngjun.park@lge.com> > --- > mm/swapfile.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/mm/swapfile.c b/mm/swapfile.c > index 8116f36e440b..46d2008e4b99 100644 > --- a/mm/swapfile.c > +++ b/mm/swapfile.c > @@ -4023,7 +4023,7 @@ static bool __has_usable_swap(void) > > void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) > { > - struct swap_info_struct *si, *next; > + struct swap_info_struct *si; > > if (!(gfp & __GFP_IO)) > return; > @@ -4042,8 +4042,7 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) > return; > > spin_lock(&swap_avail_lock); > - plist_for_each_entry_safe(si, next, &swap_avail_head, > - avail_list) { > + plist_for_each_entry(si, &swap_avail_head, avail_list) { > if (si->bdev) { > blkcg_schedule_throttle(si->bdev->bd_disk, true); > break; > -- > 2.34.1 > Thanks! Acked-by: Kairui Song <kasong@tencent.com> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate 2025-11-27 10:03 ` [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate Youngjun Park 2025-11-27 10:51 ` Baoquan He 2025-11-28 2:45 ` Kairui Song @ 2025-11-28 15:02 ` Chris Li 2 siblings, 0 replies; 7+ messages in thread From: Chris Li @ 2025-11-28 15:02 UTC (permalink / raw) To: Youngjun Park; +Cc: akpm, kasong, shikemeng, nphamcs, bhe, baohua, linux-mm Hi Youngjun, Thanks for the patch! Acked-by: Chris Li <chrisl@kernel.org> Chris On Thu, Nov 27, 2025 at 2:03 PM Youngjun Park <youngjun.park@lge.com> wrote: > > The loop breaks immediately after finding the first swap device and > never modifies the list. Replace plist_for_each_entry_safe() with > plist_for_each_entry() and remove the unused next variable. > > Signed-off-by: Youngjun Park <youngjun.park@lge.com> > --- > mm/swapfile.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/mm/swapfile.c b/mm/swapfile.c > index 8116f36e440b..46d2008e4b99 100644 > --- a/mm/swapfile.c > +++ b/mm/swapfile.c > @@ -4023,7 +4023,7 @@ static bool __has_usable_swap(void) > > void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) > { > - struct swap_info_struct *si, *next; > + struct swap_info_struct *si; > > if (!(gfp & __GFP_IO)) > return; > @@ -4042,8 +4042,7 @@ void __folio_throttle_swaprate(struct folio *folio, gfp_t gfp) > return; > > spin_lock(&swap_avail_lock); > - plist_for_each_entry_safe(si, next, &swap_avail_head, > - avail_list) { > + plist_for_each_entry(si, &swap_avail_head, avail_list) { > if (si->bdev) { > blkcg_schedule_throttle(si->bdev->bd_disk, true); > break; > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-11-28 15:03 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2025-11-27 10:03 [PATCH v2 0/2] mm/swapfile: fix and cleanup swap list iterations Youngjun Park 2025-11-27 10:03 ` [PATCH v2 1/2] mm/swapfile: fix list iteration when next node is removed during discard Youngjun Park 2025-11-27 10:48 ` Baoquan He 2025-11-27 10:03 ` [PATCH v2 2/2] mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate Youngjun Park 2025-11-27 10:51 ` Baoquan He 2025-11-28 2:45 ` Kairui Song 2025-11-28 15:02 ` Chris Li
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox