diff options
Diffstat (limited to 'fs/debugfs/file.c')
-rw-r--r-- | fs/debugfs/file.c | 91 |
1 files changed, 90 insertions, 1 deletions
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index d2ba12e23ed9..736ab3c988f2 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c | |||
@@ -22,6 +22,9 @@ | |||
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/atomic.h> | 23 | #include <linux/atomic.h> |
24 | #include <linux/device.h> | 24 | #include <linux/device.h> |
25 | #include <linux/srcu.h> | ||
26 | |||
27 | #include "internal.h" | ||
25 | 28 | ||
26 | static ssize_t default_read_file(struct file *file, char __user *buf, | 29 | static ssize_t default_read_file(struct file *file, char __user *buf, |
27 | size_t count, loff_t *ppos) | 30 | size_t count, loff_t *ppos) |
@@ -35,13 +38,99 @@ static ssize_t default_write_file(struct file *file, const char __user *buf, | |||
35 | return count; | 38 | return count; |
36 | } | 39 | } |
37 | 40 | ||
38 | const struct file_operations debugfs_file_operations = { | 41 | const struct file_operations debugfs_noop_file_operations = { |
39 | .read = default_read_file, | 42 | .read = default_read_file, |
40 | .write = default_write_file, | 43 | .write = default_write_file, |
41 | .open = simple_open, | 44 | .open = simple_open, |
42 | .llseek = noop_llseek, | 45 | .llseek = noop_llseek, |
43 | }; | 46 | }; |
44 | 47 | ||
48 | /** | ||
49 | * debugfs_use_file_start - mark the beginning of file data access | ||
50 | * @dentry: the dentry object whose data is being accessed. | ||
51 | * @srcu_idx: a pointer to some memory to store a SRCU index in. | ||
52 | * | ||
53 | * Up to a matching call to debugfs_use_file_finish(), any | ||
54 | * successive call into the file removing functions debugfs_remove() | ||
55 | * and debugfs_remove_recursive() will block. Since associated private | ||
56 | * file data may only get freed after a successful return of any of | ||
57 | * the removal functions, you may safely access it after a successful | ||
58 | * call to debugfs_use_file_start() without worrying about | ||
59 | * lifetime issues. | ||
60 | * | ||
61 | * If -%EIO is returned, the file has already been removed and thus, | ||
62 | * it is not safe to access any of its data. If, on the other hand, | ||
63 | * it is allowed to access the file data, zero is returned. | ||
64 | * | ||
65 | * Regardless of the return code, any call to | ||
66 | * debugfs_use_file_start() must be followed by a matching call | ||
67 | * to debugfs_use_file_finish(). | ||
68 | */ | ||
69 | static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) | ||
70 | __acquires(&debugfs_srcu) | ||
71 | { | ||
72 | *srcu_idx = srcu_read_lock(&debugfs_srcu); | ||
73 | barrier(); | ||
74 | if (d_unlinked(dentry)) | ||
75 | return -EIO; | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * debugfs_use_file_finish - mark the end of file data access | ||
81 | * @srcu_idx: the SRCU index "created" by a former call to | ||
82 | * debugfs_use_file_start(). | ||
83 | * | ||
84 | * Allow any ongoing concurrent call into debugfs_remove() or | ||
85 | * debugfs_remove_recursive() blocked by a former call to | ||
86 | * debugfs_use_file_start() to proceed and return to its caller. | ||
87 | */ | ||
88 | static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) | ||
89 | { | ||
90 | srcu_read_unlock(&debugfs_srcu, srcu_idx); | ||
91 | } | ||
92 | |||
93 | #define F_DENTRY(filp) ((filp)->f_path.dentry) | ||
94 | |||
95 | #define REAL_FOPS_DEREF(dentry) \ | ||
96 | ((const struct file_operations *)(dentry)->d_fsdata) | ||
97 | |||
98 | static int open_proxy_open(struct inode *inode, struct file *filp) | ||
99 | { | ||
100 | const struct dentry *dentry = F_DENTRY(filp); | ||
101 | const struct file_operations *real_fops = NULL; | ||
102 | int srcu_idx, r; | ||
103 | |||
104 | r = debugfs_use_file_start(dentry, &srcu_idx); | ||
105 | if (r) { | ||
106 | r = -ENOENT; | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | real_fops = REAL_FOPS_DEREF(dentry); | ||
111 | real_fops = fops_get(real_fops); | ||
112 | if (!real_fops) { | ||
113 | /* Huh? Module did not clean up after itself at exit? */ | ||
114 | WARN(1, "debugfs file owner did not clean up at exit: %pd", | ||
115 | dentry); | ||
116 | r = -ENXIO; | ||
117 | goto out; | ||
118 | } | ||
119 | replace_fops(filp, real_fops); | ||
120 | |||
121 | if (real_fops->open) | ||
122 | r = real_fops->open(inode, filp); | ||
123 | |||
124 | out: | ||
125 | fops_put(real_fops); | ||
126 | debugfs_use_file_finish(srcu_idx); | ||
127 | return r; | ||
128 | } | ||
129 | |||
130 | const struct file_operations debugfs_open_proxy_file_operations = { | ||
131 | .open = open_proxy_open, | ||
132 | }; | ||
133 | |||
45 | static struct dentry *debugfs_create_mode(const char *name, umode_t mode, | 134 | static struct dentry *debugfs_create_mode(const char *name, umode_t mode, |
46 | struct dentry *parent, void *value, | 135 | struct dentry *parent, void *value, |
47 | const struct file_operations *fops, | 136 | const struct file_operations *fops, |