Currently the tick subsystem stores the idle cputime accounting in private
fields, allowing cohabitation with architecture idle vtime accounting. The
former is fetched on online CPUs, the latter on offline CPUs.
For consolidation purposes, architecture vtime accounting will continue to
account the cputime but will make a break when the idle tick is
stopped. The dyntick cputime accounting will then be relayed by the tick
subsystem so that the idle cputime is still seen advancing coherently even
when the tick isn't there to flush the idle vtime.
Prepare for that and introduce three new APIs which will be used in
subsequent patches:
- vtime_dynticks_start() is deemed to be called when idle enters in
dyntick mode. The idle cputime that elapsed so far is accumulated
and accounted. Also idle time accounting is ignored.
- vtime_dynticks_stop() is deemed to be called when idle exits from
dyntick mode. The vtime entry clocks are fast-forward to current time
so that idle accounting restarts elapsing from now. Also idle time
accounting is resumed.
- vtime_reset() is deemed to be called from dynticks idle IRQ entry to
fast-forward the clock to current time so that the IRQ time is still
accounted by vtime while nohz cputime is paused.
Also accumulated vtime won't be flushed from dyntick-idle ticks to avoid
accounting twice the idle cputime, along with nohz accounting.
Co-developed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Shrikanth Hegde <sshegde@linux.ibm.com>
Link: https://patch.msgid.link/20260508131647.43868-7-frederic@kernel.org
88 lines
2.2 KiB
C
88 lines
2.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Idle functions for s390.
|
|
*
|
|
* Copyright IBM Corp. 2014
|
|
*
|
|
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/init.h>
|
|
#include <linux/cpu.h>
|
|
#include <trace/events/power.h>
|
|
#include <asm/cpu_mf.h>
|
|
#include <asm/cputime.h>
|
|
#include <asm/idle.h>
|
|
#include <asm/nmi.h>
|
|
#include <asm/smp.h>
|
|
|
|
DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
|
|
|
|
void account_idle_time_irq(void)
|
|
{
|
|
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
|
|
unsigned long idle_time;
|
|
|
|
idle_time = get_lowcore()->int_clock - idle->clock_idle_enter;
|
|
|
|
/* Account time spent with enabled wait psw loaded as idle time. */
|
|
__atomic64_add(idle_time, &idle->idle_time);
|
|
__atomic64_add_const(1, &idle->idle_count);
|
|
|
|
/* Dyntick idle time accounted by nohz/scheduler */
|
|
if (!idle->idle_dyntick)
|
|
account_idle_time(cputime_to_nsecs(idle_time));
|
|
}
|
|
|
|
void noinstr arch_cpu_idle(void)
|
|
{
|
|
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
|
|
unsigned long psw_mask;
|
|
|
|
/* Wait for external, I/O or machine check interrupt. */
|
|
psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT |
|
|
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
|
|
clear_cpu_flag(CIF_NOHZ_DELAY);
|
|
set_cpu_flag(CIF_ENABLED_WAIT);
|
|
if (smp_cpu_mtid)
|
|
stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter);
|
|
idle->clock_idle_enter = get_tod_clock_fast();
|
|
idle->timer_idle_enter = get_cpu_timer();
|
|
bpon();
|
|
__load_psw_mask(psw_mask);
|
|
}
|
|
|
|
static ssize_t show_idle_count(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
|
|
|
|
return sysfs_emit(buf, "%lu\n", READ_ONCE(idle->idle_count));
|
|
}
|
|
DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
|
|
|
|
static ssize_t show_idle_time(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
|
|
|
|
return sysfs_emit(buf, "%lu\n", READ_ONCE(idle->idle_time) >> 12);
|
|
}
|
|
DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
|
|
|
|
void arch_cpu_idle_enter(void)
|
|
{
|
|
}
|
|
|
|
void arch_cpu_idle_exit(void)
|
|
{
|
|
}
|
|
|
|
void __noreturn arch_cpu_idle_dead(void)
|
|
{
|
|
cpu_die();
|
|
}
|