KVM: s390: diag9c (directed yield) forwarding
authorPierre Morel <pmorel@linux.ibm.com>
Mon, 7 Sep 2020 13:26:07 +0000 (15:26 +0200)
committerChristian Borntraeger <borntraeger@de.ibm.com>
Tue, 9 Mar 2021 09:16:26 +0000 (10:16 +0100)
When we intercept a DIAG_9C from the guest we verify that the
target real CPU associated with the virtual CPU designated by
the guest is running and if not we forward the DIAG_9C to the
target real CPU.

To avoid a diag9c storm we allow a maximal rate of diag9c forwarding.

The rate is calculated as a count per second defined as a new
parameter of the s390 kvm module: diag9c_forwarding_hz .

The default value of 0 is to not forward diag9c.

Signed-off-by: Pierre Morel <pmorel@linux.ibm.com>
Link: https://lore.kernel.org/r/1613997661-22525-2-git-send-email-pmorel@linux.ibm.com
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Documentation/virt/kvm/s390-diag.rst
arch/s390/include/asm/kvm_host.h
arch/s390/include/asm/smp.h
arch/s390/kernel/smp.c
arch/s390/kvm/diag.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h

index eaac486..ca85f03 100644 (file)
@@ -84,3 +84,36 @@ If the function code specifies 0x501, breakpoint functions may be performed.
 This function code is handled by userspace.
 
 This diagnose function code has no subfunctions and uses no parameters.
+
+
+DIAGNOSE function code 'X'9C - Voluntary Time Slice Yield
+---------------------------------------------------------
+
+General register 1 contains the target CPU address.
+
+In a guest of a hypervisor like LPAR, KVM or z/VM using shared host CPUs,
+DIAGNOSE with function code 0x9c may improve system performance by
+yielding the host CPU on which the guest CPU is running to be assigned
+to another guest CPU, preferably the logical CPU containing the specified
+target CPU.
+
+
+DIAG 'X'9C forwarding
++++++++++++++++++++++
+
+The guest may send a DIAGNOSE 0x9c in order to yield to a certain
+other vcpu. An example is a Linux guest that tries to yield to the vcpu
+that is currently holding a spinlock, but not running.
+
+However, on the host the real cpu backing the vcpu may itself not be
+running.
+Forwarding the DIAGNOSE 0x9c initially sent by the guest to yield to
+the backing cpu will hopefully cause that cpu, and thus subsequently
+the guest's vcpu, to be scheduled.
+
+
+diag9c_forwarding_hz
+    KVM kernel parameter allowing to specify the maximum number of DIAGNOSE
+    0x9c forwarding per second in the purpose of avoiding a DIAGNOSE 0x9c
+    forwarding storm.
+    A value of 0 turns the forwarding off.
index 6bcfc56..0af3e03 100644 (file)
@@ -454,6 +454,7 @@ struct kvm_vcpu_stat {
        u64 diagnose_44;
        u64 diagnose_9c;
        u64 diagnose_9c_ignored;
+       u64 diagnose_9c_forward;
        u64 diagnose_258;
        u64 diagnose_308;
        u64 diagnose_500;
index 01e3600..e317fd4 100644 (file)
@@ -63,5 +63,6 @@ extern void __noreturn cpu_die(void);
 extern void __cpu_die(unsigned int cpu);
 extern int __cpu_disable(void);
 extern void schedule_mcck_handler(void);
+void notrace smp_yield_cpu(int cpu);
 
 #endif /* __ASM_SMP_H */
index 58c8afa..2fec2b8 100644 (file)
@@ -429,6 +429,7 @@ void notrace smp_yield_cpu(int cpu)
        asm volatile("diag %0,0,0x9c"
                     : : "d" (pcpu_devices[cpu].address));
 }
+EXPORT_SYMBOL_GPL(smp_yield_cpu);
 
 /*
  * Send cpus emergency shutdown signal. This gives the cpus the
index 5b8ec1c..02c146f 100644 (file)
@@ -150,6 +150,19 @@ static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int forward_cnt;
+static unsigned long cur_slice;
+
+static int diag9c_forwarding_overrun(void)
+{
+       /* Reset the count on a new slice */
+       if (time_after(jiffies, cur_slice)) {
+               cur_slice = jiffies;
+               forward_cnt = diag9c_forwarding_hz / HZ;
+       }
+       return forward_cnt-- <= 0 ? 1 : 0;
+}
+
 static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
 {
        struct kvm_vcpu *tcpu;
@@ -167,9 +180,21 @@ static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
        if (!tcpu)
                goto no_yield;
 
-       /* target already running */
-       if (READ_ONCE(tcpu->cpu) >= 0)
-               goto no_yield;
+       /* target guest VCPU already running */
+       if (READ_ONCE(tcpu->cpu) >= 0) {
+               if (!diag9c_forwarding_hz || diag9c_forwarding_overrun())
+                       goto no_yield;
+
+               /* target host CPU already running */
+               if (!vcpu_is_preempted(tcpu->cpu))
+                       goto no_yield;
+               smp_yield_cpu(tcpu->cpu);
+               VCPU_EVENT(vcpu, 5,
+                          "diag time slice end directed to %d: yield forwarded",
+                          tid);
+               vcpu->stat.diagnose_9c_forward++;
+               return 0;
+       }
 
        if (kvm_vcpu_yield_to(tcpu) <= 0)
                goto no_yield;
index 3331939..cfe720d 100644 (file)
@@ -158,6 +158,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        VCPU_STAT("instruction_diag_44", diagnose_44),
        VCPU_STAT("instruction_diag_9c", diagnose_9c),
        VCPU_STAT("diag_9c_ignored", diagnose_9c_ignored),
+       VCPU_STAT("diag_9c_forward", diagnose_9c_forward),
        VCPU_STAT("instruction_diag_258", diagnose_258),
        VCPU_STAT("instruction_diag_308", diagnose_308),
        VCPU_STAT("instruction_diag_500", diagnose_500),
@@ -185,6 +186,11 @@ static bool use_gisa  = true;
 module_param(use_gisa, bool, 0644);
 MODULE_PARM_DESC(use_gisa, "Use the GISA if the host supports it.");
 
+/* maximum diag9c forwarding per second */
+unsigned int diag9c_forwarding_hz;
+module_param(diag9c_forwarding_hz, uint, 0644);
+MODULE_PARM_DESC(diag9c_forwarding_hz, "Maximum diag9c forwarding per second, 0 to turn off");
+
 /*
  * For now we handle at most 16 double words as this is what the s390 base
  * kernel handles and stores in the prefix page. If we ever need to go beyond
index 79dcd64..9fad251 100644 (file)
@@ -471,4 +471,12 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
  * @kvm: the KVM guest
  */
 void kvm_s390_vcpu_crypto_reset_all(struct kvm *kvm);
+
+/**
+ * diag9c_forwarding_hz
+ *
+ * Set the maximum number of diag9c forwarding per second
+ */
+extern unsigned int diag9c_forwarding_hz;
+
 #endif