@@ -449,6 +449,9 @@ int gzvm_vm_ioctl_arch_enable_cap(struct gzvm *gzvm,
case GZVM_CAP_BLOCK_BASED_DEMAND_PAGING:
ret = gzvm_vm_arch_enable_cap(gzvm, cap, &res);
return ret;
+ case GZVM_CAP_ENABLE_IDLE:
+ ret = gzvm_vm_arch_enable_cap(gzvm, cap, &res);
+ return ret;
default:
break;
}
@@ -5,6 +5,8 @@
#include <linux/device.h>
#include <linux/soc/mediatek/gzvm_drv.h>
+#include <linux/sched.h>
+#include <linux/rcuwait.h>
/**
* gzvm_handle_guest_exception() - Handle guest exception
@@ -59,3 +61,47 @@ bool gzvm_handle_guest_hvc(struct gzvm_vcpu *vcpu)
return gzvm_arch_handle_guest_hvc(vcpu);
}
}
+
+static void vcpu_block_wait(struct gzvm_vcpu *vcpu)
+{
+ struct rcuwait *wait = &vcpu->wait;
+
+ prepare_to_rcuwait(wait);
+
+ while (true) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (vcpu->idle_events.virtio_irq) {
+ vcpu->idle_events.virtio_irq = 0;
+ break;
+ }
+ if (vcpu->idle_events.vtimer_irq) {
+ vcpu->idle_events.vtimer_irq = 0;
+ break;
+ }
+ if (signal_pending(current))
+ break;
+ schedule();
+ }
+ finish_rcuwait(wait);
+}
+
+/**
+ * gzvm_handle_guest_idle() - Handle guest vm entering idle
+ * @vcpu: Pointer to struct gzvm_vcpu struct
+ * Return:
+ */
+int gzvm_handle_guest_idle(struct gzvm_vcpu *vcpu)
+{
+ int ret = 0;
+ u64 ns = 0;
+
+ ns = gzvm_vcpu_arch_get_timer_delay_ns(vcpu);
+
+ if (ns) {
+ gzvm_vtimer_set(vcpu, ns);
+ vcpu_block_wait(vcpu);
+ gzvm_vtimer_release(vcpu);
+ }
+
+ return ret;
+}
@@ -16,8 +16,29 @@
/* maximum size needed for holding an integer */
#define ITOA_MAX_LEN 12
+/**
+ * gzvm_vcpu_wakeup_all - wakes up all vCPUs associated with the specified
+ * gzvm.
+ * @gzvm: Pointer to gzvm structure.
+ */
+void gzvm_vcpu_wakeup_all(struct gzvm *gzvm)
+{
+ for (int i = 0; i < GZVM_MAX_VCPUS; i++) {
+ if (gzvm->vcpus[i]) {
+ gzvm->vcpus[i]->idle_events.virtio_irq += 1;
+ rcuwait_wake_up(&gzvm->vcpus[i]->wait);
+ }
+ }
+}
+
static enum hrtimer_restart gzvm_vtimer_expire(struct hrtimer *hrt)
{
+ struct gzvm_vcpu *vcpu;
+
+ vcpu = container_of(hrt, struct gzvm_vcpu, gzvm_vtimer);
+
+ gzvm_vcpu_wakeup_all(vcpu->gzvm);
+
return HRTIMER_NORESTART;
}
@@ -111,7 +132,7 @@ static bool gzvm_vcpu_handle_mmio(struct gzvm_vcpu *vcpu)
static long gzvm_vcpu_run(struct gzvm_vcpu *vcpu, void __user *argp)
{
bool need_userspace = false;
- u64 exit_reason = 0;
+ u64 exit_reason;
if (copy_from_user(vcpu->run, argp, sizeof(struct gzvm_vcpu_run)))
return -EFAULT;
@@ -160,6 +181,9 @@ static long gzvm_vcpu_run(struct gzvm_vcpu *vcpu, void __user *argp)
fallthrough;
case GZVM_EXIT_GZ:
break;
+ case GZVM_EXIT_IDLE:
+ gzvm_handle_guest_idle(vcpu);
+ break;
case GZVM_EXIT_UNKNOWN:
fallthrough;
default:
@@ -170,6 +170,7 @@ gzvm_vm_ioctl_set_memory_region(struct gzvm *gzvm,
int gzvm_irqchip_inject_irq(struct gzvm *gzvm, unsigned int vcpu_idx,
u32 irq, bool level)
{
+ gzvm_vcpu_wakeup_all(gzvm);
return gzvm_arch_inject_irq(gzvm, vcpu_idx, irq, level);
}
@@ -556,6 +557,18 @@ static int setup_mem_alloc_mode(struct gzvm *vm)
return 0;
}
+static int enable_idle_support(struct gzvm *vm)
+{
+ int ret;
+ struct gzvm_enable_cap cap = {0};
+
+ cap.cap = GZVM_CAP_ENABLE_IDLE;
+ ret = gzvm_vm_ioctl_enable_cap(vm, &cap, NULL);
+ if (ret)
+ pr_info("Hypervisor doesn't support idle\n");
+ return ret;
+}
+
static struct gzvm *gzvm_create_vm(struct gzvm_driver *drv, unsigned long vm_type)
{
int ret;
@@ -604,6 +617,8 @@ static struct gzvm *gzvm_create_vm(struct gzvm_driver *drv, unsigned long vm_typ
pr_debug("VM-%u is created\n", gzvm->vm_id);
+ enable_idle_support(gzvm);
+
return gzvm;
}
@@ -130,6 +130,11 @@ struct gzvm_vcpu {
struct gzvm_vcpu_run *run;
struct gzvm_vcpu_hwstate *hwstate;
struct hrtimer gzvm_vtimer;
+ struct {
+ u32 vtimer_irq;
+ u32 virtio_irq;
+ } idle_events;
+ struct rcuwait wait;
};
struct gzvm_pinned_page {
@@ -260,6 +265,8 @@ bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu);
int gzvm_handle_relinquish(struct gzvm_vcpu *vcpu, phys_addr_t ipa);
bool gzvm_handle_guest_hvc(struct gzvm_vcpu *vcpu);
bool gzvm_arch_handle_guest_hvc(struct gzvm_vcpu *vcpu);
+int gzvm_handle_guest_idle(struct gzvm_vcpu *vcpu);
+void gzvm_vcpu_wakeup_all(struct gzvm *gzvm);
int gzvm_arch_create_device(u16 vm_id, struct gzvm_create_device *gzvm_dev);
int gzvm_arch_inject_irq(struct gzvm *gzvm, unsigned int vcpu_idx,
@@ -21,6 +21,7 @@
/* query hypervisor supported block-based demand page */
#define GZVM_CAP_BLOCK_BASED_DEMAND_PAGING 0x9201
#define GZVM_CAP_ENABLE_DEMAND_PAGING 0x9202
+#define GZVM_CAP_ENABLE_IDLE 0x9203
#define GZVM_CAP_QUERY_HYP_BATCH_PAGES 0x9204
/* sub-commands put in args[0] for GZVM_CAP_PROTECTED_VM */
@@ -188,6 +189,7 @@ enum {
GZVM_EXIT_SYSTEM_EVENT = 0x92920008,
GZVM_EXIT_SHUTDOWN = 0x92920009,
GZVM_EXIT_GZ = 0x9292000a,
+ GZVM_EXIT_IDLE = 0x9292000b,
};
/* exception definitions of GZVM_EXIT_EXCEPTION */