|
@@ -22,6 +22,9 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/atomic.h>
|
|
|
#include <linux/device.h>
|
|
|
+#include <linux/srcu.h>
|
|
|
+
|
|
|
+#include "internal.h"
|
|
|
|
|
|
static ssize_t default_read_file(struct file *file, char __user *buf,
|
|
|
size_t count, loff_t *ppos)
|
|
@@ -35,13 +38,99 @@ static ssize_t default_write_file(struct file *file, const char __user *buf,
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-const struct file_operations debugfs_file_operations = {
|
|
|
+const struct file_operations debugfs_noop_file_operations = {
|
|
|
.read = default_read_file,
|
|
|
.write = default_write_file,
|
|
|
.open = simple_open,
|
|
|
.llseek = noop_llseek,
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * debugfs_use_file_start - 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
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * 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().
|
|
|
+ */
|
|
|
+static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
|
|
|
+ __acquires(&debugfs_srcu)
|
|
|
+{
|
|
|
+ *srcu_idx = srcu_read_lock(&debugfs_srcu);
|
|
|
+ barrier();
|
|
|
+ if (d_unlinked(dentry))
|
|
|
+ return -EIO;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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().
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
|
|
|
+{
|
|
|
+ srcu_read_unlock(&debugfs_srcu, srcu_idx);
|
|
|
+}
|
|
|
+
|
|
|
+#define F_DENTRY(filp) ((filp)->f_path.dentry)
|
|
|
+
|
|
|
+#define REAL_FOPS_DEREF(dentry) \
|
|
|
+ ((const struct file_operations *)(dentry)->d_fsdata)
|
|
|
+
|
|
|
+static int open_proxy_open(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ const struct dentry *dentry = F_DENTRY(filp);
|
|
|
+ const struct file_operations *real_fops = NULL;
|
|
|
+ int srcu_idx, r;
|
|
|
+
|
|
|
+ r = debugfs_use_file_start(dentry, &srcu_idx);
|
|
|
+ if (r) {
|
|
|
+ r = -ENOENT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ real_fops = REAL_FOPS_DEREF(dentry);
|
|
|
+ real_fops = fops_get(real_fops);
|
|
|
+ if (!real_fops) {
|
|
|
+ /* Huh? Module did not clean up after itself at exit? */
|
|
|
+ WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
|
|
+ dentry);
|
|
|
+ r = -ENXIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ replace_fops(filp, real_fops);
|
|
|
+
|
|
|
+ if (real_fops->open)
|
|
|
+ r = real_fops->open(inode, filp);
|
|
|
+
|
|
|
+out:
|
|
|
+ fops_put(real_fops);
|
|
|
+ debugfs_use_file_finish(srcu_idx);
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+const struct file_operations debugfs_open_proxy_file_operations = {
|
|
|
+ .open = open_proxy_open,
|
|
|
+};
|
|
|
+
|
|
|
static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
|
|
|
struct dentry *parent, void *value,
|
|
|
const struct file_operations *fops,
|