diff mbox series

PM / devfreq: Show trans_stat info via debugfs

Message ID 20230914102317.29302-1-jiazi.li@transsion.com
State New
Headers show
Series PM / devfreq: Show trans_stat info via debugfs | expand

Commit Message

Jiazi Li Sept. 14, 2023, 10:23 a.m. UTC
For device with over PAGE_SIZE bytes trans_stat info, trans_stat
in sysfs just show a prompt information.
Create a trans_stat node in debugfs, show detailed information.

Signed-off-by: Jiazi Li <jiazi.li@transsion.com>
---
 drivers/devfreq/devfreq.c | 79 ++++++++++++++++++++++++++++++++++++++-
 include/linux/devfreq.h   |  2 +
 2 files changed, 80 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index e36cbb920ec8..08847734ecee 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -940,6 +940,8 @@  struct devfreq *devfreq_add_device(struct device *dev,
 		goto err_init;
 	}
 	create_sysfs_files(devfreq, devfreq->governor);
+	devfreq->debugfs_dir =
+		debugfs_create_dir(dev_name(&(devfreq->dev)), devfreq_debugfs);
 
 	list_add(&devfreq->node, &devfreq_list);
 
@@ -978,6 +980,7 @@  int devfreq_remove_device(struct devfreq *devfreq)
 
 	devfreq_cooling_unregister(devfreq->cdev);
 
+	debugfs_remove_recursive(devfreq->debugfs_dir);
 	if (devfreq->governor) {
 		devfreq->governor->event_handler(devfreq,
 						 DEVFREQ_GOV_STOP, NULL);
@@ -1682,6 +1685,66 @@  static ssize_t available_frequencies_show(struct device *d,
 }
 static DEVICE_ATTR_RO(available_frequencies);
 
+/**
+ * devfreq_trans_stat_show() - show trans stat info through debugfs
+ * @s:		seq_file instance to show trans_stat info of devfreq devices
+ * @data:	not used
+ *
+ * Show detailed information for some device which has more than PAGE_SIZE
+ * bytes of trans stat info.
+ */
+static int devfreq_trans_stat_show(struct seq_file *s, void *unused)
+{
+	struct devfreq *df = s->private;
+	unsigned int max_state;
+	int i, j;
+
+	if (!df->profile)
+		return -EINVAL;
+	max_state = df->max_state;
+
+	if (max_state == 0) {
+		seq_puts(s, "Not Supported.\n");
+		return 0;
+	}
+
+	mutex_lock(&df->lock);
+	if (!df->stop_polling &&
+			devfreq_update_status(df, df->previous_freq)) {
+		mutex_unlock(&df->lock);
+		return 0;
+	}
+	mutex_unlock(&df->lock);
+
+	seq_puts(s, "     From  :   To\n");
+	seq_puts(s, "           :");
+	for (i = 0; i < max_state; i++)
+		seq_printf(s, "%10lu", df->freq_table[i]);
+
+	seq_puts(s, "   time(ms)\n");
+
+	for (i = 0; i < max_state; i++) {
+		if (df->freq_table[i] == df->previous_freq)
+			seq_puts(s, "*");
+		else
+			seq_puts(s, " ");
+
+		seq_printf(s, "%10lu:", df->freq_table[i]);
+
+		for (j = 0; j < max_state; j++)
+			seq_printf(s, "%10u",
+				df->stats.trans_table[(i * max_state) + j]);
+
+		seq_printf(s, "%10llu\n", (u64)
+			jiffies64_to_msecs(df->stats.time_in_state[i]));
+	}
+
+	seq_printf(s, "Total transition : %u\n", df->stats.total_trans);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(devfreq_trans_stat);
+
 static ssize_t trans_stat_show(struct device *dev,
 			       struct device_attribute *attr, char *buf)
 {
@@ -1694,8 +1757,22 @@  static ssize_t trans_stat_show(struct device *dev,
 		return -EINVAL;
 	max_state = df->max_state;
 
-	if (max_state == 0)
+	if (max_state == 0) {
 		return sprintf(buf, "Not Supported.\n");
+	} else if (max_state > 12) {
+		/*
+		 * In theory, 13 states will use more than PAGE_SIZE
+		 * bytes, show detail info through debugfs
+		 */
+		if (!df->trans_stat)
+			df->trans_stat = debugfs_create_file("trans_stat", 0444,
+					df->debugfs_dir, df,
+					&devfreq_trans_stat_fops);
+		return sprintf(buf,
+				"This device's data size exceeds the limit of sysfs.\n"
+				"Please use debugfs for detailed information:\n"
+				"/sys/kernel/debug/devfreq/.../trans_stat\n");
+	}
 
 	mutex_lock(&df->lock);
 	if (!df->stop_polling &&
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index d312ffbac4dd..7df3f9e51d28 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -209,6 +209,8 @@  struct devfreq {
 
 	/* information for device frequency transitions */
 	struct devfreq_stats stats;
+	struct dentry *debugfs_dir;
+	struct dentry *trans_stat;
 
 	struct srcu_notifier_head transition_notifier_list;