.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note |
---|
2 | 2 | /* |
---|
3 | 3 | * |
---|
4 | | - * (C) COPYRIGHT 2011-2021 ARM Limited. All rights reserved. |
---|
| 4 | + * (C) COPYRIGHT 2011-2022 ARM Limited. All rights reserved. |
---|
5 | 5 | * |
---|
6 | 6 | * This program is free software and is provided to you under the terms of the |
---|
7 | 7 | * GNU General Public License version 2 as published by the Free Software |
---|
.. | .. |
---|
24 | 24 | */ |
---|
25 | 25 | |
---|
26 | 26 | #include <mali_kbase.h> |
---|
| 27 | +#include <mali_kbase_config_defaults.h> |
---|
27 | 28 | #include <mali_kbase_pm.h> |
---|
28 | 29 | #include <backend/gpu/mali_kbase_pm_internal.h> |
---|
29 | 30 | |
---|
.. | .. |
---|
37 | 38 | #include <backend/gpu/mali_kbase_pm_defs.h> |
---|
38 | 39 | #include <mali_linux_trace.h> |
---|
39 | 40 | |
---|
| 41 | +#if defined(CONFIG_MALI_BIFROST_DEVFREQ) || defined(CONFIG_MALI_BIFROST_DVFS) || !MALI_USE_CSF |
---|
40 | 42 | /* Shift used for kbasep_pm_metrics_data.time_busy/idle - units of (1 << 8) ns |
---|
41 | 43 | * This gives a maximum period between samples of 2^(32+8)/100 ns = slightly |
---|
42 | 44 | * under 11s. Exceeding this will cause overflow |
---|
43 | 45 | */ |
---|
44 | 46 | #define KBASE_PM_TIME_SHIFT 8 |
---|
| 47 | +#endif |
---|
45 | 48 | |
---|
46 | 49 | #if MALI_USE_CSF |
---|
47 | 50 | /* To get the GPU_ACTIVE value in nano seconds unit */ |
---|
48 | 51 | #define GPU_ACTIVE_SCALING_FACTOR ((u64)1E9) |
---|
49 | 52 | #endif |
---|
50 | 53 | |
---|
| 54 | +/* |
---|
| 55 | + * Possible state transitions |
---|
| 56 | + * ON -> ON | OFF | STOPPED |
---|
| 57 | + * STOPPED -> ON | OFF |
---|
| 58 | + * OFF -> ON |
---|
| 59 | + * |
---|
| 60 | + * |
---|
| 61 | + * ┌─e─┐┌────────────f─────────────┐ |
---|
| 62 | + * │ v│ v |
---|
| 63 | + * └───ON ──a──> STOPPED ──b──> OFF |
---|
| 64 | + * ^^ │ │ |
---|
| 65 | + * │└──────c─────┘ │ |
---|
| 66 | + * │ │ |
---|
| 67 | + * └─────────────d─────────────┘ |
---|
| 68 | + * |
---|
| 69 | + * Transition effects: |
---|
| 70 | + * a. None |
---|
| 71 | + * b. Timer expires without restart |
---|
| 72 | + * c. Timer is not stopped, timer period is unaffected |
---|
| 73 | + * d. Timer must be restarted |
---|
| 74 | + * e. Callback is executed and the timer is restarted |
---|
| 75 | + * f. Timer is cancelled, or the callback is waited on if currently executing. This is called during |
---|
| 76 | + * tear-down and should not be subject to a race from an OFF->ON transition |
---|
| 77 | + */ |
---|
| 78 | +enum dvfs_metric_timer_state { TIMER_OFF, TIMER_STOPPED, TIMER_ON }; |
---|
| 79 | + |
---|
51 | 80 | #ifdef CONFIG_MALI_BIFROST_DVFS |
---|
52 | 81 | static enum hrtimer_restart dvfs_callback(struct hrtimer *timer) |
---|
53 | 82 | { |
---|
54 | | - unsigned long flags; |
---|
55 | 83 | struct kbasep_pm_metrics_state *metrics; |
---|
56 | 84 | |
---|
57 | | - KBASE_DEBUG_ASSERT(timer != NULL); |
---|
| 85 | + if (WARN_ON(!timer)) |
---|
| 86 | + return HRTIMER_NORESTART; |
---|
58 | 87 | |
---|
59 | 88 | metrics = container_of(timer, struct kbasep_pm_metrics_state, timer); |
---|
| 89 | + |
---|
| 90 | + /* Transition (b) to fully off if timer was stopped, don't restart the timer in this case */ |
---|
| 91 | + if (atomic_cmpxchg(&metrics->timer_state, TIMER_STOPPED, TIMER_OFF) != TIMER_ON) |
---|
| 92 | + return HRTIMER_NORESTART; |
---|
| 93 | + |
---|
60 | 94 | kbase_pm_get_dvfs_action(metrics->kbdev); |
---|
61 | 95 | |
---|
62 | | - spin_lock_irqsave(&metrics->lock, flags); |
---|
63 | | - |
---|
64 | | - if (metrics->timer_active) |
---|
65 | | - hrtimer_start(timer, |
---|
66 | | - HR_TIMER_DELAY_MSEC(metrics->kbdev->pm.dvfs_period), |
---|
67 | | - HRTIMER_MODE_REL); |
---|
68 | | - |
---|
69 | | - spin_unlock_irqrestore(&metrics->lock, flags); |
---|
70 | | - |
---|
71 | | - return HRTIMER_NORESTART; |
---|
| 96 | + /* Set the new expiration time and restart (transition e) */ |
---|
| 97 | + hrtimer_forward_now(timer, HR_TIMER_DELAY_MSEC(metrics->kbdev->pm.dvfs_period)); |
---|
| 98 | + return HRTIMER_RESTART; |
---|
72 | 99 | } |
---|
73 | 100 | #endif /* CONFIG_MALI_BIFROST_DVFS */ |
---|
74 | 101 | |
---|
.. | .. |
---|
83 | 110 | |
---|
84 | 111 | KBASE_DEBUG_ASSERT(kbdev != NULL); |
---|
85 | 112 | kbdev->pm.backend.metrics.kbdev = kbdev; |
---|
86 | | - kbdev->pm.backend.metrics.time_period_start = ktime_get(); |
---|
| 113 | + kbdev->pm.backend.metrics.time_period_start = ktime_get_raw(); |
---|
87 | 114 | kbdev->pm.backend.metrics.values.time_busy = 0; |
---|
88 | 115 | kbdev->pm.backend.metrics.values.time_idle = 0; |
---|
89 | 116 | kbdev->pm.backend.metrics.values.time_in_protm = 0; |
---|
.. | .. |
---|
111 | 138 | #else |
---|
112 | 139 | KBASE_DEBUG_ASSERT(kbdev != NULL); |
---|
113 | 140 | kbdev->pm.backend.metrics.kbdev = kbdev; |
---|
114 | | - kbdev->pm.backend.metrics.time_period_start = ktime_get(); |
---|
| 141 | + kbdev->pm.backend.metrics.time_period_start = ktime_get_raw(); |
---|
115 | 142 | |
---|
116 | 143 | kbdev->pm.backend.metrics.gpu_active = false; |
---|
117 | 144 | kbdev->pm.backend.metrics.active_cl_ctx[0] = 0; |
---|
.. | .. |
---|
134 | 161 | HRTIMER_MODE_REL); |
---|
135 | 162 | kbdev->pm.backend.metrics.timer.function = dvfs_callback; |
---|
136 | 163 | kbdev->pm.backend.metrics.initialized = true; |
---|
| 164 | + atomic_set(&kbdev->pm.backend.metrics.timer_state, TIMER_OFF); |
---|
137 | 165 | kbase_pm_metrics_start(kbdev); |
---|
138 | 166 | #endif /* CONFIG_MALI_BIFROST_DVFS */ |
---|
139 | 167 | |
---|
.. | .. |
---|
152 | 180 | void kbasep_pm_metrics_term(struct kbase_device *kbdev) |
---|
153 | 181 | { |
---|
154 | 182 | #ifdef CONFIG_MALI_BIFROST_DVFS |
---|
155 | | - unsigned long flags; |
---|
156 | | - |
---|
157 | 183 | KBASE_DEBUG_ASSERT(kbdev != NULL); |
---|
158 | 184 | |
---|
159 | | - spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags); |
---|
160 | | - kbdev->pm.backend.metrics.timer_active = false; |
---|
161 | | - spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags); |
---|
162 | | - |
---|
163 | | - hrtimer_cancel(&kbdev->pm.backend.metrics.timer); |
---|
| 185 | + /* Cancel the timer, and block if the callback is currently executing (transition f) */ |
---|
164 | 186 | kbdev->pm.backend.metrics.initialized = false; |
---|
| 187 | + atomic_set(&kbdev->pm.backend.metrics.timer_state, TIMER_OFF); |
---|
| 188 | + hrtimer_cancel(&kbdev->pm.backend.metrics.timer); |
---|
165 | 189 | #endif /* CONFIG_MALI_BIFROST_DVFS */ |
---|
166 | 190 | |
---|
167 | 191 | #if MALI_USE_CSF |
---|
.. | .. |
---|
199 | 223 | * elapsed time. The lock taken inside kbase_ipa_control_query() |
---|
200 | 224 | * function can cause lot of variation. |
---|
201 | 225 | */ |
---|
202 | | - now = ktime_get(); |
---|
| 226 | + now = ktime_get_raw(); |
---|
203 | 227 | |
---|
204 | 228 | if (err) { |
---|
205 | 229 | dev_err(kbdev->dev, |
---|
.. | .. |
---|
231 | 255 | * time. |
---|
232 | 256 | */ |
---|
233 | 257 | if (!kbdev->pm.backend.metrics.skip_gpu_active_sanity_check) { |
---|
234 | | - /* Use a margin value that is approximately 1% of the time |
---|
235 | | - * difference. |
---|
| 258 | + /* The margin is scaled to allow for the worst-case |
---|
| 259 | + * scenario where the samples are maximally separated, |
---|
| 260 | + * plus a small offset for sampling errors. |
---|
236 | 261 | */ |
---|
237 | | - u64 margin_ns = diff_ns >> 6; |
---|
238 | | - if (gpu_active_counter > (diff_ns + margin_ns)) { |
---|
| 262 | + u64 const MARGIN_NS = |
---|
| 263 | + IPA_CONTROL_TIMER_DEFAULT_VALUE_MS * NSEC_PER_MSEC * 3 / 2; |
---|
| 264 | + |
---|
| 265 | + if (gpu_active_counter > (diff_ns + MARGIN_NS)) { |
---|
239 | 266 | dev_info( |
---|
240 | 267 | kbdev->dev, |
---|
241 | 268 | "GPU activity takes longer than time interval: %llu ns > %llu ns", |
---|
.. | .. |
---|
330 | 357 | #if MALI_USE_CSF |
---|
331 | 358 | kbase_pm_get_dvfs_utilisation_calc(kbdev); |
---|
332 | 359 | #else |
---|
333 | | - kbase_pm_get_dvfs_utilisation_calc(kbdev, ktime_get()); |
---|
| 360 | + kbase_pm_get_dvfs_utilisation_calc(kbdev, ktime_get_raw()); |
---|
334 | 361 | #endif |
---|
335 | 362 | |
---|
336 | 363 | memset(diff, 0, sizeof(*diff)); |
---|
.. | .. |
---|
395 | 422 | |
---|
396 | 423 | bool kbase_pm_metrics_is_active(struct kbase_device *kbdev) |
---|
397 | 424 | { |
---|
398 | | - bool isactive; |
---|
399 | | - unsigned long flags; |
---|
400 | | - |
---|
401 | 425 | KBASE_DEBUG_ASSERT(kbdev != NULL); |
---|
402 | 426 | |
---|
403 | | - spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags); |
---|
404 | | - isactive = kbdev->pm.backend.metrics.timer_active; |
---|
405 | | - spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags); |
---|
406 | | - |
---|
407 | | - return isactive; |
---|
| 427 | + return atomic_read(&kbdev->pm.backend.metrics.timer_state) == TIMER_ON; |
---|
408 | 428 | } |
---|
409 | 429 | KBASE_EXPORT_TEST_API(kbase_pm_metrics_is_active); |
---|
410 | 430 | |
---|
411 | 431 | void kbase_pm_metrics_start(struct kbase_device *kbdev) |
---|
412 | 432 | { |
---|
413 | | - unsigned long flags; |
---|
414 | | - bool update = true; |
---|
| 433 | + struct kbasep_pm_metrics_state *metrics = &kbdev->pm.backend.metrics; |
---|
415 | 434 | |
---|
416 | | - if (unlikely(!kbdev->pm.backend.metrics.initialized)) |
---|
| 435 | + if (unlikely(!metrics->initialized)) |
---|
417 | 436 | return; |
---|
418 | 437 | |
---|
419 | | - spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags); |
---|
420 | | - if (!kbdev->pm.backend.metrics.timer_active) |
---|
421 | | - kbdev->pm.backend.metrics.timer_active = true; |
---|
422 | | - else |
---|
423 | | - update = false; |
---|
424 | | - spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags); |
---|
425 | | - |
---|
426 | | - if (update) |
---|
427 | | - hrtimer_start(&kbdev->pm.backend.metrics.timer, |
---|
428 | | - HR_TIMER_DELAY_MSEC(kbdev->pm.dvfs_period), |
---|
429 | | - HRTIMER_MODE_REL); |
---|
| 438 | + /* Transition to ON, from a stopped state (transition c) */ |
---|
| 439 | + if (atomic_xchg(&metrics->timer_state, TIMER_ON) == TIMER_OFF) |
---|
| 440 | + /* Start the timer only if it's been fully stopped (transition d)*/ |
---|
| 441 | + hrtimer_start(&metrics->timer, HR_TIMER_DELAY_MSEC(kbdev->pm.dvfs_period), |
---|
| 442 | + HRTIMER_MODE_REL); |
---|
430 | 443 | } |
---|
431 | 444 | |
---|
432 | 445 | void kbase_pm_metrics_stop(struct kbase_device *kbdev) |
---|
433 | 446 | { |
---|
434 | | - unsigned long flags; |
---|
435 | | - bool update = true; |
---|
436 | | - |
---|
437 | 447 | if (unlikely(!kbdev->pm.backend.metrics.initialized)) |
---|
438 | 448 | return; |
---|
439 | 449 | |
---|
440 | | - spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags); |
---|
441 | | - if (kbdev->pm.backend.metrics.timer_active) |
---|
442 | | - kbdev->pm.backend.metrics.timer_active = false; |
---|
443 | | - else |
---|
444 | | - update = false; |
---|
445 | | - spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags); |
---|
446 | | - |
---|
447 | | - if (update) |
---|
448 | | - hrtimer_cancel(&kbdev->pm.backend.metrics.timer); |
---|
| 450 | + /* Timer is Stopped if its currently on (transition a) */ |
---|
| 451 | + atomic_cmpxchg(&kbdev->pm.backend.metrics.timer_state, TIMER_ON, TIMER_STOPPED); |
---|
449 | 452 | } |
---|
450 | 453 | |
---|
451 | 454 | |
---|
.. | .. |
---|
461 | 464 | */ |
---|
462 | 465 | static void kbase_pm_metrics_active_calc(struct kbase_device *kbdev) |
---|
463 | 466 | { |
---|
464 | | - int js; |
---|
| 467 | + unsigned int js; |
---|
465 | 468 | |
---|
466 | 469 | lockdep_assert_held(&kbdev->pm.backend.metrics.lock); |
---|
467 | 470 | |
---|
.. | .. |
---|
488 | 491 | BASE_JD_REQ_SPECIFIC_COHERENT_GROUP) |
---|
489 | 492 | ? katom->device_nr : 0; |
---|
490 | 493 | if (!WARN_ON(device_nr >= 2)) |
---|
491 | | - kbdev->pm.backend.metrics. |
---|
492 | | - active_cl_ctx[device_nr] = 1; |
---|
| 494 | + kbdev->pm.backend.metrics.active_cl_ctx[device_nr] = 1; |
---|
493 | 495 | } else { |
---|
494 | 496 | kbdev->pm.backend.metrics.active_gl_ctx[js] = 1; |
---|
495 | 497 | trace_sysgraph(SGR_ACTIVE, 0, js); |
---|
.. | .. |
---|
512 | 514 | spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags); |
---|
513 | 515 | |
---|
514 | 516 | if (!timestamp) { |
---|
515 | | - now = ktime_get(); |
---|
| 517 | + now = ktime_get_raw(); |
---|
516 | 518 | timestamp = &now; |
---|
517 | 519 | } |
---|
518 | 520 | |
---|