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
					
				| @ -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, | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user