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:
Jake Freeland 2022-09-20 19:41:10 +02:00 committed by Emmanuel Vadot
parent a11195df34
commit f697b9432d
11 changed files with 593 additions and 66 deletions

View File

@ -32,6 +32,7 @@ MAN= acct.5 \
hosts.lpd.5 \
intro.5 \
libmap.conf.5 \
lindebugfs.5 \
link.5 \
linprocfs.5 \
linsysfs.5 \

View 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.

View File

@ -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);

View File

@ -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;

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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)

View File

@ -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);
}

View 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);
}

View File

@ -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}"

View File

@ -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 \