The branch main has been updated by manu:

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

commit f697b9432d9c7aa4c5ab5f5445ef5dc1bd40ce00
Author:     Jake Freeland <jf...@freebsd.org>
AuthorDate: 2022-09-20 17:41:10 +0000
Commit:     Emmanuel Vadot <m...@freebsd.org>
CommitDate: 2022-09-20 17:41:10 +0000

    linuxkpi: drm-kmod debugfs support
    
    This diff extends LinuxKPI to support simple attribute files in debugfs.
    These simple attributes are an essential component for compiling drm-kmod
    with CONFIG_DEBUG_FS enabled.
    This will allow for easier graphics driver debugging using
    Intel's igt-gpu-tools.
    
    Reviewed by:    hselasky
    Differential Revision:  https://reviews.freebsd.org/D35883
    Sponsored by:   Google, Inc. (GSoC 2022)
---
 share/man/man5/Makefile                            |   1 +
 share/man/man5/lindebugfs.5                        |  95 ++++++++++
 sys/compat/lindebugfs/lindebugfs.c                 | 169 +++++++++++++-----
 sys/compat/linuxkpi/common/include/linux/dcache.h  |   5 +-
 sys/compat/linuxkpi/common/include/linux/debugfs.h |  43 ++++-
 sys/compat/linuxkpi/common/include/linux/fs.h      |  78 +++++++++
 .../linuxkpi/common/include/linux/seq_file.h       |  11 +-
 sys/compat/linuxkpi/common/src/linux_seq_file.c    |  58 +++++--
 sys/compat/linuxkpi/common/src/linux_simple_attr.c | 191 +++++++++++++++++++++
 sys/conf/files                                     |   5 +-
 sys/modules/linuxkpi/Makefile                      |   3 +-
 11 files changed, 593 insertions(+), 66 deletions(-)

diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile
index 9be4c8dc46b0..2d49d981c2f9 100644
--- a/share/man/man5/Makefile
+++ b/share/man/man5/Makefile
@@ -32,6 +32,7 @@ MAN=  acct.5 \
        hosts.lpd.5 \
        intro.5 \
        libmap.conf.5 \
+       lindebugfs.5 \
        link.5 \
        linprocfs.5 \
        linsysfs.5 \
diff --git a/share/man/man5/lindebugfs.5 b/share/man/man5/lindebugfs.5
new file mode 100644
index 000000000000..0d93d6aec6b9
--- /dev/null
+++ b/share/man/man5/lindebugfs.5
@@ -0,0 +1,95 @@
+.\"  SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+.\"
+.\"  Copyright (c) 2022, Jake Freeland <jf...@freebsd.org>
+.\"
+.\"  Redistribution and use in source and binary forms, with or without
+.\"  modification, are permitted provided that the following conditions
+.\"  are met:
+.\"  1. Redistributions of source code must retain the above copyright
+.\"     notice, this list of conditions and the following disclaimer.
+.\"  2. Redistributions in binary form must reproduce the above copyright
+.\"     notice, this list of conditions and the following disclaimer in the
+.\"     documentation and/or other materials provided with the distribution.
+.\"
+.\"  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\"  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\"  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\"  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\"  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\"  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\"  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\"  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\"  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\"  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\"  SUCH DAMAGE.
+
+.Dd August 10, 2022
+.Dt LINDEBUGFS 5
+.Os
+.Sh NAME
+.Nm lindebugfs
+.Nd Linux file system for debugging
+.Sh SYNOPSIS
+.Bd -literal
+lindebugfs             /sys/kernel/debug       lindebugfs      rw 0 0
+.Ed
+.Sh DESCRIPTION
+The debug file system, or debugfs, makes process debugging easier by
+providing a simple API for data transfer between the kernel and user
+space.
+Debugfs is not a general-purpose file system and should not be used as
+a storage medium.
+Instead, developers can implement the debugfs interface in their code
+to generate debug information about their program at runtime.
+FreeBSD's
+.Nm
+uses the
+.Xr pseudofs 9
+file system construction kit to model itself after Linux's debugfs.
+The
+.Nm
+API is intended for use with programs that take advantage of FreeBSD's
+LinuxKPI compatibility layer.
+.Pp
+When mounted,
+.Nm
+will populate with pseudo files from any running process that calls
+.Nm debugfs_create_file() .
+Since
+.Nm
+is a pseudo file system, file contents will be generated dynamically
+based on program provided file operations.
+The current
+.Nm
+implementation formally supports seq_file and simple_attr_file virtual
+file formats.
+.Sh EXAMPLES
+Load the
+.Nm
+kernel module:
+.Pp
+.Dl "kldload lindebugfs"
+.Pp
+Mount the
+.Nm
+file system on
+.Pa /sys/kernel/debug :
+.Pp
+.Dl "mount -t lindebugfs lindebugfs /sys/kernel/debug"
+.Sh SEE ALSO
+.Xr linprocfs 5 ,
+.Xr linsysfs 5 ,
+.Xr pseudofs 9 ,
+.Xr linux 4 ,
+.Xr mount 1
+.Sh HISTORY
+The
+.Nm
+file system first appeared in
+.Fx 12.1 .
+.Sh AUTHORS
+.An -nosplit
+The initial implementation for
+.Nm
+was created by Matthew Macy.
+This manual page was written by Jake Freeland.
diff --git a/sys/compat/lindebugfs/lindebugfs.c 
b/sys/compat/lindebugfs/lindebugfs.c
index cbfdfbbce876..b88caf9c3140 100644
--- a/sys/compat/lindebugfs/lindebugfs.c
+++ b/sys/compat/lindebugfs/lindebugfs.c
@@ -69,9 +69,9 @@ __FBSDID("$FreeBSD$");
 #include <compat/linux/linux_util.h>
 #include <fs/pseudofs/pseudofs.h>
 
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
 #include <linux/compat.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
 
 MALLOC_DEFINE(M_DFSINT, "debugfsint", "Linux debugfs internal");
 
