* [PATCH v3] LoongArch: KVM: Add KVM_LOONGARCH_GET/SET_CSR for bulk CSR migration
@ 2026-07-22 14:28 Tao Cui
0 siblings, 0 replies; only message in thread
From: Tao Cui @ 2026-07-22 14:28 UTC (permalink / raw)
To: zhaotianrui, maobibo
Cc: chenhuacai, kernel, kvm, loongarch, linux-kernel, cui.tao, cuitao
From: Tao Cui <cuitao@kylinos.cn>
Migrating a vCPU's CSR state takes one KVM_GET/SET_ONE_REG ioctl per
register, thousands of syscalls for a large VM.
Add a bulk pair, KVM_LOONGARCH_GET_CSR / KVM_SET_CSR. Userspace passes
an array of {index, data} entries; the kernel fills entries[].data on
GET and applies them on SET, both in a single call:
- KVM_LOONGARCH_GET_CSR does a single vcpu_load/put with
kvm_deliver_intr(), which pulls pending interrupts into ESTAT,
avoiding the per-register load/put side-effect that made ONE_REG
snapshots of ESTAT order-sensitive.
- KVM_LOONGARCH_SET_CSR writes all CSRs via _kvm_setcsr in one pass,
propagates _kvm_setcsr errors, and clears KVM_LARCH_HWCSR_USABLE up
front so a mid-loop failure still forces the next vcpu_load() to
reload from SW, matching KVM_SET_ONE_REG.
Entries are {index, data} rather than a positional array, so future CSRs
can be added without resizing the struct or changing the ioctl number.
CSRs above the core range (debug, breakpoint, PMU) stay on
KVM_GET/SET_ONE_REG; struct kvm_sregs is left empty, so the new ioctls
break no userspace, and userspace probes them via KVM_CAP_LOONGARCH_CSR.
The ioctl hands get_gcsr_flag() a user-controlled index, so switch that
helper (and the set_gcsr_sw_flag/set_gcsr_hw_flag init helpers) to an
unsigned index; the csr < CSR_MAX_NUMS bound check is then robust against
a sign-extended value regardless of the caller.
On a Loongson 3A6000, snapshotting one vCPU's core CSR range (0x0-0x183)
drops from 388 KVM_GET_ONE_REG calls (~2 ms) to a single
KVM_LOONGARCH_GET_CSR ioctl (~6 us).
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
Changes since v2:
- Bound-check the CSR index (< CSR_MAX_NUMS) and make
get_gcsr_flag()/set_gcsr_*_flag() take an unsigned index. An index
with bit 31 set was implicitly converted to a negative signed int,
bypassing the csr < CSR_MAX_NUMS check and causing an out-of-bounds
read/write into the SW CSR shadow.
- Reject non-zero pad/reserved fields with -EINVAL so they stay reserved.
- Add KVM_CAP_LOONGARCH_CSR for userspace feature detection.
arch/loongarch/include/asm/kvm_host.h | 2 +-
arch/loongarch/include/uapi/asm/kvm.h | 13 +++++
arch/loongarch/kvm/main.c | 6 +--
arch/loongarch/kvm/vcpu.c | 77 +++++++++++++++++++++++++++
arch/loongarch/kvm/vm.c | 1 +
include/uapi/linux/kvm.h | 5 ++
6 files changed, 100 insertions(+), 4 deletions(-)
diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h
index 23cfbecebbd7..aacf5d84217f 100644
--- a/arch/loongarch/include/asm/kvm_host.h
+++ b/arch/loongarch/include/asm/kvm_host.h
@@ -362,7 +362,7 @@ extern struct kvm_world_switch *kvm_loongarch_ops;
#define HW_GCSR (1 << 1)
#define INVALID_GCSR (1 << 2)
-int get_gcsr_flag(int csr);
+int get_gcsr_flag(unsigned int csr);
void set_hw_gcsr(int csr_id, unsigned long val);
#endif /* __ASM_LOONGARCH_KVM_HOST_H__ */
diff --git a/arch/loongarch/include/uapi/asm/kvm.h b/arch/loongarch/include/uapi/asm/kvm.h
index cd0b5c11ca9c..59b3b7efa039 100644
--- a/arch/loongarch/include/uapi/asm/kvm.h
+++ b/arch/loongarch/include/uapi/asm/kvm.h
@@ -127,6 +127,19 @@ struct kvm_sync_regs {
struct kvm_sregs {
};
+/* bulk CSR entries for VM migration */
+struct kvm_loongarch_csr_entry {
+ __u32 index;
+ __u32 reserved;
+ __u64 data;
+};
+
+struct kvm_loongarch_csrs {
+ __u32 ncsrs; /* number of csr entries */
+ __u32 pad;
+ __DECLARE_FLEX_ARRAY(struct kvm_loongarch_csr_entry, entries);
+};
+
struct kvm_iocsr_entry {
__u32 addr;
__u32 pad;
diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c
index 3e1005526f4b..d802d393cd44 100644
--- a/arch/loongarch/kvm/main.c
+++ b/arch/loongarch/kvm/main.c
@@ -19,7 +19,7 @@ struct kvm_world_switch *kvm_loongarch_ops;
static int gcsr_flag[CSR_MAX_NUMS];
static struct kvm_context __percpu *vmcs;
-int get_gcsr_flag(int csr)
+int get_gcsr_flag(unsigned int csr)
{
if (csr < CSR_MAX_NUMS)
return gcsr_flag[csr];
@@ -27,13 +27,13 @@ int get_gcsr_flag(int csr)
return INVALID_GCSR;
}
-static inline void set_gcsr_sw_flag(int csr)
+static inline void set_gcsr_sw_flag(unsigned int csr)
{
if (csr < CSR_MAX_NUMS)
gcsr_flag[csr] |= SW_GCSR;
}
-static inline void set_gcsr_hw_flag(int csr)
+static inline void set_gcsr_hw_flag(unsigned int csr)
{
if (csr < CSR_MAX_NUMS)
gcsr_flag[csr] |= HW_GCSR;
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 20c207d80e31..2bb6a7e0ea87 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -1009,6 +1009,77 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs
return -ENOIOCTLCMD;
}
+/* max CSR entries handled per ioctl */
+#define KVM_LOONGARCH_MAX_CSR_REGS 0x400
+
+/* Bulk get/set of CSR state for VM migration. */
+static int kvm_loongarch_csr_io(struct kvm_vcpu *vcpu,
+ struct kvm_loongarch_csrs __user *ucsrs,
+ bool write)
+{
+ int i, ret = 0;
+ unsigned long size;
+ struct kvm_loongarch_csrs csrs;
+ struct kvm_loongarch_csr_entry *entries;
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+
+ if (copy_from_user(&csrs, ucsrs, sizeof(csrs)))
+ return -EFAULT;
+
+ if (csrs.pad)
+ return -EINVAL;
+
+ if (csrs.ncsrs > KVM_LOONGARCH_MAX_CSR_REGS)
+ return -E2BIG;
+
+ size = array_size(csrs.ncsrs, sizeof(*entries));
+ entries = memdup_user(ucsrs->entries, size);
+ if (IS_ERR(entries))
+ return PTR_ERR(entries);
+
+ if (write) {
+ vcpu->arch.aux_inuse &= ~KVM_LARCH_HWCSR_USABLE;
+ } else {
+ preempt_disable();
+ vcpu_load(vcpu);
+ kvm_deliver_intr(vcpu);
+ vcpu->arch.aux_inuse &= ~KVM_LARCH_SWCSR_LATEST;
+ vcpu_put(vcpu);
+ preempt_enable();
+ }
+
+ for (i = 0; i < csrs.ncsrs; i++) {
+ unsigned int id = entries[i].index;
+
+ if (entries[i].reserved || id >= CSR_MAX_NUMS ||
+ (get_gcsr_flag(id) & INVALID_GCSR)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (write) {
+ ret = _kvm_setcsr(vcpu, id, entries[i].data);
+ if (ret)
+ break;
+ } else if (id == LOONGARCH_CSR_ESTAT) {
+ unsigned long estat, gintc;
+
+ /* ESTAT IP0~IP7 come from GINTC */
+ gintc = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & KVM_GINTC_IRQ_MASK;
+ estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT) & ~KVM_ESTAT_EXTI_MASK;
+ entries[i].data = estat | (gintc << VIP_DELTA);
+ } else {
+ entries[i].data = kvm_read_sw_gcsr(csr, id);
+ }
+ }
+
+ if (!write && copy_to_user(ucsrs->entries, entries, size))
+ ret = -EFAULT;
+
+ kfree(entries);
+ return ret;
+}
+
int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
{
int i;
@@ -1266,6 +1337,12 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
r = kvm_get_reg(vcpu, ®);
break;
}
+ case KVM_LOONGARCH_GET_CSR:
+ r = kvm_loongarch_csr_io(vcpu, argp, false);
+ break;
+ case KVM_LOONGARCH_SET_CSR:
+ r = kvm_loongarch_csr_io(vcpu, argp, true);
+ break;
case KVM_ENABLE_CAP: {
struct kvm_enable_cap cap;
diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c
index 1317c718f896..785f2fa0d164 100644
--- a/arch/loongarch/kvm/vm.c
+++ b/arch/loongarch/kvm/vm.c
@@ -122,6 +122,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_IOEVENTFD:
case KVM_CAP_MP_STATE:
case KVM_CAP_SET_GUEST_DEBUG:
+ case KVM_CAP_LOONGARCH_CSR:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 419011097fa8..45d9eb06cb8a 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -997,6 +997,7 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
#define KVM_CAP_S390_HPAGE_2G 249
+#define KVM_CAP_LOONGARCH_CSR 250
struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1670,4 +1671,8 @@ struct kvm_pre_fault_memory {
__u64 padding[5];
};
+/* LoongArch: bulk CSR get/set for migration */
+#define KVM_LOONGARCH_GET_CSR _IOWR(KVMIO, 0xd6, struct kvm_loongarch_csrs)
+#define KVM_LOONGARCH_SET_CSR _IOW(KVMIO, 0xd7, struct kvm_loongarch_csrs)
+
#endif /* __LINUX_KVM_H */
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-22 14:28 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-22 14:28 [PATCH v3] LoongArch: KVM: Add KVM_LOONGARCH_GET/SET_CSR for bulk CSR migration Tao Cui
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox