diff options
author | Lino Sanfilippo <LinoSanfilippo@gmx.de> | 2011-06-14 15:29:47 (GMT) |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2012-12-11 18:29:44 (GMT) |
commit | 23e964c284ca0a767b80a30482bd53b059d30391 (patch) | |
tree | 43d3cd8c5836a3c43f853366bce51f621773dedc /fs/notify/mark.c | |
parent | 986129520479d689962a42c31acdeaf854ac91f5 (diff) | |
download | linux-23e964c284ca0a767b80a30482bd53b059d30391.tar.xz |
fsnotify: use reference counting for groups
Get a group ref for each mark that is added to the groups list and release that
ref when the mark is freed in fsnotify_put_mark().
We also use get a group reference for duplicated marks and for private event
data.
Now we dont free a group any more when the number of marks becomes 0 but when
the groups ref count does. Since this will only happen when all marks are removed
from a groups mark list, we dont have to set the groups number of marks to 1 at
group creation.
Beside clearing all marks in fsnotify_destroy_group() we do also flush the
groups event queue. This is since events may hold references to groups (due to
private event data) and we have to put those references first before we get a
chance to put the final ref, which will result in a call to
fsnotify_final_destroy_group().
Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify/mark.c')
-rw-r--r-- | fs/notify/mark.c | 24 |
1 files changed, 14 insertions, 10 deletions
diff --git a/fs/notify/mark.c b/fs/notify/mark.c index f104d56..3c7a169 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -109,8 +109,11 @@ void fsnotify_get_mark(struct fsnotify_mark *mark) void fsnotify_put_mark(struct fsnotify_mark *mark) { - if (atomic_dec_and_test(&mark->refcnt)) + if (atomic_dec_and_test(&mark->refcnt)) { + if (mark->group) + fsnotify_put_group(mark->group); mark->free_mark(mark); + } } /* @@ -125,12 +128,13 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) spin_lock(&mark->lock); + fsnotify_get_group(mark->group); group = mark->group; /* something else already called this function on this mark */ if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { spin_unlock(&mark->lock); - return; + goto put_group; } mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; @@ -177,19 +181,15 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) iput(inode); - /* * We don't necessarily have a ref on mark from caller so the above iput * may have already destroyed it. Don't touch from now on. */ - /* - * 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); + atomic_dec(&group->num_marks); + +put_group: + fsnotify_put_group(group); } void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) @@ -234,6 +234,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; + fsnotify_get_group(group); mark->group = group; list_add(&mark->g_list, &group->marks_list); atomic_inc(&group->num_marks); @@ -265,6 +266,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, err: mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; list_del_init(&mark->g_list); + fsnotify_put_group(group); mark->group = NULL; atomic_dec(&group->num_marks); @@ -317,6 +319,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol assert_spin_locked(&old->lock); new->i.inode = old->i.inode; new->m.mnt = old->m.mnt; + if (old->group) + fsnotify_get_group(old->group); new->group = old->group; new->mask = old->mask; new->free_mark = old->free_mark; |