aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-05-21 17:01:58 -0400
committerEric Paris <eparis@redhat.com>2009-06-11 14:57:54 -0400
commit164bc6195139047faaf5ada1278332e99494803b (patch)
treefdc0e31f9dc15796a4777ed917533091797b8b69
parent1ef5f13c6c8acd3fd10db9f1743f3b4cf30a4abb (diff)
fsnotify: handle filesystem unmounts with fsnotify marks
When an fs is unmounted with an fsnotify mark entry attached to one of its inodes we need to destroy that mark entry and we also (like inotify) send an unmount event. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de>
-rw-r--r--fs/inode.c1
-rw-r--r--fs/notify/inode_mark.c72
-rw-r--r--include/linux/fsnotify_backend.h4
3 files changed, 77 insertions, 0 deletions
diff --git a/fs/inode.c b/fs/inode.c
index 54c63ce3de25..ca337014ae29 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -407,6 +407,7 @@ int invalidate_inodes(struct super_block *sb)
407 mutex_lock(&iprune_mutex); 407 mutex_lock(&iprune_mutex);
408 spin_lock(&inode_lock); 408 spin_lock(&inode_lock);
409 inotify_unmount_inodes(&sb->s_inodes); 409 inotify_unmount_inodes(&sb->s_inodes);
410 fsnotify_unmount_inodes(&sb->s_inodes);
410 busy = invalidate_list(&sb->s_inodes, &throw_away); 411 busy = invalidate_list(&sb->s_inodes, &throw_away);
411 spin_unlock(&inode_lock); 412 spin_unlock(&inode_lock);
412 413
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 282150f74cfa..0a499d2c6191 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -89,6 +89,7 @@
89#include <linux/mutex.h> 89#include <linux/mutex.h>
90#include <linux/slab.h> 90#include <linux/slab.h>
91#include <linux/spinlock.h> 91#include <linux/spinlock.h>
92#include <linux/writeback.h> /* for inode_lock */
92 93
93#include <asm/atomic.h> 94#include <asm/atomic.h>
94 95
@@ -351,3 +352,74 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry,
351 352
352 return ret; 353 return ret;
353} 354}
355
356/**
357 * fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes.
358 * @list: list of inodes being unmounted (sb->s_inodes)
359 *
360 * Called with inode_lock held, protecting the unmounting super block's list
361 * of inodes, and with iprune_mutex held, keeping shrink_icache_memory() at bay.
362 * We temporarily drop inode_lock, however, and CAN block.
363 */
364void fsnotify_unmount_inodes(struct list_head *list)
365{
366 struct inode *inode, *next_i, *need_iput = NULL;
367
368 list_for_each_entry_safe(inode, next_i, list, i_sb_list) {
369 struct inode *need_iput_tmp;
370
371 /*
372 * We cannot __iget() an inode in state I_CLEAR, I_FREEING,
373 * I_WILL_FREE, or I_NEW which is fine because by that point
374 * the inode cannot have any associated watches.
375 */
376 if (inode->i_state & (I_CLEAR|I_FREEING|I_WILL_FREE|I_NEW))
377 continue;
378
379 /*
380 * If i_count is zero, the inode cannot have any watches and
381 * doing an __iget/iput with MS_ACTIVE clear would actually
382 * evict all inodes with zero i_count from icache which is
383 * unnecessarily violent and may in fact be illegal to do.
384 */
385 if (!atomic_read(&inode->i_count))
386 continue;
387
388 need_iput_tmp = need_iput;
389 need_iput = NULL;
390
391 /* In case fsnotify_inode_delete() drops a reference. */
392 if (inode != need_iput_tmp)
393 __iget(inode);
394 else
395 need_iput_tmp = NULL;
396
397 /* In case the dropping of a reference would nuke next_i. */
398 if ((&next_i->i_sb_list != list) &&
399 atomic_read(&next_i->i_count) &&
400 !(next_i->i_state & (I_CLEAR | I_FREEING | I_WILL_FREE))) {
401 __iget(next_i);
402 need_iput = next_i;
403 }
404
405 /*
406 * We can safely drop inode_lock here because we hold
407 * references on both inode and next_i. Also no new inodes
408 * will be added since the umount has begun. Finally,
409 * iprune_mutex keeps shrink_icache_memory() away.
410 */
411 spin_unlock(&inode_lock);
412
413 if (need_iput_tmp)
414 iput(need_iput_tmp);
415
416 /* for each watch, send FS_UNMOUNT and then remove it */
417 fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
418
419 fsnotify_inode_delete(inode);
420
421 iput(inode);
422
423 spin_lock(&inode_lock);
424 }
425}
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index efdf9e442d86..d2c0ee30e618 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -336,6 +336,7 @@ extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry);
336extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group); 336extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group);
337extern void fsnotify_get_mark(struct fsnotify_mark_entry *entry); 337extern void fsnotify_get_mark(struct fsnotify_mark_entry *entry);
338extern void fsnotify_put_mark(struct fsnotify_mark_entry *entry); 338extern void fsnotify_put_mark(struct fsnotify_mark_entry *entry);
339extern void fsnotify_unmount_inodes(struct list_head *list);
339 340
340/* put here because inotify does some weird stuff when destroying watches */ 341/* put here because inotify does some weird stuff when destroying watches */
341extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, 342extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
@@ -365,6 +366,9 @@ static inline u32 fsnotify_get_cookie(void)
365 return 0; 366 return 0;
366} 367}
367 368
369static inline void fsnotify_unmount_inodes(struct list_head *list)
370{}
371
368#endif /* CONFIG_FSNOTIFY */ 372#endif /* CONFIG_FSNOTIFY */
369 373
370#endif /* __KERNEL __ */ 374#endif /* __KERNEL __ */