xref: /linux/arch/s390/kernel/idle.c (revision a2703c2980c0776e9be7d8f9e144afd054dddb12)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Idle functions for s390.
4  *
5  * Copyright IBM Corp. 2014
6  *
7  * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/kernel_stat.h>
12 #include <linux/sched/stat.h>
13 #include <linux/notifier.h>
14 #include <linux/init.h>
15 #include <linux/cpu.h>
16 #include <linux/export.h>
17 #include <trace/events/power.h>
18 #include <asm/cpu_mf.h>
19 #include <asm/cputime.h>
20 #include <asm/idle.h>
21 #include <asm/nmi.h>
22 #include <asm/smp.h>
23 
24 DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
25 
26 static __always_inline void __account_idle_time_irq(void)
27 {
28 	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
29 	unsigned long idle_time;
30 
31 	idle_time = idle->clock_idle_exit.tod - idle->clock_idle_enter.tod;
32 	account_idle_time(cputime_to_nsecs(idle_time));
33 }
34 
35 static __always_inline void __account_idle_time_setup(void)
36 {
37 	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
38 
39 	store_tod_clock_ext(&idle->clock_idle_enter);
40 	idle->timer_idle_enter = get_cpu_timer();
41 	idle->clock_idle_exit = idle->clock_idle_enter;
42 }
43 
44 #ifdef CONFIG_NO_HZ_COMMON
45 
46 static u64 arch_cpu_in_idle_time(int cpu)
47 {
48 	struct s390_idle_data *idle = &per_cpu(s390_idle, cpu);
49 	union tod_clock now;
50 	u64 idle_time;
51 
52 	if (!idle->in_idle)
53 		return 0;
54 	store_tod_clock_ext(&now);
55 	if (tod_after(idle->clock_idle_exit.tod, idle->clock_idle_enter.tod))
56 		idle_time = idle->clock_idle_exit.tod - idle->clock_idle_enter.tod;
57 	else
58 		idle_time = now.tod - idle->clock_idle_enter.tod;
59 	return cputime_to_nsecs(idle_time);
60 }
61 
62 static u64 arch_cpu_idle_time(int cpu, enum cpu_usage_stat idx, bool compute_delta)
63 {
64 	struct kernel_cpustat *kc = &kcpustat_cpu(cpu);
65 	u64 *cpustat = kc->cpustat;
66 	unsigned int seq;
67 	u64 idle_time;
68 
69 	/*
70 	 * The open coded seqcount writer in entry.S relies on the
71 	 * raw counting mechanism without any writer protection.
72 	 */
73 	typecheck(typeof(kc->idle_sleeptime_seq), seqcount_t);
74 	do {
75 		seq = read_seqcount_begin(&kc->idle_sleeptime_seq);
76 		idle_time = cpustat[idx];
77 		if (compute_delta)
78 			idle_time += arch_cpu_in_idle_time(cpu);
79 	} while (read_seqcount_retry(&kc->idle_sleeptime_seq, seq));
80 	return idle_time;
81 }
82 
83 u64 arch_kcpustat_field_idle(int cpu)
84 {
85 	return arch_cpu_idle_time(cpu, CPUTIME_IDLE, !nr_iowait_cpu(cpu));
86 }
87 EXPORT_SYMBOL_GPL(arch_kcpustat_field_idle);
88 
89 u64 arch_kcpustat_field_iowait(int cpu)
90 {
91 	return arch_cpu_idle_time(cpu, CPUTIME_IOWAIT, nr_iowait_cpu(cpu));
92 }
93 EXPORT_SYMBOL_GPL(arch_kcpustat_field_iowait);
94 
95 void account_idle_time_irq(void)
96 {
97 	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
98 	struct kernel_cpustat *kc = kcpustat_this_cpu;
99 
100 	write_seqcount_begin(&kc->idle_sleeptime_seq);
101 	idle->in_idle = false;
102 	__account_idle_time_irq();
103 	write_seqcount_end(&kc->idle_sleeptime_seq);
104 }
105 
106 static __always_inline void account_idle_time_setup(void)
107 {
108 	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
109 	struct kernel_cpustat *kc = kcpustat_this_cpu;
110 
111 	raw_write_seqcount_begin(&kc->idle_sleeptime_seq);
112 	idle->in_idle = true;
113 	__account_idle_time_setup();
114 	raw_write_seqcount_end(&kc->idle_sleeptime_seq);
115 }
116 
117 #else  /* CONFIG_NO_HZ_COMMON */
118 
119 void account_idle_time_irq(void)
120 {
121 	__account_idle_time_irq();
122 }
123 
124 static __always_inline void account_idle_time_setup(void)
125 {
126 	__account_idle_time_setup();
127 }
128 
129 #endif /* CONFIG_NO_HZ_COMMON */
130 
131 void noinstr arch_cpu_idle(void)
132 {
133 	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
134 	unsigned long psw_mask;
135 
136 	/* Wait for external, I/O or machine check interrupt. */
137 	psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT |
138 		   PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
139 	clear_cpu_flag(CIF_NOHZ_DELAY);
140 	set_cpu_flag(CIF_ENABLED_WAIT);
141 	if (smp_cpu_mtid)
142 		stcctm(MT_DIAG, smp_cpu_mtid, (u64 *)&idle->mt_cycles_enter);
143 	account_idle_time_setup();
144 	bpon();
145 	__load_psw_mask(psw_mask);
146 }
147 
148 void arch_cpu_idle_enter(void)
149 {
150 }
151 
152 void arch_cpu_idle_exit(void)
153 {
154 }
155 
156 void __noreturn arch_cpu_idle_dead(void)
157 {
158 	cpu_die();
159 }
160