瀏覽代碼

Merge branch 'vfs-scale-working' of git://git.kernel.org/pub/scm/linux/kernel/git/npiggin/linux-npiggin

* 'vfs-scale-working' of git://git.kernel.org/pub/scm/linux/kernel/git/npiggin/linux-npiggin: (57 commits)
  fs: scale mntget/mntput
  fs: rename vfsmount counter helpers
  fs: implement faster dentry memcmp
  fs: prefetch inode data in dcache lookup
  fs: improve scalability of pseudo filesystems
  fs: dcache per-inode inode alias locking
  fs: dcache per-bucket dcache hash locking
  bit_spinlock: add required includes
  kernel: add bl_list
  xfs: provide simple rcu-walk ACL implementation
  btrfs: provide simple rcu-walk ACL implementation
  ext2,3,4: provide simple rcu-walk ACL implementation
  fs: provide simple rcu-walk generic_check_acl implementation
  fs: provide rcu-walk aware permission i_ops
  fs: rcu-walk aware d_revalidate method
  fs: cache optimise dentry and inode for rcu-walk
  fs: dcache reduce branches in lookup path
  fs: dcache remove d_mounted
  fs: fs_struct use seqlock
  fs: rcu-walk for path lookup
  ...
Linus Torvalds 14 年之前
父節點
當前提交
b4a45f5fe8
共有 100 個文件被更改,包括 2411 次插入1089 次删除
  1. 16 13
      Documentation/filesystems/Locking
  2. 0 174
      Documentation/filesystems/dentry-locking.txt
  3. 382 0
      Documentation/filesystems/path-lookup.txt
  4. 68 1
      Documentation/filesystems/porting
  5. 57 17
      Documentation/filesystems/vfs.txt
  6. 3 3
      arch/ia64/kernel/perfmon.c
  7. 12 6
      arch/powerpc/platforms/cell/spufs/inode.c
  8. 2 6
      drivers/infiniband/hw/ipath/ipath_fs.c
  9. 1 4
      drivers/infiniband/hw/qib/qib_fs.c
  10. 1 1
      drivers/mtd/mtdchar.c
  11. 5 2
      drivers/staging/autofs/root.c
  12. 8 1
      drivers/staging/pohmelfs/inode.c
  13. 12 5
      drivers/staging/pohmelfs/path_entry.c
  14. 8 8
      drivers/staging/smbfs/cache.c
  15. 33 17
      drivers/staging/smbfs/dir.c
  16. 4 1
      drivers/staging/smbfs/file.c
  17. 8 1
      drivers/staging/smbfs/inode.c
  18. 7 5
      drivers/usb/core/inode.c
  19. 4 1
      fs/9p/acl.c
  20. 1 1
      fs/9p/acl.h
  21. 2 2
      fs/9p/vfs_dentry.c
  22. 23 16
      fs/9p/vfs_inode.c
  23. 8 5
      fs/adfs/dir.c
  24. 9 2
      fs/adfs/super.c
  25. 2 2
      fs/affs/amigaffs.c
  26. 41 27
      fs/affs/namei.c
  27. 9 2
      fs/affs/super.c
  28. 7 3
      fs/afs/dir.c
  29. 1 1
      fs/afs/internal.h
  30. 5 2
      fs/afs/security.c
  31. 9 1
      fs/afs/super.c
  32. 3 3
      fs/anon_inodes.c
  33. 11 10
      fs/autofs4/autofs_i.h
  34. 77 64
      fs/autofs4/expire.c
  35. 1 1
      fs/autofs4/inode.c
  36. 57 34
      fs/autofs4/root.c
  37. 18 5
      fs/autofs4/waitq.c
  38. 4 1
      fs/bad_inode.c
  39. 8 2
      fs/befs/linuxvfs.c
  40. 8 1
      fs/bfs/inode.c
  41. 8 1
      fs/block_dev.c
  42. 13 8
      fs/btrfs/acl.c
  43. 1 1
      fs/btrfs/ctree.h
  44. 2 2
      fs/btrfs/export.c
  45. 12 5
      fs/btrfs/inode.c
  46. 18 10
      fs/ceph/dir.c
  47. 26 12
      fs/ceph/inode.c
  48. 1 1
      fs/ceph/mds_client.c
  49. 1 1
      fs/ceph/super.h
  50. 13 3
      fs/cifs/cifsfs.c
  51. 44 33
      fs/cifs/dir.c
  52. 7 7
      fs/cifs/inode.c
  53. 2 2
      fs/cifs/link.c
  54. 3 3
      fs/cifs/readdir.c
  55. 2 2
      fs/coda/cache.c
  56. 14 6
      fs/coda/dir.c
  57. 8 1
      fs/coda/inode.c
  58. 4 2
      fs/coda/pioctl.c
  59. 2 2
      fs/configfs/configfs_internal.h
  60. 12 12
      fs/configfs/dir.c
  61. 2 6
      fs/configfs/inode.c
  62. 981 394
      fs/dcache.c
  63. 7 2
      fs/ecryptfs/dentry.c
  64. 7 5
      fs/ecryptfs/inode.c
  65. 2 2
      fs/ecryptfs/main.c
  66. 11 1
      fs/ecryptfs/super.c
  67. 8 1
      fs/efs/super.c
  68. 8 1
      fs/exofs/super.c
  69. 8 6
      fs/exportfs/expfs.c
  70. 9 2
      fs/ext2/acl.c
  71. 1 1
      fs/ext2/acl.h
  72. 8 1
      fs/ext2/super.c
  73. 9 2
      fs/ext3/acl.c
  74. 1 1
      fs/ext3/acl.h
  75. 8 1
      fs/ext3/super.c
  76. 9 2
      fs/ext4/acl.c
  77. 1 1
      fs/ext4/acl.h
  78. 8 1
      fs/ext4/super.c
  79. 10 3
      fs/fat/inode.c
  80. 13 10
      fs/fat/namei_msdos.c
  81. 35 20
      fs/fat/namei_vfat.c
  82. 3 0
      fs/filesystems.c
  83. 8 1
      fs/freevxfs/vxfs_inode.c
  84. 26 10
      fs/fs_struct.c
  85. 13 5
      fs/fuse/dir.c
  86. 10 3
      fs/fuse/inode.c
  87. 13 7
      fs/generic_acl.c
  88. 4 1
      fs/gfs2/acl.c
  89. 1 1
      fs/gfs2/acl.h
  90. 16 6
      fs/gfs2/dentry.c
  91. 2 2
      fs/gfs2/export.c
  92. 1 1
      fs/gfs2/file.c
  93. 2 2
      fs/gfs2/inode.c
  94. 1 1
      fs/gfs2/inode.h
  95. 1 1
      fs/gfs2/ops_fstype.c
  96. 12 8
      fs/gfs2/ops_inode.c
  97. 8 1
      fs/gfs2/super.c
  98. 1 1
      fs/hfs/dir.c
  99. 6 2
      fs/hfs/hfs_fs.h
  100. 9 8
      fs/hfs/string.c

+ 16 - 13
Documentation/filesystems/Locking

@@ -9,22 +9,25 @@ be able to use diff(1).
 
 
 --------------------------- dentry_operations --------------------------
 --------------------------- dentry_operations --------------------------
 prototypes:
 prototypes:
-	int (*d_revalidate)(struct dentry *, int);
-	int (*d_hash) (struct dentry *, struct qstr *);
-	int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
+	int (*d_revalidate)(struct dentry *, struct nameidata *);
+	int (*d_hash)(const struct dentry *, const struct inode *,
+			struct qstr *);
+	int (*d_compare)(const struct dentry *, const struct inode *,
+			const struct dentry *, const struct inode *,
+			unsigned int, const char *, const struct qstr *);
 	int (*d_delete)(struct dentry *);
 	int (*d_delete)(struct dentry *);
 	void (*d_release)(struct dentry *);
 	void (*d_release)(struct dentry *);
 	void (*d_iput)(struct dentry *, struct inode *);
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
 	char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
 
 
 locking rules:
 locking rules:
-		dcache_lock	rename_lock	->d_lock	may block
-d_revalidate:	no		no		no		yes
-d_hash		no		no		no		yes
-d_compare:	no		yes		no		no 
-d_delete:	yes		no		yes		no
-d_release:	no		no		no		yes
-d_iput:		no		no		no		yes
+		rename_lock	->d_lock	may block	rcu-walk
+d_revalidate:	no		no		yes (ref-walk)	maybe
+d_hash		no		no		no		maybe
+d_compare:	yes		no		no		maybe
+d_delete:	no		yes		no		no
+d_release:	no		no		yes		no
+d_iput:		no		no		yes		no
 d_dname:	no		no		no		no
 d_dname:	no		no		no		no
 
 
 --------------------------- inode_operations --------------------------- 
 --------------------------- inode_operations --------------------------- 
@@ -44,8 +47,8 @@ ata *);
 	void * (*follow_link) (struct dentry *, struct nameidata *);
 	void * (*follow_link) (struct dentry *, struct nameidata *);
 	void (*put_link) (struct dentry *, struct nameidata *, void *);
 	void (*put_link) (struct dentry *, struct nameidata *, void *);
 	void (*truncate) (struct inode *);
 	void (*truncate) (struct inode *);
-	int (*permission) (struct inode *, int, struct nameidata *);
-	int (*check_acl)(struct inode *, int);
+	int (*permission) (struct inode *, int, unsigned int);
+	int (*check_acl)(struct inode *, int, unsigned int);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
 	int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -73,7 +76,7 @@ follow_link:	no
 put_link:	no
 put_link:	no
 truncate:	yes		(see below)
 truncate:	yes		(see below)
 setattr:	yes
 setattr:	yes
-permission:	no
+permission:	no (may not block if called in rcu-walk mode)
 check_acl:	no
 check_acl:	no
 getattr:	no
 getattr:	no
 setxattr:	yes
 setxattr:	yes

+ 0 - 174
Documentation/filesystems/dentry-locking.txt

@@ -1,174 +0,0 @@
-RCU-based dcache locking model
-==============================
-
-On many workloads, the most common operation on dcache is to look up a
-dentry, given a parent dentry and the name of the child. Typically,
-for every open(), stat() etc., the dentry corresponding to the
-pathname will be looked up by walking the tree starting with the first
-component of the pathname and using that dentry along with the next
-component to look up the next level and so on. Since it is a frequent
-operation for workloads like multiuser environments and web servers,
-it is important to optimize this path.
-
-Prior to 2.5.10, dcache_lock was acquired in d_lookup and thus in
-every component during path look-up. Since 2.5.10 onwards, fast-walk
-algorithm changed this by holding the dcache_lock at the beginning and
-walking as many cached path component dentries as possible. This
-significantly decreases the number of acquisition of
-dcache_lock. However it also increases the lock hold time
-significantly and affects performance in large SMP machines. Since
-2.5.62 kernel, dcache has been using a new locking model that uses RCU
-to make dcache look-up lock-free.
-
-The current dcache locking model is not very different from the
-existing dcache locking model. Prior to 2.5.62 kernel, dcache_lock
-protected the hash chain, d_child, d_alias, d_lru lists as well as
-d_inode and several other things like mount look-up. RCU-based changes
-affect only the way the hash chain is protected. For everything else
-the dcache_lock must be taken for both traversing as well as
-updating. The hash chain updates too take the dcache_lock.  The
-significant change is the way d_lookup traverses the hash chain, it
-doesn't acquire the dcache_lock for this and rely on RCU to ensure
-that the dentry has not been *freed*.
-
-
-Dcache locking details
-======================
-
-For many multi-user workloads, open() and stat() on files are very
-frequently occurring operations. Both involve walking of path names to
-find the dentry corresponding to the concerned file. In 2.4 kernel,
-dcache_lock was held during look-up of each path component. Contention
-and cache-line bouncing of this global lock caused significant
-scalability problems. With the introduction of RCU in Linux kernel,
-this was worked around by making the look-up of path components during
-path walking lock-free.
-
-
-Safe lock-free look-up of dcache hash table
-===========================================
-
-Dcache is a complex data structure with the hash table entries also
-linked together in other lists. In 2.4 kernel, dcache_lock protected
-all the lists. We applied RCU only on hash chain walking. The rest of
-the lists are still protected by dcache_lock.  Some of the important
-changes are :
-
-1. The deletion from hash chain is done using hlist_del_rcu() macro
-   which doesn't initialize next pointer of the deleted dentry and
-   this allows us to walk safely lock-free while a deletion is
-   happening.
-
-2. Insertion of a dentry into the hash table is done using
-   hlist_add_head_rcu() which take care of ordering the writes - the
-   writes to the dentry must be visible before the dentry is
-   inserted. This works in conjunction with hlist_for_each_rcu(),
-   which has since been replaced by hlist_for_each_entry_rcu(), while
-   walking the hash chain. The only requirement is that all
-   initialization to the dentry must be done before
-   hlist_add_head_rcu() since we don't have dcache_lock protection
-   while traversing the hash chain. This isn't different from the
-   existing code.
-
-3. The dentry looked up without holding dcache_lock by cannot be
-   returned for walking if it is unhashed. It then may have a NULL
-   d_inode or other bogosity since RCU doesn't protect the other
-   fields in the dentry. We therefore use a flag DCACHE_UNHASHED to
-   indicate unhashed dentries and use this in conjunction with a
-   per-dentry lock (d_lock). Once looked up without the dcache_lock,
-   we acquire the per-dentry lock (d_lock) and check if the dentry is
-   unhashed. If so, the look-up is failed. If not, the reference count
-   of the dentry is increased and the dentry is returned.
-
-4. Once a dentry is looked up, it must be ensured during the path walk
-   for that component it doesn't go away. In pre-2.5.10 code, this was
-   done holding a reference to the dentry. dcache_rcu does the same.
-   In some sense, dcache_rcu path walking looks like the pre-2.5.10
-   version.
-
-5. All dentry hash chain updates must take the dcache_lock as well as
-   the per-dentry lock in that order. dput() does this to ensure that
-   a dentry that has just been looked up in another CPU doesn't get
-   deleted before dget() can be done on it.
-
-6. There are several ways to do reference counting of RCU protected
-   objects. One such example is in ipv4 route cache where deferred
-   freeing (using call_rcu()) is done as soon as the reference count
-   goes to zero. This cannot be done in the case of dentries because
-   tearing down of dentries require blocking (dentry_iput()) which
-   isn't supported from RCU callbacks. Instead, tearing down of
-   dentries happen synchronously in dput(), but actual freeing happens
-   later when RCU grace period is over. This allows safe lock-free
-   walking of the hash chains, but a matched dentry may have been
-   partially torn down. The checking of DCACHE_UNHASHED flag with
-   d_lock held detects such dentries and prevents them from being
-   returned from look-up.
-
-
-Maintaining POSIX rename semantics
-==================================
-
-Since look-up of dentries is lock-free, it can race against a
-concurrent rename operation. For example, during rename of file A to
-B, look-up of either A or B must succeed.  So, if look-up of B happens
-after A has been removed from the hash chain but not added to the new
-hash chain, it may fail.  Also, a comparison while the name is being
-written concurrently by a rename may result in false positive matches
-violating rename semantics.  Issues related to race with rename are
-handled as described below :
-
-1. Look-up can be done in two ways - d_lookup() which is safe from
-   simultaneous renames and __d_lookup() which is not.  If
-   __d_lookup() fails, it must be followed up by a d_lookup() to
-   correctly determine whether a dentry is in the hash table or
-   not. d_lookup() protects look-ups using a sequence lock
-   (rename_lock).
-
-2. The name associated with a dentry (d_name) may be changed if a
-   rename is allowed to happen simultaneously. To avoid memcmp() in
-   __d_lookup() go out of bounds due to a rename and false positive
-   comparison, the name comparison is done while holding the
-   per-dentry lock. This prevents concurrent renames during this
-   operation.
-
-3. Hash table walking during look-up may move to a different bucket as
-   the current dentry is moved to a different bucket due to rename.
-   But we use hlists in dcache hash table and they are
-   null-terminated.  So, even if a dentry moves to a different bucket,
-   hash chain walk will terminate. [with a list_head list, it may not
-   since termination is when the list_head in the original bucket is
-   reached].  Since we redo the d_parent check and compare name while
-   holding d_lock, lock-free look-up will not race against d_move().
-
-4. There can be a theoretical race when a dentry keeps coming back to
-   original bucket due to double moves. Due to this look-up may
-   consider that it has never moved and can end up in a infinite loop.
-   But this is not any worse that theoretical livelocks we already
-   have in the kernel.
-
-
-Important guidelines for filesystem developers related to dcache_rcu
-====================================================================
-
-1. Existing dcache interfaces (pre-2.5.62) exported to filesystem
-   don't change. Only dcache internal implementation changes. However
-   filesystems *must not* delete from the dentry hash chains directly
-   using the list macros like allowed earlier. They must use dcache
-   APIs like d_drop() or __d_drop() depending on the situation.
-
-2. d_flags is now protected by a per-dentry lock (d_lock). All access
-   to d_flags must be protected by it.
-
-3. For a hashed dentry, checking of d_count needs to be protected by
-   d_lock.
-
-
-Papers and other documentation on dcache locking
-================================================
-
-1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124).
-
-2. http://lse.sourceforge.net/locking/dcache/dcache.html
-
-
-

+ 382 - 0
Documentation/filesystems/path-lookup.txt

@@ -0,0 +1,382 @@
+Path walking and name lookup locking
+====================================
+
+Path resolution is the finding a dentry corresponding to a path name string, by
+performing a path walk. Typically, for every open(), stat() etc., the path name
+will be resolved. Paths are resolved by walking the namespace tree, starting
+with the first component of the pathname (eg. root or cwd) with a known dentry,
+then finding the child of that dentry, which is named the next component in the
+path string. Then repeating the lookup from the child dentry and finding its
+child with the next element, and so on.
+
+Since it is a frequent operation for workloads like multiuser environments and
+web servers, it is important to optimize this code.
+
+Path walking synchronisation history:
+Prior to 2.5.10, dcache_lock was acquired in d_lookup (dcache hash lookup) and
+thus in every component during path look-up. Since 2.5.10 onwards, fast-walk
+algorithm changed this by holding the dcache_lock at the beginning and walking
+as many cached path component dentries as possible. This significantly
+decreases the number of acquisition of dcache_lock. However it also increases
+the lock hold time significantly and affects performance in large SMP machines.
+Since 2.5.62 kernel, dcache has been using a new locking model that uses RCU to
+make dcache look-up lock-free.
+
+All the above algorithms required taking a lock and reference count on the
+dentry that was looked up, so that may be used as the basis for walking the
+next path element. This is inefficient and unscalable. It is inefficient
+because of the locks and atomic operations required for every dentry element
+slows things down. It is not scalable because many parallel applications that
+are path-walk intensive tend to do path lookups starting from a common dentry
+(usually, the root "/" or current working directory). So contention on these
+common path elements causes lock and cacheline queueing.
+
+Since 2.6.38, RCU is used to make a significant part of the entire path walk
+(including dcache look-up) completely "store-free" (so, no locks, atomics, or
+even stores into cachelines of common dentries). This is known as "rcu-walk"
+path walking.
+
+Path walking overview
+=====================
+
+A name string specifies a start (root directory, cwd, fd-relative) and a
+sequence of elements (directory entry names), which together refer to a path in
+the namespace. A path is represented as a (dentry, vfsmount) tuple. The name
+elements are sub-strings, seperated by '/'.
+
+Name lookups will want to find a particular path that a name string refers to
+(usually the final element, or parent of final element). This is done by taking
+the path given by the name's starting point (which we know in advance -- eg.
+current->fs->cwd or current->fs->root) as the first parent of the lookup. Then
+iteratively for each subsequent name element, look up the child of the current
+parent with the given name and if it is not the desired entry, make it the
+parent for the next lookup.
+
+A parent, of course, must be a directory, and we must have appropriate
+permissions on the parent inode to be able to walk into it.
+
+Turning the child into a parent for the next lookup requires more checks and
+procedures. Symlinks essentially substitute the symlink name for the target
+name in the name string, and require some recursive path walking.  Mount points
+must be followed into (thus changing the vfsmount that subsequent path elements
+refer to), switching from the mount point path to the root of the particular
+mounted vfsmount. These behaviours are variously modified depending on the
+exact path walking flags.
+
+Path walking then must, broadly, do several particular things:
+- find the start point of the walk;
+- perform permissions and validity checks on inodes;
+- perform dcache hash name lookups on (parent, name element) tuples;
+- traverse mount points;
+- traverse symlinks;
+- lookup and create missing parts of the path on demand.
+
+Safe store-free look-up of dcache hash table
+============================================
+
+Dcache name lookup
+------------------
+In order to lookup a dcache (parent, name) tuple, we take a hash on the tuple
+and use that to select a bucket in the dcache-hash table. The list of entries
+in that bucket is then walked, and we do a full comparison of each entry
+against our (parent, name) tuple.
+
+The hash lists are RCU protected, so list walking is not serialised with
+concurrent updates (insertion, deletion from the hash). This is a standard RCU
+list application with the exception of renames, which will be covered below.
+
+Parent and name members of a dentry, as well as its membership in the dcache
+hash, and its inode are protected by the per-dentry d_lock spinlock. A
+reference is taken on the dentry (while the fields are verified under d_lock),
+and this stabilises its d_inode pointer and actual inode. This gives a stable
+point to perform the next step of our path walk against.
+
+These members are also protected by d_seq seqlock, although this offers
+read-only protection and no durability of results, so care must be taken when
+using d_seq for synchronisation (see seqcount based lookups, below).
+
+Renames
+-------
+Back to the rename case. In usual RCU protected lists, the only operations that
+will happen to an object is insertion, and then eventually removal from the
+list. The object will not be reused until an RCU grace period is complete.
+This ensures the RCU list traversal primitives can run over the object without
+problems (see RCU documentation for how this works).
+
+However when a dentry is renamed, its hash value can change, requiring it to be
+moved to a new hash list. Allocating and inserting a new alias would be
+expensive and also problematic for directory dentries. Latency would be far to
+high to wait for a grace period after removing the dentry and before inserting
+it in the new hash bucket. So what is done is to insert the dentry into the
+new list immediately.
+
+However, when the dentry's list pointers are updated to point to objects in the
+new list before waiting for a grace period, this can result in a concurrent RCU
+lookup of the old list veering off into the new (incorrect) list and missing
+the remaining dentries on the list.
+
+There is no fundamental problem with walking down the wrong list, because the
+dentry comparisons will never match. However it is fatal to miss a matching
+dentry. So a seqlock is used to detect when a rename has occurred, and so the
+lookup can be retried.
+
+         1      2      3
+        +---+  +---+  +---+
+hlist-->| N-+->| N-+->| N-+->
+head <--+-P |<-+-P |<-+-P |
+        +---+  +---+  +---+
+
+Rename of dentry 2 may require it deleted from the above list, and inserted
+into a new list. Deleting 2 gives the following list.
+
+         1             3
+        +---+         +---+     (don't worry, the longer pointers do not
+hlist-->| N-+-------->| N-+->    impose a measurable performance overhead
+head <--+-P |<--------+-P |      on modern CPUs)
+        +---+         +---+
+          ^      2      ^
+          |    +---+    |
+          |    | N-+----+
+          +----+-P |
+               +---+
+
+This is a standard RCU-list deletion, which leaves the deleted object's
+pointers intact, so a concurrent list walker that is currently looking at
+object 2 will correctly continue to object 3 when it is time to traverse the
+next object.
+
+However, when inserting object 2 onto a new list, we end up with this:
+
+         1             3
+        +---+         +---+
+hlist-->| N-+-------->| N-+->
+head <--+-P |<--------+-P |
+        +---+         +---+
+                 2
+               +---+
+               | N-+---->
+          <----+-P |
+               +---+
+
+Because we didn't wait for a grace period, there may be a concurrent lookup
+still at 2. Now when it follows 2's 'next' pointer, it will walk off into
+another list without ever having checked object 3.
+
+A related, but distinctly different, issue is that of rename atomicity versus
+lookup operations. If a file is renamed from 'A' to 'B', a lookup must only
+find either 'A' or 'B'. So if a lookup of 'A' returns NULL, a subsequent lookup
+of 'B' must succeed (note the reverse is not true).
+
+Between deleting the dentry from the old hash list, and inserting it on the new
+hash list, a lookup may find neither 'A' nor 'B' matching the dentry. The same
+rename seqlock is also used to cover this race in much the same way, by
+retrying a negative lookup result if a rename was in progress.
+
+Seqcount based lookups
+----------------------
+In refcount based dcache lookups, d_lock is used to serialise access to
+the dentry, stabilising it while comparing its name and parent and then
+taking a reference count (the reference count then gives a stable place to
+start the next part of the path walk from).
+
+As explained above, we would like to do path walking without taking locks or
+reference counts on intermediate dentries along the path. To do this, a per
+dentry seqlock (d_seq) is used to take a "coherent snapshot" of what the dentry
+looks like (its name, parent, and inode). That snapshot is then used to start
+the next part of the path walk. When loading the coherent snapshot under d_seq,
+care must be taken to load the members up-front, and use those pointers rather
+than reloading from the dentry later on (otherwise we'd have interesting things
+like d_inode going NULL underneath us, if the name was unlinked).
+
+Also important is to avoid performing any destructive operations (pretty much:
+no non-atomic stores to shared data), and to recheck the seqcount when we are
+"done" with the operation. Retry or abort if the seqcount does not match.
+Avoiding destructive or changing operations means we can easily unwind from
+failure.
+
+What this means is that a caller, provided they are holding RCU lock to
+protect the dentry object from disappearing, can perform a seqcount based
+lookup which does not increment the refcount on the dentry or write to
+it in any way. This returned dentry can be used for subsequent operations,
+provided that d_seq is rechecked after that operation is complete.
+
+Inodes are also rcu freed, so the seqcount lookup dentry's inode may also be
+queried for permissions.
+
+With this two parts of the puzzle, we can do path lookups without taking
+locks or refcounts on dentry elements.
+
+RCU-walk path walking design
+============================
+
+Path walking code now has two distinct modes, ref-walk and rcu-walk. ref-walk
+is the traditional[*] way of performing dcache lookups using d_lock to
+serialise concurrent modifications to the dentry and take a reference count on
+it. ref-walk is simple and obvious, and may sleep, take locks, etc while path
+walking is operating on each dentry. rcu-walk uses seqcount based dentry
+lookups, and can perform lookup of intermediate elements without any stores to
+shared data in the dentry or inode. rcu-walk can not be applied to all cases,
+eg. if the filesystem must sleep or perform non trivial operations, rcu-walk
+must be switched to ref-walk mode.
+
+[*] RCU is still used for the dentry hash lookup in ref-walk, but not the full
+    path walk.
+
+Where ref-walk uses a stable, refcounted ``parent'' to walk the remaining
+path string, rcu-walk uses a d_seq protected snapshot. When looking up a
+child of this parent snapshot, we open d_seq critical section on the child
+before closing d_seq critical section on the parent. This gives an interlocking
+ladder of snapshots to walk down.
+
+
+     proc 101
+      /----------------\
+     / comm:    "vi"    \
+    /  fs.root: dentry0  \
+    \  fs.cwd:  dentry2  /
+     \                  /
+      \----------------/
+
+So when vi wants to open("/home/npiggin/test.c", O_RDWR), then it will
+start from current->fs->root, which is a pinned dentry. Alternatively,
+"./test.c" would start from cwd; both names refer to the same path in
+the context of proc101.
+
+     dentry 0
+    +---------------------+   rcu-walk begins here, we note d_seq, check the
+    | name:    "/"        |   inode's permission, and then look up the next
+    | inode:   10         |   path element which is "home"...
+    | children:"home", ...|
+    +---------------------+
+              |
+     dentry 1 V
+    +---------------------+   ... which brings us here. We find dentry1 via
+    | name:    "home"     |   hash lookup, then note d_seq and compare name
+    | inode:   678        |   string and parent pointer. When we have a match,
+    | children:"npiggin"  |   we now recheck the d_seq of dentry0. Then we
+    +---------------------+   check inode and look up the next element.
+              |
+     dentry2  V
+    +---------------------+   Note: if dentry0 is now modified, lookup is
+    | name:    "npiggin"  |   not necessarily invalid, so we need only keep a
+    | inode:   543        |   parent for d_seq verification, and grandparents
+    | children:"a.c", ... |   can be forgotten.
+    +---------------------+
+              |
+     dentry3  V
+    +---------------------+   At this point we have our destination dentry.
+    | name:    "a.c"      |   We now take its d_lock, verify d_seq of this
+    | inode:   14221      |   dentry. If that checks out, we can increment
+    | children:NULL       |   its refcount because we're holding d_lock.
+    +---------------------+
+
+Taking a refcount on a dentry from rcu-walk mode, by taking its d_lock,
+re-checking its d_seq, and then incrementing its refcount is called
+"dropping rcu" or dropping from rcu-walk into ref-walk mode.
+
+It is, in some sense, a bit of a house of cards. If the seqcount check of the
+parent snapshot fails, the house comes down, because we had closed the d_seq
+section on the grandparent, so we have nothing left to stand on. In that case,
+the path walk must be fully restarted (which we do in ref-walk mode, to avoid
+live locks). It is costly to have a full restart, but fortunately they are
+quite rare.
+
+When we reach a point where sleeping is required, or a filesystem callout
+requires ref-walk, then instead of restarting the walk, we attempt to drop rcu
+at the last known good dentry we have. Avoiding a full restart in ref-walk in
+these cases is fundamental for performance and scalability because blocking
+operations such as creates and unlinks are not uncommon.
+
+The detailed design for rcu-walk is like this:
+* LOOKUP_RCU is set in nd->flags, which distinguishes rcu-walk from ref-walk.
+* Take the RCU lock for the entire path walk, starting with the acquiring
+  of the starting path (eg. root/cwd/fd-path). So now dentry refcounts are
+  not required for dentry persistence.
+* synchronize_rcu is called when unregistering a filesystem, so we can
+  access d_ops and i_ops during rcu-walk.
+* Similarly take the vfsmount lock for the entire path walk. So now mnt
+  refcounts are not required for persistence. Also we are free to perform mount
+  lookups, and to assume dentry mount points and mount roots are stable up and
+  down the path.
+* Have a per-dentry seqlock to protect the dentry name, parent, and inode,
+  so we can load this tuple atomically, and also check whether any of its
+  members have changed.
+* Dentry lookups (based on parent, candidate string tuple) recheck the parent
+  sequence after the child is found in case anything changed in the parent
+  during the path walk.
+* inode is also RCU protected so we can load d_inode and use the inode for
+  limited things.
+* i_mode, i_uid, i_gid can be tested for exec permissions during path walk.
+* i_op can be loaded.
+* When the destination dentry is reached, drop rcu there (ie. take d_lock,
+  verify d_seq, increment refcount).
+* If seqlock verification fails anywhere along the path, do a full restart
+  of the path lookup in ref-walk mode. -ECHILD tends to be used (for want of
+  a better errno) to signal an rcu-walk failure.
+
+The cases where rcu-walk cannot continue are:
+* NULL dentry (ie. any uncached path element)
+* Following links
+
+It may be possible eventually to make following links rcu-walk aware.
+
+Uncached path elements will always require dropping to ref-walk mode, at the
+very least because i_mutex needs to be grabbed, and objects allocated.
+
+Final note:
+"store-free" path walking is not strictly store free. We take vfsmount lock
+and refcounts (both of which can be made per-cpu), and we also store to the
+stack (which is essentially CPU-local), and we also have to take locks and
+refcount on final dentry.
+
+The point is that shared data, where practically possible, is not locked
+or stored into. The result is massive improvements in performance and
+scalability of path resolution.
+
+
+Interesting statistics
+======================
+
+The following table gives rcu lookup statistics for a few simple workloads
+(2s12c24t Westmere, debian non-graphical system). Ungraceful are attempts to
+drop rcu that fail due to d_seq failure and requiring the entire path lookup
+again. Other cases are successful rcu-drops that are required before the final
+element, nodentry for missing dentry, revalidate for filesystem revalidate
+routine requiring rcu drop, permission for permission check requiring drop,
+and link for symlink traversal requiring drop.
+
+     rcu-lookups     restart  nodentry          link  revalidate  permission
+bootup     47121           0      4624          1010       10283        7852
+dbench  25386793           0   6778659(26.7%)     55         549        1156
+kbuild   2696672          10     64442(2.3%)  108764(4.0%)     1        1590
+git diff   39605           0        28             2           0         106
+vfstest 24185492        4945    708725(2.9%) 1076136(4.4%)     0        2651
+
+What this shows is that failed rcu-walk lookups, ie. ones that are restarted
+entirely with ref-walk, are quite rare. Even the "vfstest" case which
+specifically has concurrent renames/mkdir/rmdir/ creat/unlink/etc to excercise
+such races is not showing a huge amount of restarts.
+
+Dropping from rcu-walk to ref-walk mean that we have encountered a dentry where
+the reference count needs to be taken for some reason. This is either because
+we have reached the target of the path walk, or because we have encountered a
+condition that can't be resolved in rcu-walk mode.  Ideally, we drop rcu-walk
+only when we have reached the target dentry, so the other statistics show where
+this does not happen.
+
+Note that a graceful drop from rcu-walk mode due to something such as the
+dentry not existing (which can be common) is not necessarily a failure of
+rcu-walk scheme, because some elements of the path may have been walked in
+rcu-walk mode. The further we get from common path elements (such as cwd or
+root), the less contended the dentry is likely to be. The closer we are to
+common path elements, the more likely they will exist in dentry cache.
+
+
+Papers and other documentation on dcache locking
+================================================
+
+1. Scaling dcache with RCU (http://linuxjournal.com/article.php?sid=7124).
+
+2. http://lse.sourceforge.net/locking/dcache/dcache.html
+
+

+ 68 - 1
Documentation/filesystems/porting

@@ -216,7 +216,6 @@ had ->revalidate()) add calls in ->follow_link()/->readlink().
 ->d_parent changes are not protected by BKL anymore.  Read access is safe
 ->d_parent changes are not protected by BKL anymore.  Read access is safe
 if at least one of the following is true:
 if at least one of the following is true:
 	* filesystem has no cross-directory rename()
 	* filesystem has no cross-directory rename()
-	* dcache_lock is held
 	* we know that parent had been locked (e.g. we are looking at
 	* we know that parent had been locked (e.g. we are looking at
 ->d_parent of ->lookup() argument).
 ->d_parent of ->lookup() argument).
 	* we are called from ->rename().
 	* we are called from ->rename().
@@ -318,3 +317,71 @@ if it's zero is not *and* *never* *had* *been* enough.  Final unlink() and iput(
 may happen while the inode is in the middle of ->write_inode(); e.g. if you blindly
 may happen while the inode is in the middle of ->write_inode(); e.g. if you blindly
 free the on-disk inode, you may end up doing that while ->write_inode() is writing
 free the on-disk inode, you may end up doing that while ->write_inode() is writing
 to it.
 to it.
+
+---
+[mandatory]
+
+	.d_delete() now only advises the dcache as to whether or not to cache
+unreferenced dentries, and is now only called when the dentry refcount goes to
+0. Even on 0 refcount transition, it must be able to tolerate being called 0,
+1, or more times (eg. constant, idempotent).
+
+---
+[mandatory]
+
+	.d_compare() calling convention and locking rules are significantly
+changed. Read updated documentation in Documentation/filesystems/vfs.txt (and
+look at examples of other filesystems) for guidance.
+
+---
+[mandatory]
+
+	.d_hash() calling convention and locking rules are significantly
+changed. Read updated documentation in Documentation/filesystems/vfs.txt (and
+look at examples of other filesystems) for guidance.
+
+---
+[mandatory]
+	dcache_lock is gone, replaced by fine grained locks. See fs/dcache.c
+for details of what locks to replace dcache_lock with in order to protect
+particular things. Most of the time, a filesystem only needs ->d_lock, which
+protects *all* the dcache state of a given dentry.
+
+--
+[mandatory]
+
+	Filesystems must RCU-free their inodes, if they can have been accessed
+via rcu-walk path walk (basically, if the file can have had a path name in the
+vfs namespace).
+
+	i_dentry and i_rcu share storage in a union, and the vfs expects
+i_dentry to be reinitialized before it is freed, so an:
+
+  INIT_LIST_HEAD(&inode->i_dentry);
+
+must be done in the RCU callback.
+
+--
+[recommended]
+	vfs now tries to do path walking in "rcu-walk mode", which avoids
+atomic operations and scalability hazards on dentries and inodes (see
+Documentation/filesystems/path-walk.txt). d_hash and d_compare changes (above)
+are examples of the changes required to support this. For more complex
+filesystem callbacks, the vfs drops out of rcu-walk mode before the fs call, so
+no changes are required to the filesystem. However, this is costly and loses
+the benefits of rcu-walk mode. We will begin to add filesystem callbacks that
+are rcu-walk aware, shown below. Filesystems should take advantage of this
+where possible.
+
+--
+[mandatory]
+	d_revalidate is a callback that is made on every path element (if
+the filesystem provides it), which requires dropping out of rcu-walk mode. This
+may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be
+returned if the filesystem cannot handle rcu-walk. See
+Documentation/filesystems/vfs.txt for more details.
+
+	permission and check_acl are inode permission checks that are called
+on many or all directory inodes on the way down a path walk (to check for
+exec permission). These must now be rcu-walk aware (flags & IPERM_RCU). See
+Documentation/filesystems/vfs.txt for more details.

+ 57 - 17
Documentation/filesystems/vfs.txt

