mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 16:17:32 -04:00
The former s390 specific arch_cpu_idle_time() implementation was removed, since its implementation was racy and reported idle time could go backwards [1]. However this removal was not necessary, since independently of the s390 architecture specific races there exists the iowait counter update race, which can also lead to reported idle time going backwards [2]. With Frederic Weisbecker's recent cpu idle time accounting refactoring kernel_cpustat got a sequence counter. Use this to implement s390 specific variants of kcpustat_field_idle() and kcpustat_field_iowait(). This is logically a revert of [1] and moves cpu idle time accounting back into s390 architecture code, which is also more precise than the dyntick idle time accounting by nohz/scheduler. For comparing cross cpu time stamps it is necessary to use the stcke instead of the stckf instruction in irq entry path. Furthermore this open-codes a sequence lock in assembler and C code, which is required to update the irq entry time stamp to the per cpu idle_data structure in a race free manner. [1] commitbe76ea6144("s390/idle: remove arch_cpu_idle_time() and corresponding code") [2] commitead70b7523("timers/nohz: Add a comment about broken iowait counter update race") Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Acked-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
180 lines
5.1 KiB
C
180 lines
5.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_KERNEL_VTIME_H
|
|
#define _LINUX_KERNEL_VTIME_H
|
|
|
|
#include <linux/context_tracking_state.h>
|
|
#include <linux/sched.h>
|
|
|
|
/*
|
|
* Common vtime APIs
|
|
*/
|
|
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
|
|
extern void vtime_account_kernel(struct task_struct *tsk);
|
|
#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */
|
|
|
|
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
|
|
extern void vtime_user_enter(struct task_struct *tsk);
|
|
extern void vtime_user_exit(struct task_struct *tsk);
|
|
extern void vtime_guest_enter(struct task_struct *tsk);
|
|
extern void vtime_guest_exit(struct task_struct *tsk);
|
|
extern void vtime_init_idle(struct task_struct *tsk, int cpu);
|
|
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_GEN */
|
|
static inline void vtime_user_enter(struct task_struct *tsk) { }
|
|
static inline void vtime_user_exit(struct task_struct *tsk) { }
|
|
static inline void vtime_guest_enter(struct task_struct *tsk) { }
|
|
static inline void vtime_guest_exit(struct task_struct *tsk) { }
|
|
static inline void vtime_init_idle(struct task_struct *tsk, int cpu) { }
|
|
#endif
|
|
|
|
static inline bool vtime_generic_enabled_cpu(int cpu)
|
|
{
|
|
return context_tracking_enabled_cpu(cpu);
|
|
}
|
|
|
|
static inline bool vtime_generic_enabled_this_cpu(void)
|
|
{
|
|
return context_tracking_enabled_this_cpu();
|
|
}
|
|
|
|
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
|
|
extern void vtime_account_idle(struct task_struct *tsk);
|
|
extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset);
|
|
extern void vtime_account_softirq(struct task_struct *tsk);
|
|
extern void vtime_account_hardirq(struct task_struct *tsk);
|
|
extern void vtime_flush(struct task_struct *tsk);
|
|
#ifdef CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE
|
|
static inline void vtime_reset(void) { }
|
|
static inline void vtime_dyntick_start(void) { }
|
|
static inline void vtime_dyntick_stop(void) { }
|
|
#else
|
|
extern void vtime_reset(void);
|
|
extern void vtime_dyntick_start(void);
|
|
extern void vtime_dyntick_stop(void);
|
|
#endif
|
|
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
|
|
static inline void vtime_account_irq(struct task_struct *tsk, unsigned int offset) { }
|
|
static inline void vtime_account_softirq(struct task_struct *tsk) { }
|
|
static inline void vtime_account_hardirq(struct task_struct *tsk) { }
|
|
static inline void vtime_flush(struct task_struct *tsk) { }
|
|
static inline void vtime_reset(void) { }
|
|
static inline void vtime_dyntick_start(void) { }
|
|
static inline void vtime_dyntick_stop(void) { }
|
|
#endif
|
|
|
|
/*
|
|
* vtime_accounting_enabled_this_cpu() definitions/declarations
|
|
*/
|
|
#if defined(CONFIG_VIRT_CPU_ACCOUNTING_NATIVE)
|
|
|
|
static inline bool vtime_accounting_enabled_this_cpu(void) { return true; }
|
|
extern void vtime_task_switch(struct task_struct *prev);
|
|
|
|
static __always_inline void vtime_account_guest_enter(void)
|
|
{
|
|
vtime_account_kernel(current);
|
|
current->flags |= PF_VCPU;
|
|
}
|
|
|
|
static __always_inline void vtime_account_guest_exit(void)
|
|
{
|
|
vtime_account_kernel(current);
|
|
current->flags &= ~PF_VCPU;
|
|
}
|
|
|
|
#elif defined(CONFIG_VIRT_CPU_ACCOUNTING_GEN)
|
|
|
|
/*
|
|
* Checks if vtime is enabled on some CPU. Cputime readers want to be careful
|
|
* in that case and compute the tickless cputime.
|
|
* For now vtime state is tied to context tracking. We might want to decouple
|
|
* those later if necessary.
|
|
*/
|
|
static inline bool vtime_accounting_enabled(void)
|
|
{
|
|
return context_tracking_enabled();
|
|
}
|
|
|
|
static inline bool vtime_accounting_enabled_cpu(int cpu)
|
|
{
|
|
return vtime_generic_enabled_cpu(cpu);
|
|
}
|
|
|
|
static inline bool vtime_accounting_enabled_this_cpu(void)
|
|
{
|
|
return vtime_generic_enabled_this_cpu();
|
|
}
|
|
|
|
extern void vtime_task_switch_generic(struct task_struct *prev);
|
|
|
|
static inline void vtime_task_switch(struct task_struct *prev)
|
|
{
|
|
if (vtime_accounting_enabled_this_cpu())
|
|
vtime_task_switch_generic(prev);
|
|
}
|
|
|
|
static __always_inline void vtime_account_guest_enter(void)
|
|
{
|
|
if (vtime_accounting_enabled_this_cpu())
|
|
vtime_guest_enter(current);
|
|
else
|
|
current->flags |= PF_VCPU;
|
|
}
|
|
|
|
static __always_inline void vtime_account_guest_exit(void)
|
|
{
|
|
if (vtime_accounting_enabled_this_cpu())
|
|
vtime_guest_exit(current);
|
|
else
|
|
current->flags &= ~PF_VCPU;
|
|
}
|
|
|
|
#else /* !CONFIG_VIRT_CPU_ACCOUNTING */
|
|
|
|
static inline bool vtime_accounting_enabled_this_cpu(void) { return false; }
|
|
static inline void vtime_task_switch(struct task_struct *prev) { }
|
|
|
|
static __always_inline void vtime_account_guest_enter(void)
|
|
{
|
|
current->flags |= PF_VCPU;
|
|
}
|
|
|
|
static __always_inline void vtime_account_guest_exit(void)
|
|
{
|
|
current->flags &= ~PF_VCPU;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
|
|
extern void irqtime_account_irq(struct task_struct *tsk, unsigned int offset);
|
|
#else
|
|
static inline void irqtime_account_irq(struct task_struct *tsk, unsigned int offset) { }
|
|
#endif
|
|
|
|
static inline void account_softirq_enter(struct task_struct *tsk)
|
|
{
|
|
vtime_account_irq(tsk, SOFTIRQ_OFFSET);
|
|
irqtime_account_irq(tsk, SOFTIRQ_OFFSET);
|
|
}
|
|
|
|
static inline void account_softirq_exit(struct task_struct *tsk)
|
|
{
|
|
vtime_account_softirq(tsk);
|
|
irqtime_account_irq(tsk, 0);
|
|
}
|
|
|
|
static inline void account_hardirq_enter(struct task_struct *tsk)
|
|
{
|
|
vtime_account_irq(tsk, HARDIRQ_OFFSET);
|
|
irqtime_account_irq(tsk, HARDIRQ_OFFSET);
|
|
}
|
|
|
|
static inline void account_hardirq_exit(struct task_struct *tsk)
|
|
{
|
|
vtime_account_hardirq(tsk);
|
|
irqtime_account_irq(tsk, 0);
|
|
}
|
|
|
|
#endif /* _LINUX_KERNEL_VTIME_H */
|