summaryrefslogtreecommitdiff
path: root/fs/notify/mark.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/notify/mark.c')
-rw-r--r--fs/notify/mark.c66
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: