diff options
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r-- | fs/notify/fsnotify.c | 92 |
1 files changed, 70 insertions, 22 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 23b5cfbeed50..a61aaa710825 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/gfp.h> | 21 | #include <linux/gfp.h> |
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/mount.h> | ||
24 | #include <linux/srcu.h> | 25 | #include <linux/srcu.h> |
25 | 26 | ||
26 | #include <linux/fsnotify_backend.h> | 27 | #include <linux/fsnotify_backend.h> |
@@ -134,6 +135,45 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) | |||
134 | } | 135 | } |
135 | EXPORT_SYMBOL_GPL(__fsnotify_parent); | 136 | EXPORT_SYMBOL_GPL(__fsnotify_parent); |
136 | 137 | ||
138 | static void send_to_group(__u32 mask, | ||
139 | struct fsnotify_group *group, | ||
140 | void *data, int data_is, const char *file_name, | ||
141 | u32 cookie, struct fsnotify_event **event, | ||
142 | struct inode *to_tell) | ||
143 | { | ||
144 | if (!group->ops->should_send_event(group, to_tell, mask, | ||
145 | data, data_is)) | ||
146 | return; | ||
147 | if (!*event) { | ||
148 | *event = fsnotify_create_event(to_tell, mask, data, | ||
149 | data_is, file_name, | ||
150 | cookie, GFP_KERNEL); | ||
151 | /* | ||
152 | * shit, we OOM'd and now we can't tell, maybe | ||
153 | * someday someone else will want to do something | ||
154 | * here | ||
155 | */ | ||
156 | if (!*event) | ||
157 | return; | ||
158 | } | ||
159 | group->ops->handle_event(group, *event); | ||
160 | } | ||
161 | |||
162 | static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is) | ||
163 | { | ||
164 | struct path *path; | ||
165 | |||
166 | if (data_is == FSNOTIFY_EVENT_PATH) | ||
167 | path = (struct path *)data; | ||
168 | else if (data_is == FSNOTIFY_EVENT_FILE) | ||
169 | path = &((struct file *)data)->f_path; | ||
170 | else | ||
171 | return false; | ||
172 | |||
173 | /* hook in this when mnt->mnt_fsnotify_mask is defined */ | ||
174 | /* return (test_mask & path->mnt->mnt_fsnotify_mask); */ | ||
175 | return false; | ||
176 | } | ||
137 | /* | 177 | /* |
138 | * This is the main call to fsnotify. The VFS calls into hook specific functions | 178 | * This is the main call to fsnotify. The VFS calls into hook specific functions |
139 | * in linux/fsnotify.h. Those functions then in turn call here. Here will call | 179 | * in linux/fsnotify.h. Those functions then in turn call here. Here will call |
@@ -148,38 +188,46 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const | |||
148 | /* global tests shouldn't care about events on child only the specific event */ | 188 | /* global tests shouldn't care about events on child only the specific event */ |
149 | __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); | 189 | __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); |
150 | 190 | ||
151 | if (list_empty(&fsnotify_inode_groups)) | 191 | /* if no fsnotify listeners, nothing to do */ |
152 | return; | 192 | if (list_empty(&fsnotify_inode_groups) && |
193 | list_empty(&fsnotify_vfsmount_groups)) | ||
194 | return; | ||
195 | |||
196 | /* if none of the directed listeners or vfsmount listeners care */ | ||
197 | if (!(test_mask & fsnotify_inode_mask) && | ||
198 | !(test_mask & fsnotify_vfsmount_mask)) | ||
199 | return; | ||
200 | |||
201 | /* if this inode's directed listeners don't care and nothing on the vfsmount | ||
202 | * listeners list cares, nothing to do */ | ||
203 | if (!(test_mask & to_tell->i_fsnotify_mask) && | ||
204 | !needed_by_vfsmount(test_mask, data, data_is)) | ||
205 | return; | ||
153 | 206 | ||
154 | if (!(test_mask & fsnotify_inode_mask)) | ||
155 | return; | ||
156 | |||
157 | if (!(test_mask & to_tell->i_fsnotify_mask)) | ||
158 | return; | ||
159 | /* | 207 | /* |
160 | * SRCU!! the groups list is very very much read only and the path is | 208 | * SRCU!! the groups list is very very much read only and the path is |
161 | * very hot. The VAST majority of events are not going to need to do | 209 | * very hot. The VAST majority of events are not going to need to do |
162 | * anything other than walk the list so it's crazy to pre-allocate. | 210 | * anything other than walk the list so it's crazy to pre-allocate. |
163 | */ | 211 | */ |
164 | idx = srcu_read_lock(&fsnotify_grp_srcu); | 212 | idx = srcu_read_lock(&fsnotify_grp_srcu); |
165 | list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { | 213 | |
166 | if (test_mask & group->mask) { | 214 | if (test_mask & to_tell->i_fsnotify_mask) { |
167 | if (!group->ops->should_send_event(group, to_tell, mask, | 215 | list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { |
168 | data, data_is)) | 216 | if (test_mask & group->mask) { |
169 | continue; | 217 | send_to_group(mask, group, data, data_is, |
170 | if (!event) { | 218 | file_name, cookie, &event, to_tell); |
171 | event = fsnotify_create_event(to_tell, mask, data, | ||
172 | data_is, file_name, cookie, | ||
173 | GFP_KERNEL); | ||
174 | /* shit, we OOM'd and now we can't tell, maybe | ||
175 | * someday someone else will want to do something | ||
176 | * here */ | ||
177 | if (!event) | ||
178 | break; | ||
179 | } | 219 | } |
180 | group->ops->handle_event(group, event); | ||
181 | } | 220 | } |
182 | } | 221 | } |
222 | if (needed_by_vfsmount(test_mask, data, data_is)) { | ||
223 | list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { | ||
224 | if (test_mask & group->mask) { | ||
225 | send_to_group(mask, group, data, data_is, | ||
226 | file_name, cookie, &event, to_tell); | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
183 | srcu_read_unlock(&fsnotify_grp_srcu, idx); | 231 | srcu_read_unlock(&fsnotify_grp_srcu, idx); |
184 | /* | 232 | /* |
185 | * fsnotify_create_event() took a reference so the event can't be cleaned | 233 | * fsnotify_create_event() took a reference so the event can't be cleaned |