Merge tag 'driver-core-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 16 Nov 2017 16:55:30 +0000 (08:55 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 16 Nov 2017 16:55:30 +0000 (08:55 -0800)
Pull driver core updates from Greg KH:
 "Here is the set of driver core / debugfs patches for 4.15-rc1.

  Not many here, mostly all are debugfs fixes to resolve some
  long-reported problems with files going away with references to them
  in userspace. There's also some SPDX cleanups for the debugfs code, as
  well as a few other minor driver core changes for issues reported by
  people.

  All of these have been in linux-next for a week or more with no
  reported issues"

* tag 'driver-core-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core:
  driver core: Fix device link deferred probe
  debugfs: Remove redundant license text
  debugfs: add SPDX identifiers to all debugfs files
  debugfs: defer debugfs_fsdata allocation to first usage
  debugfs: call debugfs_real_fops() only after debugfs_file_get()
  debugfs: purge obsolete SRCU based removal protection
  IB/hfi1: convert to debugfs_file_get() and -put()
  debugfs: convert to debugfs_file_get() and -put()
  debugfs: debugfs_real_fops(): drop __must_hold sparse annotation
  debugfs: implement per-file removal protection
  debugfs: add support for more elaborate ->d_fsdata
  driver core: Move device_links_purge() after bus_remove_device()
  arch_topology: Fix section miss match warning due to free_raw_capacity()
  driver-core: pr_err() strings should end with newlines

drivers/base/arch_topology.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/test/test_async_driver_probe.c
drivers/infiniband/hw/hfi1/debugfs.c
fs/debugfs/file.c
fs/debugfs/inode.c
fs/debugfs/internal.h
include/linux/debugfs.h
lib/Kconfig.debug

index 0739c5b953bf8ab1b1f48d8df9a14cc9fcb76731..4de87b0b53c8f2bd1316ec97b81821457e9a319d 100644 (file)
@@ -105,7 +105,7 @@ subsys_initcall(register_cpu_capacity_sysctl);
 static u32 capacity_scale;
 static u32 *raw_capacity;
 
-static int __init free_raw_capacity(void)
+static int free_raw_capacity(void)
 {
        kfree(raw_capacity);
        raw_capacity = NULL;
index a36cf1bd07a91370bd9aac591f121399725780b2..110230d865270d50465d8d341686a8a51d0ea482 100644 (file)
@@ -1958,7 +1958,6 @@ void device_del(struct device *dev)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_DEL_DEVICE, dev);
 
-       device_links_purge(dev);
        dpm_sysfs_remove(dev);
        if (parent)
                klist_del(&dev->p->knode_parent);
@@ -1986,6 +1985,7 @@ void device_del(struct device *dev)
        device_pm_remove(dev);
        driver_deferred_probe_del(dev);
        device_remove_properties(dev);
+       device_links_purge(dev);
 
        /* Notify the platform of the removal, in case they
         * need to do anything...
index 45575e134696280b4b2de4635b9631f1737b0f58..2c964f56dafe2a98706ca9cd5520e50bacf551f6 100644 (file)
@@ -350,6 +350,15 @@ EXPORT_SYMBOL_GPL(device_bind_driver);
 static atomic_t probe_count = ATOMIC_INIT(0);
 static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
 
+static void driver_deferred_probe_add_trigger(struct device *dev,
+                                             int local_trigger_count)
+{
+       driver_deferred_probe_add(dev);
+       /* Did a trigger occur while probing? Need to re-trigger if yes */
+       if (local_trigger_count != atomic_read(&deferred_trigger_count))
+               driver_deferred_probe_trigger();
+}
+
 static int really_probe(struct device *dev, struct device_driver *drv)
 {
        int ret = -EPROBE_DEFER;
@@ -369,6 +378,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
        }
 
        ret = device_links_check_suppliers(dev);
+       if (ret == -EPROBE_DEFER)
+               driver_deferred_probe_add_trigger(dev, local_trigger_count);
        if (ret)
                return ret;
 
@@ -470,10 +481,7 @@ pinctrl_bind_failed:
        case -EPROBE_DEFER:
                /* Driver requested deferred probing */
                dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
