diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile --- a/share/man/man5/Makefile +++ b/share/man/man5/Makefile @@ -32,6 +32,7 @@ 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 --- /dev/null +++ b/share/man/man5/lindebugfs.5 @@ -0,0 +1,95 @@ +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" Copyright (c) 2022, Jake Freeland +.\" +.\" 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 debugfs 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 --- a/sys/compat/lindebugfs/lindebugfs.c +++ b/sys/compat/lindebugfs/lindebugfs.c @@ -69,9 +69,9 @@ #include #include -#include -#include #include +#include +#include MALLOC_DEFINE(M_DFSINT, "debugfsint", "Linux debugfs internal"); @@ -118,27 +118,20 @@ { 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; + + /* seq_file requires sbuf for r/w */ + lf.private_data = (void *) sb; + rc = d->dm_fops->open(&vn, &lf); if (rc < 0) { #ifdef INVARIANTS @@ -146,19 +139,23 @@ #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) { + buf = (char *) malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT); + if (buf == NULL) + return (ENOMEM); + + 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 +182,8 @@ 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 +215,43 @@ 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 +281,7 @@ struct dentry * debugfs_create_symlink(const char *name, struct dentry *parent, - const char *dest) + const char *dest) { struct dentry_meta *dm; struct dentry *dnode; @@ -299,6 +333,72 @@ pfs_destroy(dnode->d_pfs_node); } +static int +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); + return; +} + +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); + return; +} + static int debugfs_init(PFS_INIT_ARGS) { @@ -317,8 +417,13 @@ } #ifdef PR_ALLOW_MOUNT_LINSYSFS -PSEUDOFS(debugfs, 1, PR_ALLOW_MOUNT_LINSYSFS); +PSEUDOFS(lindebugfs, 1, PR_ALLOW_MOUNT_LINSYSFS); #else -PSEUDOFS(debugfs, 1, VFCF_JAIL); +PSEUDOFS(lindebugfs, 1, VFCF_JAIL); #endif + +/* Module specifications */ +static moduledata_t lindebugfs_data = { "lindebugfs", NULL, NULL }; +DECLARE_MODULE(lindebugfs, lindebugfs_data, SI_SUB_EXEC, SI_ORDER_MIDDLE); +MODULE_VERSION(lindebugfs, 1); 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 --- 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 + +#include 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 --- 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 +#include #include - #include -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 --- a/sys/compat/linuxkpi/common/include/linux/fs.h +++ b/sys/compat/linuxkpi/common/include/linux/fs.h @@ -45,6 +45,8 @@ #include #include #include +#include +#include struct module; struct kiocb; @@ -250,6 +252,7 @@ static inline int simple_open(struct inode *inode, struct file *filp) { + filp->private_data = inode->i_private; return 0; } @@ -296,6 +299,7 @@ return (-ESPIPE); } +#define default_llseek no_llseek static inline loff_t noop_llseek(struct linux_file *file, loff_t offset, int whence) @@ -318,4 +322,75 @@ return (file->f_op->mmap(file, vma)); } +static inline void +i_size_write(struct inode *inode, loff_t i_size) +{ + /* inode->i_size = i_size; */ + return; +} + +/* + * 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, const 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 --- 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 #include -#include #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 @@ .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 @@ 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 --- a/sys/compat/linuxkpi/common/src/linux_seq_file.c +++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c @@ -48,9 +48,8 @@ struct seq_file *m = f->private_data; void *p; int rc; - off_t pos = 0; - p = m->op->start(m, &pos); + p = m->op->start(m, ppos); rc = m->op->show(m, p); if (rc) return (rc); @@ -101,15 +100,20 @@ { 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; + /* + * This seq_file's ->buf is passed in through f->private_data. + * A clean seq_file implementation is needed to remove the + * FreeBSD sbuf dependency. + */ + if (f->private_data != NULL) + p->buf = (struct sbuf *) f->private_data; + p->op = op; p->file = f; + f->private_data = (void *) p; return (0); } @@ -160,3 +164,22 @@ 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); + return; +} + +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); + + return; +} 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 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c @@ -0,0 +1,193 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022, Jake Freeland + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +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)); +} + +/* + * simple_attr_release: release file's simple attribute data + * + * @inode: file inode + * @filp: file pointer + * + * Return value: 0 + */ +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, const 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); + + if (*ppos > sizeof(prebuf)) + return (-EINVAL); + + mutex_lock(&sattr->mutex); + + ret = sattr->get(sattr->data, &data); + if (ret) + goto unlock; + + scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data); + read_size = min((strlen(prebuf) + 1) - *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; + ssize_t ret; + + sattr = filp->private_data; + + if (sattr->set == NULL) + return (-EFAULT); + + if (*ppos > sizeof(data)) + 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 = strlen(buf) - *ppos; + +unlock: + mutex_unlock(&sattr->mutex); + return (ret); +} diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -4608,9 +4608,10 @@ 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 --- a/sys/modules/linuxkpi/Makefile +++ b/sys/modules/linuxkpi/Makefile @@ -22,10 +22,11 @@ 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 \