aboutsummaryrefslogtreecommitdiffstats
path: root/fs/debugfs/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/debugfs/file.c')
-rw-r--r--fs/debugfs/file.c55
1 files changed, 46 insertions, 9 deletions
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index bc3549c95574..65872340e301 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -53,6 +53,15 @@ const struct file_operations *debugfs_real_fops(const struct file *filp)
53{ 53{
54 struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata; 54 struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;
55 55
56 if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
57 /*
58 * Urgh, we've been called w/o a protecting
59 * debugfs_file_get().
60 */
61 WARN_ON(1);
62 return NULL;
63 }
64
56 return fsd->real_fops; 65 return fsd->real_fops;
57} 66}
58EXPORT_SYMBOL_GPL(debugfs_real_fops); 67EXPORT_SYMBOL_GPL(debugfs_real_fops);
@@ -74,9 +83,35 @@ EXPORT_SYMBOL_GPL(debugfs_real_fops);
74 */ 83 */
75int debugfs_file_get(struct dentry *dentry) 84int debugfs_file_get(struct dentry *dentry)
76{ 85{
77 struct debugfs_fsdata *fsd = dentry->d_fsdata; 86 struct debugfs_fsdata *fsd;
87 void *d_fsd;
88
89 d_fsd = READ_ONCE(dentry->d_fsdata);
90 if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
91 fsd = d_fsd;
92 } else {
93 fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
94 if (!fsd)
95 return -ENOMEM;
96
97 fsd->real_fops = (void *)((unsigned long)d_fsd &
98 ~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
99 refcount_set(&fsd->active_users, 1);
100 init_completion(&fsd->active_users_drained);
101 if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
102 kfree(fsd);
103 fsd = READ_ONCE(dentry->d_fsdata);
104 }
105 }
78 106
79 /* Avoid starvation of removers. */ 107 /*
108 * In case of a successful cmpxchg() above, this check is
109 * strictly necessary and must follow it, see the comment in
110 * __debugfs_remove_file().
111 * OTOH, if the cmpxchg() hasn't been executed or wasn't
112 * successful, this serves the purpose of not starving
113 * removers.
114 */
80 if (d_unlinked(dentry)) 115 if (d_unlinked(dentry))
81 return -EIO; 116 return -EIO;
82 117
@@ -98,7 +133,7 @@ EXPORT_SYMBOL_GPL(debugfs_file_get);
98 */ 133 */
99void debugfs_file_put(struct dentry *dentry) 134void debugfs_file_put(struct dentry *dentry)
100{ 135{
101 struct debugfs_fsdata *fsd = dentry->d_fsdata; 136 struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
102 137
103 if (refcount_dec_and_test(&fsd->active_users)) 138 if (refcount_dec_and_test(&fsd->active_users))
104 complete(&fsd->active_users_drained); 139 complete(&fsd->active_users_drained);
@@ -109,10 +144,11 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
109{ 144{
110 struct dentry *dentry = F_DENTRY(filp); 145 struct dentry *dentry = F_DENTRY(filp);
111 const struct file_operations *real_fops = NULL; 146 const struct file_operations *real_fops = NULL;
112 int r = 0; 147 int r;
113 148
114 if (debugfs_file_get(dentry)) 149 r = debugfs_file_get(dentry);
115 return -ENOENT; 150 if (r)
151 return r == -EIO ? -ENOENT : r;
116 152
117 real_fops = debugfs_real_fops(filp); 153 real_fops = debugfs_real_fops(filp);
118 real_fops = fops_get(real_fops); 154 real_fops = fops_get(real_fops);
@@ -233,10 +269,11 @@ static int full_proxy_open(struct inode *inode, struct file *filp)
233 struct dentry *dentry = F_DENTRY(filp); 269 struct dentry *dentry = F_DENTRY(filp);
234 const struct file_operations *real_fops = NULL; 270 const struct file_operations *real_fops = NULL;
235 struct file_operations *proxy_fops = NULL; 271 struct file_operations *proxy_fops = NULL;
236 int r = 0; 272 int r;
237 273
238 if (debugfs_file_get(dentry)) 274 r = debugfs_file_get(dentry);
239 return -ENOENT; 275 if (r)
276 return r == -EIO ? -ENOENT : r;
240 277
241 real_fops = debugfs_real_fops(filp); 278 real_fops = debugfs_real_fops(filp);
242 real_fops = fops_get(real_fops); 279 real_fops = fops_get(real_fops);