@@ -118,27 +118,17 @@ debugfs_fill(PFS_FILL_ARGS)
 {
        struct dentry_meta *d;
        struct linux_file lf = {};
-       struct seq_file *sf;
        struct vnode vn;
-       void *buf;
+       char *buf;
        int rc;
-       size_t len;
-       off_t off;
-
-       d = pn->pn_data;
+       off_t off = 0;
 
        if ((rc = linux_set_current_flags(curthread, M_NOWAIT)))
                return (rc);
+
+       d = pn->pn_data;
        vn.v_data = d->dm_data;
-       if (uio->uio_rw == UIO_READ) {
-               buf = uio->uio_iov[0].iov_base;
-               len = min(uio->uio_iov[0].iov_len, uio->uio_resid);
-       } else {
-               sbuf_finish(sb);
-               buf = sbuf_data(sb);
-               len = sbuf_len(sb);
-       }
-       off = 0;
+
        rc = d->dm_fops->open(&vn, &lf);
        if (rc < 0) {
 #ifdef INVARIANTS
@@ -146,19 +136,23 @@ debugfs_fill(PFS_FILL_ARGS)
 #endif
                return (-rc);
        }
-       sf = lf.private_data;
-       sf->buf = sb;
-       if (uio->uio_rw == UIO_READ) {
-               if (d->dm_fops->read)
-                       rc = d->dm_fops->read(&lf, NULL, len, &off);
-               else
-                       rc = -ENODEV;
-       } else {
-               if (d->dm_fops->write)
-                       rc = d->dm_fops->write(&lf, buf, len, &off);
-               else
-                       rc = -ENODEV;
+
+       rc = -ENODEV;
+       if (uio->uio_rw == UIO_READ && d->dm_fops->read) {
+               rc = -ENOMEM;
+               buf = (char *) malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT);
+               if (buf != NULL) {
+                       rc = d->dm_fops->read(&lf, buf, sb->s_size, &off);
+                       if (rc > 0)
+                               sbuf_bcpy(sb, buf, strlen(buf));
+
+                       free(buf, M_DFSINT);
+               }
+       } else if (uio->uio_rw == UIO_WRITE && d->dm_fops->write) {
+               sbuf_finish(sb);
+               rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb), &off);
        }
+
        if (d->dm_fops->release)
                d->dm_fops->release(&vn, &lf);
        else
