From 9e1c74321d87a8b079f04d89e750b39a43365e1f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:05 -0500 Subject: fsnotify: duplicate fsnotify_mark_entry data between 2 marks Simple copy fsnotify information from one mark to another in preparation for the second mark to replace the first. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0399bcb..a13cf9e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -282,12 +282,20 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou return NULL; } +void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) +{ + assert_spin_locked(&old->lock); + new->inode = old->inode; + new->group = old->group; + new->mask = old->mask; + new->free_mark = old->free_mark; +} + /* * Nothing fancy, just initialize lists and locks and counters. */ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry)) - { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); -- cgit v1.1 From 40554c3dae83bd892b7fbfaa2ea9de739cbcf065 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:05 -0500 Subject: fsnotify: allow addition of duplicate fsnotify marks This patch allows a task to add a second fsnotify mark to an inode for the same group. This mark will be added to the end of the inode's list and this will never be found by the stand fsnotify_find_mark() function. This is useful if a user wants to add a new mark before removing the old one. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a13cf9e..7d2962e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -312,9 +312,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, * event types should be delivered to which group and for which inodes. */ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, - struct fsnotify_group *group, struct inode *inode) + struct fsnotify_group *group, struct inode *inode, + int allow_dups) { - struct fsnotify_mark_entry *lentry; + struct fsnotify_mark_entry *lentry = NULL; int ret = 0; inode = igrab(inode); @@ -331,7 +332,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - lentry = fsnotify_find_mark_entry(group, inode); + if (!allow_dups) + lentry = fsnotify_find_mark_entry(group, inode); if (!lentry) { entry->group = group; entry->inode = inode; -- cgit v1.1 From 4ca763523e040dc61191d4866a82981a5d30a4e9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: add groups to fsnotify_inode_groups when registering inode watch Currently all fsnotify groups are added immediately to the fsnotify_inode_groups list upon creation. This means, even groups with no watches (common for audit) will be on the global tracking list and will get checked for every event. This patch adds groups to the global list on when the first inode mark is added to the group. Signed-of-by: Eric Paris --- fs/notify/inode_mark.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 7d2962e..a3230c4 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -323,6 +323,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, return -EINVAL; /* + * if this group isn't being testing for inode type events we need + * to start testing + */ + if (unlikely(list_empty(&group->inode_group_list))) + fsnotify_add_inode_group(group); + + /* * LOCKING ORDER!!!! * entry->lock * group->mark_lock -- cgit v1.1 From 7131485a93679ff9a543b74df280cfd119eb03ca Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: mount point listeners list and global mask currently all of the notification systems implemented select which inodes they care about and receive messages only about those inodes (or the children of those inodes.) This patch begins to flesh out fsnotify support for the concept of listeners that want to hear notification for an inode accessed below a given monut point. This patch implements a second list of fsnotify groups to hold these types of groups and a second global mask to hold the events of interest for this type of group. The reason we want a second group list and mask is because the inode based notification should_send_event support which makes each group look for a mark on the given inode. With one nfsmount listener that means that every group would have to take the inode->i_lock, look for their mark, not find one, and return for every operation. By seperating vfsmount from inode listeners only when there is a inode listener will the inode groups have to look for their mark and take the inode lock. vfsmount listeners will have to grab the lock and look for a mark but there should be fewer of them, and one vfsmount listener won't cause the i_lock to be grabbed and released for every fsnotify group on every io operation. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a3230c4..beffebb 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, */ if (unlikely(list_empty(&group->inode_group_list))) fsnotify_add_inode_group(group); + /* + * XXX This is where we could also do the fsnotify_add_vfsmount_group + * if we are setting and vfsmount mark.... + + if (unlikely(list_empty(&group->vfsmount_group_list))) + fsnotify_add_vfsmount_group(group); + */ /* * LOCKING ORDER!!!! -- cgit v1.1 From 2823e04de4f1a49087b58ff2bb8f61361ffd9321 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: put inode specific fields in an fsnotify_mark in a union The addition of marks on vfs mounts will be simplified if the inode specific parts of a mark and the vfsmnt specific parts of a mark are actually in a union so naming can be easy. This patch just implements the inode struct and the union. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index beffebb..6731408 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -117,7 +117,7 @@ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i_list) + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) new_mask |= entry->mask; inode->i_fsnotify_mask = new_mask; } @@ -148,7 +148,7 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) spin_lock(&entry->lock); group = entry->group; - inode = entry->inode; + inode = entry->i.inode; BUG_ON(group && !inode); BUG_ON(!group && inode); @@ -165,8 +165,8 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - hlist_del_init(&entry->i_list); - entry->inode = NULL; + hlist_del_init(&entry->i.i_list); + entry->i.inode = NULL; list_del_init(&entry->g_list); entry->group = NULL; @@ -248,14 +248,14 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i_list) { - list_add(&entry->free_i_list, &free_list); - hlist_del_init(&entry->i_list); + hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i.i_list) { + list_add(&entry->i.free_i_list, &free_list); + hlist_del_init(&entry->i.i_list); fsnotify_get_mark(entry); } spin_unlock(&inode->i_lock); - list_for_each_entry_safe(entry, lentry, &free_list, free_i_list) { + list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { fsnotify_destroy_mark_by_entry(entry); fsnotify_put_mark(entry); } @@ -273,7 +273,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i_list) { + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) { if (entry->group == group) { fsnotify_get_mark(entry); return entry; @@ -285,7 +285,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) { assert_spin_locked(&old->lock); - new->inode = old->inode; + new->i.inode = old->i.inode; new->group = old->group; new->mask = old->mask; new->free_mark = old->free_mark; @@ -299,10 +299,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); - INIT_HLIST_NODE(&entry->i_list); + INIT_HLIST_NODE(&entry->i.i_list); entry->group = NULL; entry->mask = 0; - entry->inode = NULL; + entry->i.inode = NULL; entry->free_mark = free_mark; } @@ -350,9 +350,9 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, lentry = fsnotify_find_mark_entry(group, inode); if (!lentry) { entry->group = group; - entry->inode = inode; + entry->i.inode = inode; - hlist_add_head(&entry->i_list, &inode->i_fsnotify_mark_entries); + hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_mark_entries); list_add(&entry->g_list, &group->mark_entries); fsnotify_get_mark(entry); /* for i_list and g_list */ -- cgit v1.1 From 098cf2fc77ee190c92bf9d08d69a13305f2487ec Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: add flags to fsnotify_mark_entries To differentiate between inode and vfsmount (or other future) types of marks we add a flags field and set the inode bit on inode marks (the only currently supported type of mark) Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 6731408..b000658 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -322,6 +322,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, if (unlikely(!inode)) return -EINVAL; + entry->flags = FSNOTIFY_MARK_FLAG_INODE; + /* * if this group isn't being testing for inode type events we need * to start testing -- cgit v1.1 From e61ce86737b4d60521e4e71f9892fe4bdcfb688b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename fsnotify_mark_entry to just fsnotify_mark The name is long and it serves no real purpose. So rename fsnotify_mark_entry to just fsnotify_mark. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index b000658..7e69f6b 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -38,12 +38,12 @@ * that lock to dereference either of these things (they could be NULL even with * the lock) * - * group->mark_lock protects the mark_entries list anchored inside a given group + * group->mark_lock protects the marks_list anchored inside a given group * and each entry is hooked via the g_list. It also sorta protects the * free_g_list, which when used is anchored by a private list on the stack of the * task which held the group->mark_lock. * - * inode->i_lock protects the i_fsnotify_mark_entries list anchored inside a + * inode->i_lock protects the i_fsnotify_marks list anchored inside a * given inode and each entry is hooked via the i_list. (and sorta the * free_i_list) * @@ -61,7 +61,7 @@ * need to be cleaned up. (fsnotify_clear_marks_by_group) * * Worst case we are given an inode and need to clean up all the marks on that - * inode. We take i_lock and walk the i_fsnotify_mark_entries safely. For each + * inode. We take i_lock and walk the i_fsnotify_marks safely. For each * mark on the list we take a reference (so the mark can't disappear under us). * We remove that mark form the inode's list of marks and we add this mark to a * private list anchored on the stack using i_free_list; At this point we no @@ -95,12 +95,12 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark_entry *entry) +void fsnotify_get_mark(struct fsnotify_mark *entry) { atomic_inc(&entry->refcnt); } -void fsnotify_put_mark(struct fsnotify_mark_entry *entry) +void fsnotify_put_mark(struct fsnotify_mark *entry) { if (atomic_dec_and_test(&entry->refcnt)) entry->free_mark(entry); @@ -111,13 +111,13 @@ void fsnotify_put_mark(struct fsnotify_mark_entry *entry) */ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct hlist_node *pos; __u32 new_mask = 0; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) new_mask |= entry->mask; inode->i_fsnotify_mask = new_mask; } @@ -140,7 +140,7 @@ void fsnotify_recalc_inode_mask(struct inode *inode) * The caller had better be holding a reference to this mark so we don't actually * do the final put under the entry->lock */ -void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) +void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry) { struct fsnotify_group *group; struct inode *inode; @@ -174,7 +174,7 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) fsnotify_put_mark(entry); /* for i_list and g_list */ /* - * this mark is now off the inode->i_fsnotify_mark_entries list and we + * this mark is now off the inode->i_fsnotify_marks list and we * hold the inode->i_lock, so this is the perfect time to update the * inode->i_fsnotify_mask */ @@ -221,11 +221,11 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) */ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) { - struct fsnotify_mark_entry *lentry, *entry; + struct fsnotify_mark *lentry, *entry; LIST_HEAD(free_list); spin_lock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &group->mark_entries, g_list) { + list_for_each_entry_safe(entry, lentry, &group->marks_list, g_list) { list_add(&entry->free_g_list, &free_list); list_del_init(&entry->g_list); fsnotify_get_mark(entry); @@ -243,12 +243,12 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) */ void fsnotify_clear_marks_by_inode(struct inode *inode) { - struct fsnotify_mark_entry *entry, *lentry; + struct fsnotify_mark *entry, *lentry; struct hlist_node *pos, *n; LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i.i_list) { + hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_marks, i.i_list) { list_add(&entry->i.free_i_list, &free_list); hlist_del_init(&entry->i.i_list); fsnotify_get_mark(entry); @@ -265,15 +265,15 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, + struct inode *inode) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) { + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) { if (entry->group == group) { fsnotify_get_mark(entry); return entry; @@ -282,7 +282,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou return NULL; } -void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) +void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); new->i.inode = old->i.inode; @@ -294,8 +294,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_ma /* * Nothing fancy, just initialize lists and locks and counters. */ -void fsnotify_init_mark(struct fsnotify_mark_entry *entry, - void (*free_mark)(struct fsnotify_mark_entry *entry)) +void fsnotify_init_mark(struct fsnotify_mark *entry, + void (*free_mark)(struct fsnotify_mark *entry)) { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); @@ -311,11 +311,11 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark_entry *entry, +int fsnotify_add_mark(struct fsnotify_mark *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark_entry *lentry = NULL; + struct fsnotify_mark *lentry = NULL; int ret = 0; inode = igrab(inode); @@ -354,8 +354,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, entry->group = group; entry->i.inode = inode; - hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_mark_entries); - list_add(&entry->g_list, &group->mark_entries); + hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_marks); + list_add(&entry->g_list, &group->marks_list); fsnotify_get_mark(entry); /* for i_list and g_list */ -- cgit v1.1 From d07754412f9cdc2f4a99318d5ee81ace6715ea99 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename fsnotify_find_mark_entry to fsnotify_find_mark the _entry portion of fsnotify functions is useless. Drop it. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 7e69f6b..01c4263 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -56,7 +56,7 @@ * - The inode is unlinked for the last time. (fsnotify_inode_remove) * - The inode is being evicted from cache. (fsnotify_inode_delete) * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) - * - Something explicitly requests that it be removed. (fsnotify_destroy_mark_by_entry) + * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) * - The fsnotify_group associated with the mark is going away and all such marks * need to be cleaned up. (fsnotify_clear_marks_by_group) * @@ -140,7 +140,7 @@ void fsnotify_recalc_inode_mask(struct inode *inode) * The caller had better be holding a reference to this mark so we don't actually * do the final put under the entry->lock */ -void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry) +void fsnotify_destroy_mark(struct fsnotify_mark *entry) { struct fsnotify_group *group; struct inode *inode; @@ -233,7 +233,7 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) spin_unlock(&group->mark_lock); list_for_each_entry_safe(entry, lentry, &free_list, free_g_list) { - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); } } @@ -256,7 +256,7 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) spin_unlock(&inode->i_lock); list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); } } @@ -265,8 +265,8 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *entry; struct hlist_node *pos; @@ -349,7 +349,7 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, spin_lock(&inode->i_lock); if (!allow_dups) - lentry = fsnotify_find_mark_entry(group, inode); + lentry = fsnotify_find_mark(group, inode); if (!lentry) { entry->group = group; entry->i.inode = inode; -- cgit v1.1 From 841bdc10f573aa010dd5818d35a5690b7d9f73ce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename mark_entry to just mark previously I used mark_entry when talking about marks on inodes. The _entry is pretty useless. Just use "mark" instead. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 148 ++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 74 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 01c4263..27c1b43 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -30,21 +30,21 @@ * There are 3 spinlocks involved with fsnotify inode marks and they MUST * be taken in order as follows: * - * entry->lock + * mark->lock * group->mark_lock * inode->i_lock * - * entry->lock protects 2 things, entry->group and entry->inode. You must hold + * mark->lock protects 2 things, mark->group and mark->inode. You must hold * that lock to dereference either of these things (they could be NULL even with * the lock) * * group->mark_lock protects the marks_list anchored inside a given group - * and each entry is hooked via the g_list. It also sorta protects the + * and each mark is hooked via the g_list. It also sorta protects the * free_g_list, which when used is anchored by a private list on the stack of the * task which held the group->mark_lock. * * inode->i_lock protects the i_fsnotify_marks list anchored inside a - * given inode and each entry is hooked via the i_list. (and sorta the + * given inode and each mark is hooked via the i_list. (and sorta the * free_i_list) * * @@ -95,15 +95,15 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark *entry) +void fsnotify_get_mark(struct fsnotify_mark *mark) { - atomic_inc(&entry->refcnt); + atomic_inc(&mark->refcnt); } -void fsnotify_put_mark(struct fsnotify_mark *entry) +void fsnotify_put_mark(struct fsnotify_mark *mark) { - if (atomic_dec_and_test(&entry->refcnt)) - entry->free_mark(entry); + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); } /* @@ -111,14 +111,14 @@ void fsnotify_put_mark(struct fsnotify_mark *entry) */ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) { - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; struct hlist_node *pos; __u32 new_mask = 0; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) - new_mask |= entry->mask; + hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) + new_mask |= mark->mask; inode->i_fsnotify_mask = new_mask; } @@ -138,40 +138,40 @@ void fsnotify_recalc_inode_mask(struct inode *inode) /* * Any time a mark is getting freed we end up here. * The caller had better be holding a reference to this mark so we don't actually - * do the final put under the entry->lock + * do the final put under the mark->lock */ -void fsnotify_destroy_mark(struct fsnotify_mark *entry) +void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct fsnotify_group *group; struct inode *inode; - spin_lock(&entry->lock); + spin_lock(&mark->lock); - group = entry->group; - inode = entry->i.inode; + group = mark->group; + inode = mark->i.inode; BUG_ON(group && !inode); BUG_ON(!group && inode); /* if !group something else already marked this to die */ if (!group) { - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); return; } /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&entry->refcnt) < 2); + BUG_ON(atomic_read(&mark->refcnt) < 2); spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - hlist_del_init(&entry->i.i_list); - entry->i.inode = NULL; + hlist_del_init(&mark->i.i_list); + mark->i.inode = NULL; - list_del_init(&entry->g_list); - entry->group = NULL; + list_del_init(&mark->g_list); + mark->group = NULL; - fsnotify_put_mark(entry); /* for i_list and g_list */ + fsnotify_put_mark(mark); /* for i_list and g_list */ /* * this mark is now off the inode->i_fsnotify_marks list and we @@ -182,21 +182,21 @@ void fsnotify_destroy_mark(struct fsnotify_mark *entry) spin_unlock(&inode->i_lock); spin_unlock(&group->mark_lock); - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); /* * Some groups like to know that marks are being freed. This is a - * callback to the group function to let it know that this entry + * callback to the group function to let it know that this mark * is being freed. */ if (group->ops->freeing_mark) - group->ops->freeing_mark(entry, group); + group->ops->freeing_mark(mark, group); /* * __fsnotify_update_child_dentry_flags(inode); * * I really want to call that, but we can't, we have no idea if the inode - * still exists the second we drop the entry->lock. + * still exists the second we drop the mark->lock. * * The next time an event arrive to this inode from one of it's children * __fsnotify_parent will see that the inode doesn't care about it's @@ -221,20 +221,20 @@ void fsnotify_destroy_mark(struct fsnotify_mark *entry) */ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) { - struct fsnotify_mark *lentry, *entry; + struct fsnotify_mark *lmark, *mark; LIST_HEAD(free_list); spin_lock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &group->marks_list, g_list) { - list_add(&entry->free_g_list, &free_list); - list_del_init(&entry->g_list); - fsnotify_get_mark(entry); + list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); } spin_unlock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &free_list, free_g_list) { - fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); + list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); } } @@ -243,21 +243,21 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) */ void fsnotify_clear_marks_by_inode(struct inode *inode) { - struct fsnotify_mark *entry, *lentry; + struct fsnotify_mark *mark, *lmark; struct hlist_node *pos, *n; LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_marks, i.i_list) { - list_add(&entry->i.free_i_list, &free_list); - hlist_del_init(&entry->i.i_list); - fsnotify_get_mark(entry); + 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(&mark->i.i_list); + fsnotify_get_mark(mark); } spin_unlock(&inode->i_lock); - list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { - fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); + list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); } } @@ -268,15 +268,15 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, struct inode *inode) { - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) { - if (entry->group == group) { - fsnotify_get_mark(entry); - return entry; + hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) { + if (mark->group == group) { + fsnotify_get_mark(mark); + return mark; } } return NULL; @@ -294,35 +294,35 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol /* * Nothing fancy, just initialize lists and locks and counters. */ -void fsnotify_init_mark(struct fsnotify_mark *entry, - void (*free_mark)(struct fsnotify_mark *entry)) +void fsnotify_init_mark(struct fsnotify_mark *mark, + void (*free_mark)(struct fsnotify_mark *mark)) { - spin_lock_init(&entry->lock); - atomic_set(&entry->refcnt, 1); - INIT_HLIST_NODE(&entry->i.i_list); - entry->group = NULL; - entry->mask = 0; - entry->i.inode = NULL; - entry->free_mark = free_mark; + spin_lock_init(&mark->lock); + atomic_set(&mark->refcnt, 1); + INIT_HLIST_NODE(&mark->i.i_list); + mark->group = NULL; + mark->mask = 0; + mark->i.inode = NULL; + mark->free_mark = free_mark; } /* - * Attach an initialized mark entry to a given group and inode. + * Attach an initialized mark mark to a given group and inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark *entry, +int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark *lentry = NULL; + struct fsnotify_mark *lmark = NULL; int ret = 0; inode = igrab(inode); if (unlikely(!inode)) return -EINVAL; - entry->flags = FSNOTIFY_MARK_FLAG_INODE; + mark->flags = FSNOTIFY_MARK_FLAG_INODE; /* * if this group isn't being testing for inode type events we need @@ -340,24 +340,24 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, /* * LOCKING ORDER!!!! - * entry->lock + * mark->lock * group->mark_lock * inode->i_lock */ - spin_lock(&entry->lock); + spin_lock(&mark->lock); spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); if (!allow_dups) - lentry = fsnotify_find_mark(group, inode); - if (!lentry) { - entry->group = group; - entry->i.inode = inode; + lmark = fsnotify_find_mark(group, inode); + if (!lmark) { + mark->group = group; + mark->i.inode = inode; - hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_marks); - list_add(&entry->g_list, &group->marks_list); + hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + list_add(&mark->g_list, &group->marks_list); - fsnotify_get_mark(entry); /* for i_list and g_list */ + fsnotify_get_mark(mark); /* for i_list and g_list */ atomic_inc(&group->num_marks); @@ -366,12 +366,12 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, spin_unlock(&inode->i_lock); spin_unlock(&group->mark_lock); - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); - if (lentry) { + if (lmark) { ret = -EEXIST; iput(inode); - fsnotify_put_mark(lentry); + fsnotify_put_mark(lmark); } else { __fsnotify_update_child_dentry_flags(inode); } -- cgit v1.1 From 35566087099c3ff8901d65ee98af56347ee66e5a Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fsnotify: take inode->i_lock inside fsnotify_find_mark_entry() All callers to fsnotify_find_mark_entry() except one take and release inode->i_lock around the call. Take the lock inside fsnotify_find_mark_entry() instead. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 27c1b43..ba6f983 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -261,12 +261,8 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } -/* - * given a group and inode, find the mark associated with that combination. - * if found take a reference to that mark and return it, else return NULL - */ -struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, - struct inode *inode) +static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; struct hlist_node *pos; @@ -282,6 +278,22 @@ struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, return NULL; } +/* + * given a group and inode, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, + struct inode *inode) +{ + struct fsnotify_mark *mark; + + spin_lock(&inode->i_lock); + mark = fsnotify_find_mark_locked(group, inode); + spin_unlock(&inode->i_lock); + + return mark; +} + void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); @@ -349,7 +361,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, spin_lock(&inode->i_lock); if (!allow_dups) - lmark = fsnotify_find_mark(group, inode); + lmark = fsnotify_find_mark_locked(group, inode); if (!lmark) { mark->group = group; mark->i.inode = inode; -- cgit v1.1 From 5444e2981c31d0ed7465475e451b8437084337e5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: split generic and inode specific mark code currently all marking is done by functions in inode-mark.c. Some of this is pretty generic and should be instead done in a generic function and we should only put the inode specific code in inode-mark.c Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 246 ++++--------------------------------------------- 1 file changed, 19 insertions(+), 227 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index ba6f983..c925579 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -16,72 +16,6 @@ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * fsnotify inode mark locking/lifetime/and refcnting - * - * REFCNT: - * The mark->refcnt tells how many "things" in the kernel currently are - * referencing this object. The object typically will live inside the kernel - * with a refcnt of 2, one for each list it is on (i_list, g_list). Any task - * which can find this object holding the appropriete locks, can take a reference - * and the object itself is guarenteed to survive until the reference is dropped. - * - * LOCKING: - * There are 3 spinlocks involved with fsnotify inode marks and they MUST - * be taken in order as follows: - * - * mark->lock - * group->mark_lock - * inode->i_lock - * - * mark->lock protects 2 things, mark->group and mark->inode. You must hold - * that lock to dereference either of these things (they could be NULL even with - * the lock) - * - * group->mark_lock protects the marks_list anchored inside a given group - * and each mark is hooked via the g_list. It also sorta protects the - * free_g_list, which when used is anchored by a private list on the stack of the - * task which held the group->mark_lock. - * - * inode->i_lock protects the i_fsnotify_marks list anchored inside a - * given inode and each mark is hooked via the i_list. (and sorta the - * free_i_list) - * - * - * LIFETIME: - * Inode marks survive between when they are added to an inode and when their - * refcnt==0. - * - * The inode mark can be cleared for a number of different reasons including: - * - The inode is unlinked for the last time. (fsnotify_inode_remove) - * - The inode is being evicted from cache. (fsnotify_inode_delete) - * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) - * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) - * - The fsnotify_group associated with the mark is going away and all such marks - * need to be cleaned up. (fsnotify_clear_marks_by_group) - * - * Worst case we are given an inode and need to clean up all the marks on that - * inode. We take i_lock and walk the i_fsnotify_marks safely. For each - * mark on the list we take a reference (so the mark can't disappear under us). - * We remove that mark form the inode's list of marks and we add this mark to a - * private list anchored on the stack using i_free_list; At this point we no - * longer fear anything finding the mark using the inode's list of marks. - * - * We can safely and locklessly run the private list on the stack of everything - * we just unattached from the original inode. For each mark on the private list - * we grab the mark-> and can thus dereference mark->group and mark->inode. If - * we see the group and inode are not NULL we take those locks. Now holding all - * 3 locks we can completely remove the mark from other tasks finding it in the - * future. Remember, 10 things might already be referencing this mark, but they - * better be holding a ref. We drop our reference we took before we unhooked it - * from the inode. When the ref hits 0 we can free the mark. - * - * Very similarly for freeing by group, except we use free_g_list. - * - * This has the very interesting property of being able to run concurrently with - * any (or all) other directions. - */ - #include #include #include @@ -95,17 +29,6 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark *mark) -{ - atomic_inc(&mark->refcnt); -} - -void fsnotify_put_mark(struct fsnotify_mark *mark) -{ - if (atomic_dec_and_test(&mark->refcnt)) - mark->free_mark(mark); -} - /* * Recalculate the mask of events relevant to a given inode locked. */ @@ -135,44 +58,18 @@ void fsnotify_recalc_inode_mask(struct inode *inode) __fsnotify_update_child_dentry_flags(inode); } -/* - * Any time a mark is getting freed we end up here. - * The caller had better be holding a reference to this mark so we don't actually - * do the final put under the mark->lock - */ -void fsnotify_destroy_mark(struct fsnotify_mark *mark) +void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) { - struct fsnotify_group *group; - struct inode *inode; - - spin_lock(&mark->lock); + struct inode *inode = mark->i.inode; - group = mark->group; - inode = mark->i.inode; + assert_spin_locked(&mark->lock); + assert_spin_locked(&mark->group->mark_lock); - BUG_ON(group && !inode); - BUG_ON(!group && inode); - - /* if !group something else already marked this to die */ - if (!group) { - spin_unlock(&mark->lock); - return; - } - - /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&mark->refcnt) < 2); - - spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); hlist_del_init(&mark->i.i_list); mark->i.inode = NULL; - list_del_init(&mark->g_list); - mark->group = NULL; - - fsnotify_put_mark(mark); /* for i_list and g_list */ - /* * this mark is now off the inode->i_fsnotify_marks list and we * hold the inode->i_lock, so this is the perfect time to update the @@ -181,61 +78,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); - spin_unlock(&group->mark_lock); - spin_unlock(&mark->lock); - - /* - * Some groups like to know that marks are being freed. This is a - * callback to the group function to let it know that this mark - * is being freed. - */ - if (group->ops->freeing_mark) - group->ops->freeing_mark(mark, group); - - /* - * __fsnotify_update_child_dentry_flags(inode); - * - * I really want to call that, but we can't, we have no idea if the inode - * still exists the second we drop the mark->lock. - * - * The next time an event arrive to this inode from one of it's children - * __fsnotify_parent will see that the inode doesn't care about it's - * children and will update all of these flags then. So really this - * is just a lazy update (and could be a perf win...) - */ - - - iput(inode); - - /* - * it's possible that this group tried to destroy itself, but this - * this mark was simultaneously being freed by inode. If that's the - * case, we finish freeing the group here. - */ - if (unlikely(atomic_dec_and_test(&group->num_marks))) - fsnotify_final_destroy_group(group); -} - -/* - * Given a group, destroy all of the marks associated with that group. - */ -void fsnotify_clear_marks_by_group(struct fsnotify_group *group) -{ - struct fsnotify_mark *lmark, *mark; - LIST_HEAD(free_list); - - spin_lock(&group->mark_lock); - list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { - list_add(&mark->free_g_list, &free_list); - list_del_init(&mark->g_list); - fsnotify_get_mark(mark); - } - spin_unlock(&group->mark_lock); - - list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { - fsnotify_destroy_mark(mark); - fsnotify_put_mark(mark); - } } /* @@ -261,8 +103,12 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } -static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *group, - struct inode *inode) +/* + * given a group and inode, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_inode_mark_locked(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; struct hlist_node *pos; @@ -282,50 +128,26 @@ static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *gr * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; spin_lock(&inode->i_lock); - mark = fsnotify_find_mark_locked(group, inode); + mark = fsnotify_find_inode_mark_locked(group, inode); spin_unlock(&inode->i_lock); return mark; } -void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) -{ - assert_spin_locked(&old->lock); - new->i.inode = old->i.inode; - new->group = old->group; - new->mask = old->mask; - new->free_mark = old->free_mark; -} - -/* - * Nothing fancy, just initialize lists and locks and counters. - */ -void fsnotify_init_mark(struct fsnotify_mark *mark, - void (*free_mark)(struct fsnotify_mark *mark)) -{ - spin_lock_init(&mark->lock); - atomic_set(&mark->refcnt, 1); - INIT_HLIST_NODE(&mark->i.i_list); - mark->group = NULL; - mark->mask = 0; - mark->i.inode = NULL; - mark->free_mark = free_mark; -} - /* * Attach an initialized mark mark to a given group and inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark *mark, - struct fsnotify_group *group, struct inode *inode, - int allow_dups) +int fsnotify_add_inode_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + int allow_dups) { struct fsnotify_mark *lmark = NULL; int ret = 0; @@ -336,56 +158,26 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, mark->flags = FSNOTIFY_MARK_FLAG_INODE; - /* - * if this group isn't being testing for inode type events we need - * to start testing - */ - if (unlikely(list_empty(&group->inode_group_list))) - fsnotify_add_inode_group(group); - /* - * XXX This is where we could also do the fsnotify_add_vfsmount_group - * if we are setting and vfsmount mark.... - - if (unlikely(list_empty(&group->vfsmount_group_list))) - fsnotify_add_vfsmount_group(group); - */ + assert_spin_locked(&mark->lock); + assert_spin_locked(&group->mark_lock); - /* - * LOCKING ORDER!!!! - * mark->lock - * group->mark_lock - * inode->i_lock - */ - spin_lock(&mark->lock); - spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); if (!allow_dups) - lmark = fsnotify_find_mark_locked(group, inode); + lmark = fsnotify_find_inode_mark_locked(group, inode); if (!lmark) { - mark->group = group; mark->i.inode = inode; hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); - list_add(&mark->g_list, &group->marks_list); - - fsnotify_get_mark(mark); /* for i_list and g_list */ - - atomic_inc(&group->num_marks); fsnotify_recalc_inode_mask_locked(inode); } spin_unlock(&inode->i_lock); - spin_unlock(&group->mark_lock); - spin_unlock(&mark->lock); if (lmark) { ret = -EEXIST; iput(inode); - fsnotify_put_mark(lmark); - } else { - __fsnotify_update_child_dentry_flags(inode); } return ret; -- cgit v1.1 From 90b1e7a57880fb66437ab7db39e1e65ca0372822 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: allow marks to not pin inodes in core inotify marks must pin inodes in core. dnotify doesn't technically need to since they are closed when the directory is closed. fanotify also need to pin inodes in core as it works today. But the next step is to introduce the concept of 'ignored masks' which is actually a mask of events for an inode of no interest. I claim that these should be liberally sent to the kernel and should not pin the inode in core. If the inode is brought back in the listener will get an event it may have thought excluded, but this is not a serious situation and one any listener should deal with. This patch lays the ground work for non-pinning inode marks by using lazy inode pinning. We do not pin a mark until it has a non-zero mask entry. If a listener new sets a mask we never pin the inode. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index c925579..4292f9e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -141,7 +141,32 @@ struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, } /* - * Attach an initialized mark mark to a given group and inode. + * If we are setting a mark mask on an inode mark we should pin the inode + * in memory. + */ +void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark, + __u32 mask) +{ + struct inode *inode; + + assert_spin_locked(&mark->lock); + + if (mask && + mark->i.inode && + !(mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) { + mark->flags |= FSNOTIFY_MARK_FLAG_OBJECT_PINNED; + inode = igrab(mark->i.inode); + /* + * we shouldn't be able to get here if the inode wasn't + * already safely held in memory. But bug in case it + * ever is wrong. + */ + BUG_ON(!inode); + } +} + +/* + * Attach an initialized mark to a given group and inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ @@ -152,10 +177,6 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_mark *lmark = NULL; int ret = 0; - inode = igrab(inode); - if (unlikely(!inode)) - return -EINVAL; - mark->flags = FSNOTIFY_MARK_FLAG_INODE; assert_spin_locked(&mark->lock); @@ -175,10 +196,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, spin_unlock(&inode->i_lock); - if (lmark) { + if (lmark) ret = -EEXIST; - iput(inode); - } return ret; } -- cgit v1.1 From 4d92604cc90aa18bbbe0f6e23b7a9fdb612836d3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: clear all fanotify marks fanotify listeners may want to clear all marks. They may want to do this to destroy all of their inode marks which have nothing but ignores. Realistically this is useful for av vendors who update policy and want to clear all of their cached allows. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 4292f9e..0c0a48b 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -104,6 +104,14 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } /* + * Given a group clear all of the inode marks associated with that group. + */ +void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_INODE); +} + +/* * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -- cgit v1.1 From 0c6532e4e3b0c8bd18dd0a5cc1894a1944997cc6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: place marks on object in order of group memory address fsnotify_marks currently are placed on objects (inodes or vfsmounts) in arbitrary order. This patch places them in order of the group memory address. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0c0a48b..83ce6db 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -174,15 +174,17 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark, } /* - * Attach an initialized mark to a given group and inode. + * Attach an initialized mark to a given inode. * These marks may be used for the fsnotify backend to determine which - * event types should be delivered to which group and for which inodes. + * event types should be delivered to which group and for which inodes. These + * marks are ordered according to the group's location in memory. */ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark *lmark = NULL; + struct fsnotify_mark *lmark; + struct hlist_node *node, *last = NULL; int ret = 0; mark->flags = FSNOTIFY_MARK_FLAG_INODE; @@ -192,21 +194,37 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, spin_lock(&inode->i_lock); - if (!allow_dups) - lmark = fsnotify_find_inode_mark_locked(group, inode); - if (!lmark) { - mark->i.inode = inode; + mark->i.inode = inode; + /* is mark the first mark? */ + if (hlist_empty(&inode->i_fsnotify_marks)) { hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + goto out; + } + + /* should mark be in the middle of the current list? */ + hlist_for_each_entry(lmark, node, &inode->i_fsnotify_marks, i.i_list) { + last = node; + + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } - fsnotify_recalc_inode_mask_locked(inode); + if (mark->group < lmark->group) + continue; + + hlist_add_before(&mark->i.i_list, &lmark->i.i_list); + goto out; } + BUG_ON(last == NULL); + /* mark should be the last entry. last is the current last entry */ + hlist_add_after(last, &mark->i.i_list); +out: + fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); - if (lmark) - ret = -EEXIST; - return ret; } -- cgit v1.1 From a4c6e9961fcb9da54648d98978d33c6fdcb7bb45 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: use _rcu functions for mark list traversal In preparation for srcu locking use all _rcu appropiete functions for mark list addition, removal, and traversal. The operations are still done under a spinlock at the end of this patch. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 83ce6db..455cb41 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -67,7 +67,7 @@ void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) spin_lock(&inode->i_lock); - hlist_del_init(&mark->i.i_list); + hlist_del_init_rcu(&mark->i.i_list); mark->i.inode = NULL; /* @@ -92,7 +92,7 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) 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(&mark->i.i_list); + hlist_del_init_rcu(&mark->i.i_list); fsnotify_get_mark(mark); } spin_unlock(&inode->i_lock); @@ -198,7 +198,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, /* is mark the first mark? */ if (hlist_empty(&inode->i_fsnotify_marks)) { - hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + hlist_add_head_rcu(&mark->i.i_list, &inode->i_fsnotify_marks); goto out; } @@ -214,13 +214,13 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, if (mark->group < lmark->group) continue; - hlist_add_before(&mark->i.i_list, &lmark->i.i_list); + hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); goto out; } BUG_ON(last == NULL); /* mark should be the last entry. last is the current last entry */ - hlist_add_after(last, &mark->i.i_list); + hlist_add_after_rcu(last, &mark->i.i_list); out: fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); -- cgit v1.1 From 700307a29ad61090dcf1d45f8f4a135f5e9211ae Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: use an explicit flag to indicate fsnotify_destroy_mark has been called Currently fsnotify check is mark->group is NULL to decide if fsnotify_destroy_mark() has already been called or not. With the upcoming rcu work it is a heck of a lot easier to use an explicit flag than worry about group being set to NULL. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/notify/inode_mark.c') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 455cb41..37b460f 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -187,7 +187,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct hlist_node *node, *last = NULL; int ret = 0; - mark->flags = FSNOTIFY_MARK_FLAG_INODE; + mark->flags |= FSNOTIFY_MARK_FLAG_INODE; assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); -- cgit v1.1