@@ -325,7 +325,8 @@ struct inode_operations {
         void * (*follow_link) (struct dentry *, struct nameidata *);
         void * (*follow_link) (struct dentry *, struct nameidata *);
         void (*put_link) (struct dentry *, struct nameidata *, void *);
         void (*put_link) (struct dentry *, struct nameidata *, void *);
 	void (*truncate) (struct inode *);
 	void (*truncate) (struct inode *);
-	int (*permission) (struct inode *, int, struct nameidata *);
+	int (*permission) (struct inode *, int, unsigned int);
+	int (*check_acl)(struct inode *, int, unsigned int);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -414,6 +415,13 @@ otherwise noted.
   permission: called by the VFS to check for access rights on a POSIX-like
   permission: called by the VFS to check for access rights on a POSIX-like
   	filesystem.
   	filesystem.
 
 
+	May be called in rcu-walk mode (flags & IPERM_RCU). If in rcu-walk
+	mode, the filesystem must check the permission without blocking or
+	storing to the inode.
+
+	If a situation is encountered that rcu-walk cannot handle, return
+	-ECHILD and it will be called again in ref-walk mode.
+
   setattr: called by the VFS to set attributes for a file. This method
   setattr: called by the VFS to set attributes for a file. This method
   	is called by chmod(2) and related system calls.
   	is called by chmod(2) and related system calls.
 
 
@@ -847,9 +855,12 @@ defined:
 
 
 struct dentry_operations {
 struct dentry_operations {
 	int (*d_revalidate)(struct dentry *, struct nameidata *);
 	int (*d_revalidate)(struct dentry *, struct nameidata *);
-	int (*d_hash) (struct dentry *, struct qstr *);
-	int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
-	int (*d_delete)(struct dentry *);
+	int (*d_hash)(const struct dentry *, const struct inode *,
+			struct qstr *);
+	int (*d_compare)(const struct dentry *, const struct inode *,
+			const struct dentry *, const struct inode *,
+			unsigned int, const char *, const struct qstr *);
+	int (*d_delete)(const struct dentry *);
 	void (*d_release)(struct dentry *);
 	void (*d_release)(struct dentry *);
 	void (*d_iput)(struct dentry *, struct inode *);
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)(struct dentry *, char *, int);
 	char *(*d_dname)(struct dentry *, char *, int);
@@ -860,13 +871,45 @@ struct dentry_operations {
 	dcache. Most filesystems leave this as NULL, because all their
 	dcache. Most filesystems leave this as NULL, because all their
 	dentries in the dcache are valid
 	dentries in the dcache are valid
 
 
-  d_hash: called when the VFS adds a dentry to the hash table
+	d_revalidate may be called in rcu-walk mode (nd->flags & LOOKUP_RCU).
+	If in rcu-walk mode, the filesystem must revalidate the dentry without
+	blocking or storing to the dentry, d_parent and d_inode should not be
+	used without care (because they can go NULL), instead nd->inode should
+	be used.
+
+	If a situation is encountered that rcu-walk cannot handle, return
+	-ECHILD and it will be called again in ref-walk mode.
+
+  d_hash: called when the VFS adds a dentry to the hash table. The first
+	dentry passed to d_hash is the parent directory that the name is
+	to be hashed into. The inode is the dentry's inode.
+
+	Same locking and synchronisation rules as d_compare regarding
+	what is safe to dereference etc.
+
+  d_compare: called to compare a dentry name with a given name. The first
+	dentry is the parent of the dentry to be compared, the second is
+	the parent's inode, then the dentry and inode (may be NULL) of the
+	child dentry. len and name string are properties of the dentry to be
+	compared. qstr is the name to compare it with.
+
+	Must be constant and idempotent, and should not take locks if
+	possible, and should not or store into the dentry or inodes.
+	Should not dereference pointers outside the dentry or inodes without
+	lots of care (eg.  d_parent, d_inode, d_name should not be used).
+
+	However, our vfsmount is pinned, and RCU held, so the dentries and
+	inodes won't disappear, neither will our sb or filesystem module.
+	->i_sb and ->d_sb may be used.
 
 
-  d_compare: called when a dentry should be compared with another
+	It is a tricky calling convention because it needs to be called under
+	"rcu-walk", ie. without any locks or references on things.
 
 
-  d_delete: called when the last reference to a dentry is
-	deleted. This means no-one is using the dentry, however it is
-	still valid and in the dcache
+  d_delete: called when the last reference to a dentry is dropped and the
+	dcache is deciding whether or not to cache it. Return 1 to delete
+	immediately, or 0 to cache the dentry. Default is NULL which means to
+	always cache a reachable dentry. d_delete must be constant and
+	idempotent.
 
 
   d_release: called when a dentry is really deallocated
   d_release: called when a dentry is really deallocated
 
 
@@ -910,14 +953,11 @@ manipulate dentries:
 	the usage count)
 	the usage count)
 
 
   dput: close a handle for a dentry (decrements the usage count). If
   dput: close a handle for a dentry (decrements the usage count). If
-	the usage count drops to 0, the "d_delete" method is called
-	and the dentry is placed on the unused list if the dentry is
-	still in its parents hash list. Putting the dentry on the
-	unused list just means that if the system needs some RAM, it
-	goes through the unused list of dentries and deallocates them.
-	If the dentry has already been unhashed and the usage count
-	drops to 0, in this case the dentry is deallocated after the
-	"d_delete" method is called
+	the usage count drops to 0, and the dentry is still in its
+	parent's hash, the "d_delete" method is called to check whether
+	it should be cached. If it should not be cached, or if the dentry
+	is not hashed, it is deleted. Otherwise cached dentries are put
+	into an LRU list to be reclaimed on memory shortage.
 
 
   d_drop: this unhashes a dentry from its parents hash list. A
   d_drop: this unhashes a dentry from its parents hash list. A
 	subsequent call to dput() will deallocate the dentry if its
 	subsequent call to dput() will deallocate the dentry if its

+ 3 - 3
arch/ia64/kernel/perfmon.c

@@ -1542,7 +1542,7 @@ pfm_exit_smpl_buffer(pfm_buffer_fmt_t *fmt)
  * any operations on the root directory. However, we need a non-trivial
  * any operations on the root directory. However, we need a non-trivial
  * d_name - pfm: will go nicely and kill the special-casing in procfs.
  * d_name - pfm: will go nicely and kill the special-casing in procfs.
  */
  */
-static struct vfsmount *pfmfs_mnt;
+static struct vfsmount *pfmfs_mnt __read_mostly;
 
 
 static int __init
 static int __init
 init_pfm_fs(void)
 init_pfm_fs(void)
@@ -2185,7 +2185,7 @@ static const struct file_operations pfm_file_ops = {
 };
 };
 
 
 static int
 static int
-pfmfs_delete_dentry(struct dentry *dentry)
+pfmfs_delete_dentry(const struct dentry *dentry)
 {
 {
 	return 1;
 	return 1;
 }
 }
@@ -2233,7 +2233,7 @@ pfm_alloc_file(pfm_context_t *ctx)
 	}
 	}
 	path.mnt = mntget(pfmfs_mnt);
 	path.mnt = mntget(pfmfs_mnt);
 
 
-	path.dentry->d_op = &pfmfs_dentry_operations;
+	d_set_d_op(path.dentry, &pfmfs_dentry_operations);
 	d_add(path.dentry, inode);
 	d_add(path.dentry, inode);
 
 
 	file = alloc_file(&path, FMODE_READ, &pfm_file_ops);
 	file = alloc_file(&path, FMODE_READ, &pfm_file_ops);

+ 12 - 6
arch/powerpc/platforms/cell/spufs/inode.c

@@ -71,12 +71,18 @@ spufs_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void
-spufs_destroy_inode(struct inode *inode)
+static void spufs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
 	kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
 }
 }
 
 
+static void spufs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, spufs_i_callback);
+}
+
 static void
 static void
 spufs_init_once(void *p)
 spufs_init_once(void *p)
 {
 {
@@ -159,18 +165,18 @@ static void spufs_prune_dir(struct dentry *dir)
 
 
 	mutex_lock(&dir->d_inode->i_mutex);
 	mutex_lock(&dir->d_inode->i_mutex);
 	list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
 	list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
-		spin_lock(&dcache_lock);
 		spin_lock(&dentry->d_lock);
 		spin_lock(&dentry->d_lock);
 		if (!(d_unhashed(dentry)) && dentry->d_inode) {
 		if (!(d_unhashed(dentry)) && dentry->d_inode) {
-			dget_locked(dentry);
+			dget_dlock(dentry);
 			__d_drop(dentry);
 			__d_drop(dentry);
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
 			simple_unlink(dir->d_inode, dentry);
 			simple_unlink(dir->d_inode, dentry);
-			spin_unlock(&dcache_lock);
+			/* XXX: what was dcache_lock protecting here? Other
+			 * filesystems (IB, configfs) release dcache_lock
+			 * before unlink */
 			dput(dentry);
 			dput(dentry);
 		} else {
 		} else {
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
-			spin_unlock(&dcache_lock);
 		}
 		}
 	}
 	}
 	shrink_dcache_parent(dir);
 	shrink_dcache_parent(dir);

+ 2 - 6
drivers/infiniband/hw/ipath/ipath_fs.c

@@ -277,18 +277,14 @@ static int remove_file(struct dentry *parent, char *name)
 		goto bail;
 		goto bail;
 	}
 	}
 
 
-	spin_lock(&dcache_lock);
 	spin_lock(&tmp->d_lock);
 	spin_lock(&tmp->d_lock);
 	if (!(d_unhashed(tmp) && tmp->d_inode)) {
 	if (!(d_unhashed(tmp) && tmp->d_inode)) {
-		dget_locked(tmp);
+		dget_dlock(tmp);
 		__d_drop(tmp);
 		__d_drop(tmp);
 		spin_unlock(&tmp->d_lock);
 		spin_unlock(&tmp->d_lock);
-		spin_unlock(&dcache_lock);
 		simple_unlink(parent->d_inode, tmp);
 		simple_unlink(parent->d_inode, tmp);
-	} else {
+	} else
 		spin_unlock(&tmp->d_lock);
 		spin_unlock(&tmp->d_lock);
-		spin_unlock(&dcache_lock);
-	}
 
 
 	ret = 0;
 	ret = 0;
 bail:
 bail:

+ 1 - 4
drivers/infiniband/hw/qib/qib_fs.c

@@ -453,17 +453,14 @@ static int remove_file(struct dentry *parent, char *name)
 		goto bail;
 		goto bail;
 	}
 	}
 
 
-	spin_lock(&dcache_lock);
 	spin_lock(&tmp->d_lock);
 	spin_lock(&tmp->d_lock);
 	if (!(d_unhashed(tmp) && tmp->d_inode)) {
 	if (!(d_unhashed(tmp) && tmp->d_inode)) {
-		dget_locked(tmp);
+		dget_dlock(tmp);
 		__d_drop(tmp);
 		__d_drop(tmp);
 		spin_unlock(&tmp->d_lock);
 		spin_unlock(&tmp->d_lock);
-		spin_unlock(&dcache_lock);
 		simple_unlink(parent->d_inode, tmp);
 		simple_unlink(parent->d_inode, tmp);
 	} else {
 	} else {
 		spin_unlock(&tmp->d_lock);
 		spin_unlock(&tmp->d_lock);
-		spin_unlock(&dcache_lock);
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;

+ 1 - 1
drivers/mtd/mtdchar.c

@@ -1201,7 +1201,7 @@ err_unregister_chdev:
 static void __exit cleanup_mtdchar(void)
 static void __exit cleanup_mtdchar(void)
 {
 {
 	unregister_mtd_user(&mtdchar_notifier);
 	unregister_mtd_user(&mtdchar_notifier);
-	mntput(mtd_inode_mnt);
+	mntput_long(mtd_inode_mnt);
 	unregister_filesystem(&mtd_inodefs_type);
 	unregister_filesystem(&mtd_inodefs_type);
 	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
 	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
 }
 }

+ 5 - 2
drivers/staging/autofs/root.c

@@ -154,13 +154,16 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str
  * yet completely filled in, and revalidate has to delay such
  * yet completely filled in, and revalidate has to delay such
  * lookups..
  * lookups..
  */
  */
-static int autofs_revalidate(struct dentry * dentry, struct nameidata *nd)
+static int autofs_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
 {
 	struct inode * dir;
 	struct inode * dir;
 	struct autofs_sb_info *sbi;
 	struct autofs_sb_info *sbi;
 	struct autofs_dir_ent *ent;
 	struct autofs_dir_ent *ent;
 	int res;
 	int res;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
 	lock_kernel();
 	lock_kernel();
 	dir = dentry->d_parent->d_inode;
 	dir = dentry->d_parent->d_inode;
 	sbi = autofs_sbi(dir->i_sb);
 	sbi = autofs_sbi(dir->i_sb);
@@ -237,7 +240,7 @@ static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentr
 	 *
 	 *
 	 * We need to do this before we release the directory semaphore.
 	 * We need to do this before we release the directory semaphore.
 	 */
 	 */
-	dentry->d_op = &autofs_dentry_operations;
+	d_set_d_op(dentry, &autofs_dentry_operations);
 	dentry->d_flags |= DCACHE_AUTOFS_PENDING;
 	dentry->d_flags |= DCACHE_AUTOFS_PENDING;
 	d_add(dentry, NULL);
 	d_add(dentry, NULL);
 
 

+ 8 - 1
drivers/staging/pohmelfs/inode.c

@@ -826,6 +826,13 @@ const struct address_space_operations pohmelfs_aops = {
 	.set_page_dirty 	= __set_page_dirty_nobuffers,
 	.set_page_dirty 	= __set_page_dirty_nobuffers,
 };
 };
 
 
+static void pohmelfs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(pohmelfs_inode_cache, POHMELFS_I(inode));
+}
+
 /*
 /*
  * ->detroy_inode() callback. Deletes inode from the caches
  * ->detroy_inode() callback. Deletes inode from the caches
  *  and frees private data.
  *  and frees private data.
@@ -842,8 +849,8 @@ static void pohmelfs_destroy_inode(struct inode *inode)
 
 
 	dprintk("%s: pi: %p, inode: %p, ino: %llu.\n",
 	dprintk("%s: pi: %p, inode: %p, ino: %llu.\n",
 		__func__, pi, &pi->vfs_inode, pi->ino);
 		__func__, pi, &pi->vfs_inode, pi->ino);
-	kmem_cache_free(pohmelfs_inode_cache, pi);
 	atomic_long_dec(&psb->total_inodes);
 	atomic_long_dec(&psb->total_inodes);
+	call_rcu(&inode->i_rcu, pohmelfs_i_callback);
 }
 }
 
 
 /*
 /*

+ 12 - 5
drivers/staging/pohmelfs/path_entry.c

@@ -83,10 +83,11 @@ out:
 int pohmelfs_path_length(struct pohmelfs_inode *pi)
 int pohmelfs_path_length(struct pohmelfs_inode *pi)
 {
 {
 	struct dentry *d, *root, *first;
 	struct dentry *d, *root, *first;
-	int len = 1; /* Root slash */
+	int len;
+	unsigned seq;
 
 
-	first = d = d_find_alias(&pi->vfs_inode);
-	if (!d) {
+	first = d_find_alias(&pi->vfs_inode);
+	if (!first) {
 		dprintk("%s: ino: %llu, mode: %o.\n", __func__, pi->ino, pi->vfs_inode.i_mode);
 		dprintk("%s: ino: %llu, mode: %o.\n", __func__, pi->ino, pi->vfs_inode.i_mode);
 		return -ENOENT;
 		return -ENOENT;
 	}
 	}
@@ -95,7 +96,11 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
 	root = dget(current->fs->root.dentry);
 	root = dget(current->fs->root.dentry);
 	spin_unlock(&current->fs->lock);
 	spin_unlock(&current->fs->lock);
 
 
-	spin_lock(&dcache_lock);
+rename_retry:
+	len = 1; /* Root slash */
+	d = first;
+	seq = read_seqbegin(&rename_lock);
+	rcu_read_lock();
 
 
 	if (!IS_ROOT(d) && d_unhashed(d))
 	if (!IS_ROOT(d) && d_unhashed(d))
 		len += UNHASHED_OBSCURE_STRING_SIZE; /* Obscure " (deleted)" string */
 		len += UNHASHED_OBSCURE_STRING_SIZE; /* Obscure " (deleted)" string */
@@ -104,7 +109,9 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
 		len += d->d_name.len + 1; /* Plus slash */
 		len += d->d_name.len + 1; /* Plus slash */
 		d = d->d_parent;
 		d = d->d_parent;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	rcu_read_unlock();
+	if (read_seqretry(&rename_lock, seq))
+		goto rename_retry;
 
 
 	dput(root);
 	dput(root);
 	dput(first);
 	dput(first);

+ 8 - 8
drivers/staging/smbfs/cache.c

@@ -62,7 +62,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
 	struct list_head *next;
 	struct list_head *next;
 	struct dentry *dentry;
 	struct dentry *dentry;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&parent->d_lock);
 	next = parent->d_subdirs.next;
 	next = parent->d_subdirs.next;
 	while (next != &parent->d_subdirs) {
 	while (next != &parent->d_subdirs) {
 		dentry = list_entry(next, struct dentry, d_u.d_child);
 		dentry = list_entry(next, struct dentry, d_u.d_child);
@@ -70,7 +70,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
 		smb_age_dentry(server, dentry);
 		smb_age_dentry(server, dentry);
 		next = next->next;
 		next = next->next;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&parent->d_lock);
 }
 }
 
 
 /*
 /*
@@ -96,13 +96,13 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
 	}
 	}
 
 
 	/* If a pointer is invalid, we search the dentry. */
 	/* If a pointer is invalid, we search the dentry. */
-	spin_lock(&dcache_lock);
+	spin_lock(&parent->d_lock);
 	next = parent->d_subdirs.next;
 	next = parent->d_subdirs.next;
 	while (next != &parent->d_subdirs) {
 	while (next != &parent->d_subdirs) {
 		dent = list_entry(next, struct dentry, d_u.d_child);
 		dent = list_entry(next, struct dentry, d_u.d_child);
 		if ((unsigned long)dent->d_fsdata == fpos) {
 		if ((unsigned long)dent->d_fsdata == fpos) {
 			if (dent->d_inode)
 			if (dent->d_inode)
-				dget_locked(dent);
+				dget(dent);
 			else
 			else
 				dent = NULL;
 				dent = NULL;
 			goto out_unlock;
 			goto out_unlock;
@@ -111,7 +111,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
 	}
 	}
 	dent = NULL;
 	dent = NULL;
 out_unlock:
 out_unlock:
-	spin_unlock(&dcache_lock);
+	spin_unlock(&parent->d_lock);
 	return dent;
 	return dent;
 }
 }
 
 
@@ -134,7 +134,7 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
 	qname->hash = full_name_hash(qname->name, qname->len);
 	qname->hash = full_name_hash(qname->name, qname->len);
 
 
 	if (dentry->d_op && dentry->d_op->d_hash)
 	if (dentry->d_op && dentry->d_op->d_hash)
-		if (dentry->d_op->d_hash(dentry, qname) != 0)
+		if (dentry->d_op->d_hash(dentry, inode, qname) != 0)
 			goto end_advance;
 			goto end_advance;
 
 
 	newdent = d_lookup(dentry, qname);
 	newdent = d_lookup(dentry, qname);
@@ -145,8 +145,8 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
 			goto end_advance;
 			goto end_advance;
 	} else {
 	} else {
 		hashed = 1;
 		hashed = 1;
-		memcpy((char *) newdent->d_name.name, qname->name,
-		       newdent->d_name.len);
+		/* dir i_mutex is locked because we're in readdir */
+		dentry_update_name_case(newdent, qname);
 	}
 	}
 
 
 	if (!newdent->d_inode) {
 	if (!newdent->d_inode) {

+ 33 - 17
drivers/staging/smbfs/dir.c

@@ -14,6 +14,7 @@
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 #include <linux/net.h>
 #include <linux/net.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
+#include <linux/namei.h>
 
 
 #include "smb_fs.h"
 #include "smb_fs.h"
 #include "smb_mount.h"
 #include "smb_mount.h"
@@ -274,9 +275,13 @@ smb_dir_open(struct inode *dir, struct file *file)
  * Dentry operations routines
  * Dentry operations routines
  */
  */
 static int smb_lookup_validate(struct dentry *, struct nameidata *);
 static int smb_lookup_validate(struct dentry *, struct nameidata *);
-static int smb_hash_dentry(struct dentry *, struct qstr *);
-static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-static int smb_delete_dentry(struct dentry *);
+static int smb_hash_dentry(const struct dentry *, const struct inode *,
+		struct qstr *);
+static int smb_compare_dentry(const struct dentry *,
+		const struct inode *,
+		const struct dentry *, const struct inode *,
+		unsigned int, const char *, const struct qstr *);
+static int smb_delete_dentry(const struct dentry *);
 
 
 static const struct dentry_operations smbfs_dentry_operations =
 static const struct dentry_operations smbfs_dentry_operations =
 {
 {
@@ -297,13 +302,20 @@ static const struct dentry_operations smbfs_dentry_operations_case =
  * This is the callback when the dcache has a lookup hit.
  * This is the callback when the dcache has a lookup hit.
  */
  */
 static int
 static int
-smb_lookup_validate(struct dentry * dentry, struct nameidata *nd)
+smb_lookup_validate(struct dentry *dentry, struct nameidata *nd)
 {
 {
-	struct smb_sb_info *server = server_from_dentry(dentry);
-	struct inode * inode = dentry->d_inode;
-	unsigned long age = jiffies - dentry->d_time;
+	struct smb_sb_info *server;
+	struct inode *inode;
+	unsigned long age;
 	int valid;
 	int valid;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	server = server_from_dentry(dentry);
+	inode = dentry->d_inode;
+	age = jiffies - dentry->d_time;
+
 	/*
 	/*
 	 * The default validation is based on dentry age:
 	 * The default validation is based on dentry age:
 	 * we believe in dentries for a few seconds.  (But each
 	 * we believe in dentries for a few seconds.  (But each
@@ -333,7 +345,8 @@ smb_lookup_validate(struct dentry * dentry, struct nameidata *nd)
 }
 }
 
 
 static int 
 static int 
-smb_hash_dentry(struct dentry *dir, struct qstr *this)
+smb_hash_dentry(const struct dentry *dir, const struct inode *inode,
+		struct qstr *this)
 {
 {
 	unsigned long hash;
 	unsigned long hash;
 	int i;
 	int i;
@@ -347,14 +360,17 @@ smb_hash_dentry(struct dentry *dir, struct qstr *this)
 }
 }
 
 
 static int
 static int
-smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b)
+smb_compare_dentry(const struct dentry *parent,
+		const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
 	int i, result = 1;
 	int i, result = 1;
 
 
-	if (a->len != b->len)
+	if (len != name->len)
 		goto out;
 		goto out;
-	for (i=0; i < a->len; i++) {
-		if (tolower(a->name[i]) != tolower(b->name[i]))
+	for (i=0; i < len; i++) {
+		if (tolower(str[i]) != tolower(name->name[i]))
 			goto out;
 			goto out;
 	}
 	}
 	result = 0;
 	result = 0;
@@ -367,7 +383,7 @@ out:
  * We use this to unhash dentries with bad inodes.
  * We use this to unhash dentries with bad inodes.
  */
  */
 static int
 static int
-smb_delete_dentry(struct dentry * dentry)
+smb_delete_dentry(const struct dentry *dentry)
 {
 {
 	if (dentry->d_inode) {
 	if (dentry->d_inode) {
 		if (is_bad_inode(dentry->d_inode)) {
 		if (is_bad_inode(dentry->d_inode)) {
@@ -390,9 +406,9 @@ smb_new_dentry(struct dentry *dentry)
 	struct smb_sb_info *server = server_from_dentry(dentry);
 	struct smb_sb_info *server = server_from_dentry(dentry);
 
 
 	if (server->mnt->flags & SMB_MOUNT_CASE)
 	if (server->mnt->flags & SMB_MOUNT_CASE)
-		dentry->d_op = &smbfs_dentry_operations_case;
+		d_set_d_op(dentry, &smbfs_dentry_operations_case);
 	else
 	else
-		dentry->d_op = &smbfs_dentry_operations;
+		d_set_d_op(dentry, &smbfs_dentry_operations);
 	dentry->d_time = jiffies;
 	dentry->d_time = jiffies;
 }
 }
 
 
@@ -454,9 +470,9 @@ smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 	add_entry:
 	add_entry:
 			server = server_from_dentry(dentry);
 			server = server_from_dentry(dentry);
 			if (server->mnt->flags & SMB_MOUNT_CASE)
 			if (server->mnt->flags & SMB_MOUNT_CASE)
-				dentry->d_op = &smbfs_dentry_operations_case;
+				d_set_d_op(dentry, &smbfs_dentry_operations_case);
 			else
 			else
-				dentry->d_op = &smbfs_dentry_operations;
+				d_set_d_op(dentry, &smbfs_dentry_operations);
 
 
 			d_add(dentry, inode);
 			d_add(dentry, inode);
 			smb_renew_times(dentry);
 			smb_renew_times(dentry);

+ 4 - 1
drivers/staging/smbfs/file.c

@@ -407,11 +407,14 @@ smb_file_release(struct inode *inode, struct file * file)
  * privileges, so we need our own check for this.
  * privileges, so we need our own check for this.
  */
  */
 static int
 static int
-smb_file_permission(struct inode *inode, int mask)
+smb_file_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	int mode = inode->i_mode;
 	int mode = inode->i_mode;
 	int error = 0;
 	int error = 0;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	VERBOSE("mode=%x, mask=%x\n", mode, mask);
 	VERBOSE("mode=%x, mask=%x\n", mode, mask);
 
 
 	/* Look at user permissions */
 	/* Look at user permissions */

+ 8 - 1
drivers/staging/smbfs/inode.c

@@ -62,11 +62,18 @@ static struct inode *smb_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void smb_destroy_inode(struct inode *inode)
+static void smb_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(smb_inode_cachep, SMB_I(inode));
 	kmem_cache_free(smb_inode_cachep, SMB_I(inode));
 }
 }
 
 
+static void smb_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, smb_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct smb_inode_info *ei = (struct smb_inode_info *) foo;
 	struct smb_inode_info *ei = (struct smb_inode_info *) foo;

+ 7 - 5
drivers/usb/core/inode.c

@@ -343,17 +343,19 @@ static int usbfs_empty (struct dentry *dentry)
 {
 {
 	struct list_head *list;
 	struct list_head *list;
 
 
-	spin_lock(&dcache_lock);
-
+	spin_lock(&dentry->d_lock);
 	list_for_each(list, &dentry->d_subdirs) {
 	list_for_each(list, &dentry->d_subdirs) {
 		struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
 		struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
+
+		spin_lock_nested(&de->d_lock, DENTRY_D_LOCK_NESTED);
 		if (usbfs_positive(de)) {
 		if (usbfs_positive(de)) {
-			spin_unlock(&dcache_lock);
+			spin_unlock(&de->d_lock);
+			spin_unlock(&dentry->d_lock);
 			return 0;
 			return 0;
 		}
 		}
+		spin_unlock(&de->d_lock);
 	}
 	}
-
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dentry->d_lock);
 	return 1;
 	return 1;
 }
 }
 
 

+ 4 - 1
fs/9p/acl.c

@@ -91,11 +91,14 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
 	return acl;
 	return acl;
 }
 }
 
 
-int v9fs_check_acl(struct inode *inode, int mask)
+int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	struct posix_acl *acl;
 	struct posix_acl *acl;
 	struct v9fs_session_info *v9ses;
 	struct v9fs_session_info *v9ses;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	v9ses = v9fs_inode2v9ses(inode);
 	v9ses = v9fs_inode2v9ses(inode);
 	if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
 	if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
 		/*
 		/*

+ 1 - 1
fs/9p/acl.h

@@ -16,7 +16,7 @@
 
 
 #ifdef CONFIG_9P_FS_POSIX_ACL
 #ifdef CONFIG_9P_FS_POSIX_ACL
 extern int v9fs_get_acl(struct inode *, struct p9_fid *);
 extern int v9fs_get_acl(struct inode *, struct p9_fid *);
-extern int v9fs_check_acl(struct inode *inode, int mask);
+extern int v9fs_check_acl(struct inode *inode, int mask, unsigned int flags);
 extern int v9fs_acl_chmod(struct dentry *);
 extern int v9fs_acl_chmod(struct dentry *);
 extern int v9fs_set_create_acl(struct dentry *,
 extern int v9fs_set_create_acl(struct dentry *,
 			       struct posix_acl *, struct posix_acl *);
 			       struct posix_acl *, struct posix_acl *);

+ 2 - 2
fs/9p/vfs_dentry.c

@@ -51,7 +51,7 @@
  *
  *
  */
  */
 
 
-static int v9fs_dentry_delete(struct dentry *dentry)
+static int v9fs_dentry_delete(const struct dentry *dentry)
 {
 {
 	P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
 	P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
 									dentry);
 									dentry);
@@ -68,7 +68,7 @@ static int v9fs_dentry_delete(struct dentry *dentry)
  *
  *
  */
  */
 
 
-static int v9fs_cached_dentry_delete(struct dentry *dentry)
+static int v9fs_cached_dentry_delete(const struct dentry *dentry)
 {
 {
 	struct inode *inode = dentry->d_inode;
 	struct inode *inode = dentry->d_inode;
 	P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
 	P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,

+ 23 - 16
fs/9p/vfs_inode.c

@@ -237,10 +237,17 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
  *
  *
  */
  */
 
 
-void v9fs_destroy_inode(struct inode *inode)
+static void v9fs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
 	kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
 }
 }
+
+void v9fs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, v9fs_i_callback);
+}
 #endif
 #endif
 
 
 /**
 /**
@@ -270,11 +277,11 @@ static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
 {
 {
 	struct dentry *dentry;
 	struct dentry *dentry;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&inode->i_lock);
 	/* Directory should have only one entry. */
 	/* Directory should have only one entry. */
 	BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
 	BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
 	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
 	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 	return dentry;
 	return dentry;
 }
 }
 
 
@@ -628,9 +635,9 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
 	}
 	}
 
 
 	if (v9ses->cache)
 	if (v9ses->cache)
-		dentry->d_op = &v9fs_cached_dentry_operations;
+		d_set_d_op(dentry, &v9fs_cached_dentry_operations);
 	else
 	else
-		dentry->d_op = &v9fs_dentry_operations;
+		d_set_d_op(dentry, &v9fs_dentry_operations);
 
 
 	d_instantiate(dentry, inode);
 	d_instantiate(dentry, inode);
 	err = v9fs_fid_add(dentry, fid);
 	err = v9fs_fid_add(dentry, fid);
@@ -742,7 +749,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
 				err);
 				err);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_cached_dentry_operations;
+		d_set_d_op(dentry, &v9fs_cached_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 		err = v9fs_fid_add(dentry, fid);
 		err = v9fs_fid_add(dentry, fid);
 		if (err < 0)
 		if (err < 0)
@@ -760,7 +767,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
 			err = PTR_ERR(inode);
 			err = PTR_ERR(inode);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_dentry_operations;
+		d_set_d_op(dentry, &v9fs_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 	}
 	}
 	/* Now set the ACL based on the default value */
 	/* Now set the ACL based on the default value */
@@ -949,7 +956,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
 				err);
 				err);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_cached_dentry_operations;
+		d_set_d_op(dentry, &v9fs_cached_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 		err = v9fs_fid_add(dentry, fid);
 		err = v9fs_fid_add(dentry, fid);
 		if (err < 0)
 		if (err < 0)
@@ -966,7 +973,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
 			err = PTR_ERR(inode);
 			err = PTR_ERR(inode);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_dentry_operations;
+		d_set_d_op(dentry, &v9fs_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 	}
 	}
 	/* Now set the ACL based on the default value */
 	/* Now set the ACL based on the default value */
@@ -1034,9 +1041,9 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
 
 
 inst_out:
 inst_out:
 	if (v9ses->cache)
 	if (v9ses->cache)
-		dentry->d_op = &v9fs_cached_dentry_operations;
+		d_set_d_op(dentry, &v9fs_cached_dentry_operations);
 	else
 	else
-		dentry->d_op = &v9fs_dentry_operations;
+		d_set_d_op(dentry, &v9fs_dentry_operations);
 
 
 	d_add(dentry, inode);
 	d_add(dentry, inode);
 	return NULL;
 	return NULL;
@@ -1702,7 +1709,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
 					err);
 					err);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_cached_dentry_operations;
+		d_set_d_op(dentry, &v9fs_cached_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 		err = v9fs_fid_add(dentry, fid);
 		err = v9fs_fid_add(dentry, fid);
 		if (err < 0)
 		if (err < 0)
@@ -1715,7 +1722,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
 			err = PTR_ERR(inode);
 			err = PTR_ERR(inode);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_dentry_operations;
+		d_set_d_op(dentry, &v9fs_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 	}
 	}
 
 
@@ -1849,7 +1856,7 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
 		ihold(old_dentry->d_inode);
 		ihold(old_dentry->d_inode);
 	}
 	}
 
 
-	dentry->d_op = old_dentry->d_op;
+	d_set_d_op(dentry, old_dentry->d_op);
 	d_instantiate(dentry, old_dentry->d_inode);
 	d_instantiate(dentry, old_dentry->d_inode);
 
 
 	return err;
 	return err;
@@ -1973,7 +1980,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
 				err);
 				err);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_cached_dentry_operations;
+		d_set_d_op(dentry, &v9fs_cached_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 		err = v9fs_fid_add(dentry, fid);
 		err = v9fs_fid_add(dentry, fid);
 		if (err < 0)
 		if (err < 0)
@@ -1989,7 +1996,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
 			err = PTR_ERR(inode);
 			err = PTR_ERR(inode);
 			goto error;
 			goto error;
 		}
 		}
-		dentry->d_op = &v9fs_dentry_operations;
+		d_set_d_op(dentry, &v9fs_dentry_operations);
 		d_instantiate(dentry, inode);
 		d_instantiate(dentry, inode);
 	}
 	}
 	/* Now set the ACL based on the default value */
 	/* Now set the ACL based on the default value */

+ 8 - 5
fs/adfs/dir.c

@@ -201,7 +201,8 @@ const struct file_operations adfs_dir_operations = {
 };
 };
 
 
 static int
 static int
-adfs_hash(struct dentry *parent, struct qstr *qstr)
+adfs_hash(const struct dentry *parent, const struct inode *inode,
+		struct qstr *qstr)
 {
 {
 	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
 	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
 	const unsigned char *name;
 	const unsigned char *name;
@@ -237,17 +238,19 @@ adfs_hash(struct dentry *parent, struct qstr *qstr)
  * requirements of the underlying filesystem.
  * requirements of the underlying filesystem.
  */
  */
 static int
 static int
-adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
+adfs_compare(const struct dentry *parent, const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
 	int i;
 	int i;
 
 
-	if (entry->len != name->len)
+	if (len != name->len)
 		return 1;
 		return 1;
 
 
 	for (i = 0; i < name->len; i++) {
 	for (i = 0; i < name->len; i++) {
 		char a, b;
 		char a, b;
 
 
-		a = entry->name[i];
+		a = str[i];
 		b = name->name[i];
 		b = name->name[i];
 
 
 		if (a >= 'A' && a <= 'Z')
 		if (a >= 'A' && a <= 'Z')
@@ -273,7 +276,7 @@ adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 	struct object_info obj;
 	struct object_info obj;
 	int error;
 	int error;
 
 
-	dentry->d_op = &adfs_dentry_operations;	
+	d_set_d_op(dentry, &adfs_dentry_operations);
 	lock_kernel();
 	lock_kernel();
 	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
 	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
 	if (error == 0) {
 	if (error == 0) {

+ 9 - 2
fs/adfs/super.c

@@ -240,11 +240,18 @@ static struct inode *adfs_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void adfs_destroy_inode(struct inode *inode)
+static void adfs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
 	kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
 }
 }
 
 
+static void adfs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, adfs_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct adfs_inode_info *ei = (struct adfs_inode_info *) foo;
 	struct adfs_inode_info *ei = (struct adfs_inode_info *) foo;
@@ -477,7 +484,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 		adfs_error(sb, "get root inode failed\n");
 		adfs_error(sb, "get root inode failed\n");
 		goto error;
 		goto error;
 	} else
 	} else
-		sb->s_root->d_op = &adfs_dentry_operations;
+		d_set_d_op(sb->s_root, &adfs_dentry_operations);
 	unlock_kernel();
 	unlock_kernel();
 	return 0;
 	return 0;
 
 

+ 2 - 2
fs/affs/amigaffs.c

@@ -128,7 +128,7 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
 	void *data = dentry->d_fsdata;
 	void *data = dentry->d_fsdata;
 	struct list_head *head, *next;
 	struct list_head *head, *next;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&inode->i_lock);
 	head = &inode->i_dentry;
 	head = &inode->i_dentry;
 	next = head->next;
 	next = head->next;
 	while (next != head) {
 	while (next != head) {
@@ -139,7 +139,7 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
 		}
 		}
 		next = next->next;
 		next = next->next;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 }
 }
 
 
 
 

+ 41 - 27
fs/affs/namei.c

@@ -13,11 +13,19 @@
 typedef int (*toupper_t)(int);
 typedef int (*toupper_t)(int);
 
 
 static int	 affs_toupper(int ch);
 static int	 affs_toupper(int ch);
-static int	 affs_hash_dentry(struct dentry *, struct qstr *);
-static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int	 affs_hash_dentry(const struct dentry *,
+		const struct inode *, struct qstr *);
+static int       affs_compare_dentry(const struct dentry *parent,
+		const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name);
 static int	 affs_intl_toupper(int ch);
 static int	 affs_intl_toupper(int ch);
-static int	 affs_intl_hash_dentry(struct dentry *, struct qstr *);
-static int       affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int	 affs_intl_hash_dentry(const struct dentry *,
+		const struct inode *, struct qstr *);
+static int       affs_intl_compare_dentry(const struct dentry *parent,
+		const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name);
 
 
 const struct dentry_operations affs_dentry_operations = {
 const struct dentry_operations affs_dentry_operations = {
 	.d_hash		= affs_hash_dentry,
 	.d_hash		= affs_hash_dentry,
@@ -58,13 +66,13 @@ affs_get_toupper(struct super_block *sb)
  * Note: the dentry argument is the parent dentry.
  * Note: the dentry argument is the parent dentry.
  */
  */
 static inline int
 static inline int
-__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
+__affs_hash_dentry(struct qstr *qstr, toupper_t toupper)
 {
 {
 	const u8 *name = qstr->name;
 	const u8 *name = qstr->name;
 	unsigned long hash;
 	unsigned long hash;
 	int i;
 	int i;
 
 
-	i = affs_check_name(qstr->name,qstr->len);
+	i = affs_check_name(qstr->name, qstr->len);
 	if (i)
 	if (i)
 		return i;
 		return i;
 
 
@@ -78,39 +86,41 @@ __affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
 }
 }
 
 
 static int
 static int
-affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+affs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *qstr)
 {
 {
-	return __affs_hash_dentry(dentry, qstr, affs_toupper);
+	return __affs_hash_dentry(qstr, affs_toupper);
 }
 }
 static int
 static int
-affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+affs_intl_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *qstr)
 {
 {
-	return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
+	return __affs_hash_dentry(qstr, affs_intl_toupper);
 }
 }
 
 
