forked from Minki/linux
0e8a631328
CONFIG_VIRT_CPU_ACCOUNTING_GEN under pseries does not provide stolen time accounting unless CONFIG_PARAVIRT_TIME_ACCOUNTING is enabled. Implement this using the VPA accumulated wait counters. Note this will not work on current KVM hosts because KVM does not implement the VPA dispatch counters (yet). It could be implemented with the dispatch trace log as it is for VIRT_CPU_ACCOUNTING_NATIVE, but that is not necessary for the more limited accounting provided by PARAVIRT_TIME_ACCOUNTING, and it is more expensive, complex, and has downsides like potential log wrap. From Shrikanth: [...] it was tested on Power10 [PowerVM] Shared LPAR. system has two LPAR. we will call first one LPAR1 and second one as LPAR2. Test was carried out in SMT=1. Similar observation was seen in SMT=8 as well. LPAR config header from each LPAR is below. LPAR1 is twice as big as LPAR2. Since Both are sharing the same underlying hardware, work stealing will happen when both the LPAR's are contending for the same resource. LPAR1: type=Shared mode=Uncapped smt=Off lcpu=40 cpus=40 ent=20.00 LPAR2: type=Shared mode=Uncapped smt=Off lcpu=20 cpus=40 ent=10.00 mpstat was used to check for the utilization. stress-ng has been used as the workload. Few cases are tested. when the both LPAR are idle there is no steal time. when LPAR1 starts running at 100% which consumes all of the physical resource, steal time starts to get accounted. With LPAR1 running at 100% and LPAR2 starts running, steal time starts increasing. This is as expected. When the LPAR2 Load is increased further, steal time increases further. Case 1: 0% LPAR1; 0% LPAR2 %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 0.00 0.00 0.05 0.00 0.00 0.00 0.00 0.00 0.00 99.95 Case 2: 100% LPAR1; 0% LPAR2 %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 97.68 0.00 0.00 0.00 0.00 0.00 2.32 0.00 0.00 0.00 Case 3: 100% LPAR1; 50% LPAR2 %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 86.34 0.00 0.10 0.00 0.00 0.03 13.54 0.00 0.00 0.00 Case 4: 100% LPAR1; 100% LPAR2 %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 78.54 0.00 0.07 0.00 0.00 0.02 21.36 0.00 0.00 0.00 Case 5: 50% LPAR1; 100% LPAR2 %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 49.37 0.00 0.00 0.00 0.00 0.00 1.17 0.00 0.00 49.47 Patch is accounting for the steal time and basic tests are holding good. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com> [mpe: Add SPDX tag to new paravirt_api_clock.h] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20220902085316.2071519-3-npiggin@gmail.com
165 lines
4.6 KiB
C
165 lines
4.6 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
#ifndef _ASM_POWERPC_PARAVIRT_H
|
|
#define _ASM_POWERPC_PARAVIRT_H
|
|
|
|
#include <linux/jump_label.h>
|
|
#include <asm/smp.h>
|
|
#ifdef CONFIG_PPC64
|
|
#include <asm/paca.h>
|
|
#include <asm/hvcall.h>
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_SPLPAR
|
|
#include <linux/smp.h>
|
|
#include <asm/kvm_guest.h>
|
|
#include <asm/cputhreads.h>
|
|
|
|
DECLARE_STATIC_KEY_FALSE(shared_processor);
|
|
|
|
static inline bool is_shared_processor(void)
|
|
{
|
|
return static_branch_unlikely(&shared_processor);
|
|
}
|
|
|
|
#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
|
|
extern struct static_key paravirt_steal_enabled;
|
|
extern struct static_key paravirt_steal_rq_enabled;
|
|
|
|
u64 pseries_paravirt_steal_clock(int cpu);
|
|
|
|
static inline u64 paravirt_steal_clock(int cpu)
|
|
{
|
|
return pseries_paravirt_steal_clock(cpu);
|
|
}
|
|
#endif
|
|
|
|
/* If bit 0 is set, the cpu has been ceded, conferred, or preempted */
|
|
static inline u32 yield_count_of(int cpu)
|
|
{
|
|
__be32 yield_count = READ_ONCE(lppaca_of(cpu).yield_count);
|
|
return be32_to_cpu(yield_count);
|
|
}
|
|
|
|
/*
|
|
* Spinlock code confers and prods, so don't trace the hcalls because the
|
|
* tracing code takes spinlocks which can cause recursion deadlocks.
|
|
*
|
|
* These calls are made while the lock is not held: the lock slowpath yields if
|
|
* it can not acquire the lock, and unlock slow path might prod if a waiter has
|
|
* yielded). So this may not be a problem for simple spin locks because the
|
|
* tracing does not technically recurse on the lock, but we avoid it anyway.
|
|
*
|
|
* However the queued spin lock contended path is more strictly ordered: the
|
|
* H_CONFER hcall is made after the task has queued itself on the lock, so then
|
|
* recursing on that lock will cause the task to then queue up again behind the
|
|
* first instance (or worse: queued spinlocks use tricks that assume a context
|
|
* never waits on more than one spinlock, so such recursion may cause random
|
|
* corruption in the lock code).
|
|
*/
|
|
static inline void yield_to_preempted(int cpu, u32 yield_count)
|
|
{
|
|
plpar_hcall_norets_notrace(H_CONFER, get_hard_smp_processor_id(cpu), yield_count);
|
|
}
|
|
|
|
static inline void prod_cpu(int cpu)
|
|
{
|
|
plpar_hcall_norets_notrace(H_PROD, get_hard_smp_processor_id(cpu));
|
|
}
|
|
|
|
static inline void yield_to_any(void)
|
|
{
|
|
plpar_hcall_norets_notrace(H_CONFER, -1, 0);
|
|
}
|
|
#else
|
|
static inline bool is_shared_processor(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline u32 yield_count_of(int cpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
extern void ___bad_yield_to_preempted(void);
|
|
static inline void yield_to_preempted(int cpu, u32 yield_count)
|
|
{
|
|
___bad_yield_to_preempted(); /* This would be a bug */
|
|
}
|
|
|
|
extern void ___bad_yield_to_any(void);
|
|
static inline void yield_to_any(void)
|
|
{
|
|
___bad_yield_to_any(); /* This would be a bug */
|
|
}
|
|
|
|
extern void ___bad_prod_cpu(void);
|
|
static inline void prod_cpu(int cpu)
|
|
{
|
|
___bad_prod_cpu(); /* This would be a bug */
|
|
}
|
|
|
|
#endif
|
|
|
|
#define vcpu_is_preempted vcpu_is_preempted
|
|
static inline bool vcpu_is_preempted(int cpu)
|
|
{
|
|
/*
|
|
* The dispatch/yield bit alone is an imperfect indicator of
|
|
* whether the hypervisor has dispatched @cpu to run on a physical
|
|
* processor. When it is clear, @cpu is definitely not preempted.
|
|
* But when it is set, it means only that it *might* be, subject to
|
|
* other conditions. So we check other properties of the VM and
|
|
* @cpu first, resorting to the yield count last.
|
|
*/
|
|
|
|
/*
|
|
* Hypervisor preemption isn't possible in dedicated processor
|
|
* mode by definition.
|
|
*/
|
|
if (!is_shared_processor())
|
|
return false;
|
|
|
|
#ifdef CONFIG_PPC_SPLPAR
|
|
if (!is_kvm_guest()) {
|
|
int first_cpu;
|
|
|
|
/*
|
|
* The result of vcpu_is_preempted() is used in a
|
|
* speculative way, and is always subject to invalidation
|
|
* by events internal and external to Linux. While we can
|
|
* be called in preemptable context (in the Linux sense),
|
|
* we're not accessing per-cpu resources in a way that can
|
|
* race destructively with Linux scheduler preemption and
|
|
* migration, and callers can tolerate the potential for
|
|
* error introduced by sampling the CPU index without
|
|
* pinning the task to it. So it is permissible to use
|
|
* raw_smp_processor_id() here to defeat the preempt debug
|
|
* warnings that can arise from using smp_processor_id()
|
|
* in arbitrary contexts.
|
|
*/
|
|
first_cpu = cpu_first_thread_sibling(raw_smp_processor_id());
|
|
|
|
/*
|
|
* The PowerVM hypervisor dispatches VMs on a whole core
|
|
* basis. So we know that a thread sibling of the local CPU
|
|
* cannot have been preempted by the hypervisor, even if it
|
|
* has called H_CONFER, which will set the yield bit.
|
|
*/
|
|
if (cpu_first_thread_sibling(cpu) == first_cpu)
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (yield_count_of(cpu) & 1)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static inline bool pv_is_native_spin_unlock(void)
|
|
{
|
|
return !is_shared_processor();
|
|
}
|
|
|
|
#endif /* _ASM_POWERPC_PARAVIRT_H */
|