[PATCH 3/5] scsi: lpfc: Replace strlcat() with seq_buf in lpfc_rx_monitor_report()
From: Ian Bridges
Date: Wed Jul 29 2026 - 11:05:25 EST
In preparation for removing the strlcat() API[1], replace its use in
lpfc_rx_monitor_report().
The function accumulates one line per ring entry, which is what
seq_buf is for. seq_buf tracks the write position, so the per entry
strlen() rescans of the destination are gone. Each record is still
formatted into the tmp buffer. seq_buf_puts() appends it only when
it fits whole, so the output keeps ending at the last complete
record. The loop still stops on overflow without consuming the
current entry, and the returned count and the ring head keep their
old meaning. The produced bytes are unchanged.
Link: https://github.com/KSPP/linux/issues/370 [1]
Signed-off-by: Ian Bridges <icb@xxxxxxxxxxxx>
---
seq_buf_puts() accepts a record under exactly the same condition as
the replaced length check, so truncation keeps ending the output at
the last complete record and a partial record can never reach the
buffer. The differential harness compared 50000 randomized rings
and buffer sizes, including buffers that overflow mid ring, and
found byte identical output, entry counts and ring heads
everywhere. The KUnit corpus reproduced the overflow case with a
populated ring as compiled kernel code and confirmed the same.
drivers/scsi/lpfc/lpfc_sli.c | 43 ++++++++++++++++++++----------------
1 file changed, 24 insertions(+), 19 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 62a30a92b792..cfa4371169f1 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -25,6 +25,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/lockdep.h>
#include <linux/dmi.h>
@@ -8109,17 +8110,18 @@ u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
u32 cnt = 0;
char tmp[DBG_LOG_STR_SZ] = {0};
bool log_to_kmsg = (!buf || !buf_len) ? true : false;
+ struct seq_buf s;
if (!log_to_kmsg) {
/* clear the buffer to be sure */
memset(buf, 0, buf_len);
- scnprintf(buf, buf_len, "\t%-16s%-16s%-16s%-16s%-8s%-8s%-8s"
- "%-8s%-8s%-8s%-16s\n",
- "MaxBPI", "Tot_Data_CMF",
- "Tot_Data_Cmd", "Tot_Data_Cmpl",
- "Lat(us)", "Avg_IO", "Max_IO", "Bsy",
- "IO_cnt", "Info", "BWutil(ms)");
+ seq_buf_init(&s, buf, buf_len);
+ seq_buf_printf(&s, "\t%-16s%-16s%-16s%-16s%-8s%-8s%-8s%-8s%-8s%-8s%-16s\n",
+ "MaxBPI", "Tot_Data_CMF",
+ "Tot_Data_Cmd", "Tot_Data_Cmpl",
+ "Lat(us)", "Avg_IO", "Max_IO", "Bsy",
+ "IO_cnt", "Info", "BWutil(ms)");
}
/* Needs to be _irq because record is called from timer interrupt
@@ -8131,24 +8133,27 @@ u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
/* Read out this entry's data. */
if (!log_to_kmsg) {
- /* If !log_to_kmsg, then store to buf. */
+ /*
+ * Drop a record whole if it does not fit, without
+ * consuming its ring entry.
+ */
scnprintf(tmp, sizeof(tmp),
- "%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu"
- "%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n",
- *head_idx, entry->max_bytes_per_interval,
- entry->cmf_bytes, entry->total_bytes,
- entry->rcv_bytes, entry->avg_io_latency,
- entry->avg_io_size, entry->max_read_cnt,
+ "%03d:\t%-16llu%-16llu%-16llu%-16llu%-8llu%-8llu%-8llu%-8u%-8u%-8u%u(%u)\n",
+ *head_idx,
+ entry->max_bytes_per_interval,
+ entry->cmf_bytes,
+ entry->total_bytes,
+ entry->rcv_bytes,
+ entry->avg_io_latency,
+ entry->avg_io_size,
+ entry->max_read_cnt,
entry->cmf_busy, entry->io_cnt,
- entry->cmf_info, entry->timer_utilization,
+ entry->cmf_info,
+ entry->timer_utilization,
entry->timer_interval);
- /* Check for buffer overflow */
- if ((strlen(buf) + strlen(tmp)) >= buf_len)
+ if (seq_buf_puts(&s, tmp) < 0)
break;
-
- /* Append entry's data to buffer */
- strlcat(buf, tmp, buf_len);
} else {
lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
"4410 %02u: MBPI %llu Xmit %llu "
--
2.47.3