aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Freeland <jfree@FreeBSD.org>2022-09-20 17:41:10 +0000
committerEmmanuel Vadot <manu@FreeBSD.org>2022-09-20 17:41:10 +0000
commitf697b9432d9c7aa4c5ab5f5445ef5dc1bd40ce00 (patch)
tree92a13addff810ac372eed1bed68bce8403a691a7
parenta11195df34f7b14740447869263d3acf8d72af42 (diff)
downloadsrc-f697b9432d9c7aa4c5ab5f5445ef5dc1bd40ce00.tar.gz
src-f697b9432d9c7aa4c5ab5f5445ef5dc1bd40ce00.zip
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)
-rw-r--r--share/man/man5/Makefile1
-rw-r--r--share/man/man5/lindebugfs.595
-rw-r--r--sys/compat/lindebugfs/lindebugfs.c169
-rw-r--r--sys/compat/linuxkpi/common/include/linux/dcache.h5
-rw-r--r--sys/compat/linuxkpi/common/include/linux/debugfs.h43
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h78
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h11
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_file.c58
-rw-r--r--sys/compat/linuxkpi/common/src/linux_simple_attr.c191
-rw-r--r--sys/conf/files5
-rw-r--r--sys/modules/linuxkpi/Makefile3
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 <jfree@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;
}
@@ -298,6 +301,12 @@ no_llseek(struct file *file, loff_t offset, int whence)
}
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 <jfree@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 \