From: Liju-clr Chen <liju-clr.chen@mediatek.com>
To: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Jonathan Corbet <corbet@lwn.net>,
"Catalin Marinas" <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>,
"Steven Rostedt" <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
Richard Cochran <richardcochran@gmail.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
Liju-clr Chen <Liju-clr.Chen@mediatek.com>,
Yingshiuan Pan <Yingshiuan.Pan@mediatek.com>,
Ze-yu Wang <Ze-yu.Wang@mediatek.com>
Cc: <devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-doc@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-trace-kernel@vger.kernel.org>, <netdev@vger.kernel.org>,
<linux-mediatek@lists.infradead.org>,
Shawn Hsiao <shawn.hsiao@mediatek.com>,
PeiLun Suei <PeiLun.Suei@mediatek.com>,
Chi-shen Yeh <Chi-shen.Yeh@mediatek.com>,
Kevenny Hsieh <Kevenny.Hsieh@mediatek.com>
Subject: [PATCH v13 15/25] virt: geniezone: Add memory pin/unpin support
Date: Thu, 14 Nov 2024 18:07:52 +0800 [thread overview]
Message-ID: <20241114100802.4116-16-liju-clr.chen@mediatek.com> (raw)
In-Reply-To: <20241114100802.4116-1-liju-clr.chen@mediatek.com>
From: Jerry Wang <ze-yu.wang@mediatek.com>
Protected VM's memory cannot be swapped out because the memory pages are
protected from host access.
Once host accesses to those protected pages, the hardware exception is
triggered and may crash the host. So, we have to make those protected
pages be ineligible for swapping or merging by the host kernel to avoid
host access. To do so, we pin the page when it is assigned (donated) to
VM and unpin when VM relinquish the pages or is destroyed. Besides, the
protected VM’s memory requires hypervisor to clear the content before
returning to host, but VMM may free those memory before clearing, it
will result in those memory pages are reclaimed and reused before
totally clearing. Using pin/unpin can also avoid the above problems.
The implementation is described as follows.
- Use rb_tree to store pinned memory pages.
- Pin the page when handling page fault.
- Unpin the pages when VM relinquish the pages or is destroyed.
Signed-off-by: Jerry Wang <ze-yu.wang@mediatek.com>
Co-developed-by: Yingshiuan Pan <yingshiuan.pan@mediatek.com>
Signed-off-by: Yingshiuan Pan <yingshiuan.pan@mediatek.com>
Signed-off-by: Yi-De Wu <yi-de.wu@mediatek.com>
Signed-off-by: Liju Chen <liju-clr.chen@mediatek.com>
---
arch/arm64/geniezone/vm.c | 8 +-
drivers/virt/geniezone/Makefile | 2 +-
drivers/virt/geniezone/gzvm_mmu.c | 103 ++++++++++++++++++++++++++
drivers/virt/geniezone/gzvm_vm.c | 21 ++++++
include/linux/soc/mediatek/gzvm_drv.h | 14 ++++
5 files changed, 145 insertions(+), 3 deletions(-)
create mode 100644 drivers/virt/geniezone/gzvm_mmu.c
diff --git a/arch/arm64/geniezone/vm.c b/arch/arm64/geniezone/vm.c
index cc6c7e99851c..ac3d163a40fd 100644
--- a/arch/arm64/geniezone/vm.c
+++ b/arch/arm64/geniezone/vm.c
@@ -220,12 +220,14 @@ static int gzvm_vm_ioctl_get_pvmfw_size(struct gzvm *gzvm,
* @gfn: Guest frame number.
* @total_pages: Total page numbers.
* @slot: Pointer to struct gzvm_memslot.
+ * @gzvm: Pointer to struct gzvm.
*
* Return: how many pages we've fill in, negative if error
*/
static int fill_constituents(struct mem_region_addr_range *consti,
int *consti_cnt, int max_nr_consti, u64 gfn,
- u32 total_pages, struct gzvm_memslot *slot)
+ u32 total_pages, struct gzvm_memslot *slot,
+ struct gzvm *gzvm)
{
u64 pfn = 0, prev_pfn = 0, gfn_end = 0;
int nr_pages = 0;
@@ -236,6 +238,8 @@ static int fill_constituents(struct mem_region_addr_range *consti,
gfn_end = gfn + total_pages;
while (i < max_nr_consti && gfn < gfn_end) {
+ if (gzvm_vm_allocate_guest_page(gzvm, slot, gfn, &pfn) != 0)
+ return -EFAULT;
if (pfn == (prev_pfn + 1)) {
consti[i].pg_cnt++;
} else {
@@ -291,7 +295,7 @@ int gzvm_vm_populate_mem_region(struct gzvm *gzvm, int slot_id)
nr_pages = fill_constituents(region->constituents,
®ion->constituent_cnt,
max_nr_consti, gfn,
- remain_pages, memslot);
+ remain_pages, memslot, gzvm);
if (nr_pages < 0) {
pr_err("Failed to fill constituents\n");
diff --git a/drivers/virt/geniezone/Makefile b/drivers/virt/geniezone/Makefile
index bc5ae49f2407..e0451145215d 100644
--- a/drivers/virt/geniezone/Makefile
+++ b/drivers/virt/geniezone/Makefile
@@ -8,4 +8,4 @@ GZVM_DIR ?= ../../../drivers/virt/geniezone
gzvm-y := $(GZVM_DIR)/gzvm_main.o $(GZVM_DIR)/gzvm_vm.o \
$(GZVM_DIR)/gzvm_vcpu.o $(GZVM_DIR)/gzvm_irqfd.o \
- $(GZVM_DIR)/gzvm_ioeventfd.o
+ $(GZVM_DIR)/gzvm_ioeventfd.o $(GZVM_DIR)/gzvm_mmu.o
diff --git a/drivers/virt/geniezone/gzvm_mmu.c b/drivers/virt/geniezone/gzvm_mmu.c
new file mode 100644
index 000000000000..743df8976dfd
--- /dev/null
+++ b/drivers/virt/geniezone/gzvm_mmu.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+
+#include <linux/soc/mediatek/gzvm_drv.h>
+
+static int cmp_ppages(struct rb_node *node, const struct rb_node *parent)
+{
+ struct gzvm_pinned_page *a = container_of(node,
+ struct gzvm_pinned_page,
+ node);
+ struct gzvm_pinned_page *b = container_of(parent,
+ struct gzvm_pinned_page,
+ node);
+
+ if (a->ipa < b->ipa)
+ return -1;
+ if (a->ipa > b->ipa)
+ return 1;
+ return 0;
+}
+
+/* Invoker of this function is responsible for locking */
+static int gzvm_insert_ppage(struct gzvm *vm, struct gzvm_pinned_page *ppage)
+{
+ if (rb_find_add(&ppage->node, &vm->pinned_pages, cmp_ppages))
+ return -EEXIST;
+ return 0;
+}
+
+static int pin_one_page(struct gzvm *vm, unsigned long hva, u64 gpa,
+ struct page **out_page)
+{
+ unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
+ struct gzvm_pinned_page *ppage = NULL;
+ struct mm_struct *mm = current->mm;
+ struct page *page = NULL;
+ int ret;
+
+ ppage = kmalloc(sizeof(*ppage), GFP_KERNEL_ACCOUNT);
+ if (!ppage)
+ return -ENOMEM;
+
+ mmap_read_lock(mm);
+ ret = pin_user_pages(hva, 1, flags, &page);
+ mmap_read_unlock(mm);
+
+ if (ret != 1 || !page) {
+ kfree(ppage);
+ return -EFAULT;
+ }
+
+ ppage->page = page;
+ ppage->ipa = gpa;
+
+ mutex_lock(&vm->mem_lock);
+ ret = gzvm_insert_ppage(vm, ppage);
+
+ /**
+ * The return of -EEXIST from gzvm_insert_ppage is considered an
+ * expected behavior in this context.
+ * This situation arises when two or more VCPUs are concurrently
+ * engaged in demand paging handling. The initial VCPU has already
+ * allocated and pinned a page, while the subsequent VCPU attempts
+ * to pin the same page again. As a result, we prompt the unpinning
+ * and release of the allocated structure, followed by a return 0.
+ */
+ if (ret == -EEXIST) {
+ kfree(ppage);
+ unpin_user_pages(&page, 1);
+ ret = 0;
+ }
+ mutex_unlock(&vm->mem_lock);
+ *out_page = page;
+
+ return ret;
+}
+
+int gzvm_vm_allocate_guest_page(struct gzvm *vm, struct gzvm_memslot *slot,
+ u64 gfn, u64 *pfn)
+{
+ struct page *page = NULL;
+ unsigned long hva;
+ int ret;
+
+ if (gzvm_gfn_to_hva_memslot(slot, gfn, (u64 *)&hva) != 0)
+ return -EINVAL;
+
+ ret = pin_one_page(vm, hva, PFN_PHYS(gfn), &page);
+ if (ret != 0)
+ return ret;
+
+ if (page == NULL)
+ return -EFAULT;
+ /**
+ * As `pin_user_pages` already gets the page struct, we don't need to
+ * call other APIs to reduce function call overhead.
+ */
+ *pfn = page_to_pfn(page);
+
+ return 0;
+}
diff --git a/drivers/virt/geniezone/gzvm_vm.c b/drivers/virt/geniezone/gzvm_vm.c
index 5fc0167d2776..12f2c3c3810f 100644
--- a/drivers/virt/geniezone/gzvm_vm.c
+++ b/drivers/virt/geniezone/gzvm_vm.c
@@ -298,6 +298,22 @@ static long gzvm_vm_ioctl(struct file *filp, unsigned int ioctl,
return ret;
}
+/* Invoker of this function is responsible for locking */
+static void gzvm_destroy_all_ppage(struct gzvm *gzvm)
+{
+ struct gzvm_pinned_page *ppage;
+ struct rb_node *node;
+
+ node = rb_first(&gzvm->pinned_pages);
+ while (node) {
+ ppage = rb_entry(node, struct gzvm_pinned_page, node);
+ unpin_user_pages_dirty_lock(&ppage->page, 1, true);
+ node = rb_next(node);
+ rb_erase(&ppage->node, &gzvm->pinned_pages);
+ kfree(ppage);
+ }
+}
+
static void gzvm_destroy_vm(struct gzvm *gzvm)
{
pr_debug("VM-%u is going to be destroyed\n", gzvm->vm_id);
@@ -314,6 +330,9 @@ static void gzvm_destroy_vm(struct gzvm *gzvm)
mutex_unlock(&gzvm->lock);
+ /* No need to lock here becauese it's single-threaded execution */
+ gzvm_destroy_all_ppage(gzvm);
+
kfree(gzvm);
}
@@ -349,6 +368,8 @@ static struct gzvm *gzvm_create_vm(struct gzvm_driver *drv, unsigned long vm_typ
gzvm->vm_id = ret;
gzvm->mm = current->mm;
mutex_init(&gzvm->lock);
+ mutex_init(&gzvm->mem_lock);
+ gzvm->pinned_pages = RB_ROOT;
ret = gzvm_vm_irqfd_init(gzvm);
if (ret) {
diff --git a/include/linux/soc/mediatek/gzvm_drv.h b/include/linux/soc/mediatek/gzvm_drv.h
index 07ab42357328..920af91ea576 100644
--- a/include/linux/soc/mediatek/gzvm_drv.h
+++ b/include/linux/soc/mediatek/gzvm_drv.h
@@ -12,6 +12,7 @@
#include <linux/mutex.h>
#include <linux/gzvm.h>
#include <linux/srcu.h>
+#include <linux/rbtree.h>
/* GZVM version encode */
#define GZVM_DRV_MAJOR_VERSION 16
@@ -112,6 +113,12 @@ struct gzvm_vcpu {
struct gzvm_vcpu_hwstate *hwstate;
};
+struct gzvm_pinned_page {
+ struct rb_node node;
+ struct page *page;
+ u64 ipa;
+};
+
/**
* struct gzvm: the following data structures are for data transferring between
* driver and hypervisor, and they're aligned with hypervisor definitions.
@@ -128,6 +135,8 @@ struct gzvm_vcpu {
* @irq_ack_notifier_list: list head for irq ack notifier
* @irq_srcu: structure data for SRCU(sleepable rcu)
* @irq_lock: lock for irq injection
+ * @pinned_pages: use rb-tree to record pin/unpin page
+ * @mem_lock: lock for memory operations
*/
struct gzvm {
struct gzvm_driver *gzvm_drv;
@@ -152,6 +161,9 @@ struct gzvm {
struct hlist_head irq_ack_notifier_list;
struct srcu_struct irq_srcu;
struct mutex irq_lock;
+
+ struct rb_root pinned_pages;
+ struct mutex mem_lock;
};
long gzvm_dev_ioctl_check_extension(struct gzvm *gzvm, unsigned long args);
@@ -178,6 +190,8 @@ int gzvm_vm_ioctl_arch_enable_cap(struct gzvm *gzvm,
int gzvm_gfn_to_hva_memslot(struct gzvm_memslot *memslot, u64 gfn,
u64 *hva_memslot);
int gzvm_vm_populate_mem_region(struct gzvm *gzvm, int slot_id);
+int gzvm_vm_allocate_guest_page(struct gzvm *gzvm, struct gzvm_memslot *slot,
+ u64 gfn, u64 *pfn);
int gzvm_vm_ioctl_create_vcpu(struct gzvm *gzvm, u32 cpuid);
int gzvm_arch_vcpu_update_one_reg(struct gzvm_vcpu *vcpu, __u64 reg_id,
--
2.18.0
next prev parent reply other threads:[~2024-11-14 10:08 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-14 10:07 [PATCH v13 00/25] GenieZone hypervisor drivers Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 01/25] virt: geniezone: enable gzvm-ko in defconfig Liju-clr Chen
2024-12-11 8:38 ` Krzysztof Kozlowski
2024-12-13 5:57 ` Liju-clr Chen (陳麗如)
2024-11-14 10:07 ` [PATCH v13 02/25] docs: geniezone: Introduce GenieZone hypervisor Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 03/25] dt-bindings: hypervisor: Add MediaTek " Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 04/25] virt: geniezone: Add GenieZone hypervisor driver Liju-clr Chen
2024-12-11 8:44 ` Krzysztof Kozlowski
2025-08-12 7:04 ` Liju-clr Chen (陳麗如)
2025-08-12 7:23 ` Krzysztof Kozlowski
2025-08-12 8:39 ` Liju-clr Chen (陳麗如)
2025-08-12 7:32 ` Krzysztof Kozlowski
2025-08-14 3:27 ` Liju-clr Chen (陳麗如)
2024-11-14 10:07 ` [PATCH v13 05/25] virt: geniezone: Add vm support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 06/25] virt: geniezone: Add set_user_memory_region for vm Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 07/25] virt: geniezone: Add vm capability check Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 08/25] virt: geniezone: Add vcpu support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 09/25] virt: geniezone: Add irqchip support for virtual interrupt injection Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 10/25] virt: geniezone: Add irqfd support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 11/25] virt: geniezone: Add ioeventfd support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 12/25] virt: geniezone: Add memory region purpose for hypervisor Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 13/25] virt: geniezone: Add dtb config support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 14/25] virt: geniezone: Optimize performance of protected VM memory Liju-clr Chen
2024-11-14 10:07 ` Liju-clr Chen [this message]
2024-12-17 10:58 ` [PATCH v13 15/25] virt: geniezone: Add memory pin/unpin support Hillf Danton
2024-11-14 10:07 ` [PATCH v13 16/25] virt: geniezone: Add demand paging support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 17/25] virt: geniezone: Add block-based " Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 18/25] virt: geniezone: Add memory relinquish support Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 19/25] virt: geniezone: Provide individual VM memory statistics within debugfs Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 20/25] virt: geniezone: Add tracing support for hyp call and vcpu exit_reason Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 21/25] virt: geniezone: Enable PTP for synchronizing time between host and guest VMs Liju-clr Chen
2024-11-14 10:07 ` [PATCH v13 22/25] virt: geniezone: Add support for virtual timer migration Liju-clr Chen
2024-11-14 10:08 ` [PATCH v13 23/25] virt: geniezone: Add support for guest VM CPU idle Liju-clr Chen
2024-12-17 10:45 ` Hillf Danton
2024-11-14 10:08 ` [PATCH v13 24/25] virt: geniezone: Emulate IPI for guest VM Liju-clr Chen
2024-11-14 10:08 ` [PATCH v13 25/25] virt: geniezone: Reduce blocked duration in hypervisor when destroying a VM Liju-clr Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241114100802.4116-16-liju-clr.chen@mediatek.com \
--to=liju-clr.chen@mediatek.com \
--cc=Chi-shen.Yeh@mediatek.com \
--cc=Kevenny.Hsieh@mediatek.com \
--cc=PeiLun.Suei@mediatek.com \
--cc=Yingshiuan.Pan@mediatek.com \
--cc=Ze-yu.Wang@mediatek.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=catalin.marinas@arm.com \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=matthias.bgg@gmail.com \
--cc=mhiramat@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=richardcochran@gmail.com \
--cc=robh@kernel.org \
--cc=rostedt@goodmis.org \
--cc=shawn.hsiao@mediatek.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®