-               driver_deferred_probe_add(dev);
-               /* Did a trigger occur while probing? Need to re-trigger if yes */
-               if (local_trigger_count != atomic_read(&deferred_trigger_count))
-                       driver_deferred_probe_trigger();
+               driver_deferred_probe_add_trigger(dev, local_trigger_count);
                break;
        case -ENODEV:
        case -ENXIO:
index 304d5c2bd5e9052d192d245ebfa3003a542e2335..a3355d66bc12df289130c7e96733d545089fe78d 100644 (file)
@@ -64,7 +64,7 @@ static int __init test_async_probe_init(void)
                                                      NULL, 0);
        if (IS_ERR(async_dev_1)) {
                error = PTR_ERR(async_dev_1);
-               pr_err("failed to create async_dev_1: %d", error);
+               pr_err("failed to create async_dev_1: %d\n", error);
                return error;
        }
 
@@ -91,7 +91,7 @@ static int __init test_async_probe_init(void)
                                                      NULL, 0);
        if (IS_ERR(async_dev_2)) {
                error = PTR_ERR(async_dev_2);
-               pr_err("failed to create async_dev_2: %d", error);
+               pr_err("failed to create async_dev_2: %d\n", error);
                goto err_unregister_async_driver;
        }
 
@@ -118,7 +118,7 @@ static int __init test_async_probe_init(void)
                                                     NULL, 0);
        if (IS_ERR(sync_dev_1)) {
                error = PTR_ERR(sync_dev_1);
-               pr_err("failed to create sync_dev_1: %d", error);
+               pr_err("failed to create sync_dev_1: %d\n", error);
                goto err_unregister_sync_driver;
        }
 
index 76157cc03ecaf453eaf0eefa265f8a8827e91b13..2e6e0c51604185e835f9eb2347825e4eeb96eb7f 100644 (file)
@@ -71,13 +71,13 @@ static ssize_t hfi1_seq_read(
        loff_t *ppos)
 {
        struct dentry *d = file->f_path.dentry;
-       int srcu_idx;
        ssize_t r;
 
-       r = debugfs_use_file_start(d, &srcu_idx);
-       if (likely(!r))
-               r = seq_read(file, buf, size, ppos);
-       debugfs_use_file_finish(srcu_idx);
+       r = debugfs_file_get(d);
+       if (unlikely(r))
+               return r;
+       r = seq_read(file, buf, size, ppos);
+       debugfs_file_put(d);
        return r;
 }
 
@@ -87,13 +87,13 @@ static loff_t hfi1_seq_lseek(
        int whence)
 {
        struct dentry *d = file->f_path.dentry;
-       int srcu_idx;
        loff_t r;
 
-       r = debugfs_use_file_start(d, &srcu_idx);
-       if (likely(!r))
-               r = seq_lseek(file, offset, whence);
-       debugfs_use_file_finish(srcu_idx);
+       r = debugfs_file_get(d);
+       if (unlikely(r))
+               return r;
+       r = seq_lseek(file, offset, whence);
+       debugfs_file_put(d);
        return r;
 }
 
index 6dabc4a10396b8627f2c8d83d5e634d28b83b517..cd12e6576b48e306cc77dd14b4e200d5f78a7930 100644 (file)
@@ -1,16 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  file.c - part of debugfs, a tiny little debug file system
  *
  *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
  *  Copyright (C) 2004 IBM Inc.
  *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License version
- *     2 as published by the Free Software Foundation.
- *
  *  debugfs is for people to use instead of /proc or /sys.
  *  See Documentation/filesystems/ for more details.
- *
  */
 
 #include <linux/module.h>
@@ -22,7 +18,6 @@
 #include <linux/slab.h>
 #include <linux/atomic.h>
 #include <linux/device.h>
-#include <linux/srcu.h>
 #include <asm/poll.h>
 
 #include "internal.h"
@@ -48,66 +43,108 @@ const struct file_operations debugfs_noop_file_operations = {
        .llseek =       noop_llseek,
 };
 
