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

Commit 3fd2e0f2 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "debugfs: defer debugfs_fsdata allocation to first usage"

parents 4e2f290b c1ba697d
Loading
Loading
Loading
Loading
+50 −60
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/srcu.h>
#include <asm/poll.h>

#include "internal.h"
@@ -48,59 +47,21 @@ const struct file_operations debugfs_noop_file_operations = {
	.llseek =	noop_llseek,
};

/**
 * debugfs_use_file_start - mark the beginning of file data access
 * @dentry: the dentry object whose data is being accessed.
 * @srcu_idx: a pointer to some memory to store a SRCU index in.
 *
 * Up to a matching call to debugfs_use_file_finish(), any
 * successive call into the file removing functions debugfs_remove()
 * and debugfs_remove_recursive() will block. Since associated private
 * file data may only get freed after a successful return of any of
 * the removal functions, you may safely access it after a successful
 * call to debugfs_use_file_start() without worrying about
 * lifetime issues.
 *
 * If -%EIO is returned, the file has already been removed and thus,
 * it is not safe to access any of its data. If, on the other hand,
 * it is allowed to access the file data, zero is returned.
 *
 * Regardless of the return code, any call to
 * debugfs_use_file_start() must be followed by a matching call
 * to debugfs_use_file_finish().
 */
int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
	__acquires(&debugfs_srcu)
{
	*srcu_idx = srcu_read_lock(&debugfs_srcu);
	barrier();
	if (d_unlinked(dentry))
		return -EIO;
	return 0;
}
EXPORT_SYMBOL_GPL(debugfs_use_file_start);

/**
 * debugfs_use_file_finish - mark the end of file data access
 * @srcu_idx: the SRCU index "created" by a former call to
 *            debugfs_use_file_start().
 *
 * Allow any ongoing concurrent call into debugfs_remove() or
 * debugfs_remove_recursive() blocked by a former call to
 * debugfs_use_file_start() to proceed and return to its caller.
 */
void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
{
	srcu_read_unlock(&debugfs_srcu, srcu_idx);
}
EXPORT_SYMBOL_GPL(debugfs_use_file_finish);

#define F_DENTRY(filp) ((filp)->f_path.dentry)

const struct file_operations *debugfs_real_fops(const struct file *filp)
{
	struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;

	if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
		/*
		 * Urgh, we've been called w/o a protecting
		 * debugfs_file_get().
		 */
		WARN_ON(1);
		return NULL;
	}

	return fsd->real_fops;
}
EXPORT_SYMBOL_GPL(debugfs_real_fops);
@@ -122,9 +83,35 @@ EXPORT_SYMBOL_GPL(debugfs_real_fops);
 */
int debugfs_file_get(struct dentry *dentry)
{
	struct debugfs_fsdata *fsd = dentry->d_fsdata;
	struct debugfs_fsdata *fsd;
	void *d_fsd;

	d_fsd = READ_ONCE(dentry->d_fsdata);
	if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
		fsd = d_fsd;
	} else {
		fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
		if (!fsd)
			return -ENOMEM;

		fsd->real_fops = (void *)((unsigned long)d_fsd &
					~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
		refcount_set(&fsd->active_users, 1);
		init_completion(&fsd->active_users_drained);
		if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
			kfree(fsd);
			fsd = READ_ONCE(dentry->d_fsdata);
		}
	}

	/* Avoid starvation of removers. */
	/*
	 * In case of a successful cmpxchg() above, this check is
	 * strictly necessary and must follow it, see the comment in
	 * __debugfs_remove_file().
	 * OTOH, if the cmpxchg() hasn't been executed or wasn't
	 * successful, this serves the purpose of not starving
	 * removers.
	 */
	if (d_unlinked(dentry))
		return -EIO;

@@ -146,7 +133,7 @@ EXPORT_SYMBOL_GPL(debugfs_file_get);
 */
void debugfs_file_put(struct dentry *dentry)
{
	struct debugfs_fsdata *fsd = dentry->d_fsdata;
	struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);

	if (refcount_dec_and_test(&fsd->active_users))
		complete(&fsd->active_users_drained);
