aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/debugfs/file.c55
-rw-r--r--fs/debugfs/inode.c36
-rw-r--r--fs/debugfs/internal.h8
3 files changed, 73 insertions, 26 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);
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index f587aded46b5..9dca4da059b3 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -184,7 +184,10 @@ static const struct super_operations debugfs_super_operations = {
184 184
185static void debugfs_release_dentry(struct dentry *dentry) 185static void debugfs_release_dentry(struct dentry *dentry)
186{ 186{
187 kfree(dentry->d_fsdata); 187 void *fsd = dentry->d_fsdata;
188
189 if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
190 kfree(dentry->d_fsdata);
188} 191}
189 192
190static struct vfsmount *debugfs_automount(struct path *path) 193static struct vfsmount *debugfs_automount(struct path *path)
@@ -344,35 +347,25 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
344{ 347{
345 struct dentry *dentry; 348 struct dentry *dentry;
346 struct inode *inode; 349 struct inode *inode;
347 struct debugfs_fsdata *fsd;
348
349 fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
350 if (!fsd)
351 return NULL;
352 350
353 if (!(mode & S_IFMT)) 351 if (!(mode & S_IFMT))
354 mode |= S_IFREG; 352 mode |= S_IFREG;
355 BUG_ON(!S_ISREG(mode)); 353 BUG_ON(!S_ISREG(mode));
356 dentry = start_creating(name, parent); 354 dentry = start_creating(name, parent);
357 355
358 if (IS_ERR(dentry)) { 356 if (IS_ERR(dentry))
359 kfree(fsd);
360 return NULL; 357 return NULL;
361 }
362 358
363 inode = debugfs_get_inode(dentry->d_sb); 359 inode = debugfs_get_inode(dentry->d_sb);
364 if (unlikely(!inode)) { 360 if (unlikely(!inode))
365 kfree(fsd);
366 return failed_creating(dentry); 361 return failed_creating(dentry);
367 }
368 362
369 inode->i_mode = mode; 363 inode->i_mode = mode;
370 inode->i_private = data; 364 inode->i_private = data;
371 365
372 inode->i_fop = proxy_fops; 366 inode->i_fop = proxy_fops;
373 fsd->real_fops = real_fops; 367 dentry->d_fsdata = (void *)((unsigned long)real_fops |
374 refcount_set(&fsd->active_users, 1); 368 DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
375 dentry->d_fsdata = fsd;
376 369
377 d_instantiate(dentry, inode); 370 d_instantiate(dentry, inode);
378 fsnotify_create(d_inode(dentry->d_parent), dentry); 371 fsnotify_create(d_inode(dentry->d_parent), dentry);
@@ -635,8 +628,17 @@ static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent)
635 628
636 simple_unlink(d_inode(parent), dentry); 629 simple_unlink(d_inode(parent), dentry);
637 d_delete(dentry); 630 d_delete(dentry);
638 fsd = dentry->d_fsdata; 631
639 init_completion(&fsd->active_users_drained); 632 /*
633 * Paired with the closing smp_mb() implied by a successful
634 * cmpxchg() in debugfs_file_get(): either
635 * debugfs_file_get() must see a dead dentry or we must see a
636 * debugfs_fsdata instance at ->d_fsdata here (or both).
637 */
638 smp_mb();
639 fsd = READ_ONCE(dentry->d_fsdata);
640 if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
641 return;
640 if (!refcount_dec_and_test(&fsd->active_users)) 642 if (!refcount_dec_and_test(&fsd->active_users))
641 wait_for_completion(&fsd->active_users_drained); 643 wait_for_completion(&fsd->active_users_drained);
642} 644}
diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h
index 0eea99432840..cb1e8139c398 100644
--- a/fs/debugfs/internal.h
+++ b/fs/debugfs/internal.h
@@ -25,4 +25,12 @@ struct debugfs_fsdata {
25 struct completion active_users_drained; 25 struct completion active_users_drained;
26}; 26};
27 27
28/*
29 * A dentry's ->d_fsdata either points to the real fops or to a
30 * dynamically allocated debugfs_fsdata instance.
31 * In order to distinguish between these two cases, a real fops
32 * pointer gets its lowest bit set.
33 */
34#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)
35
28#endif /* _DEBUGFS_INTERNAL_H_ */ 36#endif /* _DEBUGFS_INTERNAL_H_ */