]> git.openfabrics.org - ~shefty/rdma-dev.git/blobdiff - fs/ceph/dir.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[~shefty/rdma-dev.git] / fs / ceph / dir.c
index 1065ac779840ac4bb4c7f740c47c1ec33ef79787..382abc9a6a5409ffc052556f58bcb612c5680c5f 100644 (file)
@@ -40,14 +40,6 @@ int ceph_init_dentry(struct dentry *dentry)
        if (dentry->d_fsdata)
                return 0;
 
        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 */
        di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
        if (!di)
                return -ENOMEM;          /* oh well */
@@ -58,16 +50,42 @@ int ceph_init_dentry(struct dentry *dentry)
                kmem_cache_free(ceph_dentry_cachep, di);
                goto out_unlock;
        }
                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;
        di->dentry = dentry;
        di->lease_session = NULL;
-       dentry->d_fsdata = di;
        dentry->d_time = jiffies;
        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);
        return 0;
 }
 
        ceph_dentry_lru_add(dentry);
 out_unlock:
        spin_unlock(&dentry->d_lock);
        return 0;
 }
 
+struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry)
+{
+       struct inode *inode = NULL;
+
+       if (!dentry)
+               return NULL;
+
+       spin_lock(&dentry->d_lock);
+       if (dentry->d_parent) {
+               inode = dentry->d_parent->d_inode;
+               ihold(inode);
+       }
+       spin_unlock(&dentry->d_lock);
+       return inode;
+}
 
 
 /*
 
 
 /*
@@ -133,7 +151,7 @@ more:
                     d_unhashed(dentry) ? "!hashed" : "hashed",
                     parent->d_subdirs.prev, parent->d_subdirs.next);
                if (p == &parent->d_subdirs) {
                     d_unhashed(dentry) ? "!hashed" : "hashed",
                     parent->d_subdirs.prev, parent->d_subdirs.next);
                if (p == &parent->d_subdirs) {
-                       fi->at_end = 1;
+                       fi->flags |= CEPH_F_ATEND;
                        goto out_unlock;
                }
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
                        goto out_unlock;
                }
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
@@ -234,7 +252,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir)
        const int max_bytes = fsc->mount_options->max_readdir_bytes;
 
        dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
        const int max_bytes = fsc->mount_options->max_readdir_bytes;
 
        dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
-       if (fi->at_end)
+       if (fi->flags & CEPH_F_ATEND)
                return 0;
 
        /* always start with . and .. */
                return 0;
 
        /* always start with . and .. */
@@ -403,7 +421,7 @@ more:
                dout("readdir next frag is %x\n", frag);
                goto more;
        }
                dout("readdir next frag is %x\n", frag);
                goto more;
        }
-       fi->at_end = 1;
+       fi->flags |= CEPH_F_ATEND;
 
        /*
         * if dir_release_count still matches the dir, no dentries
 
        /*
         * if dir_release_count still matches the dir, no dentries
@@ -435,7 +453,7 @@ static void reset_readdir(struct ceph_file_info *fi)
                dput(fi->dentry);
                fi->dentry = NULL;
        }
                dput(fi->dentry);
                fi->dentry = NULL;
        }
-       fi->at_end = 0;
+       fi->flags &= ~CEPH_F_ATEND;
 }
 
 static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
 }
 
 static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
@@ -463,7 +481,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
                if (offset != file->f_pos) {
                        file->f_pos = offset;
                        file->f_version = 0;
                if (offset != file->f_pos) {
                        file->f_pos = offset;
                        file->f_version = 0;
-                       fi->at_end = 0;
+                       fi->flags &= ~CEPH_F_ATEND;
                }
                retval = offset;
 
                }
                retval = offset;
 
@@ -488,21 +506,13 @@ out:
 }
 
 /*
 }
 
 /*
- * Process result of a lookup/open request.
- *
- * Mainly, make sure we return the final req->r_dentry (if it already
- * existed) in place of the original VFS-provided dentry when they
- * differ.
- *
- * Gracefully handle the case where the MDS replies with -ENOENT and
- * no trace (which it may do, at its discretion, e.g., if it doesn't
- * care to issue a lease on the negative dentry).
+ * Handle lookups for the hidden .snap directory.
  */
  */
-struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
-                                 struct dentry *dentry, int err)
+int ceph_handle_snapdir(struct ceph_mds_request *req,
+                       struct dentry *dentry, int err)
 {
        struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
 {
        struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
-       struct inode *parent = dentry->d_parent->d_inode;
+       struct inode *parent = dentry->d_parent->d_inode; /* we hold i_mutex */
 
        /* .snap dir? */
        if (err == -ENOENT &&
 
        /* .snap dir? */
        if (err == -ENOENT &&
@@ -516,7 +526,23 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
                d_add(dentry, inode);
                err = 0;
        }
                d_add(dentry, inode);
                err = 0;
        }
+       return err;
+}
 
 
+/*
+ * Figure out final result of a lookup/open request.
+ *
+ * Mainly, make sure we return the final req->r_dentry (if it already
+ * existed) in place of the original VFS-provided dentry when they
+ * differ.
+ *
+ * Gracefully handle the case where the MDS replies with -ENOENT and
+ * no trace (which it may do, at its discretion, e.g., if it doesn't
+ * care to issue a lease on the negative dentry).
+ */
+struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
+                                 struct dentry *dentry, int err)
+{
        if (err == -ENOENT) {
                /* no trace? */
                err = 0;
        if (err == -ENOENT) {
                /* no trace? */
                err = 0;
@@ -610,6 +636,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
        req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);
        req->r_locked_dir = dir;
        err = ceph_mdsc_do_request(mdsc, NULL, req);
        req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE);
        req->r_locked_dir = dir;
        err = ceph_mdsc_do_request(mdsc, NULL, req);
+       err = ceph_handle_snapdir(req, dentry, err);
        dentry = ceph_finish_lookup(req, dentry, err);
        ceph_mdsc_put_request(req);  /* will dput(dentry) */
        dout("lookup result=%p\n", dentry);
        dentry = ceph_finish_lookup(req, dentry, err);
        ceph_mdsc_put_request(req);  /* will dput(dentry) */
        dout("lookup result=%p\n", dentry);
@@ -789,6 +816,7 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir,
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */
+       req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
        req->r_locked_dir = dir;
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
        req->r_locked_dir = dir;
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
@@ -887,6 +915,7 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
        req->r_dentry = dget(new_dentry);
        req->r_num_caps = 2;
        req->r_old_dentry = dget(old_dentry);
        req->r_dentry = dget(new_dentry);
        req->r_num_caps = 2;
        req->r_old_dentry = dget(old_dentry);
+       req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry);
        req->r_locked_dir = new_dir;
        req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED;
        req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL;
        req->r_locked_dir = new_dir;
        req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED;
        req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL;
@@ -1002,36 +1031,38 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
  */
 static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
  */
 static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
+       int valid = 0;
        struct inode *dir;
 
        if (nd && nd->flags & LOOKUP_RCU)
                return -ECHILD;
 
        struct inode *dir;
 
        if (nd && nd->flags & LOOKUP_RCU)
                return -ECHILD;
 
-       dir = dentry->d_parent->d_inode;
-
        dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
             dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
             ceph_dentry(dentry)->offset);
 
        dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
             dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
             ceph_dentry(dentry)->offset);
 
+       dir = ceph_get_dentry_parent_inode(dentry);
+
        /* always trust cached snapped dentries, snapdir dentry */
        if (ceph_snap(dir) != CEPH_NOSNAP) {
                dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry,
                     dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
        /* always trust cached snapped dentries, snapdir dentry */
        if (ceph_snap(dir) != CEPH_NOSNAP) {
                dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry,
                     dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
-               goto out_touch;
+               valid = 1;
+       } else if (dentry->d_inode &&
+                  ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) {
+               valid = 1;
+       } else if (dentry_lease_is_valid(dentry) ||
+                  dir_lease_is_valid(dir, dentry)) {
+               valid = 1;
        }
        }
-       if (dentry->d_inode && ceph_snap(dentry->d_inode) == CEPH_SNAPDIR)
-               goto out_touch;
-
-       if (dentry_lease_is_valid(dentry) ||
-           dir_lease_is_valid(dir, dentry))
-               goto out_touch;
 
 
-       dout("d_revalidate %p invalid\n", dentry);
-       d_drop(dentry);
-       return 0;
-out_touch:
-       ceph_dentry_lru_touch(dentry);
-       return 1;
+       dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid");
+       if (valid)
+               ceph_dentry_lru_touch(dentry);
+       else
+               d_drop(dentry);
+       iput(dir);
+       return valid;
 }
 
 /*
 }
 
 /*
@@ -1228,9 +1259,8 @@ void ceph_dentry_lru_del(struct dentry *dn)
  * Return name hash for a given dentry.  This is dependent on
  * the parent directory's hash function.
  */
  * Return name hash for a given dentry.  This is dependent on
  * the parent directory's hash function.
  */
-unsigned ceph_dentry_hash(struct dentry *dn)
+unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
 {
 {
-       struct inode *dir = dn->d_parent->d_inode;
        struct ceph_inode_info *dci = ceph_inode(dir);
 
        switch (dci->i_dir_layout.dl_dir_hash) {
        struct ceph_inode_info *dci = ceph_inode(dir);
 
        switch (dci->i_dir_layout.dl_dir_hash) {