xen/arch/arm/domain.c | 63 ++++++++++++++++++++++++++--- xen/common/domain.c | 101 ++++++++++++++++++++++++++++++++++++++++++++-- xen/include/public/vcpu.h | 15 +++++++ xen/include/xen/sched.h | 28 +++++++++---- 4 files changed, 190 insertions(+), 17 deletions(-)
From: Andrii Anisov <andrii_anisov@epam.com>
An RFC version of the runstate registration with phys address.
Runstate area access is implemented with mapping on each update once for
all accesses.
Signed-off-by: Andrii Anisov <andrii_anisov@epam.com>
---
xen/arch/arm/domain.c | 63 ++++++++++++++++++++++++++---
xen/common/domain.c | 101 ++++++++++++++++++++++++++++++++++++++++++++--
xen/include/public/vcpu.h | 15 +++++++
xen/include/xen/sched.h | 28 +++++++++----
4 files changed, 190 insertions(+), 17 deletions(-)
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
index a9f7ff5..04c4cff 100644
--- a/xen/arch/arm/domain.c
+++ b/xen/arch/arm/domain.c
@@ -274,17 +274,15 @@ static void ctxt_switch_to(struct vcpu *n)
virt_timer_restore(n);
}
-/* Update per-VCPU guest runstate shared memory area (if registered). */
-static void update_runstate_area(struct vcpu *v)
+static void update_runstate_by_gvaddr(struct vcpu *v)
{
void __user *guest_handle = NULL;
- if ( guest_handle_is_null(runstate_guest(v)) )
- return;
+ ASSERT(!guest_handle_is_null(runstate_guest_virt(v)));
if ( VM_ASSIST(v->domain, runstate_update_flag) )
{
- guest_handle = &v->runstate_guest.p->state_entry_time + 1;
+ guest_handle = &v->runstate_guest.virt.p->state_entry_time + 1;
guest_handle--;
v->runstate.state_entry_time |= XEN_RUNSTATE_UPDATE;
__raw_copy_to_guest(guest_handle,
@@ -292,7 +290,7 @@ static void update_runstate_area(struct vcpu *v)
smp_wmb();
}
- __copy_to_guest(runstate_guest(v), &v->runstate, 1);
+ __copy_to_guest(runstate_guest_virt(v), &v->runstate, 1);
if ( guest_handle )
{
@@ -303,6 +301,58 @@ static void update_runstate_area(struct vcpu *v)
}
}
+extern int map_runstate_area(struct vcpu *v, struct vcpu_runstate_info **area);
+extern void unmap_runstate_area(struct vcpu_runstate_info *area);
+
+static void update_runstate_by_gpaddr(struct vcpu *v)
+{
+ struct vcpu_runstate_info *runstate;
+
+ if ( map_runstate_area(v, &runstate) )
+ return;
+
+ if ( VM_ASSIST(v->domain, runstate_update_flag) )
+ {
+ runstate->state_entry_time |= XEN_RUNSTATE_UPDATE;
+ smp_wmb();
+ v->runstate.state_entry_time |= XEN_RUNSTATE_UPDATE;
+ }
+
+ memcpy(runstate, &v->runstate, sizeof(v->runstate));
+
+ if ( VM_ASSIST(v->domain, runstate_update_flag) )
+ {
+ runstate->state_entry_time &= ~XEN_RUNSTATE_UPDATE;
+ smp_wmb();
+ v->runstate.state_entry_time &= ~XEN_RUNSTATE_UPDATE;
+ }
+
+ unmap_runstate_area(runstate);
+}
+
+/* Update per-VCPU guest runstate shared memory area (if registered). */
+static void update_runstate_area(struct vcpu *v)
+{
+ if ( xchg(&v->runstate_in_use, 1) )
+ return;
+
+ switch ( v->runstate_guest_type )
+ {
+ case RUNSTATE_NONE:
+ break;
+
+ case RUNSTATE_VADDR:
+ update_runstate_by_gvaddr(v);
+ break;
+
+ case RUNSTATE_PADDR:
+ update_runstate_by_gpaddr(v);
+ break;
+ }
+
+ xchg(&v->runstate_in_use, 0);
+}
+
static void schedule_tail(struct vcpu *prev)
{
ctxt_switch_from(prev);
@@ -998,6 +1048,7 @@ long do_arm_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) a
{
case VCPUOP_register_vcpu_info:
case VCPUOP_register_runstate_memory_area:
+ case VCPUOP_register_runstate_phys_memory_area:
return do_vcpu_op(cmd, vcpuid, arg);
default:
return -EINVAL;
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 32bca8d..f167a68 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -700,6 +700,68 @@ int rcu_lock_live_remote_domain_by_id(domid_t dom, struct domain **d)
return 0;
}
+void unmap_runstate_area(struct vcpu_runstate_info *area)
+{
+ mfn_t mfn;
+
+ ASSERT(area != NULL);
+
+ mfn = _mfn(domain_page_map_to_mfn(area));
+
+ unmap_domain_page_global((void *)
+ ((unsigned long)area &
+ PAGE_MASK));
+
+ put_page_and_type(mfn_to_page(mfn));
+}
+
+int map_runstate_area(struct vcpu *v, struct vcpu_runstate_info **area)
+{
+ unsigned long offset = v->runstate_guest.phys & ~PAGE_MASK;
+ gfn_t gfn = gaddr_to_gfn(v->runstate_guest.phys);
+ struct domain *d = v->domain;
+ void *mapping;
+ struct page_info *page;
+ size_t size = sizeof(struct vcpu_runstate_info);
+
+ if ( offset > (PAGE_SIZE - size) )
+ return -EINVAL;
+
+ page = get_page_from_gfn(d, gfn_x(gfn), NULL, P2M_ALLOC);
+ if ( !page )
+ return -EINVAL;
+
+ if ( !get_page_type(page, PGT_writable_page) )
+ {
+ put_page(page);
+ return -EINVAL;
+ }
+
+ mapping = __map_domain_page_global(page);
+
+ if ( mapping == NULL )
+ {
+ put_page_and_type(page);
+ return -ENOMEM;
+ }
+
+ *area = mapping + offset;
+
+ return 0;
+}
+
+static void discard_runstate_area(struct vcpu *v)
+{
+ v->runstate_guest_type = RUNSTATE_NONE;
+}
+
+static void discard_runstate_area_locked(struct vcpu *v)
+{
+ while ( xchg(&v->runstate_in_use, 1) );
+ discard_runstate_area(v);
+ xchg(&v->runstate_in_use, 0);
+}
+
int domain_kill(struct domain *d)
{
int rc = 0;
@@ -738,7 +800,10 @@ int domain_kill(struct domain *d)
if ( cpupool_move_domain(d, cpupool0) )
return -ERESTART;
for_each_vcpu ( d, v )
+ {
+ discard_runstate_area_locked(v);
unmap_vcpu_info(v);
+ }
d->is_dying = DOMDYING_dead;
/* Mem event cleanup has to go here because the rings
* have to be put before we call put_domain. */
@@ -1192,7 +1257,7 @@ int domain_soft_reset(struct domain *d)
for_each_vcpu ( d, v )
{
- set_xen_guest_handle(runstate_guest(v), NULL);
+ discard_runstate_area_locked(v);
unmap_vcpu_info(v);
}
@@ -1520,18 +1585,46 @@ long do_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg)
break;
rc = 0;
- runstate_guest(v) = area.addr.h;
+
+ while( xchg(&v->runstate_in_use, 1) == 0);
+
+ discard_runstate_area(v);
+
+ runstate_guest_virt(v) = area.addr.h;
+ v->runstate_guest_type = RUNSTATE_VADDR;
if ( v == current )
{
- __copy_to_guest(runstate_guest(v), &v->runstate, 1);
+ __copy_to_guest(runstate_guest_virt(v), &v->runstate, 1);
}
else
{
vcpu_runstate_get(v, &runstate);
- __copy_to_guest(runstate_guest(v), &runstate, 1);
+ __copy_to_guest(runstate_guest_virt(v), &runstate, 1);
}
+ xchg(&v->runstate_in_use, 0);
+
+ break;
+ }
+
+ case VCPUOP_register_runstate_phys_memory_area:
+ {
+ struct vcpu_register_runstate_memory_area area;
+
+ rc = -EFAULT;
+ if ( copy_from_guest(&area, arg, 1) )
+ break;
+
+ while( xchg(&v->runstate_in_use, 1) == 0);
+
+ discard_runstate_area(v);
+ v->runstate_guest.phys = area.addr.p;
+ v->runstate_guest_type = RUNSTATE_PADDR;
+
+ xchg(&v->runstate_in_use, 0);
+ rc = 0;
+
break;
}
diff --git a/xen/include/public/vcpu.h b/xen/include/public/vcpu.h
index 3623af9..d7da4a3 100644
--- a/xen/include/public/vcpu.h
+++ b/xen/include/public/vcpu.h
@@ -235,6 +235,21 @@ struct vcpu_register_time_memory_area {
typedef struct vcpu_register_time_memory_area vcpu_register_time_memory_area_t;
DEFINE_XEN_GUEST_HANDLE(vcpu_register_time_memory_area_t);
+/*
+ * Register a shared memory area from which the guest may obtain its own
+ * runstate information without needing to execute a hypercall.
+ * Notes:
+ * 1. The registered address must be guest's physical address.
+ * 2. The registered runstate area should not cross page boundary.
+ * 3. Only one shared area may be registered per VCPU. The shared area is
+ * updated by the hypervisor each time the VCPU is scheduled. Thus
+ * runstate.state will always be RUNSTATE_running and
+ * runstate.state_entry_time will indicate the system time at which the
+ * VCPU was last scheduled to run.
+ * @extra_arg == pointer to vcpu_register_runstate_memory_area structure.
+ */
+#define VCPUOP_register_runstate_phys_memory_area 14
+
#endif /* __XEN_PUBLIC_VCPU_H__ */
/*
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index edee52d..8ac597b 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -163,17 +163,31 @@ struct vcpu
void *sched_priv; /* scheduler-specific data */
struct vcpu_runstate_info runstate;
+
+ enum {
+ RUNSTATE_NONE = 0,
+ RUNSTATE_PADDR = 1,
+ RUNSTATE_VADDR = 2,
+ } runstate_guest_type;
+
+ unsigned long runstate_in_use;
+
+ union
+ {
#ifndef CONFIG_COMPAT
-# define runstate_guest(v) ((v)->runstate_guest)
- XEN_GUEST_HANDLE(vcpu_runstate_info_t) runstate_guest; /* guest address */
+# define runstate_guest_virt(v) ((v)->runstate_guest.virt)
+ XEN_GUEST_HANDLE(vcpu_runstate_info_t) virt; /* guest address */
#else
-# define runstate_guest(v) ((v)->runstate_guest.native)
- union {
- XEN_GUEST_HANDLE(vcpu_runstate_info_t) native;
- XEN_GUEST_HANDLE(vcpu_runstate_info_compat_t) compat;
- } runstate_guest; /* guest address */
+# define runstate_guest_virt(v) ((v)->runstate_guest.virt.native)
+ union {
+ XEN_GUEST_HANDLE(vcpu_runstate_info_t) native;
+ XEN_GUEST_HANDLE(vcpu_runstate_info_compat_t) compat;
+ } virt; /* guest address */
#endif
+ paddr_t phys;
+ } runstate_guest;
+
/* last time when vCPU is scheduled out */
uint64_t last_run_time;
--
2.7.4
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
© 2016 - 2024 Red Hat, Inc.