diff options
Diffstat (limited to 'fs/debugfs/file.c')
-rw-r--r-- | fs/debugfs/file.c | 55 |
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 | } |
58 | EXPORT_SYMBOL_GPL(debugfs_real_fops); | 67 | EXPORT_SYMBOL_GPL(debugfs_real_fops); |
@@ -74,9 +83,35 @@ EXPORT_SYMBOL_GPL(debugfs_real_fops); | |||
74 | */ | 83 | */ |
75 | int debugfs_file_get(struct dentry *dentry) | 84 | int 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 | */ |
99 | void debugfs_file_put(struct dentry *dentry) | 134 | void 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); |