Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2217564/?format=api
{ "id": 2217564, "url": "http://patchwork.ozlabs.org/api/patches/2217564/?format=api", "web_url": "http://patchwork.ozlabs.org/project/kvm-riscv/patch/202603301612587174XZ6QMCrymBqv30S6BN50@zte.com.cn/", "project": { "id": 70, "url": "http://patchwork.ozlabs.org/api/projects/70/?format=api", "name": "Linux KVM RISC-V", "link_name": "kvm-riscv", "list_id": "kvm-riscv.lists.infradead.org", "list_email": "kvm-riscv@lists.infradead.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "http://lists.infradead.org/pipermail/kvm-riscv/", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<202603301612587174XZ6QMCrymBqv30S6BN50@zte.com.cn>", "list_archive_url": null, "date": "2026-03-30T08:12:58", "name": "[v4,2/2] RISC-V: KVM: Split huge pages during fault handling for dirty logging", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "ad34b9ca4a6e465d69bb078270b785fccb4a9265", "submitter": { "id": 91800, "url": "http://patchwork.ozlabs.org/api/people/91800/?format=api", "name": "", "email": "wang.yechao255@zte.com.cn" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/kvm-riscv/patch/202603301612587174XZ6QMCrymBqv30S6BN50@zte.com.cn/mbox/", "series": [ { "id": 497970, "url": "http://patchwork.ozlabs.org/api/series/497970/?format=api", "web_url": "http://patchwork.ozlabs.org/project/kvm-riscv/list/?series=497970", "date": "2026-03-30T08:08:17", "name": "RISC-V: KVM: Fix hugepage mapping handling during dirty logging", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/497970/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2217564/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2217564/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <kvm-riscv-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n secure) header.d=lists.infradead.org header.i=@lists.infradead.org\n header.a=rsa-sha256 header.s=bombadil.20210309 header.b=xgN2oKm1;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n secure) header.d=infradead.org header.i=@infradead.org header.a=rsa-sha256\n header.s=desiato.20200630 header.b=KlWXwKKU;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=none (no SPF record) smtp.mailfrom=lists.infradead.org\n (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org;\n envelope-from=kvm-riscv-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from bombadil.infradead.org (bombadil.infradead.org\n [IPv6:2607:7c80:54:3::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fkkXr2jqjz1xrn\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 30 Mar 2026 19:13:48 +1100 (AEDT)", "from localhost ([::1] helo=bombadil.infradead.org)\n\tby bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1w77ko-0000000ApCB-3FpK;\n\tMon, 30 Mar 2026 08:13:46 +0000", "from desiato.infradead.org ([2001:8b0:10b:1:d65d:64ff:fe57:4e05])\n\tby bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1w77km-0000000ApBv-3p3f;\n\tMon, 30 Mar 2026 08:13:45 +0000", "from mxct.zte.com.cn ([183.62.165.209])\n\tby desiato.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1w77kj-0000000DHw8-0pyx;\n\tMon, 30 Mar 2026 08:13:43 +0000", "from mse-fl1.zte.com.cn (unknown [10.5.228.132])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby mxct.zte.com.cn (FangMail) with ESMTPS id 4fkkWw63wqz4xQXB;\n\tMon, 30 Mar 2026 16:13:00 +0800 (CST)", "from szxlzmapp01.zte.com.cn ([10.5.231.85])\n\tby mse-fl1.zte.com.cn with SMTP id 62U8CuS6055225;\n\tMon, 30 Mar 2026 16:12:56 +0800 (+08)\n\t(envelope-from wang.yechao255@zte.com.cn)", "from mapi (szxlzmapp02[null])\n\tby mapi (Zmail) with MAPI id mid12;\n\tMon, 30 Mar 2026 16:12:58 +0800 (CST)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=lists.infradead.org; s=bombadil.20210309; h=Sender:\n\tContent-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post:\n\tList-Archive:List-Unsubscribe:List-Id:Subject:Cc:To:From:Mime-Version:Date:\n\tReferences:In-Reply-To:Message-ID:Reply-To:Content-ID:Content-Description:\n\tResent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:\n\tList-Owner; bh=GIjGWGcpfdibGFZ93VBmTN0vZR7mFw/hLJXXiPx/pr8=; b=xgN2oKm1LbHjqf\n\tfbYgPvdhTkp/dXLpEaBrStiRSl67oKFE1aIKzCKyIX4ZczdCNbimedwSElU2+hIgZyZQ5ruEZAJds\n\t58o7anTPiBGKSiC2kprxpq5h2JM+lkiBBAjVXMb12rg2tPBD29Wko989z/z6jBf1DGk/nU8mHm6y3\n\t+r/UiPM4f8L1UdbRsctbmheX7eIVEOKrRaZKuYD9+XkDYj0zR1xXcggMA7zJb5FGUiTHOWHqI/1lV\n\tH0xmT9J//O59c/gogFAfRoVJ8Ciik6hn2+cHX/moUHO0fCApVRg6U0EzEPUiyq0wRLXz7zXoAJy7k\n\tkV+PWispeRGK1dvVg2bA==;", "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=infradead.org; s=desiato.20200630; h=Content-Type:Subject:Cc:To:From:\n\tMime-Version:Date:References:In-Reply-To:Message-ID:Sender:Reply-To:\n\tContent-Transfer-Encoding:Content-ID:Content-Description;\n\tbh=Si/5wraTAOZ2qFub6LskIiqD5+w6RD8DbTI0lIDzVb4=; b=KlWXwKKUVaj8P44N2WkV6OJaJg\n\td9ibT0U035x/yi4rZwGrO430yMBkCAogbVoia4HoI2t4k5aa/SB+clRcRm8wh5CWZBGvy1BZWWkzU\n\tYc2lQqZvD3NX9QDqYYvFO6/0TkQkkLeJZO4tPGfbLzpIHxm5mA6+OI6I1jDP4hP88Z356dFGxIfqf\n\taosE6309spDhZ2MK3bPGrLdmW2ObS8X9rzY6S49KD1bcom/FebC4yh87JzgIAXYRupBKOIGUBmOHK\n\tGVPt5MNjiXt4PSinPLUNbGrR5gf+1b/1YAFp5fZT2DL0JWts7R3o/Mlen0pkIVhh4rBESyoU8alvN\n\tgdY+2Naw==;" ], "X-Zmail-TransId": "2b0469ca308a8c5-48805", "X-Mailer": "Zmail v1.0", "Message-ID": "<202603301612587174XZ6QMCrymBqv30S6BN50@zte.com.cn>", "In-Reply-To": "<202603301608170032mtkGKX7wRcAkPKDQ5I-F@zte.com.cn>", "References": "202603301608170032mtkGKX7wRcAkPKDQ5I-F@zte.com.cn", "Date": "Mon, 30 Mar 2026 16:12:58 +0800 (CST)", "Mime-Version": "1.0", "From": "<wang.yechao255@zte.com.cn>", "To": "<anup@brainfault.org>, <atish.patra@linux.dev>, <pjw@kernel.org>,\n <palmer@dabbelt.com>, <aou@eecs.berkeley.edu>, <alex@ghiti.fr>", "Cc": "<kvm@vger.kernel.org>, <kvm-riscv@lists.infradead.org>,\n <linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>,\n <wang.yechao255@zte.com.cn>", "Subject": "=?utf-8?q?=C2=A0=5BPATCH_v4_2/2=5D_RISC-V=3A_KVM=3A_Split_huge_page?=\n\t=?utf-8?q?s_during_fault_handling_for_dirty_logging?=", "X-MAIL": "mse-fl1.zte.com.cn 62U8CuS6055225", "X-TLS": "YES", "X-SPF-DOMAIN": "zte.com.cn", "X-ENVELOPE-SENDER": "wang.yechao255@zte.com.cn", "X-SPF": "None", "X-SOURCE-IP": "10.5.228.132 unknown Mon, 30 Mar 2026 16:13:00 +0800", "X-Fangmail-Anti-Spam-Filtered": "true", "X-Fangmail-MID-QID": "69CA308C.002/4fkkWw63wqz4xQXB", "X-Bad-Reply": "References and In-Reply-To but no 'Re:' in Subject.", "X-CRM114-Version": "20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 ", "X-CRM114-CacheID": "sfid-20260330_091341_866532_27A19796 ", "X-CRM114-Status": "GOOD ( 20.12 )", "X-Spam-Score": "-0.3 (/)", "X-Spam-Report": "Spam detection software,\n running on the system \"desiato.infradead.org\",\n has NOT identified this incoming email as spam. The original\n message has been attached to this so you can view it or label\n similar future email. If you have any questions, see\n the administrator of that system for details.\n Content preview: From: Wang Yechao <wang.yechao255@zte.com.cn> During dirty\n logging,\n all huge pages are write-protected. When the guest writes to a write-protected\n huge page,\n a page fault is triggered. Before recovering the write permission,\n the huge page must be [...]\n Content analysis details: (-0.3 points, 5.0 required)\n pts rule name description\n ---- ----------------------\n --------------------------------------------------\n -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/,\n medium trust\n [183.62.165.209 listed in list.dnswl.org]\n -0.0 SPF_PASS SPF: sender matches SPF record\n 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record\n -1.0 RCVD_IN_MSPIKE_H5 RBL: Excellent reputation (+5)\n [183.62.165.209 listed in wl.mailspike.net]\n 1.0 RCVD_IN_VALIDITY_SAFE_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to\n Validity was blocked. See\n https://knowledge.validity.com/hc/en-us/articles/20961730681243\n for more information.\n [183.62.165.209 listed in sa-accredit.habeas.com]\n 1.0 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED RBL: ADMINISTRATOR NOTICE: The\n query to Validity was blocked. See\n https://knowledge.validity.com/hc/en-us/articles/20961730681243\n for more information.\n [183.62.165.209 listed in\n sa-trusted.bondedsender.org]\n 0.0 UNPARSEABLE_RELAY Informational: message has unparseable relay\n lines\n -0.0 RCVD_IN_MSPIKE_WL Mailspike good senders\n 1.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to\n Validity was blocked. See\n https://knowledge.validity.com/hc/en-us/articles/20961730681243\n for more information.\n [183.62.165.209 listed in\n bl.score.senderscore.com]", "X-BeenThere": "kvm-riscv@lists.infradead.org", "X-Mailman-Version": "2.1.34", "Precedence": "list", "List-Id": "<kvm-riscv.lists.infradead.org>", "List-Unsubscribe": "<http://lists.infradead.org/mailman/options/kvm-riscv>,\n <mailto:kvm-riscv-request@lists.infradead.org?subject=unsubscribe>", "List-Archive": "<http://lists.infradead.org/pipermail/kvm-riscv/>", "List-Post": "<mailto:kvm-riscv@lists.infradead.org>", "List-Help": "<mailto:kvm-riscv-request@lists.infradead.org?subject=help>", "List-Subscribe": "<http://lists.infradead.org/mailman/listinfo/kvm-riscv>,\n <mailto:kvm-riscv-request@lists.infradead.org?subject=subscribe>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Sender": "\"kvm-riscv\" <kvm-riscv-bounces@lists.infradead.org>", "Errors-To": "kvm-riscv-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org" }, "content": "From: Wang Yechao <wang.yechao255@zte.com.cn>\n\nDuring dirty logging, all huge pages are write-protected. When the guest\nwrites to a write-protected huge page, a page fault is triggered. Before\nrecovering the write permission, the huge page must be split into smaller\npages (e.g., 4K). After splitting, the normal mapping process proceeds,\nallowing write permission to be restored at the smaller page granularity.\n\nIf dirty logging is disabled because migration failed or was cancelled,\nonly recover the write permission at the 4K level, and skip recovering the\nhuge page mapping at this time to avoid the overhead of freeing page tables.\nThe huge page mapping can be recovered in the ioctl context, similar to x86,\nin a later patch.\n\nSigned-off-by: Wang Yechao <wang.yechao255@zte.com.cn>\n---\n arch/riscv/include/asm/kvm_gstage.h | 4 +\n arch/riscv/kvm/gstage.c | 126 ++++++++++++++++++++++++++++\n 2 files changed, 130 insertions(+)", "diff": "diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h\nindex 595e2183173e..373748c6745e 100644\n--- a/arch/riscv/include/asm/kvm_gstage.h\n+++ b/arch/riscv/include/asm/kvm_gstage.h\n@@ -53,6 +53,10 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n \t\t\t bool page_rdonly, bool page_exec,\n \t\t\t struct kvm_gstage_mapping *out_map);\n\n+int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n+ struct kvm_mmu_memory_cache *pcache,\n+ gpa_t addr, u32 target_level, bool flush);\n+\n enum kvm_riscv_gstage_op {\n \tGSTAGE_OP_NOP = 0,\t/* Nothing */\n \tGSTAGE_OP_CLEAR,\t/* Clear/Unmap */\ndiff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c\nindex d2001d508046..ffec3e5ddcaf 100644\n--- a/arch/riscv/kvm/gstage.c\n+++ b/arch/riscv/kvm/gstage.c\n@@ -163,13 +163,32 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,\n \treturn 0;\n }\n\n+static void kvm_riscv_gstage_update_pte_prot(struct kvm_gstage *gstage, u32 level,\n+\t\t\t\t\t gpa_t addr, pte_t *ptep, pgprot_t prot)\n+{\n+\tpte_t new_pte;\n+\n+\tif (pgprot_val(pte_pgprot(ptep_get(ptep))) == pgprot_val(prot))\n+\t\treturn;\n+\n+\tnew_pte = pfn_pte(pte_pfn(ptep_get(ptep)), prot);\n+\tnew_pte = pte_mkdirty(new_pte);\n+\n+\tset_pte(ptep, new_pte);\n+\n+\tgstage_tlb_flush(gstage, level, addr);\n+}\n+\n int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n \t\t\t struct kvm_mmu_memory_cache *pcache,\n \t\t\t gpa_t gpa, phys_addr_t hpa, unsigned long page_size,\n \t\t\t bool page_rdonly, bool page_exec,\n \t\t\t struct kvm_gstage_mapping *out_map)\n {\n+\tbool found_leaf;\n+\tu32 ptep_level;\n \tpgprot_t prot;\n+\tpte_t *ptep;\n \tint ret;\n\n \tout_map->addr = gpa;\n@@ -203,12 +222,119 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n \t\telse\n \t\t\tprot = PAGE_WRITE;\n \t}\n+\n+\tfound_leaf = kvm_riscv_gstage_get_leaf(gstage, gpa, &ptep, &ptep_level);\n+\tif (found_leaf) {\n+\t\t/*\n+\t\t * ptep_level is the current gstage mapping level of addr, out_map->level\n+\t\t * is the required mapping level during fault handling.\n+\t\t *\n+\t\t * 1) ptep_level > out_map->level\n+\t\t * This happens when dirty logging is enabled and huge pages are used.\n+\t\t * KVM must track the pages at 4K level, and split the huge mapping\n+\t\t * into 4K mappings.\n+\t\t *\n+\t\t * 2) ptep_level < out_map->level\n+\t\t * This happens when dirty logging is disabled and huge pages are used.\n+\t\t * The gstage is split into 4K mappings, but the out_map level is now\n+\t\t * back to the huge page level. Ignore the out_map level this time, and\n+\t\t * just update the pte prot here. Otherwise, we would fall back to mapping\n+\t\t * the gstage at huge page level in `kvm_riscv_gstage_set_pte`, with the\n+\t\t * overhead of freeing the page tables(not support now), which would slow\n+\t\t * down the vCPUs' performance.\n+\t\t *\n+\t\t * It is better to recover the huge page mapping in the ioctl context when\n+\t\t * disabling dirty logging.\n+\t\t *\n+\t\t * 3) ptep_level == out_map->level\n+\t\t * We already have the ptep, just update the pte prot if the pfn not change.\n+\t\t * There is no need to invoke `kvm_riscv_gstage_set_pte` again.\n+\t\t */\n+\t\tif (ptep_level > out_map->level) {\n+\t\t\tkvm_riscv_gstage_split_huge(gstage, pcache, gpa,\n+\t\t\t\t\t\t out_map->level, true);\n+\t\t} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {\n+\t\t\tkvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n \tout_map->pte = pfn_pte(PFN_DOWN(hpa), prot);\n \tout_map->pte = pte_mkdirty(out_map->pte);\n\n \treturn kvm_riscv_gstage_set_pte(gstage, pcache, out_map);\n }\n\n+static inline unsigned long make_child_pte(unsigned long huge_pte, int index,\n+\t\t\t\t\t unsigned long child_page_size)\n+{\n+\tunsigned long child_pte = huge_pte;\n+\tunsigned long child_pfn_offset;\n+\n+\t/*\n+\t * The child_pte already has the base address of the huge page being\n+\t * split. So we just have to OR in the offset to the page at the next\n+\t * lower level for the given index.\n+\t */\n+\tchild_pfn_offset = index * (child_page_size / PAGE_SIZE);\n+\tchild_pte |= pte_val(pfn_pte(child_pfn_offset, __pgprot(0)));\n+\n+\treturn child_pte;\n+}\n+\n+int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n+\t\t\t\tstruct kvm_mmu_memory_cache *pcache,\n+\t\t\t\tgpa_t addr, u32 target_level, bool flush)\n+{\n+\tu32 current_level = kvm_riscv_gstage_pgd_levels - 1;\n+\tpte_t *next_ptep = (pte_t *)gstage->pgd;\n+\tunsigned long huge_pte, child_pte;\n+\tunsigned long child_page_size;\n+\tpte_t *ptep;\n+\tint i, ret;\n+\n+\tif (!pcache)\n+\t\treturn -ENOMEM;\n+\n+\twhile(current_level > target_level) {\n+\t\tptep = (pte_t *)&next_ptep[gstage_pte_index(addr, current_level)];\n+\n+\t\tif (!pte_val(ptep_get(ptep)))\n+\t\t\tbreak;\n+\n+\t\tif (!gstage_pte_leaf(ptep)) {\n+\t\t\tnext_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep));\n+\t\t\tcurrent_level--;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\thuge_pte = pte_val(ptep_get(ptep));\n+\n+\t\tret = gstage_level_to_page_size(current_level - 1, &child_page_size);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tnext_ptep = kvm_mmu_memory_cache_alloc(pcache);\n+\t\tif (!next_ptep)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tfor (i = 0; i < PTRS_PER_PTE; i++) {\n+\t\t\tchild_pte = make_child_pte(huge_pte, i, child_page_size);\n+\t\t\tset_pte((pte_t *)&next_ptep[i], __pte(child_pte));\n+\t\t}\n+\n+\t\tset_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)),\n+\t\t\t\t__pgprot(_PAGE_TABLE)));\n+\n+\t\tif (flush)\n+\t\t\tgstage_tlb_flush(gstage, current_level, addr);\n+\n+\t\tcurrent_level--;\n+\t}\n+\n+\treturn 0;\n+}\n+\n void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,\n \t\t\t pte_t *ptep, u32 ptep_level, enum kvm_riscv_gstage_op op)\n {\n", "prefixes": [ "v4", "2/2" ] }