diff options
Diffstat (limited to 'fs/debugfs/inode.c')
-rw-r--r-- | fs/debugfs/inode.c | 36 |
1 files changed, 19 insertions, 17 deletions
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 | ||
185 | static void debugfs_release_dentry(struct dentry *dentry) | 185 | static 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 | ||
190 | static struct vfsmount *debugfs_automount(struct path *path) | 193 | static 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 | } |