diff options
Diffstat (limited to 'fs/notify/mark.c')
-rw-r--r-- | fs/notify/mark.c | 66 |
1 files changed, 43 insertions, 23 deletions
diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 8a15c64fbe80..e8c2f829ce65 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -320,12 +320,13 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b) static int fsnotify_attach_connector_to_object( struct fsnotify_mark_connector **connp, + spinlock_t *lock, struct inode *inode, struct vfsmount *mnt) { struct fsnotify_mark_connector *conn; - conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_ATOMIC); + conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL); if (!conn) return -ENOMEM; INIT_HLIST_HEAD(&conn->list); @@ -341,7 +342,12 @@ static int fsnotify_attach_connector_to_object( * lockless_dereference() in fsnotify(). */ smp_wmb(); - *connp = conn; + spin_lock(lock); + if (!*connp) + *connp = conn; + else + kmem_cache_free(fsnotify_mark_connector_cachep, conn); + spin_unlock(lock); return 0; } @@ -352,20 +358,35 @@ static int fsnotify_attach_connector_to_object( * to which group and for which inodes. These marks are ordered according to * priority, highest number first, and then by the group's location in memory. */ -int fsnotify_add_mark_list(struct fsnotify_mark_connector **connp, - struct fsnotify_mark *mark, struct inode *inode, - struct vfsmount *mnt, int allow_dups) +static int fsnotify_add_mark_list(struct fsnotify_mark *mark, + struct inode *inode, struct vfsmount *mnt, + int allow_dups) { struct fsnotify_mark *lmark, *last = NULL; struct fsnotify_mark_connector *conn; + struct fsnotify_mark_connector **connp; + spinlock_t *lock; int cmp; - int err; + int err = 0; + + if (WARN_ON(!inode && !mnt)) + return -EINVAL; + if (inode) { + connp = &inode->i_fsnotify_marks; + lock = &inode->i_lock; + } else { + connp = &real_mount(mnt)->mnt_fsnotify_marks; + lock = &mnt->mnt_root->d_lock; + } if (!*connp) { - err = fsnotify_attach_connector_to_object(connp, inode, mnt); + err = fsnotify_attach_connector_to_object(connp, lock, + inode, mnt); if (err) return err; } + spin_lock(&mark->lock); + spin_lock(lock); conn = *connp; /* is mark the first mark? */ @@ -380,8 +401,10 @@ int fsnotify_add_mark_list(struct fsnotify_mark_connector **connp, hlist_for_each_entry(lmark, &conn->list, obj_list) { last = lmark; - if ((lmark->group == mark->group) && !allow_dups) - return -EEXIST; + if ((lmark->group == mark->group) && !allow_dups) { + err = -EEXIST; + goto out_err; + } cmp = fsnotify_compare_groups(lmark->group, mark->group); if (cmp >= 0) { @@ -395,7 +418,10 @@ int fsnotify_add_mark_list(struct fsnotify_mark_connector **connp, hlist_add_behind_rcu(&mark->obj_list, &last->obj_list); added: mark->connector = conn; - return 0; +out_err: + spin_unlock(lock); + spin_unlock(&mark->lock); + return err; } /* @@ -427,22 +453,16 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, list_add(&mark->g_list, &group->marks_list); atomic_inc(&group->num_marks); fsnotify_get_mark(mark); /* for i_list and g_list */ - - if (inode) { - ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); - if (ret) - goto err; - } else if (mnt) { - ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups); - if (ret) - goto err; - } else { - BUG(); - } spin_unlock(&mark->lock); + ret = fsnotify_add_mark_list(mark, inode, mnt, allow_dups); + if (ret) + goto err; + if (inode) - __fsnotify_update_child_dentry_flags(inode); + fsnotify_recalc_inode_mask(inode); + else + fsnotify_recalc_vfsmount_mask(mnt); return ret; err: |