mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
fsnotify: get rid of fsnotify_destroy_mark_locked()
fsnotify_destroy_mark_locked() is subtle to use because it temporarily releases group->mark_mutex. To avoid future problems with this function, split it into two. fsnotify_detach_mark() is the part that needs group->mark_mutex and fsnotify_free_mark() is the part that must be called outside of group->mark_mutex. This way it's much clearer what's going on and we also avoid some pointless acquisitions of group->mark_mutex. Signed-off-by: Jan Kara <jack@suse.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
925d1132a0
commit
4712e722f9
4 changed files with 61 additions and 41 deletions
|
@ -154,6 +154,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
|
||||||
struct dnotify_struct *dn;
|
struct dnotify_struct *dn;
|
||||||
struct dnotify_struct **prev;
|
struct dnotify_struct **prev;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
bool free = false;
|
||||||
|
|
||||||
inode = file_inode(filp);
|
inode = file_inode(filp);
|
||||||
if (!S_ISDIR(inode->i_mode))
|
if (!S_ISDIR(inode->i_mode))
|
||||||
|
@ -182,11 +183,15 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
|
||||||
|
|
||||||
/* nothing else could have found us thanks to the dnotify_groups
|
/* nothing else could have found us thanks to the dnotify_groups
|
||||||
mark_mutex */
|
mark_mutex */
|
||||||
if (dn_mark->dn == NULL)
|
if (dn_mark->dn == NULL) {
|
||||||
fsnotify_destroy_mark_locked(fsn_mark, dnotify_group);
|
fsnotify_detach_mark(fsn_mark);
|
||||||
|
free = true;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&dnotify_group->mark_mutex);
|
mutex_unlock(&dnotify_group->mark_mutex);
|
||||||
|
|
||||||
|
if (free)
|
||||||
|
fsnotify_free_mark(fsn_mark);
|
||||||
fsnotify_put_mark(fsn_mark);
|
fsnotify_put_mark(fsn_mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,9 +367,10 @@ out:
|
||||||
spin_unlock(&fsn_mark->lock);
|
spin_unlock(&fsn_mark->lock);
|
||||||
|
|
||||||
if (destroy)
|
if (destroy)
|
||||||
fsnotify_destroy_mark_locked(fsn_mark, dnotify_group);
|
fsnotify_detach_mark(fsn_mark);
|
||||||
|
|
||||||
mutex_unlock(&dnotify_group->mark_mutex);
|
mutex_unlock(&dnotify_group->mark_mutex);
|
||||||
|
if (destroy)
|
||||||
|
fsnotify_free_mark(fsn_mark);
|
||||||
fsnotify_put_mark(fsn_mark);
|
fsnotify_put_mark(fsn_mark);
|
||||||
out_err:
|
out_err:
|
||||||
if (new_fsn_mark)
|
if (new_fsn_mark)
|
||||||
|
|
|
@ -529,8 +529,10 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
|
||||||
removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
|
removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
|
||||||
&destroy_mark);
|
&destroy_mark);
|
||||||
if (destroy_mark)
|
if (destroy_mark)
|
||||||
fsnotify_destroy_mark_locked(fsn_mark, group);
|
fsnotify_detach_mark(fsn_mark);
|
||||||
mutex_unlock(&group->mark_mutex);
|
mutex_unlock(&group->mark_mutex);
|
||||||
|
if (destroy_mark)
|
||||||
|
fsnotify_free_mark(fsn_mark);
|
||||||
|
|
||||||
fsnotify_put_mark(fsn_mark);
|
fsnotify_put_mark(fsn_mark);
|
||||||
if (removed & real_mount(mnt)->mnt_fsnotify_mask)
|
if (removed & real_mount(mnt)->mnt_fsnotify_mask)
|
||||||
|
@ -557,8 +559,10 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
|
||||||
removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
|
removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
|
||||||
&destroy_mark);
|
&destroy_mark);
|
||||||
if (destroy_mark)
|
if (destroy_mark)
|
||||||
fsnotify_destroy_mark_locked(fsn_mark, group);
|
fsnotify_detach_mark(fsn_mark);
|
||||||
mutex_unlock(&group->mark_mutex);
|
mutex_unlock(&group->mark_mutex);
|
||||||
|
if (destroy_mark)
|
||||||
|
fsnotify_free_mark(fsn_mark);
|
||||||
|
|
||||||
/* matches the fsnotify_find_inode_mark() */
|
/* matches the fsnotify_find_inode_mark() */
|
||||||
fsnotify_put_mark(fsn_mark);
|
fsnotify_put_mark(fsn_mark);
|
||||||
|
|
|
@ -122,26 +122,27 @@ u32 fsnotify_recalc_mask(struct hlist_head *head)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Any time a mark is getting freed we end up here.
|
* Remove mark from inode / vfsmount list, group list, drop inode reference
|
||||||
* The caller had better be holding a reference to this mark so we don't actually
|
* if we got one.
|
||||||
* do the final put under the mark->lock
|
*
|
||||||
|
* Must be called with group->mark_mutex held.
|
||||||
*/
|
*/
|
||||||
void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
|
void fsnotify_detach_mark(struct fsnotify_mark *mark)
|
||||||
struct fsnotify_group *group)
|
|
||||||
{
|
{
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
|
struct fsnotify_group *group = mark->group;
|
||||||
|
|
||||||
BUG_ON(!mutex_is_locked(&group->mark_mutex));
|
BUG_ON(!mutex_is_locked(&group->mark_mutex));
|
||||||
|
|
||||||
spin_lock(&mark->lock);
|
spin_lock(&mark->lock);
|
||||||
|
|
||||||
/* something else already called this function on this mark */
|
/* something else already called this function on this mark */
|
||||||
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
|
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
|
||||||
spin_unlock(&mark->lock);
|
spin_unlock(&mark->lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
|
mark->flags &= ~FSNOTIFY_MARK_FLAG_ATTACHED;
|
||||||
|
|
||||||
if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
|
if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
|
||||||
inode = mark->inode;
|
inode = mark->inode;
|
||||||
|
@ -150,6 +151,12 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
|
||||||
fsnotify_destroy_vfsmount_mark(mark);
|
fsnotify_destroy_vfsmount_mark(mark);
|
||||||
else
|
else
|
||||||
BUG();
|
BUG();
|
||||||
|
/*
|
||||||
|
* Note that we didn't update flags telling whether inode cares about
|
||||||
|
* what's happening with children. We update these flags from
|
||||||
|
* __fsnotify_parent() lazily when next event happens on one of our
|
||||||
|
* children.
|
||||||
|
*/
|
||||||
|
|
||||||
list_del_init(&mark->g_list);
|
list_del_init(&mark->g_list);
|
||||||
|
|
||||||
|
@ -157,18 +164,32 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
|
||||||
|
|
||||||
if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED))
|
if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED))
|
||||||
iput(inode);
|
iput(inode);
|
||||||
/* release lock temporarily */
|
|
||||||
mutex_unlock(&group->mark_mutex);
|
atomic_dec(&group->num_marks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free fsnotify mark. The freeing is actually happening from a kthread which
|
||||||
|
* first waits for srcu period end. Caller must have a reference to the mark
|
||||||
|
* or be protected by fsnotify_mark_srcu.
|
||||||
|
*/
|
||||||
|
void fsnotify_free_mark(struct fsnotify_mark *mark)
|
||||||
|
{
|
||||||
|
struct fsnotify_group *group = mark->group;
|
||||||
|
|
||||||
|
spin_lock(&mark->lock);
|
||||||
|
/* something else already called this function on this mark */
|
||||||
|
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
|
||||||
|
spin_unlock(&mark->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
|
||||||
|
spin_unlock(&mark->lock);
|
||||||
|
|
||||||
spin_lock(&destroy_lock);
|
spin_lock(&destroy_lock);
|
||||||
list_add(&mark->g_list, &destroy_list);
|
list_add(&mark->g_list, &destroy_list);
|
||||||
spin_unlock(&destroy_lock);
|
spin_unlock(&destroy_lock);
|
||||||
wake_up(&destroy_waitq);
|
wake_up(&destroy_waitq);
|
||||||
/*
|
|
||||||
* We don't necessarily have a ref on mark from caller so the above destroy
|
|
||||||
* may have actually freed it, unless this group provides a 'freeing_mark'
|
|
||||||
* function which must be holding a reference.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some groups like to know that marks are being freed. This is a
|
* Some groups like to know that marks are being freed. This is a
|
||||||
|
@ -177,30 +198,15 @@ void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
|
||||||
*/
|
*/
|
||||||
if (group->ops->freeing_mark)
|
if (group->ops->freeing_mark)
|
||||||
group->ops->freeing_mark(mark, 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 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...)
|
|
||||||
*/
|
|
||||||
|
|
||||||
atomic_dec(&group->num_marks);
|
|
||||||
|
|
||||||
mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsnotify_destroy_mark(struct fsnotify_mark *mark,
|
void fsnotify_destroy_mark(struct fsnotify_mark *mark,
|
||||||
struct fsnotify_group *group)
|
struct fsnotify_group *group)
|
||||||
{
|
{
|
||||||
mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
|
mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
|
||||||
fsnotify_destroy_mark_locked(mark, group);
|
fsnotify_detach_mark(mark);
|
||||||
mutex_unlock(&group->mark_mutex);
|
mutex_unlock(&group->mark_mutex);
|
||||||
|
fsnotify_free_mark(mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
|
void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
|
||||||
|
@ -342,7 +348,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
|
||||||
* inode->i_lock
|
* inode->i_lock
|
||||||
*/
|
*/
|
||||||
spin_lock(&mark->lock);
|
spin_lock(&mark->lock);
|
||||||
mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE;
|
mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_ATTACHED;
|
||||||
|
|
||||||
fsnotify_get_group(group);
|
fsnotify_get_group(group);
|
||||||
mark->group = group;
|
mark->group = group;
|
||||||
|
@ -448,8 +454,9 @@ void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group,
|
||||||
}
|
}
|
||||||
mark = list_first_entry(&to_free, struct fsnotify_mark, g_list);
|
mark = list_first_entry(&to_free, struct fsnotify_mark, g_list);
|
||||||
fsnotify_get_mark(mark);
|
fsnotify_get_mark(mark);
|
||||||
fsnotify_destroy_mark_locked(mark, group);
|
fsnotify_detach_mark(mark);
|
||||||
mutex_unlock(&group->mark_mutex);
|
mutex_unlock(&group->mark_mutex);
|
||||||
|
fsnotify_free_mark(mark);
|
||||||
fsnotify_put_mark(mark);
|
fsnotify_put_mark(mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,7 @@ struct fsnotify_mark {
|
||||||
#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04
|
#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04
|
||||||
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08
|
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08
|
||||||
#define FSNOTIFY_MARK_FLAG_ALIVE 0x10
|
#define FSNOTIFY_MARK_FLAG_ALIVE 0x10
|
||||||
|
#define FSNOTIFY_MARK_FLAG_ATTACHED 0x20
|
||||||
unsigned int flags; /* flags [mark->lock] */
|
unsigned int flags; /* flags [mark->lock] */
|
||||||
void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
|
void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
|
||||||
};
|
};
|
||||||
|
@ -353,8 +354,10 @@ extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct fsnotify_
|
||||||
/* given a group and a mark, flag mark to be freed when all references are dropped */
|
/* given a group and a mark, flag mark to be freed when all references are dropped */
|
||||||
extern void fsnotify_destroy_mark(struct fsnotify_mark *mark,
|
extern void fsnotify_destroy_mark(struct fsnotify_mark *mark,
|
||||||
struct fsnotify_group *group);
|
struct fsnotify_group *group);
|
||||||
extern void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark,
|
/* detach mark from inode / mount list, group list, drop inode reference */
|
||||||
struct fsnotify_group *group);
|
extern void fsnotify_detach_mark(struct fsnotify_mark *mark);
|
||||||
|
/* free mark */
|
||||||
|
extern void fsnotify_free_mark(struct fsnotify_mark *mark);
|
||||||
/* run all the marks in a group, and clear all of the vfsmount marks */
|
/* run all the marks in a group, and clear all of the vfsmount marks */
|
||||||
extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group);
|
extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group);
|
||||||
/* run all the marks in a group, and clear all of the inode marks */
|
/* run all the marks in a group, and clear all of the inode marks */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue