diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/notify/fsnotify.c | 92 | ||||
-rw-r--r-- | fs/notify/fsnotify.h | 6 | ||||
-rw-r--r-- | fs/notify/group.c | 33 | ||||
-rw-r--r-- | fs/notify/inode_mark.c | 7 |
4 files changed, 112 insertions, 26 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 |
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 5bd22412017f..2ba59158969f 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h | |||
@@ -10,14 +10,20 @@ | |||
10 | extern struct srcu_struct fsnotify_grp_srcu; | 10 | extern struct srcu_struct fsnotify_grp_srcu; |
11 | /* all groups which receive inode fsnotify events */ | 11 | /* all groups which receive inode fsnotify events */ |
12 | extern struct list_head fsnotify_inode_groups; | 12 | extern struct list_head fsnotify_inode_groups; |
13 | /* all groups which receive vfsmount fsnotify events */ | ||
14 | extern struct list_head fsnotify_vfsmount_groups; | ||
13 | /* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ | 15 | /* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ |
14 | extern __u32 fsnotify_inode_mask; | 16 | extern __u32 fsnotify_inode_mask; |
17 | /* all bitwise OR of all event types (FS_*) for all fsnotify_vfsmount_groups */ | ||
18 | extern __u32 fsnotify_vfsmount_mask; | ||
15 | 19 | ||
16 | /* destroy all events sitting in this groups notification queue */ | 20 | /* destroy all events sitting in this groups notification queue */ |
17 | extern void fsnotify_flush_notify(struct fsnotify_group *group); | 21 | extern void fsnotify_flush_notify(struct fsnotify_group *group); |
18 | 22 | ||
19 | /* add a group to the inode group list */ | 23 | /* add a group to the inode group list */ |
20 | extern void fsnotify_add_inode_group(struct fsnotify_group *group); | 24 | extern void fsnotify_add_inode_group(struct fsnotify_group *group); |
25 | /* add a group to the vfsmount group list */ | ||
26 | extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); | ||
21 | /* final kfree of a group */ | 27 | /* final kfree of a group */ |
22 | extern void fsnotify_final_destroy_group(struct fsnotify_group *group); | 28 | extern void fsnotify_final_destroy_group(struct fsnotify_group *group); |
23 | 29 | ||
diff --git a/fs/notify/group.c b/fs/notify/group.c index 34fccbd2809c..aa4654fe6ec2 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c | |||
@@ -32,10 +32,14 @@ | |||
32 | static DEFINE_MUTEX(fsnotify_grp_mutex); | 32 | static DEFINE_MUTEX(fsnotify_grp_mutex); |
33 | /* protects reads while running the fsnotify_groups list */ | 33 | /* protects reads while running the fsnotify_groups list */ |
34 | struct srcu_struct fsnotify_grp_srcu; | 34 | struct srcu_struct fsnotify_grp_srcu; |
35 | /* all groups registered to receive filesystem notifications */ | 35 | /* all groups registered to receive inode filesystem notifications */ |
36 | LIST_HEAD(fsnotify_inode_groups); | 36 | LIST_HEAD(fsnotify_inode_groups); |
37 | /* all groups registered to receive mount point filesystem notifications */ | ||
38 | LIST_HEAD(fsnotify_vfsmount_groups); | ||
37 | /* bitwise OR of all events (FS_*) interesting to some group on this system */ | 39 | /* bitwise OR of all events (FS_*) interesting to some group on this system */ |
38 | __u32 fsnotify_inode_mask; | 40 | __u32 fsnotify_inode_mask; |
41 | /* bitwise OR of all events (FS_*) interesting to some group on this system */ | ||
42 | __u32 fsnotify_vfsmount_mask; | ||
39 | 43 | ||
40 | /* | 44 | /* |
41 | * When a new group registers or changes it's set of interesting events | 45 | * When a new group registers or changes it's set of interesting events |
@@ -44,14 +48,20 @@ __u32 fsnotify_inode_mask; | |||
44 | void fsnotify_recalc_global_mask(void) | 48 | void fsnotify_recalc_global_mask(void) |
45 | { | 49 | { |
46 | struct fsnotify_group *group; | 50 | struct fsnotify_group *group; |
47 | __u32 mask = 0; | 51 | __u32 inode_mask = 0; |
52 | __u32 vfsmount_mask = 0; | ||
48 | int idx; | 53 | int idx; |
49 | 54 | ||
50 | idx = srcu_read_lock(&fsnotify_grp_srcu); | 55 | idx = srcu_read_lock(&fsnotify_grp_srcu); |
51 | list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) | 56 | list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) |
52 | mask |= group->mask; | 57 | inode_mask |= group->mask; |
58 | list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) | ||
59 | vfsmount_mask |= group->mask; | ||
60 | |||
53 | srcu_read_unlock(&fsnotify_grp_srcu, idx); | 61 | srcu_read_unlock(&fsnotify_grp_srcu, idx); |
54 | fsnotify_inode_mask = mask; | 62 | |
63 | fsnotify_inode_mask = inode_mask; | ||
64 | fsnotify_vfsmount_mask = vfsmount_mask; | ||
55 | } | 65 | } |
56 | 66 | ||
57 | /* | 67 | /* |
@@ -77,6 +87,17 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) | |||
77 | fsnotify_recalc_global_mask(); | 87 | fsnotify_recalc_global_mask(); |
78 | } | 88 | } |
79 | 89 | ||
90 | void fsnotify_add_vfsmount_group(struct fsnotify_group *group) | ||
91 | { | ||
92 | mutex_lock(&fsnotify_grp_mutex); | ||
93 | |||
94 | if (!group->on_vfsmount_group_list) | ||
95 | list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); | ||
96 | group->on_vfsmount_group_list = 1; | ||
97 | |||
98 | mutex_unlock(&fsnotify_grp_mutex); | ||
99 | } | ||
100 | |||
80 | void fsnotify_add_inode_group(struct fsnotify_group *group) | 101 | void fsnotify_add_inode_group(struct fsnotify_group *group) |
81 | { | 102 | { |
82 | mutex_lock(&fsnotify_grp_mutex); | 103 | mutex_lock(&fsnotify_grp_mutex); |
@@ -132,6 +153,9 @@ static void __fsnotify_evict_group(struct fsnotify_group *group) | |||
132 | if (group->on_inode_group_list) | 153 | if (group->on_inode_group_list) |
133 | list_del_rcu(&group->inode_group_list); | 154 | list_del_rcu(&group->inode_group_list); |
134 | group->on_inode_group_list = 0; | 155 | group->on_inode_group_list = 0; |
156 | if (group->on_vfsmount_group_list) | ||
157 | list_del_rcu(&group->vfsmount_group_list); | ||
158 | group->on_vfsmount_group_list = 0; | ||
135 | } | 159 | } |
136 | 160 | ||
137 | /* | 161 | /* |
@@ -197,6 +221,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) | |||
197 | group->max_events = UINT_MAX; | 221 | group->max_events = UINT_MAX; |
198 | 222 | ||
199 | INIT_LIST_HEAD(&group->inode_group_list); | 223 | INIT_LIST_HEAD(&group->inode_group_list); |
224 | INIT_LIST_HEAD(&group->vfsmount_group_list); | ||
200 | 225 | ||
201 | spin_lock_init(&group->mark_lock); | 226 | spin_lock_init(&group->mark_lock); |
202 | INIT_LIST_HEAD(&group->mark_entries); | 227 | INIT_LIST_HEAD(&group->mark_entries); |
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a3230c485531..beffebb64627 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c | |||
@@ -328,6 +328,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, | |||
328 | */ | 328 | */ |
329 | if (unlikely(list_empty(&group->inode_group_list))) | 329 | if (unlikely(list_empty(&group->inode_group_list))) |
330 | fsnotify_add_inode_group(group); | 330 | fsnotify_add_inode_group(group); |
331 | /* | ||
332 | * XXX This is where we could also do the fsnotify_add_vfsmount_group | ||
333 | * if we are setting and vfsmount mark.... | ||
334 | |||
335 | if (unlikely(list_empty(&group->vfsmount_group_list))) | ||
336 | fsnotify_add_vfsmount_group(group); | ||
337 | */ | ||
331 | 338 | ||
332 | /* | 339 | /* |
333 | * LOCKING ORDER!!!! | 340 | * LOCKING ORDER!!!! |