diff options
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r-- | fs/notify/fsnotify.c | 69 |
1 files changed, 45 insertions, 24 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 4788c866473a..4678b416241e 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c | |||
@@ -144,14 +144,15 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) | |||
144 | { | 144 | { |
145 | struct fsnotify_mark *mark; | 145 | struct fsnotify_mark *mark; |
146 | struct hlist_node *node; | 146 | struct hlist_node *node; |
147 | int idx; | ||
148 | |||
149 | idx = srcu_read_lock(&fsnotify_mark_srcu); | ||
147 | 150 | ||
148 | if (!hlist_empty(&inode->i_fsnotify_marks)) { | 151 | if (!hlist_empty(&inode->i_fsnotify_marks)) { |
149 | spin_lock(&inode->i_lock); | 152 | hlist_for_each_entry_rcu(mark, node, &inode->i_fsnotify_marks, i.i_list) { |
150 | hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { | ||
151 | if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) | 153 | if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) |
152 | mark->ignored_mask = 0; | 154 | mark->ignored_mask = 0; |
153 | } | 155 | } |
154 | spin_unlock(&inode->i_lock); | ||
155 | } | 156 | } |
156 | 157 | ||
157 | if (data_is == FSNOTIFY_EVENT_FILE) { | 158 | if (data_is == FSNOTIFY_EVENT_FILE) { |
@@ -159,14 +160,14 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) | |||
159 | 160 | ||
160 | mnt = ((struct file *)data)->f_path.mnt; | 161 | mnt = ((struct file *)data)->f_path.mnt; |
161 | if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { | 162 | if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { |
162 | spin_lock(&mnt->mnt_root->d_lock); | 163 | hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { |
163 | hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { | ||
164 | if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) | 164 | if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) |
165 | mark->ignored_mask = 0; | 165 | mark->ignored_mask = 0; |
166 | } | 166 | } |
167 | spin_unlock(&mnt->mnt_root->d_lock); | ||
168 | } | 167 | } |
169 | } | 168 | } |
169 | |||
170 | srcu_read_unlock(&fsnotify_mark_srcu, idx); | ||
170 | } | 171 | } |
171 | 172 | ||
172 | static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, | 173 | static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, |
@@ -208,8 +209,10 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) | |||
208 | int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | 209 | int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, |
209 | const unsigned char *file_name, u32 cookie) | 210 | const unsigned char *file_name, u32 cookie) |
210 | { | 211 | { |
212 | struct fsnotify_mark *mark; | ||
211 | struct fsnotify_group *group; | 213 | struct fsnotify_group *group; |
212 | struct fsnotify_event *event = NULL; | 214 | struct fsnotify_event *event = NULL; |
215 | struct hlist_node *node; | ||
213 | struct vfsmount *mnt = NULL; | 216 | struct vfsmount *mnt = NULL; |
214 | int idx, ret = 0; | 217 | int idx, ret = 0; |
215 | /* global tests shouldn't care about events on child only the specific event */ | 218 | /* global tests shouldn't care about events on child only the specific event */ |
@@ -237,35 +240,47 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | |||
237 | !needed_by_vfsmount(test_mask, mnt)) | 240 | !needed_by_vfsmount(test_mask, mnt)) |
238 | return 0; | 241 | return 0; |
239 | 242 | ||
240 | /* | 243 | idx = srcu_read_lock(&fsnotify_mark_srcu); |
241 | * SRCU!! the groups list is very very much read only and the path is | ||
242 | * very hot. The VAST majority of events are not going to need to do | ||
243 | * anything other than walk the list so it's crazy to pre-allocate. | ||
244 | */ | ||
245 | idx = srcu_read_lock(&fsnotify_grp_srcu); | ||
246 | 244 | ||
247 | if (test_mask & to_tell->i_fsnotify_mask) { | 245 | if (test_mask & to_tell->i_fsnotify_mask) { |
248 | list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { | 246 | hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { |
249 | if (test_mask & group->mask) { | 247 | |
250 | ret = send_to_group(group, to_tell, NULL, mask, data, data_is, | 248 | pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", |
251 | cookie, file_name, &event); | 249 | __func__, mark, mark->mask, mark->ignored_mask); |
250 | |||
251 | if (test_mask & mark->mask & ~mark->ignored_mask) { | ||
252 | group = mark->group; | ||
253 | if (!group) | ||
254 | continue; | ||
255 | ret = send_to_group(group, to_tell, NULL, mask, | ||
256 | data, data_is, cookie, file_name, | ||
257 | &event); | ||
252 | if (ret) | 258 | if (ret) |
253 | goto out; | 259 | goto out; |
254 | } | 260 | } |
255 | } | 261 | } |
256 | } | 262 | } |
257 | if (needed_by_vfsmount(test_mask, mnt)) { | 263 | |
258 | list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { | 264 | if (mnt && (test_mask & mnt->mnt_fsnotify_mask)) { |
259 | if (test_mask & group->mask) { | 265 | hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { |
260 | ret = send_to_group(group, to_tell, mnt, mask, data, data_is, | 266 | |
261 | cookie, file_name, &event); | 267 | pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", |
268 | __func__, mark, mark->mask, mark->ignored_mask); | ||
269 | |||
270 | if (test_mask & mark->mask & ~mark->ignored_mask) { | ||
271 | group = mark->group; | ||
272 | if (!group) | ||
273 | continue; | ||
274 | ret = send_to_group(group, to_tell, mnt, mask, | ||
275 | data, data_is, cookie, file_name, | ||
276 | &event); | ||
262 | if (ret) | 277 | if (ret) |
263 | goto out; | 278 | goto out; |
264 | } | 279 | } |
265 | } | 280 | } |
266 | } | 281 | } |
267 | out: | 282 | out: |
268 | srcu_read_unlock(&fsnotify_grp_srcu, idx); | 283 | srcu_read_unlock(&fsnotify_mark_srcu, idx); |
269 | /* | 284 | /* |
270 | * fsnotify_create_event() took a reference so the event can't be cleaned | 285 | * fsnotify_create_event() took a reference so the event can't be cleaned |
271 | * up while we are still trying to add it to lists, drop that one. | 286 | * up while we are still trying to add it to lists, drop that one. |
@@ -279,8 +294,14 @@ EXPORT_SYMBOL_GPL(fsnotify); | |||
279 | 294 | ||
280 | static __init int fsnotify_init(void) | 295 | static __init int fsnotify_init(void) |
281 | { | 296 | { |
297 | int ret; | ||
298 | |||
282 | BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); | 299 | BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); |
283 | 300 | ||
284 | return init_srcu_struct(&fsnotify_grp_srcu); | 301 | ret = init_srcu_struct(&fsnotify_mark_srcu); |
302 | if (ret) | ||
303 | panic("initializing fsnotify_mark_srcu"); | ||
304 | |||
305 | return 0; | ||
285 | } | 306 | } |
286 | subsys_initcall(fsnotify_init); | 307 | core_initcall(fsnotify_init); |