-static inline int
-__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
+static inline int __affs_compare_dentry(unsigned int len,
+		const char *str, const struct qstr *name, toupper_t toupper)
 {
 {
-	const u8 *aname = a->name;
-	const u8 *bname = b->name;
-	int len;
+	const u8 *aname = str;
+	const u8 *bname = name->name;
 
 
-	/* 'a' is the qstr of an already existing dentry, so the name
-	 * must be valid. 'b' must be validated first.
+	/*
+	 * 'str' is the name of an already existing dentry, so the name
+	 * must be valid. 'name' must be validated first.
 	 */
 	 */
 
 
-	if (affs_check_name(b->name,b->len))
+	if (affs_check_name(name->name, name->len))
 		return 1;
 		return 1;
 
 
-	/* If the names are longer than the allowed 30 chars,
+	/*
+	 * If the names are longer than the allowed 30 chars,
 	 * the excess is ignored, so their length may differ.
 	 * the excess is ignored, so their length may differ.
 	 */
 	 */
-	len = a->len;
 	if (len >= 30) {
 	if (len >= 30) {
-		if (b->len < 30)
+		if (name->len < 30)
 			return 1;
 			return 1;
 		len = 30;
 		len = 30;
-	} else if (len != b->len)
+	} else if (len != name->len)
 		return 1;
 		return 1;
 
 
 	for (; len > 0; len--)
 	for (; len > 0; len--)
@@ -121,14 +131,18 @@ __affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, tou
 }
 }
 
 
 static int
 static int
-affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+affs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
-	return __affs_compare_dentry(dentry, a, b, affs_toupper);
+	return __affs_compare_dentry(len, str, name, affs_toupper);
 }
 }
 static int
 static int
-affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
-	return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
+	return __affs_compare_dentry(len, str, name, affs_intl_toupper);
 }
 }
 
 
 /*
 /*
@@ -226,7 +240,7 @@ affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 		if (IS_ERR(inode))
 		if (IS_ERR(inode))
 			return ERR_CAST(inode);
 			return ERR_CAST(inode);
 	}
 	}
-	dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
+	d_set_d_op(dentry, AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations);
 	d_add(dentry, inode);
 	d_add(dentry, inode);
 	return NULL;
 	return NULL;
 }
 }

+ 9 - 2
fs/affs/super.c

@@ -95,11 +95,18 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
 	return &i->vfs_inode;
 	return &i->vfs_inode;
 }
 }
 
 
-static void affs_destroy_inode(struct inode *inode)
+static void affs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
 	kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
 }
 }
 
 
+static void affs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, affs_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct affs_inode_info *ei = (struct affs_inode_info *) foo;
 	struct affs_inode_info *ei = (struct affs_inode_info *) foo;
@@ -475,7 +482,7 @@ got_root:
 		printk(KERN_ERR "AFFS: Get root inode failed\n");
 		printk(KERN_ERR "AFFS: Get root inode failed\n");
 		goto out_error;
 		goto out_error;
 	}
 	}
-	sb->s_root->d_op = &affs_dentry_operations;
+	d_set_d_op(sb->s_root, &affs_dentry_operations);
 
 
 	pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
 	pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
 	return 0;
 	return 0;

+ 7 - 3
fs/afs/dir.c

@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/pagemap.h>
 #include <linux/pagemap.h>
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 #include <linux/sched.h>
 #include <linux/sched.h>
@@ -23,7 +24,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
 static int afs_dir_open(struct inode *inode, struct file *file);
 static int afs_dir_open(struct inode *inode, struct file *file);
 static int afs_readdir(struct file *file, void *dirent, filldir_t filldir);
 static int afs_readdir(struct file *file, void *dirent, filldir_t filldir);
 static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
 static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
-static int afs_d_delete(struct dentry *dentry);
+static int afs_d_delete(const struct dentry *dentry);
 static void afs_d_release(struct dentry *dentry);
 static void afs_d_release(struct dentry *dentry);
 static int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
 static int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
 				  loff_t fpos, u64 ino, unsigned dtype);
 				  loff_t fpos, u64 ino, unsigned dtype);
@@ -581,7 +582,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
 	}
 	}
 
 
 success:
 success:
-	dentry->d_op = &afs_fs_dentry_operations;
+	d_set_d_op(dentry, &afs_fs_dentry_operations);
 
 
 	d_add(dentry, inode);
 	d_add(dentry, inode);
 	_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%llu }",
 	_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%llu }",
@@ -607,6 +608,9 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 	void *dir_version;
 	void *dir_version;
 	int ret;
 	int ret;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
 	vnode = AFS_FS_I(dentry->d_inode);
 	vnode = AFS_FS_I(dentry->d_inode);
 
 
 	if (dentry->d_inode)
 	if (dentry->d_inode)
@@ -730,7 +734,7 @@ out_bad:
  * - called from dput() when d_count is going to 0.
  * - called from dput() when d_count is going to 0.
  * - return 1 to request dentry be unhashed, 0 otherwise
  * - return 1 to request dentry be unhashed, 0 otherwise
  */
  */
-static int afs_d_delete(struct dentry *dentry)
+static int afs_d_delete(const struct dentry *dentry)
 {
 {
 	_enter("%s", dentry->d_name.name);
 	_enter("%s", dentry->d_name.name);
 
 

+ 1 - 1
fs/afs/internal.h

@@ -624,7 +624,7 @@ extern void afs_clear_permits(struct afs_vnode *);
 extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
 extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
 extern void afs_zap_permits(struct rcu_head *);
 extern void afs_zap_permits(struct rcu_head *);
 extern struct key *afs_request_key(struct afs_cell *);
 extern struct key *afs_request_key(struct afs_cell *);
-extern int afs_permission(struct inode *, int);
+extern int afs_permission(struct inode *, int, unsigned int);
 
 
 /*
 /*
  * server.c
  * server.c

+ 5 - 2
fs/afs/security.c

@@ -285,13 +285,16 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
  * - AFS ACLs are attached to directories only, and a file is controlled by its
  * - AFS ACLs are attached to directories only, and a file is controlled by its
  *   parent directory's ACL
  *   parent directory's ACL
  */
  */
-int afs_permission(struct inode *inode, int mask)
+int afs_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	struct afs_vnode *vnode = AFS_FS_I(inode);
 	struct afs_vnode *vnode = AFS_FS_I(inode);
 	afs_access_t uninitialized_var(access);
 	afs_access_t uninitialized_var(access);
 	struct key *key;
 	struct key *key;
 	int ret;
 	int ret;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	_enter("{{%x:%u},%lx},%x,",
 	_enter("{{%x:%u},%lx},%x,",
 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 
 
@@ -347,7 +350,7 @@ int afs_permission(struct inode *inode, int mask)
 	}
 	}
 
 
 	key_put(key);
 	key_put(key);
-	ret = generic_permission(inode, mask, NULL);
+	ret = generic_permission(inode, mask, flags, NULL);
 	_leave(" = %d", ret);
 	_leave(" = %d", ret);
 	return ret;
 	return ret;
 
 

+ 9 - 1
fs/afs/super.c

@@ -498,6 +498,14 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
 	return &vnode->vfs_inode;
 	return &vnode->vfs_inode;
 }
 }
 
 
+static void afs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(afs_inode_cachep, vnode);
+}
+
 /*
 /*
  * destroy an AFS inode struct
  * destroy an AFS inode struct
  */
  */
@@ -511,7 +519,7 @@ static void afs_destroy_inode(struct inode *inode)
 
 
 	ASSERTCMP(vnode->server, ==, NULL);
 	ASSERTCMP(vnode->server, ==, NULL);
 
 
-	kmem_cache_free(afs_inode_cachep, vnode);
+	call_rcu(&inode->i_rcu, afs_i_callback);
 	atomic_dec(&afs_count_active_inodes);
 	atomic_dec(&afs_count_active_inodes);
 }
 }
 
 

+ 3 - 3
fs/anon_inodes.c

@@ -102,7 +102,7 @@ struct file *anon_inode_getfile(const char *name,
 	this.name = name;
 	this.name = name;
 	this.len = strlen(name);
 	this.len = strlen(name);
 	this.hash = 0;
 	this.hash = 0;
-	path.dentry = d_alloc(anon_inode_mnt->mnt_sb->s_root, &this);
+	path.dentry = d_alloc_pseudo(anon_inode_mnt->mnt_sb, &this);
 	if (!path.dentry)
 	if (!path.dentry)
 		goto err_module;
 		goto err_module;
 
 
@@ -113,7 +113,7 @@ struct file *anon_inode_getfile(const char *name,
 	 */
 	 */
 	ihold(anon_inode_inode);
 	ihold(anon_inode_inode);
 
 
-	path.dentry->d_op = &anon_inodefs_dentry_operations;
+	d_set_d_op(path.dentry, &anon_inodefs_dentry_operations);
 	d_instantiate(path.dentry, anon_inode_inode);
 	d_instantiate(path.dentry, anon_inode_inode);
 
 
 	error = -ENFILE;
 	error = -ENFILE;
@@ -232,7 +232,7 @@ static int __init anon_inode_init(void)
 	return 0;
 	return 0;
 
 
 err_mntput:
 err_mntput:
-	mntput(anon_inode_mnt);
+	mntput_long(anon_inode_mnt);
 err_unregister_filesystem:
 err_unregister_filesystem:
 	unregister_filesystem(&anon_inode_fs_type);
 	unregister_filesystem(&anon_inode_fs_type);
 err_exit:
 err_exit:

+ 11 - 10
fs/autofs4/autofs_i.h

@@ -16,6 +16,7 @@
 #include <linux/auto_fs4.h>
 #include <linux/auto_fs4.h>
 #include <linux/auto_dev-ioctl.h>
 #include <linux/auto_dev-ioctl.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/list.h>
 
 
 /* This is the range of ioctl() numbers we claim as ours */
 /* This is the range of ioctl() numbers we claim as ours */
@@ -60,6 +61,8 @@ do {							\
 		current->pid, __func__, ##args);	\
 		current->pid, __func__, ##args);	\
 } while (0)
 } while (0)
 
 
+extern spinlock_t autofs4_lock;
+
 /* Unified info structure.  This is pointed to by both the dentry and
 /* Unified info structure.  This is pointed to by both the dentry and
    inode structures.  Each file in the filesystem has an instance of this
    inode structures.  Each file in the filesystem has an instance of this
    structure.  It holds a reference to the dentry, so dentries are never
    structure.  It holds a reference to the dentry, so dentries are never
@@ -254,17 +257,15 @@ static inline int simple_positive(struct dentry *dentry)
 	return dentry->d_inode && !d_unhashed(dentry);
 	return dentry->d_inode && !d_unhashed(dentry);
 }
 }
 
 
-static inline int __simple_empty(struct dentry *dentry)
+static inline void __autofs4_add_expiring(struct dentry *dentry)
 {
 {
-	struct dentry *child;
-	int ret = 0;
-
-	list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child)
-		if (simple_positive(child))
-			goto out;
-	ret = 1;
-out:
-	return ret;
+	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+	struct autofs_info *ino = autofs4_dentry_ino(dentry);
+	if (ino) {
+		if (list_empty(&ino->expiring))
+			list_add(&ino->expiring, &sbi->expiring_list);
+	}
+	return;
 }
 }
 
 
 static inline void autofs4_add_expiring(struct dentry *dentry)
 static inline void autofs4_add_expiring(struct dentry *dentry)

+ 77 - 64
fs/autofs4/expire.c

@@ -91,24 +91,64 @@ done:
 }
 }
 
 
 /*
 /*
- * Calculate next entry in top down tree traversal.
- * From next_mnt in namespace.c - elegant.
+ * Calculate and dget next entry in top down tree traversal.
  */
  */
-static struct dentry *next_dentry(struct dentry *p, struct dentry *root)
+static struct dentry *get_next_positive_dentry(struct dentry *prev,
+						struct dentry *root)
 {
 {
-	struct list_head *next = p->d_subdirs.next;
+	struct list_head *next;
+	struct dentry *p, *ret;
+
+	if (prev == NULL)
+		return dget(prev);
 
 
+	spin_lock(&autofs4_lock);
+relock:
+	p = prev;
+	spin_lock(&p->d_lock);
+again:
+	next = p->d_subdirs.next;
 	if (next == &p->d_subdirs) {
 	if (next == &p->d_subdirs) {
 		while (1) {
 		while (1) {
-			if (p == root)
+			struct dentry *parent;
+
+			if (p == root) {
+				spin_unlock(&p->d_lock);
+				spin_unlock(&autofs4_lock);
+				dput(prev);
 				return NULL;
 				return NULL;
+			}
+
+			parent = p->d_parent;
+			if (!spin_trylock(&parent->d_lock)) {
+				spin_unlock(&p->d_lock);
+				cpu_relax();
+				goto relock;
+			}
+			spin_unlock(&p->d_lock);
 			next = p->d_u.d_child.next;
 			next = p->d_u.d_child.next;
-			if (next != &p->d_parent->d_subdirs)
+			p = parent;
+			if (next != &parent->d_subdirs)
 				break;
 				break;
-			p = p->d_parent;
 		}
 		}
 	}
 	}
-	return list_entry(next, struct dentry, d_u.d_child);
+	ret = list_entry(next, struct dentry, d_u.d_child);
+
+	spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
+	/* Negative dentry - try next */
+	if (!simple_positive(ret)) {
+		spin_unlock(&ret->d_lock);
+		p = ret;
+		goto again;
+	}
+	dget_dlock(ret);
+	spin_unlock(&ret->d_lock);
+	spin_unlock(&p->d_lock);
+	spin_unlock(&autofs4_lock);
+
+	dput(prev);
+
+	return ret;
 }
 }
 
 
 /*
 /*
@@ -158,18 +198,11 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
 	if (!simple_positive(top))
 	if (!simple_positive(top))
 		return 1;
 		return 1;
 
 
-	spin_lock(&dcache_lock);
-	for (p = top; p; p = next_dentry(p, top)) {
-		/* Negative dentry - give up */
-		if (!simple_positive(p))
-			continue;
-
+	p = NULL;
+	while ((p = get_next_positive_dentry(p, top))) {
 		DPRINTK("dentry %p %.*s",
 		DPRINTK("dentry %p %.*s",
 			p, (int) p->d_name.len, p->d_name.name);
 			p, (int) p->d_name.len, p->d_name.name);
 
 
-		p = dget(p);
-		spin_unlock(&dcache_lock);
-
 		/*
 		/*
 		 * Is someone visiting anywhere in the subtree ?
 		 * Is someone visiting anywhere in the subtree ?
 		 * If there's no mount we need to check the usage
 		 * If there's no mount we need to check the usage
@@ -198,16 +231,13 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
 			else
 			else
 				ino_count++;
 				ino_count++;
 
 
-			if (atomic_read(&p->d_count) > ino_count) {
+			if (p->d_count > ino_count) {
 				top_ino->last_used = jiffies;
 				top_ino->last_used = jiffies;
 				dput(p);
 				dput(p);
 				return 1;
 				return 1;
 			}
 			}
 		}
 		}
-		dput(p);
-		spin_lock(&dcache_lock);
 	}
 	}
-	spin_unlock(&dcache_lock);
 
 
 	/* Timeout of a tree mount is ultimately determined by its top dentry */
 	/* Timeout of a tree mount is ultimately determined by its top dentry */
 	if (!autofs4_can_expire(top, timeout, do_now))
 	if (!autofs4_can_expire(top, timeout, do_now))
@@ -226,32 +256,21 @@ static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
 	DPRINTK("parent %p %.*s",
 	DPRINTK("parent %p %.*s",
 		parent, (int)parent->d_name.len, parent->d_name.name);
 		parent, (int)parent->d_name.len, parent->d_name.name);
 
 
-	spin_lock(&dcache_lock);
-	for (p = parent; p; p = next_dentry(p, parent)) {
-		/* Negative dentry - give up */
-		if (!simple_positive(p))
-			continue;
-
+	p = NULL;
+	while ((p = get_next_positive_dentry(p, parent))) {
 		DPRINTK("dentry %p %.*s",
 		DPRINTK("dentry %p %.*s",
 			p, (int) p->d_name.len, p->d_name.name);
 			p, (int) p->d_name.len, p->d_name.name);
 
 
-		p = dget(p);
-		spin_unlock(&dcache_lock);
-
 		if (d_mountpoint(p)) {
 		if (d_mountpoint(p)) {
 			/* Can we umount this guy */
 			/* Can we umount this guy */
 			if (autofs4_mount_busy(mnt, p))
 			if (autofs4_mount_busy(mnt, p))
-				goto cont;
+				continue;
 
 
 			/* Can we expire this guy */
 			/* Can we expire this guy */
 			if (autofs4_can_expire(p, timeout, do_now))
 			if (autofs4_can_expire(p, timeout, do_now))
 				return p;
 				return p;
 		}
 		}
-cont:
-		dput(p);
-		spin_lock(&dcache_lock);
 	}
 	}
-	spin_unlock(&dcache_lock);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -276,7 +295,9 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
 		struct autofs_info *ino = autofs4_dentry_ino(root);
 		struct autofs_info *ino = autofs4_dentry_ino(root);
 		if (d_mountpoint(root)) {
 		if (d_mountpoint(root)) {
 			ino->flags |= AUTOFS_INF_MOUNTPOINT;
 			ino->flags |= AUTOFS_INF_MOUNTPOINT;
-			root->d_mounted--;
+			spin_lock(&root->d_lock);
+			root->d_flags &= ~DCACHE_MOUNTED;
+			spin_unlock(&root->d_lock);
 		}
 		}
 		ino->flags |= AUTOFS_INF_EXPIRING;
 		ino->flags |= AUTOFS_INF_EXPIRING;
 		init_completion(&ino->expire_complete);
 		init_completion(&ino->expire_complete);
@@ -302,8 +323,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 {
 {
 	unsigned long timeout;
 	unsigned long timeout;
 	struct dentry *root = sb->s_root;
 	struct dentry *root = sb->s_root;
+	struct dentry *dentry;
 	struct dentry *expired = NULL;
 	struct dentry *expired = NULL;
-	struct list_head *next;
 	int do_now = how & AUTOFS_EXP_IMMEDIATE;
 	int do_now = how & AUTOFS_EXP_IMMEDIATE;
 	int exp_leaves = how & AUTOFS_EXP_LEAVES;
 	int exp_leaves = how & AUTOFS_EXP_LEAVES;
 	struct autofs_info *ino;
 	struct autofs_info *ino;
@@ -315,23 +336,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 	now = jiffies;
 	now = jiffies;
 	timeout = sbi->exp_timeout;
 	timeout = sbi->exp_timeout;
 
 
-	spin_lock(&dcache_lock);
-	next = root->d_subdirs.next;
-
-	/* On exit from the loop expire is set to a dgot dentry
-	 * to expire or it's NULL */
-	while ( next != &root->d_subdirs ) {
-		struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
-
-		/* Negative dentry - give up */
-		if (!simple_positive(dentry)) {
-			next = next->next;
-			continue;
-		}
-
-		dentry = dget(dentry);
-		spin_unlock(&dcache_lock);
-
+	dentry = NULL;
+	while ((dentry = get_next_positive_dentry(dentry, root))) {
 		spin_lock(&sbi->fs_lock);
 		spin_lock(&sbi->fs_lock);
 		ino = autofs4_dentry_ino(dentry);
 		ino = autofs4_dentry_ino(dentry);
 
 
@@ -347,7 +353,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 
 
 			/* Path walk currently on this dentry? */
 			/* Path walk currently on this dentry? */
 			ino_count = atomic_read(&ino->count) + 2;
 			ino_count = atomic_read(&ino->count) + 2;
-			if (atomic_read(&dentry->d_count) > ino_count)
+			if (dentry->d_count > ino_count)
 				goto next;
 				goto next;
 
 
 			/* Can we umount this guy */
 			/* Can we umount this guy */
@@ -369,7 +375,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 		if (!exp_leaves) {
 		if (!exp_leaves) {
 			/* Path walk currently on this dentry? */
 			/* Path walk currently on this dentry? */
 			ino_count = atomic_read(&ino->count) + 1;
 			ino_count = atomic_read(&ino->count) + 1;
-			if (atomic_read(&dentry->d_count) > ino_count)
+			if (dentry->d_count > ino_count)
 				goto next;
 				goto next;
 
 
 			if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
 			if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
@@ -383,7 +389,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 		} else {
 		} else {
 			/* Path walk currently on this dentry? */
 			/* Path walk currently on this dentry? */
 			ino_count = atomic_read(&ino->count) + 1;
 			ino_count = atomic_read(&ino->count) + 1;
-			if (atomic_read(&dentry->d_count) > ino_count)
+			if (dentry->d_count > ino_count)
 				goto next;
 				goto next;
 
 
 			expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
 			expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
@@ -394,11 +400,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 		}
 		}
 next:
 next:
 		spin_unlock(&sbi->fs_lock);
 		spin_unlock(&sbi->fs_lock);
-		dput(dentry);
-		spin_lock(&dcache_lock);
-		next = next->next;
 	}
 	}
-	spin_unlock(&dcache_lock);
 	return NULL;
 	return NULL;
 
 
 found:
 found:
@@ -408,9 +410,13 @@ found:
 	ino->flags |= AUTOFS_INF_EXPIRING;
 	ino->flags |= AUTOFS_INF_EXPIRING;
 	init_completion(&ino->expire_complete);
 	init_completion(&ino->expire_complete);
 	spin_unlock(&sbi->fs_lock);
 	spin_unlock(&sbi->fs_lock);
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
+	spin_lock(&expired->d_parent->d_lock);
+	spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
 	list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
 	list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&expired->d_lock);
+	spin_unlock(&expired->d_parent->d_lock);
+	spin_unlock(&autofs4_lock);
 	return expired;
 	return expired;
 }
 }
 
 
@@ -499,7 +505,14 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
 
 
 		spin_lock(&sbi->fs_lock);
 		spin_lock(&sbi->fs_lock);
 		if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
 		if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
-			sb->s_root->d_mounted++;
+			spin_lock(&sb->s_root->d_lock);
+			/*
+			 * If we haven't been expired away, then reset
+			 * mounted status.
+			 */
+			if (mnt->mnt_parent != mnt)
+				sb->s_root->d_flags |= DCACHE_MOUNTED;
+			spin_unlock(&sb->s_root->d_lock);
 			ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
 			ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
 		}
 		}
 		ino->flags &= ~AUTOFS_INF_EXPIRING;
 		ino->flags &= ~AUTOFS_INF_EXPIRING;

+ 1 - 1
fs/autofs4/inode.c

@@ -309,7 +309,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
 		goto fail_iput;
 		goto fail_iput;
 	pipe = NULL;
 	pipe = NULL;
 
 
-	root->d_op = &autofs4_sb_dentry_operations;
+	d_set_d_op(root, &autofs4_sb_dentry_operations);
 	root->d_fsdata = ino;
 	root->d_fsdata = ino;
 
 
 	/* Can this call block? */
 	/* Can this call block? */

+ 57 - 34
fs/autofs4/root.c

@@ -23,6 +23,8 @@
 
 
 #include "autofs_i.h"
 #include "autofs_i.h"
 
 
+DEFINE_SPINLOCK(autofs4_lock);
+
 static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
 static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
 static int autofs4_dir_unlink(struct inode *,struct dentry *);
 static int autofs4_dir_unlink(struct inode *,struct dentry *);
 static int autofs4_dir_rmdir(struct inode *,struct dentry *);
 static int autofs4_dir_rmdir(struct inode *,struct dentry *);
@@ -142,12 +144,15 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
 	 * autofs file system so just let the libfs routines handle
 	 * autofs file system so just let the libfs routines handle
 	 * it.
 	 * it.
 	 */
 	 */
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
+	spin_lock(&dentry->d_lock);
 	if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
 	if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
+		spin_unlock(&autofs4_lock);
 		return -ENOENT;
 		return -ENOENT;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dentry->d_lock);
+	spin_unlock(&autofs4_lock);
 
 
 out:
 out:
 	return dcache_dir_open(inode, file);
 	return dcache_dir_open(inode, file);
@@ -252,9 +257,11 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
 	/* We trigger a mount for almost all flags */
 	/* We trigger a mount for almost all flags */
 	lookup_type = autofs4_need_mount(nd->flags);
 	lookup_type = autofs4_need_mount(nd->flags);
 	spin_lock(&sbi->fs_lock);
 	spin_lock(&sbi->fs_lock);
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
+	spin_lock(&dentry->d_lock);
 	if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
 	if (!(lookup_type || ino->flags & AUTOFS_INF_PENDING)) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
+		spin_unlock(&autofs4_lock);
 		spin_unlock(&sbi->fs_lock);
 		spin_unlock(&sbi->fs_lock);
 		goto follow;
 		goto follow;
 	}
 	}
@@ -266,7 +273,8 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
 	 */
 	 */
 	if (ino->flags & AUTOFS_INF_PENDING ||
 	if (ino->flags & AUTOFS_INF_PENDING ||
 	    (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
 	    (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
+		spin_unlock(&autofs4_lock);
 		spin_unlock(&sbi->fs_lock);
 		spin_unlock(&sbi->fs_lock);
 
 
 		status = try_to_fill_dentry(dentry, nd->flags);
 		status = try_to_fill_dentry(dentry, nd->flags);
@@ -275,7 +283,8 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
 
 
 		goto follow;
 		goto follow;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dentry->d_lock);
+	spin_unlock(&autofs4_lock);
 	spin_unlock(&sbi->fs_lock);
 	spin_unlock(&sbi->fs_lock);
 follow:
 follow:
 	/*
 	/*
@@ -306,12 +315,19 @@ out_error:
  */
  */
 static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
 static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
 {
-	struct inode *dir = dentry->d_parent->d_inode;
-	struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
-	int oz_mode = autofs4_oz_mode(sbi);
+	struct inode *dir;
+	struct autofs_sb_info *sbi;
+	int oz_mode;
 	int flags = nd ? nd->flags : 0;
 	int flags = nd ? nd->flags : 0;
 	int status = 1;
 	int status = 1;
 
 
+	if (flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	dir = dentry->d_parent->d_inode;
+	sbi = autofs4_sbi(dir->i_sb);
+	oz_mode = autofs4_oz_mode(sbi);
+
 	/* Pending dentry */
 	/* Pending dentry */
 	spin_lock(&sbi->fs_lock);
 	spin_lock(&sbi->fs_lock);
 	if (autofs4_ispending(dentry)) {
 	if (autofs4_ispending(dentry)) {
@@ -346,12 +362,14 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
 		return 0;
 		return 0;
 
 
 	/* Check for a non-mountpoint directory with no contents */
 	/* Check for a non-mountpoint directory with no contents */
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
+	spin_lock(&dentry->d_lock);
 	if (S_ISDIR(dentry->d_inode->i_mode) &&
 	if (S_ISDIR(dentry->d_inode->i_mode) &&
 	    !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
 	    !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
 		DPRINTK("dentry=%p %.*s, emptydir",
 		DPRINTK("dentry=%p %.*s, emptydir",
 			 dentry, dentry->d_name.len, dentry->d_name.name);
 			 dentry, dentry->d_name.len, dentry->d_name.name);
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
+		spin_unlock(&autofs4_lock);
 
 
 		/* The daemon never causes a mount to trigger */
 		/* The daemon never causes a mount to trigger */
 		if (oz_mode)
 		if (oz_mode)
@@ -367,7 +385,8 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
 
 
 		return status;
 		return status;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dentry->d_lock);
+	spin_unlock(&autofs4_lock);
 
 
 	return 1;
 	return 1;
 }
 }
@@ -422,7 +441,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
 	const unsigned char *str = name->name;
 	const unsigned char *str = name->name;
 	struct list_head *p, *head;
 	struct list_head *p, *head;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
 	spin_lock(&sbi->lookup_lock);
 	spin_lock(&sbi->lookup_lock);
 	head = &sbi->active_list;
 	head = &sbi->active_list;
 	list_for_each(p, head) {
 	list_for_each(p, head) {
@@ -436,7 +455,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
 		spin_lock(&active->d_lock);
 		spin_lock(&active->d_lock);
 
 
 		/* Already gone? */
 		/* Already gone? */
-		if (atomic_read(&active->d_count) == 0)
+		if (active->d_count == 0)
 			goto next;
 			goto next;
 
 
 		qstr = &active->d_name;
 		qstr = &active->d_name;
@@ -452,17 +471,17 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
 			goto next;
 			goto next;
 
 
 		if (d_unhashed(active)) {
 		if (d_unhashed(active)) {
-			dget(active);
+			dget_dlock(active);
 			spin_unlock(&active->d_lock);
 			spin_unlock(&active->d_lock);
 			spin_unlock(&sbi->lookup_lock);
 			spin_unlock(&sbi->lookup_lock);
-			spin_unlock(&dcache_lock);
+			spin_unlock(&autofs4_lock);
 			return active;
 			return active;
 		}
 		}
 next:
 next:
 		spin_unlock(&active->d_lock);
 		spin_unlock(&active->d_lock);
 	}
 	}
 	spin_unlock(&sbi->lookup_lock);
 	spin_unlock(&sbi->lookup_lock);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&autofs4_lock);
 
 
 	return NULL;
 	return NULL;
 }
 }
@@ -477,7 +496,7 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
 	const unsigned char *str = name->name;
 	const unsigned char *str = name->name;
 	struct list_head *p, *head;
 	struct list_head *p, *head;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
 	spin_lock(&sbi->lookup_lock);
 	spin_lock(&sbi->lookup_lock);
 	head = &sbi->expiring_list;
 	head = &sbi->expiring_list;
 	list_for_each(p, head) {
 	list_for_each(p, head) {
@@ -507,17 +526,17 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
 			goto next;
 			goto next;
 
 
 		if (d_unhashed(expiring)) {
 		if (d_unhashed(expiring)) {
-			dget(expiring);
+			dget_dlock(expiring);
 			spin_unlock(&expiring->d_lock);
 			spin_unlock(&expiring->d_lock);
 			spin_unlock(&sbi->lookup_lock);
 			spin_unlock(&sbi->lookup_lock);
-			spin_unlock(&dcache_lock);
+			spin_unlock(&autofs4_lock);
 			return expiring;
 			return expiring;
 		}
 		}
 next:
 next:
 		spin_unlock(&expiring->d_lock);
 		spin_unlock(&expiring->d_lock);
 	}
 	}
 	spin_unlock(&sbi->lookup_lock);
 	spin_unlock(&sbi->lookup_lock);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&autofs4_lock);
 
 
 	return NULL;
 	return NULL;
 }
 }
@@ -559,7 +578,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
 		 * we check for the hashed dentry and return the newly
 		 * we check for the hashed dentry and return the newly
 		 * hashed dentry.
 		 * hashed dentry.
 		 */
 		 */
-		dentry->d_op = &autofs4_root_dentry_operations;
+		d_set_d_op(dentry, &autofs4_root_dentry_operations);
 
 
 		/*
 		/*
 		 * And we need to ensure that the same dentry is used for
 		 * And we need to ensure that the same dentry is used for
@@ -698,9 +717,9 @@ static int autofs4_dir_symlink(struct inode *dir,
 	d_add(dentry, inode);
 	d_add(dentry, inode);
 
 
 	if (dir == dir->i_sb->s_root->d_inode)
 	if (dir == dir->i_sb->s_root->d_inode)
-		dentry->d_op = &autofs4_root_dentry_operations;
+		d_set_d_op(dentry, &autofs4_root_dentry_operations);
 	else
 	else
-		dentry->d_op = &autofs4_dentry_operations;
+		d_set_d_op(dentry, &autofs4_dentry_operations);
 
 
 	dentry->d_fsdata = ino;
 	dentry->d_fsdata = ino;
 	ino->dentry = dget(dentry);
 	ino->dentry = dget(dentry);
@@ -753,12 +772,12 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
 
 
 	dir->i_mtime = CURRENT_TIME;
 	dir->i_mtime = CURRENT_TIME;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
 	autofs4_add_expiring(dentry);
 	autofs4_add_expiring(dentry);
 	spin_lock(&dentry->d_lock);
 	spin_lock(&dentry->d_lock);
 	__d_drop(dentry);
 	__d_drop(dentry);
 	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dentry->d_lock);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&autofs4_lock);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -775,16 +794,20 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
 	if (!autofs4_oz_mode(sbi))
 	if (!autofs4_oz_mode(sbi))
 		return -EACCES;
 		return -EACCES;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&autofs4_lock);
+	spin_lock(&sbi->lookup_lock);
+	spin_lock(&dentry->d_lock);
 	if (!list_empty(&dentry->d_subdirs)) {
 	if (!list_empty(&dentry->d_subdirs)) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
+		spin_unlock(&sbi->lookup_lock);
+		spin_unlock(&autofs4_lock);
 		return -ENOTEMPTY;
 		return -ENOTEMPTY;
 	}
 	}
-	autofs4_add_expiring(dentry);
-	spin_lock(&dentry->d_lock);
+	__autofs4_add_expiring(dentry);
+	spin_unlock(&sbi->lookup_lock);
 	__d_drop(dentry);
 	__d_drop(dentry);
 	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dentry->d_lock);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&autofs4_lock);
 
 
 	if (atomic_dec_and_test(&ino->count)) {
 	if (atomic_dec_and_test(&ino->count)) {
 		p_ino = autofs4_dentry_ino(dentry->d_parent);
 		p_ino = autofs4_dentry_ino(dentry->d_parent);
@@ -829,9 +852,9 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 	d_add(dentry, inode);
 	d_add(dentry, inode);
 
 
 	if (dir == dir->i_sb->s_root->d_inode)
 	if (dir == dir->i_sb->s_root->d_inode)
-		dentry->d_op = &autofs4_root_dentry_operations;
+		d_set_d_op(dentry, &autofs4_root_dentry_operations);
 	else
 	else
-		dentry->d_op = &autofs4_dentry_operations;
+		d_set_d_op(dentry, &autofs4_dentry_operations);
 
 
 	dentry->d_fsdata = ino;
 	dentry->d_fsdata = ino;
 	ino->dentry = dget(dentry);
 	ino->dentry = dget(dentry);

+ 18 - 5
fs/autofs4/waitq.c

@@ -186,16 +186,26 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
 {
 {
 	struct dentry *root = sbi->sb->s_root;
 	struct dentry *root = sbi->sb->s_root;
 	struct dentry *tmp;
 	struct dentry *tmp;
-	char *buf = *name;
+	char *buf;
 	char *p;
 	char *p;
-	int len = 0;
+	int len;
+	unsigned seq;
 
 
-	spin_lock(&dcache_lock);
+rename_retry:
+	buf = *name;
+	len = 0;
+
+	seq = read_seqbegin(&rename_lock);
+	rcu_read_lock();
+	spin_lock(&autofs4_lock);
 	for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
 	for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
 		len += tmp->d_name.len + 1;
 		len += tmp->d_name.len + 1;
 
 
 	if (!len || --len > NAME_MAX) {
 	if (!len || --len > NAME_MAX) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&autofs4_lock);
+		rcu_read_unlock();
+		if (read_seqretry(&rename_lock, seq))
+			goto rename_retry;
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -208,7 +218,10 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
 		p -= tmp->d_name.len;
 		p -= tmp->d_name.len;
 		strncpy(p, tmp->d_name.name, tmp->d_name.len);
 		strncpy(p, tmp->d_name.name, tmp->d_name.len);
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&autofs4_lock);
+	rcu_read_unlock();
+	if (read_seqretry(&rename_lock, seq))
+		goto rename_retry;
 
 
 	return len;
 	return len;
 }
 }

+ 4 - 1
fs/bad_inode.c

@@ -229,8 +229,11 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
 	return -EIO;
 	return -EIO;
 }
 }
 
 
-static int bad_inode_permission(struct inode *inode, int mask)
+static int bad_inode_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	return -EIO;
 	return -EIO;
 }
 }
 
 

+ 8 - 2
fs/befs/linuxvfs.c

@@ -284,12 +284,18 @@ befs_alloc_inode(struct super_block *sb)
         return &bi->vfs_inode;
         return &bi->vfs_inode;
 }
 }
 
 
-static void
-befs_destroy_inode(struct inode *inode)
+static void befs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
         kmem_cache_free(befs_inode_cachep, BEFS_I(inode));
         kmem_cache_free(befs_inode_cachep, BEFS_I(inode));
 }
 }
 
 
+static void befs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, befs_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
         struct befs_inode_info *bi = (struct befs_inode_info *) foo;
         struct befs_inode_info *bi = (struct befs_inode_info *) foo;

+ 8 - 1
fs/bfs/inode.c

@@ -248,11 +248,18 @@ static struct inode *bfs_alloc_inode(struct super_block *sb)
 	return &bi->vfs_inode;
 	return &bi->vfs_inode;
 }
 }
 
 
-static void bfs_destroy_inode(struct inode *inode)
+static void bfs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(bfs_inode_cachep, BFS_I(inode));
 	kmem_cache_free(bfs_inode_cachep, BFS_I(inode));
 }
 }
 
 
+static void bfs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, bfs_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct bfs_inode_info *bi = foo;
 	struct bfs_inode_info *bi = foo;

+ 8 - 1
fs/block_dev.c

@@ -409,13 +409,20 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void bdev_destroy_inode(struct inode *inode)
+static void bdev_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
 	struct bdev_inode *bdi = BDEV_I(inode);
 	struct bdev_inode *bdi = BDEV_I(inode);
 
 
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(bdev_cachep, bdi);
 	kmem_cache_free(bdev_cachep, bdi);
 }
 }
 
 
+static void bdev_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, bdev_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct bdev_inode *ei = (struct bdev_inode *) foo;
 	struct bdev_inode *ei = (struct bdev_inode *) foo;

+ 13 - 8
fs/btrfs/acl.c

@@ -185,18 +185,23 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
 	return ret;
 	return ret;
 }
 }
 
 
-int btrfs_check_acl(struct inode *inode, int mask)
+int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	struct posix_acl *acl;
 	int error = -EAGAIN;
 	int error = -EAGAIN;
 
 
-	acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
+	if (flags & IPERM_FLAG_RCU) {
+		if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
+			error = -ECHILD;
 
 
-	if (IS_ERR(acl))
-		return PTR_ERR(acl);
-	if (acl) {
-		error = posix_acl_permission(inode, acl, mask);
-		posix_acl_release(acl);
+	} else {
+		struct posix_acl *acl;
+		acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		if (acl) {
+			error = posix_acl_permission(inode, acl, mask);
+			posix_acl_release(acl);
+		}
 	}
 	}
 
 
 	return error;
 	return error;

+ 1 - 1
fs/btrfs/ctree.h

@@ -2544,7 +2544,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait);
 
 
 /* acl.c */
 /* acl.c */
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
-int btrfs_check_acl(struct inode *inode, int mask);
+int btrfs_check_acl(struct inode *inode, int mask, unsigned int flags);
 #else
 #else
 #define btrfs_check_acl NULL
 #define btrfs_check_acl NULL
 #endif
 #endif

