ceph: handle racing calls to ceph_init_dentry
authorSage Weil <sage@newdream.net>
Tue, 26 Jul 2011 18:30:15 +0000 (11:30 -0700)
committerSage Weil <sage@newdream.net>
Tue, 26 Jul 2011 18:30:15 +0000 (11:30 -0700)
The ->lookup() and prepopulate_readdir() callers are working with unhashed
dentries, so we don't have to worry.  The export.c callers, though, need
to initialize something they got back from d_obtain_alias() and are
potentially racing with other callers.  Make sure we don't return unless
the dentry is properly initialized (by us or someone else).

Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Reviewed-by: Yehuda Sadeh <yehuda@hq.newdream.net>
Signed-off-by: Sage Weil <sage@newdream.net>
fs/ceph/dir.c

index f39a409..883c954 100644 (file)
@@ -40,14 +40,6 @@ int ceph_init_dentry(struct dentry *dentry)
        if (dentry->d_fsdata)
                return 0;
 
-       if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
-           ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
-               d_set_d_op(dentry, &ceph_dentry_ops);
-       else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
-               d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
-       else
-               d_set_d_op(dentry, &ceph_snap_dentry_ops);
-
        di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
        if (!di)
                return -ENOMEM;          /* oh well */
@@ -58,10 +50,21 @@ int ceph_init_dentry(struct dentry *dentry)
                kmem_cache_free(ceph_dentry_cachep, di);
                goto out_unlock;
        }
+
+       if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
+           ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
+               d_set_d_op(dentry, &ceph_dentry_ops);
+       else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
+               d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
+       else
+               d_set_d_op(dentry, &ceph_snap_dentry_ops);
+
        di->dentry = dentry;
        di->lease_session = NULL;
-       dentry->d_fsdata = di;
        dentry->d_time = jiffies;
+       /* avoid reordering d_fsdata setup so that the check above is safe */
+       smp_mb();
+       dentry->d_fsdata = di;
        ceph_dentry_lru_add(dentry);
 out_unlock:
        spin_unlock(&dentry->d_lock);