@@ -185,8 +179,8 @@ debugfs_fill_data(PFS_FILL_ARGS)
 
 struct dentry *
 debugfs_create_file(const char *name, umode_t mode,
-                   struct dentry *parent, void *data,
-                   const struct file_operations *fops)
+    struct dentry *parent, void *data,
+    const struct file_operations *fops)
 {
        struct dentry_meta *dm;
        struct dentry *dnode;
@@ -218,6 +212,43 @@ debugfs_create_file(const char *name, umode_t mode,
        return (dnode);
 }
 
+/*
+ * NOTE: Files created with the _unsafe moniker will not be protected from
+ * debugfs core file removals. It is the responsibility of @fops to protect
+ * its file using debugfs_file_get() and debugfs_file_put().
+ *
+ * FreeBSD's LinuxKPI lindebugfs does not perform file removals at the time
+ * of writing. Therefore there is no difference between functions with _unsafe
+ * and functions without _unsafe when using lindebugfs. Functions with _unsafe
+ * exist only for Linux compatibility.
+ */
+struct dentry *
+debugfs_create_file_unsafe(const char *name, umode_t mode,
+    struct dentry *parent, void *data,
+    const struct file_operations *fops)
+{
+       return (debugfs_create_file(name, mode, parent, data, fops));
+}
+
+struct dentry *
+debugfs_create_mode_unsafe(const char *name, umode_t mode,
+    struct dentry *parent, void *data,
+    const struct file_operations *fops,
+    const struct file_operations *fops_ro,
+    const struct file_operations *fops_wo)
+{
+       umode_t read = mode & S_IRUGO;
+       umode_t write = mode & S_IWUGO;
+
+       if (read && !write)
+               return (debugfs_create_file_unsafe(name, mode, parent, data, 
fops_ro));
+
+       if (write && !read)
+               return (debugfs_create_file_unsafe(name, mode, parent, data, 
fops_wo));
+
+       return (debugfs_create_file_unsafe(name, mode, parent, data, fops));
+}
+
 struct dentry *
 debugfs_create_dir(const char *name, struct dentry *parent)
 {
@@ -247,7 +278,7 @@ debugfs_create_dir(const char *name, struct dentry *parent)
 
 struct dentry *
 debugfs_create_symlink(const char *name, struct dentry *parent,
-       const char *dest)
+    const char *dest)
 {
        struct dentry_meta *dm;
        struct dentry *dnode;
@@ -300,7 +331,71 @@ debugfs_remove_recursive(struct dentry *dnode)
 }
 
 static int
-debugfs_init(PFS_INIT_ARGS)
+debugfs_bool_get(void *data, uint64_t *ullval)
+{
+       bool *bval = data;
+
+       if (*bval)
+               *ullval = 1;
+       else
+               *ullval = 0;
+
+       return (0);
+}
+
+static int
+debugfs_bool_set(void *data, uint64_t ullval)
+{
+       bool *bval = data;
+
+       if (ullval)
+               *bval = 1;
+       else
+               *bval = 0;
+
+       return (0);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_bool, debugfs_bool_get, debugfs_bool_set, 
"%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_bool_wo, NULL, debugfs_bool_set, "%llu\n");
+
+void
+debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, 
bool *value)
+{
+       debugfs_create_mode_unsafe(name, mode, parent, value, &fops_bool,
+           &fops_bool_ro, &fops_bool_wo);
+}
+
+static int
+debugfs_ulong_get(void *data, uint64_t *value)
+{
+       uint64_t *uldata = data;
+       *value = *uldata;
+       return (0);
+}
+
+static int
+debugfs_ulong_set(void *data, uint64_t value)
+{
+       uint64_t *uldata = data;
+       *uldata = value;
+       return (0);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong, debugfs_ulong_get, debugfs_ulong_set, 
"%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_ro, debugfs_ulong_get, NULL, "%llu\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_ulong_wo, NULL, debugfs_ulong_set, "%llu\n");
+
+void
+debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, 
unsigned long *value)
+{
+       debugfs_create_mode_unsafe(name, mode, parent, value, &fops_ulong,
+           &fops_ulong_ro, &fops_ulong_wo);
+}
+
+static int
+lindebugfs_init(PFS_INIT_ARGS)
 {
 
        debugfs_root = pi->pi_root;
@@ -311,14 +406,10 @@ debugfs_init(PFS_INIT_ARGS)
 }
 
 static int
-debugfs_uninit(PFS_INIT_ARGS)
+lindebugfs_uninit(PFS_INIT_ARGS)
 {
        return (0);
 }
 
-#ifdef PR_ALLOW_MOUNT_LINSYSFS
-PSEUDOFS(debugfs, 1, PR_ALLOW_MOUNT_LINSYSFS);
-#else
-PSEUDOFS(debugfs, 1, VFCF_JAIL);
-#endif
+PSEUDOFS(lindebugfs, 1, VFCF_JAIL);
 MODULE_DEPEND(lindebugfs, linuxkpi, 1, 1, 1);
diff --git a/sys/compat/linuxkpi/common/include/linux/dcache.h 
b/sys/compat/linuxkpi/common/include/linux/dcache.h
index 9f9943a18dc6..512a29ec046a 100644
--- a/sys/compat/linuxkpi/common/include/linux/dcache.h
+++ b/sys/compat/linuxkpi/common/include/linux/dcache.h
@@ -29,8 +29,9 @@
 #ifndef _LINUXKPI_LINUX_DCACHE_H
 #define        _LINUXKPI_LINUX_DCACHE_H
 
-struct vnode;
-struct pfs_node;
+#include <sys/vnode.h>
+
+#include <fs/pseudofs/pseudofs.h>
 
 struct dentry {
        struct vnode *d_inode;
diff --git a/sys/compat/linuxkpi/common/include/linux/debugfs.h 
b/sys/compat/linuxkpi/common/include/linux/debugfs.h
index 6a3273cbbf3c..ba1fa009dc62 100644
--- a/sys/compat/linuxkpi/common/include/linux/debugfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/debugfs.h
@@ -31,21 +31,52 @@
 #define _LINUXKPI_LINUX_DEBUGFS_H_
 
 #include <linux/fs.h>
+#include <linux/module.h>
 #include <linux/seq_file.h>
-
 #include <linux/types.h>
 
-void debugfs_remove(struct dentry *dentry);
+MALLOC_DECLARE(M_DFSINT);
+
+struct debugfs_reg32 {
+       char *name;
+       unsigned long offset;
+};
+
+struct debugfs_regset32 {
+       const struct debugfs_reg32 *regs;
+       int nregs;
+};
 
 struct dentry *debugfs_create_file(const char *name, umode_t mode,
-                                  struct dentry *parent, void *data,
-                                  const struct file_operations *fops);
+    struct dentry *parent, void *data,
+    const struct file_operations *fops);
+
+struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
+    struct dentry *parent, void *data,
+    const struct file_operations *fops);
+
+struct dentry *debugfs_create_mode_unsafe(const char *name, umode_t mode,
+    struct dentry *parent, void *data,
+    const struct file_operations *fops,
+    const struct file_operations *fops_ro,
+    const struct file_operations *fops_wo);
 
 struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
 
 struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
-                                     const char *dest);
+    const char *dest);
+
+void debugfs_remove(struct dentry *dentry);
 
 void debugfs_remove_recursive(struct dentry *dentry);
 
-#endif
+#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \
+       DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt)
+
+void debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent,
+    bool *value);
+
+void debugfs_create_ulong(const char *name, umode_t mode, struct dentry 
*parent,
+    unsigned long *value);
+
+#endif /* _LINUXKPI_LINUX_DEBUGFS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h 
b/sys/compat/linuxkpi/common/include/linux/fs.h
index a21c140d5a9b..55c2ee3e00c6 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -45,6 +45,8 @@
 #include <linux/dcache.h>
 #include <linux/capability.h>
 #include <linux/wait_bit.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
 
 struct module;
 struct kiocb;
@@ -250,6 +252,7 @@ nonseekable_open(struct inode *inode, struct file *filp)
 static inline int
 simple_open(struct inode *inode, struct file *filp)
 {
+       filp->private_data = inode->i_private;
        return 0;
 }
 
@@ -297,6 +300,12 @@ no_llseek(struct file *file, loff_t offset, int whence)
        return (-ESPIPE);
 }
 
+static inline loff_t
+default_llseek(struct file *file, loff_t offset, int whence)
+{
+       return (no_llseek(file, offset, whence));
+}
+
 static inline loff_t
 noop_llseek(struct linux_file *file, loff_t offset, int whence)
 {
@@ -318,4 +327,73 @@ call_mmap(struct linux_file *file, struct vm_area_struct 
*vma)
        return (file->f_op->mmap(file, vma));
 }
 
