Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6468808 authored by Nicolai Stange's avatar Nicolai Stange Committed by Greg Kroah-Hartman
Browse files

debugfs: add support for self-protecting attribute file fops



In order to protect them against file removal issues, debugfs_create_file()
creates a lifetime managing proxy around each struct file_operations
handed in.

In cases where this struct file_operations is able to manage file lifetime
by itself already, the proxy created by debugfs is a waste of resources.

The most common class of struct file_operations given to debugfs are those
defined by means of the DEFINE_SIMPLE_ATTRIBUTE() macro.

Introduce a DEFINE_DEBUGFS_ATTRIBUTE() macro to allow any
struct file_operations of this class to be easily made file lifetime aware
and thus, to be operated unproxied.

Specifically, introduce debugfs_attr_read() and debugfs_attr_write()
which wrap simple_attr_read() and simple_attr_write() under the protection
of a debugfs_use_file_start()/debugfs_use_file_finish() pair.

Make DEFINE_DEBUGFS_ATTRIBUTE() set the defined struct file_operations'
->read() and ->write() members to these wrappers.

Export debugfs_create_file_unsafe() in order to allow debugfs users to
create their files in non-proxying operation mode.

Signed-off-by: default avatarNicolai Stange <nicstange@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 49d200de
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -284,6 +284,34 @@ const struct file_operations debugfs_full_proxy_file_operations = {
	.open = full_proxy_open,
};

ssize_t debugfs_attr_read(struct file *file, char __user *buf,
			size_t len, loff_t *ppos)
{
	ssize_t ret;
	int srcu_idx;

	ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
	if (likely(!ret))
		ret = simple_attr_read(file, buf, len, ppos);
	debugfs_use_file_finish(srcu_idx);
	return ret;
}
EXPORT_SYMBOL_GPL(debugfs_attr_read);

ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
			 size_t len, loff_t *ppos)
{
	ssize_t ret;
	int srcu_idx;

	ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
	if (likely(!ret))
		ret = simple_attr_write(file, buf, len, ppos);
	debugfs_use_file_finish(srcu_idx);
	return ret;
}
EXPORT_SYMBOL_GPL(debugfs_attr_write);

static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
					  struct dentry *parent, void *value,
				          const struct file_operations *fops,
+28 −0
Original line number Diff line number Diff line
@@ -369,6 +369,33 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
}
EXPORT_SYMBOL_GPL(debugfs_create_file);

/**
 * debugfs_create_file_unsafe - create a file in the debugfs filesystem
 * @name: a pointer to a string containing the name of the file to create.
 * @mode: the permission that the file should have.
 * @parent: a pointer to the parent dentry for this file.  This should be a
 *          directory dentry if set.  If this parameter is NULL, then the
 *          file will be created in the root of the debugfs filesystem.
 * @data: a pointer to something that the caller will want to get to later
 *        on.  The inode.i_private pointer will point to this value on
 *        the open() call.
 * @fops: a pointer to a struct file_operations that should be used for
 *        this file.
 *
 * debugfs_create_file_unsafe() is completely analogous to
 * debugfs_create_file(), the only difference being that the fops
 * handed it will not get protected against file removals by the
 * debugfs core.
 *
 * It is your responsibility to protect your struct file_operation
 * methods against file removals by means of debugfs_use_file_start()
 * and debugfs_use_file_finish(). ->open() is still protected by
 * debugfs though.
 *
 * Any struct file_operations defined by means of
 * DEFINE_DEBUGFS_ATTRIBUTE() is protected against file removals and
 * thus, may be used here.
 */
struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
				   struct dentry *parent, void *data,
				   const struct file_operations *fops)
@@ -379,6 +406,7 @@ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
					&debugfs_noop_file_operations,
				fops);
}
EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);

/**
 * debugfs_create_file_size - create a file in the debugfs filesystem
+26 −0
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ extern struct srcu_struct debugfs_srcu;
struct dentry *debugfs_create_file(const char *name, umode_t mode,
				   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_file_size(const char *name, umode_t mode,
					struct dentry *parent, void *data,
@@ -74,6 +77,26 @@ int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)

void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);

ssize_t debugfs_attr_read(struct file *file, char __user *buf,
			size_t len, loff_t *ppos);
ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
			size_t len, loff_t *ppos);

#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)		\
static int __fops ## _open(struct inode *inode, struct file *file)	\
{									\
	__simple_attr_check_format(__fmt, 0ull);			\
	return simple_attr_open(inode, file, __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  = generic_file_llseek,				\
}

struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
                struct dentry *new_dir, const char *new_name);

@@ -193,6 +216,9 @@ static inline void debugfs_use_file_finish(int srcu_idx)
	__releases(&debugfs_srcu)
{ }

#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)	\
	static const struct file_operations __fops = { 0 }

static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
                struct dentry *new_dir, char *new_name)
{