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