]>
Commit | Line | Data |
---|---|---|
b5f87f15 MS |
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> | |
32ef5517 | 15 | #include <linux/sched/cputime.h> |
b5f87f15 MS |
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 | ||
7a5388de | 22 | void enabled_wait(void) |
b5f87f15 | 23 | { |
0429fbc0 | 24 | struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); |
b5f87f15 MS |
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 | ||
200e7c0f HC |
38 | trace_hardirqs_off(); |
39 | ||
b5f87f15 | 40 | /* Account time spent with enabled wait psw loaded as idle time. */ |
1ce21804 | 41 | write_seqcount_begin(&idle->seqcount); |
b5f87f15 MS |
42 | idle_time = idle->clock_idle_exit - idle->clock_idle_enter; |
43 | idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; | |
44 | idle->idle_time += idle_time; | |
45 | idle->idle_count++; | |
18b43a9b | 46 | account_idle_time(cputime_to_nsecs(idle_time)); |
1ce21804 | 47 | write_seqcount_end(&idle->seqcount); |
b5f87f15 | 48 | } |
7a5388de | 49 | NOKPROBE_SYMBOL(enabled_wait); |
b5f87f15 MS |
50 | |
51 | static ssize_t show_idle_count(struct device *dev, | |
52 | struct device_attribute *attr, char *buf) | |
53 | { | |
54 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); | |
55 | unsigned long long idle_count; | |
1ce21804 | 56 | unsigned int seq; |
b5f87f15 MS |
57 | |
58 | do { | |
1ce21804 | 59 | seq = read_seqcount_begin(&idle->seqcount); |
187b5f41 CB |
60 | idle_count = READ_ONCE(idle->idle_count); |
61 | if (READ_ONCE(idle->clock_idle_enter)) | |
b5f87f15 | 62 | idle_count++; |
1ce21804 | 63 | } while (read_seqcount_retry(&idle->seqcount, seq)); |
b5f87f15 MS |
64 | return sprintf(buf, "%llu\n", idle_count); |
65 | } | |
66 | DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); | |
67 | ||
68 | static ssize_t show_idle_time(struct device *dev, | |
69 | struct device_attribute *attr, char *buf) | |
70 | { | |
71 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); | |
72 | unsigned long long now, idle_time, idle_enter, idle_exit; | |
1ce21804 | 73 | unsigned int seq; |
b5f87f15 MS |
74 | |
75 | do { | |
76 | now = get_tod_clock(); | |
1ce21804 | 77 | seq = read_seqcount_begin(&idle->seqcount); |
187b5f41 CB |
78 | idle_time = READ_ONCE(idle->idle_time); |
79 | idle_enter = READ_ONCE(idle->clock_idle_enter); | |
80 | idle_exit = READ_ONCE(idle->clock_idle_exit); | |
1ce21804 | 81 | } while (read_seqcount_retry(&idle->seqcount, seq)); |
b5f87f15 MS |
82 | idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; |
83 | return sprintf(buf, "%llu\n", idle_time >> 12); | |
84 | } | |
85 | DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); | |
86 | ||
42b425b3 | 87 | u64 arch_cpu_idle_time(int cpu) |
b5f87f15 MS |
88 | { |
89 | struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); | |
90 | unsigned long long now, idle_enter, idle_exit; | |
1ce21804 | 91 | unsigned int seq; |
b5f87f15 MS |
92 | |
93 | do { | |
94 | now = get_tod_clock(); | |
1ce21804 | 95 | seq = read_seqcount_begin(&idle->seqcount); |
187b5f41 CB |
96 | idle_enter = READ_ONCE(idle->clock_idle_enter); |
97 | idle_exit = READ_ONCE(idle->clock_idle_exit); | |
1ce21804 | 98 | } while (read_seqcount_retry(&idle->seqcount, seq)); |
42b425b3 FW |
99 | |
100 | return cputime_to_nsecs(idle_enter ? ((idle_exit ?: now) - idle_enter) : 0); | |
b5f87f15 MS |
101 | } |
102 | ||
103 | void arch_cpu_idle_enter(void) | |
104 | { | |
105 | local_mcck_disable(); | |
106 | } | |
107 | ||
108 | void arch_cpu_idle(void) | |
109 | { | |
110 | if (!test_cpu_flag(CIF_MCCK_PENDING)) | |
111 | /* Halt the cpu and keep track of cpu time accounting. */ | |
112 | enabled_wait(); | |
113 | local_irq_enable(); | |
114 | } | |
115 | ||
116 | void arch_cpu_idle_exit(void) | |
117 | { | |
118 | local_mcck_enable(); | |
119 | if (test_cpu_flag(CIF_MCCK_PENDING)) | |
120 | s390_handle_mcck(); | |
121 | } | |
122 | ||
123 | void arch_cpu_idle_dead(void) | |
124 | { | |
125 | cpu_die(); | |
126 | } |