From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from out-180.mta1.migadu.com (out-180.mta1.migadu.com [95.215.58.180]) (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 F3AD441A57E for ; Mon, 27 Jul 2026 13:50:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.180 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785160242; cv=none; b=kcQv6TT0y6etQpLYeC+TVNVZPy+sWqr3GjJmXgkcAQE7R0AG2LjgBoLyhUijQyJ549iJLy20XizpFKjPFluOGv9kMFpFOJRR98Tk4/1Qk2umF2jIUNX1k4VzKK9W0vr2ih7csaKs7MgPALbw1PFwdfP7zKlra2FxVnlS5jJcJmU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785160242; c=relaxed/simple; bh=YaGs/eebU2Bg9+K6Zj/PqWT8F4H061H6aDBAD6H2XHE=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type: Content-type; b=K8/AVKj9jCPXsmUE8Ja/0kzFBBecLoXXF44QBKRe13wR4hposd/pNyU7w8wswad6Oz36VmpbS08KbDoqA9ne9vK/PGbSiE6B6mLUJYoL+jg39z8hXtHJfg8/2S4TxggLoE3ZirX7/koheapc1ztW62xJ4vz0SaIf/UcbsMBToak= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=hJN+gDnG; arc=none smtp.client-ip=95.215.58.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="hJN+gDnG" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1785160236; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-type:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=BGyK7+kni4WDqT8HPxpy+b9wtS9F9Uy+0Ux7JobaO0E=; b=hJN+gDnGWewd8FsTyxLP4GzfWLXlWOQ1NwTUXSYh0KUrI9kyOT3Jf9Dfv41TGAB1FEz4bQ J78WQ8PfLzJPerzXXsjCBpnwC0ghpoF+fkroNrPgrneH4gxKQkTh+3tmOi0Y1Wcad4Koab 4hIVNbj1660ogWP5AvrenHRomPg7zmM= From: Baoquan He To: linux-mm@kvack.org Cc: akpm@linux-foundation.org, chrisl@kernel.org, nphamcs@gmail.com, kasong@tencent.com, baohua@kernel.org, youngjun.park@lge.com, hannes@cmpxchg.org, yosry@kernel.org, david@kernel.org, shikemeng@huaweicloud.com, chengming.zhou@linux.dev, linux-kernel@vger.kernel.org, Baoquan He Subject: [RFC PATCH 00/11] mm, swap: dynamic cluster management for xswap devices Date: Mon, 27 Jul 2026 21:50:11 +0800 Message-ID: <20260727135029.1059441-1-baoquan.he@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-type: text/plain Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT This series implements step 3 of the incremental path proposed in [1]: a virtual swap device with dynamic cluster growth and shrink, backed only by zswap, no writeback yet. During the discussion in [2], Chris and Nhat debated xarray-based vs array-based cluster lookup for virtual swap. Chris argued for keeping the swap table and cluster_info in the same place with O(1) array indexing, and suggested that the kvmalloc grow approach here should provide the cluster management VS needs. He further proposed using the same grow technique for the future per-slot backend pointer array (vs_table), keeping a single coherent array layout throughout. This series therefore serves as the foundation: once the dynamic cluster management lands, Nhat's VS series can build the per-slot backend pointer, writeback, and rmap on top of it — without an extra xarray lookup in the cluster access path. A note on naming: Nhat's series uses "virtual swap" (VS), Chris originally used "ghost swap", and now suggests "xswap" (extendable swap). This series uses "xswap" as a temporary convention; the final name is open for discussion. xswap decouple swap slots from physical backing storage. Currently zswap requires a backing swap device sized for the full virtual capacity, even when writeback is never needed — wasting disk space on slots that zswap will never use. An xswap device is a swapfile: a 4K on-disk header advertises the capacity, but there is no swap data section. This eliminates the wasted backing storage. This series replaces the fixed allocation with a VM_SPARSE vmalloc area that grows on demand and shrinks when clusters are freed, within the bounds set at swapon. The cluster_info pages are mapped lazily via vm_area_map_pages() and unmapped in bulk when contiguous free clusters accumulate at the tail. Lookup is O(1) via simple array indexing, which matters on the swapout hot path. The trade-off is coarser shrink granularity (full pages, not individual clusters), but for the initial landing where swap usage tends to be ratchet-like, this is a reasonable choice. [1] https://lore.kernel.org/all/al8ohWshSSZ64AtT@MiWiFi-R3L-srv/ [2] https://lore.kernel.org/all/CACePvbVJGVDbhvPRNsZx-f4t16TU-t6H754JOUQQ4uF2Xe6Q5w@mail.gmail.com/ Design ====== The cluster_info[] array lives in a VM_SPARSE vm_struct. Physical pages are allocated and mapped into it in chunks of XSWAP_GROW_CLUSTERS (256, configurable). When the allocator runs out of free clusters and the mapped range hasn't reached the ceiling, the grow path maps more pages. Symmetrically, when clusters are freed, a shrink path unmap pages from the tail if enough contiguous free clusters accumulate. To avoid scanning cluster_info[] on every shrink opportunity, a nr_free_tail counter provides O(1) detection — it tracks how many clusters at the tail are free. Shrink fires when the counter reaches the threshold. A per-device runtime ceiling (nr_clusters) allows userspace to cap the mapped range below the hard limit, and a debugfs knob exposes it for live tuning without swapoff/swapon. Testing ====== 1. Create xswap swapfile touch swap.4G truncate -s 4G swap.4G mkswap swap.4G dd if=swap.4G of=ghost.4G bs=4096 count=1 2. swapon swapon ghost.4G 3. memory pressure testing echo 1 > /sys/module/zswap/parameters/enabled stress-ng --vm 1 --vm-bytes 4G --vm-keep --timeout 120s 4. shrink and grow memory via debugfs knob root@fedora:~# cat /sys/kernel/debug/xswap/type0_cluster_limit 4096 root@fedora:~# echo 2048 > /sys/kernel/debug/xswap/type0_cluster_limit root@fedora:~# cat /sys/kernel/debug/xswap/type0_cluster_limit 2048 I did test cases and all passed: swapon, swapoff swapon, during stress-ng shrink and grow xswap size swapon, stress-ng, then poweroff when stress-ng finished ===== Baoquan He (10): mm, swap: add CONFIG_XSWAP and xswap fields to swap_info_struct mm, swap: add xswap cluster grow via VM_SPARSE vmalloc mm, swap: add xswap grow trigger on cluster allocation mm, swap: add xswap_try_shrink and shrink trigger on cluster free mm, swap: free backing pages in xswap_unmap_clusters mm, swap: add nr_free_tail for O(1) xswap shrink detection mm, swap: add adjustable runtime ceiling (nr_clusters) for xswap mm, swap: add debugfs knob for xswap per-device cluster limit mm, swap: defer xswap shrink to workqueue to avoid lock recursion mm, swap: serialize xswap map/unmap with a mutex Chris Li (1): mm: xswap support for zswap include/linux/swap.h | 12 + mm/Kconfig | 9 + mm/page_io.c | 16 + mm/swap_state.c | 7 + mm/swapfile.c | 677 +++++++++++++++++++++++++++++++++++++++++-- mm/zswap.c | 9 +- 6 files changed, 709 insertions(+), 21 deletions(-) -- 2.54.0