# Commit d68069e6cbf58dc25cfcf85ec5fe96fd1fe0e560
# Date 2010-04-14 11:29:05 +0100
# Author Keir Fraser <keir.fraser@citrix.com>
# Committer Keir Fraser <keir.fraser@citrix.com>
Architecture-independent, and tasklet-based, continue_hypercall_on_cpu().

Signed-off-by: Juergen Gross <juergen.gross@ts.fujitsu.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -1651,82 +1651,6 @@ void sync_vcpu_execstate(struct vcpu *v)
     flush_tlb_mask(&v->vcpu_dirty_cpumask);
 }
 
-struct migrate_info {
-    long (*func)(void *data);
-    void *data;
-    void (*saved_schedule_tail)(struct vcpu *);
-    cpumask_t saved_affinity;
-    unsigned int nest;
-};
-
-static void continue_hypercall_on_cpu_helper(struct vcpu *v)
-{
-    struct cpu_user_regs *regs = guest_cpu_user_regs();
-    struct migrate_info *info = v->arch.continue_info;
-    cpumask_t mask = info->saved_affinity;
-    void (*saved_schedule_tail)(struct vcpu *) = info->saved_schedule_tail;
-
-    regs->eax = info->func(info->data);
-
-    if ( info->nest-- == 0 )
-    {
-        xfree(info);
-        v->arch.schedule_tail = saved_schedule_tail;
-        v->arch.continue_info = NULL;
-        vcpu_unlock_affinity(v, &mask);
-    }
-
-    (*saved_schedule_tail)(v);
-}
-
-int continue_hypercall_on_cpu(int cpu, long (*func)(void *data), void *data)
-{
-    struct vcpu *v = current;
-    struct migrate_info *info;
-    cpumask_t mask = cpumask_of_cpu(cpu);
-    int rc;
-
-    if ( cpu == smp_processor_id() )
-        return func(data);
-
-    info = v->arch.continue_info;
-    if ( info == NULL )
-    {
-        info = xmalloc(struct migrate_info);
-        if ( info == NULL )
-            return -ENOMEM;
-
-        rc = vcpu_lock_affinity(v, &mask);
-        if ( rc )
-        {
-            xfree(info);
-            return rc;
-        }
-
-        info->saved_schedule_tail = v->arch.schedule_tail;
-        info->saved_affinity = mask;
-        info->nest = 0;
-
-        v->arch.schedule_tail = continue_hypercall_on_cpu_helper;
-        v->arch.continue_info = info;
-    }
-    else
-    {
-        BUG_ON(info->nest != 0);
-        rc = vcpu_locked_change_affinity(v, &mask);
-        if ( rc )
-            return rc;
-        info->nest++;
-    }
-
-    info->func = func;
-    info->data = data;
-
-    /* Dummy return value will be overwritten by new schedule_tail. */
-    BUG_ON(!test_bit(SCHEDULE_SOFTIRQ, &softirq_pending(smp_processor_id())));
-    return 0;
-}
-
 #define next_arg(fmt, args) ({                                              \
     unsigned long __arg;                                                    \
     switch ( *(fmt)++ )                                                     \
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -931,6 +931,73 @@ long vm_assist(struct domain *p, unsigne
     return -ENOSYS;
 }
 
