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); } inthome | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69ea3f3b.37598.3a29eb6c>
