Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 23 Apr 2026 15:48:11 +0000
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: f4418cf954c2 - main - LinuxKPI: Update seq_file to properly implement the iterator interface
Message-ID:  <69ea3f3b.37598.3a29eb6c@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=f4418cf954c299fa0934f110d6f5e9d50f2d24c5

commit f4418cf954c299fa0934f110d6f5e9d50f2d24c5
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2026-04-23 15:46:54 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2026-04-23 15:46:54 +0000

    LinuxKPI: Update seq_file to properly implement the iterator interface
    
    The seq_file.rst documentation in the Linux kernel documents the
    iterator interface for the seq_file structure.  In particular, the
    ppos passed to seq_read is a logical offset into a seq_file managed by
    the iterator interface, not an offset into the generated data.  For
    example, if a seq_file outputs state for each node in a linked-list or
    array, *ppos might be used as the index of the node to output, not a
    byte offset.
    
    Rewrite seq_read to honor this contract which fixes a few bugs:
    
    - Treat *ppos as a logical iterator offset that is only updated by the
      next callback after outputting a single item via the show method.
    
    - Use a loop to permit outputting descriptions of multiple items if
      the user buffer is large enough.
    
    - Always invoke the stop method after terminating the loop to cleanup
      any state setup by start (e.g. if start allocated a buffer or
      obtained a lock, the stop method is called to cleanup).
    
    While here, implement support for SEQ_SKIP as documented in the Linux
    documentation even though it is not currently used in the tree.
    
    Reviewed by:    bz
    Sponsored by:   AFRL, DARPA
    Differential Revision:  https://reviews.freebsd.org/D55899
---
 .../linuxkpi/common/include/linux/seq_file.h       |  2 +
 sys/compat/linuxkpi/common/src/linux_seq_file.c    | 64 +++++++++++++++++++---
 2 files changed, 59 insertions(+), 7 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index 786c09bd6a20..be03c4768b07 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -39,6 +39,8 @@
 #undef file
 #define inode vnode
 
+#define	SEQ_SKIP	1
+
 MALLOC_DECLARE(M_LSEQ);
 
 #define	DEFINE_SHOW_ATTRIBUTE(__name)					\
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index eae414ea696e..efa41b28c6b1 100644
--- a/sys/compat/linuxkpi/common/src/linux_seq_file.c
+++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c
@@ -45,23 +45,73 @@ seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos)
 	struct seq_file *m;
 	struct sbuf *sbuf;
 	void *p;
-	ssize_t rc;
+	ssize_t rc, oldlen;
+	size_t todo;
 
 	m = f->private_data;
 	sbuf = m->buf;
 	sbuf_clear(sbuf);
 
 	p = m->op->start(m, ppos);
-	rc = m->op->show(m, p);
-	if (rc)
+	if (p == NULL)
+		return (0);
+
+	rc = 0;
+	while (size > sbuf_len(sbuf)) {
+		oldlen = sbuf_len(sbuf);
+		rc = m->op->show(m, p);
+		if (rc < 0)
+			break;
+
+		if (rc == SEQ_SKIP) {
+			/* Discard any data written in show callback. */
+			sbuf_setpos(sbuf, oldlen);
+			rc = 0;
+		}
+
+		/*
+		 * If the sbuf has overflowed, bail.  Discard any
+		 * partial output from the show callback for this item
+		 * preserving output from any earlier items.  Since we
+		 * break before calling the next callback to update
+		 * *ppos, a subsequent read() will start by displaying
+		 * the current item.  However, if the current item
+		 * could not be displayed by itself, still fail with
+		 * ENOMEM rather than returning EOF.
+		 */
+		if (sbuf_error(sbuf)) {
+			if (oldlen != 0)
+				sbuf_setpos(sbuf, oldlen);
+			break;
+		}
+
+		/*
+		 * XXX: The seq_file documentation claims that Linux
+		 * warns if this callback doesn't update the value in
+		 * *ppos.  We don't bother warning here.
+		 */
+		p = m->op->next(m, p, ppos);
+		if (p == NULL)
+			break;
+	}
+	m->op->stop(m, p);
+
+	if (rc < 0)
 		return (rc);
 
 	rc = sbuf_finish(sbuf);
-	if (rc)
-		return (rc);
+	if (rc != 0)
+		return (-rc);
+
+	todo = sbuf_len(sbuf);
+	if (todo > size)
+		todo = size;
+
+	rc = copy_to_user(ubuf, sbuf_data(sbuf), todo);
+	if (rc != 0)
+		return (-EFAULT);
 
-	return (simple_read_from_buffer(ubuf, size, ppos, sbuf_data(sbuf),
-	    sbuf_len(sbuf)));
+	return (todo);
 }
 
 int


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69ea3f3b.37598.3a29eb6c>