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

Reply via email to