Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

RISC-V: KVM: Implement VCPU interrupts and requests handling

This patch implements VCPU interrupts and requests which are both
asynchronous events.

The VCPU interrupts can be set/unset using KVM_INTERRUPT ioctl from
user-space. In future, the in-kernel IRQCHIP emulation will use
kvm_riscv_vcpu_set_interrupt() and kvm_riscv_vcpu_unset_interrupt()
functions to set/unset VCPU interrupts.

Important VCPU requests implemented by this patch are:
KVM_REQ_SLEEP - set whenever VCPU itself goes to sleep state
KVM_REQ_VCPU_RESET - set whenever VCPU reset is requested

The WFI trap-n-emulate (added later) will use KVM_REQ_SLEEP request
and kvm_riscv_vcpu_has_interrupt() function.

The KVM_REQ_VCPU_RESET request will be used by SBI emulation (added
later) to power-up a VCPU in power-off state. The user-space can use
the GET_MPSTATE/SET_MPSTATE ioctls to get/set power state of a VCPU.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Alexander Graf <graf@amazon.com>
Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>

authored by

Anup Patel and committed by
Anup Patel
cce69aff a33c72fa

+197 -13
+23
arch/riscv/include/asm/kvm_host.h
··· 127 127 /* CPU CSR context upon Guest VCPU reset */ 128 128 struct kvm_vcpu_csr guest_reset_csr; 129 129 130 + /* 131 + * VCPU interrupts 132 + * 133 + * We have a lockless approach for tracking pending VCPU interrupts 134 + * implemented using atomic bitops. The irqs_pending bitmap represent 135 + * pending interrupts whereas irqs_pending_mask represent bits changed 136 + * in irqs_pending. Our approach is modeled around multiple producer 137 + * and single consumer problem where the consumer is the VCPU itself. 138 + */ 139 + unsigned long irqs_pending; 140 + unsigned long irqs_pending_mask; 141 + 142 + /* VCPU power-off state */ 143 + bool power_off; 144 + 130 145 /* Don't run the VCPU (blocked) */ 131 146 bool pause; 132 147 ··· 164 149 struct kvm_cpu_trap *trap); 165 150 166 151 static inline void __kvm_riscv_switch_to(struct kvm_vcpu_arch *vcpu_arch) {} 152 + 153 + int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq); 154 + int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq); 155 + void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu); 156 + void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu); 157 + bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, unsigned long mask); 158 + void kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu); 159 + void kvm_riscv_vcpu_power_on(struct kvm_vcpu *vcpu); 167 160 168 161 #endif /* __RISCV_KVM_HOST_H__ */
+3
arch/riscv/include/uapi/asm/kvm.h
··· 18 18 19 19 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 20 20 21 + #define KVM_INTERRUPT_SET -1U 22 + #define KVM_INTERRUPT_UNSET -2U 23 + 21 24 /* for KVM_GET_REGS and KVM_SET_REGS */ 22 25 struct kvm_regs { 23 26 };
+171 -13
arch/riscv/kvm/vcpu.c
··· 11 11 #include <linux/err.h> 12 12 #include <linux/kdebug.h> 13 13 #include <linux/module.h> 14 + #include <linux/percpu.h> 14 15 #include <linux/uaccess.h> 15 16 #include <linux/vmalloc.h> 16 17 #include <linux/sched/signal.h> ··· 58 57 memcpy(csr, reset_csr, sizeof(*csr)); 59 58 60 59 memcpy(cntx, reset_cntx, sizeof(*cntx)); 60 + 61 + WRITE_ONCE(vcpu->arch.irqs_pending, 0); 62 + WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0); 61 63 } 62 64 63 65 int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) ··· 104 100 105 101 int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) 106 102 { 107 - /* TODO: */ 108 - return 0; 103 + return kvm_riscv_vcpu_has_interrupts(vcpu, 1UL << IRQ_VS_TIMER); 109 104 } 110 105 111 106 void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) ··· 117 114 118 115 int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) 119 116 { 120 - /* TODO: */ 121 - return 0; 117 + return (kvm_riscv_vcpu_has_interrupts(vcpu, -1UL) && 118 + !vcpu->arch.power_off && !vcpu->arch.pause); 122 119 } 123 120 124 121 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) 125 122 { 126 - /* TODO: */ 127 - return 0; 123 + return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; 128 124 } 129 125 130 126 bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) 131 127 { 132 - /* TODO: */ 133 - return false; 128 + return (vcpu->arch.guest_context.sstatus & SR_SPP) ? true : false; 134 129 } 135 130 136 131 vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) ··· 139 138 long kvm_arch_vcpu_async_ioctl(struct file *filp, 140 139 unsigned int ioctl, unsigned long arg) 141 140 { 142 - /* TODO; */ 141 + struct kvm_vcpu *vcpu = filp->private_data; 142 + void __user *argp = (void __user *)arg; 143 + 144 + if (ioctl == KVM_INTERRUPT) { 145 + struct kvm_interrupt irq; 146 + 147 + if (copy_from_user(&irq, argp, sizeof(irq))) 148 + return -EFAULT; 149 + 150 + if (irq.irq == KVM_INTERRUPT_SET) 151 + return kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_EXT); 152 + else 153 + return kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_EXT); 154 + } 155 + 143 156 return -ENOIOCTLCMD; 144 157 } 145 158 ··· 202 187 return -EINVAL; 203 188 } 204 189 190 + void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) 191 + { 192 + struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; 193 + unsigned long mask, val; 194 + 195 + if (READ_ONCE(vcpu->arch.irqs_pending_mask)) { 196 + mask = xchg_acquire(&vcpu->arch.irqs_pending_mask, 0); 197 + val = READ_ONCE(vcpu->arch.irqs_pending) & mask; 198 + 199 + csr->hvip &= ~mask; 200 + csr->hvip |= val; 201 + } 202 + } 203 + 204 + void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) 205 + { 206 + unsigned long hvip; 207 + struct kvm_vcpu_arch *v = &vcpu->arch; 208 + struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; 209 + 210 + /* Read current HVIP and VSIE CSRs */ 211 + csr->vsie = csr_read(CSR_VSIE); 212 + 213 + /* Sync-up HVIP.VSSIP bit changes does by Guest */ 214 + hvip = csr_read(CSR_HVIP); 215 + if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) { 216 + if (hvip & (1UL << IRQ_VS_SOFT)) { 217 + if (!test_and_set_bit(IRQ_VS_SOFT, 218 + &v->irqs_pending_mask)) 219 + set_bit(IRQ_VS_SOFT, &v->irqs_pending); 220 + } else { 221 + if (!test_and_set_bit(IRQ_VS_SOFT, 222 + &v->irqs_pending_mask)) 223 + clear_bit(IRQ_VS_SOFT, &v->irqs_pending); 224 + } 225 + } 226 + } 227 + 228 + int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) 229 + { 230 + if (irq != IRQ_VS_SOFT && 231 + irq != IRQ_VS_TIMER && 232 + irq != IRQ_VS_EXT) 233 + return -EINVAL; 234 + 235 + set_bit(irq, &vcpu->arch.irqs_pending); 236 + smp_mb__before_atomic(); 237 + set_bit(irq, &vcpu->arch.irqs_pending_mask); 238 + 239 + kvm_vcpu_kick(vcpu); 240 + 241 + return 0; 242 + } 243 + 244 + int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) 245 + { 246 + if (irq != IRQ_VS_SOFT && 247 + irq != IRQ_VS_TIMER && 248 + irq != IRQ_VS_EXT) 249 + return -EINVAL; 250 + 251 + clear_bit(irq, &vcpu->arch.irqs_pending); 252 + smp_mb__before_atomic(); 253 + set_bit(irq, &vcpu->arch.irqs_pending_mask); 254 + 255 + return 0; 256 + } 257 + 258 + bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, unsigned long mask) 259 + { 260 + unsigned long ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK) 261 + << VSIP_TO_HVIP_SHIFT) & mask; 262 + 263 + return (READ_ONCE(vcpu->arch.irqs_pending) & ie) ? true : false; 264 + } 265 + 266 + void kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu) 267 + { 268 + vcpu->arch.power_off = true; 269 + kvm_make_request(KVM_REQ_SLEEP, vcpu); 270 + kvm_vcpu_kick(vcpu); 271 + } 272 + 273 + void kvm_riscv_vcpu_power_on(struct kvm_vcpu *vcpu) 274 + { 275 + vcpu->arch.power_off = false; 276 + kvm_vcpu_wake_up(vcpu); 277 + } 278 + 205 279 int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, 206 280 struct kvm_mp_state *mp_state) 207 281 { 208 - /* TODO: */ 282 + if (vcpu->arch.power_off) 283 + mp_state->mp_state = KVM_MP_STATE_STOPPED; 284 + else 285 + mp_state->mp_state = KVM_MP_STATE_RUNNABLE; 286 + 209 287 return 0; 210 288 } 211 289 212 290 int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, 213 291 struct kvm_mp_state *mp_state) 214 292 { 215 - /* TODO: */ 216 - return 0; 293 + int ret = 0; 294 + 295 + switch (mp_state->mp_state) { 296 + case KVM_MP_STATE_RUNNABLE: 297 + vcpu->arch.power_off = false; 298 + break; 299 + case KVM_MP_STATE_STOPPED: 300 + kvm_riscv_vcpu_power_off(vcpu); 301 + break; 302 + default: 303 + ret = -EINVAL; 304 + } 305 + 306 + return ret; 217 307 } 218 308 219 309 int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, ··· 342 222 343 223 static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu) 344 224 { 345 - /* TODO: */ 225 + struct rcuwait *wait = kvm_arch_vcpu_get_wait(vcpu); 226 + 227 + if (kvm_request_pending(vcpu)) { 228 + if (kvm_check_request(KVM_REQ_SLEEP, vcpu)) { 229 + rcuwait_wait_event(wait, 230 + (!vcpu->arch.power_off) && (!vcpu->arch.pause), 231 + TASK_INTERRUPTIBLE); 232 + 233 + if (vcpu->arch.power_off || vcpu->arch.pause) { 234 + /* 235 + * Awaken to handle a signal, request to 236 + * sleep again later. 237 + */ 238 + kvm_make_request(KVM_REQ_SLEEP, vcpu); 239 + } 240 + } 241 + 242 + if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu)) 243 + kvm_riscv_reset_vcpu(vcpu); 244 + } 245 + } 246 + 247 + static void kvm_riscv_update_hvip(struct kvm_vcpu *vcpu) 248 + { 249 + struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; 250 + 251 + csr_write(CSR_HVIP, csr->hvip); 346 252 } 347 253 348 254 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) ··· 432 286 srcu_read_unlock(&vcpu->kvm->srcu, vcpu->arch.srcu_idx); 433 287 smp_mb__after_srcu_read_unlock(); 434 288 289 + /* 290 + * We might have got VCPU interrupts updated asynchronously 291 + * so update it in HW. 292 + */ 293 + kvm_riscv_vcpu_flush_interrupts(vcpu); 294 + 295 + /* Update HVIP CSR for current CPU */ 296 + kvm_riscv_update_hvip(vcpu); 297 + 435 298 if (ret <= 0 || 436 299 kvm_request_pending(vcpu)) { 437 300 vcpu->mode = OUTSIDE_GUEST_MODE; ··· 467 312 trap.stval = csr_read(CSR_STVAL); 468 313 trap.htval = csr_read(CSR_HTVAL); 469 314 trap.htinst = csr_read(CSR_HTINST); 315 + 316 + /* Syncup interrupts state with HW */ 317 + kvm_riscv_vcpu_sync_interrupts(vcpu); 470 318 471 319 /* 472 320 * We may have taken a host interrupt in VS/VU-mode (i.e.