+static inline void
+i_size_write(struct inode *inode, loff_t i_size)
+{
+}
+
+/*
+ * simple_read_from_buffer: copy data from kernel-space origin
+ * buffer into user-space destination buffer
+ *
+ * @dest: destination buffer
+ * @read_size: number of bytes to be transferred
+ * @ppos: starting transfer position pointer
+ * @orig: origin buffer
+ * @buf_size: size of destination and origin buffers
+ *
+ * Return value:
+ * On success, total bytes copied with *ppos incremented accordingly.
+ * On failure, negative value.
+ */
+static inline ssize_t
+simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos,
+    void *orig, size_t buf_size)
+{
+       void *read_pos = ((char *) orig) + *ppos;
+       size_t buf_remain = buf_size - *ppos;
+       ssize_t num_read;
+
+       if (buf_remain < 0 || buf_remain > buf_size)
+               return -EINVAL;
+
+       if (read_size > buf_remain)
+               read_size = buf_remain;
+
+       /* copy_to_user returns number of bytes NOT read */
+       num_read = read_size - copy_to_user(dest, read_pos, read_size);
+       if (num_read == 0)
+               return -EFAULT;
+       *ppos += num_read;
+
+       return (num_read);
+}
+
+MALLOC_DECLARE(M_LSATTR);
+
+#define DEFINE_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt)           \
+static inline int                                                      \
+__fops ## _open(struct inode *inode, struct file *filp)                        
\
+{                                                                      \
+       return (simple_attr_open(inode, filp, __get, __set, __fmt));    \
+}                                                                      \
+static const struct file_operations __fops = {                         \
+       .owner   = THIS_MODULE,                                         \
+       .open    = __fops ## _open,                                     \
+       .release = simple_attr_release,                                 \
+       .read    = simple_attr_read,                                    \
+       .write   = simple_attr_write,                                   \
+       .llseek  = no_llseek                                            \
+}
+
+int simple_attr_open(struct inode *inode, struct file *filp,
+    int (*get)(void *, uint64_t *), int (*set)(void *, uint64_t),
+    const char *fmt);
+
+int simple_attr_release(struct inode *inode, struct file *filp);
+
+ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, 
loff_t *ppos);
+
+ssize_t simple_attr_write(struct file *filp, const char *buf, size_t 
write_size, loff_t *ppos);
+
 #endif /* _LINUXKPI_LINUX_FS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h 
b/sys/compat/linuxkpi/common/include/linux/seq_file.h
index 9ab5ecce7768..f40b7984aa50 100644
--- a/sys/compat/linuxkpi/common/include/linux/seq_file.h
+++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h
@@ -32,11 +32,12 @@
 
 #include <linux/types.h>
 #include <linux/fs.h>
-#include <sys/sbuf.h>
 
 #undef file
 #define inode vnode
 
+MALLOC_DECLARE(M_LSEQ);
+
 #define        DEFINE_SHOW_ATTRIBUTE(__name)                                   
\
 static int __name ## _open(struct inode *inode, struct linux_file *file)       
\
 {                                                                      \
@@ -51,11 +52,8 @@ static const struct file_operations __name ## _fops = {      
                \
        .release        = single_release,                               \
 }
 