+ 2 - 2
fs/btrfs/export.c

@@ -110,7 +110,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
 
 
 	dentry = d_obtain_alias(inode);
 	dentry = d_obtain_alias(inode);
 	if (!IS_ERR(dentry))
 	if (!IS_ERR(dentry))
-		dentry->d_op = &btrfs_dentry_operations;
+		d_set_d_op(dentry, &btrfs_dentry_operations);
 	return dentry;
 	return dentry;
 fail:
 fail:
 	srcu_read_unlock(&fs_info->subvol_srcu, index);
 	srcu_read_unlock(&fs_info->subvol_srcu, index);
@@ -225,7 +225,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
 	key.offset = 0;
 	key.offset = 0;
 	dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL));
 	dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL));
 	if (!IS_ERR(dentry))
 	if (!IS_ERR(dentry))
-		dentry->d_op = &btrfs_dentry_operations;
+		d_set_d_op(dentry, &btrfs_dentry_operations);
 	return dentry;
 	return dentry;
 fail:
 fail:
 	btrfs_free_path(path);
 	btrfs_free_path(path);

+ 12 - 5
fs/btrfs/inode.c

@@ -4084,7 +4084,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 	int index;
 	int index;
 	int ret;
 	int ret;
 
 
-	dentry->d_op = &btrfs_dentry_operations;
+	d_set_d_op(dentry, &btrfs_dentry_operations);
 
 
 	if (dentry->d_name.len > BTRFS_NAME_LEN)
 	if (dentry->d_name.len > BTRFS_NAME_LEN)
 		return ERR_PTR(-ENAMETOOLONG);
 		return ERR_PTR(-ENAMETOOLONG);
@@ -4127,7 +4127,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 	return inode;
 	return inode;
 }
 }
 
 
-static int btrfs_dentry_delete(struct dentry *dentry)
+static int btrfs_dentry_delete(const struct dentry *dentry)
 {
 {
 	struct btrfs_root *root;
 	struct btrfs_root *root;
 
 
@@ -6495,6 +6495,13 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
 	return inode;
 	return inode;
 }
 }
 
 
+static void btrfs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
+}
+
 void btrfs_destroy_inode(struct inode *inode)
 void btrfs_destroy_inode(struct inode *inode)
 {
 {
 	struct btrfs_ordered_extent *ordered;
 	struct btrfs_ordered_extent *ordered;
@@ -6564,7 +6571,7 @@ void btrfs_destroy_inode(struct inode *inode)
 	inode_tree_del(inode);
 	inode_tree_del(inode);
 	btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
 	btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
 free:
 free:
-	kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
+	call_rcu(&inode->i_rcu, btrfs_i_callback);
 }
 }
 
 
 int btrfs_drop_inode(struct inode *inode)
 int btrfs_drop_inode(struct inode *inode)
@@ -7204,11 +7211,11 @@ static int btrfs_set_page_dirty(struct page *page)
 	return __set_page_dirty_nobuffers(page);
 	return __set_page_dirty_nobuffers(page);
 }
 }
 
 
-static int btrfs_permission(struct inode *inode, int mask)
+static int btrfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
 	if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))
 		return -EACCES;
 		return -EACCES;
-	return generic_permission(inode, mask, btrfs_check_acl);
+	return generic_permission(inode, mask, flags, btrfs_check_acl);
 }
 }
 
 
 static const struct inode_operations btrfs_dir_inode_operations = {
 static const struct inode_operations btrfs_dir_inode_operations = {

+ 18 - 10
fs/ceph/dir.c

@@ -42,11 +42,11 @@ int ceph_init_dentry(struct dentry *dentry)
 
 
 	if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
 	if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
 	    ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
 	    ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)
-		dentry->d_op = &ceph_dentry_ops;
+		d_set_d_op(dentry, &ceph_dentry_ops);
 	else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
 	else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)
-		dentry->d_op = &ceph_snapdir_dentry_ops;
+		d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
 	else
 	else
-		dentry->d_op = &ceph_snap_dentry_ops;
+		d_set_d_op(dentry, &ceph_snap_dentry_ops);
 
 
 	di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
 	di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);
 	if (!di)
 	if (!di)
@@ -112,7 +112,7 @@ static int __dcache_readdir(struct file *filp,
 	dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos,
 	dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos,
 	     last);
 	     last);
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&parent->d_lock);
 
 
 	/* start at beginning? */
 	/* start at beginning? */
 	if (filp->f_pos == 2 || last == NULL ||
 	if (filp->f_pos == 2 || last == NULL ||
@@ -136,6 +136,7 @@ more:
 			fi->at_end = 1;
 			fi->at_end = 1;
 			goto out_unlock;
 			goto out_unlock;
 		}
 		}
+		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
 		if (!d_unhashed(dentry) && dentry->d_inode &&
 		if (!d_unhashed(dentry) && dentry->d_inode &&
 		    ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
 		    ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
 		    ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
 		    ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
@@ -145,13 +146,15 @@ more:
 		     dentry->d_name.len, dentry->d_name.name, di->offset,
 		     dentry->d_name.len, dentry->d_name.name, di->offset,
 		     filp->f_pos, d_unhashed(dentry) ? " unhashed" : "",
 		     filp->f_pos, d_unhashed(dentry) ? " unhashed" : "",
 		     !dentry->d_inode ? " null" : "");
 		     !dentry->d_inode ? " null" : "");
+		spin_unlock(&dentry->d_lock);
 		p = p->prev;
 		p = p->prev;
 		dentry = list_entry(p, struct dentry, d_u.d_child);
 		dentry = list_entry(p, struct dentry, d_u.d_child);
 		di = ceph_dentry(dentry);
 		di = ceph_dentry(dentry);
 	}
 	}
 
 
-	atomic_inc(&dentry->d_count);
-	spin_unlock(&dcache_lock);
+	dget_dlock(dentry);
+	spin_unlock(&dentry->d_lock);
+	spin_unlock(&parent->d_lock);
 
 
 	dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos,
 	dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos,
 	     dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
 	     dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
@@ -177,19 +180,19 @@ more:
 
 
 	filp->f_pos++;
 	filp->f_pos++;
 
 
-	/* make sure a dentry wasn't dropped while we didn't have dcache_lock */
+	/* make sure a dentry wasn't dropped while we didn't have parent lock */
 	if (!ceph_i_test(dir, CEPH_I_COMPLETE)) {
 	if (!ceph_i_test(dir, CEPH_I_COMPLETE)) {
 		dout(" lost I_COMPLETE on %p; falling back to mds\n", dir);
 		dout(" lost I_COMPLETE on %p; falling back to mds\n", dir);
 		err = -EAGAIN;
 		err = -EAGAIN;
 		goto out;
 		goto out;
 	}
 	}
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&parent->d_lock);
 	p = p->prev;	/* advance to next dentry */
 	p = p->prev;	/* advance to next dentry */
 	goto more;
 	goto more;
 
 
 out_unlock:
 out_unlock:
-	spin_unlock(&dcache_lock);
+	spin_unlock(&parent->d_lock);
 out:
 out:
 	if (last)
 	if (last)
 		dput(last);
 		dput(last);
@@ -987,7 +990,12 @@ 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)
 {
 {
-	struct inode *dir = dentry->d_parent->d_inode;
+	struct inode *dir;
+
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	dir = dentry->d_parent->d_inode;
 
 
 	dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
 	dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry,
 	     dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
 	     dentry->d_name.len, dentry->d_name.name, dentry->d_inode,

+ 26 - 12
fs/ceph/inode.c

@@ -368,6 +368,15 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
 	return &ci->vfs_inode;
 	return &ci->vfs_inode;
 }
 }
 
 
+static void ceph_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	struct ceph_inode_info *ci = ceph_inode(inode);
+
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(ceph_inode_cachep, ci);
+}
+
 void ceph_destroy_inode(struct inode *inode)
 void ceph_destroy_inode(struct inode *inode)
 {
 {
 	struct ceph_inode_info *ci = ceph_inode(inode);
 	struct ceph_inode_info *ci = ceph_inode(inode);
@@ -407,7 +416,7 @@ void ceph_destroy_inode(struct inode *inode)
 	if (ci->i_xattrs.prealloc_blob)
 	if (ci->i_xattrs.prealloc_blob)
 		ceph_buffer_put(ci->i_xattrs.prealloc_blob);
 		ceph_buffer_put(ci->i_xattrs.prealloc_blob);
 
 
-	kmem_cache_free(ceph_inode_cachep, ci);
+	call_rcu(&inode->i_rcu, ceph_i_callback);
 }
 }
 
 
 
 
@@ -841,13 +850,13 @@ static void ceph_set_dentry_offset(struct dentry *dn)
 	di->offset = ceph_inode(inode)->i_max_offset++;
 	di->offset = ceph_inode(inode)->i_max_offset++;
 	spin_unlock(&inode->i_lock);
 	spin_unlock(&inode->i_lock);
 
 
-	spin_lock(&dcache_lock);
-	spin_lock(&dn->d_lock);
+	spin_lock(&dir->d_lock);
+	spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED);
 	list_move(&dn->d_u.d_child, &dir->d_subdirs);
 	list_move(&dn->d_u.d_child, &dir->d_subdirs);
 	dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset,
 	dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset,
 	     dn->d_u.d_child.prev, dn->d_u.d_child.next);
 	     dn->d_u.d_child.prev, dn->d_u.d_child.next);
 	spin_unlock(&dn->d_lock);
 	spin_unlock(&dn->d_lock);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dir->d_lock);
 }
 }
 
 
 /*
 /*
@@ -879,8 +888,8 @@ static struct dentry *splice_dentry(struct dentry *dn, struct inode *in,
 	} else if (realdn) {
 	} else if (realdn) {
 		dout("dn %p (%d) spliced with %p (%d) "
 		dout("dn %p (%d) spliced with %p (%d) "
 		     "inode %p ino %llx.%llx\n",
 		     "inode %p ino %llx.%llx\n",
-		     dn, atomic_read(&dn->d_count),
-		     realdn, atomic_read(&realdn->d_count),
+		     dn, dn->d_count,
+		     realdn, realdn->d_count,
 		     realdn->d_inode, ceph_vinop(realdn->d_inode));
 		     realdn->d_inode, ceph_vinop(realdn->d_inode));
 		dput(dn);
 		dput(dn);
 		dn = realdn;
 		dn = realdn;
@@ -1231,11 +1240,11 @@ retry_lookup:
 			goto retry_lookup;
 			goto retry_lookup;
 		} else {
 		} else {
 			/* reorder parent's d_subdirs */
 			/* reorder parent's d_subdirs */
-			spin_lock(&dcache_lock);
-			spin_lock(&dn->d_lock);
+			spin_lock(&parent->d_lock);
+			spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED);
 			list_move(&dn->d_u.d_child, &parent->d_subdirs);
 			list_move(&dn->d_u.d_child, &parent->d_subdirs);
 			spin_unlock(&dn->d_lock);
 			spin_unlock(&dn->d_lock);
-			spin_unlock(&dcache_lock);
+			spin_unlock(&parent->d_lock);
 		}
 		}
 
 
 		di = dn->d_fsdata;
 		di = dn->d_fsdata;
@@ -1772,12 +1781,17 @@ int ceph_do_getattr(struct inode *inode, int mask)
  * Check inode permissions.  We verify we have a valid value for
  * Check inode permissions.  We verify we have a valid value for
  * the AUTH cap, then call the generic handler.
  * the AUTH cap, then call the generic handler.
  */
  */
-int ceph_permission(struct inode *inode, int mask)
+int ceph_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	int err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED);
+	int err;
+
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
+	err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED);
 
 
 	if (!err)
 	if (!err)
-		err = generic_permission(inode, mask, NULL);
+		err = generic_permission(inode, mask, flags, NULL);
 	return err;
 	return err;
 }
 }
 
 

+ 1 - 1
fs/ceph/mds_client.c

@@ -1486,7 +1486,7 @@ retry:
 	*base = ceph_ino(temp->d_inode);
 	*base = ceph_ino(temp->d_inode);
 	*plen = len;
 	*plen = len;
 	dout("build_path on %p %d built %llx '%.*s'\n",
 	dout("build_path on %p %d built %llx '%.*s'\n",
-	     dentry, atomic_read(&dentry->d_count), *base, len, path);
+	     dentry, dentry->d_count, *base, len, path);
 	return path;
 	return path;
 }
 }
 
 

+ 1 - 1
fs/ceph/super.h

@@ -665,7 +665,7 @@ extern void ceph_queue_invalidate(struct inode *inode);
 extern void ceph_queue_writeback(struct inode *inode);
 extern void ceph_queue_writeback(struct inode *inode);
 
 
 extern int ceph_do_getattr(struct inode *inode, int mask);
 extern int ceph_do_getattr(struct inode *inode, int mask);
-extern int ceph_permission(struct inode *inode, int mask);
+extern int ceph_permission(struct inode *inode, int mask, unsigned int flags);
 extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
 extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
 extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
 			struct kstat *stat);
 			struct kstat *stat);

+ 13 - 3
fs/cifs/cifsfs.c

@@ -283,10 +283,13 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 	return 0;
 }
 }
 
 
-static int cifs_permission(struct inode *inode, int mask)
+static int cifs_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	struct cifs_sb_info *cifs_sb;
 	struct cifs_sb_info *cifs_sb;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	cifs_sb = CIFS_SB(inode->i_sb);
 	cifs_sb = CIFS_SB(inode->i_sb);
 
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
@@ -298,7 +301,7 @@ static int cifs_permission(struct inode *inode, int mask)
 		on the client (above and beyond ACL on servers) for
 		on the client (above and beyond ACL on servers) for
 		servers which do not support setting and viewing mode bits,
 		servers which do not support setting and viewing mode bits,
 		so allowing client to check permissions is useful */
 		so allowing client to check permissions is useful */
-		return generic_permission(inode, mask, NULL);
+		return generic_permission(inode, mask, flags, NULL);
 }
 }
 
 
 static struct kmem_cache *cifs_inode_cachep;
 static struct kmem_cache *cifs_inode_cachep;
@@ -334,10 +337,17 @@ cifs_alloc_inode(struct super_block *sb)
 	return &cifs_inode->vfs_inode;
 	return &cifs_inode->vfs_inode;
 }
 }
 
 
+static void cifs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
+}
+
 static void
 static void
 cifs_destroy_inode(struct inode *inode)
 cifs_destroy_inode(struct inode *inode)
 {
 {
-	kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
+	call_rcu(&inode->i_rcu, cifs_i_callback);
 }
 }
 
 
 static void
 static void

+ 44 - 33
fs/cifs/dir.c

@@ -135,9 +135,9 @@ static void setup_cifs_dentry(struct cifsTconInfo *tcon,
 			      struct inode *newinode)
 			      struct inode *newinode)
 {
 {
 	if (tcon->nocase)
 	if (tcon->nocase)
-		direntry->d_op = &cifs_ci_dentry_ops;
+		d_set_d_op(direntry, &cifs_ci_dentry_ops);
 	else
 	else
-		direntry->d_op = &cifs_dentry_ops;
+		d_set_d_op(direntry, &cifs_dentry_ops);
 	d_instantiate(direntry, newinode);
 	d_instantiate(direntry, newinode);
 }
 }
 
 
@@ -421,9 +421,9 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
 		rc = cifs_get_inode_info_unix(&newinode, full_path,
 		rc = cifs_get_inode_info_unix(&newinode, full_path,
 						inode->i_sb, xid);
 						inode->i_sb, xid);
 		if (pTcon->nocase)
 		if (pTcon->nocase)
-			direntry->d_op = &cifs_ci_dentry_ops;
+			d_set_d_op(direntry, &cifs_ci_dentry_ops);
 		else
 		else
-			direntry->d_op = &cifs_dentry_ops;
+			d_set_d_op(direntry, &cifs_dentry_ops);
 
 
 		if (rc == 0)
 		if (rc == 0)
 			d_instantiate(direntry, newinode);
 			d_instantiate(direntry, newinode);
@@ -604,9 +604,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
 
 
 	if ((rc == 0) && (newInode != NULL)) {
 	if ((rc == 0) && (newInode != NULL)) {
 		if (pTcon->nocase)
 		if (pTcon->nocase)
-			direntry->d_op = &cifs_ci_dentry_ops;
+			d_set_d_op(direntry, &cifs_ci_dentry_ops);
 		else
 		else
-			direntry->d_op = &cifs_dentry_ops;
+			d_set_d_op(direntry, &cifs_dentry_ops);
 		d_add(direntry, newInode);
 		d_add(direntry, newInode);
 		if (posix_open) {
 		if (posix_open) {
 			filp = lookup_instantiate_filp(nd, direntry,
 			filp = lookup_instantiate_filp(nd, direntry,
@@ -634,9 +634,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
 		rc = 0;
 		rc = 0;
 		direntry->d_time = jiffies;
 		direntry->d_time = jiffies;
 		if (pTcon->nocase)
 		if (pTcon->nocase)
-			direntry->d_op = &cifs_ci_dentry_ops;
+			d_set_d_op(direntry, &cifs_ci_dentry_ops);
 		else
 		else
-			direntry->d_op = &cifs_dentry_ops;
+			d_set_d_op(direntry, &cifs_dentry_ops);
 		d_add(direntry, NULL);
 		d_add(direntry, NULL);
 	/*	if it was once a directory (but how can we tell?) we could do
 	/*	if it was once a directory (but how can we tell?) we could do
 		shrink_dcache_parent(direntry); */
 		shrink_dcache_parent(direntry); */
@@ -656,22 +656,37 @@ lookup_out:
 static int
 static int
 cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
 cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
 {
 {
-	int isValid = 1;
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
 
 
 	if (direntry->d_inode) {
 	if (direntry->d_inode) {
 		if (cifs_revalidate_dentry(direntry))
 		if (cifs_revalidate_dentry(direntry))
 			return 0;
 			return 0;
-	} else {
-		cFYI(1, "neg dentry 0x%p name = %s",
-			 direntry, direntry->d_name.name);
-		if (time_after(jiffies, direntry->d_time + HZ) ||
-			!lookupCacheEnabled) {
-			d_drop(direntry);
-			isValid = 0;
-		}
+		else
+			return 1;
 	}
 	}
 
 
-	return isValid;
+	/*
+	 * This may be nfsd (or something), anyway, we can't see the
+	 * intent of this. So, since this can be for creation, drop it.
+	 */
+	if (!nd)
+		return 0;
+
+	/*
+	 * Drop the negative dentry, in order to make sure to use the
+	 * case sensitive name which is specified by user if this is
+	 * for creation.
+	 */
+	if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
+		if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
+			return 0;
+	}
+
+	if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled)
+		return 0;
+
+	return 1;
 }
 }
 
 
 /* static int cifs_d_delete(struct dentry *direntry)
 /* static int cifs_d_delete(struct dentry *direntry)
@@ -688,9 +703,10 @@ const struct dentry_operations cifs_dentry_ops = {
 /* d_delete:       cifs_d_delete,      */ /* not needed except for debugging */
 /* d_delete:       cifs_d_delete,      */ /* not needed except for debugging */
 };
 };
 
 
-static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
+static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *q)
 {
 {
-	struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
+	struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
 	unsigned long hash;
 	unsigned long hash;
 	int i;
 	int i;
 
 
@@ -703,21 +719,16 @@ static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
 	return 0;
 	return 0;
 }
 }
 
 
-static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
-			   struct qstr *b)
+static int cifs_ci_compare(const struct dentry *parent,
+		const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
-	struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
-
-	if ((a->len == b->len) &&
-	    (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
-		/*
-		 * To preserve case, don't let an existing negative dentry's
-		 * case take precedence.  If a is not a negative dentry, this
-		 * should have no side effects
-		 */
-		memcpy((void *)a->name, b->name, a->len);
+	struct nls_table *codepage = CIFS_SB(pinode->i_sb)->local_nls;
+
+	if ((name->len == len) &&
+	    (nls_strnicmp(codepage, name->name, str, len) == 0))
 		return 0;
 		return 0;
-	}
 	return 1;
 	return 1;
 }
 }
 
 

+ 7 - 7
fs/cifs/inode.c

@@ -809,14 +809,14 @@ inode_has_hashed_dentries(struct inode *inode)
 {
 {
 	struct dentry *dentry;
 	struct dentry *dentry;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&inode->i_lock);
 	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
 	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
 		if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
 		if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
-			spin_unlock(&dcache_lock);
+			spin_unlock(&inode->i_lock);
 			return true;
 			return true;
 		}
 		}
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 	return false;
 	return false;
 }
 }
 
 
@@ -1319,9 +1319,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
 	to set uid/gid */
 	to set uid/gid */
 			inc_nlink(inode);
 			inc_nlink(inode);
 			if (pTcon->nocase)
 			if (pTcon->nocase)
-				direntry->d_op = &cifs_ci_dentry_ops;
+				d_set_d_op(direntry, &cifs_ci_dentry_ops);
 			else
 			else
-				direntry->d_op = &cifs_dentry_ops;
+				d_set_d_op(direntry, &cifs_dentry_ops);
 
 
 			cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb);
 			cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb);
 			cifs_fill_uniqueid(inode->i_sb, &fattr);
 			cifs_fill_uniqueid(inode->i_sb, &fattr);
@@ -1363,9 +1363,9 @@ mkdir_get_info:
 						 inode->i_sb, xid, NULL);
 						 inode->i_sb, xid, NULL);
 
 
 		if (pTcon->nocase)
 		if (pTcon->nocase)
-			direntry->d_op = &cifs_ci_dentry_ops;
+			d_set_d_op(direntry, &cifs_ci_dentry_ops);
 		else
 		else
-			direntry->d_op = &cifs_dentry_ops;
+			d_set_d_op(direntry, &cifs_dentry_ops);
 		d_instantiate(direntry, newinode);
 		d_instantiate(direntry, newinode);
 		 /* setting nlink not necessary except in cases where we
 		 /* setting nlink not necessary except in cases where we
 		  * failed to get it from the server or was set bogus */
 		  * failed to get it from the server or was set bogus */

+ 2 - 2
fs/cifs/link.c

@@ -525,9 +525,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
 			      rc);
 			      rc);
 		} else {
 		} else {
 			if (pTcon->nocase)
 			if (pTcon->nocase)
-				direntry->d_op = &cifs_ci_dentry_ops;
+				d_set_d_op(direntry, &cifs_ci_dentry_ops);
 			else
 			else
-				direntry->d_op = &cifs_dentry_ops;
+				d_set_d_op(direntry, &cifs_dentry_ops);
 			d_instantiate(direntry, newinode);
 			d_instantiate(direntry, newinode);
 		}
 		}
 	}
 	}

+ 3 - 3
fs/cifs/readdir.c

@@ -79,7 +79,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
 	cFYI(1, "For %s", name->name);
 	cFYI(1, "For %s", name->name);
 
 
 	if (parent->d_op && parent->d_op->d_hash)
 	if (parent->d_op && parent->d_op->d_hash)
-		parent->d_op->d_hash(parent, name);
+		parent->d_op->d_hash(parent, parent->d_inode, name);
 	else
 	else
 		name->hash = full_name_hash(name->name, name->len);
 		name->hash = full_name_hash(name->name, name->len);
 
 
@@ -103,9 +103,9 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
 	}
 	}
 
 
 	if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
 	if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
-		dentry->d_op = &cifs_ci_dentry_ops;
+		d_set_d_op(dentry, &cifs_ci_dentry_ops);
 	else
 	else
-		dentry->d_op = &cifs_dentry_ops;
+		d_set_d_op(dentry, &cifs_dentry_ops);
 
 
 	alias = d_materialise_unique(dentry, inode);
 	alias = d_materialise_unique(dentry, inode);
 	if (alias != NULL) {
 	if (alias != NULL) {

+ 2 - 2
fs/coda/cache.c

@@ -93,7 +93,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
 	struct list_head *child;
 	struct list_head *child;
 	struct dentry *de;
 	struct dentry *de;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&parent->d_lock);
 	list_for_each(child, &parent->d_subdirs)
 	list_for_each(child, &parent->d_subdirs)
 	{
 	{
 		de = list_entry(child, struct dentry, d_u.d_child);
 		de = list_entry(child, struct dentry, d_u.d_child);
@@ -102,7 +102,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
 			continue;
 			continue;
 		coda_flag_inode(de->d_inode, flag);
 		coda_flag_inode(de->d_inode, flag);
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&parent->d_lock);
 	return; 
 	return; 
 }
 }
 
 

+ 14 - 6
fs/coda/dir.c

@@ -18,6 +18,7 @@
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
+#include <linux/namei.h>
 
 
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 
 
@@ -47,7 +48,7 @@ static int coda_readdir(struct file *file, void *buf, filldir_t filldir);
 
 
 /* dentry ops */
 /* dentry ops */
 static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd);
 static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd);
-static int coda_dentry_delete(struct dentry *);
+static int coda_dentry_delete(const struct dentry *);
 
 
 /* support routines */
 /* support routines */
 static int coda_venus_readdir(struct file *coda_file, void *buf,
 static int coda_venus_readdir(struct file *coda_file, void *buf,
@@ -125,7 +126,7 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struc
 		return ERR_PTR(error);
 		return ERR_PTR(error);
 
 
 exit:
 exit:
-	entry->d_op = &coda_dentry_operations;
+	d_set_d_op(entry, &coda_dentry_operations);
 
 
 	if (inode && (type & CODA_NOCACHE))
 	if (inode && (type & CODA_NOCACHE))
 		coda_flag_inode(inode, C_VATTR | C_PURGE);
 		coda_flag_inode(inode, C_VATTR | C_PURGE);
@@ -134,10 +135,13 @@ exit:
 }
 }
 
 
 
 
-int coda_permission(struct inode *inode, int mask)
+int coda_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	int error;
 	int error;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
 	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
  
 	if (!mask)
 	if (!mask)
@@ -541,9 +545,13 @@ out:
 /* called when a cache lookup succeeds */
 /* called when a cache lookup succeeds */
 static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd)
 static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd)
 {
 {
-	struct inode *inode = de->d_inode;
+	struct inode *inode;
 	struct coda_inode_info *cii;
 	struct coda_inode_info *cii;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	inode = de->d_inode;
 	if (!inode || coda_isroot(inode))
 	if (!inode || coda_isroot(inode))
 		goto out;
 		goto out;
 	if (is_bad_inode(inode))
 	if (is_bad_inode(inode))
@@ -559,7 +567,7 @@ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd)
 	if (cii->c_flags & C_FLUSH) 
 	if (cii->c_flags & C_FLUSH) 
 		coda_flag_inode_children(inode, C_FLUSH);
 		coda_flag_inode_children(inode, C_FLUSH);
 
 
-	if (atomic_read(&de->d_count) > 1)
+	if (de->d_count > 1)
 		/* pretend it's valid, but don't change the flags */
 		/* pretend it's valid, but don't change the flags */
 		goto out;
 		goto out;
 
 
@@ -577,7 +585,7 @@ out:
  * This is the callback from dput() when d_count is going to 0.
  * This is the callback from dput() when d_count is going to 0.
  * We use this to unhash dentries with bad inodes.
  * We use this to unhash dentries with bad inodes.
  */
  */
-static int coda_dentry_delete(struct dentry * dentry)
+static int coda_dentry_delete(const struct dentry * dentry)
 {
 {
 	int flags;
 	int flags;
 
 

+ 8 - 1
fs/coda/inode.c

@@ -56,11 +56,18 @@ static struct inode *coda_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void coda_destroy_inode(struct inode *inode)
+static void coda_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(coda_inode_cachep, ITOC(inode));
 	kmem_cache_free(coda_inode_cachep, ITOC(inode));
 }
 }
 
 
+static void coda_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, coda_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct coda_inode_info *ei = (struct coda_inode_info *) foo;
 	struct coda_inode_info *ei = (struct coda_inode_info *) foo;

+ 4 - 2
fs/coda/pioctl.c

@@ -24,7 +24,7 @@
 #include <linux/coda_psdev.h>
 #include <linux/coda_psdev.h>
 
 
 /* pioctl ops */
 /* pioctl ops */
-static int coda_ioctl_permission(struct inode *inode, int mask);
+static int coda_ioctl_permission(struct inode *inode, int mask, unsigned int flags);
 static long coda_pioctl(struct file *filp, unsigned int cmd,
 static long coda_pioctl(struct file *filp, unsigned int cmd,
 			unsigned long user_data);
 			unsigned long user_data);
 
 
@@ -41,8 +41,10 @@ const struct file_operations coda_ioctl_operations = {
 };
 };
 
 
 /* the coda pioctl inode ops */
 /* the coda pioctl inode ops */
-static int coda_ioctl_permission(struct inode *inode, int mask)
+static int coda_ioctl_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
 	return (mask & MAY_EXEC) ? -EACCES : 0;
 	return (mask & MAY_EXEC) ? -EACCES : 0;
 }
 }
 
 

+ 2 - 2
fs/configfs/configfs_internal.h

@@ -120,7 +120,7 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
 {
 {
 	struct config_item * item = NULL;
 	struct config_item * item = NULL;
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&dentry->d_lock);
 	if (!d_unhashed(dentry)) {
 	if (!d_unhashed(dentry)) {
 		struct configfs_dirent * sd = dentry->d_fsdata;
 		struct configfs_dirent * sd = dentry->d_fsdata;
 		if (sd->s_type & CONFIGFS_ITEM_LINK) {
 		if (sd->s_type & CONFIGFS_ITEM_LINK) {
@@ -129,7 +129,7 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
 		} else
 		} else
 			item = config_item_get(sd->s_element);
 			item = config_item_get(sd->s_element);
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dentry->d_lock);
 
 
 	return item;
 	return item;
 }
 }

+ 12 - 12
fs/configfs/dir.c

@@ -67,7 +67,7 @@ static void configfs_d_iput(struct dentry * dentry,
  * We _must_ delete our dentries on last dput, as the chain-to-parent
  * We _must_ delete our dentries on last dput, as the chain-to-parent
  * behavior is required to clear the parents of default_groups.
  * behavior is required to clear the parents of default_groups.
  */
  */
-static int configfs_d_delete(struct dentry *dentry)
+static int configfs_d_delete(const struct dentry *dentry)
 {
 {
 	return 1;
 	return 1;
 }
 }
@@ -232,10 +232,8 @@ int configfs_make_dirent(struct configfs_dirent * parent_sd,
 
 
 	sd->s_mode = mode;
 	sd->s_mode = mode;
 	sd->s_dentry = dentry;
 	sd->s_dentry = dentry;
-	if (dentry) {
+	if (dentry)
 		dentry->d_fsdata = configfs_get(sd);
 		dentry->d_fsdata = configfs_get(sd);
-		dentry->d_op = &configfs_dentry_ops;
-	}
 
 
 	return 0;
 	return 0;
 }
 }