@@ -157,10 +144,11 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
{
	struct dentry *dentry = F_DENTRY(filp);
	const struct file_operations *real_fops = NULL;
	int r = 0;
	int r;

	if (debugfs_file_get(dentry))
		return -ENOENT;
	r = debugfs_file_get(dentry);
	if (r)
		return r == -EIO ? -ENOENT : r;

	real_fops = debugfs_real_fops(filp);
	real_fops = fops_get(real_fops);
@@ -192,13 +180,13 @@ const struct file_operations debugfs_open_proxy_file_operations = {
static ret_type full_proxy_ ## name(proto)				\
{									\
	struct dentry *dentry = F_DENTRY(filp);			\
	const struct file_operations *real_fops =			\
		debugfs_real_fops(filp);				\
	const struct file_operations *real_fops;			\
	ret_type r;							\
									\
	r = debugfs_file_get(dentry);					\
	if (unlikely(r))						\
		return r;						\
	real_fops = debugfs_real_fops(filp);				\
	r = real_fops->name(args);					\
	debugfs_file_put(dentry);					\
	return r;							\
@@ -225,13 +213,14 @@ FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
static unsigned int full_proxy_poll(struct file *filp,
				struct poll_table_struct *wait)
{
	const struct file_operations *real_fops = debugfs_real_fops(filp);
	struct dentry *dentry = F_DENTRY(filp);
	unsigned int r = 0;
	const struct file_operations *real_fops;

	if (debugfs_file_get(dentry))
		return POLLHUP;

	real_fops = debugfs_real_fops(filp);
	r = real_fops->poll(filp, wait);
	debugfs_file_put(dentry);
	return r;
@@ -280,10 +269,11 @@ static int full_proxy_open(struct inode *inode, struct file *filp)
	struct dentry *dentry = F_DENTRY(filp);
	const struct file_operations *real_fops = NULL;
	struct file_operations *proxy_fops = NULL;
	int r = 0;
	int r;

	if (debugfs_file_get(dentry))
		return -ENOENT;
	r = debugfs_file_get(dentry);
	if (r)
		return r == -EIO ? -ENOENT : r;

	real_fops = debugfs_real_fops(filp);
	real_fops = fops_get(real_fops);
+19 −24
Original line number Diff line number Diff line
@@ -27,14 +27,11 @@
#include <linux/parser.h>
#include <linux/magic.h>
#include <linux/slab.h>
#include <linux/srcu.h>

#include "internal.h"

#define DEBUGFS_DEFAULT_MODE	0700

DEFINE_SRCU(debugfs_srcu);

static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
static bool debugfs_registered;
@@ -187,6 +184,9 @@ static const struct super_operations debugfs_super_operations = {

static void debugfs_release_dentry(struct dentry *dentry)
{
	void *fsd = dentry->d_fsdata;

	if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
		kfree(dentry->d_fsdata);
}

@@ -347,35 +347,25 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
{
	struct dentry *dentry;
	struct inode *inode;
	struct debugfs_fsdata *fsd;

	fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
	if (!fsd)
		return NULL;

	if (!(mode & S_IFMT))
		mode |= S_IFREG;
	BUG_ON(!S_ISREG(mode));
	dentry = start_creating(name, parent);

	if (IS_ERR(dentry)) {
		kfree(fsd);
	if (IS_ERR(dentry))
		return NULL;
	}

	inode = debugfs_get_inode(dentry->d_sb);
	if (unlikely(!inode)) {
		kfree(fsd);
	if (unlikely(!inode))
		return failed_creating(dentry);
	}

	inode->i_mode = mode;
	inode->i_private = data;

	inode->i_fop = proxy_fops;
	fsd->real_fops = real_fops;
	refcount_set(&fsd->active_users, 1);
	dentry->d_fsdata = fsd;
	dentry->d_fsdata = (void *)((unsigned long)real_fops |
				DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);

	d_instantiate(dentry, inode);
	fsnotify_create(d_inode(dentry->d_parent), dentry);
@@ -638,8 +628,17 @@ static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent)

	simple_unlink(d_inode(parent), dentry);
	d_delete(dentry);
	fsd = dentry->d_fsdata;
	init_completion(&fsd->active_users_drained);

	/*
	 * Paired with the closing smp_mb() implied by a successful
	 * cmpxchg() in debugfs_file_get(): either
	 * debugfs_file_get() must see a dead dentry or we must see a
	 * debugfs_fsdata instance at ->d_fsdata here (or both).
	 */
	smp_mb();
	fsd = READ_ONCE(dentry->d_fsdata);
	if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
		return;
	if (!refcount_dec_and_test(&fsd->active_users))
		wait_for_completion(&fsd->active_users_drained);
}
@@ -693,8 +692,6 @@ void debugfs_remove(struct dentry *dentry)
	inode_unlock(d_inode(parent));
	if (!ret)
		simple_release_fs(&debugfs_mount, &debugfs_mount_count);

	synchronize_srcu(&debugfs_srcu);
}
EXPORT_SYMBOL_GPL(debugfs_remove);

@@ -768,8 +765,6 @@ void debugfs_remove_recursive(struct dentry *dentry)
	if (!__debugfs_remove(child, parent))
		simple_release_fs(&debugfs_mount, &debugfs_mount_count);
	inode_unlock(d_inode(parent));

	synchronize_srcu(&debugfs_srcu);
}
EXPORT_SYMBOL_GPL(debugfs_remove_recursive);

+8 −0
Original line number Diff line number Diff line
@@ -25,4 +25,12 @@ struct debugfs_fsdata {
	struct completion active_users_drained;
};

/*
 * A dentry's ->d_fsdata either points to the real fops or to a
 * dynamically allocated debugfs_fsdata instance.
 * In order to distinguish between these two cases, a real fops
 * pointer gets its lowest bit set.
 */
#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)

#endif /* _DEBUGFS_INTERNAL_H_ */
+0 −19
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@

struct device;
struct file_operations;
struct srcu_struct;

struct debugfs_blob_wrapper {
	void *data;
@@ -43,8 +42,6 @@ struct debugfs_regset32 {

extern struct dentry *arch_debugfs_dir;

extern struct srcu_struct debugfs_srcu;

#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)		\
static int __fops ## _open(struct inode *inode, struct file *file)	\
{									\
@@ -90,11 +87,6 @@ struct dentry *debugfs_create_automount(const char *name,
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);

int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
	__acquires(&debugfs_srcu);

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

const struct file_operations *debugfs_real_fops(const struct file *filp);

int debugfs_file_get(struct dentry *dentry);
@@ -227,17 +219,6 @@ static inline void debugfs_remove(struct dentry *dentry)
static inline void debugfs_remove_recursive(struct dentry *dentry)
{ }

static inline int debugfs_use_file_start(const struct dentry *dentry,
					int *srcu_idx)
	__acquires(&debugfs_srcu)
{
	return 0;
}

static inline void debugfs_use_file_finish(int srcu_idx)
	__releases(&debugfs_srcu)
{ }

static inline int debugfs_file_get(struct dentry *dentry)
{
	return 0;
+10 −8
Original line number Diff line number Diff line
/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2015, 2017-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -78,17 +78,18 @@ static ssize_t debug_read_helper(struct file *file, char __user *buff,
	struct dentry *d = file->f_path.dentry;
	char *buffer;
	int bsize;
	int srcu_idx;
	int r;

	r = debugfs_use_file_start(d, &srcu_idx);
	if (!r) {
	r = debugfs_file_get(d);
	if (r)
		return r;

	ilctxt = file->private_data;
	r = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
	}
	debugfs_use_file_finish(srcu_idx);
	if (r)
	if (r) {
		debugfs_file_put(d);
		return r;
	}

	buffer = kmalloc(count, GFP_KERNEL);
	if (!buffer) {
@@ -110,6 +111,7 @@ static ssize_t debug_read_helper(struct file *file, char __user *buff,

done:
	ipc_log_context_put(ilctxt);
	debugfs_file_put(d);
	return bsize;
}

Loading