+struct migrate_info {
+    long (*func)(void *data);
+    void *data;
+    struct vcpu *vcpu;
+    unsigned int nest;
+};
+
+static DEFINE_PER_CPU(struct migrate_info *, continue_info);
+
+static void continue_hypercall_tasklet_handler(unsigned long _info)
+{
+    struct migrate_info *info = (struct migrate_info *)_info;
+    struct vcpu *v = info->vcpu;
+
+    vcpu_sleep_sync(v);
+
+    this_cpu(continue_info) = info;
+    return_reg(v) = info->func(info->data);
+    this_cpu(continue_info) = NULL;
+
+    if ( info->nest-- == 0 )
+    {
+        xfree(info);
+        vcpu_unpause(v);
+    }
+}
+
+int continue_hypercall_on_cpu(int cpu, long (*func)(void *data), void *data)
+{
+    struct vcpu *curr = current;
+    struct migrate_info *info;
+
+    if ( cpu == smp_processor_id() )
+        return func(data);
+
+    info = this_cpu(continue_info);
+    if ( info == NULL )
+    {
+        info = xmalloc(struct migrate_info);
+        if ( info == NULL )
+            return -ENOMEM;
+
+        info->vcpu = curr;
+        info->nest = 0;
+
+        tasklet_init(
+            &curr->continue_hypercall_tasklet,
+            continue_hypercall_tasklet_handler,
+            (unsigned long)info);
+
+        vcpu_pause_nosync(curr);
+    }
+    else
+    {
+        BUG_ON(info->nest != 0);
+        info->nest++;
+    }
+
+    info->func = func;
+    info->data = data;
+
+    tasklet_schedule_on_cpu(&curr->continue_hypercall_tasklet, cpu);
+
+    /* Dummy return value will be overwritten by tasklet. */
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
--- a/xen/common/schedule.c
+++ b/xen/common/schedule.c
@@ -407,27 +407,19 @@ void cpu_disable_scheduler(void)
     }
 }
 
-static int __vcpu_set_affinity(
-    struct vcpu *v, cpumask_t *affinity,
-    bool_t old_lock_status, bool_t new_lock_status)
+int vcpu_set_affinity(struct vcpu *v, cpumask_t *affinity)
 {
     cpumask_t online_affinity, old_affinity;
 
+    if ( v->domain->is_pinned )
+        return -EINVAL;
+
     cpus_and(online_affinity, *affinity, cpu_online_map);
     if ( cpus_empty(online_affinity) )
         return -EINVAL;
 
     vcpu_schedule_lock_irq(v);
 
-    if ( v->affinity_locked != old_lock_status )
-    {
-        BUG_ON(!v->affinity_locked);
-        vcpu_schedule_unlock_irq(v);
-        return -EBUSY;
-    }
-
-    v->affinity_locked = new_lock_status;
-
     old_affinity = v->cpu_affinity;
     v->cpu_affinity = *affinity;
     *affinity = old_affinity;
@@ -445,36 +437,6 @@ static int __vcpu_set_affinity(
     return 0;
 }
 
-int vcpu_set_affinity(struct vcpu *v, cpumask_t *affinity)
-{
-    if ( v->domain->is_pinned )
-        return -EINVAL;
-    return __vcpu_set_affinity(v, affinity, 0, 0);
-}
-
-int vcpu_lock_affinity(struct vcpu *v, cpumask_t *affinity)
-{
-    return __vcpu_set_affinity(v, affinity, 0, 1);
-}
-
-int vcpu_locked_change_affinity(struct vcpu *v, cpumask_t *affinity)
-{
-    return __vcpu_set_affinity(v, affinity, 1, 1);
-}
-
-void vcpu_unlock_affinity(struct vcpu *v, cpumask_t *affinity)
-{
-    cpumask_t online_affinity;
-
-    /* Do not fail if no CPU in old affinity mask is online. */
-    cpus_and(online_affinity, *affinity, cpu_online_map);
-    if ( cpus_empty(online_affinity) )
-        *affinity = cpu_online_map;
-
-    if ( __vcpu_set_affinity(v, affinity, 1, 0) != 0 )
-        BUG();
-}
-
 /* Block the currently-executing domain until a pertinent event occurs. */
 static long do_block(void)
 {
--- a/xen/include/asm-ia64/linux-xen/asm/ptrace.h
+++ b/xen/include/asm-ia64/linux-xen/asm/ptrace.h
@@ -198,6 +198,8 @@ static inline struct cpu_user_regs *vcpu
 	return (struct cpu_user_regs *)((unsigned long)v + IA64_STK_OFFSET) - 1;
 }
 
+#define return_reg(v) (vcpu_regs(v)->r8)
+
 struct cpu_user_regs *guest_cpu_user_regs(void);
 
 extern void show_stack(struct task_struct *task, unsigned long *sp);
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -397,9 +397,6 @@ struct arch_vcpu
     void (*ctxt_switch_from) (struct vcpu *);
     void (*ctxt_switch_to) (struct vcpu *);
 
-    /* Record information required to continue execution after migration */
-    void *continue_info;
-
     /* Bounce information for propagating an exception to guest OS. */
     struct trap_bounce trap_bounce;
 
@@ -475,9 +472,6 @@ struct arch_vcpu
 #define hvm_vmx         hvm_vcpu.u.vmx
 #define hvm_svm         hvm_vcpu.u.svm
 
-/* Continue the current hypercall via func(data) on specified cpu. */
-int continue_hypercall_on_cpu(int cpu, long (*func)(void *data), void *data);
-
 void vcpu_show_execution_state(struct vcpu *);
 void vcpu_show_registers(const struct vcpu *);
 
--- a/xen/include/asm-x86/regs.h
+++ b/xen/include/asm-x86/regs.h
@@ -19,4 +19,6 @@
     (diff == 0);                                                              \
 })
 
