diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0c0a48b1659f..83ce6db34039 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,20 +194,36 @@ 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); - - fsnotify_recalc_inode_mask_locked(inode); + goto out; } - spin_unlock(&inode->i_lock); + /* 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) - ret = -EEXIST; + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } + + 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); return ret; } diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index ec580a25d293..c4b3f14d2530 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -141,34 +141,46 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct vfsmount *mnt, 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_VFSMOUNT; - /* - * LOCKING ORDER!!!! - * mark->lock - * group->mark_lock - * mnt->mnt_root->d_lock - */ assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); spin_lock(&mnt->mnt_root->d_lock); - if (!allow_dups) - lmark = fsnotify_find_vfsmount_mark_locked(group, mnt); - if (!lmark) { - mark->m.mnt = mnt; + mark->m.mnt = mnt; + /* is mark the first mark? */ + if (hlist_empty(&mnt->mnt_fsnotify_marks)) { hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); - - fsnotify_recalc_vfsmount_mask_locked(mnt); - } else { - ret = -EEXIST; + goto out; } + /* should mark be in the middle of the current list? */ + hlist_for_each_entry(lmark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + last = node; + + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } + + if (mark->group < lmark->group) + continue; + + hlist_add_before(&mark->m.m_list, &lmark->m.m_list); + goto out; + } + + BUG_ON(last == NULL); + /* mark should be the last entry. last is the current last entry */ + hlist_add_after(last, &mark->m.m_list); +out: + fsnotify_recalc_vfsmount_mask_locked(mnt); spin_unlock(&mnt->mnt_root->d_lock); return ret;