.. | .. |
---|
19 | 19 | * The idle + run duration is specified via separate helpers and that allows |
---|
20 | 20 | * idle injection to be started. |
---|
21 | 21 | * |
---|
22 | | - * The idle injection kthreads will call play_idle() with the idle duration |
---|
23 | | - * specified as per the above. |
---|
| 22 | + * The idle injection kthreads will call play_idle_precise() with the idle |
---|
| 23 | + * duration and max allowed latency specified as per the above. |
---|
24 | 24 | * |
---|
25 | 25 | * After all of them have been woken up, a timer is set to start the next idle |
---|
26 | 26 | * injection cycle. |
---|
.. | .. |
---|
43 | 43 | #include <linux/sched.h> |
---|
44 | 44 | #include <linux/slab.h> |
---|
45 | 45 | #include <linux/smpboot.h> |
---|
| 46 | +#include <linux/idle_inject.h> |
---|
46 | 47 | |
---|
47 | 48 | #include <uapi/linux/sched/types.h> |
---|
48 | 49 | |
---|
.. | .. |
---|
59 | 60 | /** |
---|
60 | 61 | * struct idle_inject_device - idle injection data |
---|
61 | 62 | * @timer: idle injection period timer |
---|
62 | | - * @idle_duration_ms: duration of CPU idle time to inject |
---|
63 | | - * @run_duration_ms: duration of CPU run time to allow |
---|
| 63 | + * @idle_duration_us: duration of CPU idle time to inject |
---|
| 64 | + * @run_duration_us: duration of CPU run time to allow |
---|
| 65 | + * @latency_us: max allowed latency |
---|
64 | 66 | * @cpumask: mask of CPUs affected by idle injection |
---|
65 | 67 | */ |
---|
66 | 68 | struct idle_inject_device { |
---|
67 | 69 | struct hrtimer timer; |
---|
68 | | - unsigned int idle_duration_ms; |
---|
69 | | - unsigned int run_duration_ms; |
---|
70 | | - unsigned long int cpumask[0]; |
---|
| 70 | + unsigned int idle_duration_us; |
---|
| 71 | + unsigned int run_duration_us; |
---|
| 72 | + unsigned int latency_us; |
---|
| 73 | + unsigned long cpumask[]; |
---|
71 | 74 | }; |
---|
72 | 75 | |
---|
73 | 76 | static DEFINE_PER_CPU(struct idle_inject_thread, idle_inject_thread); |
---|
.. | .. |
---|
98 | 101 | * |
---|
99 | 102 | * This function is called when the idle injection timer expires. It wakes up |
---|
100 | 103 | * idle injection tasks associated with the timer and they, in turn, invoke |
---|
101 | | - * play_idle() to inject a specified amount of CPU idle time. |
---|
| 104 | + * play_idle_precise() to inject a specified amount of CPU idle time. |
---|
102 | 105 | * |
---|
103 | 106 | * Return: HRTIMER_RESTART. |
---|
104 | 107 | */ |
---|
105 | 108 | static enum hrtimer_restart idle_inject_timer_fn(struct hrtimer *timer) |
---|
106 | 109 | { |
---|
107 | | - unsigned int duration_ms; |
---|
| 110 | + unsigned int duration_us; |
---|
108 | 111 | struct idle_inject_device *ii_dev = |
---|
109 | 112 | container_of(timer, struct idle_inject_device, timer); |
---|
110 | 113 | |
---|
111 | | - duration_ms = READ_ONCE(ii_dev->run_duration_ms); |
---|
112 | | - duration_ms += READ_ONCE(ii_dev->idle_duration_ms); |
---|
| 114 | + duration_us = READ_ONCE(ii_dev->run_duration_us); |
---|
| 115 | + duration_us += READ_ONCE(ii_dev->idle_duration_us); |
---|
113 | 116 | |
---|
114 | 117 | idle_inject_wakeup(ii_dev); |
---|
115 | 118 | |
---|
116 | | - hrtimer_forward_now(timer, ms_to_ktime(duration_ms)); |
---|
| 119 | + hrtimer_forward_now(timer, ns_to_ktime(duration_us * NSEC_PER_USEC)); |
---|
117 | 120 | |
---|
118 | 121 | return HRTIMER_RESTART; |
---|
119 | 122 | } |
---|
.. | .. |
---|
122 | 125 | * idle_inject_fn - idle injection work function |
---|
123 | 126 | * @cpu: the CPU owning the task |
---|
124 | 127 | * |
---|
125 | | - * This function calls play_idle() to inject a specified amount of CPU idle |
---|
126 | | - * time. |
---|
| 128 | + * This function calls play_idle_precise() to inject a specified amount of CPU |
---|
| 129 | + * idle time. |
---|
127 | 130 | */ |
---|
128 | 131 | static void idle_inject_fn(unsigned int cpu) |
---|
129 | 132 | { |
---|
.. | .. |
---|
138 | 141 | */ |
---|
139 | 142 | iit->should_run = 0; |
---|
140 | 143 | |
---|
141 | | - play_idle(READ_ONCE(ii_dev->idle_duration_ms)); |
---|
| 144 | + play_idle_precise(READ_ONCE(ii_dev->idle_duration_us) * NSEC_PER_USEC, |
---|
| 145 | + READ_ONCE(ii_dev->latency_us) * NSEC_PER_USEC); |
---|
142 | 146 | } |
---|
143 | 147 | |
---|
144 | 148 | /** |
---|
145 | 149 | * idle_inject_set_duration - idle and run duration update helper |
---|
146 | | - * @run_duration_ms: CPU run time to allow in milliseconds |
---|
147 | | - * @idle_duration_ms: CPU idle time to inject in milliseconds |
---|
| 150 | + * @run_duration_us: CPU run time to allow in microseconds |
---|
| 151 | + * @idle_duration_us: CPU idle time to inject in microseconds |
---|
148 | 152 | */ |
---|
149 | 153 | void idle_inject_set_duration(struct idle_inject_device *ii_dev, |
---|
150 | | - unsigned int run_duration_ms, |
---|
151 | | - unsigned int idle_duration_ms) |
---|
| 154 | + unsigned int run_duration_us, |
---|
| 155 | + unsigned int idle_duration_us) |
---|
152 | 156 | { |
---|
153 | | - if (run_duration_ms && idle_duration_ms) { |
---|
154 | | - WRITE_ONCE(ii_dev->run_duration_ms, run_duration_ms); |
---|
155 | | - WRITE_ONCE(ii_dev->idle_duration_ms, idle_duration_ms); |
---|
| 157 | + if (run_duration_us && idle_duration_us) { |
---|
| 158 | + WRITE_ONCE(ii_dev->run_duration_us, run_duration_us); |
---|
| 159 | + WRITE_ONCE(ii_dev->idle_duration_us, idle_duration_us); |
---|
156 | 160 | } |
---|
157 | 161 | } |
---|
158 | 162 | |
---|
159 | 163 | /** |
---|
160 | 164 | * idle_inject_get_duration - idle and run duration retrieval helper |
---|
161 | | - * @run_duration_ms: memory location to store the current CPU run time |
---|
162 | | - * @idle_duration_ms: memory location to store the current CPU idle time |
---|
| 165 | + * @run_duration_us: memory location to store the current CPU run time |
---|
| 166 | + * @idle_duration_us: memory location to store the current CPU idle time |
---|
163 | 167 | */ |
---|
164 | 168 | void idle_inject_get_duration(struct idle_inject_device *ii_dev, |
---|
165 | | - unsigned int *run_duration_ms, |
---|
166 | | - unsigned int *idle_duration_ms) |
---|
| 169 | + unsigned int *run_duration_us, |
---|
| 170 | + unsigned int *idle_duration_us) |
---|
167 | 171 | { |
---|
168 | | - *run_duration_ms = READ_ONCE(ii_dev->run_duration_ms); |
---|
169 | | - *idle_duration_ms = READ_ONCE(ii_dev->idle_duration_ms); |
---|
| 172 | + *run_duration_us = READ_ONCE(ii_dev->run_duration_us); |
---|
| 173 | + *idle_duration_us = READ_ONCE(ii_dev->idle_duration_us); |
---|
| 174 | +} |
---|
| 175 | + |
---|
| 176 | +/** |
---|
| 177 | + * idle_inject_set_latency - set the maximum latency allowed |
---|
| 178 | + * @latency_us: set the latency requirement for the idle state |
---|
| 179 | + */ |
---|
| 180 | +void idle_inject_set_latency(struct idle_inject_device *ii_dev, |
---|
| 181 | + unsigned int latency_us) |
---|
| 182 | +{ |
---|
| 183 | + WRITE_ONCE(ii_dev->latency_us, latency_us); |
---|
170 | 184 | } |
---|
171 | 185 | |
---|
172 | 186 | /** |
---|
.. | .. |
---|
181 | 195 | */ |
---|
182 | 196 | int idle_inject_start(struct idle_inject_device *ii_dev) |
---|
183 | 197 | { |
---|
184 | | - unsigned int idle_duration_ms = READ_ONCE(ii_dev->idle_duration_ms); |
---|
185 | | - unsigned int run_duration_ms = READ_ONCE(ii_dev->run_duration_ms); |
---|
| 198 | + unsigned int idle_duration_us = READ_ONCE(ii_dev->idle_duration_us); |
---|
| 199 | + unsigned int run_duration_us = READ_ONCE(ii_dev->run_duration_us); |
---|
186 | 200 | |
---|
187 | | - if (!idle_duration_ms || !run_duration_ms) |
---|
| 201 | + if (!idle_duration_us || !run_duration_us) |
---|
188 | 202 | return -EINVAL; |
---|
189 | 203 | |
---|
190 | 204 | pr_debug("Starting injecting idle cycles on CPUs '%*pbl'\n", |
---|
.. | .. |
---|
193 | 207 | idle_inject_wakeup(ii_dev); |
---|
194 | 208 | |
---|
195 | 209 | hrtimer_start(&ii_dev->timer, |
---|
196 | | - ms_to_ktime(idle_duration_ms + run_duration_ms), |
---|
| 210 | + ns_to_ktime((idle_duration_us + run_duration_us) * |
---|
| 211 | + NSEC_PER_USEC), |
---|
197 | 212 | HRTIMER_MODE_REL); |
---|
198 | 213 | |
---|
199 | 214 | return 0; |
---|
.. | .. |
---|
254 | 269 | */ |
---|
255 | 270 | static void idle_inject_setup(unsigned int cpu) |
---|
256 | 271 | { |
---|
257 | | - struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO / 2 }; |
---|
258 | | - |
---|
259 | | - sched_setscheduler(current, SCHED_FIFO, ¶m); |
---|
| 272 | + sched_set_fifo(current); |
---|
260 | 273 | } |
---|
261 | 274 | |
---|
262 | 275 | /** |
---|
.. | .. |
---|
296 | 309 | cpumask_copy(to_cpumask(ii_dev->cpumask), cpumask); |
---|
297 | 310 | hrtimer_init(&ii_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
---|
298 | 311 | ii_dev->timer.function = idle_inject_timer_fn; |
---|
| 312 | + ii_dev->latency_us = UINT_MAX; |
---|
299 | 313 | |
---|
300 | 314 | for_each_cpu(cpu, to_cpumask(ii_dev->cpumask)) { |
---|
301 | 315 | |
---|