KVM: s390: diag9c (directed yield) forwarding
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>
This commit is contained in:
parent
38860756a1
commit
87e28a15c4
7 changed files with 78 additions and 3 deletions
|
@ -84,3 +84,36 @@ If the function code specifies 0x501, breakpoint functions may be performed.
|
||||||
This function code is handled by userspace.
|
This function code is handled by userspace.
|
||||||
|
|
||||||
This diagnose function code has no subfunctions and uses no parameters.
|
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.
|
||||||
|
|
|
@ -454,6 +454,7 @@ struct kvm_vcpu_stat {
|
||||||
u64 diagnose_44;
|
u64 diagnose_44;
|
||||||
u64 diagnose_9c;
|
u64 diagnose_9c;
|
||||||
u64 diagnose_9c_ignored;
|
u64 diagnose_9c_ignored;
|
||||||
|
u64 diagnose_9c_forward;
|
||||||
u64 diagnose_258;
|
u64 diagnose_258;
|
||||||
u64 diagnose_308;
|
u64 diagnose_308;
|
||||||
u64 diagnose_500;
|
u64 diagnose_500;
|
||||||
|
|
|
@ -63,5 +63,6 @@ extern void __noreturn cpu_die(void);
|
||||||
extern void __cpu_die(unsigned int cpu);
|
extern void __cpu_die(unsigned int cpu);
|
||||||
extern int __cpu_disable(void);
|
extern int __cpu_disable(void);
|
||||||
extern void schedule_mcck_handler(void);
|
extern void schedule_mcck_handler(void);
|
||||||
|
void notrace smp_yield_cpu(int cpu);
|
||||||
|
|
||||||
#endif /* __ASM_SMP_H */
|
#endif /* __ASM_SMP_H */
|
||||||
|
|
|
@ -429,6 +429,7 @@ void notrace smp_yield_cpu(int cpu)
|
||||||
asm volatile("diag %0,0,0x9c"
|
asm volatile("diag %0,0,0x9c"
|
||||||
: : "d" (pcpu_devices[cpu].address));
|
: : "d" (pcpu_devices[cpu].address));
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(smp_yield_cpu);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send cpus emergency shutdown signal. This gives the cpus the
|
* Send cpus emergency shutdown signal. This gives the cpus the
|
||||||
|
|
|
@ -150,6 +150,19 @@ static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
|
||||||
return 0;
|
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)
|
static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct kvm_vcpu *tcpu;
|
struct kvm_vcpu *tcpu;
|
||||||
|
@ -167,10 +180,22 @@ static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
|
||||||
if (!tcpu)
|
if (!tcpu)
|
||||||
goto no_yield;
|
goto no_yield;
|
||||||
|
|
||||||
/* target already running */
|
/* target guest VCPU already running */
|
||||||
if (READ_ONCE(tcpu->cpu) >= 0)
|
if (READ_ONCE(tcpu->cpu) >= 0) {
|
||||||
|
if (!diag9c_forwarding_hz || diag9c_forwarding_overrun())
|
||||||
goto no_yield;
|
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)
|
if (kvm_vcpu_yield_to(tcpu) <= 0)
|
||||||
goto no_yield;
|
goto no_yield;
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
||||||
VCPU_STAT("instruction_diag_44", diagnose_44),
|
VCPU_STAT("instruction_diag_44", diagnose_44),
|
||||||
VCPU_STAT("instruction_diag_9c", diagnose_9c),
|
VCPU_STAT("instruction_diag_9c", diagnose_9c),
|
||||||
VCPU_STAT("diag_9c_ignored", diagnose_9c_ignored),
|
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_258", diagnose_258),
|
||||||
VCPU_STAT("instruction_diag_308", diagnose_308),
|
VCPU_STAT("instruction_diag_308", diagnose_308),
|
||||||
VCPU_STAT("instruction_diag_500", diagnose_500),
|
VCPU_STAT("instruction_diag_500", diagnose_500),
|
||||||
|
@ -185,6 +186,11 @@ static bool use_gisa = true;
|
||||||
module_param(use_gisa, bool, 0644);
|
module_param(use_gisa, bool, 0644);
|
||||||
MODULE_PARM_DESC(use_gisa, "Use the GISA if the host supports it.");
|
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
|
* 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
|
* kernel handles and stores in the prefix page. If we ever need to go beyond
|
||||||
|
|
|
@ -471,4 +471,12 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
|
||||||
* @kvm: the KVM guest
|
* @kvm: the KVM guest
|
||||||
*/
|
*/
|
||||||
void kvm_s390_vcpu_crypto_reset_all(struct kvm *kvm);
|
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
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue