| .. | .. |
|---|
| 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 | |
|---|