+#define F_DENTRY(filp) ((filp)->f_path.dentry)
+
+const struct file_operations *debugfs_real_fops(const struct file *filp)
+{
+       struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata;
+
+       if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) {
+               /*
+                * Urgh, we've been called w/o a protecting
+                * debugfs_file_get().
+                */
+               WARN_ON(1);
+               return NULL;
+       }
+
+       return fsd->real_fops;
+}
+EXPORT_SYMBOL_GPL(debugfs_real_fops);
+
 /**
- * debugfs_use_file_start - mark the beginning of file data access
+ * debugfs_file_get - mark the beginning of file data access
  * @dentry: the dentry object whose data is being accessed.
- * @srcu_idx: a pointer to some memory to store a SRCU index in.
  *
- * Up to a matching call to debugfs_use_file_finish(), any
- * successive call into the file removing functions debugfs_remove()
- * and debugfs_remove_recursive() will block. Since associated private
+ * Up to a matching call to debugfs_file_put(), any successive call
+ * into the file removing functions debugfs_remove() and
+ * debugfs_remove_recursive() will block. Since associated private
  * file data may only get freed after a successful return of any of
  * the removal functions, you may safely access it after a successful
- * call to debugfs_use_file_start() without worrying about
- * lifetime issues.
+ * call to debugfs_file_get() without worrying about lifetime issues.
  *
  * If -%EIO is returned, the file has already been removed and thus,
  * it is not safe to access any of its data. If, on the other hand,
  * it is allowed to access the file data, zero is returned.
- *
- * Regardless of the return code, any call to
- * debugfs_use_file_start() must be followed by a matching call
- * to debugfs_use_file_finish().
  */
-int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
-       __acquires(&debugfs_srcu)
+int debugfs_file_get(struct dentry *dentry)
 {
-       *srcu_idx = srcu_read_lock(&debugfs_srcu);
-       barrier();
+       struct debugfs_fsdata *fsd;
+       void *d_fsd;
+
+       d_fsd = READ_ONCE(dentry->d_fsdata);
+       if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) {
+               fsd = d_fsd;
+       } else {
+               fsd = kmalloc(sizeof(*fsd), GFP_KERNEL);
+               if (!fsd)
+                       return -ENOMEM;
+
+               fsd->real_fops = (void *)((unsigned long)d_fsd &
+                                       ~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
+               refcount_set(&fsd->active_users, 1);
+               init_completion(&fsd->active_users_drained);
+               if (cmpxchg(&dentry->d_fsdata, d_fsd, fsd) != d_fsd) {
+                       kfree(fsd);
+                       fsd = READ_ONCE(dentry->d_fsdata);
+               }
+       }
+
+       /*
+        * In case of a successful cmpxchg() above, this check is
+        * strictly necessary and must follow it, see the comment in
+        * __debugfs_remove_file().
+        * OTOH, if the cmpxchg() hasn't been executed or wasn't
+        * successful, this serves the purpose of not starving
+        * removers.
+        */
        if (d_unlinked(dentry))
                return -EIO;
+
+       if (!refcount_inc_not_zero(&fsd->active_users))
+               return -EIO;
+
        return 0;
 }
-EXPORT_SYMBOL_GPL(debugfs_use_file_start);
+EXPORT_SYMBOL_GPL(debugfs_file_get);
 
 /**
- * debugfs_use_file_finish - mark the end of file data access
- * @srcu_idx: the SRCU index "created" by a former call to
- *            debugfs_use_file_start().
+ * debugfs_file_put - mark the end of file data access
+ * @dentry: the dentry object formerly passed to
+ *          debugfs_file_get().
  *
  * Allow any ongoing concurrent call into debugfs_remove() or
  * debugfs_remove_recursive() blocked by a former call to
- * debugfs_use_file_start() to proceed and return to its caller.
+ * debugfs_file_get() to proceed and return to its caller.
  */
-void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
+void debugfs_file_put(struct dentry *dentry)
 {
-       srcu_read_unlock(&debugfs_srcu, srcu_idx);
-}
-EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
+       struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
 
