xref: /openbmc/linux/arch/s390/kernel/idle.c (revision b5f87f15e20092c060f465b283b07a76af7f2e5f)
1*b5f87f15SMartin Schwidefsky /*
2*b5f87f15SMartin Schwidefsky  * Idle functions for s390.
3*b5f87f15SMartin Schwidefsky  *
4*b5f87f15SMartin Schwidefsky  * Copyright IBM Corp. 2014
5*b5f87f15SMartin Schwidefsky  *
6*b5f87f15SMartin Schwidefsky  * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
7*b5f87f15SMartin Schwidefsky  */
8*b5f87f15SMartin Schwidefsky 
9*b5f87f15SMartin Schwidefsky #include <linux/kernel.h>
10*b5f87f15SMartin Schwidefsky #include <linux/kernel_stat.h>
11*b5f87f15SMartin Schwidefsky #include <linux/kprobes.h>
12*b5f87f15SMartin Schwidefsky #include <linux/notifier.h>
13*b5f87f15SMartin Schwidefsky #include <linux/init.h>
14*b5f87f15SMartin Schwidefsky #include <linux/cpu.h>
15*b5f87f15SMartin Schwidefsky #include <asm/cputime.h>
16*b5f87f15SMartin Schwidefsky #include <asm/nmi.h>
17*b5f87f15SMartin Schwidefsky #include <asm/smp.h>
18*b5f87f15SMartin Schwidefsky #include "entry.h"
19*b5f87f15SMartin Schwidefsky 
20*b5f87f15SMartin Schwidefsky static DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
21*b5f87f15SMartin Schwidefsky 
22*b5f87f15SMartin Schwidefsky void __kprobes enabled_wait(void)
23*b5f87f15SMartin Schwidefsky {
24*b5f87f15SMartin Schwidefsky 	struct s390_idle_data *idle = &__get_cpu_var(s390_idle);
25*b5f87f15SMartin Schwidefsky 	unsigned long long idle_time;
26*b5f87f15SMartin Schwidefsky 	unsigned long psw_mask;
27*b5f87f15SMartin Schwidefsky 
28*b5f87f15SMartin Schwidefsky 	trace_hardirqs_on();
29*b5f87f15SMartin Schwidefsky 
30*b5f87f15SMartin Schwidefsky 	/* Wait for external, I/O or machine check interrupt. */
31*b5f87f15SMartin Schwidefsky 	psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT |
32*b5f87f15SMartin Schwidefsky 		PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
33*b5f87f15SMartin Schwidefsky 	clear_cpu_flag(CIF_NOHZ_DELAY);
34*b5f87f15SMartin Schwidefsky 
35*b5f87f15SMartin Schwidefsky 	/* Call the assembler magic in entry.S */
36*b5f87f15SMartin Schwidefsky 	psw_idle(idle, psw_mask);
37*b5f87f15SMartin Schwidefsky 
38*b5f87f15SMartin Schwidefsky 	/* Account time spent with enabled wait psw loaded as idle time. */
39*b5f87f15SMartin Schwidefsky 	idle->sequence++;
40*b5f87f15SMartin Schwidefsky 	smp_wmb();
41*b5f87f15SMartin Schwidefsky 	idle_time = idle->clock_idle_exit - idle->clock_idle_enter;
42*b5f87f15SMartin Schwidefsky 	idle->clock_idle_enter = idle->clock_idle_exit = 0ULL;
43*b5f87f15SMartin Schwidefsky 	idle->idle_time += idle_time;
44*b5f87f15SMartin Schwidefsky 	idle->idle_count++;
45*b5f87f15SMartin Schwidefsky 	account_idle_time(idle_time);
46*b5f87f15SMartin Schwidefsky 	smp_wmb();
47*b5f87f15SMartin Schwidefsky 	idle->sequence++;
48*b5f87f15SMartin Schwidefsky }
49*b5f87f15SMartin Schwidefsky 
50*b5f87f15SMartin Schwidefsky static ssize_t show_idle_count(struct device *dev,
51*b5f87f15SMartin Schwidefsky 				struct device_attribute *attr, char *buf)
52*b5f87f15SMartin Schwidefsky {
53*b5f87f15SMartin Schwidefsky 	struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
54*b5f87f15SMartin Schwidefsky 	unsigned long long idle_count;
55*b5f87f15SMartin Schwidefsky 	unsigned int sequence;
56*b5f87f15SMartin Schwidefsky 
57*b5f87f15SMartin Schwidefsky 	do {
58*b5f87f15SMartin Schwidefsky 		sequence = ACCESS_ONCE(idle->sequence);
59*b5f87f15SMartin Schwidefsky 		idle_count = ACCESS_ONCE(idle->idle_count);
60*b5f87f15SMartin Schwidefsky 		if (ACCESS_ONCE(idle->clock_idle_enter))
61*b5f87f15SMartin Schwidefsky 			idle_count++;
62*b5f87f15SMartin Schwidefsky 	} while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence));
63*b5f87f15SMartin Schwidefsky 	return sprintf(buf, "%llu\n", idle_count);
64*b5f87f15SMartin Schwidefsky }
65*b5f87f15SMartin Schwidefsky DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
66*b5f87f15SMartin Schwidefsky 
67*b5f87f15SMartin Schwidefsky static ssize_t show_idle_time(struct device *dev,
68*b5f87f15SMartin Schwidefsky 				struct device_attribute *attr, char *buf)
69*b5f87f15SMartin Schwidefsky {
70*b5f87f15SMartin Schwidefsky 	struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
71*b5f87f15SMartin Schwidefsky 	unsigned long long now, idle_time, idle_enter, idle_exit;
72*b5f87f15SMartin Schwidefsky 	unsigned int sequence;
73*b5f87f15SMartin Schwidefsky 
74*b5f87f15SMartin Schwidefsky 	do {
75*b5f87f15SMartin Schwidefsky 		now = get_tod_clock();
76*b5f87f15SMartin Schwidefsky 		sequence = ACCESS_ONCE(idle->sequence);
77*b5f87f15SMartin Schwidefsky 		idle_time = ACCESS_ONCE(idle->idle_time);
78*b5f87f15SMartin Schwidefsky 		idle_enter = ACCESS_ONCE(idle->clock_idle_enter);
79*b5f87f15SMartin Schwidefsky 		idle_exit = ACCESS_ONCE(idle->clock_idle_exit);
80*b5f87f15SMartin Schwidefsky 	} while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence));
81*b5f87f15SMartin Schwidefsky 	idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0;
82*b5f87f15SMartin Schwidefsky 	return sprintf(buf, "%llu\n", idle_time >> 12);
83*b5f87f15SMartin Schwidefsky }
84*b5f87f15SMartin Schwidefsky DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
85*b5f87f15SMartin Schwidefsky 
86*b5f87f15SMartin Schwidefsky cputime64_t arch_cpu_idle_time(int cpu)
87*b5f87f15SMartin Schwidefsky {
88*b5f87f15SMartin Schwidefsky 	struct s390_idle_data *idle = &per_cpu(s390_idle, cpu);
89*b5f87f15SMartin Schwidefsky 	unsigned long long now, idle_enter, idle_exit;
90*b5f87f15SMartin Schwidefsky 	unsigned int sequence;
91*b5f87f15SMartin Schwidefsky 
92*b5f87f15SMartin Schwidefsky 	do {
93*b5f87f15SMartin Schwidefsky 		now = get_tod_clock();
94*b5f87f15SMartin Schwidefsky 		sequence = ACCESS_ONCE(idle->sequence);
95*b5f87f15SMartin Schwidefsky 		idle_enter = ACCESS_ONCE(idle->clock_idle_enter);
96*b5f87f15SMartin Schwidefsky 		idle_exit = ACCESS_ONCE(idle->clock_idle_exit);
97*b5f87f15SMartin Schwidefsky 	} while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence));
98*b5f87f15SMartin Schwidefsky 	return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0;
99*b5f87f15SMartin Schwidefsky }
100*b5f87f15SMartin Schwidefsky 
101*b5f87f15SMartin Schwidefsky void arch_cpu_idle_enter(void)
102*b5f87f15SMartin Schwidefsky {
103*b5f87f15SMartin Schwidefsky 	local_mcck_disable();
104*b5f87f15SMartin Schwidefsky }
105*b5f87f15SMartin Schwidefsky 
106*b5f87f15SMartin Schwidefsky void arch_cpu_idle(void)
107*b5f87f15SMartin Schwidefsky {
108*b5f87f15SMartin Schwidefsky 	if (!test_cpu_flag(CIF_MCCK_PENDING))
109*b5f87f15SMartin Schwidefsky 		/* Halt the cpu and keep track of cpu time accounting. */
110*b5f87f15SMartin Schwidefsky 		enabled_wait();
111*b5f87f15SMartin Schwidefsky 	local_irq_enable();
112*b5f87f15SMartin Schwidefsky }
113*b5f87f15SMartin Schwidefsky 
114*b5f87f15SMartin Schwidefsky void arch_cpu_idle_exit(void)
115*b5f87f15SMartin Schwidefsky {
116*b5f87f15SMartin Schwidefsky 	local_mcck_enable();
117*b5f87f15SMartin Schwidefsky 	if (test_cpu_flag(CIF_MCCK_PENDING))
118*b5f87f15SMartin Schwidefsky 		s390_handle_mcck();
119*b5f87f15SMartin Schwidefsky }
120*b5f87f15SMartin Schwidefsky 
121*b5f87f15SMartin Schwidefsky void arch_cpu_idle_dead(void)
122*b5f87f15SMartin Schwidefsky {
123*b5f87f15SMartin Schwidefsky 	cpu_die();
124*b5f87f15SMartin Schwidefsky }
125