-struct seq_operations;
-
 struct seq_file {
-       struct sbuf     *buf;
-
+       struct sbuf *buf;
        const struct seq_operations *op;
        const struct linux_file *file;
        void *private;
@@ -78,7 +76,8 @@ off_t seq_lseek(struct linux_file *file, off_t offset, int 
whence);
 int single_open(struct linux_file *, int (*)(struct seq_file *, void *), void 
*);
 int single_release(struct inode *, struct linux_file *);
 
-#define seq_printf(m, fmt, ...) sbuf_printf((m)->buf, (fmt), ##__VA_ARGS__)
+void seq_vprintf(struct seq_file *m, const char *fmt, va_list args);
+void seq_printf(struct seq_file *m, const char *fmt, ...);
 
 #define seq_puts(m, str)       sbuf_printf((m)->buf, str)
 #define seq_putc(m, str)       sbuf_putc((m)->buf, str)
diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c 
b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index ed23bf8d010f..4099c9b4d062 100644
--- a/sys/compat/linuxkpi/common/src/linux_seq_file.c
+++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c
@@ -45,16 +45,35 @@ MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file");
 ssize_t
 seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
 {
-       struct seq_file *m = f->private_data;
+       struct seq_file *m;
+       struct sbuf *sbuf;
        void *p;
-       int rc;
-       off_t pos = 0;
+       ssize_t rc;
+
+       m = f->private_data;
+       sbuf = m->buf;
 
-       p = m->op->start(m, &pos);
+       p = m->op->start(m, ppos);
        rc = m->op->show(m, p);
        if (rc)
                return (rc);
-       return (size);
+
+       rc = sbuf_finish(sbuf);
+       if (rc)
+               return (rc);
+
+       rc = sbuf_len(sbuf);
+       if (*ppos >= rc || size < 1)
+               return (-EINVAL);
+
+       size = min(rc - *ppos, size);
+       rc = strscpy(ubuf, sbuf_data(sbuf) + *ppos, size);
+
+       /* add 1 for null terminator */
+       if (rc > 0)
+               rc += 1;
+
+       return (rc);
 }
 
 int
@@ -101,15 +120,13 @@ seq_open(struct linux_file *f, const struct 
seq_operations *op)
 {
        struct seq_file *p;
 
-       if (f->private_data != NULL)
-               log(LOG_WARNING, "%s private_data not NULL", __func__);
-
        if ((p = malloc(sizeof(*p), M_LSEQ, M_NOWAIT|M_ZERO)) == NULL)
                return (-ENOMEM);
 
-       f->private_data = p;
-       p->op = op;
+       p->buf = sbuf_new_auto();
        p->file = f;
+       p->op = op;
+       f->private_data = (void *) p;
        return (0);
 }
 
@@ -138,9 +155,14 @@ int
 seq_release(struct inode *inode __unused, struct linux_file *file)
 {
        struct seq_file *m;
+       struct sbuf *s;
 
        m = file->private_data;
+       s = m->buf;
+
+       sbuf_delete(s);
        free(m, M_LSEQ);
+
        return (0);
 }
 
@@ -160,3 +182,19 @@ single_release(struct vnode *v, struct linux_file *f)
        free(__DECONST(void *, op), M_LSEQ);
        return (rc);
 }