@@ -278,7 +276,6 @@ static int create_dir(struct config_item * k, struct dentry * p,
 		error = configfs_create(d, mode, init_dir);
 		error = configfs_create(d, mode, init_dir);
 		if (!error) {
 		if (!error) {
 			inc_nlink(p->d_inode);
 			inc_nlink(p->d_inode);
-			(d)->d_op = &configfs_dentry_ops;
 		} else {
 		} else {
 			struct configfs_dirent *sd = d->d_fsdata;
 			struct configfs_dirent *sd = d->d_fsdata;
 			if (sd) {
 			if (sd) {
@@ -371,9 +368,7 @@ int configfs_create_link(struct configfs_symlink *sl,
 				   CONFIGFS_ITEM_LINK);
 				   CONFIGFS_ITEM_LINK);
 	if (!err) {
 	if (!err) {
 		err = configfs_create(dentry, mode, init_symlink);
 		err = configfs_create(dentry, mode, init_symlink);
-		if (!err)
-			dentry->d_op = &configfs_dentry_ops;
-		else {
+		if (err) {
 			struct configfs_dirent *sd = dentry->d_fsdata;
 			struct configfs_dirent *sd = dentry->d_fsdata;
 			if (sd) {
 			if (sd) {
 				spin_lock(&configfs_dirent_lock);
 				spin_lock(&configfs_dirent_lock);
@@ -399,8 +394,7 @@ static void remove_dir(struct dentry * d)
 	if (d->d_inode)
 	if (d->d_inode)
 		simple_rmdir(parent->d_inode,d);
 		simple_rmdir(parent->d_inode,d);
 
 
-	pr_debug(" o %s removing done (%d)\n",d->d_name.name,
-		 atomic_read(&d->d_count));
+	pr_debug(" o %s removing done (%d)\n",d->d_name.name, d->d_count);
 
 
 	dput(parent);
 	dput(parent);
 }
 }
@@ -448,7 +442,7 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
 		return error;
 		return error;
 	}
 	}
 
 
-	dentry->d_op = &configfs_dentry_ops;
+	d_set_d_op(dentry, &configfs_dentry_ops);
 	d_rehash(dentry);
 	d_rehash(dentry);
 
 
 	return 0;
 	return 0;
@@ -493,7 +487,11 @@ static struct dentry * configfs_lookup(struct inode *dir,
 		 * If it doesn't exist and it isn't a NOT_PINNED item,
 		 * If it doesn't exist and it isn't a NOT_PINNED item,
 		 * it must be negative.
 		 * it must be negative.
 		 */
 		 */
-		return simple_lookup(dir, dentry, nd);
+		if (dentry->d_name.len > NAME_MAX)
+			return ERR_PTR(-ENAMETOOLONG);
+		d_set_d_op(dentry, &configfs_dentry_ops);
+		d_add(dentry, NULL);
+		return NULL;
 	}
 	}
 
 
 out:
 out:
@@ -685,6 +683,7 @@ static int create_default_group(struct config_group *parent_group,
 	ret = -ENOMEM;
 	ret = -ENOMEM;
 	child = d_alloc(parent, &name);
 	child = d_alloc(parent, &name);
 	if (child) {
 	if (child) {
+		d_set_d_op(child, &configfs_dentry_ops);
 		d_add(child, NULL);
 		d_add(child, NULL);
 
 
 		ret = configfs_attach_group(&parent_group->cg_item,
 		ret = configfs_attach_group(&parent_group->cg_item,
@@ -1682,6 +1681,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 	err = -ENOMEM;
 	err = -ENOMEM;
 	dentry = d_alloc(configfs_sb->s_root, &name);
 	dentry = d_alloc(configfs_sb->s_root, &name);
 	if (dentry) {
 	if (dentry) {
+		d_set_d_op(dentry, &configfs_dentry_ops);
 		d_add(dentry, NULL);
 		d_add(dentry, NULL);
 
 
 		err = configfs_attach_group(sd->s_element, &group->cg_item,
 		err = configfs_attach_group(sd->s_element, &group->cg_item,

+ 2 - 6
fs/configfs/inode.c

@@ -250,18 +250,14 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
 	struct dentry * dentry = sd->s_dentry;
 	struct dentry * dentry = sd->s_dentry;
 
 
 	if (dentry) {
 	if (dentry) {
-		spin_lock(&dcache_lock);
 		spin_lock(&dentry->d_lock);
 		spin_lock(&dentry->d_lock);
 		if (!(d_unhashed(dentry) && dentry->d_inode)) {
 		if (!(d_unhashed(dentry) && dentry->d_inode)) {
-			dget_locked(dentry);
+			dget_dlock(dentry);
 			__d_drop(dentry);
 			__d_drop(dentry);
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
-			spin_unlock(&dcache_lock);
 			simple_unlink(parent->d_inode, dentry);
 			simple_unlink(parent->d_inode, dentry);
-		} else {
+		} else
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
-			spin_unlock(&dcache_lock);
-		}
 	}
 	}
 }
 }
 
 

+ 981 - 394
fs/dcache.c

@@ -33,20 +33,58 @@
 #include <linux/bootmem.h>
 #include <linux/bootmem.h>
 #include <linux/fs_struct.h>
 #include <linux/fs_struct.h>
 #include <linux/hardirq.h>
 #include <linux/hardirq.h>
+#include <linux/bit_spinlock.h>
+#include <linux/rculist_bl.h>
 #include "internal.h"
 #include "internal.h"
 
 
+/*
+ * Usage:
+ * dcache->d_inode->i_lock protects:
+ *   - i_dentry, d_alias, d_inode of aliases
+ * dcache_hash_bucket lock protects:
+ *   - the dcache hash table
+ * s_anon bl list spinlock protects:
+ *   - the s_anon list (see __d_drop)
+ * dcache_lru_lock protects:
+ *   - the dcache lru lists and counters
+ * d_lock protects:
+ *   - d_flags
+ *   - d_name
+ *   - d_lru
+ *   - d_count
+ *   - d_unhashed()
+ *   - d_parent and d_subdirs
+ *   - childrens' d_child and d_parent
+ *   - d_alias, d_inode
+ *
+ * Ordering:
+ * dentry->d_inode->i_lock
+ *   dentry->d_lock
+ *     dcache_lru_lock
+ *     dcache_hash_bucket lock
+ *     s_anon lock
+ *
+ * If there is an ancestor relationship:
+ * dentry->d_parent->...->d_parent->d_lock
+ *   ...
+ *     dentry->d_parent->d_lock
+ *       dentry->d_lock
+ *
+ * If no ancestor relationship:
+ * if (dentry1 < dentry2)
+ *   dentry1->d_lock
+ *     dentry2->d_lock
+ */
 int sysctl_vfs_cache_pressure __read_mostly = 100;
 int sysctl_vfs_cache_pressure __read_mostly = 100;
 EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
 EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
 
 
- __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock);
+static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lru_lock);
 __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
 __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
 
 
-EXPORT_SYMBOL(dcache_lock);
+EXPORT_SYMBOL(rename_lock);
 
 
 static struct kmem_cache *dentry_cache __read_mostly;
 static struct kmem_cache *dentry_cache __read_mostly;
 
 
-#define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname))
-
 /*
 /*
  * This is the single most critical data structure when it comes
  * This is the single most critical data structure when it comes
  * to the dcache: the hashtable for lookups. Somebody should try
  * to the dcache: the hashtable for lookups. Somebody should try
@@ -60,22 +98,51 @@ static struct kmem_cache *dentry_cache __read_mostly;
 
 
 static unsigned int d_hash_mask __read_mostly;
 static unsigned int d_hash_mask __read_mostly;
 static unsigned int d_hash_shift __read_mostly;
 static unsigned int d_hash_shift __read_mostly;
-static struct hlist_head *dentry_hashtable __read_mostly;
+
+struct dcache_hash_bucket {
+	struct hlist_bl_head head;
+};
+static struct dcache_hash_bucket *dentry_hashtable __read_mostly;
+
+static inline struct dcache_hash_bucket *d_hash(struct dentry *parent,
+					unsigned long hash)
+{
+	hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;
+	hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);
+	return dentry_hashtable + (hash & D_HASHMASK);
+}
+
+static inline void spin_lock_bucket(struct dcache_hash_bucket *b)
+{
+	bit_spin_lock(0, (unsigned long *)&b->head.first);
+}
+
+static inline void spin_unlock_bucket(struct dcache_hash_bucket *b)
+{
+	__bit_spin_unlock(0, (unsigned long *)&b->head.first);
+}
 
 
 /* Statistics gathering. */
 /* Statistics gathering. */
 struct dentry_stat_t dentry_stat = {
 struct dentry_stat_t dentry_stat = {
 	.age_limit = 45,
 	.age_limit = 45,
 };
 };
 
 
-static struct percpu_counter nr_dentry __cacheline_aligned_in_smp;
-static struct percpu_counter nr_dentry_unused __cacheline_aligned_in_smp;
+static DEFINE_PER_CPU(unsigned int, nr_dentry);
 
 
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
+static int get_nr_dentry(void)
+{
+	int i;
+	int sum = 0;
+	for_each_possible_cpu(i)
+		sum += per_cpu(nr_dentry, i);
+	return sum < 0 ? 0 : sum;
+}
+
 int proc_nr_dentry(ctl_table *table, int write, void __user *buffer,
 int proc_nr_dentry(ctl_table *table, int write, void __user *buffer,
 		   size_t *lenp, loff_t *ppos)
 		   size_t *lenp, loff_t *ppos)
 {
 {
-	dentry_stat.nr_dentry = percpu_counter_sum_positive(&nr_dentry);
-	dentry_stat.nr_unused = percpu_counter_sum_positive(&nr_dentry_unused);
+	dentry_stat.nr_dentry = get_nr_dentry();
 	return proc_dointvec(table, write, buffer, lenp, ppos);
 	return proc_dointvec(table, write, buffer, lenp, ppos);
 }
 }
 #endif
 #endif
@@ -91,35 +158,50 @@ static void __d_free(struct rcu_head *head)
 }
 }
 
 
 /*
 /*
- * no dcache_lock, please.
+ * no locks, please.
  */
  */
 static void d_free(struct dentry *dentry)
 static void d_free(struct dentry *dentry)
 {
 {
-	percpu_counter_dec(&nr_dentry);
+	BUG_ON(dentry->d_count);
+	this_cpu_dec(nr_dentry);
 	if (dentry->d_op && dentry->d_op->d_release)
 	if (dentry->d_op && dentry->d_op->d_release)
 		dentry->d_op->d_release(dentry);
 		dentry->d_op->d_release(dentry);
 
 
 	/* if dentry was never inserted into hash, immediate free is OK */
 	/* if dentry was never inserted into hash, immediate free is OK */
-	if (hlist_unhashed(&dentry->d_hash))
+	if (hlist_bl_unhashed(&dentry->d_hash))
 		__d_free(&dentry->d_u.d_rcu);
 		__d_free(&dentry->d_u.d_rcu);
 	else
 	else
 		call_rcu(&dentry->d_u.d_rcu, __d_free);
 		call_rcu(&dentry->d_u.d_rcu, __d_free);
 }
 }
 
 
+/**
+ * dentry_rcuwalk_barrier - invalidate in-progress rcu-walk lookups
+ * After this call, in-progress rcu-walk path lookup will fail. This
+ * should be called after unhashing, and after changing d_inode (if
+ * the dentry has not already been unhashed).
+ */
+static inline void dentry_rcuwalk_barrier(struct dentry *dentry)
+{
+	assert_spin_locked(&dentry->d_lock);
+	/* Go through a barrier */
+	write_seqcount_barrier(&dentry->d_seq);
+}
+
 /*
 /*
  * Release the dentry's inode, using the filesystem
  * Release the dentry's inode, using the filesystem
- * d_iput() operation if defined.
+ * d_iput() operation if defined. Dentry has no refcount
+ * and is unhashed.
  */
  */
 static void dentry_iput(struct dentry * dentry)
 static void dentry_iput(struct dentry * dentry)
 	__releases(dentry->d_lock)
 	__releases(dentry->d_lock)
-	__releases(dcache_lock)
+	__releases(dentry->d_inode->i_lock)
 {
 {
 	struct inode *inode = dentry->d_inode;
 	struct inode *inode = dentry->d_inode;
 	if (inode) {
 	if (inode) {
 		dentry->d_inode = NULL;
 		dentry->d_inode = NULL;
 		list_del_init(&dentry->d_alias);
 		list_del_init(&dentry->d_alias);
 		spin_unlock(&dentry->d_lock);
 		spin_unlock(&dentry->d_lock);
-		spin_unlock(&dcache_lock);
+		spin_unlock(&inode->i_lock);
 		if (!inode->i_nlink)
 		if (!inode->i_nlink)
 			fsnotify_inoderemove(inode);
 			fsnotify_inoderemove(inode);
 		if (dentry->d_op && dentry->d_op->d_iput)
 		if (dentry->d_op && dentry->d_op->d_iput)
@@ -128,40 +210,72 @@ static void dentry_iput(struct dentry * dentry)
 			iput(inode);
 			iput(inode);
 	} else {
 	} else {
 		spin_unlock(&dentry->d_lock);
 		spin_unlock(&dentry->d_lock);
-		spin_unlock(&dcache_lock);
 	}
 	}
 }
 }
 
 
 /*
 /*
- * dentry_lru_(add|del|move_tail) must be called with dcache_lock held.
+ * Release the dentry's inode, using the filesystem
+ * d_iput() operation if defined. dentry remains in-use.
+ */
+static void dentry_unlink_inode(struct dentry * dentry)
+	__releases(dentry->d_lock)
+	__releases(dentry->d_inode->i_lock)
+{
+	struct inode *inode = dentry->d_inode;
+	dentry->d_inode = NULL;
+	list_del_init(&dentry->d_alias);
+	dentry_rcuwalk_barrier(dentry);
+	spin_unlock(&dentry->d_lock);
+	spin_unlock(&inode->i_lock);
+	if (!inode->i_nlink)
+		fsnotify_inoderemove(inode);
+	if (dentry->d_op && dentry->d_op->d_iput)
+		dentry->d_op->d_iput(dentry, inode);
+	else
+		iput(inode);
+}
+
+/*
+ * dentry_lru_(add|del|move_tail) must be called with d_lock held.
  */
  */
 static void dentry_lru_add(struct dentry *dentry)
 static void dentry_lru_add(struct dentry *dentry)
 {
 {
 	if (list_empty(&dentry->d_lru)) {
 	if (list_empty(&dentry->d_lru)) {
+		spin_lock(&dcache_lru_lock);
 		list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 		list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 		dentry->d_sb->s_nr_dentry_unused++;
 		dentry->d_sb->s_nr_dentry_unused++;
-		percpu_counter_inc(&nr_dentry_unused);
+		dentry_stat.nr_unused++;
+		spin_unlock(&dcache_lru_lock);
 	}
 	}
 }
 }
 
 
+static void __dentry_lru_del(struct dentry *dentry)
+{
+	list_del_init(&dentry->d_lru);
+	dentry->d_sb->s_nr_dentry_unused--;
+	dentry_stat.nr_unused--;
+}
+
 static void dentry_lru_del(struct dentry *dentry)
 static void dentry_lru_del(struct dentry *dentry)
 {
 {
 	if (!list_empty(&dentry->d_lru)) {
 	if (!list_empty(&dentry->d_lru)) {
-		list_del_init(&dentry->d_lru);
-		dentry->d_sb->s_nr_dentry_unused--;
-		percpu_counter_dec(&nr_dentry_unused);
+		spin_lock(&dcache_lru_lock);
+		__dentry_lru_del(dentry);
+		spin_unlock(&dcache_lru_lock);
 	}
 	}
 }
 }
 
 
 static void dentry_lru_move_tail(struct dentry *dentry)
 static void dentry_lru_move_tail(struct dentry *dentry)
 {
 {
+	spin_lock(&dcache_lru_lock);
 	if (list_empty(&dentry->d_lru)) {
 	if (list_empty(&dentry->d_lru)) {
 		list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 		list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 		dentry->d_sb->s_nr_dentry_unused++;
 		dentry->d_sb->s_nr_dentry_unused++;
-		percpu_counter_inc(&nr_dentry_unused);
+		dentry_stat.nr_unused++;
 	} else {
 	} else {
 		list_move_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 		list_move_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
 	}
 	}
+	spin_unlock(&dcache_lru_lock);
 }
 }
 
 
 /**
 /**
@@ -171,22 +285,115 @@ static void dentry_lru_move_tail(struct dentry *dentry)
  * The dentry must already be unhashed and removed from the LRU.
  * The dentry must already be unhashed and removed from the LRU.
  *
  *
  * If this is the root of the dentry tree, return NULL.
  * If this is the root of the dentry tree, return NULL.
+ *
+ * dentry->d_lock and parent->d_lock must be held by caller, and are dropped by
+ * d_kill.
  */
  */
-static struct dentry *d_kill(struct dentry *dentry)
+static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
 	__releases(dentry->d_lock)
 	__releases(dentry->d_lock)
-	__releases(dcache_lock)
+	__releases(parent->d_lock)
+	__releases(dentry->d_inode->i_lock)
 {
 {
-	struct dentry *parent;
-
+	dentry->d_parent = NULL;
 	list_del(&dentry->d_u.d_child);
 	list_del(&dentry->d_u.d_child);
-	/*drops the locks, at that point nobody can reach this dentry */
+	if (parent)
+		spin_unlock(&parent->d_lock);
 	dentry_iput(dentry);
 	dentry_iput(dentry);
+	/*
+	 * dentry_iput drops the locks, at which point nobody (except
+	 * transient RCU lookups) can reach this dentry.
+	 */
+	d_free(dentry);
+	return parent;
+}
+
+/**
+ * d_drop - drop a dentry
+ * @dentry: dentry to drop
+ *
+ * d_drop() unhashes the entry from the parent dentry hashes, so that it won't
+ * be found through a VFS lookup any more. Note that this is different from
+ * deleting the dentry - d_delete will try to mark the dentry negative if
+ * possible, giving a successful _negative_ lookup, while d_drop will
+ * just make the cache lookup fail.
+ *
+ * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
+ * reason (NFS timeouts or autofs deletes).
+ *
+ * __d_drop requires dentry->d_lock.
+ */
+void __d_drop(struct dentry *dentry)
+{
+	if (!(dentry->d_flags & DCACHE_UNHASHED)) {
+		if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED)) {
+			bit_spin_lock(0,
+				(unsigned long *)&dentry->d_sb->s_anon.first);
+			dentry->d_flags |= DCACHE_UNHASHED;
+			hlist_bl_del_init(&dentry->d_hash);
+			__bit_spin_unlock(0,
+				(unsigned long *)&dentry->d_sb->s_anon.first);
+		} else {
+			struct dcache_hash_bucket *b;
+			b = d_hash(dentry->d_parent, dentry->d_name.hash);
+			spin_lock_bucket(b);
+			/*
+			 * We may not actually need to put DCACHE_UNHASHED
+			 * manipulations under the hash lock, but follow
+			 * the principle of least surprise.
+			 */
+			dentry->d_flags |= DCACHE_UNHASHED;
+			hlist_bl_del_rcu(&dentry->d_hash);
+			spin_unlock_bucket(b);
+			dentry_rcuwalk_barrier(dentry);
+		}
+	}
+}
+EXPORT_SYMBOL(__d_drop);
+
+void d_drop(struct dentry *dentry)
+{
+	spin_lock(&dentry->d_lock);
+	__d_drop(dentry);
+	spin_unlock(&dentry->d_lock);
+}
+EXPORT_SYMBOL(d_drop);
+
+/*
+ * Finish off a dentry we've decided to kill.
+ * dentry->d_lock must be held, returns with it unlocked.
+ * If ref is non-zero, then decrement the refcount too.
+ * Returns dentry requiring refcount drop, or NULL if we're done.
+ */
+static inline struct dentry *dentry_kill(struct dentry *dentry, int ref)
+	__releases(dentry->d_lock)
+{
+	struct inode *inode;
+	struct dentry *parent;
+
+	inode = dentry->d_inode;
+	if (inode && !spin_trylock(&inode->i_lock)) {
+relock:
+		spin_unlock(&dentry->d_lock);
+		cpu_relax();
+		return dentry; /* try again with same dentry */
+	}
 	if (IS_ROOT(dentry))
 	if (IS_ROOT(dentry))
 		parent = NULL;
 		parent = NULL;
 	else
 	else
 		parent = dentry->d_parent;
 		parent = dentry->d_parent;
-	d_free(dentry);
-	return parent;
+	if (parent && !spin_trylock(&parent->d_lock)) {
+		if (inode)
+			spin_unlock(&inode->i_lock);
+		goto relock;
+	}
+
+	if (ref)
+		dentry->d_count--;
+	/* if dentry was on the d_lru list delete it from there */
+	dentry_lru_del(dentry);
+	/* if it was on the hash then remove it */
+	__d_drop(dentry);
+	return d_kill(dentry, parent);
 }
 }
 
 
 /* 
 /* 
@@ -214,34 +421,26 @@ static struct dentry *d_kill(struct dentry *dentry)
  * call the dentry unlink method as well as removing it from the queues and
  * call the dentry unlink method as well as removing it from the queues and
  * releasing its resources. If the parent dentries were scheduled for release
  * releasing its resources. If the parent dentries were scheduled for release
  * they too may now get deleted.
  * they too may now get deleted.
- *
- * no dcache lock, please.
  */
  */
-
 void dput(struct dentry *dentry)
 void dput(struct dentry *dentry)
 {
 {
 	if (!dentry)
 	if (!dentry)
 		return;
 		return;
 
 
 repeat:
 repeat:
-	if (atomic_read(&dentry->d_count) == 1)
+	if (dentry->d_count == 1)
 		might_sleep();
 		might_sleep();
-	if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
-		return;
-
 	spin_lock(&dentry->d_lock);
 	spin_lock(&dentry->d_lock);
-	if (atomic_read(&dentry->d_count)) {
+	BUG_ON(!dentry->d_count);
+	if (dentry->d_count > 1) {
+		dentry->d_count--;
 		spin_unlock(&dentry->d_lock);
 		spin_unlock(&dentry->d_lock);
-		spin_unlock(&dcache_lock);
 		return;
 		return;
 	}
 	}
 
 
-	/*
-	 * AV: ->d_delete() is _NOT_ allowed to block now.
-	 */
-	if (dentry->d_op && dentry->d_op->d_delete) {
+	if (dentry->d_flags & DCACHE_OP_DELETE) {
 		if (dentry->d_op->d_delete(dentry))
 		if (dentry->d_op->d_delete(dentry))
-			goto unhash_it;
+			goto kill_it;
 	}
 	}
 
 
 	/* Unreachable? Get rid of it */
 	/* Unreachable? Get rid of it */
@@ -252,16 +451,12 @@ repeat:
 	dentry->d_flags |= DCACHE_REFERENCED;
 	dentry->d_flags |= DCACHE_REFERENCED;
 	dentry_lru_add(dentry);
 	dentry_lru_add(dentry);
 
 
- 	spin_unlock(&dentry->d_lock);
-	spin_unlock(&dcache_lock);
+	dentry->d_count--;
+	spin_unlock(&dentry->d_lock);
 	return;
 	return;
 
 
-unhash_it:
-	__d_drop(dentry);
 kill_it:
 kill_it:
-	/* if dentry was on the d_lru list delete it from there */
-	dentry_lru_del(dentry);
-	dentry = d_kill(dentry);
+	dentry = dentry_kill(dentry, 1);
 	if (dentry)
 	if (dentry)
 		goto repeat;
 		goto repeat;
 }
 }
@@ -284,9 +479,9 @@ int d_invalidate(struct dentry * dentry)
 	/*
 	/*
 	 * If it's already been dropped, return OK.
 	 * If it's already been dropped, return OK.
 	 */
 	 */
-	spin_lock(&dcache_lock);
+	spin_lock(&dentry->d_lock);
 	if (d_unhashed(dentry)) {
 	if (d_unhashed(dentry)) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
 		return 0;
 		return 0;
 	}
 	}
 	/*
 	/*
@@ -294,9 +489,9 @@ int d_invalidate(struct dentry * dentry)
 	 * to get rid of unused child entries.
 	 * to get rid of unused child entries.
 	 */
 	 */
 	if (!list_empty(&dentry->d_subdirs)) {
 	if (!list_empty(&dentry->d_subdirs)) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&dentry->d_lock);
 		shrink_dcache_parent(dentry);
 		shrink_dcache_parent(dentry);
-		spin_lock(&dcache_lock);
+		spin_lock(&dentry->d_lock);
 	}
 	}
 
 
 	/*
 	/*
@@ -309,35 +504,61 @@ int d_invalidate(struct dentry * dentry)
 	 * we might still populate it if it was a
 	 * we might still populate it if it was a
 	 * working directory or similar).
 	 * working directory or similar).
 	 */
 	 */
-	spin_lock(&dentry->d_lock);
-	if (atomic_read(&dentry->d_count) > 1) {
+	if (dentry->d_count > 1) {
 		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
 		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
-			spin_unlock(&dcache_lock);
 			return -EBUSY;
 			return -EBUSY;
 		}
 		}
 	}
 	}
 
 
 	__d_drop(dentry);
 	__d_drop(dentry);
 	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dentry->d_lock);
-	spin_unlock(&dcache_lock);
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL(d_invalidate);
 EXPORT_SYMBOL(d_invalidate);
 
 
-/* This should be called _only_ with dcache_lock held */
-static inline struct dentry * __dget_locked(struct dentry *dentry)
+/* This must be called with d_lock held */
+static inline void __dget_dlock(struct dentry *dentry)
 {
 {
-	atomic_inc(&dentry->d_count);
-	dentry_lru_del(dentry);
-	return dentry;
+	dentry->d_count++;
 }
 }
 
 
-struct dentry * dget_locked(struct dentry *dentry)
+static inline void __dget(struct dentry *dentry)
 {
 {
-	return __dget_locked(dentry);
+	spin_lock(&dentry->d_lock);
+	__dget_dlock(dentry);
+	spin_unlock(&dentry->d_lock);
+}
+
+struct dentry *dget_parent(struct dentry *dentry)
+{
+	struct dentry *ret;
+
+repeat:
+	/*
+	 * Don't need rcu_dereference because we re-check it was correct under
+	 * the lock.
+	 */
+	rcu_read_lock();
+	ret = dentry->d_parent;
+	if (!ret) {
+		rcu_read_unlock();
+		goto out;
+	}
+	spin_lock(&ret->d_lock);
+	if (unlikely(ret != dentry->d_parent)) {
+		spin_unlock(&ret->d_lock);
+		rcu_read_unlock();
+		goto repeat;
+	}
+	rcu_read_unlock();
+	BUG_ON(!ret->d_count);
+	ret->d_count++;
+	spin_unlock(&ret->d_lock);
+out:
+	return ret;
 }
 }
-EXPORT_SYMBOL(dget_locked);
+EXPORT_SYMBOL(dget_parent);
 
 
 /**
 /**
  * d_find_alias - grab a hashed alias of inode
  * d_find_alias - grab a hashed alias of inode
@@ -355,42 +576,51 @@ EXPORT_SYMBOL(dget_locked);
  * any other hashed alias over that one unless @want_discon is set,
  * any other hashed alias over that one unless @want_discon is set,
  * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
  * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
  */
  */
-
-static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
+static struct dentry *__d_find_alias(struct inode *inode, int want_discon)
 {
 {
-	struct list_head *head, *next, *tmp;
-	struct dentry *alias, *discon_alias=NULL;
+	struct dentry *alias, *discon_alias;
 
 
-	head = &inode->i_dentry;
-	next = inode->i_dentry.next;
-	while (next != head) {
-		tmp = next;
-		next = tmp->next;
-		prefetch(next);
-		alias = list_entry(tmp, struct dentry, d_alias);
+again:
+	discon_alias = NULL;
+	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
+		spin_lock(&alias->d_lock);
  		if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
  		if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
 			if (IS_ROOT(alias) &&
 			if (IS_ROOT(alias) &&
-			    (alias->d_flags & DCACHE_DISCONNECTED))
+			    (alias->d_flags & DCACHE_DISCONNECTED)) {
 				discon_alias = alias;
 				discon_alias = alias;
-			else if (!want_discon) {
-				__dget_locked(alias);
+			} else if (!want_discon) {
+				__dget_dlock(alias);
+				spin_unlock(&alias->d_lock);
+				return alias;
+			}
+		}
+		spin_unlock(&alias->d_lock);
+	}
+	if (discon_alias) {
+		alias = discon_alias;
+		spin_lock(&alias->d_lock);
+		if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
+			if (IS_ROOT(alias) &&
+			    (alias->d_flags & DCACHE_DISCONNECTED)) {
+				__dget_dlock(alias);
+				spin_unlock(&alias->d_lock);
 				return alias;
 				return alias;
 			}
 			}
 		}
 		}
+		spin_unlock(&alias->d_lock);
+		goto again;
 	}
 	}
-	if (discon_alias)
-		__dget_locked(discon_alias);
-	return discon_alias;
+	return NULL;
 }
 }
 
 
-struct dentry * d_find_alias(struct inode *inode)
+struct dentry *d_find_alias(struct inode *inode)
 {
 {
 	struct dentry *de = NULL;
 	struct dentry *de = NULL;
 
 
 	if (!list_empty(&inode->i_dentry)) {
 	if (!list_empty(&inode->i_dentry)) {
-		spin_lock(&dcache_lock);
+		spin_lock(&inode->i_lock);
 		de = __d_find_alias(inode, 0);
 		de = __d_find_alias(inode, 0);
-		spin_unlock(&dcache_lock);
+		spin_unlock(&inode->i_lock);
 	}
 	}
 	return de;
 	return de;
 }
 }
@@ -404,54 +634,61 @@ void d_prune_aliases(struct inode *inode)
 {
 {
 	struct dentry *dentry;
 	struct dentry *dentry;
 restart:
 restart:
-	spin_lock(&dcache_lock);
+	spin_lock(&inode->i_lock);
 	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
 	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
 		spin_lock(&dentry->d_lock);
 		spin_lock(&dentry->d_lock);
-		if (!atomic_read(&dentry->d_count)) {
-			__dget_locked(dentry);
+		if (!dentry->d_count) {
+			__dget_dlock(dentry);
 			__d_drop(dentry);
 			__d_drop(dentry);
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
-			spin_unlock(&dcache_lock);
+			spin_unlock(&inode->i_lock);
 			dput(dentry);
 			dput(dentry);
 			goto restart;
 			goto restart;
 		}
 		}
 		spin_unlock(&dentry->d_lock);
 		spin_unlock(&dentry->d_lock);
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 }
 }
 EXPORT_SYMBOL(d_prune_aliases);
 EXPORT_SYMBOL(d_prune_aliases);
 
 
 /*
 /*
- * Throw away a dentry - free the inode, dput the parent.  This requires that
- * the LRU list has already been removed.
+ * Try to throw away a dentry - free the inode, dput the parent.
+ * Requires dentry->d_lock is held, and dentry->d_count == 0.
+ * Releases dentry->d_lock.
  *
  *
- * Try to prune ancestors as well.  This is necessary to prevent
- * quadratic behavior of shrink_dcache_parent(), but is also expected
- * to be beneficial in reducing dentry cache fragmentation.
+ * This may fail if locks cannot be acquired no problem, just try again.
  */
  */
-static void prune_one_dentry(struct dentry * dentry)
+static void try_prune_one_dentry(struct dentry *dentry)
 	__releases(dentry->d_lock)
 	__releases(dentry->d_lock)
-	__releases(dcache_lock)
-	__acquires(dcache_lock)
 {
 {
-	__d_drop(dentry);
-	dentry = d_kill(dentry);
+	struct dentry *parent;
 
 
+	parent = dentry_kill(dentry, 0);
 	/*
 	/*
-	 * Prune ancestors.  Locking is simpler than in dput(),
-	 * because dcache_lock needs to be taken anyway.
+	 * If dentry_kill returns NULL, we have nothing more to do.
+	 * if it returns the same dentry, trylocks failed. In either
+	 * case, just loop again.
+	 *
+	 * Otherwise, we need to prune ancestors too. This is necessary
+	 * to prevent quadratic behavior of shrink_dcache_parent(), but
+	 * is also expected to be beneficial in reducing dentry cache
+	 * fragmentation.
 	 */
 	 */
-	spin_lock(&dcache_lock);
+	if (!parent)
+		return;
+	if (parent == dentry)
+		return;
+
+	/* Prune ancestors. */
+	dentry = parent;
 	while (dentry) {
 	while (dentry) {
-		if (!atomic_dec_and_lock(&dentry->d_count, &dentry->d_lock))
+		spin_lock(&dentry->d_lock);
+		if (dentry->d_count > 1) {
+			dentry->d_count--;
+			spin_unlock(&dentry->d_lock);
 			return;
 			return;
-
-		if (dentry->d_op && dentry->d_op->d_delete)
-			dentry->d_op->d_delete(dentry);
-		dentry_lru_del(dentry);
-		__d_drop(dentry);
-		dentry = d_kill(dentry);
-		spin_lock(&dcache_lock);
+		}
+		dentry = dentry_kill(dentry, 1);
 	}
 	}
 }
 }
 
 
@@ -459,24 +696,35 @@ static void shrink_dentry_list(struct list_head *list)
 {
 {
 	struct dentry *dentry;
 	struct dentry *dentry;
 
 
-	while (!list_empty(list)) {
-		dentry = list_entry(list->prev, struct dentry, d_lru);
-		dentry_lru_del(dentry);
+	rcu_read_lock();
+	for (;;) {
+		dentry = list_entry_rcu(list->prev, struct dentry, d_lru);
+		if (&dentry->d_lru == list)
+			break; /* empty */
+		spin_lock(&dentry->d_lock);
+		if (dentry != list_entry(list->prev, struct dentry, d_lru)) {
+			spin_unlock(&dentry->d_lock);
+			continue;
+		}
 
 
 		/*
 		/*
 		 * We found an inuse dentry which was not removed from
 		 * We found an inuse dentry which was not removed from
 		 * the LRU because of laziness during lookup.  Do not free
 		 * the LRU because of laziness during lookup.  Do not free
 		 * it - just keep it off the LRU list.
 		 * it - just keep it off the LRU list.
 		 */
 		 */
-		spin_lock(&dentry->d_lock);
-		if (atomic_read(&dentry->d_count)) {
+		if (dentry->d_count) {
+			dentry_lru_del(dentry);
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
 			continue;
 			continue;
 		}
 		}
-		prune_one_dentry(dentry);
-		/* dentry->d_lock was dropped in prune_one_dentry() */
-		cond_resched_lock(&dcache_lock);
+
+		rcu_read_unlock();
+
+		try_prune_one_dentry(dentry);
+
+		rcu_read_lock();
 	}
 	}
+	rcu_read_unlock();
 }
 }
 
 
 /**
 /**
@@ -495,42 +743,44 @@ static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags)
 	LIST_HEAD(tmp);
 	LIST_HEAD(tmp);
 	int cnt = *count;
 	int cnt = *count;
 
 
-	spin_lock(&dcache_lock);
+relock:
+	spin_lock(&dcache_lru_lock);
 	while (!list_empty(&sb->s_dentry_lru)) {
 	while (!list_empty(&sb->s_dentry_lru)) {
 		dentry = list_entry(sb->s_dentry_lru.prev,
 		dentry = list_entry(sb->s_dentry_lru.prev,
 				struct dentry, d_lru);
 				struct dentry, d_lru);
 		BUG_ON(dentry->d_sb != sb);
 		BUG_ON(dentry->d_sb != sb);
 
 
+		if (!spin_trylock(&dentry->d_lock)) {
+			spin_unlock(&dcache_lru_lock);
+			cpu_relax();
+			goto relock;
+		}
+
 		/*
 		/*
 		 * If we are honouring the DCACHE_REFERENCED flag and the
 		 * If we are honouring the DCACHE_REFERENCED flag and the
 		 * dentry has this flag set, don't free it.  Clear the flag
 		 * dentry has this flag set, don't free it.  Clear the flag
 		 * and put it back on the LRU.
 		 * and put it back on the LRU.
 		 */
 		 */
-		if (flags & DCACHE_REFERENCED) {
-			spin_lock(&dentry->d_lock);
-			if (dentry->d_flags & DCACHE_REFERENCED) {
-				dentry->d_flags &= ~DCACHE_REFERENCED;
-				list_move(&dentry->d_lru, &referenced);
-				spin_unlock(&dentry->d_lock);
-				cond_resched_lock(&dcache_lock);
-				continue;
-			}
+		if (flags & DCACHE_REFERENCED &&
+				dentry->d_flags & DCACHE_REFERENCED) {
+			dentry->d_flags &= ~DCACHE_REFERENCED;
+			list_move(&dentry->d_lru, &referenced);
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dentry->d_lock);
+		} else {
+			list_move_tail(&dentry->d_lru, &tmp);
+			spin_unlock(&dentry->d_lock);
+			if (!--cnt)
+				break;
 		}
 		}
-
-		list_move_tail(&dentry->d_lru, &tmp);
-		if (!--cnt)
-			break;
-		cond_resched_lock(&dcache_lock);
+		cond_resched_lock(&dcache_lru_lock);
 	}
 	}
-
-	*count = cnt;
-	shrink_dentry_list(&tmp);
-
 	if (!list_empty(&referenced))
 	if (!list_empty(&referenced))
 		list_splice(&referenced, &sb->s_dentry_lru);
 		list_splice(&referenced, &sb->s_dentry_lru);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dcache_lru_lock);
 
 
+	shrink_dentry_list(&tmp);
+
+	*count = cnt;
 }
 }
 
 
 /**
 /**
@@ -546,13 +796,12 @@ static void prune_dcache(int count)
 {
 {
 	struct super_block *sb, *p = NULL;
 	struct super_block *sb, *p = NULL;
 	int w_count;
 	int w_count;
-	int unused = percpu_counter_sum_positive(&nr_dentry_unused);
+	int unused = dentry_stat.nr_unused;
 	int prune_ratio;
 	int prune_ratio;
 	int pruned;
 	int pruned;
 
 
 	if (unused == 0 || count == 0)
 	if (unused == 0 || count == 0)
 		return;
 		return;
-	spin_lock(&dcache_lock);
 	if (count >= unused)
 	if (count >= unused)
 		prune_ratio = 1;
 		prune_ratio = 1;
 	else
 	else
@@ -589,11 +838,9 @@ static void prune_dcache(int count)
 		if (down_read_trylock(&sb->s_umount)) {
 		if (down_read_trylock(&sb->s_umount)) {
 			if ((sb->s_root != NULL) &&
 			if ((sb->s_root != NULL) &&
 			    (!list_empty(&sb->s_dentry_lru))) {
 			    (!list_empty(&sb->s_dentry_lru))) {
-				spin_unlock(&dcache_lock);
 				__shrink_dcache_sb(sb, &w_count,
 				__shrink_dcache_sb(sb, &w_count,
 						DCACHE_REFERENCED);
 						DCACHE_REFERENCED);
 				pruned -= w_count;
 				pruned -= w_count;
-				spin_lock(&dcache_lock);
 			}
 			}
 			up_read(&sb->s_umount);
 			up_read(&sb->s_umount);
 		}
 		}
@@ -609,7 +856,6 @@ static void prune_dcache(int count)
 	if (p)
 	if (p)
 		__put_super(p);
 		__put_super(p);
 	spin_unlock(&sb_lock);
 	spin_unlock(&sb_lock);
-	spin_unlock(&dcache_lock);
 }
 }
 
 
 /**
 /**
@@ -623,12 +869,14 @@ void shrink_dcache_sb(struct super_block *sb)
 {
 {
 	LIST_HEAD(tmp);
 	LIST_HEAD(tmp);
 
 
-	spin_lock(&dcache_lock);
+	spin_lock(&dcache_lru_lock);
 	while (!list_empty(&sb->s_dentry_lru)) {
 	while (!list_empty(&sb->s_dentry_lru)) {
 		list_splice_init(&sb->s_dentry_lru, &tmp);
 		list_splice_init(&sb->s_dentry_lru, &tmp);
+		spin_unlock(&dcache_lru_lock);
 		shrink_dentry_list(&tmp);
 		shrink_dentry_list(&tmp);
+		spin_lock(&dcache_lru_lock);
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dcache_lru_lock);
 }
 }
 EXPORT_SYMBOL(shrink_dcache_sb);
 EXPORT_SYMBOL(shrink_dcache_sb);
 
 
@@ -645,10 +893,10 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 	BUG_ON(!IS_ROOT(dentry));
 	BUG_ON(!IS_ROOT(dentry));
 
 
 	/* detach this root from the system */
 	/* detach this root from the system */
