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