extern int fsnotify_compare_groups(struct fsnotify_group *a,
struct fsnotify_group *b);
-/* Add mark to a proper place in mark list */
-extern int fsnotify_add_mark_list(struct fsnotify_mark_connector **connp,
- struct fsnotify_mark *mark,
- struct inode *inode, struct vfsmount *mnt,
- int allow_dups);
-/* add a mark to an inode */
-extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
- struct fsnotify_group *group, struct inode *inode,
- int allow_dups);
-/* add a mark to a vfsmount */
-extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
- struct fsnotify_group *group, struct vfsmount *mnt,
- int allow_dups);
-
/* vfsmount specific destruction of a mark */
extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark);
/* inode specific destruction of a mark */
return mark;
}
-/*
- * 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. These
- * marks are ordered according to priority, highest number first, and then by
- * 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)
-{
- int ret;
-
- BUG_ON(!mutex_is_locked(&group->mark_mutex));
- assert_spin_locked(&mark->lock);
-
- spin_lock(&inode->i_lock);
- ret = fsnotify_add_mark_list(&inode->i_fsnotify_marks, mark, inode,
- NULL, allow_dups);
- inode->i_fsnotify_mask = fsnotify_recalc_mask(inode->i_fsnotify_marks);
- spin_unlock(&inode->i_lock);
-
- return ret;
-}
-
/**
* fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes.
* @sb: superblock being unmounted.
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);
* 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;
}
* 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? */
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) {
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;
}
/*
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:
return mark;
}
-
-/*
- * Attach an initialized mark to a given group and vfsmount.
- * These marks may be used for the fsnotify backend to determine which
- * event types should be delivered to which groups.
- */
-int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
- struct fsnotify_group *group, struct vfsmount *mnt,
- int allow_dups)
-{
- struct mount *m = real_mount(mnt);
- int ret;
-
- BUG_ON(!mutex_is_locked(&group->mark_mutex));
- assert_spin_locked(&mark->lock);
-
- spin_lock(&mnt->mnt_root->d_lock);
- ret = fsnotify_add_mark_list(&m->mnt_fsnotify_marks, mark, NULL, mnt,
- allow_dups);
- m->mnt_fsnotify_mask = fsnotify_recalc_mask(m->mnt_fsnotify_marks);
- spin_unlock(&mnt->mnt_root->d_lock);
-
- return ret;
-}