+#define return_reg(v) ((v)->arch.guest_context.user_regs.eax)
+
 #endif /* __X86_REGS_H__ */
--- a/xen/include/xen/domain.h
+++ b/xen/include/xen/domain.h
@@ -3,6 +3,7 @@
 #define __XEN_DOMAIN_H__
 
 #include <public/xen.h>
+#include <asm/domain.h>
 
 typedef union {
     struct vcpu_guest_context *nat;
@@ -62,6 +63,9 @@ int arch_vcpu_reset(struct vcpu *);
 bool_t domctl_lock_acquire(void);
 void domctl_lock_release(void);
 
+/* Continue the current hypercall via func(data) on specified cpu. */
+int continue_hypercall_on_cpu(int cpu, long (*func)(void *data), void *data);
+
 extern unsigned int xen_processor_pmbits;
 
 #endif /* __XEN_DOMAIN_H__ */
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -15,7 +15,7 @@
 #include <xen/timer.h>
 #include <xen/grant_table.h>
 #include <xen/rangeset.h>
-#include <asm/domain.h>
+#include <xen/domain.h>
 #include <xen/xenoprof.h>
 #include <xen/rcupdate.h>
 #include <xen/irq.h>
@@ -132,8 +132,6 @@ struct vcpu 
     bool_t           defer_shutdown;
     /* VCPU is paused following shutdown request (d->is_shutting_down)? */
     bool_t           paused_for_shutdown;
-    /* VCPU affinity is temporarily locked from controller changes? */
-    bool_t           affinity_locked;
 
     /*
      * > 0: a single port is being polled;
@@ -157,6 +155,9 @@ struct vcpu 
     /* Bitmask of CPUs which are holding onto this VCPU's state. */
     cpumask_t        vcpu_dirty_cpumask;
 
+    /* Tasklet for continue_hypercall_on_cpu(). */
+    struct tasklet   continue_hypercall_tasklet;
+
     struct arch_vcpu arch;
 };
 
@@ -576,9 +577,6 @@ void cpu_init(void);
 void vcpu_force_reschedule(struct vcpu *v);
 void cpu_disable_scheduler(void);
 int vcpu_set_affinity(struct vcpu *v, cpumask_t *affinity);
-int vcpu_lock_affinity(struct vcpu *v, cpumask_t *affinity);
-int vcpu_locked_change_affinity(struct vcpu *v, cpumask_t *affinity);
-void vcpu_unlock_affinity(struct vcpu *v, cpumask_t *affinity);
 
 void vcpu_runstate_get(struct vcpu *v, struct vcpu_runstate_info *runstate);
 uint64_t get_cpu_idle_time(unsigned int cpu);