-	spin_lock(&dcache_lock);
+	spin_lock(&dentry->d_lock);
 	dentry_lru_del(dentry);
 	dentry_lru_del(dentry);
 	__d_drop(dentry);
 	__d_drop(dentry);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&dentry->d_lock);
 
 
 	for (;;) {
 	for (;;) {
 		/* descend to the first leaf in the current subtree */
 		/* descend to the first leaf in the current subtree */
@@ -657,14 +905,16 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 
 
 			/* this is a branch with children - detach all of them
 			/* this is a branch with children - detach all of them
 			 * from the system in one go */
 			 * from the system in one go */
-			spin_lock(&dcache_lock);
+			spin_lock(&dentry->d_lock);
 			list_for_each_entry(loop, &dentry->d_subdirs,
 			list_for_each_entry(loop, &dentry->d_subdirs,
 					    d_u.d_child) {
 					    d_u.d_child) {
+				spin_lock_nested(&loop->d_lock,
+						DENTRY_D_LOCK_NESTED);
 				dentry_lru_del(loop);
 				dentry_lru_del(loop);
 				__d_drop(loop);
 				__d_drop(loop);
-				cond_resched_lock(&dcache_lock);
+				spin_unlock(&loop->d_lock);
 			}
 			}
-			spin_unlock(&dcache_lock);
+			spin_unlock(&dentry->d_lock);
 
 
 			/* move to the first child */
 			/* move to the first child */
 			dentry = list_entry(dentry->d_subdirs.next,
 			dentry = list_entry(dentry->d_subdirs.next,
@@ -676,7 +926,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 		do {
 		do {
 			struct inode *inode;
 			struct inode *inode;
 
 
-			if (atomic_read(&dentry->d_count) != 0) {
+			if (dentry->d_count != 0) {
 				printk(KERN_ERR
 				printk(KERN_ERR
 				       "BUG: Dentry %p{i=%lx,n=%s}"
 				       "BUG: Dentry %p{i=%lx,n=%s}"
 				       " still in use (%d)"
 				       " still in use (%d)"
@@ -685,20 +935,23 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 				       dentry->d_inode ?
 				       dentry->d_inode ?
 				       dentry->d_inode->i_ino : 0UL,
 				       dentry->d_inode->i_ino : 0UL,
 				       dentry->d_name.name,
 				       dentry->d_name.name,
-				       atomic_read(&dentry->d_count),
+				       dentry->d_count,
 				       dentry->d_sb->s_type->name,
 				       dentry->d_sb->s_type->name,
 				       dentry->d_sb->s_id);
 				       dentry->d_sb->s_id);
 				BUG();
 				BUG();
 			}
 			}
 
 
-			if (IS_ROOT(dentry))
+			if (IS_ROOT(dentry)) {
 				parent = NULL;
 				parent = NULL;
-			else {
+				list_del(&dentry->d_u.d_child);
+			} else {
 				parent = dentry->d_parent;
 				parent = dentry->d_parent;
-				atomic_dec(&parent->d_count);
+				spin_lock(&parent->d_lock);
+				parent->d_count--;
+				list_del(&dentry->d_u.d_child);
+				spin_unlock(&parent->d_lock);
 			}
 			}
 
 
-			list_del(&dentry->d_u.d_child);
 			detached++;
 			detached++;
 
 
 			inode = dentry->d_inode;
 			inode = dentry->d_inode;
@@ -728,8 +981,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 
 
 /*
 /*
  * destroy the dentries attached to a superblock on unmounting
  * destroy the dentries attached to a superblock on unmounting
- * - we don't need to use dentry->d_lock, and only need dcache_lock when
- *   removing the dentry from the system lists and hashes because:
+ * - we don't need to use dentry->d_lock because:
  *   - the superblock is detached from all mountings and open files, so the
  *   - the superblock is detached from all mountings and open files, so the
  *     dentry trees will not be rearranged by the VFS
  *     dentry trees will not be rearranged by the VFS
  *   - s_umount is write-locked, so the memory pressure shrinker will ignore
  *   - s_umount is write-locked, so the memory pressure shrinker will ignore
@@ -746,11 +998,13 @@ void shrink_dcache_for_umount(struct super_block *sb)
 
 
 	dentry = sb->s_root;
 	dentry = sb->s_root;
 	sb->s_root = NULL;
 	sb->s_root = NULL;
-	atomic_dec(&dentry->d_count);
+	spin_lock(&dentry->d_lock);
+	dentry->d_count--;
+	spin_unlock(&dentry->d_lock);
 	shrink_dcache_for_umount_subtree(dentry);
 	shrink_dcache_for_umount_subtree(dentry);
 
 
-	while (!hlist_empty(&sb->s_anon)) {
-		dentry = hlist_entry(sb->s_anon.first, struct dentry, d_hash);
+	while (!hlist_bl_empty(&sb->s_anon)) {
+		dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash);
 		shrink_dcache_for_umount_subtree(dentry);
 		shrink_dcache_for_umount_subtree(dentry);
 	}
 	}
 }
 }
@@ -768,15 +1022,20 @@ void shrink_dcache_for_umount(struct super_block *sb)
  * Return true if the parent or its subdirectories contain
  * Return true if the parent or its subdirectories contain
  * a mount point
  * a mount point
  */
  */
- 
 int have_submounts(struct dentry *parent)
 int have_submounts(struct dentry *parent)
 {
 {
-	struct dentry *this_parent = parent;
+	struct dentry *this_parent;
 	struct list_head *next;
 	struct list_head *next;
+	unsigned seq;
+	int locked = 0;
+
+	seq = read_seqbegin(&rename_lock);
+again:
+	this_parent = parent;
 
 
-	spin_lock(&dcache_lock);
 	if (d_mountpoint(parent))
 	if (d_mountpoint(parent))
 		goto positive;
 		goto positive;
+	spin_lock(&this_parent->d_lock);
 repeat:
 repeat:
 	next = this_parent->d_subdirs.next;
 	next = this_parent->d_subdirs.next;
 resume:
 resume:
@@ -784,27 +1043,65 @@ resume:
 		struct list_head *tmp = next;
 		struct list_head *tmp = next;
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
 		next = tmp->next;
 		next = tmp->next;
+
+		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
 		/* Have we found a mount point ? */
 		/* Have we found a mount point ? */
-		if (d_mountpoint(dentry))
+		if (d_mountpoint(dentry)) {
+			spin_unlock(&dentry->d_lock);
+			spin_unlock(&this_parent->d_lock);
 			goto positive;
 			goto positive;
+		}
 		if (!list_empty(&dentry->d_subdirs)) {
 		if (!list_empty(&dentry->d_subdirs)) {
+			spin_unlock(&this_parent->d_lock);
+			spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
 			this_parent = dentry;
 			this_parent = dentry;
+			spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
 			goto repeat;
 			goto repeat;
 		}
 		}
+		spin_unlock(&dentry->d_lock);
 	}
 	}
 	/*
 	/*
 	 * All done at this level ... ascend and resume the search.
 	 * All done at this level ... ascend and resume the search.
 	 */
 	 */
 	if (this_parent != parent) {
 	if (this_parent != parent) {
-		next = this_parent->d_u.d_child.next;
-		this_parent = this_parent->d_parent;
+		struct dentry *tmp;
+		struct dentry *child;
+
+		tmp = this_parent->d_parent;
+		rcu_read_lock();
+		spin_unlock(&this_parent->d_lock);
+		child = this_parent;
+		this_parent = tmp;
+		spin_lock(&this_parent->d_lock);
+		/* might go back up the wrong parent if we have had a rename
+		 * or deletion */
+		if (this_parent != child->d_parent ||
+			 (!locked && read_seqretry(&rename_lock, seq))) {
+			spin_unlock(&this_parent->d_lock);
+			rcu_read_unlock();
+			goto rename_retry;
+		}
+		rcu_read_unlock();
+		next = child->d_u.d_child.next;
 		goto resume;
 		goto resume;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&this_parent->d_lock);
+	if (!locked && read_seqretry(&rename_lock, seq))
+		goto rename_retry;
+	if (locked)
+		write_sequnlock(&rename_lock);
 	return 0; /* No mount points found in tree */
 	return 0; /* No mount points found in tree */
 positive:
 positive:
-	spin_unlock(&dcache_lock);
+	if (!locked && read_seqretry(&rename_lock, seq))
+		goto rename_retry;
+	if (locked)
+		write_sequnlock(&rename_lock);
 	return 1;
 	return 1;
+
+rename_retry:
+	locked = 1;
+	write_seqlock(&rename_lock);
+	goto again;
 }
 }
 EXPORT_SYMBOL(have_submounts);
 EXPORT_SYMBOL(have_submounts);
 
 
@@ -824,11 +1121,16 @@ EXPORT_SYMBOL(have_submounts);
  */
  */
 static int select_parent(struct dentry * parent)
 static int select_parent(struct dentry * parent)
 {
 {
-	struct dentry *this_parent = parent;
+	struct dentry *this_parent;
 	struct list_head *next;
 	struct list_head *next;
+	unsigned seq;
 	int found = 0;
 	int found = 0;
+	int locked = 0;
 
 
-	spin_lock(&dcache_lock);
+	seq = read_seqbegin(&rename_lock);
+again:
+	this_parent = parent;
+	spin_lock(&this_parent->d_lock);
 repeat:
 repeat:
 	next = this_parent->d_subdirs.next;
 	next = this_parent->d_subdirs.next;
 resume:
 resume:
@@ -837,11 +1139,13 @@ resume:
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
 		next = tmp->next;
 		next = tmp->next;
 
 
+		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+
 		/* 
 		/* 
 		 * move only zero ref count dentries to the end 
 		 * move only zero ref count dentries to the end 
 		 * of the unused list for prune_dcache
 		 * of the unused list for prune_dcache
 		 */
 		 */
-		if (!atomic_read(&dentry->d_count)) {
+		if (!dentry->d_count) {
 			dentry_lru_move_tail(dentry);
 			dentry_lru_move_tail(dentry);
 			found++;
 			found++;
 		} else {
 		} else {
@@ -853,28 +1157,63 @@ resume:
 		 * ensures forward progress). We'll be coming back to find
 		 * ensures forward progress). We'll be coming back to find
 		 * the rest.
 		 * the rest.
 		 */
 		 */
-		if (found && need_resched())
+		if (found && need_resched()) {
+			spin_unlock(&dentry->d_lock);
 			goto out;
 			goto out;
+		}
 
 
 		/*
 		/*
 		 * Descend a level if the d_subdirs list is non-empty.
 		 * Descend a level if the d_subdirs list is non-empty.
 		 */
 		 */
 		if (!list_empty(&dentry->d_subdirs)) {
 		if (!list_empty(&dentry->d_subdirs)) {
+			spin_unlock(&this_parent->d_lock);
+			spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
 			this_parent = dentry;
 			this_parent = dentry;
+			spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
 			goto repeat;
 			goto repeat;
 		}
 		}
+
+		spin_unlock(&dentry->d_lock);
 	}
 	}
 	/*
 	/*
 	 * All done at this level ... ascend and resume the search.
 	 * All done at this level ... ascend and resume the search.
 	 */
 	 */
 	if (this_parent != parent) {
 	if (this_parent != parent) {
-		next = this_parent->d_u.d_child.next;
-		this_parent = this_parent->d_parent;
+		struct dentry *tmp;
+		struct dentry *child;
+
+		tmp = this_parent->d_parent;
+		rcu_read_lock();
+		spin_unlock(&this_parent->d_lock);
+		child = this_parent;
+		this_parent = tmp;
+		spin_lock(&this_parent->d_lock);
+		/* might go back up the wrong parent if we have had a rename
+		 * or deletion */
+		if (this_parent != child->d_parent ||
+			(!locked && read_seqretry(&rename_lock, seq))) {
+			spin_unlock(&this_parent->d_lock);
+			rcu_read_unlock();
+			goto rename_retry;
+		}
+		rcu_read_unlock();
+		next = child->d_u.d_child.next;
 		goto resume;
 		goto resume;
 	}
 	}
 out:
 out:
-	spin_unlock(&dcache_lock);
+	spin_unlock(&this_parent->d_lock);
+	if (!locked && read_seqretry(&rename_lock, seq))
+		goto rename_retry;
+	if (locked)
+		write_sequnlock(&rename_lock);
 	return found;
 	return found;
+
+rename_retry:
+	if (found)
+		return found;
+	locked = 1;
+	write_seqlock(&rename_lock);
+	goto again;
 }
 }
 
 
 /**
 /**
@@ -908,16 +1247,13 @@ EXPORT_SYMBOL(shrink_dcache_parent);
  */
  */
 static int shrink_dcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask)
 static int shrink_dcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask)
 {
 {
-	int nr_unused;
-
 	if (nr) {
 	if (nr) {
 		if (!(gfp_mask & __GFP_FS))
 		if (!(gfp_mask & __GFP_FS))
 			return -1;
 			return -1;
 		prune_dcache(nr);
 		prune_dcache(nr);
 	}
 	}
 
 
-	nr_unused = percpu_counter_sum_positive(&nr_dentry_unused);
-	return (nr_unused / 100) * sysctl_vfs_cache_pressure;
+	return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
 }
 }
 
 
 static struct shrinker dcache_shrinker = {
 static struct shrinker dcache_shrinker = {
@@ -960,38 +1296,52 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
 	memcpy(dname, name->name, name->len);
 	memcpy(dname, name->name, name->len);
 	dname[name->len] = 0;
 	dname[name->len] = 0;
 
 
-	atomic_set(&dentry->d_count, 1);
+	dentry->d_count = 1;
 	dentry->d_flags = DCACHE_UNHASHED;
 	dentry->d_flags = DCACHE_UNHASHED;
 	spin_lock_init(&dentry->d_lock);
 	spin_lock_init(&dentry->d_lock);
+	seqcount_init(&dentry->d_seq);
 	dentry->d_inode = NULL;
 	dentry->d_inode = NULL;
 	dentry->d_parent = NULL;
 	dentry->d_parent = NULL;
 	dentry->d_sb = NULL;
 	dentry->d_sb = NULL;
 	dentry->d_op = NULL;
 	dentry->d_op = NULL;
 	dentry->d_fsdata = NULL;
 	dentry->d_fsdata = NULL;
-	dentry->d_mounted = 0;
-	INIT_HLIST_NODE(&dentry->d_hash);
+	INIT_HLIST_BL_NODE(&dentry->d_hash);
 	INIT_LIST_HEAD(&dentry->d_lru);
 	INIT_LIST_HEAD(&dentry->d_lru);
 	INIT_LIST_HEAD(&dentry->d_subdirs);
 	INIT_LIST_HEAD(&dentry->d_subdirs);
 	INIT_LIST_HEAD(&dentry->d_alias);
 	INIT_LIST_HEAD(&dentry->d_alias);
+	INIT_LIST_HEAD(&dentry->d_u.d_child);
 
 
 	if (parent) {
 	if (parent) {
-		dentry->d_parent = dget(parent);
+		spin_lock(&parent->d_lock);
+		/*
+		 * don't need child lock because it is not subject
+		 * to concurrency here
+		 */
+		__dget_dlock(parent);
+		dentry->d_parent = parent;
 		dentry->d_sb = parent->d_sb;
 		dentry->d_sb = parent->d_sb;
-	} else {
-		INIT_LIST_HEAD(&dentry->d_u.d_child);
-	}
-
-	spin_lock(&dcache_lock);
-	if (parent)
 		list_add(&dentry->d_u.d_child, &parent->d_subdirs);
 		list_add(&dentry->d_u.d_child, &parent->d_subdirs);
-	spin_unlock(&dcache_lock);
+		spin_unlock(&parent->d_lock);
+	}
 
 
-	percpu_counter_inc(&nr_dentry);
+	this_cpu_inc(nr_dentry);
 
 
 	return dentry;
 	return dentry;
 }
 }
 EXPORT_SYMBOL(d_alloc);
 EXPORT_SYMBOL(d_alloc);
 
 
+struct dentry *d_alloc_pseudo(struct super_block *sb, const struct qstr *name)
+{
+	struct dentry *dentry = d_alloc(NULL, name);
+	if (dentry) {
+		dentry->d_sb = sb;
+		dentry->d_parent = dentry;
+		dentry->d_flags |= DCACHE_DISCONNECTED;
+	}
+	return dentry;
+}
+EXPORT_SYMBOL(d_alloc_pseudo);
+
 struct dentry *d_alloc_name(struct dentry *parent, const char *name)
 struct dentry *d_alloc_name(struct dentry *parent, const char *name)
 {
 {
 	struct qstr q;
 	struct qstr q;
@@ -1003,12 +1353,36 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name)
 }
 }
 EXPORT_SYMBOL(d_alloc_name);
 EXPORT_SYMBOL(d_alloc_name);
 
 
-/* the caller must hold dcache_lock */
+void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
+{
+	BUG_ON(dentry->d_op);
+	BUG_ON(dentry->d_flags & (DCACHE_OP_HASH	|
+				DCACHE_OP_COMPARE	|
+				DCACHE_OP_REVALIDATE	|
+				DCACHE_OP_DELETE ));
+	dentry->d_op = op;
+	if (!op)
+		return;
+	if (op->d_hash)
+		dentry->d_flags |= DCACHE_OP_HASH;
+	if (op->d_compare)
+		dentry->d_flags |= DCACHE_OP_COMPARE;
+	if (op->d_revalidate)
+		dentry->d_flags |= DCACHE_OP_REVALIDATE;
+	if (op->d_delete)
+		dentry->d_flags |= DCACHE_OP_DELETE;
+
+}
+EXPORT_SYMBOL(d_set_d_op);
+
 static void __d_instantiate(struct dentry *dentry, struct inode *inode)
 static void __d_instantiate(struct dentry *dentry, struct inode *inode)
 {
 {
+	spin_lock(&dentry->d_lock);
 	if (inode)
 	if (inode)
 		list_add(&dentry->d_alias, &inode->i_dentry);
 		list_add(&dentry->d_alias, &inode->i_dentry);
 	dentry->d_inode = inode;
 	dentry->d_inode = inode;
+	dentry_rcuwalk_barrier(dentry);
+	spin_unlock(&dentry->d_lock);
 	fsnotify_d_instantiate(dentry, inode);
 	fsnotify_d_instantiate(dentry, inode);
 }
 }
 
 
@@ -1030,9 +1404,11 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
 void d_instantiate(struct dentry *entry, struct inode * inode)
 void d_instantiate(struct dentry *entry, struct inode * inode)
 {
 {
 	BUG_ON(!list_empty(&entry->d_alias));
 	BUG_ON(!list_empty(&entry->d_alias));
-	spin_lock(&dcache_lock);
+	if (inode)
+		spin_lock(&inode->i_lock);
 	__d_instantiate(entry, inode);
 	__d_instantiate(entry, inode);
-	spin_unlock(&dcache_lock);
+	if (inode)
+		spin_unlock(&inode->i_lock);
 	security_d_instantiate(entry, inode);
 	security_d_instantiate(entry, inode);
 }
 }
 EXPORT_SYMBOL(d_instantiate);
 EXPORT_SYMBOL(d_instantiate);
@@ -1069,15 +1445,18 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry,
 	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
 	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
 		struct qstr *qstr = &alias->d_name;
 		struct qstr *qstr = &alias->d_name;
 
 
+		/*
+		 * Don't need alias->d_lock here, because aliases with
+		 * d_parent == entry->d_parent are not subject to name or
+		 * parent changes, because the parent inode i_mutex is held.
+		 */
 		if (qstr->hash != hash)
 		if (qstr->hash != hash)
 			continue;
 			continue;
 		if (alias->d_parent != entry->d_parent)
 		if (alias->d_parent != entry->d_parent)
 			continue;
 			continue;
-		if (qstr->len != len)
+		if (dentry_cmp(qstr->name, qstr->len, name, len))
 			continue;
 			continue;
-		if (memcmp(qstr->name, name, len))
-			continue;
-		dget_locked(alias);
+		__dget(alias);
 		return alias;
 		return alias;
 	}
 	}
 
 
@@ -1091,9 +1470,11 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
 
 
 	BUG_ON(!list_empty(&entry->d_alias));
 	BUG_ON(!list_empty(&entry->d_alias));
 
 
-	spin_lock(&dcache_lock);
+	if (inode)
+		spin_lock(&inode->i_lock);
 	result = __d_instantiate_unique(entry, inode);
 	result = __d_instantiate_unique(entry, inode);
-	spin_unlock(&dcache_lock);
+	if (inode)
+		spin_unlock(&inode->i_lock);
 
 
 	if (!result) {
 	if (!result) {
 		security_d_instantiate(entry, inode);
 		security_d_instantiate(entry, inode);
@@ -1134,14 +1515,6 @@ struct dentry * d_alloc_root(struct inode * root_inode)
 }
 }
 EXPORT_SYMBOL(d_alloc_root);
 EXPORT_SYMBOL(d_alloc_root);
 
 
-static inline struct hlist_head *d_hash(struct dentry *parent,
-					unsigned long hash)
-{
-	hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;
-	hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);
-	return dentry_hashtable + (hash & D_HASHMASK);
-}
-
 /**
 /**
  * d_obtain_alias - find or allocate a dentry for a given inode
  * d_obtain_alias - find or allocate a dentry for a given inode
  * @inode: inode to allocate the dentry for
  * @inode: inode to allocate the dentry for
@@ -1182,10 +1555,11 @@ struct dentry *d_obtain_alias(struct inode *inode)
 	}
 	}
 	tmp->d_parent = tmp; /* make sure dput doesn't croak */
 	tmp->d_parent = tmp; /* make sure dput doesn't croak */
 
 
-	spin_lock(&dcache_lock);
+
+	spin_lock(&inode->i_lock);
 	res = __d_find_alias(inode, 0);
 	res = __d_find_alias(inode, 0);
 	if (res) {
 	if (res) {
-		spin_unlock(&dcache_lock);
+		spin_unlock(&inode->i_lock);
 		dput(tmp);
 		dput(tmp);
 		goto out_iput;
 		goto out_iput;
 	}
 	}
@@ -1195,12 +1569,14 @@ struct dentry *d_obtain_alias(struct inode *inode)
 	tmp->d_sb = inode->i_sb;
 	tmp->d_sb = inode->i_sb;
 	tmp->d_inode = inode;
 	tmp->d_inode = inode;
 	tmp->d_flags |= DCACHE_DISCONNECTED;
 	tmp->d_flags |= DCACHE_DISCONNECTED;
-	tmp->d_flags &= ~DCACHE_UNHASHED;
 	list_add(&tmp->d_alias, &inode->i_dentry);
 	list_add(&tmp->d_alias, &inode->i_dentry);
-	hlist_add_head(&tmp->d_hash, &inode->i_sb->s_anon);
+	bit_spin_lock(0, (unsigned long *)&tmp->d_sb->s_anon.first);
+	tmp->d_flags &= ~DCACHE_UNHASHED;
+	hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon);
+	__bit_spin_unlock(0, (unsigned long *)&tmp->d_sb->s_anon.first);
 	spin_unlock(&tmp->d_lock);
 	spin_unlock(&tmp->d_lock);
+	spin_unlock(&inode->i_lock);
 
 
-	spin_unlock(&dcache_lock);
 	return tmp;
 	return tmp;
 
 
  out_iput:
  out_iput:
@@ -1230,18 +1606,18 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
 	struct dentry *new = NULL;
 	struct dentry *new = NULL;
 
 
 	if (inode && S_ISDIR(inode->i_mode)) {
 	if (inode && S_ISDIR(inode->i_mode)) {
-		spin_lock(&dcache_lock);
+		spin_lock(&inode->i_lock);
 		new = __d_find_alias(inode, 1);
 		new = __d_find_alias(inode, 1);
 		if (new) {
 		if (new) {
 			BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
 			BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
-			spin_unlock(&dcache_lock);
+			spin_unlock(&inode->i_lock);
 			security_d_instantiate(new, inode);
 			security_d_instantiate(new, inode);
 			d_move(new, dentry);
 			d_move(new, dentry);
 			iput(inode);
 			iput(inode);
 		} else {
 		} else {
-			/* already taking dcache_lock, so d_add() by hand */
+			/* already taking inode->i_lock, so d_add() by hand */
 			__d_instantiate(dentry, inode);
 			__d_instantiate(dentry, inode);
-			spin_unlock(&dcache_lock);
+			spin_unlock(&inode->i_lock);
 			security_d_instantiate(dentry, inode);
 			security_d_instantiate(dentry, inode);
 			d_rehash(dentry);
 			d_rehash(dentry);
 		}
 		}
@@ -1314,10 +1690,10 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
 	 * Negative dentry: instantiate it unless the inode is a directory and
 	 * Negative dentry: instantiate it unless the inode is a directory and
 	 * already has a dentry.
 	 * already has a dentry.
 	 */
 	 */
-	spin_lock(&dcache_lock);
+	spin_lock(&inode->i_lock);
 	if (!S_ISDIR(inode->i_mode) || list_empty(&inode->i_dentry)) {
 	if (!S_ISDIR(inode->i_mode) || list_empty(&inode->i_dentry)) {
 		__d_instantiate(found, inode);
 		__d_instantiate(found, inode);
-		spin_unlock(&dcache_lock);
+		spin_unlock(&inode->i_lock);
 		security_d_instantiate(found, inode);
 		security_d_instantiate(found, inode);
 		return found;
 		return found;
 	}
 	}
@@ -1327,8 +1703,8 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
 	 * reference to it, move it in place and use it.
 	 * reference to it, move it in place and use it.
 	 */
 	 */
 	new = list_entry(inode->i_dentry.next, struct dentry, d_alias);
 	new = list_entry(inode->i_dentry.next, struct dentry, d_alias);
-	dget_locked(new);
-	spin_unlock(&dcache_lock);
+	__dget(new);
+	spin_unlock(&inode->i_lock);
 	security_d_instantiate(found, inode);
 	security_d_instantiate(found, inode);
 	d_move(new, found);
 	d_move(new, found);
 	iput(inode);
 	iput(inode);
@@ -1341,6 +1717,112 @@ err_out:
 }
 }
 EXPORT_SYMBOL(d_add_ci);
 EXPORT_SYMBOL(d_add_ci);
 
 
+/**
+ * __d_lookup_rcu - search for a dentry (racy, store-free)
+ * @parent: parent dentry
+ * @name: qstr of name we wish to find
+ * @seq: returns d_seq value at the point where the dentry was found
+ * @inode: returns dentry->d_inode when the inode was found valid.
+ * Returns: dentry, or NULL
+ *
+ * __d_lookup_rcu is the dcache lookup function for rcu-walk name
+ * resolution (store-free path walking) design described in
+ * Documentation/filesystems/path-lookup.txt.
+ *
+ * This is not to be used outside core vfs.
+ *
+ * __d_lookup_rcu must only be used in rcu-walk mode, ie. with vfsmount lock
+ * held, and rcu_read_lock held. The returned dentry must not be stored into
+ * without taking d_lock and checking d_seq sequence count against @seq
+ * returned here.
+ *
+ * A refcount may be taken on the found dentry with the __d_rcu_to_refcount
+ * function.
+ *
+ * Alternatively, __d_lookup_rcu may be called again to look up the child of
+ * the returned dentry, so long as its parent's seqlock is checked after the
+ * child is looked up. Thus, an interlocking stepping of sequence lock checks
+ * is formed, giving integrity down the path walk.
+ */
+struct dentry *__d_lookup_rcu(struct dentry *parent, struct qstr *name,
+				unsigned *seq, struct inode **inode)
+{
+	unsigned int len = name->len;
+	unsigned int hash = name->hash;
+	const unsigned char *str = name->name;
+	struct dcache_hash_bucket *b = d_hash(parent, hash);
+	struct hlist_bl_node *node;
+	struct dentry *dentry;
+
+	/*
+	 * Note: There is significant duplication with __d_lookup_rcu which is
+	 * required to prevent single threaded performance regressions
+	 * especially on architectures where smp_rmb (in seqcounts) are costly.
+	 * Keep the two functions in sync.
+	 */
+
+	/*
+	 * The hash list is protected using RCU.
+	 *
+	 * Carefully use d_seq when comparing a candidate dentry, to avoid
+	 * races with d_move().
+	 *
+	 * It is possible that concurrent renames can mess up our list
+	 * walk here and result in missing our dentry, resulting in the
+	 * false-negative result. d_lookup() protects against concurrent
+	 * renames using rename_lock seqlock.
+	 *
+	 * See Documentation/vfs/dcache-locking.txt for more details.
+	 */
+	hlist_bl_for_each_entry_rcu(dentry, node, &b->head, d_hash) {
+		struct inode *i;
+		const char *tname;
+		int tlen;
+
+		if (dentry->d_name.hash != hash)
+			continue;
+
+seqretry:
+		*seq = read_seqcount_begin(&dentry->d_seq);
+		if (dentry->d_parent != parent)
+			continue;
+		if (d_unhashed(dentry))
+			continue;
+		tlen = dentry->d_name.len;
+		tname = dentry->d_name.name;
+		i = dentry->d_inode;
+		prefetch(tname);
+		if (i)
+			prefetch(i);
+		/*
+		 * This seqcount check is required to ensure name and
+		 * len are loaded atomically, so as not to walk off the
+		 * edge of memory when walking. If we could load this
+		 * atomically some other way, we could drop this check.
+		 */
+		if (read_seqcount_retry(&dentry->d_seq, *seq))
+			goto seqretry;
+		if (parent->d_flags & DCACHE_OP_COMPARE) {
+			if (parent->d_op->d_compare(parent, *inode,
+						dentry, i,
+						tlen, tname, name))
+				continue;
+		} else {
+			if (dentry_cmp(tname, tlen, str, len))
+				continue;
+		}
+		/*
+		 * No extra seqcount check is required after the name
+		 * compare. The caller must perform a seqcount check in
+		 * order to do anything useful with the returned dentry
+		 * anyway.
+		 */
+		*inode = i;
+		return dentry;
+	}
+	return NULL;
+}
+
 /**
 /**
  * d_lookup - search for a dentry
  * d_lookup - search for a dentry
  * @parent: parent dentry
  * @parent: parent dentry
@@ -1352,10 +1834,10 @@ EXPORT_SYMBOL(d_add_ci);
  * dentry is returned. The caller must use dput to free the entry when it has
  * dentry is returned. The caller must use dput to free the entry when it has
  * finished using it. %NULL is returned if the dentry does not exist.
  * finished using it. %NULL is returned if the dentry does not exist.
  */
  */
-struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
+struct dentry *d_lookup(struct dentry *parent, struct qstr *name)
 {
 {
-	struct dentry * dentry = NULL;
-	unsigned long seq;
+	struct dentry *dentry;
+	unsigned seq;
 
 
         do {
         do {
                 seq = read_seqbegin(&rename_lock);
                 seq = read_seqbegin(&rename_lock);
@@ -1367,7 +1849,7 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
 }
 }
 EXPORT_SYMBOL(d_lookup);
 EXPORT_SYMBOL(d_lookup);
 
 
-/*
+/**
  * __d_lookup - search for a dentry (racy)
  * __d_lookup - search for a dentry (racy)
  * @parent: parent dentry
  * @parent: parent dentry
  * @name: qstr of name we wish to find
  * @name: qstr of name we wish to find
@@ -1382,16 +1864,23 @@ EXPORT_SYMBOL(d_lookup);
  *
  *
  * __d_lookup callers must be commented.
  * __d_lookup callers must be commented.
  */
  */
-struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
+struct dentry *__d_lookup(struct dentry *parent, struct qstr *name)
 {
 {
 	unsigned int len = name->len;
 	unsigned int len = name->len;
 	unsigned int hash = name->hash;
 	unsigned int hash = name->hash;
 	const unsigned char *str = name->name;
 	const unsigned char *str = name->name;
-	struct hlist_head *head = d_hash(parent,hash);
+	struct dcache_hash_bucket *b = d_hash(parent, hash);
+	struct hlist_bl_node *node;
 	struct dentry *found = NULL;
 	struct dentry *found = NULL;
-	struct hlist_node *node;
 	struct dentry *dentry;
 	struct dentry *dentry;
 
 
+	/*
+	 * Note: There is significant duplication with __d_lookup_rcu which is
+	 * required to prevent single threaded performance regressions
+	 * especially on architectures where smp_rmb (in seqcounts) are costly.
+	 * Keep the two functions in sync.
+	 */
+
 	/*
 	/*
 	 * The hash list is protected using RCU.
 	 * The hash list is protected using RCU.
 	 *
 	 *
@@ -1407,25 +1896,16 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
 	 */
 	 */
 	rcu_read_lock();
 	rcu_read_lock();
 	
 	
-	hlist_for_each_entry_rcu(dentry, node, head, d_hash) {
-		struct qstr *qstr;
+	hlist_bl_for_each_entry_rcu(dentry, node, &b->head, d_hash) {
+		const char *tname;
+		int tlen;
 
 
 		if (dentry->d_name.hash != hash)
 		if (dentry->d_name.hash != hash)
 			continue;
 			continue;
-		if (dentry->d_parent != parent)
-			continue;
 
 
 		spin_lock(&dentry->d_lock);
 		spin_lock(&dentry->d_lock);
-
-		/*
-		 * Recheck the dentry after taking the lock - d_move may have
-		 * changed things. Don't bother checking the hash because
-		 * we're about to compare the whole name anyway.
-		 */
 		if (dentry->d_parent != parent)
 		if (dentry->d_parent != parent)
 			goto next;
 			goto next;
-
-		/* non-existing due to RCU? */
 		if (d_unhashed(dentry))
 		if (d_unhashed(dentry))
 			goto next;
 			goto next;
 
 
@@ -1433,18 +1913,19 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
 		 * It is safe to compare names since d_move() cannot
 		 * It is safe to compare names since d_move() cannot
 		 * change the qstr (protected by d_lock).
 		 * change the qstr (protected by d_lock).
 		 */
 		 */
-		qstr = &dentry->d_name;
-		if (parent->d_op && parent->d_op->d_compare) {
-			if (parent->d_op->d_compare(parent, qstr, name))
+		tlen = dentry->d_name.len;
+		tname = dentry->d_name.name;
+		if (parent->d_flags & DCACHE_OP_COMPARE) {
+			if (parent->d_op->d_compare(parent, parent->d_inode,
+						dentry, dentry->d_inode,
+						tlen, tname, name))
 				goto next;
 				goto next;
 		} else {
 		} else {
-			if (qstr->len != len)
-				goto next;
-			if (memcmp(qstr->name, str, len))
+			if (dentry_cmp(tname, tlen, str, len))
 				goto next;
 				goto next;
 		}
 		}
 
 
-		atomic_inc(&dentry->d_count);
+		dentry->d_count++;
 		found = dentry;
 		found = dentry;
 		spin_unlock(&dentry->d_lock);
 		spin_unlock(&dentry->d_lock);
 		break;
 		break;
@@ -1473,8 +1954,8 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
 	 * routine may choose to leave the hash value unchanged.
 	 * routine may choose to leave the hash value unchanged.
 	 */
 	 */
 	name->hash = full_name_hash(name->name, name->len);
 	name->hash = full_name_hash(name->name, name->len);
-	if (dir->d_op && dir->d_op->d_hash) {
-		if (dir->d_op->d_hash(dir, name) < 0)
+	if (dir->d_flags & DCACHE_OP_HASH) {
+		if (dir->d_op->d_hash(dir, dir->d_inode, name) < 0)
 			goto out;
 			goto out;
 	}
 	}
 	dentry = d_lookup(dir, name);
 	dentry = d_lookup(dir, name);
@@ -1483,34 +1964,32 @@ out:
 }
 }
 
 
 /**
 /**
- * d_validate - verify dentry provided from insecure source
+ * d_validate - verify dentry provided from insecure source (deprecated)
  * @dentry: The dentry alleged to be valid child of @dparent
  * @dentry: The dentry alleged to be valid child of @dparent
  * @dparent: The parent dentry (known to be valid)
  * @dparent: The parent dentry (known to be valid)
  *
  *
  * An insecure source has sent us a dentry, here we verify it and dget() it.
  * An insecure source has sent us a dentry, here we verify it and dget() it.
  * This is used by ncpfs in its readdir implementation.
  * This is used by ncpfs in its readdir implementation.
  * Zero is returned in the dentry is invalid.
  * Zero is returned in the dentry is invalid.
+ *
+ * This function is slow for big directories, and deprecated, do not use it.
  */
  */
-int d_validate(struct dentry *dentry, struct dentry *parent)
+int d_validate(struct dentry *dentry, struct dentry *dparent)
 {
 {
-	struct hlist_head *head = d_hash(parent, dentry->d_name.hash);
-	struct hlist_node *node;
-	struct dentry *d;
-
-	/* Check whether the ptr might be valid at all.. */
-	if (!kmem_ptr_validate(dentry_cache, dentry))
-		return 0;
-	if (dentry->d_parent != parent)
-		return 0;
+	struct dentry *child;
 
 
-	rcu_read_lock();
-	hlist_for_each_entry_rcu(d, node, head, d_hash) {
-		if (d == dentry) {
-			dget(dentry);
+	spin_lock(&dparent->d_lock);
+	list_for_each_entry(child, &dparent->d_subdirs, d_u.d_child) {
+		if (dentry == child) {
+			spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+			__dget_dlock(dentry);
+			spin_unlock(&dentry->d_lock);
+			spin_unlock(&dparent->d_lock);
 			return 1;
 			return 1;
 		}
 		}
 	}
 	}
-	rcu_read_unlock();
+	spin_unlock(&dparent->d_lock);
+
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL(d_validate);
 EXPORT_SYMBOL(d_validate);
@@ -1538,16 +2017,23 @@ EXPORT_SYMBOL(d_validate);
  
  
 void d_delete(struct dentry * dentry)
 void d_delete(struct dentry * dentry)
 {
 {
+	struct inode *inode;
 	int isdir = 0;
 	int isdir = 0;
 	/*
 	/*
 	 * Are we the only user?
 	 * Are we the only user?
 	 */
 	 */
-	spin_lock(&dcache_lock);
+again:
 	spin_lock(&dentry->d_lock);
 	spin_lock(&dentry->d_lock);
-	isdir = S_ISDIR(dentry->d_inode->i_mode);
-	if (atomic_read(&dentry->d_count) == 1) {
+	inode = dentry->d_inode;
+	isdir = S_ISDIR(inode->i_mode);
+	if (dentry->d_count == 1) {
+		if (inode && !spin_trylock(&inode->i_lock)) {
+			spin_unlock(&dentry->d_lock);
+			cpu_relax();
+			goto again;
+		}
 		dentry->d_flags &= ~DCACHE_CANT_MOUNT;
 		dentry->d_flags &= ~DCACHE_CANT_MOUNT;
-		dentry_iput(dentry);
+		dentry_unlink_inode(dentry);
 		fsnotify_nameremove(dentry, isdir);
 		fsnotify_nameremove(dentry, isdir);
 		return;
 		return;
 	}
 	}
@@ -1556,17 +2042,18 @@ void d_delete(struct dentry * dentry)
 		__d_drop(dentry);
 		__d_drop(dentry);
 
 
 	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dentry->d_lock);
-	spin_unlock(&dcache_lock);
 
 
 	fsnotify_nameremove(dentry, isdir);
 	fsnotify_nameremove(dentry, isdir);
 }
 }
 EXPORT_SYMBOL(d_delete);
 EXPORT_SYMBOL(d_delete);
 
 
-static void __d_rehash(struct dentry * entry, struct hlist_head *list)
+static void __d_rehash(struct dentry * entry, struct dcache_hash_bucket *b)
 {
 {
-
+	BUG_ON(!d_unhashed(entry));
+	spin_lock_bucket(b);
  	entry->d_flags &= ~DCACHE_UNHASHED;
  	entry->d_flags &= ~DCACHE_UNHASHED;
- 	hlist_add_head_rcu(&entry->d_hash, list);
+	hlist_bl_add_head_rcu(&entry->d_hash, &b->head);
+	spin_unlock_bucket(b);
 }
 }
 
 
 static void _d_rehash(struct dentry * entry)
 static void _d_rehash(struct dentry * entry)
@@ -1583,25 +2070,39 @@ static void _d_rehash(struct dentry * entry)
  
  
 void d_rehash(struct dentry * entry)
 void d_rehash(struct dentry * entry)
 {
 {
-	spin_lock(&dcache_lock);
 	spin_lock(&entry->d_lock);
 	spin_lock(&entry->d_lock);
 	_d_rehash(entry);
 	_d_rehash(entry);
 	spin_unlock(&entry->d_lock);
 	spin_unlock(&entry->d_lock);
-	spin_unlock(&dcache_lock);
 }
 }
 EXPORT_SYMBOL(d_rehash);
 EXPORT_SYMBOL(d_rehash);
 
 
-/*
- * When switching names, the actual string doesn't strictly have to
- * be preserved in the target - because we're dropping the target
- * anyway. As such, we can just do a simple memcpy() to copy over
- * the new name before we switch.
+/**
+ * dentry_update_name_case - update case insensitive dentry with a new name
+ * @dentry: dentry to be updated
+ * @name: new name
  *
  *
- * Note that we have to be a lot more careful about getting the hash
- * switched - we have to switch the hash value properly even if it
- * then no longer matches the actual (corrupted) string of the target.
- * The hash value has to match the hash queue that the dentry is on..
+ * Update a case insensitive dentry with new case of name.
+ *
+ * dentry must have been returned by d_lookup with name @name. Old and new
+ * name lengths must match (ie. no d_compare which allows mismatched name
+ * lengths).
+ *
+ * Parent inode i_mutex must be held over d_lookup and into this call (to
+ * keep renames and concurrent inserts, and readdir(2) away).
  */
  */
+void dentry_update_name_case(struct dentry *dentry, struct qstr *name)
+{
+	BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
+	BUG_ON(dentry->d_name.len != name->len); /* d_lookup gives this */
+
+	spin_lock(&dentry->d_lock);
+	write_seqcount_begin(&dentry->d_seq);
+	memcpy((unsigned char *)dentry->d_name.name, name->name, name->len);
+	write_seqcount_end(&dentry->d_seq);
+	spin_unlock(&dentry->d_lock);
+}
+EXPORT_SYMBOL(dentry_update_name_case);
+
 static void switch_names(struct dentry *dentry, struct dentry *target)
 static void switch_names(struct dentry *dentry, struct dentry *target)
 {
 {
 	if (dname_external(target)) {
 	if (dname_external(target)) {
@@ -1643,54 +2144,84 @@ static void switch_names(struct dentry *dentry, struct dentry *target)
 	swap(dentry->d_name.len, target->d_name.len);
 	swap(dentry->d_name.len, target->d_name.len);
 }
 }
 
 
+static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target)
+{
+	/*
+	 * XXXX: do we really need to take target->d_lock?
+	 */
+	if (IS_ROOT(dentry) || dentry->d_parent == target->d_parent)
+		spin_lock(&target->d_parent->d_lock);
+	else {
+		if (d_ancestor(dentry->d_parent, target->d_parent)) {
+			spin_lock(&dentry->d_parent->d_lock);
+			spin_lock_nested(&target->d_parent->d_lock,
+						DENTRY_D_LOCK_NESTED);
+		} else {
+			spin_lock(&target->d_parent->d_lock);
+			spin_lock_nested(&dentry->d_parent->d_lock,
+						DENTRY_D_LOCK_NESTED);
+		}
+	}
+	if (target < dentry) {
+		spin_lock_nested(&target->d_lock, 2);
+		spin_lock_nested(&dentry->d_lock, 3);
+	} else {
+		spin_lock_nested(&dentry->d_lock, 2);
+		spin_lock_nested(&target->d_lock, 3);
+	}
+}
+
+static void dentry_unlock_parents_for_move(struct dentry *dentry,
+					struct dentry *target)
+{
+	if (target->d_parent != dentry->d_parent)
+		spin_unlock(&dentry->d_parent->d_lock);
+	if (target->d_parent != target)
+		spin_unlock(&target->d_parent->d_lock);
+}
+
 /*
 /*
- * We cannibalize "target" when moving dentry on top of it,
- * because it's going to be thrown away anyway. We could be more
- * polite about it, though.
- *
- * This forceful removal will result in ugly /proc output if
- * somebody holds a file open that got deleted due to a rename.
- * We could be nicer about the deleted file, and let it show
- * up under the name it had before it was deleted rather than
- * under the original name of the file that was moved on top of it.
+ * When switching names, the actual string doesn't strictly have to
+ * be preserved in the target - because we're dropping the target
+ * anyway. As such, we can just do a simple memcpy() to copy over
+ * the new name before we switch.
+ *
+ * Note that we have to be a lot more careful about getting the hash
+ * switched - we have to switch the hash value properly even if it
+ * then no longer matches the actual (corrupted) string of the target.
+ * The hash value has to match the hash queue that the dentry is on..
  */
  */
- 
 /*
 /*
- * d_move_locked - move a dentry
+ * d_move - move a dentry
  * @dentry: entry to move
  * @dentry: entry to move
  * @target: new dentry
  * @target: new dentry
  *
  *
  * Update the dcache to reflect the move of a file name. Negative
  * Update the dcache to reflect the move of a file name. Negative
  * dcache entries should not be moved in this way.
  * dcache entries should not be moved in this way.
  */
  */
-static void d_move_locked(struct dentry * dentry, struct dentry * target)
+void d_move(struct dentry * dentry, struct dentry * target)
 {
 {
-	struct hlist_head *list;
-
 	if (!dentry->d_inode)
 	if (!dentry->d_inode)
 		printk(KERN_WARNING "VFS: moving negative dcache entry\n");
 		printk(KERN_WARNING "VFS: moving negative dcache entry\n");
 
 
+	BUG_ON(d_ancestor(dentry, target));
+	BUG_ON(d_ancestor(target, dentry));
+
 	write_seqlock(&rename_lock);
 	write_seqlock(&rename_lock);
-	/*
-	 * XXXX: do we really need to take target->d_lock?
-	 */
-	if (target < dentry) {
-		spin_lock(&target->d_lock);
-		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
-	} else {
-		spin_lock(&dentry->d_lock);
-		spin_lock_nested(&target->d_lock, DENTRY_D_LOCK_NESTED);
-	}
 
 
-	/* Move the dentry to the target hash queue, if on different bucket */
-	if (d_unhashed(dentry))
-		goto already_unhashed;
+	dentry_lock_for_move(dentry, target);
 
 
-	hlist_del_rcu(&dentry->d_hash);
+	write_seqcount_begin(&dentry->d_seq);
+	write_seqcount_begin(&target->d_seq);
 
 
-already_unhashed:
-	list = d_hash(target->d_parent, target->d_name.hash);
-	__d_rehash(dentry, list);
+	/* __d_drop does write_seqcount_barrier, but they're OK to nest. */
+
+	/*
+	 * Move the dentry to the target hash queue. Don't bother checking
+	 * for the same hash queue because of how unlikely it is.
+	 */
+	__d_drop(dentry);
+	__d_rehash(dentry, d_hash(target->d_parent, target->d_name.hash));
 
 
 	/* Unhash the target: dput() will then get rid of it */
 	/* Unhash the target: dput() will then get rid of it */
 	__d_drop(target);
 	__d_drop(target);
@@ -1715,27 +2246,16 @@ already_unhashed:
 	}
 	}
 
 
 	list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
 	list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
+
+	write_seqcount_end(&target->d_seq);
+	write_seqcount_end(&dentry->d_seq);
+
+	dentry_unlock_parents_for_move(dentry, target);
 	spin_unlock(&target->d_lock);
 	spin_unlock(&target->d_lock);
 	fsnotify_d_move(dentry);
 	fsnotify_d_move(dentry);
 	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dentry->d_lock);
 	write_sequnlock(&rename_lock);
 	write_sequnlock(&rename_lock);
 }
 }
-
-/**
- * d_move - move a dentry
- * @dentry: entry to move
- * @target: new dentry
- *
- * Update the dcache to reflect the move of a file name. Negative
- * dcache entries should not be moved in this way.
- */
-
-void d_move(struct dentry * dentry, struct dentry * target)
-{
-	spin_lock(&dcache_lock);
-	d_move_locked(dentry, target);
-	spin_unlock(&dcache_lock);
-}
 EXPORT_SYMBOL(d_move);
 EXPORT_SYMBOL(d_move);
 
 
 /**
 /**
@@ -1761,13 +2281,13 @@ struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2)
  * This helper attempts to cope with remotely renamed directories
  * This helper attempts to cope with remotely renamed directories
  *
  *
  * It assumes that the caller is already holding
  * It assumes that the caller is already holding
- * dentry->d_parent->d_inode->i_mutex and the dcache_lock
+ * dentry->d_parent->d_inode->i_mutex and the inode->i_lock
  *
  *
  * Note: If ever the locking in lock_rename() changes, then please
  * Note: If ever the locking in lock_rename() changes, then please
  * remember to update this too...
  * remember to update this too...
  */
  */
-static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias)
-	__releases(dcache_lock)
+static struct dentry *__d_unalias(struct inode *inode,
+		struct dentry *dentry, struct dentry *alias)
 {
 {
 	struct mutex *m1 = NULL, *m2 = NULL;
 	struct mutex *m1 = NULL, *m2 = NULL;
 	struct dentry *ret;
 	struct dentry *ret;
@@ -1790,10 +2310,10 @@ static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias)
 		goto out_err;
 		goto out_err;
 	m2 = &alias->d_parent->d_inode->i_mutex;
 	m2 = &alias->d_parent->d_inode->i_mutex;
 out_unalias:
 out_unalias:
-	d_move_locked(alias, dentry);
+	d_move(alias, dentry);
 	ret = alias;
 	ret = alias;
 out_err:
 out_err:
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 	if (m2)
 	if (m2)
 		mutex_unlock(m2);
 		mutex_unlock(m2);
 	if (m1)
 	if (m1)
@@ -1804,17 +2324,23 @@ out_err:
 /*
 /*
  * Prepare an anonymous dentry for life in the superblock's dentry tree as a
  * Prepare an anonymous dentry for life in the superblock's dentry tree as a
  * named dentry in place of the dentry to be replaced.
  * named dentry in place of the dentry to be replaced.
+ * returns with anon->d_lock held!
  */
  */
 static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
 static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
 {
 {
 	struct dentry *dparent, *aparent;
 	struct dentry *dparent, *aparent;
 
 
-	switch_names(dentry, anon);
-	swap(dentry->d_name.hash, anon->d_name.hash);
+	dentry_lock_for_move(anon, dentry);
+
+	write_seqcount_begin(&dentry->d_seq);
+	write_seqcount_begin(&anon->d_seq);
 
 
 	dparent = dentry->d_parent;
 	dparent = dentry->d_parent;
 	aparent = anon->d_parent;
 	aparent = anon->d_parent;
 
 
+	switch_names(dentry, anon);
+	swap(dentry->d_name.hash, anon->d_name.hash);
+
 	dentry->d_parent = (aparent == anon) ? dentry : aparent;
 	dentry->d_parent = (aparent == anon) ? dentry : aparent;
 	list_del(&dentry->d_u.d_child);
 	list_del(&dentry->d_u.d_child);
 	if (!IS_ROOT(dentry))
 	if (!IS_ROOT(dentry))
@@ -1829,6 +2355,13 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
 	else
 	else
 		INIT_LIST_HEAD(&anon->d_u.d_child);
 		INIT_LIST_HEAD(&anon->d_u.d_child);
 
 
+	write_seqcount_end(&dentry->d_seq);
+	write_seqcount_end(&anon->d_seq);
+
+	dentry_unlock_parents_for_move(anon, dentry);
+	spin_unlock(&dentry->d_lock);
+
+	/* anon->d_lock still locked, returns locked */
 	anon->d_flags &= ~DCACHE_DISCONNECTED;
 	anon->d_flags &= ~DCACHE_DISCONNECTED;
 }
 }
 
 
@@ -1846,14 +2379,15 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
 
 
 	BUG_ON(!d_unhashed(dentry));
 	BUG_ON(!d_unhashed(dentry));
 
 
-	spin_lock(&dcache_lock);
-
 	if (!inode) {
 	if (!inode) {
 		actual = dentry;
 		actual = dentry;
 		__d_instantiate(dentry, NULL);
 		__d_instantiate(dentry, NULL);
-		goto found_lock;
+		d_rehash(actual);
+		goto out_nolock;
 	}
 	}
 
 
+	spin_lock(&inode->i_lock);
+
 	if (S_ISDIR(inode->i_mode)) {
 	if (S_ISDIR(inode->i_mode)) {
 		struct dentry *alias;
 		struct dentry *alias;
 
 
@@ -1864,13 +2398,12 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
 			/* Is this an anonymous mountpoint that we could splice
 			/* Is this an anonymous mountpoint that we could splice
 			 * into our tree? */
 			 * into our tree? */
 			if (IS_ROOT(alias)) {
 			if (IS_ROOT(alias)) {
-				spin_lock(&alias->d_lock);
 				__d_materialise_dentry(dentry, alias);
 				__d_materialise_dentry(dentry, alias);
 				__d_drop(alias);
 				__d_drop(alias);
 				goto found;
 				goto found;
 			}
 			}
 			/* Nope, but we must(!) avoid directory aliasing */
 			/* Nope, but we must(!) avoid directory aliasing */
-			actual = __d_unalias(dentry, alias);
+			actual = __d_unalias(inode, dentry, alias);
 			if (IS_ERR(actual))
 			if (IS_ERR(actual))
 				dput(alias);
 				dput(alias);
 			goto out_nolock;
 			goto out_nolock;
@@ -1881,15 +2414,14 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
 	actual = __d_instantiate_unique(dentry, inode);
 	actual = __d_instantiate_unique(dentry, inode);
 	if (!actual)
 	if (!actual)
 		actual = dentry;
 		actual = dentry;
-	else if (unlikely(!d_unhashed(actual)))
-		goto shouldnt_be_hashed;
+	else
+		BUG_ON(!d_unhashed(actual));
 
 
-found_lock:
 	spin_lock(&actual->d_lock);
 	spin_lock(&actual->d_lock);
 found:
 found:
 	_d_rehash(actual);
 	_d_rehash(actual);
 	spin_unlock(&actual->d_lock);
 	spin_unlock(&actual->d_lock);
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 out_nolock:
 out_nolock:
 	if (actual == dentry) {
 	if (actual == dentry) {
 		security_d_instantiate(dentry, inode);
 		security_d_instantiate(dentry, inode);
@@ -1898,10 +2430,6 @@ out_nolock:
 
 
 	iput(inode);
 	iput(inode);
 	return actual;
 	return actual;
-
-shouldnt_be_hashed:
-	spin_unlock(&dcache_lock);
-	BUG();
 }
 }
 EXPORT_SYMBOL_GPL(d_materialise_unique);
 EXPORT_SYMBOL_GPL(d_materialise_unique);
 
 
@@ -1928,7 +2456,7 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
  * @buffer: pointer to the end of the buffer
  * @buffer: pointer to the end of the buffer
  * @buflen: pointer to buffer length
  * @buflen: pointer to buffer length
  *
  *
- * Caller holds the dcache_lock.
+ * Caller holds the rename_lock.
  *
  *
  * If path is not reachable from the supplied root, then the value of
  * If path is not reachable from the supplied root, then the value of
  * root is changed (without modifying refcounts).
  * root is changed (without modifying refcounts).
@@ -1956,7 +2484,9 @@ static int prepend_path(const struct path *path, struct path *root,
 		}
 		}
 		parent = dentry->d_parent;
 		parent = dentry->d_parent;
 		prefetch(parent);
 		prefetch(parent);
+		spin_lock(&dentry->d_lock);
 		error = prepend_name(buffer, buflen, &dentry->d_name);
 		error = prepend_name(buffer, buflen, &dentry->d_name);
+		spin_unlock(&dentry->d_lock);
 		if (!error)
 		if (!error)
 			error = prepend(buffer, buflen, "/", 1);
 			error = prepend(buffer, buflen, "/", 1);
 		if (error)
 		if (error)
@@ -2012,9 +2542,9 @@ char *__d_path(const struct path *path, struct path *root,
 	int error;
 	int error;
 
 
 	prepend(&res, &buflen, "\0", 1);
 	prepend(&res, &buflen, "\0", 1);
-	spin_lock(&dcache_lock);
+	write_seqlock(&rename_lock);
 	error = prepend_path(path, root, &res, &buflen);
 	error = prepend_path(path, root, &res, &buflen);
-	spin_unlock(&dcache_lock);
+	write_sequnlock(&rename_lock);
 
 
 	if (error)
 	if (error)
 		return ERR_PTR(error);
 		return ERR_PTR(error);
@@ -2076,12 +2606,12 @@ char *d_path(const struct path *path, char *buf, int buflen)
 		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 
 
 	get_fs_root(current->fs, &root);
 	get_fs_root(current->fs, &root);
-	spin_lock(&dcache_lock);
+	write_seqlock(&rename_lock);
 	tmp = root;
 	tmp = root;
 	error = path_with_deleted(path, &tmp, &res, &buflen);
 	error = path_with_deleted(path, &tmp, &res, &buflen);
 	if (error)
 	if (error)
 		res = ERR_PTR(error);
 		res = ERR_PTR(error);
-	spin_unlock(&dcache_lock);
+	write_sequnlock(&rename_lock);
 	path_put(&root);
 	path_put(&root);
 	return res;
 	return res;
 }
 }
@@ -2107,12 +2637,12 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
 		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 		return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 
 
 	get_fs_root(current->fs, &root);
 	get_fs_root(current->fs, &root);
-	spin_lock(&dcache_lock);
+	write_seqlock(&rename_lock);
 	tmp = root;
 	tmp = root;
 	error = path_with_deleted(path, &tmp, &res, &buflen);
 	error = path_with_deleted(path, &tmp, &res, &buflen);
 	if (!error && !path_equal(&tmp, &root))
 	if (!error && !path_equal(&tmp, &root))
 		error = prepend_unreachable(&res, &buflen);
 		error = prepend_unreachable(&res, &buflen);
-	spin_unlock(&dcache_lock);
+	write_sequnlock(&rename_lock);
 	path_put(&root);
 	path_put(&root);
 	if (error)
 	if (error)
 		res =  ERR_PTR(error);
 		res =  ERR_PTR(error);
@@ -2144,7 +2674,7 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen,
 /*
 /*
  * Write full pathname from the root of the filesystem into the buffer.
  * Write full pathname from the root of the filesystem into the buffer.
  */
  */
-char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
+static char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
 {
 {
 	char *end = buf + buflen;
 	char *end = buf + buflen;
 	char *retval;
 	char *retval;
@@ -2158,10 +2688,13 @@ char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
 
 
 	while (!IS_ROOT(dentry)) {
 	while (!IS_ROOT(dentry)) {
 		struct dentry *parent = dentry->d_parent;
 		struct dentry *parent = dentry->d_parent;
+		int error;
 
 
 		prefetch(parent);
 		prefetch(parent);
-		if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
-		    (prepend(&end, &buflen, "/", 1) != 0))
+		spin_lock(&dentry->d_lock);
+		error = prepend_name(&end, &buflen, &dentry->d_name);
+		spin_unlock(&dentry->d_lock);
+		if (error != 0 || prepend(&end, &buflen, "/", 1) != 0)
 			goto Elong;
 			goto Elong;
 
 
 		retval = end;
 		retval = end;
@@ -2171,14 +2704,25 @@ char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
 Elong:
 Elong:
 	return ERR_PTR(-ENAMETOOLONG);
 	return ERR_PTR(-ENAMETOOLONG);
 }
 }
-EXPORT_SYMBOL(__dentry_path);
+
+char *dentry_path_raw(struct dentry *dentry, char *buf, int buflen)
+{
+	char *retval;
+
+	write_seqlock(&rename_lock);
+	retval = __dentry_path(dentry, buf, buflen);
+	write_sequnlock(&rename_lock);
+
+	return retval;
+}
+EXPORT_SYMBOL(dentry_path_raw);
 
 
 char *dentry_path(struct dentry *dentry, char *buf, int buflen)
 char *dentry_path(struct dentry *dentry, char *buf, int buflen)
 {
 {
 	char *p = NULL;
 	char *p = NULL;
 	char *retval;
 	char *retval;
 
 
-	spin_lock(&dcache_lock);
+	write_seqlock(&rename_lock);
 	if (d_unlinked(dentry)) {
 	if (d_unlinked(dentry)) {
 		p = buf + buflen;
 		p = buf + buflen;
 		if (prepend(&p, &buflen, "//deleted", 10) != 0)
 		if (prepend(&p, &buflen, "//deleted", 10) != 0)
@@ -2186,12 +2730,11 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
 		buflen++;
 		buflen++;
 	}
 	}
 	retval = __dentry_path(dentry, buf, buflen);
 	retval = __dentry_path(dentry, buf, buflen);
-	spin_unlock(&dcache_lock);
+	write_sequnlock(&rename_lock);
 	if (!IS_ERR(retval) && p)
 	if (!IS_ERR(retval) && p)
 		*p = '/';	/* restore '/' overriden with '\0' */
 		*p = '/';	/* restore '/' overriden with '\0' */
 	return retval;
 	return retval;
 Elong:
 Elong:
-	spin_unlock(&dcache_lock);
 	return ERR_PTR(-ENAMETOOLONG);
 	return ERR_PTR(-ENAMETOOLONG);
 }
 }
 
 
@@ -2225,7 +2768,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
 	get_fs_root_and_pwd(current->fs, &root, &pwd);
 	get_fs_root_and_pwd(current->fs, &root, &pwd);
 
 
 	error = -ENOENT;
 	error = -ENOENT;
-	spin_lock(&dcache_lock);
+	write_seqlock(&rename_lock);
 	if (!d_unlinked(pwd.dentry)) {
 	if (!d_unlinked(pwd.dentry)) {
 		unsigned long len;
 		unsigned long len;
 		struct path tmp = root;
 		struct path tmp = root;
@@ -2234,7 +2777,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
 
 
 		prepend(&cwd, &buflen, "\0", 1);
 		prepend(&cwd, &buflen, "\0", 1);
 		error = prepend_path(&pwd, &tmp, &cwd, &buflen);
 		error = prepend_path(&pwd, &tmp, &cwd, &buflen);
-		spin_unlock(&dcache_lock);
+		write_sequnlock(&rename_lock);
 
 
 		if (error)
 		if (error)
 			goto out;
 			goto out;
@@ -2253,8 +2796,9 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
 			if (copy_to_user(buf, cwd, len))
 			if (copy_to_user(buf, cwd, len))
 				error = -EFAULT;
 				error = -EFAULT;
 		}
 		}
-	} else
-		spin_unlock(&dcache_lock);
+	} else {
+		write_sequnlock(&rename_lock);
+	}
 
 
 out:
 out:
 	path_put(&pwd);
 	path_put(&pwd);
@@ -2282,25 +2826,25 @@ out:
 int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
 int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
 {
 {
 	int result;
 	int result;
-	unsigned long seq;
+	unsigned seq;
 
 
 	if (new_dentry == old_dentry)
 	if (new_dentry == old_dentry)
 		return 1;
 		return 1;
 
 
-	/*
-	 * Need rcu_readlock to protect against the d_parent trashing
-	 * due to d_move
-	 */
-	rcu_read_lock();
 	do {
 	do {
 		/* for restarting inner loop in case of seq retry */
 		/* for restarting inner loop in case of seq retry */
 		seq = read_seqbegin(&rename_lock);
 		seq = read_seqbegin(&rename_lock);
+		/*
+		 * Need rcu_readlock to protect against the d_parent trashing
+		 * due to d_move
+		 */
+		rcu_read_lock();
 		if (d_ancestor(old_dentry, new_dentry))
 		if (d_ancestor(old_dentry, new_dentry))
 			result = 1;
 			result = 1;
 		else
 		else
 			result = 0;
 			result = 0;
+		rcu_read_unlock();
 	} while (read_seqretry(&rename_lock, seq));
 	} while (read_seqretry(&rename_lock, seq));
-	rcu_read_unlock();
 
 
 	return result;
 	return result;
 }
 }
@@ -2332,10 +2876,15 @@ EXPORT_SYMBOL(path_is_under);
 
 
 void d_genocide(struct dentry *root)
 void d_genocide(struct dentry *root)
 {
 {
-	struct dentry *this_parent = root;
+	struct dentry *this_parent;
 	struct list_head *next;
 	struct list_head *next;
+	unsigned seq;
+	int locked = 0;
 
 
-	spin_lock(&dcache_lock);
+	seq = read_seqbegin(&rename_lock);
+again:
+	this_parent = root;
+	spin_lock(&this_parent->d_lock);
 repeat:
 repeat:
 	next = this_parent->d_subdirs.next;
 	next = this_parent->d_subdirs.next;
 resume:
 resume:
@@ -2343,21 +2892,62 @@ resume:
 		struct list_head *tmp = next;
 		struct list_head *tmp = next;
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
 		next = tmp->next;
 		next = tmp->next;
-		if (d_unhashed(dentry)||!dentry->d_inode)
+
+		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+		if (d_unhashed(dentry) || !dentry->d_inode) {
+			spin_unlock(&dentry->d_lock);
 			continue;
 			continue;
+		}
 		if (!list_empty(&dentry->d_subdirs)) {
 		if (!list_empty(&dentry->d_subdirs)) {
+			spin_unlock(&this_parent->d_lock);
+			spin_release(&dentry->d_lock.dep_map, 1, _RET_IP_);
 			this_parent = dentry;
 			this_parent = dentry;
+			spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
 			goto repeat;
 			goto repeat;
 		}
 		}
-		atomic_dec(&dentry->d_count);
+		if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
+			dentry->d_flags |= DCACHE_GENOCIDE;
+			dentry->d_count--;
+		}
+		spin_unlock(&dentry->d_lock);
 	}
 	}
 	if (this_parent != root) {
 	if (this_parent != root) {
-		next = this_parent->d_u.d_child.next;
-		atomic_dec(&this_parent->d_count);
-		this_parent = this_parent->d_parent;
+		struct dentry *tmp;
+		struct dentry *child;
+
+		tmp = this_parent->d_parent;
+		if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
+			this_parent->d_flags |= DCACHE_GENOCIDE;
+			this_parent->d_count--;
+		}
+		rcu_read_lock();
+		spin_unlock(&this_parent->d_lock);
+		child = this_parent;
+		this_parent = tmp;
+		spin_lock(&this_parent->d_lock);
+		/* might go back up the wrong parent if we have had a rename
+		 * or deletion */
+		if (this_parent != child->d_parent ||
+			 (!locked && read_seqretry(&rename_lock, seq))) {
+			spin_unlock(&this_parent->d_lock);
+			rcu_read_unlock();
+			goto rename_retry;
+		}
+		rcu_read_unlock();
+		next = child->d_u.d_child.next;
 		goto resume;
 		goto resume;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&this_parent->d_lock);
+	if (!locked && read_seqretry(&rename_lock, seq))
+		goto rename_retry;
+	if (locked)
+		write_sequnlock(&rename_lock);
+	return;
+
+rename_retry:
+	locked = 1;
+	write_seqlock(&rename_lock);
+	goto again;
 }
 }
 
 
 /**
 /**
@@ -2411,7 +3001,7 @@ static void __init dcache_init_early(void)
 
 
 	dentry_hashtable =
 	dentry_hashtable =
 		alloc_large_system_hash("Dentry cache",
 		alloc_large_system_hash("Dentry cache",
-					sizeof(struct hlist_head),
+					sizeof(struct dcache_hash_bucket),
 					dhash_entries,
 					dhash_entries,
 					13,
 					13,
 					HASH_EARLY,
 					HASH_EARLY,
@@ -2420,16 +3010,13 @@ static void __init dcache_init_early(void)
 					0);
 					0);
 
 
 	for (loop = 0; loop < (1 << d_hash_shift); loop++)
 	for (loop = 0; loop < (1 << d_hash_shift); loop++)
-		INIT_HLIST_HEAD(&dentry_hashtable[loop]);
+		INIT_HLIST_BL_HEAD(&dentry_hashtable[loop].head);
 }
 }
 
 
 static void __init dcache_init(void)
 static void __init dcache_init(void)
 {
 {
 	int loop;
 	int loop;
 
 
-	percpu_counter_init(&nr_dentry, 0);
-	percpu_counter_init(&nr_dentry_unused, 0);
-
 	/* 
 	/* 
 	 * A constructor could be added for stable state like the lists,
 	 * A constructor could be added for stable state like the lists,
 	 * but it is probably not worth it because of the cache nature
 	 * but it is probably not worth it because of the cache nature
@@ -2446,7 +3033,7 @@ static void __init dcache_init(void)
 
 
 	dentry_hashtable =
 	dentry_hashtable =
 		alloc_large_system_hash("Dentry cache",
 		alloc_large_system_hash("Dentry cache",
-					sizeof(struct hlist_head),
+					sizeof(struct dcache_hash_bucket),
 					dhash_entries,
 					dhash_entries,
 					13,
 					13,
 					0,
 					0,
@@ -2455,7 +3042,7 @@ static void __init dcache_init(void)
 					0);
 					0);
 
 
 	for (loop = 0; loop < (1 << d_hash_shift); loop++)
 	for (loop = 0; loop < (1 << d_hash_shift); loop++)
-		INIT_HLIST_HEAD(&dentry_hashtable[loop]);
+		INIT_HLIST_BL_HEAD(&dentry_hashtable[loop].head);
 }
 }
 
 
 /* SLAB cache for __getname() consumers */
 /* SLAB cache for __getname() consumers */

+ 7 - 2
fs/ecryptfs/dentry.c

@@ -44,12 +44,17 @@
  */
  */
 static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
 {
-	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
-	struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
+	struct dentry *lower_dentry;
+	struct vfsmount *lower_mnt;
 	struct dentry *dentry_save;
 	struct dentry *dentry_save;
 	struct vfsmount *vfsmount_save;
 	struct vfsmount *vfsmount_save;
 	int rc = 1;
 	int rc = 1;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	lower_dentry = ecryptfs_dentry_to_lower(dentry);
+	lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
 	if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
 	if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
 		goto out;
 		goto out;
 	dentry_save = nd->path.dentry;
 	dentry_save = nd->path.dentry;

+ 7 - 5
fs/ecryptfs/inode.c

@@ -260,7 +260,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
 				   ecryptfs_dentry->d_parent));
 				   ecryptfs_dentry->d_parent));
 	lower_inode = lower_dentry->d_inode;
 	lower_inode = lower_dentry->d_inode;
 	fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode);
 	fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode);
-	BUG_ON(!atomic_read(&lower_dentry->d_count));
+	BUG_ON(!lower_dentry->d_count);
 	ecryptfs_set_dentry_private(ecryptfs_dentry,
 	ecryptfs_set_dentry_private(ecryptfs_dentry,
 				    kmem_cache_alloc(ecryptfs_dentry_info_cache,
 				    kmem_cache_alloc(ecryptfs_dentry_info_cache,
 						     GFP_KERNEL));
 						     GFP_KERNEL));
@@ -441,7 +441,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
 	struct qstr lower_name;
 	struct qstr lower_name;
 	int rc = 0;
 	int rc = 0;
 
 
-	ecryptfs_dentry->d_op = &ecryptfs_dops;
+	d_set_d_op(ecryptfs_dentry, &ecryptfs_dops);
 	if ((ecryptfs_dentry->d_name.len == 1
 	if ((ecryptfs_dentry->d_name.len == 1
 	     && !strcmp(ecryptfs_dentry->d_name.name, "."))
 	     && !strcmp(ecryptfs_dentry->d_name.name, "."))
 	    || (ecryptfs_dentry->d_name.len == 2
 	    || (ecryptfs_dentry->d_name.len == 2
@@ -454,7 +454,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
 	lower_name.hash = ecryptfs_dentry->d_name.hash;
 	lower_name.hash = ecryptfs_dentry->d_name.hash;
 	if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
 	if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
 		rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
 		rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
-						    &lower_name);
+				lower_dir_dentry->d_inode, &lower_name);
 		if (rc < 0)
 		if (rc < 0)
 			goto out_d_drop;
 			goto out_d_drop;
 	}
 	}
@@ -489,7 +489,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
 	lower_name.hash = full_name_hash(lower_name.name, lower_name.len);
 	lower_name.hash = full_name_hash(lower_name.name, lower_name.len);
 	if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
 	if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
 		rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
 		rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
-						    &lower_name);
+				lower_dir_dentry->d_inode, &lower_name);
 		if (rc < 0)
 		if (rc < 0)
 			goto out_d_drop;
 			goto out_d_drop;
 	}
 	}
@@ -980,8 +980,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
 }
 }
 
 
 static int
 static int
-ecryptfs_permission(struct inode *inode, int mask)
+ecryptfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
 	return inode_permission(ecryptfs_inode_to_lower(inode), mask);
 	return inode_permission(ecryptfs_inode_to_lower(inode), mask);
 }
 }
 
 

+ 2 - 2
fs/ecryptfs/main.c

@@ -189,7 +189,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
 	if (special_file(lower_inode->i_mode))
 	if (special_file(lower_inode->i_mode))
 		init_special_inode(inode, lower_inode->i_mode,
 		init_special_inode(inode, lower_inode->i_mode,
 				   lower_inode->i_rdev);
 				   lower_inode->i_rdev);
-	dentry->d_op = &ecryptfs_dops;
+	d_set_d_op(dentry, &ecryptfs_dops);
 	fsstack_copy_attr_all(inode, lower_inode);
 	fsstack_copy_attr_all(inode, lower_inode);
 	/* This size will be overwritten for real files w/ headers and
 	/* This size will be overwritten for real files w/ headers and
 	 * other metadata */
 	 * other metadata */
@@ -594,7 +594,7 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
 		deactivate_locked_super(s);
 		deactivate_locked_super(s);
 		goto out;
 		goto out;
 	}
 	}
-	s->s_root->d_op = &ecryptfs_dops;
+	d_set_d_op(s->s_root, &ecryptfs_dops);
 	s->s_root->d_sb = s;
 	s->s_root->d_sb = s;
 	s->s_root->d_parent = s->s_root;
 	s->s_root->d_parent = s->s_root;
 
 

+ 11 - 1
fs/ecryptfs/super.c

@@ -62,6 +62,16 @@ out:
 	return inode;
 	return inode;
 }
 }
 
 
+static void ecryptfs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	struct ecryptfs_inode_info *inode_info;
+	inode_info = ecryptfs_inode_to_private(inode);
+
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
+}
+
 /**
 /**
  * ecryptfs_destroy_inode
  * ecryptfs_destroy_inode
  * @inode: The ecryptfs inode
  * @inode: The ecryptfs inode
@@ -88,7 +98,7 @@ static void ecryptfs_destroy_inode(struct inode *inode)
 		}
 		}
 	}
 	}
 	ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
 	ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
-	kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
+	call_rcu(&inode->i_rcu, ecryptfs_i_callback);
 }
 }
 
 
 /**
 /**

+ 8 - 1
fs/efs/super.c

@@ -65,11 +65,18 @@ static struct inode *efs_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void efs_destroy_inode(struct inode *inode)
+static void efs_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
 	kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
 }
 }
 
 
+static void efs_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, efs_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct efs_inode_info *ei = (struct efs_inode_info *) foo;
 	struct efs_inode_info *ei = (struct efs_inode_info *) foo;

+ 8 - 1
fs/exofs/super.c

@@ -150,12 +150,19 @@ static struct inode *exofs_alloc_inode(struct super_block *sb)
 	return &oi->vfs_inode;
 	return &oi->vfs_inode;
 }
 }
 
 
+static void exofs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(exofs_inode_cachep, exofs_i(inode));
+}
+
 /*
 /*
  * Remove an inode from the cache
  * Remove an inode from the cache
  */
  */
 static void exofs_destroy_inode(struct inode *inode)
 static void exofs_destroy_inode(struct inode *inode)
 {
 {
-	kmem_cache_free(exofs_inode_cachep, exofs_i(inode));
+	call_rcu(&inode->i_rcu, exofs_i_callback);
 }
 }
 
 
 /*
 /*

+ 8 - 6
fs/exportfs/expfs.c

@@ -43,24 +43,26 @@ find_acceptable_alias(struct dentry *result,
 		void *context)
 		void *context)
 {
 {
 	struct dentry *dentry, *toput = NULL;
 	struct dentry *dentry, *toput = NULL;
+	struct inode *inode;
 
 
 	if (acceptable(context, result))
 	if (acceptable(context, result))
 		return result;
 		return result;
 
 
-	spin_lock(&dcache_lock);
-	list_for_each_entry(dentry, &result->d_inode->i_dentry, d_alias) {
-		dget_locked(dentry);
-		spin_unlock(&dcache_lock);
+	inode = result->d_inode;
+	spin_lock(&inode->i_lock);
+	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+		dget(dentry);
+		spin_unlock(&inode->i_lock);
 		if (toput)
 		if (toput)
 			dput(toput);
 			dput(toput);
 		if (dentry != result && acceptable(context, dentry)) {
 		if (dentry != result && acceptable(context, dentry)) {
 			dput(result);
 			dput(result);
 			return dentry;
 			return dentry;
 		}
 		}
-		spin_lock(&dcache_lock);
+		spin_lock(&inode->i_lock);
 		toput = dentry;
 		toput = dentry;
 	}
 	}
-	spin_unlock(&dcache_lock);
+	spin_unlock(&inode->i_lock);
 
 
 	if (toput)
 	if (toput)
 		dput(toput);
 		dput(toput);

+ 9 - 2
fs/ext2/acl.c

@@ -232,10 +232,17 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 }
 }
 
 
 int
 int
-ext2_check_acl(struct inode *inode, int mask)
+ext2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+	struct posix_acl *acl;
+
+	if (flags & IPERM_FLAG_RCU) {
+		if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
+			return -ECHILD;
+		return -EAGAIN;
+	}
 
 
+	acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
 	if (IS_ERR(acl))
 	if (IS_ERR(acl))
 		return PTR_ERR(acl);
 		return PTR_ERR(acl);
 	if (acl) {
 	if (acl) {

+ 1 - 1
fs/ext2/acl.h

@@ -54,7 +54,7 @@ static inline int ext2_acl_count(size_t size)
 #ifdef CONFIG_EXT2_FS_POSIX_ACL
 #ifdef CONFIG_EXT2_FS_POSIX_ACL
 
 
 /* acl.c */
 /* acl.c */
-extern int ext2_check_acl (struct inode *, int);
+extern int ext2_check_acl (struct inode *, int, unsigned int);
 extern int ext2_acl_chmod (struct inode *);
 extern int ext2_acl_chmod (struct inode *);
 extern int ext2_init_acl (struct inode *, struct inode *);
 extern int ext2_init_acl (struct inode *, struct inode *);
 
 

+ 8 - 1
fs/ext2/super.c

@@ -161,11 +161,18 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void ext2_destroy_inode(struct inode *inode)
+static void ext2_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
 	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
 }
 }
 
 
+static void ext2_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, ext2_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
 	struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;

+ 9 - 2
fs/ext3/acl.c

@@ -240,10 +240,17 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 }
 
 
 int
 int
-ext3_check_acl(struct inode *inode, int mask)
+ext3_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
+	struct posix_acl *acl;
+
+	if (flags & IPERM_FLAG_RCU) {
+		if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
+			return -ECHILD;
+		return -EAGAIN;
+	}
 
 
+	acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
 	if (IS_ERR(acl))
 	if (IS_ERR(acl))
 		return PTR_ERR(acl);
 		return PTR_ERR(acl);
 	if (acl) {
 	if (acl) {

+ 1 - 1
fs/ext3/acl.h

@@ -54,7 +54,7 @@ static inline int ext3_acl_count(size_t size)
 #ifdef CONFIG_EXT3_FS_POSIX_ACL
 #ifdef CONFIG_EXT3_FS_POSIX_ACL
 
 
 /* acl.c */
 /* acl.c */
-extern int ext3_check_acl (struct inode *, int);
+extern int ext3_check_acl (struct inode *, int, unsigned int);
 extern int ext3_acl_chmod (struct inode *);
 extern int ext3_acl_chmod (struct inode *);
 extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
 extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
 
 

+ 8 - 1
fs/ext3/super.c

@@ -479,6 +479,13 @@ static struct inode *ext3_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
+static void ext3_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(ext3_inode_cachep, EXT3_I(inode));
+}
+
 static void ext3_destroy_inode(struct inode *inode)
 static void ext3_destroy_inode(struct inode *inode)
 {
 {
 	if (!list_empty(&(EXT3_I(inode)->i_orphan))) {
 	if (!list_empty(&(EXT3_I(inode)->i_orphan))) {
@@ -489,7 +496,7 @@ static void ext3_destroy_inode(struct inode *inode)
 				false);
 				false);
 		dump_stack();
 		dump_stack();
 	}
 	}
-	kmem_cache_free(ext3_inode_cachep, EXT3_I(inode));
+	call_rcu(&inode->i_rcu, ext3_i_callback);
 }
 }
 
 
 static void init_once(void *foo)
 static void init_once(void *foo)

