git: a8e31ab2e93f - stable/13 - linuxkpi: drm-kmod debugfs support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 29 Nov 2022 10:39:10 UTC
The branch stable/13 has been updated by manu: URL: https://cgit.FreeBSD.org/src/commit/?id=a8e31ab2e93f29858913f384dfc17cdaa77656f3 commit a8e31ab2e93f29858913f384dfc17cdaa77656f3 Author: Jake Freeland <jfree@FreeBSD.org> AuthorDate: 2022-09-20 17:41:10 +0000 Commit: Emmanuel Vadot <manu@FreeBSD.org> CommitDate: 2022-11-29 10:09:45 +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) (cherry picked from commit f697b9432d9c7aa4c5ab5f5445ef5dc1bd40ce00) --- 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 96fc1e052d95..9dbff0820211 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; } @@ -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 4e7582b7acc2..3b15c04503e2 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; @@ -81,7 +79,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 3ac8315a425f..3e82206c0c5a 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); } @@ -161,9 +178,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); } @@ -193,3 +215,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 8d8b27940948..9c8cfcbab9db 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4655,9 +4655,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 57fa81f969d6..2ed6cda98b17 100644 --- a/sys/modules/linuxkpi/Makefile +++ b/sys/modules/linuxkpi/Makefile @@ -21,10 +21,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 \