Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142701655
D35883.id108422.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D35883.id108422.diff
View Options
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 <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");
@@ -117,48 +117,40 @@
debugfs_fill(PFS_FILL_ARGS)
{
struct dentry_meta *d;
- struct linux_file lf = {};
- struct seq_file *sf;
+ struct linux_file lf;
struct vnode vn;
- void *buf;
+ char buf[sb->s_size];
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
printf("%s:%d open failed with %d\n", __FUNCTION__, __LINE__, rc);
#endif
- return (-rc);
+ 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[0] = '\0';
+ rc = d->dm_fops->read(&lf, buf, sizeof(buf), &off);
+ if (strlen(buf) > 0)
+ sbuf_bcpy(sb, buf, strlen(buf));
+ } 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
@@ -168,7 +160,7 @@
#ifdef INVARIANTS
printf("%s:%d read/write failed with %d\n", __FUNCTION__, __LINE__, rc);
#endif
- return (-rc);
+ return (rc);
}
return (0);
}
@@ -321,4 +313,9 @@
#else
PSEUDOFS(debugfs, 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 <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
--- a/sys/compat/linuxkpi/common/include/linux/debugfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/debugfs.h
@@ -31,10 +31,22 @@
#define _LINUXKPI_LINUX_DEBUGFS_H_
#include <linux/fs.h>
+#include <linux/module.h>
#include <linux/seq_file.h>
-
#include <linux/types.h>
+MALLOC_DECLARE(M_DFSINT);
+
+struct debugfs_reg32 {
+ char *name;
+ unsigned long offset;
+};
+
+struct debugfs_regset32 {
+ const struct debugfs_reg32 *regs;
+ int nregs;
+};
+
void debugfs_remove(struct dentry *dentry);
struct dentry *debugfs_create_file(const char *name, umode_t mode,
@@ -48,4 +60,287 @@
void debugfs_remove_recursive(struct dentry *dentry);
-#endif
+/*
+ * debugfs_create_file_unsafe: create a new debugfs file with given file
+ * attributes
+ *
+ * @name: file name
+ * @mode: file permissions
+ * @parent: parent dentry
+ * @data: file data
+ * @fops: file operations
+ *
+ * Create a new debugfs file with given arguments.
+ *
+ * NOTE: the _unsafe moniker denotes that @fops are not protected against debugfs
+ * file removals. This behavior is not implemented in FreeBSD at this time.
+ *
+ * Return: new file dentry pointer
+ */
+static inline struct dentry *
+debugfs_create_file_unsafe(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
+{
+ /*
+ * redirect to std debugfs_create_file() because safety has not been
+ * inegrated into lindebugfs
+ */
+ return debugfs_create_file(name, mode, parent, data, fops);
+}
+
+/*
+ * debugfs_create_mode_unsafe: create a new debugfs file with file operations
+ * dependent on @mode
+ *
+ * @name: file name
+ * @mode: file permissions
+ * @parent: parent dentry
+ * @data: file data
+ * @fops: file operations
+ * @fops_ro: read only file operations
+ * @fops_wo: write only file operations
+ *
+ * Create a new debugfs file with given arguments. Care should be taken
+ * when &ing S_IWUGO and S_IRUGO with @mode to see if r/w bits are set.
+ * Use the correct file operations based on those r/w bits. If both r/w
+ * bits are set, use @fops as file operations.
+ *
+ * Return: new file dentry pointer
+ */
+static inline 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);
+}
+
+#define DEFINE_DEBUGFS_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 = debugfs_attr_read, \
+ .write = debugfs_attr_write, \
+ .llseek = no_llseek \
+}
+
+/*
+ * debugfs_attr_read: reads data from a debugfs file attribute into
+ * a user space buffer
+ *
+ * @filp: file pointer
+ * @buf: user space buffer
+ * @read_size: number bytes to be transferred
+ * @ppos: starting pointer position for transfer
+ *
+ * Marks file access using using a @filp dentry, calls a simple
+ * attribute read, and unmarks file access using the @filp dentry.
+ *
+ * debugfs file safety is not implemented in FreeBSD so this function
+ * performs a simple attribute read.
+ *
+ * Return value:
+ * On success, number of bytes read from kernel-space buffer
+ * On failure, negative signed ERRNO
+ */
+static inline ssize_t
+debugfs_attr_read(struct file *filp, char __user *buf,
+ size_t read_size, loff_t *ppos)
+{
+ return simple_attr_read(filp, buf, read_size, ppos);
+}
+
+/*
+ * debugfs_attr_write: writes data from user space buffer into a
+ * debugfs file attribute
+ *
+ * @filp: file pointer
+ * @buf: user space buffer
+ * @write_size: number bytes to be transferred
+ * @ppos: starting pointer position for transfer
+ *
+ * Marks file access using using a @filp dentry, calls a simple
+ * attribute write, and unmarks file access using the @filp dentry.
+ *
+ * debugfs file safety is not implemented in FreeBSD so this function
+ * performs a simple attribute write.
+ *
+ * Return value:
+ * On success, number of bytes written to simple_attr .data member
+ * On failure, negative signed ERRNO
+ */
+static inline ssize_t
+debugfs_attr_write(struct file *filp, const char __user *buf,
+ size_t write_size, loff_t *ppos)
+{
+ return simple_attr_write(filp, buf, write_size, ppos);
+}
+
+/*
+ * debugfs_read_file_bool: read debugfs boolean file and transfer
+ * contents into user space buffer
+ *
+ * @filp: debugfs boolean file pointer
+ * @user_buf: user space buffer
+ * @read_size: number of bytes to be read
+ * @ppos: pointer to starting read position in file
+ *
+ * Boolean value is stored in filp->private_data. If this is 'Y',
+ * place "Y\n" in user buffer. If 'N', place "N\n" into user buffer.
+ * Depending on lindebugfs's file access implementaion, debugfs_file_get
+ * and debugfs_file_put may be necessary to mark and unmark file
+ * access to the filesystem.
+ *
+ * Return value: number of bytes read from debugfs boolean file
+ */
+static inline ssize_t
+debugfs_read_file_bool(struct file *filp, char __user *user_buf,
+ size_t read_size, loff_t *ppos)
+{
+ bool *value;
+ char *buf;
+
+ value = filp->private_data;
+ if (*value)
+ buf = "Y\n";
+ else
+ buf = "N\n";
+
+ return simple_read_from_buffer(user_buf, read_size, ppos, buf, 2);
+}
+
+/*
+ * debugfs_write_file_bool: write contents of user space buffer into
+ * debugfs boolean file
+ *
+ * @filp: debugfs boolean file pointer
+ * @user_buf: user space buffer
+ * @write_size: number of bytes to be read
+ * @ppos: pointer to starting write position in file
+ *
+ * Transfer contents of @user_buf into kernel-space boolean variable using
+ * kstrtobool_from_user. Place this boolean value in filp->private_data.
+ * Depending on lindebugfs's file access implementaion, debugfs_file_get
+ * and debugfs_file_put may be necessary to mark and unmark file access to
+ * the filesystem.
+ *
+ * Return value:
+ * Number of bytes written into debugfs boolean file.
+ */
+static inline ssize_t
+debugfs_write_file_bool(struct file *filp, const char __user *user_buf,
+ size_t write_size, loff_t *ppos)
+{
+ ssize_t ret;
+ bool *dest = filp->private_data;
+ bool value;
+
+ ret = kstrtobool_from_user(user_buf, write_size, &value);
+ if (ret)
+ return ret;
+
+ *dest = value;
+
+ return write_size;
+}
+
+static const struct file_operations fops_bool = {
+ .read = debugfs_read_file_bool,
+ .write = debugfs_write_file_bool,
+ .open = simple_open,
+ .llseek = no_llseek
+};
+
+static const struct file_operations fops_bool_ro = {
+ .read = debugfs_read_file_bool,
+ .open = simple_open,
+ .llseek = no_llseek
+};
+
+static const struct file_operations fops_bool_wo = {
+ .write = debugfs_write_file_bool,
+ .open = simple_open,
+ .llseek = no_llseek
+};
+
+/*
+ * debugfs_create_bool: create a debugfs file that stores boolean values.
+ *
+ * @name: pointer to the desired new file name
+ * @mode: new file permissions
+ * @parent: dentry pointer to the parent directory of the new file. If NULL,
+ * create at root of debugfs file system
+ * @value: pointer to boolean value that will be written into the new file
+ *
+ * Create a new debugfs file named @name intended to store @value's boolean
+ * value. File attributes can be set through the @mode argument.
+ */
+static inline 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 inline int
+debugfs_ulong_set(void *data, uint64_t value)
+{
+ uint64_t *ludata = data;
+ *ludata = value;
+ return 0;
+}
+
+static inline int
+debugfs_ulong_get(void *data, uint64_t *value)
+{
+ uint64_t *ludata = data;
+ *value = *ludata;
+ 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");
+
+/*
+ * debugfs_create_ulong: create a debugfs file that stores ulong values.
+ *
+ * @name: pointer to the desired new file name
+ * @mode: new file permissions
+ * @parent: dentry pointer to the parent directory of the new file. If NULL,
+ * create at root of debugfs file system
+ * @value: pointer to boolean value that will be written into the new file
+ *
+ * Create a new debugfs file named @name intended to store @value's ulong
+ * value. File attributes can be set through the @mode argument.
+ */
+static inline 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;
+}
+
+#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 <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 @@
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,76 @@
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, char *buf, size_t read_size, loff_t *ppos);
+
+ssize_t simple_attr_write(struct file *filp, const char __user *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 <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 @@
.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/include/linux/string.h b/sys/compat/linuxkpi/common/include/linux/string.h
--- a/sys/compat/linuxkpi/common/include/linux/string.h
+++ b/sys/compat/linuxkpi/common/include/linux/string.h
@@ -97,6 +97,20 @@
return (dst);
}
+static inline char *
+strndup_user(const char __user *ustr, long n)
+{
+ char *kstr;
+
+ kstr = memdup_user(ustr, n);
+ if (IS_ERR(kstr))
+ return kstr;
+
+ kstr[n-1] = '\0';
+
+ return kstr;
+}
+
static inline char *
kstrdup(const char *string, gfp_t gfp)
{
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,195 @@
+/*-
+ * 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);
+}
+
+/*
+ * 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, 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 __user *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
@@ -4604,9 +4604,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 \
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 23, 10:32 AM (9 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27882263
Default Alt Text
D35883.id108422.diff (24 KB)
Attached To
Mode
D35883: LinuxKPI drm-kmod debugfs support
Attached
Detach File
Event Timeline
Log In to Comment