+ 9 - 2
fs/ext4/acl.c

@@ -238,10 +238,17 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 }
 
 
 int
 int
-ext4_check_acl(struct inode *inode, int mask)
+ext4_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	struct posix_acl *acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
+	struct posix_acl *acl;
+
+	if (flags & IPERM_FLAG_RCU) {
+		if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
+			return -ECHILD;
+		return -EAGAIN;
+	}
 
 
+	acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
 	if (IS_ERR(acl))
 	if (IS_ERR(acl))
 		return PTR_ERR(acl);
 		return PTR_ERR(acl);
 	if (acl) {
 	if (acl) {

+ 1 - 1
fs/ext4/acl.h

@@ -54,7 +54,7 @@ static inline int ext4_acl_count(size_t size)
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
 
 
 /* acl.c */
 /* acl.c */
-extern int ext4_check_acl(struct inode *, int);
+extern int ext4_check_acl(struct inode *, int, unsigned int);
 extern int ext4_acl_chmod(struct inode *);
 extern int ext4_acl_chmod(struct inode *);
 extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
 extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
 
 

+ 8 - 1
fs/ext4/super.c

@@ -841,6 +841,13 @@ static int ext4_drop_inode(struct inode *inode)
 	return drop;
 	return drop;
 }
 }
 
 
+static void ext4_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
+}
+
 static void ext4_destroy_inode(struct inode *inode)
 static void ext4_destroy_inode(struct inode *inode)
 {
 {
 	ext4_ioend_wait(inode);
 	ext4_ioend_wait(inode);
@@ -853,7 +860,7 @@ static void ext4_destroy_inode(struct inode *inode)
 				true);
 				true);
 		dump_stack();
 		dump_stack();
 	}
 	}
-	kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
+	call_rcu(&inode->i_rcu, ext4_i_callback);
 }
 }
 
 
 static void init_once(void *foo)
 static void init_once(void *foo)

+ 10 - 3
fs/fat/inode.c

@@ -514,11 +514,18 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
 	return &ei->vfs_inode;
 	return &ei->vfs_inode;
 }
 }
 
 
-static void fat_destroy_inode(struct inode *inode)
+static void fat_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
 	kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
 }
 }
 
 
+static void fat_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, fat_i_callback);
+}
+
 static void init_once(void *foo)
 static void init_once(void *foo)
 {
 {
 	struct msdos_inode_info *ei = (struct msdos_inode_info *)foo;
 	struct msdos_inode_info *ei = (struct msdos_inode_info *)foo;
@@ -743,7 +750,7 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb,
 	 */
 	 */
 	result = d_obtain_alias(inode);
 	result = d_obtain_alias(inode);
 	if (!IS_ERR(result))
 	if (!IS_ERR(result))
-		result->d_op = sb->s_root->d_op;
+		d_set_d_op(result, sb->s_root->d_op);
 	return result;
 	return result;
 }
 }
 
 
@@ -793,7 +800,7 @@ static struct dentry *fat_get_parent(struct dentry *child)
 
 
 	parent = d_obtain_alias(inode);
 	parent = d_obtain_alias(inode);
 	if (!IS_ERR(parent))
 	if (!IS_ERR(parent))
-		parent->d_op = sb->s_root->d_op;
+		d_set_d_op(parent, sb->s_root->d_op);
 out:
 out:
 	unlock_super(sb);
 	unlock_super(sb);
 
 

+ 13 - 10
fs/fat/namei_msdos.c

@@ -148,7 +148,8 @@ static int msdos_find(struct inode *dir, const unsigned char *name, int len,
  * that the existing dentry can be used. The msdos fs routines will
  * that the existing dentry can be used. The msdos fs routines will
  * return ENOENT or EINVAL as appropriate.
  * return ENOENT or EINVAL as appropriate.
  */
  */
-static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
+static int msdos_hash(const struct dentry *dentry, const struct inode *inode,
+	       struct qstr *qstr)
 {
 {
 	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
 	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
 	unsigned char msdos_name[MSDOS_NAME];
 	unsigned char msdos_name[MSDOS_NAME];
@@ -164,16 +165,18 @@ static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
  * Compare two msdos names. If either of the names are invalid,
  * Compare two msdos names. If either of the names are invalid,
  * we fall back to doing the standard name comparison.
  * we fall back to doing the standard name comparison.
  */
  */
-static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+static int msdos_cmp(const struct dentry *parent, const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
-	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
+	struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
 	unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
 	unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
 	int error;
 	int error;
 
 
-	error = msdos_format_name(a->name, a->len, a_msdos_name, options);
+	error = msdos_format_name(name->name, name->len, a_msdos_name, options);
 	if (error)
 	if (error)
 		goto old_compare;
 		goto old_compare;
-	error = msdos_format_name(b->name, b->len, b_msdos_name, options);
+	error = msdos_format_name(str, len, b_msdos_name, options);
 	if (error)
 	if (error)
 		goto old_compare;
 		goto old_compare;
 	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
 	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
@@ -182,8 +185,8 @@ out:
 
 
 old_compare:
 old_compare:
 	error = 1;
 	error = 1;
-	if (a->len == b->len)
-		error = memcmp(a->name, b->name, a->len);
+	if (name->len == len)
+		error = memcmp(name->name, str, len);
 	goto out;
 	goto out;
 }
 }
 
 
@@ -224,10 +227,10 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
 	}
 	}
 out:
 out:
 	unlock_super(sb);
 	unlock_super(sb);
-	dentry->d_op = &msdos_dentry_operations;
+	d_set_d_op(dentry, &msdos_dentry_operations);
 	dentry = d_splice_alias(inode, dentry);
 	dentry = d_splice_alias(inode, dentry);
 	if (dentry)
 	if (dentry)
-		dentry->d_op = &msdos_dentry_operations;
+		d_set_d_op(dentry, &msdos_dentry_operations);
 	return dentry;
 	return dentry;
 
 
 error:
 error:
@@ -670,7 +673,7 @@ static int msdos_fill_super(struct super_block *sb, void *data, int silent)
 	}
 	}
 
 
 	sb->s_flags |= MS_NOATIME;
 	sb->s_flags |= MS_NOATIME;
-	sb->s_root->d_op = &msdos_dentry_operations;
+	d_set_d_op(sb->s_root, &msdos_dentry_operations);
 	unlock_super(sb);
 	unlock_super(sb);
 	return 0;
 	return 0;
 }
 }

+ 35 - 20
fs/fat/namei_vfat.c

@@ -43,6 +43,9 @@ static int vfat_revalidate_shortname(struct dentry *dentry)
 
 
 static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
 static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
 {
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
 	/* This is not negative dentry. Always valid. */
 	/* This is not negative dentry. Always valid. */
 	if (dentry->d_inode)
 	if (dentry->d_inode)
 		return 1;
 		return 1;
@@ -51,6 +54,9 @@ static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
 
 
 static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
 static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
 {
 {
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
 	/*
 	/*
 	 * This is not negative dentry. Always valid.
 	 * This is not negative dentry. Always valid.
 	 *
 	 *
@@ -85,22 +91,26 @@ static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
 }
 }
 
 
 /* returns the length of a struct qstr, ignoring trailing dots */
 /* returns the length of a struct qstr, ignoring trailing dots */
-static unsigned int vfat_striptail_len(struct qstr *qstr)
+static unsigned int __vfat_striptail_len(unsigned int len, const char *name)
 {
 {
-	unsigned int len = qstr->len;
-
-	while (len && qstr->name[len - 1] == '.')
+	while (len && name[len - 1] == '.')
 		len--;
 		len--;
 	return len;
 	return len;
 }
 }
 
 
+static unsigned int vfat_striptail_len(const struct qstr *qstr)
+{
+	return __vfat_striptail_len(qstr->len, qstr->name);
+}
+
 /*
 /*
  * Compute the hash for the vfat name corresponding to the dentry.
  * Compute the hash for the vfat name corresponding to the dentry.
  * Note: if the name is invalid, we leave the hash code unchanged so
  * Note: if the name is invalid, we leave the hash code unchanged so
  * that the existing dentry can be used. The vfat fs routines will
  * that the existing dentry can be used. The vfat fs routines will
  * return ENOENT or EINVAL as appropriate.
  * return ENOENT or EINVAL as appropriate.
  */
  */
-static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
+static int vfat_hash(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *qstr)
 {
 {
 	qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));
 	qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));
 	return 0;
 	return 0;
@@ -112,9 +122,10 @@ static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
  * that the existing dentry can be used. The vfat fs routines will
  * that the existing dentry can be used. The vfat fs routines will
  * return ENOENT or EINVAL as appropriate.
  * return ENOENT or EINVAL as appropriate.
  */
  */
-static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
+static int vfat_hashi(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *qstr)
 {
 {
-	struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
+	struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
 	const unsigned char *name;
 	const unsigned char *name;
 	unsigned int len;
 	unsigned int len;
 	unsigned long hash;
 	unsigned long hash;
@@ -133,16 +144,18 @@ static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
 /*
 /*
  * Case insensitive compare of two vfat names.
  * Case insensitive compare of two vfat names.
  */
  */
-static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
+static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
-	struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
+	struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
 	unsigned int alen, blen;
 	unsigned int alen, blen;
 
 
 	/* A filename cannot end in '.' or we treat it like it has none */
 	/* A filename cannot end in '.' or we treat it like it has none */
-	alen = vfat_striptail_len(a);
-	blen = vfat_striptail_len(b);
+	alen = vfat_striptail_len(name);
+	blen = __vfat_striptail_len(len, str);
 	if (alen == blen) {
 	if (alen == blen) {
-		if (nls_strnicmp(t, a->name, b->name, alen) == 0)
+		if (nls_strnicmp(t, name->name, str, alen) == 0)
 			return 0;
 			return 0;
 	}
 	}
 	return 1;
 	return 1;
@@ -151,15 +164,17 @@ static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
 /*
 /*
  * Case sensitive compare of two vfat names.
  * Case sensitive compare of two vfat names.
  */
  */
-static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+static int vfat_cmp(const struct dentry *parent, const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
 	unsigned int alen, blen;
 	unsigned int alen, blen;
 
 
 	/* A filename cannot end in '.' or we treat it like it has none */
 	/* A filename cannot end in '.' or we treat it like it has none */
-	alen = vfat_striptail_len(a);
-	blen = vfat_striptail_len(b);
+	alen = vfat_striptail_len(name);
+	blen = __vfat_striptail_len(len, str);
 	if (alen == blen) {
 	if (alen == blen) {
-		if (strncmp(a->name, b->name, alen) == 0)
+		if (strncmp(name->name, str, alen) == 0)
 			return 0;
 			return 0;
 	}
 	}
 	return 1;
 	return 1;
@@ -757,11 +772,11 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
 
 
 out:
 out:
 	unlock_super(sb);
 	unlock_super(sb);
-	dentry->d_op = sb->s_root->d_op;
+	d_set_d_op(dentry, sb->s_root->d_op);
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
 	dentry = d_splice_alias(inode, dentry);
 	dentry = d_splice_alias(inode, dentry);
 	if (dentry) {
 	if (dentry) {
-		dentry->d_op = sb->s_root->d_op;
+		d_set_d_op(dentry, sb->s_root->d_op);
 		dentry->d_time = dentry->d_parent->d_inode->i_version;
 		dentry->d_time = dentry->d_parent->d_inode->i_version;
 	}
 	}
 	return dentry;
 	return dentry;
@@ -1063,9 +1078,9 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent)
 	}
 	}
 
 
 	if (MSDOS_SB(sb)->options.name_check != 's')
 	if (MSDOS_SB(sb)->options.name_check != 's')
-		sb->s_root->d_op = &vfat_ci_dentry_ops;
+		d_set_d_op(sb->s_root, &vfat_ci_dentry_ops);
 	else
 	else
-		sb->s_root->d_op = &vfat_dentry_ops;
+		d_set_d_op(sb->s_root, &vfat_dentry_ops);
 
 
 	unlock_super(sb);
 	unlock_super(sb);
 	return 0;
 	return 0;

+ 3 - 0
fs/filesystems.c

@@ -115,6 +115,9 @@ int unregister_filesystem(struct file_system_type * fs)
 		tmp = &(*tmp)->next;
 		tmp = &(*tmp)->next;
 	}
 	}
 	write_unlock(&file_systems_lock);
 	write_unlock(&file_systems_lock);
+
+	synchronize_rcu();
+
 	return -EINVAL;
 	return -EINVAL;
 }
 }
 
 

+ 8 - 1
fs/freevxfs/vxfs_inode.c

@@ -337,6 +337,13 @@ vxfs_iget(struct super_block *sbp, ino_t ino)
 	return ip;
 	return ip;
 }
 }
 
 
+static void vxfs_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(vxfs_inode_cachep, inode->i_private);
+}
+
 /**
 /**
  * vxfs_evict_inode - remove inode from main memory
  * vxfs_evict_inode - remove inode from main memory
  * @ip:		inode to discard.
  * @ip:		inode to discard.
@@ -350,5 +357,5 @@ vxfs_evict_inode(struct inode *ip)
 {
 {
 	truncate_inode_pages(&ip->i_data, 0);
 	truncate_inode_pages(&ip->i_data, 0);
 	end_writeback(ip);
 	end_writeback(ip);
-	kmem_cache_free(vxfs_inode_cachep, ip->i_private);
+	call_rcu(&ip->i_rcu, vxfs_i_callback);
 }
 }

+ 26 - 10
fs/fs_struct.c

@@ -14,12 +14,14 @@ void set_fs_root(struct fs_struct *fs, struct path *path)
 	struct path old_root;
 	struct path old_root;
 
 
 	spin_lock(&fs->lock);
 	spin_lock(&fs->lock);
+	write_seqcount_begin(&fs->seq);
 	old_root = fs->root;
 	old_root = fs->root;
 	fs->root = *path;
 	fs->root = *path;
-	path_get(path);
+	path_get_long(path);
+	write_seqcount_end(&fs->seq);
 	spin_unlock(&fs->lock);
 	spin_unlock(&fs->lock);
 	if (old_root.dentry)
 	if (old_root.dentry)
-		path_put(&old_root);
+		path_put_long(&old_root);
 }
 }
 
 
 /*
 /*
@@ -31,13 +33,15 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path)
 	struct path old_pwd;
 	struct path old_pwd;
 
 
 	spin_lock(&fs->lock);
 	spin_lock(&fs->lock);
+	write_seqcount_begin(&fs->seq);
 	old_pwd = fs->pwd;
 	old_pwd = fs->pwd;
 	fs->pwd = *path;
 	fs->pwd = *path;
-	path_get(path);
+	path_get_long(path);
+	write_seqcount_end(&fs->seq);
 	spin_unlock(&fs->lock);
 	spin_unlock(&fs->lock);
 
 
 	if (old_pwd.dentry)
 	if (old_pwd.dentry)
-		path_put(&old_pwd);
+		path_put_long(&old_pwd);
 }
 }
 
 
 void chroot_fs_refs(struct path *old_root, struct path *new_root)
 void chroot_fs_refs(struct path *old_root, struct path *new_root)
@@ -52,31 +56,33 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
 		fs = p->fs;
 		fs = p->fs;
 		if (fs) {
 		if (fs) {
 			spin_lock(&fs->lock);
 			spin_lock(&fs->lock);
+			write_seqcount_begin(&fs->seq);
 			if (fs->root.dentry == old_root->dentry
 			if (fs->root.dentry == old_root->dentry
 			    && fs->root.mnt == old_root->mnt) {
 			    && fs->root.mnt == old_root->mnt) {
-				path_get(new_root);
+				path_get_long(new_root);
 				fs->root = *new_root;
 				fs->root = *new_root;
 				count++;
 				count++;
 			}
 			}
 			if (fs->pwd.dentry == old_root->dentry
 			if (fs->pwd.dentry == old_root->dentry
 			    && fs->pwd.mnt == old_root->mnt) {
 			    && fs->pwd.mnt == old_root->mnt) {
-				path_get(new_root);
+				path_get_long(new_root);
 				fs->pwd = *new_root;
 				fs->pwd = *new_root;
 				count++;
 				count++;
 			}
 			}
+			write_seqcount_end(&fs->seq);
 			spin_unlock(&fs->lock);
 			spin_unlock(&fs->lock);
 		}
 		}
 		task_unlock(p);
 		task_unlock(p);
 	} while_each_thread(g, p);
 	} while_each_thread(g, p);
 	read_unlock(&tasklist_lock);
 	read_unlock(&tasklist_lock);
 	while (count--)
 	while (count--)
-		path_put(old_root);
+		path_put_long(old_root);
 }
 }
 
 
 void free_fs_struct(struct fs_struct *fs)
 void free_fs_struct(struct fs_struct *fs)
 {
 {
-	path_put(&fs->root);
-	path_put(&fs->pwd);
+	path_put_long(&fs->root);
+	path_put_long(&fs->pwd);
 	kmem_cache_free(fs_cachep, fs);
 	kmem_cache_free(fs_cachep, fs);
 }
 }
 
 
@@ -88,8 +94,10 @@ void exit_fs(struct task_struct *tsk)
 		int kill;
 		int kill;
 		task_lock(tsk);
 		task_lock(tsk);
 		spin_lock(&fs->lock);
 		spin_lock(&fs->lock);
+		write_seqcount_begin(&fs->seq);
 		tsk->fs = NULL;
 		tsk->fs = NULL;
 		kill = !--fs->users;
 		kill = !--fs->users;
+		write_seqcount_end(&fs->seq);
 		spin_unlock(&fs->lock);
 		spin_unlock(&fs->lock);
 		task_unlock(tsk);
 		task_unlock(tsk);
 		if (kill)
 		if (kill)
@@ -105,8 +113,15 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
 		fs->users = 1;
 		fs->users = 1;
 		fs->in_exec = 0;
 		fs->in_exec = 0;
 		spin_lock_init(&fs->lock);
 		spin_lock_init(&fs->lock);
+		seqcount_init(&fs->seq);
 		fs->umask = old->umask;
 		fs->umask = old->umask;
-		get_fs_root_and_pwd(old, &fs->root, &fs->pwd);
+
+		spin_lock(&old->lock);
+		fs->root = old->root;
+		path_get_long(&fs->root);
+		fs->pwd = old->pwd;
+		path_get_long(&fs->pwd);
+		spin_unlock(&old->lock);
 	}
 	}
 	return fs;
 	return fs;
 }
 }
@@ -144,6 +159,7 @@ EXPORT_SYMBOL(current_umask);
 struct fs_struct init_fs = {
 struct fs_struct init_fs = {
 	.users		= 1,
 	.users		= 1,
 	.lock		= __SPIN_LOCK_UNLOCKED(init_fs.lock),
 	.lock		= __SPIN_LOCK_UNLOCKED(init_fs.lock),
+	.seq		= SEQCNT_ZERO,
 	.umask		= 0022,
 	.umask		= 0022,
 };
 };
 
 

+ 13 - 5
fs/fuse/dir.c

@@ -156,8 +156,12 @@ u64 fuse_get_attr_version(struct fuse_conn *fc)
  */
  */
 static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
 static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
 {
 {
-	struct inode *inode = entry->d_inode;
+	struct inode *inode;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	inode = entry->d_inode;
 	if (inode && is_bad_inode(inode))
 	if (inode && is_bad_inode(inode))
 		return 0;
 		return 0;
 	else if (fuse_dentry_time(entry) < get_jiffies_64()) {
 	else if (fuse_dentry_time(entry) < get_jiffies_64()) {
@@ -347,7 +351,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
 	}
 	}
 
 
 	entry = newent ? newent : entry;
 	entry = newent ? newent : entry;
-	entry->d_op = &fuse_dentry_operations;
+	d_set_d_op(entry, &fuse_dentry_operations);
 	if (outarg_valid)
 	if (outarg_valid)
 		fuse_change_entry_timeout(entry, &outarg);
 		fuse_change_entry_timeout(entry, &outarg);
 	else
 	else
@@ -981,12 +985,15 @@ static int fuse_access(struct inode *inode, int mask)
  * access request is sent.  Execute permission is still checked
  * access request is sent.  Execute permission is still checked
  * locally based on file mode.
  * locally based on file mode.
  */
  */
-static int fuse_permission(struct inode *inode, int mask)
+static int fuse_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	bool refreshed = false;
 	bool refreshed = false;
 	int err = 0;
 	int err = 0;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	if (!fuse_allow_task(fc, current))
 	if (!fuse_allow_task(fc, current))
 		return -EACCES;
 		return -EACCES;
 
 
@@ -1001,7 +1008,7 @@ static int fuse_permission(struct inode *inode, int mask)
 	}
 	}
 
 
 	if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
 	if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
-		err = generic_permission(inode, mask, NULL);
+		err = generic_permission(inode, mask, flags, NULL);
 
 
 		/* If permission is denied, try to refresh file
 		/* If permission is denied, try to refresh file
 		   attributes.  This is also needed, because the root
 		   attributes.  This is also needed, because the root
@@ -1009,7 +1016,8 @@ static int fuse_permission(struct inode *inode, int mask)
 		if (err == -EACCES && !refreshed) {
 		if (err == -EACCES && !refreshed) {
 			err = fuse_do_getattr(inode, NULL, NULL);
 			err = fuse_do_getattr(inode, NULL, NULL);
 			if (!err)
 			if (!err)
-				err = generic_permission(inode, mask, NULL);
+				err = generic_permission(inode, mask,
+							flags, NULL);
 		}
 		}
 
 
 		/* Note: the opposite of the above test does not
 		/* Note: the opposite of the above test does not

+ 10 - 3
fs/fuse/inode.c

@@ -99,6 +99,13 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
 	return inode;
 	return inode;
 }
 }
 
 
+static void fuse_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
+	kmem_cache_free(fuse_inode_cachep, inode);
+}
+
 static void fuse_destroy_inode(struct inode *inode)
 static void fuse_destroy_inode(struct inode *inode)
 {
 {
 	struct fuse_inode *fi = get_fuse_inode(inode);
 	struct fuse_inode *fi = get_fuse_inode(inode);
@@ -106,7 +113,7 @@ static void fuse_destroy_inode(struct inode *inode)
 	BUG_ON(!list_empty(&fi->queued_writes));
 	BUG_ON(!list_empty(&fi->queued_writes));
 	if (fi->forget_req)
 	if (fi->forget_req)
 		fuse_request_free(fi->forget_req);
 		fuse_request_free(fi->forget_req);
-	kmem_cache_free(fuse_inode_cachep, inode);
+	call_rcu(&inode->i_rcu, fuse_i_callback);
 }
 }
 
 
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
 void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
@@ -619,7 +626,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb,
 
 
 	entry = d_obtain_alias(inode);
 	entry = d_obtain_alias(inode);
 	if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID) {
 	if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID) {
-		entry->d_op = &fuse_dentry_operations;
+		d_set_d_op(entry, &fuse_dentry_operations);
 		fuse_invalidate_entry_cache(entry);
 		fuse_invalidate_entry_cache(entry);
 	}
 	}
 
 
@@ -721,7 +728,7 @@ static struct dentry *fuse_get_parent(struct dentry *child)
 
 
 	parent = d_obtain_alias(inode);
 	parent = d_obtain_alias(inode);
 	if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID) {
 	if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID) {
-		parent->d_op = &fuse_dentry_operations;
+		d_set_d_op(parent, &fuse_dentry_operations);
 		fuse_invalidate_entry_cache(parent);
 		fuse_invalidate_entry_cache(parent);
 	}
 	}
 
 

+ 13 - 7
fs/generic_acl.c

@@ -190,14 +190,20 @@ generic_acl_chmod(struct inode *inode)
 }
 }
 
 
 int
 int
-generic_check_acl(struct inode *inode, int mask)
+generic_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
-
-	if (acl) {
-		int error = posix_acl_permission(inode, acl, mask);
-		posix_acl_release(acl);
-		return error;
+	if (flags & IPERM_FLAG_RCU) {
+		if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
+			return -ECHILD;
+	} else {
+		struct posix_acl *acl;
+
+		acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
+		if (acl) {
+			int error = posix_acl_permission(inode, acl, mask);
+			posix_acl_release(acl);
+			return error;
+		}
 	}
 	}
 	return -EAGAIN;
 	return -EAGAIN;
 }
 }

+ 4 - 1
fs/gfs2/acl.c

@@ -75,11 +75,14 @@ static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type)
  * Returns: errno
  * Returns: errno
  */
  */
 
 
-int gfs2_check_acl(struct inode *inode, int mask)
+int gfs2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
 {
 	struct posix_acl *acl;
 	struct posix_acl *acl;
 	int error;
 	int error;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
 	acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
 	acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
 	if (IS_ERR(acl))
 	if (IS_ERR(acl))
 		return PTR_ERR(acl);
 		return PTR_ERR(acl);

+ 1 - 1
fs/gfs2/acl.h

@@ -16,7 +16,7 @@
 #define GFS2_POSIX_ACL_DEFAULT		"posix_acl_default"
 #define GFS2_POSIX_ACL_DEFAULT		"posix_acl_default"
 #define GFS2_ACL_MAX_ENTRIES		25
 #define GFS2_ACL_MAX_ENTRIES		25
 
 
-extern int gfs2_check_acl(struct inode *inode, int mask);
+extern int gfs2_check_acl(struct inode *inode, int mask, unsigned int);
 extern int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode);
 extern int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode);
 extern int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr);
 extern int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr);
 extern const struct xattr_handler gfs2_xattr_system_handler;
 extern const struct xattr_handler gfs2_xattr_system_handler;

+ 16 - 6
fs/gfs2/dentry.c

@@ -11,6 +11,7 @@
 #include <linux/completion.h>
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/namei.h>
 #include <linux/crc32.h>
 #include <linux/crc32.h>
 
 
 #include "gfs2.h"
 #include "gfs2.h"
@@ -34,15 +35,23 @@
 
 
 static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd)
 static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd)
 {
 {
-	struct dentry *parent = dget_parent(dentry);
-	struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode);
-	struct gfs2_inode *dip = GFS2_I(parent->d_inode);
-	struct inode *inode = dentry->d_inode;
+	struct dentry *parent;
+	struct gfs2_sbd *sdp;
+	struct gfs2_inode *dip;
+	struct inode *inode;
 	struct gfs2_holder d_gh;
 	struct gfs2_holder d_gh;
 	struct gfs2_inode *ip = NULL;
 	struct gfs2_inode *ip = NULL;
 	int error;
 	int error;
 	int had_lock = 0;
 	int had_lock = 0;
 
 
+	if (nd->flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	parent = dget_parent(dentry);
+	sdp = GFS2_SB(parent->d_inode);
+	dip = GFS2_I(parent->d_inode);
+	inode = dentry->d_inode;
+
 	if (inode) {
 	if (inode) {
 		if (is_bad_inode(inode))
 		if (is_bad_inode(inode))
 			goto invalid;
 			goto invalid;
@@ -100,13 +109,14 @@ fail:
 	return 0;
 	return 0;
 }
 }
 
 
-static int gfs2_dhash(struct dentry *dentry, struct qstr *str)
+static int gfs2_dhash(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *str)
 {
 {
 	str->hash = gfs2_disk_hash(str->name, str->len);
 	str->hash = gfs2_disk_hash(str->name, str->len);
 	return 0;
 	return 0;
 }
 }
 
 
-static int gfs2_dentry_delete(struct dentry *dentry)
+static int gfs2_dentry_delete(const struct dentry *dentry)
 {
 {
 	struct gfs2_inode *ginode;
 	struct gfs2_inode *ginode;
 
 

+ 2 - 2
fs/gfs2/export.c

@@ -130,7 +130,7 @@ static struct dentry *gfs2_get_parent(struct dentry *child)
 
 
 	dentry = d_obtain_alias(gfs2_lookupi(child->d_inode, &gfs2_qdotdot, 1));
 	dentry = d_obtain_alias(gfs2_lookupi(child->d_inode, &gfs2_qdotdot, 1));
 	if (!IS_ERR(dentry))
 	if (!IS_ERR(dentry))
-		dentry->d_op = &gfs2_dops;
+		d_set_d_op(dentry, &gfs2_dops);
 	return dentry;
 	return dentry;
 }
 }
 
 
@@ -158,7 +158,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
 out_inode:
 out_inode:
 	dentry = d_obtain_alias(inode);
 	dentry = d_obtain_alias(inode);
 	if (!IS_ERR(dentry))
 	if (!IS_ERR(dentry))
-		dentry->d_op = &gfs2_dops;
+		d_set_d_op(dentry, &gfs2_dops);
 	return dentry;
 	return dentry;
 }
 }
 
 

+ 1 - 1
fs/gfs2/file.c

@@ -241,7 +241,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
 	    !capable(CAP_LINUX_IMMUTABLE))
 	    !capable(CAP_LINUX_IMMUTABLE))
 		goto out;
 		goto out;
 	if (!IS_IMMUTABLE(inode)) {
 	if (!IS_IMMUTABLE(inode)) {
-		error = gfs2_permission(inode, MAY_WRITE);
+		error = gfs2_permission(inode, MAY_WRITE, 0);
 		if (error)
 		if (error)
 			goto out;
 			goto out;
 	}
 	}

+ 2 - 2
fs/gfs2/inode.c

@@ -509,7 +509,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
 	}
 	}
 
 
 	if (!is_root) {
 	if (!is_root) {
-		error = gfs2_permission(dir, MAY_EXEC);
+		error = gfs2_permission(dir, MAY_EXEC, 0);
 		if (error)
 		if (error)
 			goto out;
 			goto out;
 	}
 	}
@@ -539,7 +539,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
 {
 {
 	int error;
 	int error;
 
 
-	error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
+	error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
 	if (error)
 	if (error)
 		return error;
 		return error;
 
 

+ 1 - 1
fs/gfs2/inode.h

@@ -113,7 +113,7 @@ extern struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
 extern struct inode *gfs2_createi(struct gfs2_holder *ghs,
 extern struct inode *gfs2_createi(struct gfs2_holder *ghs,
 				  const struct qstr *name,
 				  const struct qstr *name,
 				  unsigned int mode, dev_t dev);
 				  unsigned int mode, dev_t dev);
-extern int gfs2_permission(struct inode *inode, int mask);
+extern int gfs2_permission(struct inode *inode, int mask, unsigned int flags);
 extern int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
 extern int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
 extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
 extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
 extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
 extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);

+ 1 - 1
fs/gfs2/ops_fstype.c

@@ -440,7 +440,7 @@ static int gfs2_lookup_root(struct super_block *sb, struct dentry **dptr,
 		iput(inode);
 		iput(inode);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	dentry->d_op = &gfs2_dops;
+	d_set_d_op(dentry, &gfs2_dops);
 	*dptr = dentry;
 	*dptr = dentry;
 	return 0;
 	return 0;
 }
 }

+ 12 - 8
fs/gfs2/ops_inode.c

@@ -106,7 +106,7 @@ static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
 {
 {
 	struct inode *inode = NULL;
 	struct inode *inode = NULL;
 
 
-	dentry->d_op = &gfs2_dops;
+	d_set_d_op(dentry, &gfs2_dops);
 
 
 	inode = gfs2_lookupi(dir, &dentry->d_name, 0);
 	inode = gfs2_lookupi(dir, &dentry->d_name, 0);
 	if (inode && IS_ERR(inode))
 	if (inode && IS_ERR(inode))
@@ -166,7 +166,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
 	if (error)
 	if (error)
 		goto out_child;
 		goto out_child;
 
 
-	error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC);
+	error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
 	if (error)
 	if (error)
 		goto out_gunlock;
 		goto out_gunlock;
 
 
@@ -289,7 +289,7 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
 	if (IS_APPEND(&dip->i_inode))
 	if (IS_APPEND(&dip->i_inode))
 		return -EPERM;
 		return -EPERM;
 
 
-	error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC);
+	error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
 	if (error)
 	if (error)
 		return error;
 		return error;
 
 
@@ -822,7 +822,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
 			}
 			}
 		}
 		}
 	} else {
 	} else {
-		error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC);
+		error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
 		if (error)
 		if (error)
 			goto out_gunlock;
 			goto out_gunlock;
 
 
@@ -857,7 +857,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
 	/* Check out the dir to be renamed */
 	/* Check out the dir to be renamed */
 
 
 	if (dir_rename) {
 	if (dir_rename) {
-		error = gfs2_permission(odentry->d_inode, MAY_WRITE);
+		error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
 		if (error)
 		if (error)
 			goto out_gunlock;
 			goto out_gunlock;
 	}
 	}
@@ -1041,13 +1041,17 @@ static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
  * Returns: errno
  * Returns: errno
  */
  */
 
 
-int gfs2_permission(struct inode *inode, int mask)
+int gfs2_permission(struct inode *inode, int mask, unsigned int flags)
 {
 {
-	struct gfs2_inode *ip = GFS2_I(inode);
+	struct gfs2_inode *ip;
 	struct gfs2_holder i_gh;
 	struct gfs2_holder i_gh;
 	int error;
 	int error;
 	int unlock = 0;
 	int unlock = 0;
 
 
+	if (flags & IPERM_FLAG_RCU)
+		return -ECHILD;
+
+	ip = GFS2_I(inode);
 	if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
 	if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
 		error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
 		error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
 		if (error)
 		if (error)
@@ -1058,7 +1062,7 @@ int gfs2_permission(struct inode *inode, int mask)
 	if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
 	if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
 		error = -EACCES;
 		error = -EACCES;
 	else
 	else
-		error = generic_permission(inode, mask, gfs2_check_acl);
+		error = generic_permission(inode, mask, flags, gfs2_check_acl);
 	if (unlock)
 	if (unlock)
 		gfs2_glock_dq_uninit(&i_gh);
 		gfs2_glock_dq_uninit(&i_gh);
 
 

+ 8 - 1
fs/gfs2/super.c

@@ -1405,11 +1405,18 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb)
 	return &ip->i_inode;
 	return &ip->i_inode;
 }
 }
 
 
-static void gfs2_destroy_inode(struct inode *inode)
+static void gfs2_i_callback(struct rcu_head *head)
 {
 {
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	INIT_LIST_HEAD(&inode->i_dentry);
 	kmem_cache_free(gfs2_inode_cachep, inode);
 	kmem_cache_free(gfs2_inode_cachep, inode);
 }
 }
 
 
+static void gfs2_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, gfs2_i_callback);
+}
+
 const struct super_operations gfs2_super_ops = {
 const struct super_operations gfs2_super_ops = {
 	.alloc_inode		= gfs2_alloc_inode,
 	.alloc_inode		= gfs2_alloc_inode,
 	.destroy_inode		= gfs2_destroy_inode,
 	.destroy_inode		= gfs2_destroy_inode,

+ 1 - 1
fs/hfs/dir.c

@@ -25,7 +25,7 @@ static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
 	struct inode *inode = NULL;
 	struct inode *inode = NULL;
 	int res;
 	int res;
 
 
-	dentry->d_op = &hfs_dentry_operations;
+	d_set_d_op(dentry, &hfs_dentry_operations);
 
 
 	hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
 	hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
 	hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
 	hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);

+ 6 - 2
fs/hfs/hfs_fs.h

@@ -213,10 +213,14 @@ extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
 /* string.c */
 /* string.c */
 extern const struct dentry_operations hfs_dentry_operations;
 extern const struct dentry_operations hfs_dentry_operations;
 
 
-extern int hfs_hash_dentry(struct dentry *, struct qstr *);
+extern int hfs_hash_dentry(const struct dentry *, const struct inode *,
+		struct qstr *);
 extern int hfs_strcmp(const unsigned char *, unsigned int,
 extern int hfs_strcmp(const unsigned char *, unsigned int,
 		      const unsigned char *, unsigned int);
 		      const unsigned char *, unsigned int);
-extern int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+extern int hfs_compare_dentry(const struct dentry *parent,
+		const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name);
 
 
 /* trans.c */
 /* trans.c */
 extern void hfs_asc2mac(struct super_block *, struct hfs_name *, struct qstr *);
 extern void hfs_asc2mac(struct super_block *, struct hfs_name *, struct qstr *);

+ 9 - 8
fs/hfs/string.c

@@ -51,7 +51,8 @@ static unsigned char caseorder[256] = {
 /*
 /*
  * Hash a string to an integer in a case-independent way
  * Hash a string to an integer in a case-independent way
  */
  */
-int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
+int hfs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+		struct qstr *this)
 {
 {
 	const unsigned char *name = this->name;
 	const unsigned char *name = this->name;
 	unsigned int hash, len = this->len;
 	unsigned int hash, len = this->len;
@@ -92,21 +93,21 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1,
  * Test for equality of two strings in the HFS filename character ordering.
  * Test for equality of two strings in the HFS filename character ordering.
  * return 1 on failure and 0 on success
  * return 1 on failure and 0 on success
  */
  */
-int hfs_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2)
+int hfs_compare_dentry(const struct dentry *parent, const struct inode *pinode,
+		const struct dentry *dentry, const struct inode *inode,
+		unsigned int len, const char *str, const struct qstr *name)
 {
 {
 	const unsigned char *n1, *n2;
 	const unsigned char *n1, *n2;
-	int len;
 
 
-	len = s1->len;
 	if (len >= HFS_NAMELEN) {
 	if (len >= HFS_NAMELEN) {
-		if (s2->len < HFS_NAMELEN)
+		if (name->len < HFS_NAMELEN)
 			return 1;
 			return 1;
 		len = HFS_NAMELEN;
 		len = HFS_NAMELEN;
-	} else if (len != s2->len)
+	} else if (len != name->len)
 		return 1;
 		return 1;
 
 
-	n1 = s1->name;
-	n2 = s2->name;
+	n1 = str;
+	n2 = name->name;
 	while (len--) {
 	while (len--) {
 		if (caseorder[*n1++] != caseorder[*n2++])
 		if (caseorder[*n1++] != caseorder[*n2++])
 			return 1;
 			return 1;

部分文件因文件數量過多而無法顯示