mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: Nicolai Stange <nicstange@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									49d200deaa
								
							
						
					
					
						commit
						c646880814
					
				
					 3 changed files with 82 additions and 0 deletions
				
			
		| 
						 | 
					@ -284,6 +284,34 @@ const struct file_operations debugfs_full_proxy_file_operations = {
 | 
				
			||||||
	.open = full_proxy_open,
 | 
						.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,
 | 
					static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
 | 
				
			||||||
					  struct dentry *parent, void *value,
 | 
										  struct dentry *parent, void *value,
 | 
				
			||||||
				          const struct file_operations *fops,
 | 
									          const struct file_operations *fops,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -369,6 +369,33 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(debugfs_create_file);
 | 
					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 *debugfs_create_file_unsafe(const char *name, umode_t mode,
 | 
				
			||||||
				   struct dentry *parent, void *data,
 | 
									   struct dentry *parent, void *data,
 | 
				
			||||||
				   const struct file_operations *fops)
 | 
									   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,
 | 
										&debugfs_noop_file_operations,
 | 
				
			||||||
				fops);
 | 
									fops);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * debugfs_create_file_size - create a file in the debugfs filesystem
 | 
					 * debugfs_create_file_size - create a file in the debugfs filesystem
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,6 +50,9 @@ extern struct srcu_struct debugfs_srcu;
 | 
				
			||||||
struct dentry *debugfs_create_file(const char *name, umode_t mode,
 | 
					struct dentry *debugfs_create_file(const char *name, umode_t mode,
 | 
				
			||||||
				   struct dentry *parent, void *data,
 | 
									   struct dentry *parent, void *data,
 | 
				
			||||||
				   const struct file_operations *fops);
 | 
									   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 *debugfs_create_file_size(const char *name, umode_t mode,
 | 
				
			||||||
					struct dentry *parent, void *data,
 | 
										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);
 | 
					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 *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
 | 
				
			||||||
                struct dentry *new_dir, const char *new_name);
 | 
					                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)
 | 
						__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,
 | 
					static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
 | 
				
			||||||
                struct dentry *new_dir, char *new_name)
 | 
					                struct dentry *new_dir, char *new_name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue