From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from out-182.mta1.migadu.com (out-182.mta1.migadu.com [95.215.58.182]) (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 9870B248880 for ; Tue, 7 Jul 2026 08:27:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783412841; cv=none; b=MUkaqHH4FpqRrhDrxmj+wcTrWnZRgDAs4L8M5RRZHkfVac8xeLLy3T29EYV4a3Bzld4MGXNsb5whY75Jo4KQQP//bG9upUu799mGM7CrfSFUU9BTUQLlUHjZt4yQLdv7HWjeVoHEsucF2noLeg/0Qrv8I5CQ68/bNlUpyu9b3O8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783412841; c=relaxed/simple; bh=ziEghxJtstIMWBP2vf7h2rmq57xjE+0keTSpmG8OdGw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type:Content-type; b=MLK6of3Gs0VJaK0vVM+Ncz//KCWuii8t0Eqrl1WMkPBfydat5jwGKfYyyVePSQ5J1ktxXDE2GhAMKG96yo4aPKcv07im85IRAQwJD1mAQAcgetZOpS+w4re6g31cPOqryTNmGkVDGqj6EQQlHmaapk6IuC/pjvwKZTYMIB4oPb4= 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=DGZZjplH; arc=none smtp.client-ip=95.215.58.182 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="DGZZjplH" 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=1783412837; 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: in-reply-to:in-reply-to:references:references; bh=gozPqlohPzHMzky6X0UHxWSkHReQK7p56E2rZcCLooQ=; b=DGZZjplHesAoUb55/bOlTDZ/QtYu+pGcmAWzLDkQPhEf/lSj/C+BXPdmak1XO5zajuXPnN gkI9GW8jStsZv9X6lE43GLrtjjuHwBXEGJiTgpiviNMCVz5b+gpCDPreXUCwXsfS3x3/9T ZwA1pHdsCocDYuKY+NgZls+pptEl4Iw= From: Baoquan He To: linux-mm@kvack.org Cc: akpm@linux-foundation.org, chrisl@kernel.org, kasong@tencent.com, shikemeng@huaweicloud.com, nphamcs@gmail.com, baohua@kernel.org, youngjun.park@lge.com, hannes@cmpxchg.org, yosry@kernel.org, chengming.zhou@linux.dev, linux-kernel@vger.kernel.org, Baoquan He Subject: [RFC PATCH 06/10] mm/zswap: implement ghost-to-physical writeback for backend switching Date: Tue, 7 Jul 2026 16:26:08 +0800 Message-ID: <20260707082614.95030-7-baoquan.he@linux.dev> In-Reply-To: <20260707082614.95030-1-baoquan.he@linux.dev> References: <20260707082614.95030-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 Add zswap_writeback_ghost() to handle zswap writeback when the backing swap device is a ghost swapfile (SWP_GHOST). Previously, writeback for ghost entries was rejected with -EINVAL. The new writeback path: 1. Allocates a physical swap slot from any real swap device via alloc_phys_swap_slot(). 2. Creates a swap-cache folio attached to the ghost swap entry. 3. Decompresses the zswap entry into the folio. 4. Stores the ghost->physical mapping in the ghost's redirect_xa. 5. Temporarily redirects folio->swap to the physical entry and calls __swap_writepage() to write to the physical device. 6. On success, the xarray mapping persists. When the swap-cache folio is later removed, __swap_cache_do_del_folio() restores the Redirect entry from the xarray. This enables the backend switch from zswap (compressed in memory) to physical swap (on disk) without changing the PTE — the PTE continues to point to the ghost swap entry, and the swapin path forwards the I/O via the Redirect entry. Modify zswap_writeback_entry() to call zswap_writeback_ghost() for ghost-backed entries instead of returning -EINVAL. Signed-off-by: Baoquan He --- mm/zswap.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/mm/zswap.c b/mm/zswap.c index bdf8283fd880..2137e8f14413 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -978,6 +978,90 @@ static bool zswap_decompress(struct zswap_entry *entry, struct folio *folio) * the swap cache, the compressed version stored by zswap can be * freed. */ +/* + * Ghost swapfile writeback: allocate a physical swap slot, decompress + * the zswap entry into a swap-cache folio, write it to the physical + * device via __swap_writepage, and set up a Redirect entry on the + * ghost swap table so future swapins are forwarded to the physical + * device. + */ +static int zswap_writeback_ghost(struct zswap_entry *entry, + swp_entry_t swpentry, + struct swap_info_struct *ghost_si, + pgoff_t offset) +{ + swp_entry_t phys_entry; + struct folio *folio; + struct mempolicy *mpol; + swp_entry_t saved_entry; + int ret = 0; + + /* Allocate a physical swap slot from a real device */ + phys_entry = alloc_phys_swap_slot(); + if (!phys_entry.val) + return -ENOMEM; + + /* Create a swap-cache folio attached to the ghost swap entry */ + mpol = get_task_policy(current); + folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol, + NO_INTERLEAVE_INDEX, NULL); + if (IS_ERR(folio)) { + ret = PTR_ERR(folio); + goto free_phys; + } + + /* Decompress zswap data into the folio */ + if (!zswap_decompress(entry, folio)) { + ret = -EIO; + goto del_folio; + } + + count_vm_event(ZSWPWB); + if (entry->objcg) + count_objcg_events(entry->objcg, ZSWPWB, 1); + + zswap_entry_free(entry); + + folio_mark_uptodate(folio); + folio_set_reclaim(folio); + + /* + * Store the redirect mapping in the xarray so it persists + * across swap-cache PFN cycles. The swap table currently + * holds a PFN (the swap cache folio); when the folio is + * eventually removed from swap cache, the Redirect entry + * will be restored from the xarray. + */ + xa_store(&ghost_si->redirect_xa, offset, + xa_mk_value(phys_entry.val), GFP_ATOMIC); + + /* + * Write the folio to the physical swap device. Temporarily + * redirect folio->swap to the physical entry so + * __swap_writepage uses the correct bdev and sector. + */ + saved_entry = folio->swap; + folio->swap = phys_entry; + ret = __swap_writepage(folio, NULL); + folio->swap = saved_entry; + + if (ret) { + xa_erase(&ghost_si->redirect_xa, offset); + goto del_folio; + } + + folio_put(folio); + return 0; + +del_folio: + swap_cache_del_folio(folio); + folio_unlock(folio); + folio_put(folio); +free_phys: + free_phys_swap_slot(phys_entry); + return ret; +} + static int zswap_writeback_entry(struct zswap_entry *entry, swp_entry_t swpentry) { @@ -994,11 +1078,6 @@ static int zswap_writeback_entry(struct zswap_entry *entry, if (!si) return -ENOENT; - if (si->flags & SWP_GHOST) { - put_swap_device(si); - return -EINVAL; - } - /* * Verify the swap table Pointer entry still references this * zswap_entry. If the entry was invalidated or replaced by a @@ -1015,6 +1094,13 @@ static int zswap_writeback_entry(struct zswap_entry *entry, } spin_unlock(&ci->lock); + /* Ghost swapfile: write back to a physical swap device */ + if (si->flags & SWP_GHOST) { + ret = zswap_writeback_ghost(entry, swpentry, si, offset); + put_swap_device(si); + return ret; + } + mpol = get_task_policy(current); folio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol, NO_INTERLEAVE_INDEX, NULL); -- 2.54.0