-#define F_DENTRY(filp) ((filp)->f_path.dentry)
+       if (refcount_dec_and_test(&fsd->active_users))
+               complete(&fsd->active_users_drained);
+}
+EXPORT_SYMBOL_GPL(debugfs_file_put);
 
 static int open_proxy_open(struct inode *inode, struct file *filp)
 {
-       const struct dentry *dentry = F_DENTRY(filp);
+       struct dentry *dentry = F_DENTRY(filp);
        const struct file_operations *real_fops = NULL;
-       int srcu_idx, r;
+       int r;
 
-       r = debugfs_use_file_start(dentry, &srcu_idx);
-       if (r) {
-               r = -ENOENT;
-               goto out;
-       }
+       r = debugfs_file_get(dentry);
+       if (r)
+               return r == -EIO ? -ENOENT : r;
 
        real_fops = debugfs_real_fops(filp);
        real_fops = fops_get(real_fops);
@@ -124,7 +161,7 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
                r = real_fops->open(inode, filp);
 
 out:
-       debugfs_use_file_finish(srcu_idx);
+       debugfs_file_put(dentry);
        return r;
 }
 
@@ -138,16 +175,16 @@ const struct file_operations debugfs_open_proxy_file_operations = {
 #define FULL_PROXY_FUNC(name, ret_type, filp, proto, args)             \
 static ret_type full_proxy_ ## name(proto)                             \
 {                                                                      \
-       const struct dentry *dentry = F_DENTRY(filp);                   \
-       const struct file_operations *real_fops =                       \
-               debugfs_real_fops(filp);                                \
-       int srcu_idx;                                                   \
+       struct dentry *dentry = F_DENTRY(filp);                 \
+       const struct file_operations *real_fops;                        \
        ret_type r;                                                     \
                                                                        \
-       r = debugfs_use_file_start(dentry, &srcu_idx);                  \
-       if (likely(!r))                                         \
-               r = real_fops->name(args);                              \
-       debugfs_use_file_finish(srcu_idx);                              \
+       r = debugfs_file_get(dentry);                                   \
+       if (unlikely(r))                                                \
+               return r;                                               \
+       real_fops = debugfs_real_fops(filp);                            \
+       r = real_fops->name(args);                                      \
+       debugfs_file_put(dentry);                                       \
        return r;                                                       \
 }
 
@@ -172,18 +209,16 @@ FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
 static unsigned int full_proxy_poll(struct file *filp,
                                struct poll_table_struct *wait)
 {
-       const struct dentry *dentry = F_DENTRY(filp);
-       const struct file_operations *real_fops = debugfs_real_fops(filp);
-       int srcu_idx;
+       struct dentry *dentry = F_DENTRY(filp);
        unsigned int r = 0;
+       const struct file_operations *real_fops;
 
-       if (debugfs_use_file_start(dentry, &srcu_idx)) {
-               debugfs_use_file_finish(srcu_idx);
+       if (debugfs_file_get(dentry))
                return POLLHUP;
-       }
 
+       real_fops = debugfs_real_fops(filp);
        r = real_fops->poll(filp, wait);
-       debugfs_use_file_finish(srcu_idx);
+       debugfs_file_put(dentry);
        return r;
 }
 
@@ -227,16 +262,14 @@ static void __full_proxy_fops_init(struct file_operations *proxy_fops,
 
 static int full_proxy_open(struct inode *inode, struct file *filp)
 {
-       const struct dentry *dentry = F_DENTRY(filp);
+       struct dentry *dentry = F_DENTRY(filp);
        const struct file_operations *real_fops = NULL;
        struct file_operations *proxy_fops = NULL;
-       int srcu_idx, r;
+       int r;
 
-       r = debugfs_use_file_start(dentry, &srcu_idx);
-       if (r) {
-               r = -ENOENT;
-               goto out;
-       }
+       r = debugfs_file_get(dentry);
+       if (r)
+               return r == -EIO ? -ENOENT : r;
 
        real_fops = debugfs_real_fops(filp);
        real_fops = fops_get(real_fops);
@@ -274,7 +307,7 @@ free_proxy:
        kfree(proxy_fops);
        fops_put(real_fops);
 out:
-       debugfs_use_file_finish(srcu_idx);
+       debugfs_file_put(dentry);
        return r;
 }
 
@@ -285,13 +318,14 @@ const struct file_operations debugfs_full_proxy_file_operations = {
 ssize_t debugfs_attr_read(struct file *file, char __user *buf,
                        size_t len, loff_t *ppos)
 {
+       struct dentry *dentry = F_DENTRY(file);
        ssize_t ret;
-       int srcu_idx;
 
-       ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
-       if (likely(!ret))
-               ret = simple_attr_read(file, buf, len, ppos);
-       debugfs_use_file_finish(srcu_idx);
+       ret = debugfs_file_get(dentry);
+       if (unlikely(ret))
+               return ret;
+       ret = simple_attr_read(file, buf, len, ppos);
+       debugfs_file_put(dentry);
        return ret;
 }
 EXPORT_SYMBOL_GPL(debugfs_attr_read);
@@ -299,13 +333,14 @@ EXPORT_SYMBOL_GPL(debugfs_attr_read);
 ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
                         size_t len, loff_t *ppos)
 {
+       struct dentry *dentry = F_DENTRY(file);
        ssize_t ret;
-       int srcu_idx;
 
-       ret = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
-       if (likely(!ret))
-               ret = simple_attr_write(file, buf, len, ppos);
-       debugfs_use_file_finish(srcu_idx);
+       ret = debugfs_file_get(dentry);
+       if (unlikely(ret))
+               return ret;
+       ret = simple_attr_write(file, buf, len, ppos);
+       debugfs_file_put(dentry);
        return ret;
 }
 EXPORT_SYMBOL_GPL(debugfs_attr_write);
@@ -739,14 +774,14 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
 {
        char buf[3];
        bool val;
-       int r, srcu_idx;
+       int r;
+       struct dentry *dentry = F_DENTRY(file);
 
-       r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
-       if (likely(!r))
-               val = *(bool *)file->private_data;
-       debugfs_use_file_finish(srcu_idx);
-       if (r)
+       r = debugfs_file_get(dentry);
+       if (unlikely(r))
                return r;
+       val = *(bool *)file->private_data;
+       debugfs_file_put(dentry);
 
        if (val)
                buf[0] = 'Y';
@@ -764,8 +799,9 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
        char buf[32];
        size_t buf_size;
        bool bv;
-       int r, srcu_idx;
+       int r;
        bool *val = file->private_data;
+       struct dentry *dentry = F_DENTRY(file);
 
        buf_size = min(count, (sizeof(buf)-1));
        if (copy_from_user(buf, user_buf, buf_size))
@@ -773,12 +809,11 @@ ssize_t debugfs_write_file_bool(struct file *file, const char __user *user_buf,
 
        buf[buf_size] = '\0';
        if (strtobool(buf, &bv) == 0) {
-               r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
-               if (likely(!r))
-                       *val = bv;
-               debugfs_use_file_finish(srcu_idx);
-               if (r)
+               r = debugfs_file_get(dentry);
+               if (unlikely(r))
                        return r;
+               *val = bv;
+               debugfs_file_put(dentry);
        }
 
        return count;
@@ -840,14 +875,15 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
        struct debugfs_blob_wrapper *blob = file->private_data;
+       struct dentry *dentry = F_DENTRY(file);
        ssize_t r;
-       int srcu_idx;
 
-       r = debugfs_use_file_start(F_DENTRY(file), &srcu_idx);
-       if (likely(!r))
-               r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
-                                       blob->size);
-       debugfs_use_file_finish(srcu_idx);
+       r = debugfs_file_get(dentry);
+       if (unlikely(r))
+               return r;
+       r = simple_read_from_buffer(user_buf, count, ppos, blob->data,
+                               blob->size);
+       debugfs_file_put(dentry);
        return r;
 }
 
index c59f015f386eba1e58b4cba397e1b4910e62ace9..63a998c3f2522d2d727a52bf1a8a9aae4a7c70cd 100644 (file)
@@ -1,16 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  inode.c - part of debugfs, a tiny little debug file system
  *
  *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
  *  Copyright (C) 2004 IBM Inc.
  *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License version
- *     2 as published by the Free Software Foundation.
- *
  *  debugfs is for people to use instead of /proc or /sys.
  *  See ./Documentation/core-api/kernel-api.rst for more details.
- *
  */
 
 #include <linux/module.h>
 #include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
-#include <linux/srcu.h>
 
 #include "internal.h"
 
 #define DEBUGFS_DEFAULT_MODE   0700
 
-DEFINE_SRCU(debugfs_srcu);
-
 static struct vfsmount *debugfs_mount;
 static int debugfs_mount_count;
 static bool debugfs_registered;
@@ -185,6 +178,14 @@ static const struct super_operations debugfs_super_operations = {
        .evict_inode    = debugfs_evict_inode,
 };
 
+static void debugfs_release_dentry(struct dentry *dentry)
+{
+       void *fsd = dentry->d_fsdata;
+
+       if (!((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))
+               kfree(dentry->d_fsdata);
+}
+
 static struct vfsmount *debugfs_automount(struct path *path)
 {
        debugfs_automount_t f;
@@ -194,6 +195,7 @@ static struct vfsmount *debugfs_automount(struct path *path)
 
 static const struct dentry_operations debugfs_dops = {
        .d_delete = always_delete_dentry,
+       .d_release = debugfs_release_dentry,
        .d_automount = debugfs_automount,
 };
 
@@ -358,7 +360,8 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
        inode->i_private = data;
 
        inode->i_fop = proxy_fops;
-       dentry->d_fsdata = (void *)real_fops;
+       dentry->d_fsdata = (void *)((unsigned long)real_fops |
+                               DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
 
        d_instantiate(dentry, inode);
        fsnotify_create(d_inode(dentry->d_parent), dentry);
@@ -615,18 +618,43 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
 }
 EXPORT_SYMBOL_GPL(debugfs_create_symlink);
 
+static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent)
+{
+       struct debugfs_fsdata *fsd;
+
+       simple_unlink(d_inode(parent), dentry);
+       d_delete(dentry);
+
+       /*
+        * Paired with the closing smp_mb() implied by a successful
+        * cmpxchg() in debugfs_file_get(): either
+        * debugfs_file_get() must see a dead dentry or we must see a
+        * debugfs_fsdata instance at ->d_fsdata here (or both).
+        */
+       smp_mb();
+       fsd = READ_ONCE(dentry->d_fsdata);
+       if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
+               return;
+       if (!refcount_dec_and_test(&fsd->active_users))
+               wait_for_completion(&fsd->active_users_drained);
+}
+
 static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
 {
        int ret = 0;
 
        if (simple_positive(dentry)) {
                dget(dentry);
-               if (d_is_dir(dentry))
-                       ret = simple_rmdir(d_inode(parent), dentry);
-               else
-                       simple_unlink(d_inode(parent), dentry);
-               if (!ret)
-                       d_delete(dentry);
+               if (!d_is_reg(dentry)) {
+                       if (d_is_dir(dentry))
+                               ret = simple_rmdir(d_inode(parent), dentry);
+                       else
+                               simple_unlink(d_inode(parent), dentry);
+                       if (!ret)
+                               d_delete(dentry);
+               } else {
+                       __debugfs_remove_file(dentry, parent);
+               }
                dput(dentry);
        }
        return ret;
@@ -660,8 +688,6 @@ void debugfs_remove(struct dentry *dentry)
        inode_unlock(d_inode(parent));
        if (!ret)
                simple_release_fs(&debugfs_mount, &debugfs_mount_count);
-
-       synchronize_srcu(&debugfs_srcu);
 }
 EXPORT_SYMBOL_GPL(debugfs_remove);
 
@@ -735,8 +761,6 @@ void debugfs_remove_recursive(struct dentry *dentry)
        if (!__debugfs_remove(child, parent))
                simple_release_fs(&debugfs_mount, &debugfs_mount_count);
        inode_unlock(d_inode(parent));
-
-       synchronize_srcu(&debugfs_srcu);
 }
 EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
 
index b3e8443a1f478dd9884aa5ae830260ff1adce9e5..f0d73d86cc1a8a7d725a3a8cd7d2b52fda5168f9 100644 (file)
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  internal.h - declarations internal to debugfs
  *
  *  Copyright (C) 2016 Nicolai Stange <nicstange@gmail.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License version
- *     2 as published by the Free Software Foundation.
- *
  */
 
 #ifndef _DEBUGFS_INTERNAL_H_
@@ -19,4 +15,18 @@ extern const struct file_operations debugfs_noop_file_operations;
 extern const struct file_operations debugfs_open_proxy_file_operations;
 extern const struct file_operations debugfs_full_proxy_file_operations;
 
+struct debugfs_fsdata {
+       const struct file_operations *real_fops;
+       refcount_t active_users;
+       struct completion active_users_drained;
+};
+
+/*
+ * A dentry's ->d_fsdata either points to the real fops or to a
+ * dynamically allocated debugfs_fsdata instance.
+ * In order to distinguish between these two cases, a real fops
+ * pointer gets its lowest bit set.
+ */
+#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)
+
 #endif /* _DEBUGFS_INTERNAL_H_ */
index b93efc8feecd1f67e9b79fdc62abc96b30e13b1b..f36ecc2a57128cdf5df582b0b7b00d125ffd7ca7 100644 (file)
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  debugfs.h - a tiny little debug file system
  *
  *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
  *  Copyright (C) 2004 IBM Inc.
  *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License version
- *     2 as published by the Free Software Foundation.
- *
  *  debugfs is for people to use instead of /proc or /sys.
  *  See Documentation/filesystems/ for more details.
  */
@@ -23,7 +20,6 @@
 
 struct device;
 struct file_operations;
-struct srcu_struct;
 
 struct debugfs_blob_wrapper {
        void *data;
@@ -43,25 +39,6 @@ struct debugfs_regset32 {
 
 extern struct dentry *arch_debugfs_dir;
 
-extern struct srcu_struct debugfs_srcu;
-
-/**
- * debugfs_real_fops - getter for the real file operation
- * @filp: a pointer to a struct file
- *
- * Must only be called under the protection established by
- * debugfs_use_file_start().
- */
-static inline const struct file_operations *debugfs_real_fops(const struct file *filp)
-       __must_hold(&debugfs_srcu)
-{
-       /*
-        * Neither the pointer to the struct file_operations, nor its
-        * contents ever change -- srcu_dereference() is not needed here.
-        */
-       return filp->f_path.dentry->d_fsdata;
-}
-
 #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)          \
 static int __fops ## _open(struct inode *inode, struct file *file)     \
 {                                                                      \
@@ -107,10 +84,10 @@ struct dentry *debugfs_create_automount(const char *name,
 void debugfs_remove(struct dentry *dentry);
 void debugfs_remove_recursive(struct dentry *dentry);
 
-int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
-       __acquires(&debugfs_srcu);
+const struct file_operations *debugfs_real_fops(const struct file *filp);
 
-void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu);
+int debugfs_file_get(struct dentry *dentry);
+void debugfs_file_put(struct dentry *dentry);
 
 ssize_t debugfs_attr_read(struct file *file, char __user *buf,
                        size_t len, loff_t *ppos);
@@ -239,15 +216,12 @@ static inline void debugfs_remove(struct dentry *dentry)
 static inline void debugfs_remove_recursive(struct dentry *dentry)
 { }
 
-static inline int debugfs_use_file_start(const struct dentry *dentry,
-                                       int *srcu_idx)
-       __acquires(&debugfs_srcu)
+static inline int debugfs_file_get(struct dentry *dentry)
 {
        return 0;
 }
 
-static inline void debugfs_use_file_finish(int srcu_idx)
-       __releases(&debugfs_srcu)
+static inline void debugfs_file_put(struct dentry *dentry)
 { }
 
 static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,
index 5402e395465926939b2f16c9d1a327686a550f1d..8ffd891857aba74eb4c309c692e6ae7034bd4a7b 100644 (file)
@@ -280,7 +280,6 @@ config PAGE_OWNER
 
 config DEBUG_FS
        bool "Debug Filesystem"
-       select SRCU
        help
          debugfs is a virtual file system that kernel developers use to put
          debugging files into.  Enable this option to be able to read and