[PATCH 06/14] fsnotify: clear fsobject marks with object lock held

From: Lino Sanfilippo
Date: Wed Jan 19 2011 - 11:46:14 EST


It is now possible to lock a mark with the fsobject lock held. So instead of
iterating the marks list of an inode/vfsmount and putting all marks on a temporary
free list and freeing that list afterwards, the marks are already freed
while the list is iterated.

Signed-off-by: Lino Sanfilippo <LinoSanfilippo@xxxxxx>
---
fs/notify/inode_mark.c | 28 +++++++++++++------
fs/notify/vfsmount_mark.c | 55 ++++++++++++++++++++-----------------
include/linux/fsnotify_backend.h | 2 -
3 files changed, 49 insertions(+), 36 deletions(-)

diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 7403404..35bd4e3 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -105,22 +105,32 @@ void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark)
*/
void fsnotify_clear_marks_by_inode(struct inode *inode)
{
- struct fsnotify_mark *mark, *lmark;
+ struct fsnotify_mark *mark;
struct hlist_node *pos, *n;
- LIST_HEAD(free_list);

spin_lock(&inode->i_lock);
hlist_for_each_entry_safe(mark, pos, n, &inode->i_fsnotify_marks, i.i_list) {
- list_add(&mark->i.free_i_list, &free_list);
- hlist_del_init_rcu(&mark->i.i_list);
- fsnotify_get_mark(mark);
- }
- spin_unlock(&inode->i_lock);
+ spin_lock(&mark->lock);
+ if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
+ spin_unlock(&mark->lock);
+ continue;
+ }
+ mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
+ mark->i.inode = NULL;

- list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) {
- fsnotify_destroy_mark(mark);
+ if (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED) {
+ iput(inode);
+ mark->flags &= ~FSNOTIFY_MARK_FLAG_OBJECT_PINNED;
+ }
+ spin_unlock(&mark->lock);
+
+ /* remove mark from inode */
+ hlist_del_init_rcu(&mark->i.i_list);
+ /* release ref from list */
fsnotify_put_mark(mark);
}
+ fsnotify_recalc_inode_mask_locked(inode);
+ spin_unlock(&inode->i_lock);
}

/*
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
index 20286e3..d253a39 100644
--- a/fs/notify/vfsmount_mark.c
+++ b/fs/notify/vfsmount_mark.c
@@ -30,31 +30,6 @@
#include <linux/fsnotify_backend.h>
#include "fsnotify.h"

-void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
-{
- struct fsnotify_mark *mark, *lmark;
- struct hlist_node *pos, *n;
- LIST_HEAD(free_list);
-
- spin_lock(&mnt->mnt_root->d_lock);
- hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) {
- list_add(&mark->m.free_m_list, &free_list);
- hlist_del_init_rcu(&mark->m.m_list);
- fsnotify_get_mark(mark);
- }
- spin_unlock(&mnt->mnt_root->d_lock);
-
- list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) {
- fsnotify_destroy_mark(mark);
- fsnotify_put_mark(mark);
- }
-}
-
-void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group)
-{
- fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_VFSMOUNT);
-}
-
/*
* Recalculate the mask of events relevant to a given vfsmount locked.
*/
@@ -71,6 +46,36 @@ static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt)
mnt->mnt_fsnotify_mask = new_mask;
}

+void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
+{
+ struct fsnotify_mark *mark;
+ struct hlist_node *pos, *n;
+
+ spin_lock(&mnt->mnt_root->d_lock);
+ hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) {
+ spin_lock(&mark->lock);
+ if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
+ spin_unlock(&mark->lock);
+ continue;
+ }
+ mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
+ mark->m.mnt = NULL;
+ spin_unlock(&mark->lock);
+
+ /* remove mark from vfsmount*/
+ hlist_del_init_rcu(&mark->m.m_list);
+ /* release ref from list */
+ fsnotify_put_mark(mark);
+ }
+ fsnotify_recalc_vfsmount_mask_locked(mnt);
+ spin_unlock(&mnt->mnt_root->d_lock);
+}
+
+void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group)
+{
+ fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_VFSMOUNT);
+}
+
/*
* Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types
* any notifier is interested in hearing for this mount point
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index b018e78..f6891f9 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -255,7 +255,6 @@ struct fsnotify_event {
struct fsnotify_inode_mark {
struct inode *inode; /* inode this mark is associated with */
struct hlist_node i_list; /* list of marks by inode->i_fsnotify_marks */
- struct list_head free_i_list; /* tmp list used when freeing this mark */
};

/*
@@ -264,7 +263,6 @@ struct fsnotify_inode_mark {
struct fsnotify_vfsmount_mark {
struct vfsmount *mnt; /* vfsmount this mark is associated with */
struct hlist_node m_list; /* list of marks by inode->i_fsnotify_marks */
- struct list_head free_m_list; /* tmp list used when freeing this mark */
};

/*
--
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/