Loading drivers/infiniband/hw/hfi1/debugfs.c +10 −10 Original line number Diff line number Diff line Loading @@ -67,13 +67,13 @@ static ssize_t hfi1_seq_read( loff_t *ppos) { struct dentry *d = file->f_path.dentry; int srcu_idx; ssize_t r; r = debugfs_use_file_start(d, &srcu_idx); if (likely(!r)) r = debugfs_file_get(d); if (unlikely(r)) return r; r = seq_read(file, buf, size, ppos); debugfs_use_file_finish(srcu_idx); debugfs_file_put(d); return r; } Loading @@ -83,13 +83,13 @@ static loff_t hfi1_seq_lseek( int whence) { struct dentry *d = file->f_path.dentry; int srcu_idx; loff_t r; r = debugfs_use_file_start(d, &srcu_idx); if (likely(!r)) r = debugfs_file_get(d); if (unlikely(r)) return r; r = seq_lseek(file, offset, whence); debugfs_use_file_finish(srcu_idx); debugfs_file_put(d); return r; } Loading fs/debugfs/file.c +125 −85 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -48,66 +47,108 @@ const struct file_operations debugfs_noop_file_operations = { .llseek = noop_llseek, }; #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); /** * debugfs_use_file_start - mark the beginning of file data access * debugfs_file_get - 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 * Up to a matching call to debugfs_file_put(), 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. * call to debugfs_file_get() 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) int debugfs_file_get(struct dentry *dentry) { *srcu_idx = srcu_read_lock(&debugfs_srcu); barrier(); 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); } } /* * 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; if (!refcount_inc_not_zero(&fsd->active_users)) return -EIO; return 0; } EXPORT_SYMBOL_GPL(debugfs_use_file_start); EXPORT_SYMBOL_GPL(debugfs_file_get); /** * 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(). * debugfs_file_put - mark the end of file data access * @dentry: the dentry object formerly passed to * debugfs_file_get(). * * 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. * debugfs_file_get() to proceed and return to its caller. */ void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) void debugfs_file_put(struct dentry *dentry) { srcu_read_unlock(&debugfs_srcu, srcu_idx); } EXPORT_SYMBOL_GPL(debugfs_use_file_finish); struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata); #define F_DENTRY(filp) ((filp)->f_path.dentry) if (refcount_dec_and_test(&fsd->active_users)) complete(&fsd->active_users_drained); } EXPORT_SYMBOL_GPL(debugfs_file_put); static int open_proxy_open(struct inode *inode, struct file *filp) { const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops = NULL; int srcu_idx, r; int r; r = debugfs_use_file_start(dentry, &srcu_idx); if (r) { r = -ENOENT; goto out; } r = debugfs_file_get(dentry); if (r) return r == -EIO ? -ENOENT : r; real_fops = debugfs_real_fops(filp); real_fops = fops_get(real_fops); Loading @@ -124,7 +165,7 @@ static int open_proxy_open(struct inode *inode, struct file *filp) r = real_fops->open(inode, filp); out: debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading @@ -138,16 +179,16 @@ const struct file_operations debugfs_open_proxy_file_operations = { #define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \ static ret_type full_proxy_ ## name(proto) \ { \ const struct dentry *dentry = F_DENTRY(filp); \ const struct file_operations *real_fops = \ debugfs_real_fops(filp); \ int srcu_idx; \ struct dentry *dentry = F_DENTRY(filp); \ const struct file_operations *real_fops; \ ret_type r; \ \ r = debugfs_use_file_start(dentry, &srcu_idx); \ if (likely(!r)) \ r = debugfs_file_get(dentry); \ if (unlikely(r)) \ return r; \ real_fops = debugfs_real_fops(filp); \ r = real_fops->name(args); \ debugfs_use_file_finish(srcu_idx); \ debugfs_file_put(dentry); \ return r; \ } Loading @@ -172,18 +213,16 @@ FULL_PROXY_FUNC(unlocked_ioctl, long, filp, static unsigned int full_proxy_poll(struct file *filp, struct poll_table_struct *wait) { const struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops = debugfs_real_fops(filp); int srcu_idx; struct dentry *dentry = F_DENTRY(filp); unsigned int r = 0; const struct file_operations *real_fops; if (debugfs_use_file_start(dentry, &srcu_idx)) { debugfs_use_file_finish(srcu_idx); if (debugfs_file_get(dentry)) return POLLHUP; } real_fops = debugfs_real_fops(filp); r = real_fops->poll(filp, wait); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading Loading @@ -227,16 +266,14 @@ static void __full_proxy_fops_init(struct file_operations *proxy_fops, static int full_proxy_open(struct inode *inode, struct file *filp) { const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops = NULL; struct file_operations *proxy_fops = NULL; int srcu_idx, r; int r; r = debugfs_use_file_start(dentry, &srcu_idx); if (r) { r = -ENOENT; goto out; } r = debugfs_file_get(dentry); if (r) return r == -EIO ? -ENOENT : r; real_fops = debugfs_real_fops(filp); real_fops = fops_get(real_fops); Loading Loading @@ -274,7 +311,7 @@ static int full_proxy_open(struct inode *inode, struct file *filp) kfree(proxy_fops); fops_put(real_fops); out: debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading @@ -285,13 +322,14 @@ const struct file_operations debugfs_full_proxy_file_operations = { ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { struct dentry *dentry = F_DENTRY(file); ssize_t ret; int srcu_idx; ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!ret)) ret = debugfs_file_get(dentry); if (unlikely(ret)) return ret; ret = simple_attr_read(file, buf, len, ppos); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return ret; } EXPORT_SYMBOL_GPL(debugfs_attr_read); Loading @@ -299,13 +337,14 @@ EXPORT_SYMBOL_GPL(debugfs_attr_read); ssize_t debugfs_attr_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct dentry *dentry = F_DENTRY(file); ssize_t ret; int srcu_idx; ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!ret)) ret = debugfs_file_get(dentry); if (unlikely(ret)) return ret; ret = simple_attr_write(file, buf, len, ppos); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return ret; } EXPORT_SYMBOL_GPL(debugfs_attr_write); Loading Loading @@ -739,14 +778,14 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf, { char buf[3]; bool val; int r, srcu_idx; int r; struct dentry *dentry = F_DENTRY(file); r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!r)) val = *(bool *)file->private_data; debugfs_use_file_finish(srcu_idx); if (r) r = debugfs_file_get(dentry); if (unlikely(r)) return r; val = *(bool *)file->private_data; debugfs_file_put(dentry); if (val) buf[0] = 'Y'; Loading @@ -764,8 +803,9 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf, char buf[32]; size_t buf_size; bool bv; int r, srcu_idx; int r; bool *val = file->private_data; struct dentry *dentry = F_DENTRY(file); buf_size = min(count, (sizeof(buf)-1)); if (copy_from_user(buf, user_buf, buf_size)) Loading @@ -773,12 +813,11 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf, buf[buf_size] = '\0'; if (strtobool(buf, &bv) == 0) { r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!r)) *val = bv; debugfs_use_file_finish(srcu_idx); if (r) r = debugfs_file_get(dentry); if (unlikely(r)) return r; *val = bv; debugfs_file_put(dentry); } return count; Loading Loading @@ -840,14 +879,15 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct debugfs_blob_wrapper *blob = file->private_data; struct dentry *dentry = F_DENTRY(file); ssize_t r; int srcu_idx; r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!r)) r = debugfs_file_get(dentry); if (unlikely(r)) return r; r = simple_read_from_buffer(user_buf, count, ppos, blob->data, blob->size); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading fs/debugfs/inode.c +42 −14 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -185,6 +182,14 @@ static const struct super_operations debugfs_super_operations = { .evict_inode = debugfs_evict_inode, }; 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); } static struct vfsmount *debugfs_automount(struct path *path) { debugfs_automount_t f; Loading @@ -194,6 +199,7 @@ static struct vfsmount *debugfs_automount(struct path *path) static const struct dentry_operations debugfs_dops = { .d_delete = always_delete_dentry, .d_release = debugfs_release_dentry, .d_automount = debugfs_automount, }; Loading Loading @@ -324,7 +330,8 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode, inode->i_private = data; inode->i_fop = proxy_fops; dentry->d_fsdata = (void *)real_fops; 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); Loading Loading @@ -581,18 +588,43 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, } EXPORT_SYMBOL_GPL(debugfs_create_symlink); static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent) { struct debugfs_fsdata *fsd; simple_unlink(d_inode(parent), dentry); d_delete(dentry); /* * 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); } static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) { int ret = 0; if (simple_positive(dentry)) { dget(dentry); if (!d_is_reg(dentry)) { if (d_is_dir(dentry)) ret = simple_rmdir(d_inode(parent), dentry); else simple_unlink(d_inode(parent), dentry); if (!ret) d_delete(dentry); } else { __debugfs_remove_file(dentry, parent); } dput(dentry); } return ret; Loading Loading @@ -626,8 +658,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); Loading Loading @@ -701,8 +731,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); Loading fs/debugfs/internal.h +16 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,8 @@ #ifndef _DEBUGFS_INTERNAL_H_ #define _DEBUGFS_INTERNAL_H_ #include <linux/refcount.h> struct file_operations; /* declared over in file.c */ Loading @@ -19,4 +21,18 @@ extern const struct file_operations debugfs_noop_file_operations; extern const struct file_operations debugfs_open_proxy_file_operations; extern const struct file_operations debugfs_full_proxy_file_operations; struct debugfs_fsdata { const struct file_operations *real_fops; refcount_t active_users; 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_ */ include/linux/debugfs.h +5 −28 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ struct device; struct file_operations; struct srcu_struct; struct debugfs_blob_wrapper { void *data; Loading @@ -43,25 +42,6 @@ struct debugfs_regset32 { extern struct dentry *arch_debugfs_dir; extern struct srcu_struct debugfs_srcu; /** * debugfs_real_fops - getter for the real file operation * @filp: a pointer to a struct file * * Must only be called under the protection established by * debugfs_use_file_start(). */ static inline const struct file_operations *debugfs_real_fops(struct file *filp) __must_hold(&debugfs_srcu) { /* * Neither the pointer to the struct file_operations, nor its * contents ever change -- srcu_dereference() is not needed here. */ return filp->f_path.dentry->d_fsdata; } #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ static int __fops ## _open(struct inode *inode, struct file *file) \ { \ Loading Loading @@ -105,10 +85,10 @@ 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); const struct file_operations *debugfs_real_fops(const struct file *filp); void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); int debugfs_file_get(struct dentry *dentry); void debugfs_file_put(struct dentry *dentry); ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); Loading Loading @@ -223,15 +203,12 @@ 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) static inline int debugfs_file_get(struct dentry *dentry) { return 0; } static inline void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) static inline void debugfs_file_put(struct dentry *dentry) { } static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf, Loading Loading
drivers/infiniband/hw/hfi1/debugfs.c +10 −10 Original line number Diff line number Diff line Loading @@ -67,13 +67,13 @@ static ssize_t hfi1_seq_read( loff_t *ppos) { struct dentry *d = file->f_path.dentry; int srcu_idx; ssize_t r; r = debugfs_use_file_start(d, &srcu_idx); if (likely(!r)) r = debugfs_file_get(d); if (unlikely(r)) return r; r = seq_read(file, buf, size, ppos); debugfs_use_file_finish(srcu_idx); debugfs_file_put(d); return r; } Loading @@ -83,13 +83,13 @@ static loff_t hfi1_seq_lseek( int whence) { struct dentry *d = file->f_path.dentry; int srcu_idx; loff_t r; r = debugfs_use_file_start(d, &srcu_idx); if (likely(!r)) r = debugfs_file_get(d); if (unlikely(r)) return r; r = seq_lseek(file, offset, whence); debugfs_use_file_finish(srcu_idx); debugfs_file_put(d); return r; } Loading
fs/debugfs/file.c +125 −85 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -48,66 +47,108 @@ const struct file_operations debugfs_noop_file_operations = { .llseek = noop_llseek, }; #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); /** * debugfs_use_file_start - mark the beginning of file data access * debugfs_file_get - 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 * Up to a matching call to debugfs_file_put(), 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. * call to debugfs_file_get() 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) int debugfs_file_get(struct dentry *dentry) { *srcu_idx = srcu_read_lock(&debugfs_srcu); barrier(); 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); } } /* * 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; if (!refcount_inc_not_zero(&fsd->active_users)) return -EIO; return 0; } EXPORT_SYMBOL_GPL(debugfs_use_file_start); EXPORT_SYMBOL_GPL(debugfs_file_get); /** * 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(). * debugfs_file_put - mark the end of file data access * @dentry: the dentry object formerly passed to * debugfs_file_get(). * * 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. * debugfs_file_get() to proceed and return to its caller. */ void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) void debugfs_file_put(struct dentry *dentry) { srcu_read_unlock(&debugfs_srcu, srcu_idx); } EXPORT_SYMBOL_GPL(debugfs_use_file_finish); struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata); #define F_DENTRY(filp) ((filp)->f_path.dentry) if (refcount_dec_and_test(&fsd->active_users)) complete(&fsd->active_users_drained); } EXPORT_SYMBOL_GPL(debugfs_file_put); static int open_proxy_open(struct inode *inode, struct file *filp) { const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops = NULL; int srcu_idx, r; int r; r = debugfs_use_file_start(dentry, &srcu_idx); if (r) { r = -ENOENT; goto out; } r = debugfs_file_get(dentry); if (r) return r == -EIO ? -ENOENT : r; real_fops = debugfs_real_fops(filp); real_fops = fops_get(real_fops); Loading @@ -124,7 +165,7 @@ static int open_proxy_open(struct inode *inode, struct file *filp) r = real_fops->open(inode, filp); out: debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading @@ -138,16 +179,16 @@ const struct file_operations debugfs_open_proxy_file_operations = { #define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \ static ret_type full_proxy_ ## name(proto) \ { \ const struct dentry *dentry = F_DENTRY(filp); \ const struct file_operations *real_fops = \ debugfs_real_fops(filp); \ int srcu_idx; \ struct dentry *dentry = F_DENTRY(filp); \ const struct file_operations *real_fops; \ ret_type r; \ \ r = debugfs_use_file_start(dentry, &srcu_idx); \ if (likely(!r)) \ r = debugfs_file_get(dentry); \ if (unlikely(r)) \ return r; \ real_fops = debugfs_real_fops(filp); \ r = real_fops->name(args); \ debugfs_use_file_finish(srcu_idx); \ debugfs_file_put(dentry); \ return r; \ } Loading @@ -172,18 +213,16 @@ FULL_PROXY_FUNC(unlocked_ioctl, long, filp, static unsigned int full_proxy_poll(struct file *filp, struct poll_table_struct *wait) { const struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops = debugfs_real_fops(filp); int srcu_idx; struct dentry *dentry = F_DENTRY(filp); unsigned int r = 0; const struct file_operations *real_fops; if (debugfs_use_file_start(dentry, &srcu_idx)) { debugfs_use_file_finish(srcu_idx); if (debugfs_file_get(dentry)) return POLLHUP; } real_fops = debugfs_real_fops(filp); r = real_fops->poll(filp, wait); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading Loading @@ -227,16 +266,14 @@ static void __full_proxy_fops_init(struct file_operations *proxy_fops, static int full_proxy_open(struct inode *inode, struct file *filp) { const struct dentry *dentry = F_DENTRY(filp); struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops = NULL; struct file_operations *proxy_fops = NULL; int srcu_idx, r; int r; r = debugfs_use_file_start(dentry, &srcu_idx); if (r) { r = -ENOENT; goto out; } r = debugfs_file_get(dentry); if (r) return r == -EIO ? -ENOENT : r; real_fops = debugfs_real_fops(filp); real_fops = fops_get(real_fops); Loading Loading @@ -274,7 +311,7 @@ static int full_proxy_open(struct inode *inode, struct file *filp) kfree(proxy_fops); fops_put(real_fops); out: debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading @@ -285,13 +322,14 @@ const struct file_operations debugfs_full_proxy_file_operations = { ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { struct dentry *dentry = F_DENTRY(file); ssize_t ret; int srcu_idx; ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!ret)) ret = debugfs_file_get(dentry); if (unlikely(ret)) return ret; ret = simple_attr_read(file, buf, len, ppos); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return ret; } EXPORT_SYMBOL_GPL(debugfs_attr_read); Loading @@ -299,13 +337,14 @@ EXPORT_SYMBOL_GPL(debugfs_attr_read); ssize_t debugfs_attr_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct dentry *dentry = F_DENTRY(file); ssize_t ret; int srcu_idx; ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!ret)) ret = debugfs_file_get(dentry); if (unlikely(ret)) return ret; ret = simple_attr_write(file, buf, len, ppos); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return ret; } EXPORT_SYMBOL_GPL(debugfs_attr_write); Loading Loading @@ -739,14 +778,14 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf, { char buf[3]; bool val; int r, srcu_idx; int r; struct dentry *dentry = F_DENTRY(file); r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!r)) val = *(bool *)file->private_data; debugfs_use_file_finish(srcu_idx); if (r) r = debugfs_file_get(dentry); if (unlikely(r)) return r; val = *(bool *)file->private_data; debugfs_file_put(dentry); if (val) buf[0] = 'Y'; Loading @@ -764,8 +803,9 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf, char buf[32]; size_t buf_size; bool bv; int r, srcu_idx; int r; bool *val = file->private_data; struct dentry *dentry = F_DENTRY(file); buf_size = min(count, (sizeof(buf)-1)); if (copy_from_user(buf, user_buf, buf_size)) Loading @@ -773,12 +813,11 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf, buf[buf_size] = '\0'; if (strtobool(buf, &bv) == 0) { r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!r)) *val = bv; debugfs_use_file_finish(srcu_idx); if (r) r = debugfs_file_get(dentry); if (unlikely(r)) return r; *val = bv; debugfs_file_put(dentry); } return count; Loading Loading @@ -840,14 +879,15 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct debugfs_blob_wrapper *blob = file->private_data; struct dentry *dentry = F_DENTRY(file); ssize_t r; int srcu_idx; r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx); if (likely(!r)) r = debugfs_file_get(dentry); if (unlikely(r)) return r; r = simple_read_from_buffer(user_buf, count, ppos, blob->data, blob->size); debugfs_use_file_finish(srcu_idx); debugfs_file_put(dentry); return r; } Loading
fs/debugfs/inode.c +42 −14 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -185,6 +182,14 @@ static const struct super_operations debugfs_super_operations = { .evict_inode = debugfs_evict_inode, }; 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); } static struct vfsmount *debugfs_automount(struct path *path) { debugfs_automount_t f; Loading @@ -194,6 +199,7 @@ static struct vfsmount *debugfs_automount(struct path *path) static const struct dentry_operations debugfs_dops = { .d_delete = always_delete_dentry, .d_release = debugfs_release_dentry, .d_automount = debugfs_automount, }; Loading Loading @@ -324,7 +330,8 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode, inode->i_private = data; inode->i_fop = proxy_fops; dentry->d_fsdata = (void *)real_fops; 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); Loading Loading @@ -581,18 +588,43 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, } EXPORT_SYMBOL_GPL(debugfs_create_symlink); static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent) { struct debugfs_fsdata *fsd; simple_unlink(d_inode(parent), dentry); d_delete(dentry); /* * 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); } static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) { int ret = 0; if (simple_positive(dentry)) { dget(dentry); if (!d_is_reg(dentry)) { if (d_is_dir(dentry)) ret = simple_rmdir(d_inode(parent), dentry); else simple_unlink(d_inode(parent), dentry); if (!ret) d_delete(dentry); } else { __debugfs_remove_file(dentry, parent); } dput(dentry); } return ret; Loading Loading @@ -626,8 +658,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); Loading Loading @@ -701,8 +731,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); Loading
fs/debugfs/internal.h +16 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,8 @@ #ifndef _DEBUGFS_INTERNAL_H_ #define _DEBUGFS_INTERNAL_H_ #include <linux/refcount.h> struct file_operations; /* declared over in file.c */ Loading @@ -19,4 +21,18 @@ extern const struct file_operations debugfs_noop_file_operations; extern const struct file_operations debugfs_open_proxy_file_operations; extern const struct file_operations debugfs_full_proxy_file_operations; struct debugfs_fsdata { const struct file_operations *real_fops; refcount_t active_users; 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_ */
include/linux/debugfs.h +5 −28 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ struct device; struct file_operations; struct srcu_struct; struct debugfs_blob_wrapper { void *data; Loading @@ -43,25 +42,6 @@ struct debugfs_regset32 { extern struct dentry *arch_debugfs_dir; extern struct srcu_struct debugfs_srcu; /** * debugfs_real_fops - getter for the real file operation * @filp: a pointer to a struct file * * Must only be called under the protection established by * debugfs_use_file_start(). */ static inline const struct file_operations *debugfs_real_fops(struct file *filp) __must_hold(&debugfs_srcu) { /* * Neither the pointer to the struct file_operations, nor its * contents ever change -- srcu_dereference() is not needed here. */ return filp->f_path.dentry->d_fsdata; } #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ static int __fops ## _open(struct inode *inode, struct file *file) \ { \ Loading Loading @@ -105,10 +85,10 @@ 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); const struct file_operations *debugfs_real_fops(const struct file *filp); void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); int debugfs_file_get(struct dentry *dentry); void debugfs_file_put(struct dentry *dentry); ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); Loading Loading @@ -223,15 +203,12 @@ 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) static inline int debugfs_file_get(struct dentry *dentry) { return 0; } static inline void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) static inline void debugfs_file_put(struct dentry *dentry) { } static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf, Loading