Message ID | 1334599360-15346-4-git-send-email-apw@canonical.com |
---|---|
State | New |
Headers | show |
On 16.04.2012 20:02, Andy Whitcroft wrote: > From: Lino Sanfilippo <LinoSanfilippo@gmx.de> > > 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> > > (cherry-picked from commit 0f948ec4aff7eb629b08e7dd3d9761be17a1ae9e git://git.infradead.org/users/eparis/notify.git) > BugLink: http://bugs.launchpad.net/bugs/922906 > Signed-off-by: Andy Whitcroft <apw@canonical.com> > --- > fs/notify/group.c | 28 ++++++++++------------------ > fs/notify/inotify/inotify_fsnotify.c | 2 ++ > fs/notify/inotify/inotify_user.c | 1 + > fs/notify/mark.c | 24 ++++++++++++++---------- > 4 files changed, 27 insertions(+), 28 deletions(-) > > diff --git a/fs/notify/group.c b/fs/notify/group.c > index 59a6b53..37569ec 100644 > --- a/fs/notify/group.c > +++ b/fs/notify/group.c > @@ -34,9 +34,6 @@ > */ > void fsnotify_final_destroy_group(struct fsnotify_group *group) > { > - /* clear the notification queue of all events */ > - fsnotify_flush_notify(group); > - > if (group->ops->free_group_priv) > group->ops->free_group_priv(group); > > @@ -44,12 +41,10 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group) > } > > /* > - * Trying to get rid of a group. We need to first get rid of any outstanding > - * allocations and then free the group. Remember that fsnotify_clear_marks_by_group > - * could miss marks that are being freed by inode and those marks could still > - * hold a reference to this group (via group->num_marks) If we get into that > - * situtation, the fsnotify_final_destroy_group will get called when that final > - * mark is freed. > + * Trying to get rid of a group. Remove all marks, flush all events and release > + * the group reference. > + * Note that another thread calling fsnotify_clear_marks_by_group() may still > + * hold a ref to the group. > */ > void fsnotify_destroy_group(struct fsnotify_group *group) > { > @@ -58,9 +53,10 @@ void fsnotify_destroy_group(struct fsnotify_group *group) > > synchronize_srcu(&fsnotify_mark_srcu); > > - /* past the point of no return, matches the initial value of 1 */ > - if (atomic_dec_and_test(&group->num_marks)) > - fsnotify_final_destroy_group(group); > + /* clear the notification queue of all events */ > + fsnotify_flush_notify(group); > + > + fsnotify_put_group(group); > } > > /* > @@ -77,7 +73,7 @@ void fsnotify_get_group(struct fsnotify_group *group) > void fsnotify_put_group(struct fsnotify_group *group) > { > if (atomic_dec_and_test(&group->refcnt)) > - fsnotify_destroy_group(group); > + fsnotify_final_destroy_group(group); > } > EXPORT_SYMBOL(fsnotify_put_group); > > @@ -94,11 +90,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) > > /* set to 0 when there a no external references to this group */ > atomic_set(&group->refcnt, 1); > - /* > - * hits 0 when there are no external references AND no marks for > - * this group > - */ > - atomic_set(&group->num_marks, 1); > + atomic_set(&group->num_marks, 0); > > mutex_init(&group->notification_mutex); > INIT_LIST_HEAD(&group->notification_list); > diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c > index e3cbd74..74977fb 100644 > --- a/fs/notify/inotify/inotify_fsnotify.c > +++ b/fs/notify/inotify/inotify_fsnotify.c > @@ -118,6 +118,7 @@ static int inotify_handle_event(struct fsnotify_group *group, > > fsn_event_priv = &event_priv->fsnotify_event_priv_data; > > + fsnotify_get_group(group); > fsn_event_priv->group = group; > event_priv->wd = wd; > > @@ -210,6 +211,7 @@ void inotify_free_event_priv(struct fsnotify_event_private_data *fsn_event_priv) > event_priv = container_of(fsn_event_priv, struct inotify_event_private_data, > fsnotify_event_priv_data); > > + fsnotify_put_group(fsn_event_priv->group); > kmem_cache_free(event_priv_cachep, event_priv); > } > > diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c > index dbafbfc..246250f 100644 > --- a/fs/notify/inotify/inotify_user.c > +++ b/fs/notify/inotify/inotify_user.c > @@ -531,6 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, > > fsn_event_priv = &event_priv->fsnotify_event_priv_data; > > + fsnotify_get_group(group); > fsn_event_priv->group = group; > event_priv->wd = i_mark->wd; > Want to check with the applied code tomorrow... Just looks a bit curious without a matching put... > diff --git a/fs/notify/mark.c b/fs/notify/mark.c > index 54f36db..5b784aa 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); > + } > } > EXPORT_SYMBOL(fsnotify_put_mark); > > @@ -126,12 +129,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; > @@ -178,19 +182,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); > } > EXPORT_SYMBOL(fsnotify_destroy_mark); > > @@ -236,6 +236,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); > @@ -267,6 +268,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); > > @@ -320,6 +322,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;
diff --git a/fs/notify/group.c b/fs/notify/group.c index 59a6b53..37569ec 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -34,9 +34,6 @@ */ void fsnotify_final_destroy_group(struct fsnotify_group *group) { - /* clear the notification queue of all events */ - fsnotify_flush_notify(group); - if (group->ops->free_group_priv) group->ops->free_group_priv(group); @@ -44,12 +41,10 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group) } /* - * Trying to get rid of a group. We need to first get rid of any outstanding - * allocations and then free the group. Remember that fsnotify_clear_marks_by_group - * could miss marks that are being freed by inode and those marks could still - * hold a reference to this group (via group->num_marks) If we get into that - * situtation, the fsnotify_final_destroy_group will get called when that final - * mark is freed. + * Trying to get rid of a group. Remove all marks, flush all events and release + * the group reference. + * Note that another thread calling fsnotify_clear_marks_by_group() may still + * hold a ref to the group. */ void fsnotify_destroy_group(struct fsnotify_group *group) { @@ -58,9 +53,10 @@ void fsnotify_destroy_group(struct fsnotify_group *group) synchronize_srcu(&fsnotify_mark_srcu); - /* past the point of no return, matches the initial value of 1 */ - if (atomic_dec_and_test(&group->num_marks)) - fsnotify_final_destroy_group(group); + /* clear the notification queue of all events */ + fsnotify_flush_notify(group); + + fsnotify_put_group(group); } /* @@ -77,7 +73,7 @@ void fsnotify_get_group(struct fsnotify_group *group) void fsnotify_put_group(struct fsnotify_group *group) { if (atomic_dec_and_test(&group->refcnt)) - fsnotify_destroy_group(group); + fsnotify_final_destroy_group(group); } EXPORT_SYMBOL(fsnotify_put_group); @@ -94,11 +90,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) /* set to 0 when there a no external references to this group */ atomic_set(&group->refcnt, 1); - /* - * hits 0 when there are no external references AND no marks for - * this group - */ - atomic_set(&group->num_marks, 1); + atomic_set(&group->num_marks, 0); mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e3cbd74..74977fb 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -118,6 +118,7 @@ static int inotify_handle_event(struct fsnotify_group *group, fsn_event_priv = &event_priv->fsnotify_event_priv_data; + fsnotify_get_group(group); fsn_event_priv->group = group; event_priv->wd = wd; @@ -210,6 +211,7 @@ void inotify_free_event_priv(struct fsnotify_event_private_data *fsn_event_priv) event_priv = container_of(fsn_event_priv, struct inotify_event_private_data, fsnotify_event_priv_data); + fsnotify_put_group(fsn_event_priv->group); kmem_cache_free(event_priv_cachep, event_priv); } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index dbafbfc..246250f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -531,6 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, fsn_event_priv = &event_priv->fsnotify_event_priv_data; + fsnotify_get_group(group); fsn_event_priv->group = group; event_priv->wd = i_mark->wd; diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 54f36db..5b784aa 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); + } } EXPORT_SYMBOL(fsnotify_put_mark); @@ -126,12 +129,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; @@ -178,19 +182,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); } EXPORT_SYMBOL(fsnotify_destroy_mark); @@ -236,6 +236,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); @@ -267,6 +268,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); @@ -320,6 +322,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;