1 // SPDX-License-Identifier: GPL-2.0
3 * Idle functions for s390.
5 * Copyright IBM Corp. 2014
7 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
10 #include <linux/kernel.h>
11 #include <linux/kernel_stat.h>
12 #include <linux/kprobes.h>
13 #include <linux/notifier.h>
14 #include <linux/init.h>
15 #include <linux/cpu.h>
16 #include <linux/sched/cputime.h>
21 static DEFINE_PER_CPU(struct s390_idle_data
, s390_idle
);
23 void enabled_wait(void)
25 struct s390_idle_data
*idle
= this_cpu_ptr(&s390_idle
);
26 unsigned long long idle_time
;
27 unsigned long psw_mask
;
31 /* Wait for external, I/O or machine check interrupt. */
32 psw_mask
= PSW_KERNEL_BITS
| PSW_MASK_WAIT
| PSW_MASK_DAT
|
33 PSW_MASK_IO
| PSW_MASK_EXT
| PSW_MASK_MCHECK
;
34 clear_cpu_flag(CIF_NOHZ_DELAY
);
36 /* Call the assembler magic in entry.S */
37 psw_idle(idle
, psw_mask
);
41 /* Account time spent with enabled wait psw loaded as idle time. */
42 write_seqcount_begin(&idle
->seqcount
);
43 idle_time
= idle
->clock_idle_exit
- idle
->clock_idle_enter
;
44 idle
->clock_idle_enter
= idle
->clock_idle_exit
= 0ULL;
45 idle
->idle_time
+= idle_time
;
47 account_idle_time(cputime_to_nsecs(idle_time
));
48 write_seqcount_end(&idle
->seqcount
);
50 NOKPROBE_SYMBOL(enabled_wait
);
52 static ssize_t
show_idle_count(struct device
*dev
,
53 struct device_attribute
*attr
, char *buf
)
55 struct s390_idle_data
*idle
= &per_cpu(s390_idle
, dev
->id
);
56 unsigned long long idle_count
;
60 seq
= read_seqcount_begin(&idle
->seqcount
);
61 idle_count
= READ_ONCE(idle
->idle_count
);
62 if (READ_ONCE(idle
->clock_idle_enter
))
64 } while (read_seqcount_retry(&idle
->seqcount
, seq
));
65 return sprintf(buf
, "%llu\n", idle_count
);
67 DEVICE_ATTR(idle_count
, 0444, show_idle_count
, NULL
);
69 static ssize_t
show_idle_time(struct device
*dev
,
70 struct device_attribute
*attr
, char *buf
)
72 unsigned long long now
, idle_time
, idle_enter
, idle_exit
, in_idle
;
73 struct s390_idle_data
*idle
= &per_cpu(s390_idle
, dev
->id
);
77 seq
= read_seqcount_begin(&idle
->seqcount
);
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
);
81 } while (read_seqcount_retry(&idle
->seqcount
, seq
));
83 now
= get_tod_clock();
86 in_idle
= idle_exit
- idle_enter
;
87 } else if (now
> idle_enter
) {
88 in_idle
= now
- idle_enter
;
92 return sprintf(buf
, "%llu\n", idle_time
>> 12);
94 DEVICE_ATTR(idle_time_us
, 0444, show_idle_time
, NULL
);
96 u64
arch_cpu_idle_time(int cpu
)
98 struct s390_idle_data
*idle
= &per_cpu(s390_idle
, cpu
);
99 unsigned long long now
, idle_enter
, idle_exit
, in_idle
;
103 seq
= read_seqcount_begin(&idle
->seqcount
);
104 idle_enter
= READ_ONCE(idle
->clock_idle_enter
);
105 idle_exit
= READ_ONCE(idle
->clock_idle_exit
);
106 } while (read_seqcount_retry(&idle
->seqcount
, seq
));
108 now
= get_tod_clock();
111 in_idle
= idle_exit
- idle_enter
;
112 } else if (now
> idle_enter
) {
113 in_idle
= now
- idle_enter
;
116 return cputime_to_nsecs(in_idle
);
119 void arch_cpu_idle_enter(void)
121 local_mcck_disable();
124 void arch_cpu_idle(void)
126 if (!test_cpu_flag(CIF_MCCK_PENDING
))
127 /* Halt the cpu and keep track of cpu time accounting. */
132 void arch_cpu_idle_exit(void)
135 if (test_cpu_flag(CIF_MCCK_PENDING
))
139 void arch_cpu_idle_dead(void)