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.

kvm: x86: hyperv: guest->host event signaling via eventfd

In Hyper-V, the fast guest->host notification mechanism is the
SIGNAL_EVENT hypercall, with a single parameter of the connection ID to
signal.

Currently this hypercall incurs a user exit and requires the userspace
to decode the parameters and trigger the notification of the potentially
different I/O context.

To avoid the costly user exit, process this hypercall and signal the
corresponding eventfd in KVM, similar to ioeventfd. The association
between the connection id and the eventfd is established via the newly
introduced KVM_HYPERV_EVENTFD ioctl, and maintained in an
(srcu-protected) IDR.

Signed-off-by: Roman Kagan <rkagan@virtuozzo.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
[asm/hyperv.h changes approved by KY Srinivasan. - Radim]
Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>

authored by

Roman Kagan and committed by
Radim Krčmář
faeb7833 cbc0236a

+164 -1
+32
Documentation/virtual/kvm/api.txt
··· 3516 3516 This ioctl can be used to unregister the guest memory region registered 3517 3517 with KVM_MEMORY_ENCRYPT_REG_REGION ioctl above. 3518 3518 3519 + 4.113 KVM_HYPERV_EVENTFD 3520 + 3521 + Capability: KVM_CAP_HYPERV_EVENTFD 3522 + Architectures: x86 3523 + Type: vm ioctl 3524 + Parameters: struct kvm_hyperv_eventfd (in) 3525 + 3526 + This ioctl (un)registers an eventfd to receive notifications from the guest on 3527 + the specified Hyper-V connection id through the SIGNAL_EVENT hypercall, without 3528 + causing a user exit. SIGNAL_EVENT hypercall with non-zero event flag number 3529 + (bits 24-31) still triggers a KVM_EXIT_HYPERV_HCALL user exit. 3530 + 3531 + struct kvm_hyperv_eventfd { 3532 + __u32 conn_id; 3533 + __s32 fd; 3534 + __u32 flags; 3535 + __u32 padding[3]; 3536 + }; 3537 + 3538 + The conn_id field should fit within 24 bits: 3539 + 3540 + #define KVM_HYPERV_CONN_ID_MASK 0x00ffffff 3541 + 3542 + The acceptable values for the flags field are: 3543 + 3544 + #define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) 3545 + 3546 + Returns: 0 on success, 3547 + -EINVAL if conn_id or flags is outside the allowed range 3548 + -ENOENT on deassign if the conn_id isn't registered 3549 + -EEXIST on assign if the conn_id is already registered 3550 + 3519 3551 3520 3552 5. The kvm_run structure 3521 3553 ------------------------
+2
arch/x86/include/asm/kvm_host.h
··· 754 754 u64 hv_crash_ctl; 755 755 756 756 HV_REFERENCE_TSC_PAGE tsc_ref; 757 + 758 + struct idr conn_to_evt; 757 759 }; 758 760 759 761 enum kvm_irqchip_mode {
+2
arch/x86/include/uapi/asm/hyperv.h
··· 303 303 #define HV_STATUS_INVALID_HYPERCALL_CODE 2 304 304 #define HV_STATUS_INVALID_HYPERCALL_INPUT 3 305 305 #define HV_STATUS_INVALID_ALIGNMENT 4 306 + #define HV_STATUS_INVALID_PARAMETER 5 306 307 #define HV_STATUS_INSUFFICIENT_MEMORY 11 308 + #define HV_STATUS_INVALID_PORT_ID 17 307 309 #define HV_STATUS_INVALID_CONNECTION_ID 18 308 310 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 309 311
+102 -1
arch/x86/kvm/hyperv.c
··· 29 29 #include <linux/kvm_host.h> 30 30 #include <linux/highmem.h> 31 31 #include <linux/sched/cputime.h> 32 + #include <linux/eventfd.h> 32 33 33 34 #include <asm/apicdef.h> 34 35 #include <trace/events/kvm.h> ··· 1227 1226 return 1; 1228 1227 } 1229 1228 1229 + static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, bool fast, u64 param) 1230 + { 1231 + struct eventfd_ctx *eventfd; 1232 + 1233 + if (unlikely(!fast)) { 1234 + int ret; 1235 + gpa_t gpa = param; 1236 + 1237 + if ((gpa & (__alignof__(param) - 1)) || 1238 + offset_in_page(gpa) + sizeof(param) > PAGE_SIZE) 1239 + return HV_STATUS_INVALID_ALIGNMENT; 1240 + 1241 + ret = kvm_vcpu_read_guest(vcpu, gpa, &param, sizeof(param)); 1242 + if (ret < 0) 1243 + return HV_STATUS_INVALID_ALIGNMENT; 1244 + } 1245 + 1246 + /* 1247 + * Per spec, bits 32-47 contain the extra "flag number". However, we 1248 + * have no use for it, and in all known usecases it is zero, so just 1249 + * report lookup failure if it isn't. 1250 + */ 1251 + if (param & 0xffff00000000ULL) 1252 + return HV_STATUS_INVALID_PORT_ID; 1253 + /* remaining bits are reserved-zero */ 1254 + if (param & ~KVM_HYPERV_CONN_ID_MASK) 1255 + return HV_STATUS_INVALID_HYPERCALL_INPUT; 1256 + 1257 + /* conn_to_evt is protected by vcpu->kvm->srcu */ 1258 + eventfd = idr_find(&vcpu->kvm->arch.hyperv.conn_to_evt, param); 1259 + if (!eventfd) 1260 + return HV_STATUS_INVALID_PORT_ID; 1261 + 1262 + eventfd_signal(eventfd, 1); 1263 + return HV_STATUS_SUCCESS; 1264 + } 1265 + 1230 1266 int kvm_hv_hypercall(struct kvm_vcpu *vcpu) 1231 1267 { 1232 1268 u64 param, ingpa, outgpa, ret; ··· 1314 1276 case HVCALL_NOTIFY_LONG_SPIN_WAIT: 1315 1277 kvm_vcpu_on_spin(vcpu, true); 1316 1278 break; 1317 - case HVCALL_POST_MESSAGE: 1318 1279 case HVCALL_SIGNAL_EVENT: 1280 + res = kvm_hvcall_signal_event(vcpu, fast, ingpa); 1281 + if (res != HV_STATUS_INVALID_PORT_ID) 1282 + break; 1283 + /* maybe userspace knows this conn_id: fall through */ 1284 + case HVCALL_POST_MESSAGE: 1319 1285 /* don't bother userspace if it has no way to handle it */ 1320 1286 if (!vcpu_to_synic(vcpu)->active) { 1321 1287 res = HV_STATUS_INVALID_HYPERCALL_CODE; ··· 1347 1305 void kvm_hv_init_vm(struct kvm *kvm) 1348 1306 { 1349 1307 mutex_init(&kvm->arch.hyperv.hv_lock); 1308 + idr_init(&kvm->arch.hyperv.conn_to_evt); 1350 1309 } 1351 1310 1352 1311 void kvm_hv_destroy_vm(struct kvm *kvm) 1353 1312 { 1313 + struct eventfd_ctx *eventfd; 1314 + int i; 1315 + 1316 + idr_for_each_entry(&kvm->arch.hyperv.conn_to_evt, eventfd, i) 1317 + eventfd_ctx_put(eventfd); 1318 + idr_destroy(&kvm->arch.hyperv.conn_to_evt); 1319 + } 1320 + 1321 + static int kvm_hv_eventfd_assign(struct kvm *kvm, u32 conn_id, int fd) 1322 + { 1323 + struct kvm_hv *hv = &kvm->arch.hyperv; 1324 + struct eventfd_ctx *eventfd; 1325 + int ret; 1326 + 1327 + eventfd = eventfd_ctx_fdget(fd); 1328 + if (IS_ERR(eventfd)) 1329 + return PTR_ERR(eventfd); 1330 + 1331 + mutex_lock(&hv->hv_lock); 1332 + ret = idr_alloc(&hv->conn_to_evt, eventfd, conn_id, conn_id + 1, 1333 + GFP_KERNEL); 1334 + mutex_unlock(&hv->hv_lock); 1335 + 1336 + if (ret >= 0) 1337 + return 0; 1338 + 1339 + if (ret == -ENOSPC) 1340 + ret = -EEXIST; 1341 + eventfd_ctx_put(eventfd); 1342 + return ret; 1343 + } 1344 + 1345 + static int kvm_hv_eventfd_deassign(struct kvm *kvm, u32 conn_id) 1346 + { 1347 + struct kvm_hv *hv = &kvm->arch.hyperv; 1348 + struct eventfd_ctx *eventfd; 1349 + 1350 + mutex_lock(&hv->hv_lock); 1351 + eventfd = idr_remove(&hv->conn_to_evt, conn_id); 1352 + mutex_unlock(&hv->hv_lock); 1353 + 1354 + if (!eventfd) 1355 + return -ENOENT; 1356 + 1357 + synchronize_srcu(&kvm->srcu); 1358 + eventfd_ctx_put(eventfd); 1359 + return 0; 1360 + } 1361 + 1362 + int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args) 1363 + { 1364 + if ((args->flags & ~KVM_HYPERV_EVENTFD_DEASSIGN) || 1365 + (args->conn_id & ~KVM_HYPERV_CONN_ID_MASK)) 1366 + return -EINVAL; 1367 + 1368 + if (args->flags == KVM_HYPERV_EVENTFD_DEASSIGN) 1369 + return kvm_hv_eventfd_deassign(kvm, args->conn_id); 1370 + return kvm_hv_eventfd_assign(kvm, args->conn_id, args->fd); 1354 1371 }
+1
arch/x86/kvm/hyperv.h
··· 90 90 91 91 void kvm_hv_init_vm(struct kvm *kvm); 92 92 void kvm_hv_destroy_vm(struct kvm *kvm); 93 + int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args); 93 94 94 95 #endif
+10
arch/x86/kvm/x86.c
··· 2809 2809 case KVM_CAP_HYPERV_SYNIC: 2810 2810 case KVM_CAP_HYPERV_SYNIC2: 2811 2811 case KVM_CAP_HYPERV_VP_INDEX: 2812 + case KVM_CAP_HYPERV_EVENTFD: 2812 2813 case KVM_CAP_PCI_SEGMENT: 2813 2814 case KVM_CAP_DEBUGREGS: 2814 2815 case KVM_CAP_X86_ROBUST_SINGLESTEP: ··· 4481 4480 r = -ENOTTY; 4482 4481 if (kvm_x86_ops->mem_enc_unreg_region) 4483 4482 r = kvm_x86_ops->mem_enc_unreg_region(kvm, &region); 4483 + break; 4484 + } 4485 + case KVM_HYPERV_EVENTFD: { 4486 + struct kvm_hyperv_eventfd hvevfd; 4487 + 4488 + r = -EFAULT; 4489 + if (copy_from_user(&hvevfd, argp, sizeof(hvevfd))) 4490 + goto out; 4491 + r = kvm_vm_ioctl_hv_eventfd(kvm, &hvevfd); 4484 4492 break; 4485 4493 } 4486 4494 default:
+15
include/uapi/linux/kvm.h
··· 936 936 #define KVM_CAP_PPC_GET_CPU_CHAR 151 937 937 #define KVM_CAP_S390_BPB 152 938 938 #define KVM_CAP_GET_MSR_FEATURES 153 939 + #define KVM_CAP_HYPERV_EVENTFD 154 939 940 940 941 #ifdef KVM_CAP_IRQ_ROUTING 941 942 ··· 1376 1375 #define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region) 1377 1376 #define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region) 1378 1377 1378 + /* Available with KVM_CAP_HYPERV_EVENTFD */ 1379 + #define KVM_HYPERV_EVENTFD _IOW(KVMIO, 0xbd, struct kvm_hyperv_eventfd) 1380 + 1381 + 1379 1382 /* Secure Encrypted Virtualization command */ 1380 1383 enum sev_cmd_id { 1381 1384 /* Guest initialization commands */ ··· 1519 1514 #define KVM_ARM_DEV_EL1_VTIMER (1 << 0) 1520 1515 #define KVM_ARM_DEV_EL1_PTIMER (1 << 1) 1521 1516 #define KVM_ARM_DEV_PMU (1 << 2) 1517 + 1518 + struct kvm_hyperv_eventfd { 1519 + __u32 conn_id; 1520 + __s32 fd; 1521 + __u32 flags; 1522 + __u32 padding[3]; 1523 + }; 1524 + 1525 + #define KVM_HYPERV_CONN_ID_MASK 0x00ffffff 1526 + #define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) 1522 1527 1523 1528 #endif /* __LINUX_KVM_H */