+
+void
+seq_vprintf(struct seq_file *m, const char *fmt, va_list args)
+{
+       sbuf_vprintf(m->buf, fmt, args);
+}
+
+void
+seq_printf(struct seq_file *m, const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       seq_vprintf(m, fmt, args);
+       va_end(args);
+}
diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c 
b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
new file mode 100644
index 000000000000..5cc0c0984755
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
@@ -0,0 +1,191 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022, Jake Freeland <jf...@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <linux/fs.h>
+
+MALLOC_DEFINE(M_LSATTR, "simple_attr", "Linux Simple Attribute File");
+
+struct simple_attr {
+       int (*get)(void *, uint64_t *);
+       int (*set)(void *, uint64_t);
+       void *data;
+       const char *fmt;
+       struct mutex mutex;
+};
+
+/*
+ * simple_attr_open: open and populate simple attribute data
+ *
+ * @inode: file inode
+ * @filp: file pointer
+ * @get: ->get() for reading file data
+ * @set: ->set() for writing file data
+ * @fmt: format specifier for data returned by @get
+ *
+ * Memory allocate a simple_attr and appropriately initialize its members.
+ * The simple_attr must be stored in filp->private_data.
+ * Simple attr files do not support seeking. Open the file as nonseekable.
+ *
+ * Return value: simple attribute file descriptor
+ */
+int
+simple_attr_open(struct inode *inode, struct file *filp,
+    int (*get)(void *, uint64_t *), int (*set)(void *, uint64_t),
+    const char *fmt)
+{
+       struct simple_attr *sattr;
+       sattr = malloc(sizeof(*sattr), M_LSATTR, M_ZERO | M_NOWAIT);
+       if (sattr == NULL)
+               return (-ENOMEM);
+
+       sattr->get = get;
+       sattr->set = set;
+       sattr->data = inode->i_private;
+       sattr->fmt = fmt;
+       mutex_init(&sattr->mutex);
+
+       filp->private_data = (void *) sattr;
+
+       return (nonseekable_open(inode, filp));
+}
+
+int
+simple_attr_release(struct inode *inode, struct file *filp)
+{
+       free(filp->private_data, M_LSATTR);
+       return (0);
+}
+
+/*
+ * simple_attr_read: read simple attr data and transfer into buffer
+ *
+ * @filp: file pointer
+ * @buf: kernel space buffer
+ * @read_size: number of bytes to be transferred
+ * @ppos: starting pointer position for transfer
+ *
+ * The simple_attr structure is stored in filp->private_data.
+ * ->get() retrieves raw file data.
+ * The ->fmt specifier can format this data to be human readable.
+ * This output is then transferred into the @buf buffer.
+ *
+ * Return value:
+ * On success, number of bytes transferred
+ * On failure, negative signed ERRNO
+ */
+ssize_t
+simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos)
+{
+       struct simple_attr *sattr;
+       uint64_t data;
+       ssize_t ret;
+       char prebuf[24];
+
+       sattr = filp->private_data;
+
+       if (sattr->get == NULL)
+               return (-EFAULT);
+
+       mutex_lock(&sattr->mutex);
+
+       ret = sattr->get(sattr->data, &data);
+       if (ret)
+               goto unlock;
+
+       scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data);
+
+       ret = strlen(prebuf) + 1;
+       if (*ppos >= ret || read_size < 1) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       read_size = min(ret - *ppos, read_size);
+       ret = strscpy(buf, prebuf + *ppos, read_size);
+
+       /* add 1 for null terminator */
+       if (ret > 0)
+               ret += 1;
+
+unlock:
+       mutex_unlock(&sattr->mutex);
+       return (ret);
+}
+
+/*
+ * simple_attr_write: write contents of buffer into simple attribute file
+ *
+ * @filp: file pointer
+ * @buf: kernel space buffer
+ * @write_size: number bytes to be transferred
+ * @ppos: starting pointer position for transfer
+ *
+ * The simple_attr structure is stored in filp->private_data.
+ * Convert the @buf string to unsigned long long.
+ * ->set() writes unsigned long long data into the simple attr file.
+ *
+ * Return value:
+ * On success, number of bytes written to simple attr
+ * On failure, negative signed ERRNO
+ */
+ssize_t
+simple_attr_write(struct file *filp, const char *buf, size_t write_size, 
loff_t *ppos)
+{
+       struct simple_attr *sattr;
+       unsigned long long data;
+       size_t bufsize;
+       ssize_t ret;
+
+       sattr = filp->private_data;
+       bufsize = strlen(buf) + 1;
+
+       if (sattr->set == NULL)
+               return (-EFAULT);
+
+       if (*ppos >= bufsize || write_size < 1)
+               return (-EINVAL);
+
+       mutex_lock(&sattr->mutex);
+
+       ret = kstrtoull(buf + *ppos, 0, &data);
+       if (ret)
+               goto unlock;
+
+       ret = sattr->set(sattr->data, data);
+       if (ret)
+               goto unlock;
+
+       ret = bufsize - *ppos;
+
+unlock:
+       mutex_unlock(&sattr->mutex);
+       return (ret);
+}
diff --git a/sys/conf/files b/sys/conf/files
index 4397dcba280a..725e140dca19 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4607,9 +4607,10 @@ compat/linuxkpi/common/src/linux_xarray.c        
optional compat_linuxkpi \
        compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/lkpi_iic_if.m       optional compat_linuxkpi
 
-compat/linuxkpi/common/src/linux_seq_file.c            optional 
compat_linuxkpi | lindebugfs \
+compat/linuxkpi/common/src/linux_seq_file.c    optional compat_linuxkpi | 
lindebugfs \
+       compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/linux_simple_attr.c optional compat_linuxkpi | 
lindebugfs \
        compile-with "${LINUXKPI_C}"
-
 compat/lindebugfs/lindebugfs.c                 optional lindebugfs \
        compile-with "${LINUXKPI_C}"
 
diff --git a/sys/modules/linuxkpi/Makefile b/sys/modules/linuxkpi/Makefile
index 21663a078027..aa2c452db03a 100644
--- a/sys/modules/linuxkpi/Makefile
+++ b/sys/modules/linuxkpi/Makefile
@@ -22,10 +22,11 @@ SRCS=       linux_compat.c \
        linux_pci.c \
        linux_radix.c \
        linux_rcu.c \
-       linux_seq_file.c \
        linux_schedule.c \
+       linux_seq_file.c \
        linux_shmemfs.c \
        linux_shrinker.c \
+       linux_simple_attr.c \
        linux_skbuff.c \
        linux_slab.c \
        linux_tasklet.c \

Reply via email to