Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
[pandora-kernel.git] / fs / ceph / dir.c
index 382abc9..bca3948 100644 (file)
@@ -108,7 +108,7 @@ static unsigned fpos_off(loff_t p)
  * falling back to a "normal" sync readdir if any dentries in the dir
  * are dropped.
  *
- * I_COMPLETE tells indicates we have all dentries in the dir.  It is
+ * D_COMPLETE tells indicates we have all dentries in the dir.  It is
  * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by
  * the MDS if/when the directory is modified).
  */
@@ -199,8 +199,8 @@ more:
        filp->f_pos++;
 
        /* make sure a dentry wasn't dropped while we didn't have parent lock */
-       if (!ceph_i_test(dir, CEPH_I_COMPLETE)) {
-               dout(" lost I_COMPLETE on %p; falling back to mds\n", dir);
+       if (!ceph_dir_test_complete(dir)) {
+               dout(" lost D_COMPLETE on %p; falling back to mds\n", dir);
                err = -EAGAIN;
                goto out;
        }
@@ -285,7 +285,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir)
        if ((filp->f_pos == 2 || fi->dentry) &&
            !ceph_test_mount_opt(fsc, NOASYNCREADDIR) &&
            ceph_snap(inode) != CEPH_SNAPDIR &&
-           (ci->i_ceph_flags & CEPH_I_COMPLETE) &&
+           ceph_dir_test_complete(inode) &&
            __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) {
                spin_unlock(&inode->i_lock);
                err = __dcache_readdir(filp, dirent, filldir);
@@ -351,7 +351,7 @@ more:
 
                if (!req->r_did_prepopulate) {
                        dout("readdir !did_prepopulate");
-                       fi->dir_release_count--;    /* preclude I_COMPLETE */
+                       fi->dir_release_count--;    /* preclude D_COMPLETE */
                }
 
                /* note next offset and last dentry name */
@@ -430,8 +430,7 @@ more:
         */
        spin_lock(&inode->i_lock);
        if (ci->i_release_count == fi->dir_release_count) {
-               dout(" marking %p complete\n", inode);
-               /* ci->i_ceph_flags |= CEPH_I_COMPLETE; */
+               ceph_dir_set_complete(inode);
                ci->i_max_offset = filp->f_pos;
        }
        spin_unlock(&inode->i_lock);
@@ -614,7 +613,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                            fsc->mount_options->snapdir_name,
                            dentry->d_name.len) &&
                    !is_root_ceph_dentry(dir, dentry) &&
-                   (ci->i_ceph_flags & CEPH_I_COMPLETE) &&
+                   ceph_dir_test_complete(dir) &&
                    (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) {
                        spin_unlock(&dir->i_lock);
                        dout(" dir %p complete, -ENOENT\n", dir);
@@ -934,7 +933,7 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
                 */
 
                /* d_move screws up d_subdirs order */
-               ceph_i_clear(new_dir, CEPH_I_COMPLETE);
+               ceph_dir_clear_complete(new_dir);
 
                d_move(old_dentry, new_dentry);
 
@@ -1092,7 +1091,75 @@ static int ceph_snapdir_d_revalidate(struct dentry *dentry,
        return 1;
 }
 
+/*
+ * Set/clear/test dir complete flag on the dir's dentry.
+ */
+static struct dentry * __d_find_any_alias(struct inode *inode)
+{
+       struct dentry *alias;
+
+       if (list_empty(&inode->i_dentry))
+               return NULL;
+       alias = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
+       return alias;
+}
+
+void ceph_dir_set_complete(struct inode *inode)
+{
+       struct dentry *dentry = __d_find_any_alias(inode);
+       
+       if (dentry && ceph_dentry(dentry)) {
+               dout(" marking %p (%p) complete\n", inode, dentry);
+               set_bit(CEPH_D_COMPLETE, &ceph_dentry(dentry)->flags);
+       }
+}
+
+void ceph_dir_clear_complete(struct inode *inode)
+{
+       struct dentry *dentry = __d_find_any_alias(inode);
 
+       if (dentry && ceph_dentry(dentry)) {
+               dout(" marking %p (%p) NOT complete\n", inode, dentry);
+               clear_bit(CEPH_D_COMPLETE, &ceph_dentry(dentry)->flags);
+       }
+}
+
+bool ceph_dir_test_complete(struct inode *inode)
+{
+       struct dentry *dentry = __d_find_any_alias(inode);
+
+       if (dentry && ceph_dentry(dentry))
+               return test_bit(CEPH_D_COMPLETE, &ceph_dentry(dentry)->flags);
+       return false;
+}
+
+/*
+ * When the VFS prunes a dentry from the cache, we need to clear the
+ * complete flag on the parent directory.
+ *
+ * Called under dentry->d_lock.
+ */
+static void ceph_d_prune(struct dentry *dentry)
+{
+       struct ceph_dentry_info *di;
+
+       dout("ceph_d_prune %p\n", dentry);
+
+       /* do we have a valid parent? */
+       if (!dentry->d_parent || IS_ROOT(dentry))
+               return;
+
+       /* if we are not hashed, we don't affect D_COMPLETE */
+       if (d_unhashed(dentry))
+               return;
+
+       /*
+        * we hold d_lock, so d_parent is stable, and d_fsdata is never
+        * cleared until d_release
+        */
+       di = ceph_dentry(dentry->d_parent);
+       clear_bit(CEPH_D_COMPLETE, &di->flags);
+}
 
 /*
  * read() on a dir.  This weird interface hack only works if mounted
@@ -1306,6 +1373,7 @@ const struct inode_operations ceph_dir_iops = {
 const struct dentry_operations ceph_dentry_ops = {
        .d_revalidate = ceph_d_revalidate,
        .d_release = ceph_d_release,
+       .d_prune = ceph_d_prune,
 };
 
 const struct dentry_operations ceph_snapdir_dentry_ops = {
@@ -1315,4 +1383,5 @@ const struct dentry_operations ceph_snapdir_dentry_ops = {
 
 const struct dentry_operations ceph_snap_dentry_ops = {
        .d_release = ceph_d_release,
+       .d_prune = ceph_d_prune,
 };