This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 827b455f68bf16c846ac743055e4ddb0a3183e6e Author: guanyi <[email protected]> AuthorDate: Fri May 16 16:07:54 2025 +0800 driver/devfreq: add procfs for devfreq > 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 <[email protected]> --- drivers/devfreq/CMakeLists.txt | 4 + drivers/devfreq/Kconfig | 15 ++ drivers/devfreq/Make.defs | 6 + drivers/devfreq/devfreq.c | 35 +++ drivers/devfreq/devfreq_procfs.c | 414 ++++++++++++++++++++++++++++++++++++ drivers/devfreq/devfreq_qos.c | 9 + drivers/drivers_initialize.c | 5 + include/nuttx/devfreq.h | 32 +++ include/nuttx/devfreq/devfreq_qos.h | 3 + 9 files changed, 523 insertions(+) diff --git a/drivers/devfreq/CMakeLists.txt b/drivers/devfreq/CMakeLists.txt index 91ba9ef4760..9fb34381e49 100644 --- a/drivers/devfreq/CMakeLists.txt +++ b/drivers/devfreq/CMakeLists.txt @@ -21,5 +21,9 @@ if(CONFIG_DEVFREQ) set(SRCS devfreq.c devfreq_performance.c devfreq_powersave.c devfreq_qos.c) + if(CONFIG_DEVFREQ_PROCFS) + list(APPEND SRCS devfreq_procfs.c) + endif() + target_sources(drivers PRIVATE ${SRCS}) endif() diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 7bb0a18ab51..cd0b562913f 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -33,4 +33,19 @@ config DEVFREQ_DEFAULT_GOV_PASSIVE endchoice +config DEVFREQ_PROCFS + bool "devfreq_procfs" + default n + depends on FS_PROCFS + select FS_PROCFS_REGISTER + ---help--- + devfreq procfs support + +config DEVFREQ_PROCFS_QOS + bool "devfreq_procfs_qos" + default n + depends on DEVFREQ_PROCFS + ---help--- + devfreq procfs show qos requests and their callers + endif diff --git a/drivers/devfreq/Make.defs b/drivers/devfreq/Make.defs index e50951e5b6b..59baad2d023 100644 --- a/drivers/devfreq/Make.defs +++ b/drivers/devfreq/Make.defs @@ -24,6 +24,12 @@ ifeq ($(CONFIG_DEVFREQ),y) CSRCS += devfreq.c devfreq_performance.c devfreq_powersave.c devfreq_qos.c +ifeq ($(CONFIG_DEVFREQ_PROCFS),y) + +CSRCS += devfreq_procfs.c + +endif + DEPPATH += --dep-path devfreq VPATH += devfreq diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 2bb3a599290..c2116315b5c 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -480,6 +480,7 @@ FAR struct devfreq_s *devfreq_register( BLOCKING_INIT_NOTIFIER_HEAD(&devfreq->notifier_list); nxmutex_init(&devfreq->lock); + strlcpy(devfreq->name, name, NAME_MAX); devfreq->driver = driver; devfreq->priv = priv; devfreq->suspended = false; @@ -854,3 +855,37 @@ FAR struct devfreq_s *devfreq_find_by_name(FAR const char *name) nxmutex_unlock(&g_devfreq_list_lock); return NULL; } + +/**************************************************************************** + * Name: devfreq_find_by_index + * + * Description: + * find a devfreq entry from global list by index + * + * Input Parameters: + * index - devfreq index + * + * Returned Value: + * devfreq handle + * + ****************************************************************************/ + +FAR struct devfreq_s *devfreq_find_by_index(size_t index) +{ + FAR struct devfreq_s *devfreq; + size_t i = 0; + + nxmutex_lock(&g_devfreq_list_lock); + + list_for_every_entry(&g_devfreq_list, devfreq, struct devfreq_s, node) + { + if (index == i++) + { + nxmutex_unlock(&g_devfreq_list_lock); + return devfreq; + } + } + + nxmutex_unlock(&g_devfreq_list_lock); + return NULL; +} diff --git a/drivers/devfreq/devfreq_procfs.c b/drivers/devfreq/devfreq_procfs.c new file mode 100644 index 00000000000..700e1a960aa --- /dev/null +++ b/drivers/devfreq/devfreq_procfs.c @@ -0,0 +1,414 @@ +/**************************************************************************** + * drivers/devfreq/devfreq_procfs.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <execinfo.h> +#include <stdio.h> +#include <string.h> + +#include <nuttx/devfreq.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/procfs.h> +#include <nuttx/kmalloc.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Determines the size of an intermediate buffer that must be large enough + * to handle the longest line generated by this logic. + */ + +#define DEVFREQ_LINELEN 256 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct devfreq_procfs_s +{ + struct procfs_file_s base; + FAR struct devfreq_s *devfreq; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* File system methods */ + +static int devfreq_open(FAR struct file *filep, + FAR const char *relpath, + int oflags, mode_t mode); +static int devfreq_close(FAR struct file *filep); +static ssize_t devfreq_read(FAR struct file *filep, + FAR char *buffer, + size_t buflen); +static ssize_t devfreq_write(FAR struct file *filep, + FAR const char *buffer, + size_t buflen); +static int devfreq_dup(FAR const struct file *oldp, + FAR struct file *newp); +static int devfreq_opendir(FAR const char *relpath, + FAR struct fs_dirent_s **dir); +static int devfreq_readdir(FAR struct fs_dirent_s *dir, + FAR struct dirent *entry); +static int devfreq_closedir(FAR struct fs_dirent_s *dir); +static int devfreq_rewinddir(FAR struct fs_dirent_s *dir); +static int devfreq_stat(FAR const char *relpath, FAR struct stat *buf); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct procfs_operations g_devfreq_operations = +{ + .open = devfreq_open, /* open */ + .close = devfreq_close, /* close */ + .read = devfreq_read, /* read */ + .write = devfreq_write, /* write */ + .poll = NULL, /* poll */ + .dup = devfreq_dup, /* dup */ + + .opendir = devfreq_opendir, /* opendir */ + .closedir = devfreq_closedir, /* closedir */ + .readdir = devfreq_readdir, /* readdir */ + .rewinddir = devfreq_rewinddir, /* rewinddir */ + .stat = devfreq_stat, /* stat */ +}; + +static const struct procfs_entry_s g_devfreq_procfs_root = +{ + "devfreq", &g_devfreq_operations, PROCFS_DIR_TYPE +}; + +static const struct procfs_entry_s g_devfreq_procfs_entry = +{ + "devfreq/**", &g_devfreq_operations, PROCFS_UNKOWN_TYPE +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: devfreq_open + ****************************************************************************/ + +static int devfreq_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct devfreq_s *devfreq; + FAR struct devfreq_procfs_s *devfreq_procfs; + + relpath += strlen("devfreq/"); + devfreq = devfreq_find_by_name(relpath); + if (!devfreq) + { + return -ENOENT; + } + + devfreq_procfs = kmm_zalloc(sizeof(struct devfreq_procfs_s)); + if (!devfreq_procfs) + { + return -ENOMEM; + } + + devfreq_procfs->devfreq = devfreq; + filep->f_priv = devfreq_procfs; + return 0; +} + +/**************************************************************************** + * Name: devfreq_close + ****************************************************************************/ + +static int devfreq_close(FAR struct file *filep) +{ + DEBUGASSERT(filep->f_priv); + + kmm_free(filep->f_priv); + filep->f_priv = NULL; + return 0; +} + +/**************************************************************************** + * Name: devfreq_read + ****************************************************************************/ + +static ssize_t devfreq_read(FAR struct file *filep, + FAR char *buffer, size_t buflen) +{ + FAR struct devfreq_procfs_s *devfreq_procfs = filep->f_priv; + FAR struct devfreq_s *devfreq = devfreq_procfs->devfreq; +#ifdef CONFIG_DEVFREQ_PROCFS_QOS + FAR struct qos_request_s *qos; + void **stack; + int depth; +#endif + off_t offset = filep->f_pos; + size_t i; + + nxmutex_lock(&devfreq->lock); + + procfs_sprintf(buffer, buflen, &offset, + " devfreq: %s\n" + " governor: %s\n" + " cur_freq: %"PRIu32"\n" + " suspended: %s\n", + devfreq->name, + devfreq->governor->name, + devfreq->cur, + devfreq->suspended ? "True" : "False"); + + if (devfreq->freq_table) + { + procfs_sprintf(buffer, buflen, &offset, " freq_table: "); + for (i = 0; devfreq->freq_table[i] != DEVFREQ_ENTRY_END; i++) + { + if (devfreq->freq_table[i] == DEVFREQ_ENTRY_INVALID) + { + continue; + } + + procfs_sprintf(buffer, buflen, &offset, + " %"PRIu32"", devfreq->freq_table[i]); + } + + procfs_sprintf(buffer, buflen, &offset, "\n"); + } + +#ifdef CONFIG_DEVFREQ_PROCFS_QOS + procfs_sprintf(buffer, buflen, &offset, + " qos_list(min, max, backtrace):\n"); + plist_for_each_entry(qos, &devfreq->constraints.min_requests, min_req) + { + stack = backtrace_get(qos->backtrace, &depth); + procfs_sprintf(buffer, buflen, &offset, + " %"PRIu32", %"PRIu32",", + qos->min_req.prio, qos->max_req.prio); + for (i = 0; i < depth; i++) + { + procfs_sprintf(buffer, buflen, &offset, " %p", stack[i]); + } + + procfs_sprintf(buffer, buflen, &offset, "\n"); + } +#endif + + nxmutex_unlock(&devfreq->lock); + + if (offset < 0) + { + offset = -offset; + } + else + { + offset = 0; + } + + filep->f_pos += offset; + return offset; +} + +/**************************************************************************** + * Name: devfreq_write + ****************************************************************************/ + +static ssize_t devfreq_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + return buflen; +} + +/**************************************************************************** + * Name: devfreq_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int devfreq_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + newp->f_priv = oldp->f_priv; + return 0; +} + +/**************************************************************************** + * Name: devfreq_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int devfreq_opendir(FAR const char *relpath, + FAR struct fs_dirent_s **dir) +{ + FAR struct procfs_dir_priv_s *level1; + + level1 = kmm_zalloc(sizeof(struct procfs_dir_priv_s)); + if (!level1) + { + *dir = NULL; + return -ENOMEM; + } + + level1->level = 1; + + level1->nentries = UINT16_MAX; + + *dir = (FAR struct fs_dirent_s *)level1; + return 0; +} + +/**************************************************************************** + * Name: devfreq_closedir + * + * Description: + * Close the directory listing + * + ****************************************************************************/ + +static int devfreq_closedir(FAR struct fs_dirent_s *dir) +{ + kmm_free(dir); + return 0; +} + +/**************************************************************************** + * Name: devfreq_readdir + * + * Description: + * Read the next directory entry + * + ****************************************************************************/ + +static int devfreq_readdir(FAR struct fs_dirent_s *dir, + FAR struct dirent *entry) +{ + FAR struct devfreq_s *devfreq; + FAR struct procfs_dir_priv_s *level1; + + DEBUGASSERT(dir); + level1 = (FAR struct procfs_dir_priv_s *)dir; + devfreq = devfreq_find_by_index(level1->index); + if (!devfreq) + { + return -ENOENT; + } + + entry->d_type = DTYPE_FILE; + strlcpy(entry->d_name, devfreq->name, NAME_MAX); + level1->index++; + return 0; +} + +/**************************************************************************** + * Name: devfreq_rewinddir + * + * Description: + * Reset directory read to the first entry + * + ****************************************************************************/ + +static int devfreq_rewinddir(FAR struct fs_dirent_s *dir) +{ + FAR struct procfs_dir_priv_s *level1; + + DEBUGASSERT(dir); + level1 = (FAR struct procfs_dir_priv_s *)dir; + level1->index = 0; + return 0; +} + +/**************************************************************************** + * Name: devfreq_stat + * + * Description: + * Return information about a file or directory + * + ****************************************************************************/ + +static int devfreq_stat(FAR const char *relpath, FAR struct stat *buf) +{ + FAR struct devfreq_s *devfreq; + + memset(buf, 0, sizeof(struct stat)); + + if (strcmp(relpath, "devfreq") == 0 || strcmp(relpath, "devfreq/") == 0) + { + buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; + } + else + { + relpath += strlen("devfreq/"); + devfreq = devfreq_find_by_name(relpath); + if (!devfreq) + { + return -ENOENT; + } + + buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: devfreq_procfs_initialize + * + * Description: + * initialize procfs for devfreq, called by devfreq_initialize() + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void devfreq_procfs_initialize(void) +{ + int ret; + + ret = procfs_register(&g_devfreq_procfs_root); + if (ret == 0) + { + ret = procfs_register(&g_devfreq_procfs_entry); + } + + DEBUGASSERT(ret == 0); +} diff --git a/drivers/devfreq/devfreq_qos.c b/drivers/devfreq/devfreq_qos.c index 251b25f0880..501f30e3d63 100644 --- a/drivers/devfreq/devfreq_qos.c +++ b/drivers/devfreq/devfreq_qos.c @@ -25,6 +25,7 @@ #include <nuttx/devfreq/devfreq_qos.h> #include <nuttx/kmalloc.h> #include <errno.h> +#include <execinfo.h> /**************************************************************************** * Public Functions @@ -89,6 +90,10 @@ FAR struct qos_request_s *qos_add_request( plist_add(&req->min_req, &constraints->min_requests); plist_add(&req->max_req, &constraints->max_requests); +#ifdef CONFIG_DEVFREQ_PROCFS_QOS + req->backtrace = backtrace_record(0); +#endif + return req; } @@ -118,6 +123,10 @@ int qos_remove_request(FAR struct qos_constraints_s *constraints, plist_del(&req->min_req, &constraints->min_requests); plist_del(&req->max_req, &constraints->max_requests); +#ifdef CONFIG_DEVFREQ_PROCFS_QOS + backtrace_remove(req->backtrace); +#endif + kmm_free(req); return 0; diff --git a/drivers/drivers_initialize.c b/drivers/drivers_initialize.c index 968abaf2821..43dce646413 100644 --- a/drivers/drivers_initialize.c +++ b/drivers/drivers_initialize.c @@ -26,6 +26,7 @@ #include <nuttx/clk/clk_provider.h> #include <nuttx/crypto/crypto.h> +#include <nuttx/devfreq.h> #include <nuttx/drivers/drivers.h> #include <nuttx/drivers/rpmsgdev.h> #include <nuttx/drivers/rpmsgblk.h> @@ -125,6 +126,10 @@ void drivers_initialize(void) serial_rtt_initialize(); #endif +#if defined(CONFIG_DEVFREQ_PROCFS) + devfreq_procfs_initialize(); +#endif + #if defined(CONFIG_DEV_NULL) devnull_register(); /* Standard /dev/null */ #endif diff --git a/include/nuttx/devfreq.h b/include/nuttx/devfreq.h index 367986e5b50..2487a05bf1e 100644 --- a/include/nuttx/devfreq.h +++ b/include/nuttx/devfreq.h @@ -310,6 +310,38 @@ int devfreq_qos_remove_request(FAR struct devfreq_s *devfreq, FAR struct devfreq_s *devfreq_find_by_name(FAR const char *name); +/**************************************************************************** + * Name: devfreq_find_by_index + * + * Description: + * find a devfreq entry from global list by index + * + * Input Parameters: + * index - devfreq index + * + * Returned Value: + * devfreq handle + * + ****************************************************************************/ + +FAR struct devfreq_s *devfreq_find_by_index(size_t index); + +/**************************************************************************** + * Name: devfreq_procfs_initialize + * + * Description: + * initialize procfs for devfreq, called by devfreq_initialize() + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void devfreq_procfs_initialize(void); + #ifdef CONFIG_DEVFREQ_DEFAULT_GOV_PASSIVE #define devfreq_default_governor() NULL #else diff --git a/include/nuttx/devfreq/devfreq_qos.h b/include/nuttx/devfreq/devfreq_qos.h index 47837b4c6e0..d85e135033f 100644 --- a/include/nuttx/devfreq/devfreq_qos.h +++ b/include/nuttx/devfreq/devfreq_qos.h @@ -41,6 +41,9 @@ struct qos_request_s { struct plist_node min_req; struct plist_node max_req; +#ifdef CONFIG_DEVFREQ_PROCFS_QOS + int backtrace; +#endif }; struct qos_constraints_s
