From nobody Tue Sep 29 14:53:47 2026 Received: from lgeamrelo07.lge.com (lgeamrelo07.lge.com [156.147.51.103]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6907717BB21 for ; Thu, 6 Aug 2026 19:47:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=156.147.51.103 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786045653; cv=none; b=kWAZ69+x29GCUKVBl62+OAPf5r8DfSaRAJPHtQpsKIASSPNyKYRSBC4kgVMf7qchlORr4L3bgMSSmnkb6yjUFMjCe0CqCNq2eRwq9lwB28aUTSMcE7BAF+wuIZUQJbOsCVt3HsxMlT+sheB8bHrvwWWj5xdP4csRIIZ3y2RsL0g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786045653; c=relaxed/simple; bh=QQKjo93ZuzovifOR8s6LG813NUTc/wmSy6fhf8EJuIc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=J1GhNCMv9jBWkWdY+p8+d8e2zn2+vUlR5/hR1/qp+9NoQAA3AmJyzGxe5A+tJ1oUZxtDqmaLnwQi3GLq1VGUtBZGYkGlj87++V/s6tTPFbNxgqu4U7zFLtUScJcuWrpbUGDzojqNw+VWATOtE4MRztjofHxQejcjeeglykbngb8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=lge.com; spf=pass smtp.mailfrom=lge.com; arc=none smtp.client-ip=156.147.51.103 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=lge.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lge.com Received: from unknown (HELO yjaykim-PowerEdge-T330.lge.net) (10.177.112.156) by 156.147.51.103 with ESMTP; 7 Aug 2026 04:32:28 +0900 X-Original-SENDERIP: 10.177.112.156 X-Original-MAILFROM: youngjun.park@lge.com From: Youngjun Park To: Andrew Morton Cc: Chris Li , Kairui Song , Kemeng Shi , Nhat Pham , Baoquan He , Barry Song , Youngjun Park , her0gyugyu@gmail.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 1/2] mm/swap: fix stale comment on swap_info_struct::cluster_info Date: Fri, 7 Aug 2026 04:32:27 +0900 Message-Id: <20260806193228.458685-2-youngjun.park@lge.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260806193228.458685-1-youngjun.park@lge.com> References: <20260806193228.458685-1-youngjun.park@lge.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" setup_swap_clusters_info() allocates cluster_info for every swap area, not only for SSDs. Signed-off-by: Youngjun Park Acked-by: Kairui Song Reviewed-by: Barry Song Reviewed-by: Baoquan He --- include/linux/swap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 2cb1d29307c5..2b14e2e9673b 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -246,7 +246,7 @@ struct swap_info_struct { 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; /* cluster info. Only for SSD */ + struct swap_cluster_info *cluster_info; /* array, one entry per cluster */ struct list_head free_clusters; /* free clusters list */ struct list_head full_clusters; /* full clusters list */ struct list_head nonfull_clusters[SWAP_NR_ORDERS]; --=20 2.48.1 From nobody Tue Sep 29 14:53:47 2026 Received: from lgeamrelo07.lge.com (lgeamrelo07.lge.com [156.147.51.103]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 690DF23E325 for ; Thu, 6 Aug 2026 19:47:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=156.147.51.103 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786045652; cv=none; b=UN1Gt+4YfjWusU6wXi2SPN4qWGYfV9aPvPAQ1suE3wS1Itx+3Cf+7suyuitX4q8HEsZNInjLW7T01qgn/qEMBDP3Yc1Ev/5lCOpcs5cDYLC76/5Ld0jtFDH8gvwMaTvzWtMyXw7lYDsIBkxdQeU73g7Z05q4VXHXCr9QChpg14o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786045652; c=relaxed/simple; bh=uwcXHzl7wnyDlxbZn3rxHVc4pomS97TzEgOZEuoarI4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=fK4xcCchGUPDpcNlnSVYYqA+hJWOF/kQLE39u5B+t+gU77Kn6ZO56CkcvwgJxAa9B6MK8SPw9PYDaAog0qlRWsAuH4cLYSgOl907/YAmO77B7W0EXossiCvs6ZR8SFWQa/bg9CNqwH/fXPQfNim+FYHpzTuofolfjk7LrFq0WoM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=lge.com; spf=pass smtp.mailfrom=lge.com; arc=none smtp.client-ip=156.147.51.103 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=lge.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=lge.com Received: from unknown (HELO yjaykim-PowerEdge-T330.lge.net) (10.177.112.156) by 156.147.51.103 with ESMTP; 7 Aug 2026 04:32:28 +0900 X-Original-SENDERIP: 10.177.112.156 X-Original-MAILFROM: youngjun.park@lge.com From: Youngjun Park To: Andrew Morton Cc: Chris Li , Kairui Song , Kemeng Shi , Nhat Pham , Baoquan He , Barry Song , Youngjun Park , her0gyugyu@gmail.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 2/2] mm/swap: scan by cluster in find_next_to_unuse() Date: Fri, 7 Aug 2026 04:32:28 +0900 Message-Id: <20260806193228.458685-3-youngjun.park@lge.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260806193228.458685-1-youngjun.park@lge.com> References: <20260806193228.458685-1-youngjun.park@lge.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" find_next_to_unuse() walks every offset from 0 to si->max, and swapoff restarts that walk on each retry, so the cost scales with the size of the device rather than with the few slots the shmem and mmlist passes could not free. It has caused stalls before. The flat walk predates the swap table. Slot state now lives in a per cluster table, and wait_for_allocation() stops all allocation before try_to_unuse() runs, so a cluster that holds no slot in use stays that way. Skip such a cluster instead of reading all of its entries. Commit dc644a073769 ("mm: add three more cond_resched() in swapoff") answered those stalls with a cond_resched() every 256 offsets. A walk bounded by one cluster no longer needs that counter. The loop now runs at most SWAPFILE_CLUSTER times before it returns or reschedules, the same bound swap_reclaim_full_clusters() already scans between cond_resched() calls. The scan end is clamped to si->max, so the walk stops there rather than running into the masked tail of the last cluster. ci->count is read without ci->lock, so READ_ONCE() marks the read for KCSAN. Allocation is already stopped, so the count can only drop, and a slot stops being counted only after its folio has left the swap cache. An empty cluster therefore holds nothing for try_to_unuse() to act on. Signed-off-by: Youngjun Park Reviewed-by: Barry Song Acked-by: Kairui Song Reviewed-by: Baoquan He --- mm/swapfile.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index dea2d3b36e06..0d24efd32eb0 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -370,8 +370,6 @@ static void discard_swap_cluster(struct swap_info_struc= t *si, } } =20 -#define LATENCY_LIMIT 256 - static inline bool cluster_is_empty(struct swap_cluster_info *info) { return info->count =3D=3D 0; @@ -2763,7 +2761,9 @@ static int unuse_mm(struct mm_struct *mm, unsigned in= t type) static unsigned int find_next_to_unuse(struct swap_info_struct *si, unsigned int prev) { - unsigned int i; + struct swap_cluster_info *ci; + unsigned long i, end; + unsigned int ci_off; unsigned long swp_tb; =20 /* @@ -2772,19 +2772,36 @@ static unsigned int find_next_to_unuse(struct swap_= info_struct *si, * hits are okay, and sys_swapoff() has already prevented new * allocations from this area (while holding swap_lock). */ - for (i =3D prev + 1; i < si->max; i++) { - swp_tb =3D swap_table_get(__swap_offset_to_cluster(si, i), - i % SWAPFILE_CLUSTER); - if (!swp_tb_is_null(swp_tb) && !swp_tb_is_bad(swp_tb)) - break; - if ((i % LATENCY_LIMIT) =3D=3D 0) + i =3D prev + 1; + while (i < si->max) { + ci =3D __swap_offset_to_cluster(si, i); + end =3D min_t(unsigned long, + ALIGN_DOWN(i, SWAPFILE_CLUSTER) + SWAPFILE_CLUSTER, + si->max); + + /* + * An empty cluster has no slot in use, so skip it whole. + * A slot is uncounted only after its folio left the swap + * cache, so there is nothing here for try_to_unuse() to act on. + * Count only drops here, so a READ_ONCE() without ci->lock is + * enough, unlike in every other cluster_is_empty() caller. + */ + if (!READ_ONCE(ci->count)) { + i =3D end; cond_resched(); - } + continue; + } =20 - if (i =3D=3D si->max) - i =3D 0; + ci_off =3D i % SWAPFILE_CLUSTER; + for (; i < end; ci_off++, i++) { + swp_tb =3D swap_table_get(ci, ci_off); + if (!swp_tb_is_null(swp_tb) && !swp_tb_is_bad(swp_tb)) + return i; + } + cond_resched(); + } =20 - return i; + return 0; } =20 static int try_to_unuse(unsigned int type) --=20 2.48.1