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)
This commit is contained in:
parent
a11195df34
commit
f697b9432d
@ -32,6 +32,7 @@ MAN= acct.5 \
|
||||
hosts.lpd.5 \
|
||||
intro.5 \
|
||||
libmap.conf.5 \
|
||||
lindebugfs.5 \
|
||||
link.5 \
|
||||
linprocfs.5 \
|
||||
linsysfs.5 \
|
||||
|
95
share/man/man5/lindebugfs.5
Normal file
95
share/man/man5/lindebugfs.5
Normal file
@ -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.
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
p = m->op->start(m, &pos);
|
||||
m = f->private_data;
|
||||
sbuf = m->buf;
|
||||
|
||||
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);
|
||||
}
|
||||
|
191
sys/compat/linuxkpi/common/src/linux_simple_attr.c
Normal file
191
sys/compat/linuxkpi/common/src/linux_simple_attr.c
Normal file
@ -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);
|
||||
}
|
@ -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}"
|
||||
|
||||
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user