[PATCH] PM / devfreq: Convert to seq_buf
Biju <[email protected]>
| Newsgroups | org.kernel.vger.linux-renesas-soc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
From: Biju Das <[email protected]> The devfreq transition table sysfs attribute (trans_stat_show()) builds its output using manual sysfs_emit_at() calls with hand-rolled length tracking and PAGE_SIZE bounds checks scattered throughout the loops. On SoCs with large frequency tables, such as Renesas RZ/G3L, the resulting table can exceed PAGE_SIZE. In that case the function bails out entirely and returns -EFBIG, logging: devfreq transition table exceeds PAGE_SIZE. Disabling which makes the attribute completely unusable on affected platforms. Convert trans_stat_show() to use struct seq_buf instead. seq_buf_printf() tracks the buffer position and handles overflow internally, removing the need for manual length checks before every write. If the table still overflows PAGE_SIZE, truncate the output to PAGE_SIZE - 1, NUL-terminate it, and log a rate-limited warning rather than failing the read outright, so the attribute still returns a usable (if truncated) table instead of nothing. Suggested-by: Geert Uytterhoeven <[email protected]> Signed-off-by: Biju Das <[email protected]> --- drivers/devfreq/devfreq.c | 64 ++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index f08fc6966eae..3c4c6fa23cfe 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -28,6 +28,7 @@ #include <linux/hrtimer.h> #include <linux/of.h> #include <linux/pm_qos.h> +#include <linux/seq_buf.h> #include <linux/units.h> #define CREATE_TRACE_POINTS @@ -1661,9 +1662,10 @@ static ssize_t trans_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *df = to_devfreq(dev); - ssize_t len = 0; - int i, j; unsigned int max_state; + struct seq_buf s; + ssize_t len; + int i, j; if (!df->profile) return -EINVAL; @@ -1672,6 +1674,8 @@ static ssize_t trans_stat_show(struct device *dev, if (max_state == 0) return sysfs_emit(buf, "Not Supported.\n"); + seq_buf_init(&s, buf, PAGE_SIZE); + mutex_lock(&df->lock); if (!df->stop_polling && devfreq_update_status(df, df->previous_freq)) { @@ -1680,47 +1684,39 @@ static ssize_t trans_stat_show(struct device *dev, } mutex_unlock(&df->lock); - len += sysfs_emit_at(buf, len, " From : To\n"); - len += sysfs_emit_at(buf, len, " :"); - for (i = 0; i < max_state; i++) { - if (len >= PAGE_SIZE - 1) - break; - len += sysfs_emit_at(buf, len, "%10lu", - df->freq_table[i]); - } + seq_buf_printf(&s, " From : To\n"); + seq_buf_printf(&s, " :"); + for (i = 0; i < max_state; i++) + seq_buf_printf(&s, "%10lu", df->freq_table[i]); - if (len >= PAGE_SIZE - 1) - return PAGE_SIZE - 1; - len += sysfs_emit_at(buf, len, " time(ms)\n"); + seq_buf_printf(&s, " time(ms)\n"); for (i = 0; i < max_state; i++) { - if (len >= PAGE_SIZE - 1) - break; if (df->freq_table[i] == df->previous_freq) - len += sysfs_emit_at(buf, len, "*"); + seq_buf_printf(&s, "*"); else - len += sysfs_emit_at(buf, len, " "); - if (len >= PAGE_SIZE - 1) - break; - len += sysfs_emit_at(buf, len, "%10lu:", df->freq_table[i]); + seq_buf_printf(&s, " "); + seq_buf_printf(&s, "%10lu:", df->freq_table[i]); for (j = 0; j < max_state; j++) { - if (len >= PAGE_SIZE - 1) - break; - len += sysfs_emit_at(buf, len, "%10u", - df->stats.trans_table[(i * max_state) + j]); + seq_buf_printf(&s, "%10u", + df->stats.trans_table[(i * max_state) + j]); } - if (len >= PAGE_SIZE - 1) - break; - len += sysfs_emit_at(buf, len, "%10llu\n", (u64) - jiffies64_to_msecs(df->stats.time_in_state[i])); + seq_buf_printf(&s, "%10llu\n", (u64) + jiffies64_to_msecs(df->stats.time_in_state[i])); } - if (len < PAGE_SIZE - 1) - len += sysfs_emit_at(buf, len, "Total transition : %u\n", - df->stats.total_trans); - if (len >= PAGE_SIZE - 1) { - pr_warn_once("devfreq transition table exceeds PAGE_SIZE. Disabling\n"); - return -EFBIG; + seq_buf_printf(&s, "Total transition : %u\n", df->stats.total_trans); + len = seq_buf_used(&s); + /* + * If an overflow occurs, seq_buf_used() can return a value greater + * than PAGE_SIZE. Clamp the return length to PAGE_SIZE - 1 and + * ensure a null-terminator is written to prevent the sysfs + * "bad count" or out-of-bounds warning. + */ + if (seq_buf_has_overflowed(&s)) { + pr_warn_ratelimited("devfreq transition table truncated due to PAGE_SIZE limit\n"); + len = PAGE_SIZE - 1; + buf[len] = '\0'; } return len; -- 2.43.0