drivers/devfreq: introduce device frequency scaling framework - #19741
Conversation
This commit introduces a devfreq framework to manage device frequency scaling. The framework includes the following features: 1.devfreq governor - provide governor ops, including init, start, stop, exit - default governor, performance & powersave - customized governor, device can provide governor when register 2.runtime register and unregister - device can runtime register & unregister, search by name 3.suspend and resume - suspend and resume frequency scaling 4.notify - register & unregister notifier callback, notify frequency changes 5.qos support - simplified QoS, manage multiple freq range request - including init, add/remove/update request, get value Signed-off-by: guanyi <guanyi@xiaomi.com>
> ls /proc/devfreq /proc/devfreq: test_devfreq > cat /proc/devfreq/test_devfreq devfreq: test_devfreq governor: test_devfreq_governor cur_freq: 500 suspended: False freq_table: 100 300 500 700 900 qos_list(min, max, backtrace): 195, 829, 0x4007c26 0x40a0e0e 0x405c706 0x4011186 0x4010dca 0x42777cc 0x4062f7e 0x409da6a Signed-off-by: guanyi <guanyi@xiaomi.com>
It's better not to use global governor, as modifying one device will cause all devices' governor to be modified. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
we do not hope the governor and driver in devfreq to be modified. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
Add devfreq ondemand governor that scales device frequency based on CPU load. When CPU load exceeds the configured threshold, frequency is set to maximum; otherwise it is scaled proportionally. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
devfreq_qos_add_request -> devfreq_refresh_limit -> devfreq_limit_governor -> devfreq_gov_ondemand_limit, here use governor_data but it's 0x0 Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
we may call devfreq_find_by_name() in pm_callback, and shouldn't call nxmutex_lock() in idle_loop, so replace mutex to spinlock. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
Add the ability to set frequency constraints via procfs write. Supported formats: echo <min>,<max> > /proc/devfreq/<name> - set frequency range echo 0,0 > /proc/devfreq/<name> - remove constraint The QoS request is bound to the devfreq device lifetime so that shell commands like echo (which open, write, close immediately) work correctly. Leading whitespace in the write buffer is skipped to handle extra writes from nsh echo (e.g. trailing newline). Also add write permissions in devfreq_stat() and a procfs_qos field in devfreq_s guarded by CONFIG_DEVFREQ_PROCFS. Signed-off-by: guanyi3 <guanyi3@xiaomi.com> (cherry picked from commit 70ae195c84f35a4d0b85fcc14187989b60fc0280)
When multiple QoS requests have no overlapping frequency range (min > max), the previous behavior always clamped to the lower frequency. Add a conflict_policy field to devfreq_driver_s so callers can choose between DEVFREQ_CONFLICT_PREFER_HIGH (default, choose higher freq) and DEVFREQ_CONFLICT_PREFER_LOW (choose lower freq) at registration. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
When devfreq_gov_ondemand_stop() is called from idle task context, work_cancel() is used instead of work_cancel_sync(), which does not wait for the currently running worker to complete. If devfreq_gov_ondemand_exit() then frees governor_data, the worker may still be accessing it, causing a use-after-free crash. Fix this by: - Nullifying dev->governor_data under dev->lock in exit before freeing. - Moving the governor_data read inside dev->lock in the worker and adding a NULL check to bail out early if data has been freed. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
…iver_target The cached devfreq->cur may become stale when the hardware frequency is changed externally (e.g. by another core or governor). This causes driver_target to incorrectly skip frequency transitions when the target matches the cached value but differs from the actual hardware frequency. Use driver->get_frequency() to read the real hardware frequency for the unchanged check, and sync devfreq->cur on match to keep the cache correct. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
QOS_REQ_MIN should return the highest value among all min requests (most restrictive lower bound), but plist_first returns the lowest. QOS_REQ_MAX should return the lowest value among all max requests (most restrictive upper bound), but plist_last returns the highest. This caused qos constraints to be ineffective. For example, two requests (32, 208000) and (104000, 104000) would merge to (32, 208000) instead of the correct (104000, 104000). Fix by using plist_last for QOS_REQ_MIN and plist_first for QOS_REQ_MAX. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
When CONFIG_LIBC_BACKTRACE_DEPTH is not set or <= 0, backtrace_get() is a macro that always sets depth to 0, making the for-loop body unreachable (Coverity CID 8405332 DEADCODE). Wrap backtrace_get() call, the loop, and related variable declarations with #if CONFIG_LIBC_BACKTRACE_DEPTH > 0 to eliminate the dead code and avoid unused variable warnings. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
Add Kconfig, Make.defs, and CMakeLists.txt entries for the ondemand governor so it can be enabled via CONFIG_DEVFREQ_GOV_ONDEMAND. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
Document the device frequency scaling framework: the QoS/governor arbitration model, the lower-half driver interface, built-in governors, in-kernel QoS requests, change notifications, procfs, and suspend/resume. Signed-off-by: guanyi3 <guanyi3@xiaomi.com>
|
Thanks for the comment ! |
|
@zzby0 thank you for explanation. Please include a board config example for some popular board like esp32-devkit or stm32f4discovery |
|
|
||
| cpuload = devfreq_gov_ondemand_cpuload(); | ||
| nxmutex_lock(&dev->lock); | ||
| data = dev->governor_data; |
There was a problem hiding this comment.
@zzby0 @xiaoxiang781216 hi, this file doesn't compile. Is there any commit missing in this PR?
There was a problem hiding this comment.
Okay, I'll check it right away.
There was a problem hiding this comment.
Thanks so much for pointing out the problem 👍
I will fix in 19788 😄
Summary
This PR introduces a device frequency scaling (devfreq) framework for
NuttX, providing generic dynamic voltage and frequency scaling (DVFS)
infrastructure for any clock-scalable device (CPU, GPU, memory bus, DSP, etc.).
Unlike a single system-wide CPU policy, devfreq manages any number of
independent devices, each registered by name with its own frequency table and
governor.
Architecture — the framework separates policy from mechanism:
struct devfreq_driver_s) is provided by the platform: anascending frequency table plus
target_index/get_frequencycallbacks. Thelower half is only ever told "go to table entry N".
[min, max]window; theframework aggregates all windows (highest
min, lowestmax) into a singleclamp. When windows do not intersect, the driver's
conflict_policy(
PREFER_HIGH/PREFER_LOW) decides the winner.performance(top of window),powersave(bottom ofwindow), and
ondemand(load-driven scaling).the lower half.
Components:
drivers/devfreq/devfreq.c) — registration, tablevalidation, QoS resolution, governor dispatch, suspend/resume, change
notifier chain.
performance,powersave,ondemand.devfreq_qos.c) built on a priority-sorted list(
include/nuttx/plist.h)./proc/devfreq/<name>) — read frequency table / currentfrequency / governor / QoS list; write to install a frequency constraint
from user space.
include/nuttx/devfreq.h) and documentation(
Documentation/.../special/devfreq.rst).Impact
CONFIG_DEVFREQ(defaultn); no effect on existing configurations.drivers/devfreq/subsystem wired intodrivers/Kconfig,drivers/Makefile, andCMakeLists.txt.CONFIG_DEVFREQ_PROCFSis set, exposes/proc/devfreq/<name>(read status/table, write<min> <max>in kHz toconstrain,
0 0to clear). RequiresCONFIG_FS_PROCFS; auto-selectsCONFIG_FS_PROCFS_REGISTER.CONFIG_DEVFREQ,CONFIG_DEVFREQ_PROCFS,CONFIG_DEVFREQ_PROCFS_QOS,CONFIG_DEVFREQ_GOV_ONDEMAND,CONFIG_DEVFREQ_SAMPLE_RATE,CONFIG_DEVFREQ_LOAD_THRESHOLD.include/nuttx/plist.h(priority-sorted list, header-only),a dependency of the QoS engine.
Documentation/components/drivers/special/.Testing
Host & Targets
gcc13.4.0 (sim),arm-none-eabi-gcc10.3.1 (arm)tools/checkpatch.shpasses on all commits.Build Verification
sim:nshLD nuttxOK, 0 warningsqemu-armv7a:nshLD nuttxOK, 0 warningsRuntime Functional Test — Simulator
A temporary dummy lower-half driver (table
200000 400000 600000 800000kHz)was registered as
/proc/devfreq/testto exercise the framework end to end(scaffolding removed after testing).
performance governor (selects the top of the resolved window):
powersave governor (selects the bottom of the resolved window):
Runtime Functional Test — Real Hardware (BES2800bp, Cortex-M55)
The framework was also validated on real silicon driving the CPU clock through
a platform CPU devfreq lower half (
/proc/devfreq/cpu, table32 26000 52000 104000 208000 320000kHz, performance governor). The lower-halfCPU driver is board/downstream code and is not part of this PR.
Scenario 1 — Concurrent QoS requests with conflicting windows.
Two requests are installed: one caps at
208000, another pins[320000, 320000]. The windows do not intersect (aggregate min320000>aggregate max
208000), so the driver'sconflict_policyresolves it — herethe higher frequency wins and the CPU runs at 320 MHz:
Scenario 2 — Updating a QoS request re-resolves and changes the real clock.
The
[320000, 320000]request is updated to[208000, 208000]via procfs. Thewindows now intersect at
208000, and the CPU frequency drops to 208 MHz onhardware:
Coverage Summary
Verified across simulator and hardware: device registration, procfs read/write,
QoS
[min, max]aggregation (upper cap and lower floor), multi-requesterconflict resolution via
conflict_policy, governor selection (performance =top-of-window, powersave = bottom-of-window), QoS clear, invalid-input
rejection, and lower-half
target_index/get_frequencyinvocation with areal frequency change on Cortex-M55.
Documentation
Documentation/components/drivers/special/devfreq.rstrenders correctly viamake html.Notes
All temporary test scaffolding (the sim dummy driver) was reverted after
testing and is not part of this PR.