cifs: fix noserverino handling when unix extensions are enabled
[safe/jmp/linux-2.6] / fs / ocfs2 / dcache.c
index 09efe24..b4957c7 100644 (file)
@@ -38,6 +38,7 @@
 #include "dlmglue.h"
 #include "file.h"
 #include "inode.h"
+#include "super.h"
 
 
 static int ocfs2_dentry_revalidate(struct dentry *dentry,
@@ -84,6 +85,17 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry,
                goto bail;
        }
 
+       /*
+        * If the last lookup failed to create dentry lock, let us
+        * redo it.
+        */
+       if (!dentry->d_fsdata) {
+               mlog(0, "Inode %llu doesn't have dentry lock, "
+                    "returning false\n",
+                    (unsigned long long)OCFS2_I(inode)->ip_blkno);
+               goto bail;
+       }
+
        ret = 1;
 
 bail:
@@ -128,9 +140,9 @@ static int ocfs2_match_dentry(struct dentry *dentry,
 /*
  * Walk the inode alias list, and find a dentry which has a given
  * parent. ocfs2_dentry_attach_lock() wants to find _any_ alias as it
- * is looking for a dentry_lock reference. The vote thread is looking
- * to unhash aliases, so we allow it to skip any that already have
- * that property.
+ * is looking for a dentry_lock reference. The downconvert thread is
+ * looking to unhash aliases, so we allow it to skip any that already
+ * have that property.
  */
 struct dentry *ocfs2_find_local_alias(struct inode *inode,
                                      u64 parent_blkno,
@@ -183,9 +195,6 @@ DEFINE_SPINLOCK(dentry_attach_lock);
  * The dir cluster lock (held at either PR or EX mode) protects us
  * from unlink and rename on other nodes.
  *
- * The 'create' flag tells us whether we're doing this as a result of
- * a file creation.
- *
  * A dput() can happen asynchronously due to pruning, so we cover
  * attaching and detaching the dentry lock with a
  * dentry_attach_lock.
@@ -199,16 +208,15 @@ DEFINE_SPINLOCK(dentry_attach_lock);
  */
 int ocfs2_dentry_attach_lock(struct dentry *dentry,
                             struct inode *inode,
-                            u64 parent_blkno,
-                            int create)
+                            u64 parent_blkno)
 {
        int ret;
        struct dentry *alias;
        struct ocfs2_dentry_lock *dl = dentry->d_fsdata;
 
-       mlog(0, "Attach \"%.*s\", parent %llu, create %d, fsdata: %p\n",
+       mlog(0, "Attach \"%.*s\", parent %llu, fsdata: %p\n",
             dentry->d_name.len, dentry->d_name.name,
-            (unsigned long long)parent_blkno, create, dl);
+            (unsigned long long)parent_blkno, dl);
 
        /*
         * Negative dentry. We ignore these for now.
@@ -242,10 +250,9 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry,
                 * since we have it pinned, so our reference is safe.
                 */
                dl = alias->d_fsdata;
-               mlog_bug_on_msg(!dl, "parent %llu, ino %llu, create %d\n",
+               mlog_bug_on_msg(!dl, "parent %llu, ino %llu\n",
                                (unsigned long long)parent_blkno,
-                               (unsigned long long)OCFS2_I(inode)->ip_blkno,
-                               create);
+                               (unsigned long long)OCFS2_I(inode)->ip_blkno);
 
                mlog_bug_on_msg(dl->dl_parent_blkno != parent_blkno,
                                " \"%.*s\": old parent: %llu, new: %llu\n",
@@ -271,7 +278,7 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry,
        dl->dl_count = 0;
        /*
         * Does this have to happen below, for all attaches, in case
-        * the struct inode gets blown away by votes?
+        * the struct inode gets blown away by the downconvert thread?
         */
        dl->dl_inode = igrab(inode);
        dl->dl_parent_blkno = parent_blkno;
@@ -284,36 +291,82 @@ out_attach:
        spin_unlock(&dentry_attach_lock);
 
        /*
-        * Creation of a new file means that nobody can possibly have
-        * this name in the system, which means that acquiry of those
-        * locks can easily be optimized.
-        */
-       if (create) {
-               ret = ocfs2_create_new_lock(OCFS2_SB(inode->i_sb),
-                                           &dl->dl_lockres, 0);
-               if (ret)
-                       mlog_errno(ret);
-               goto out;
-       }
-
-       /*
         * This actually gets us our PRMODE level lock. From now on,
         * we'll have a notification if one of these names is
         * destroyed on another node.
         */
        ret = ocfs2_dentry_lock(dentry, 0);
-       if (ret) {
+       if (!ret)
+               ocfs2_dentry_unlock(dentry, 0);
+       else
                mlog_errno(ret);
-               goto out;
+
+       /*
+        * In case of error, manually free the allocation and do the iput().
+        * We need to do this because error here means no d_instantiate(),
+        * which means iput() will not be called during dput(dentry).
+        */
+       if (ret < 0 && !alias) {
+               ocfs2_lock_res_free(&dl->dl_lockres);
+               BUG_ON(dl->dl_count != 1);
+               spin_lock(&dentry_attach_lock);
+               dentry->d_fsdata = NULL;
+               spin_unlock(&dentry_attach_lock);
+               kfree(dl);
+               iput(inode);
        }
-       ocfs2_dentry_unlock(dentry, 0);
 
-out:
        dput(alias);
 
        return ret;
 }
 
+DEFINE_SPINLOCK(dentry_list_lock);
+
+/* We limit the number of dentry locks to drop in one go. We have
+ * this limit so that we don't starve other users of ocfs2_wq. */
+#define DL_INODE_DROP_COUNT 64
+
+/* Drop inode references from dentry locks */
+static void __ocfs2_drop_dl_inodes(struct ocfs2_super *osb, int drop_count)
+{
+       struct ocfs2_dentry_lock *dl;
+
+       spin_lock(&dentry_list_lock);
+       while (osb->dentry_lock_list && (drop_count < 0 || drop_count--)) {
+               dl = osb->dentry_lock_list;
+               osb->dentry_lock_list = dl->dl_next;
+               spin_unlock(&dentry_list_lock);
+               iput(dl->dl_inode);
+               kfree(dl);
+               spin_lock(&dentry_list_lock);
+       }
+       spin_unlock(&dentry_list_lock);
+}
+
+void ocfs2_drop_dl_inodes(struct work_struct *work)
+{
+       struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
+                                              dentry_lock_work);
+
+       __ocfs2_drop_dl_inodes(osb, DL_INODE_DROP_COUNT);
+       /*
+        * Don't queue dropping if umount is in progress. We flush the
+        * list in ocfs2_dismount_volume
+        */
+       spin_lock(&dentry_list_lock);
+       if (osb->dentry_lock_list &&
+           !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
+               queue_work(ocfs2_wq, &osb->dentry_lock_work);
+       spin_unlock(&dentry_list_lock);
+}
+
+/* Flush the whole work queue */
+void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb)
+{
+       __ocfs2_drop_dl_inodes(osb, -1);
+}
+
 /*
  * ocfs2_dentry_iput() and friends.
  *
@@ -340,14 +393,22 @@ static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb,
 {
        ocfs2_simple_drop_lockres(osb, &dl->dl_lockres);
        ocfs2_lock_res_free(&dl->dl_lockres);
-       iput(dl->dl_inode);
-       kfree(dl);
+
+       /* We leave dropping of inode reference to ocfs2_wq as that can
+        * possibly lead to inode deletion which gets tricky */
+       spin_lock(&dentry_list_lock);
+       if (!osb->dentry_lock_list &&
+           !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
+               queue_work(ocfs2_wq, &osb->dentry_lock_work);
+       dl->dl_next = osb->dentry_lock_list;
+       osb->dentry_lock_list = dl;
+       spin_unlock(&dentry_list_lock);
 }
 
 void ocfs2_dentry_lock_put(struct ocfs2_super *osb,
                           struct ocfs2_dentry_lock *dl)
 {
-       int unlock = 0;
+       int unlock;
 
        BUG_ON(dl->dl_count == 0);
 
@@ -364,12 +425,24 @@ static void ocfs2_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
        struct ocfs2_dentry_lock *dl = dentry->d_fsdata;
 
-       mlog_bug_on_msg(!dl && !(dentry->d_flags & DCACHE_DISCONNECTED),
-                       "dentry: %.*s\n", dentry->d_name.len,
-                       dentry->d_name.name);
+       if (!dl) {
+               /*
+                * No dentry lock is ok if we're disconnected or
+                * unhashed.
+                */
+               if (!(dentry->d_flags & DCACHE_DISCONNECTED) &&
+                   !d_unhashed(dentry)) {
+                       unsigned long long ino = 0ULL;
+                       if (inode)
+                               ino = (unsigned long long)OCFS2_I(inode)->ip_blkno;
+                       mlog(ML_ERROR, "Dentry is missing cluster lock. "
+                            "inode: %llu, d_flags: 0x%x, d_name: %.*s\n",
+                            ino, dentry->d_flags, dentry->d_name.len,
+                            dentry->d_name.name);
+               }
 
-       if (!dl)
                goto out;
+       }
 
        mlog_bug_on_msg(dl->dl_count == 0, "dentry: %.*s, count: %u\n",
                        dentry->d_name.len, dentry->d_name.name,
@@ -396,7 +469,7 @@ out:
  * directory locks. The dentries have already been deleted on other
  * nodes via ocfs2_remote_dentry_delete().
  *
- * Normally, the VFS handles the d_move() for the file sytem, after
+ * Normally, the VFS handles the d_move() for the file system, after
  * the ->rename() callback. OCFS2 wants to handle this internally, so
  * the new lock can be created atomically with respect to the cluster.
  */
@@ -414,18 +487,20 @@ void ocfs2_dentry_move(struct dentry *dentry, struct dentry *target,
         * XXX: Is there any advantage to dropping the lock here?
         */
        if (old_dir == new_dir)
-               return;
+               goto out_move;
 
        ocfs2_dentry_lock_put(osb, dentry->d_fsdata);
 
        dentry->d_fsdata = NULL;
-       ret = ocfs2_dentry_attach_lock(dentry, inode,
-                                      OCFS2_I(new_dir)->ip_blkno, 0);
+       ret = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(new_dir)->ip_blkno);
        if (ret)
                mlog_errno(ret);
+
+out_move:
+       d_move(dentry, target);
 }
 
-struct dentry_operations ocfs2_dentry_ops = {
+const struct dentry_operations ocfs2_dentry_ops = {
        .d_revalidate           = ocfs2_dentry_revalidate,
        .d_iput                 = ocfs2_dentry_iput,
 };