aboutsummaryrefslogtreecommitdiffstats
path: root/fs/debugfs/inode.c
diff options
context:
space:
mode:
authorNicolai Stange <nicstange@gmail.com>2017-10-30 19:15:54 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-11-07 14:25:03 -0500
commit7d39bc50c47b3f8ed0e1a9d671ecb9ec02f10a2d (patch)
treeba7e45925bc61a6dbb4749ee503a481449966112 /fs/debugfs/inode.c
parent154b9d7512ae012aca7b4e90af67a72419ad1941 (diff)
debugfs: defer debugfs_fsdata allocation to first usage
Currently, __debugfs_create_file allocates one struct debugfs_fsdata instance for every file created. However, there are potentially many debugfs file around, most of which are never touched by userspace. Thus, defer the allocations to the first usage, i.e. to the first debugfs_file_get(). A dentry's ->d_fsdata starts out to point to the "real", user provided fops. After a debugfs_fsdata instance has been allocated (and the real fops pointer has been moved over into its ->real_fops member), ->d_fsdata is changed to point to it from then on. The two cases are distinguished by setting BIT(0) for the real fops case. struct debugfs_fsdata's foremost purpose is to track active users and to make debugfs_remove() block until they are done. Since no debugfs_fsdata instance means no active users, make debugfs_remove() return immediately in this case. Take care of possible races between debugfs_file_get() and debugfs_remove(): either debugfs_remove() must see a debugfs_fsdata instance and thus wait for possible active users or debugfs_file_get() must see a dead dentry and return immediately. Make a dentry's ->d_release(), i.e. debugfs_release_dentry(), check whether ->d_fsdata is actually a debugfs_fsdata instance before kfree()ing it. Similarly, make debugfs_real_fops() check whether ->d_fsdata is actually a debugfs_fsdata instance before returning it, otherwise emit a warning. The set of possible error codes returned from debugfs_file_get() has grown from -EIO to -EIO and -ENOMEM. Make open_proxy_open() and full_proxy_open() pass the -ENOMEM onwards to their callers. Signed-off-by: Nicolai Stange <nicstange@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/debugfs/inode.c')
-rw-r--r--fs/debugfs/inode.c36
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
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}