diff options
Diffstat (limited to 'fs/notify')
-rw-r--r-- | fs/notify/fsnotify.c | 36 | ||||
-rw-r--r-- | fs/notify/fsnotify.h | 4 | ||||
-rw-r--r-- | fs/notify/inode_mark.c | 25 | ||||
-rw-r--r-- | fs/notify/mark.c | 36 | ||||
-rw-r--r-- | fs/notify/vfsmount_mark.c | 8 |
5 files changed, 78 insertions, 31 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 700129940c6e..41e39102743a 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c | |||
@@ -229,8 +229,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | |||
229 | &fsnotify_mark_srcu); | 229 | &fsnotify_mark_srcu); |
230 | } | 230 | } |
231 | 231 | ||
232 | /* | ||
233 | * We need to merge inode & vfsmount mark lists so that inode mark | ||
234 | * ignore masks are properly reflected for mount mark notifications. | ||
235 | * That's why this traversal is so complicated... | ||
236 | */ | ||
232 | while (inode_node || vfsmount_node) { | 237 | while (inode_node || vfsmount_node) { |
233 | inode_group = vfsmount_group = NULL; | 238 | inode_group = NULL; |
239 | inode_mark = NULL; | ||
240 | vfsmount_group = NULL; | ||
241 | vfsmount_mark = NULL; | ||
234 | 242 | ||
235 | if (inode_node) { | 243 | if (inode_node) { |
236 | inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), | 244 | inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), |
@@ -244,21 +252,19 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, | |||
244 | vfsmount_group = vfsmount_mark->group; | 252 | vfsmount_group = vfsmount_mark->group; |
245 | } | 253 | } |
246 | 254 | ||
247 | if (inode_group > vfsmount_group) { | 255 | if (inode_group && vfsmount_group) { |
248 | /* handle inode */ | 256 | int cmp = fsnotify_compare_groups(inode_group, |
249 | ret = send_to_group(to_tell, inode_mark, NULL, mask, | 257 | vfsmount_group); |
250 | data, data_is, cookie, file_name); | 258 | if (cmp > 0) { |
251 | /* we didn't use the vfsmount_mark */ | 259 | inode_group = NULL; |
252 | vfsmount_group = NULL; | 260 | inode_mark = NULL; |
253 | } else if (vfsmount_group > inode_group) { | 261 | } else if (cmp < 0) { |
254 | ret = send_to_group(to_tell, NULL, vfsmount_mark, mask, | 262 | vfsmount_group = NULL; |
255 | data, data_is, cookie, file_name); | 263 | vfsmount_mark = NULL; |
256 | inode_group = NULL; | 264 | } |
257 | } else { | ||
258 | ret = send_to_group(to_tell, inode_mark, vfsmount_mark, | ||
259 | mask, data, data_is, cookie, | ||
260 | file_name); | ||
261 | } | 265 | } |
266 | ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask, | ||
267 | data, data_is, cookie, file_name); | ||
262 | 268 | ||
263 | if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS)) | 269 | if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS)) |
264 | goto out; | 270 | goto out; |
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 9c0898c4cfe1..3b68b0ae0a97 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h | |||
@@ -12,6 +12,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group); | |||
12 | /* protects reads of inode and vfsmount marks list */ | 12 | /* protects reads of inode and vfsmount marks list */ |
13 | extern struct srcu_struct fsnotify_mark_srcu; | 13 | extern struct srcu_struct fsnotify_mark_srcu; |
14 | 14 | ||
15 | /* compare two groups for sorting of marks lists */ | ||
16 | extern int fsnotify_compare_groups(struct fsnotify_group *a, | ||
17 | struct fsnotify_group *b); | ||
18 | |||
15 | extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark, | 19 | extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark, |
16 | __u32 mask); | 20 | __u32 mask); |
17 | /* add a mark to an inode */ | 21 | /* add a mark to an inode */ |
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 9ce062218de9..dfbf5447eea4 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c | |||
@@ -194,6 +194,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, | |||
194 | { | 194 | { |
195 | struct fsnotify_mark *lmark, *last = NULL; | 195 | struct fsnotify_mark *lmark, *last = NULL; |
196 | int ret = 0; | 196 | int ret = 0; |
197 | int cmp; | ||
197 | 198 | ||
198 | mark->flags |= FSNOTIFY_MARK_FLAG_INODE; | 199 | mark->flags |= FSNOTIFY_MARK_FLAG_INODE; |
199 | 200 | ||
@@ -219,11 +220,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, | |||
219 | goto out; | 220 | goto out; |
220 | } | 221 | } |
221 | 222 | ||
222 | if (mark->group->priority < lmark->group->priority) | 223 | cmp = fsnotify_compare_groups(lmark->group, mark->group); |
223 | continue; | 224 | if (cmp < 0) |
224 | |||
225 | if ((mark->group->priority == lmark->group->priority) && | ||
226 | (mark->group < lmark->group)) | ||
227 | continue; | 225 | continue; |
228 | 226 | ||
229 | hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); | 227 | hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); |
@@ -288,20 +286,25 @@ void fsnotify_unmount_inodes(struct list_head *list) | |||
288 | spin_unlock(&inode->i_lock); | 286 | spin_unlock(&inode->i_lock); |
289 | 287 | ||
290 | /* In case the dropping of a reference would nuke next_i. */ | 288 | /* In case the dropping of a reference would nuke next_i. */ |
291 | if ((&next_i->i_sb_list != list) && | 289 | while (&next_i->i_sb_list != list) { |
292 | atomic_read(&next_i->i_count)) { | ||
293 | spin_lock(&next_i->i_lock); | 290 | spin_lock(&next_i->i_lock); |
294 | if (!(next_i->i_state & (I_FREEING | I_WILL_FREE))) { | 291 | if (!(next_i->i_state & (I_FREEING | I_WILL_FREE)) && |
292 | atomic_read(&next_i->i_count)) { | ||
295 | __iget(next_i); | 293 | __iget(next_i); |
296 | need_iput = next_i; | 294 | need_iput = next_i; |
295 | spin_unlock(&next_i->i_lock); | ||
296 | break; | ||
297 | } | 297 | } |
298 | spin_unlock(&next_i->i_lock); | 298 | spin_unlock(&next_i->i_lock); |
299 | next_i = list_entry(next_i->i_sb_list.next, | ||
300 | struct inode, i_sb_list); | ||
299 | } | 301 | } |
300 | 302 | ||
301 | /* | 303 | /* |
302 | * We can safely drop inode_sb_list_lock here because we hold | 304 | * We can safely drop inode_sb_list_lock here because either |
303 | * references on both inode and next_i. Also no new inodes | 305 | * we actually hold references on both inode and next_i or |
304 | * will be added since the umount has begun. | 306 | * end of list. Also no new inodes will be added since the |
307 | * umount has begun. | ||
305 | */ | 308 | */ |
306 | spin_unlock(&inode_sb_list_lock); | 309 | spin_unlock(&inode_sb_list_lock); |
307 | 310 | ||
diff --git a/fs/notify/mark.c b/fs/notify/mark.c index d90deaa08e78..34c38fabf514 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c | |||
@@ -210,6 +210,42 @@ void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mas | |||
210 | } | 210 | } |
211 | 211 | ||
212 | /* | 212 | /* |
213 | * Sorting function for lists of fsnotify marks. | ||
214 | * | ||
215 | * Fanotify supports different notification classes (reflected as priority of | ||
216 | * notification group). Events shall be passed to notification groups in | ||
217 | * decreasing priority order. To achieve this marks in notification lists for | ||
218 | * inodes and vfsmounts are sorted so that priorities of corresponding groups | ||
219 | * are descending. | ||
220 | * | ||
221 | * Furthermore correct handling of the ignore mask requires processing inode | ||
222 | * and vfsmount marks of each group together. Using the group address as | ||
223 | * further sort criterion provides a unique sorting order and thus we can | ||
224 | * merge inode and vfsmount lists of marks in linear time and find groups | ||
225 | * present in both lists. | ||
226 | * | ||
227 | * A return value of 1 signifies that b has priority over a. | ||
228 | * A return value of 0 signifies that the two marks have to be handled together. | ||
229 | * A return value of -1 signifies that a has priority over b. | ||
230 | */ | ||
231 | int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b) | ||
232 | { | ||
233 | if (a == b) | ||
234 | return 0; | ||
235 | if (!a) | ||
236 | return 1; | ||
237 | if (!b) | ||
238 | return -1; | ||
239 | if (a->priority < b->priority) | ||
240 | return 1; | ||
241 | if (a->priority > b->priority) | ||
242 | return -1; | ||
243 | if (a < b) | ||
244 | return 1; | ||
245 | return -1; | ||
246 | } | ||
247 | |||
248 | /* | ||
213 | * Attach an initialized mark to a given group and fs object. | 249 | * Attach an initialized mark to a given group and fs object. |
214 | * These marks may be used for the fsnotify backend to determine which | 250 | * These marks may be used for the fsnotify backend to determine which |
215 | * event types should be delivered to which group. | 251 | * event types should be delivered to which group. |
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index ac851e8376b1..faefa72a11eb 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c | |||
@@ -153,6 +153,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, | |||
153 | struct mount *m = real_mount(mnt); | 153 | struct mount *m = real_mount(mnt); |
154 | struct fsnotify_mark *lmark, *last = NULL; | 154 | struct fsnotify_mark *lmark, *last = NULL; |
155 | int ret = 0; | 155 | int ret = 0; |
156 | int cmp; | ||
156 | 157 | ||
157 | mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; | 158 | mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; |
158 | 159 | ||
@@ -178,11 +179,8 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, | |||
178 | goto out; | 179 | goto out; |
179 | } | 180 | } |
180 | 181 | ||
181 | if (mark->group->priority < lmark->group->priority) | 182 | cmp = fsnotify_compare_groups(lmark->group, mark->group); |
182 | continue; | 183 | if (cmp < 0) |
183 | |||
184 | if ((mark->group->priority == lmark->group->priority) && | ||
185 | (mark->group < lmark->group)) | ||
186 | continue; | 184 | continue; |
187 | 185 | ||
188 | hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list); | 186 | hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list); |