Merge branch 'vfs-scale-working' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 Jan 2011 16:56:33 +0000 (08:56 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 Jan 2011 16:56:33 +0000 (08:56 -0800)
* '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
  ...

212 files changed:
Documentation/filesystems/Locking
Documentation/filesystems/dentry-locking.txt [deleted file]
Documentation/filesystems/path-lookup.txt [new file with mode: 0644]
Documentation/filesystems/porting
Documentation/filesystems/vfs.txt
arch/ia64/kernel/perfmon.c
arch/powerpc/platforms/cell/spufs/inode.c
drivers/infiniband/hw/ipath/ipath_fs.c
drivers/infiniband/hw/qib/qib_fs.c
drivers/mtd/mtdchar.c
drivers/staging/autofs/root.c
drivers/staging/pohmelfs/inode.c
drivers/staging/pohmelfs/path_entry.c
drivers/staging/smbfs/cache.c
drivers/staging/smbfs/dir.c
drivers/staging/smbfs/file.c
drivers/staging/smbfs/inode.c
drivers/usb/core/inode.c
fs/9p/acl.c
fs/9p/acl.h
fs/9p/vfs_dentry.c
fs/9p/vfs_inode.c
fs/adfs/dir.c
fs/adfs/super.c
fs/affs/amigaffs.c
fs/affs/namei.c
fs/affs/super.c
fs/afs/dir.c
fs/afs/internal.h
fs/afs/security.c
fs/afs/super.c
fs/anon_inodes.c
fs/autofs4/autofs_i.h
fs/autofs4/expire.c
fs/autofs4/inode.c
fs/autofs4/root.c
fs/autofs4/waitq.c
fs/bad_inode.c
fs/befs/linuxvfs.c
fs/bfs/inode.c
fs/block_dev.c
fs/btrfs/acl.c
fs/btrfs/ctree.h
fs/btrfs/export.c
fs/btrfs/inode.c
fs/ceph/dir.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/super.h
fs/cifs/cifsfs.c
fs/cifs/dir.c
fs/cifs/inode.c
fs/cifs/link.c
fs/cifs/readdir.c
fs/coda/cache.c
fs/coda/dir.c
fs/coda/inode.c
fs/coda/pioctl.c
fs/configfs/configfs_internal.h
fs/configfs/dir.c
fs/configfs/inode.c
fs/dcache.c
fs/ecryptfs/dentry.c
fs/ecryptfs/inode.c
fs/ecryptfs/main.c
fs/ecryptfs/super.c
fs/efs/super.c
fs/exofs/super.c
fs/exportfs/expfs.c
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext2/super.c
fs/ext3/acl.c
fs/ext3/acl.h
fs/ext3/super.c
fs/ext4/acl.c
fs/ext4/acl.h
fs/ext4/super.c
fs/fat/inode.c
fs/fat/namei_msdos.c
fs/fat/namei_vfat.c
fs/filesystems.c
fs/freevxfs/vxfs_inode.c
fs/fs_struct.c
fs/fuse/dir.c
fs/fuse/inode.c
fs/generic_acl.c
fs/gfs2/acl.c
fs/gfs2/acl.h
fs/gfs2/dentry.c
fs/gfs2/export.c
fs/gfs2/file.c
fs/gfs2/inode.c
fs/gfs2/inode.h
fs/gfs2/ops_fstype.c
fs/gfs2/ops_inode.c
fs/gfs2/super.c
fs/hfs/dir.c
fs/hfs/hfs_fs.h
fs/hfs/string.c
fs/hfs/super.c
fs/hfs/sysdep.c
fs/hfsplus/dir.c
fs/hfsplus/hfsplus_fs.h
fs/hfsplus/super.c
fs/hfsplus/unicode.c
fs/hostfs/hostfs_kern.c
fs/hpfs/dentry.c
fs/hpfs/namei.c
fs/hpfs/super.c
fs/hppfs/hppfs.c
fs/hugetlbfs/inode.c
fs/inode.c
fs/internal.h
fs/isofs/inode.c
fs/isofs/namei.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/super.c
fs/jfs/acl.c
fs/jfs/jfs_acl.h
fs/jfs/namei.c
fs/jfs/super.c
fs/libfs.c
fs/locks.c
fs/logfs/dir.c
fs/logfs/inode.c
fs/minix/inode.c
fs/minix/namei.c
fs/namei.c
fs/namespace.c
fs/ncpfs/dir.c
fs/ncpfs/inode.c
fs/ncpfs/ncplib_kernel.h
fs/nfs/dir.c
fs/nfs/getroot.c
fs/nfs/inode.c
fs/nfs/namespace.c
fs/nfs/unlink.c
fs/nfsd/vfs.c
fs/nilfs2/inode.c
fs/nilfs2/nilfs.h
fs/nilfs2/super.c
fs/notify/fsnotify.c
fs/ntfs/inode.c
fs/ocfs2/acl.c
fs/ocfs2/acl.h
fs/ocfs2/dcache.c
fs/ocfs2/dlmfs/dlmfs.c
fs/ocfs2/export.c
fs/ocfs2/file.c
fs/ocfs2/file.h
fs/ocfs2/namei.c
fs/ocfs2/super.c
fs/openpromfs/inode.c
fs/pipe.c
fs/pnode.c
fs/proc/base.c
fs/proc/generic.c
fs/proc/inode.c
fs/proc/proc_sysctl.c
fs/qnx4/inode.c
fs/reiserfs/super.c
fs/reiserfs/xattr.c
fs/romfs/super.c
fs/squashfs/super.c
fs/super.c
fs/sysfs/dir.c
fs/sysfs/inode.c
fs/sysfs/sysfs.h
fs/sysv/inode.c
fs/sysv/namei.c
fs/sysv/super.c
fs/ubifs/super.c
fs/udf/super.c
fs/ufs/super.c
fs/xfs/linux-2.6/xfs_acl.c
fs/xfs/xfs_acl.h
fs/xfs/xfs_iget.c
include/linux/bit_spinlock.h
include/linux/coda_linux.h
include/linux/dcache.h
include/linux/fs.h
include/linux/fs_struct.h
include/linux/fsnotify.h
include/linux/fsnotify_backend.h
include/linux/generic_acl.h
include/linux/list_bl.h [new file with mode: 0644]
include/linux/mount.h
include/linux/namei.h
include/linux/ncp_fs.h
include/linux/nfs_fs.h
include/linux/path.h
include/linux/posix_acl.h
include/linux/rculist_bl.h [new file with mode: 0644]
include/linux/reiserfs_xattr.h
include/linux/security.h
include/linux/seqlock.h
include/linux/slab.h
ipc/mqueue.c
kernel/cgroup.c
mm/filemap.c
mm/shmem.c
mm/slab.c
mm/slob.c
mm/slub.c
mm/util.c
net/socket.c
net/sunrpc/rpc_pipe.c
security/security.c
security/selinux/selinuxfs.c
security/tomoyo/realpath.c

index 33fa3e5..977d891 100644 (file)
@@ -9,22 +9,25 @@ be able to use diff(1).
 
 --------------------------- dentry_operations --------------------------
 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 *);
        void (*d_release)(struct dentry *);
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
 
 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
 
 --------------------------- inode_operations --------------------------- 
@@ -44,8 +47,8 @@ ata *);
        void * (*follow_link) (struct dentry *, struct nameidata *);
        void (*put_link) (struct dentry *, struct nameidata *, void *);
        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 (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -73,7 +76,7 @@ follow_link:  no
 put_link:      no
 truncate:      yes             (see below)
 setattr:       yes
-permission:    no
+permission:    no (may not block if called in rcu-walk mode)
 check_acl:     no
 getattr:       no
 setxattr:      yes
diff --git a/Documentation/filesystems/dentry-locking.txt b/Documentation/filesystems/dentry-locking.txt
deleted file mode 100644 (file)
index 79334ed..0000000
+++ /dev/null
@@ -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
-
-
-
diff --git a/Documentation/filesystems/path-lookup.txt b/Documentation/filesystems/path-lookup.txt
new file mode 100644 (file)
index 0000000..eb59c8b
--- /dev/null
@@ -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
+
+
index b12c895..07a32b4 100644 (file)
@@ -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
 if at least one of the following is true:
        * filesystem has no cross-directory rename()
-       * dcache_lock is held
        * we know that parent had been locked (e.g. we are looking at
 ->d_parent of ->lookup() argument).
        * 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
 free the on-disk inode, you may end up doing that while ->write_inode() is writing
 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.
index 20899e0..fbb324e 100644 (file)
@@ -325,7 +325,8 @@ struct inode_operations {
         void * (*follow_link) (struct dentry *, struct nameidata *);
         void (*put_link) (struct dentry *, struct nameidata *, void *);
        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 (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        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
        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
        is called by chmod(2) and related system calls.
 
@@ -847,9 +855,12 @@ defined:
 
 struct dentry_operations {
        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_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
@@ -860,13 +871,45 @@ struct dentry_operations {
        dcache. Most filesystems leave this as NULL, because all their
        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
 
@@ -910,14 +953,11 @@ manipulate dentries:
        the usage count)
 
   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
        subsequent call to dput() will deallocate the dentry if its
index 39e534f..f099b82 100644 (file)
@@ -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
  * 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
 init_pfm_fs(void)
@@ -2185,7 +2185,7 @@ static const struct file_operations pfm_file_ops = {
 };
 
 static int
-pfmfs_delete_dentry(struct dentry *dentry)
+pfmfs_delete_dentry(const struct dentry *dentry)
 {
        return 1;
 }
@@ -2233,7 +2233,7 @@ pfm_alloc_file(pfm_context_t *ctx)
        }
        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);
 
        file = alloc_file(&path, FMODE_READ, &pfm_file_ops);
index 3532b92..856e9c3 100644 (file)
@@ -71,12 +71,18 @@ spufs_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void spufs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, spufs_i_callback);
+}
+
 static void
 spufs_init_once(void *p)
 {
@@ -159,18 +165,18 @@ static void spufs_prune_dir(struct dentry *dir)
 
        mutex_lock(&dir->d_inode->i_mutex);
        list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
-               spin_lock(&dcache_lock);
                spin_lock(&dentry->d_lock);
                if (!(d_unhashed(dentry)) && dentry->d_inode) {
-                       dget_locked(dentry);
+                       dget_dlock(dentry);
                        __d_drop(dentry);
                        spin_unlock(&dentry->d_lock);
                        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);
                } else {
                        spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
                }
        }
        shrink_dcache_parent(dir);
index 8c8afc7..31ae1b1 100644 (file)
@@ -277,18 +277,14 @@ static int remove_file(struct dentry *parent, char *name)
                goto bail;
        }
 
-       spin_lock(&dcache_lock);
        spin_lock(&tmp->d_lock);
        if (!(d_unhashed(tmp) && tmp->d_inode)) {
-               dget_locked(tmp);
+               dget_dlock(tmp);
                __d_drop(tmp);
                spin_unlock(&tmp->d_lock);
-               spin_unlock(&dcache_lock);
                simple_unlink(parent->d_inode, tmp);
-       } else {
+       } else
                spin_unlock(&tmp->d_lock);
-               spin_unlock(&dcache_lock);
-       }
 
        ret = 0;
 bail:
index f99bddc..df7fa25 100644 (file)
@@ -453,17 +453,14 @@ static int remove_file(struct dentry *parent, char *name)
                goto bail;
        }
 
-       spin_lock(&dcache_lock);
        spin_lock(&tmp->d_lock);
        if (!(d_unhashed(tmp) && tmp->d_inode)) {
-               dget_locked(tmp);
+               dget_dlock(tmp);
                __d_drop(tmp);
                spin_unlock(&tmp->d_lock);
-               spin_unlock(&dcache_lock);
                simple_unlink(parent->d_inode, tmp);
        } else {
                spin_unlock(&tmp->d_lock);
-               spin_unlock(&dcache_lock);
        }
 
        ret = 0;
index 4759d82..f511dd1 100644 (file)
@@ -1201,7 +1201,7 @@ err_unregister_chdev:
 static void __exit cleanup_mtdchar(void)
 {
        unregister_mtd_user(&mtdchar_notifier);
-       mntput(mtd_inode_mnt);
+       mntput_long(mtd_inode_mnt);
        unregister_filesystem(&mtd_inodefs_type);
        __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
 }
index 0fdec4b..bf0e975 100644 (file)
@@ -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
  * 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 autofs_sb_info *sbi;
        struct autofs_dir_ent *ent;
        int res;
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
        lock_kernel();
        dir = dentry->d_parent->d_inode;
        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.
         */
-       dentry->d_op = &autofs_dentry_operations;
+       d_set_d_op(dentry, &autofs_dentry_operations);
        dentry->d_flags |= DCACHE_AUTOFS_PENDING;
        d_add(dentry, NULL);
 
index 61685cc..cc8d284 100644 (file)
@@ -826,6 +826,13 @@ const struct address_space_operations pohmelfs_aops = {
        .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
  *  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",
                __func__, pi, &pi->vfs_inode, pi->ino);
-       kmem_cache_free(pohmelfs_inode_cache, pi);
        atomic_long_dec(&psb->total_inodes);
+       call_rcu(&inode->i_rcu, pohmelfs_i_callback);
 }
 
 /*
index 8ec83d2..400a9fc 100644 (file)
@@ -83,10 +83,11 @@ out:
 int pohmelfs_path_length(struct pohmelfs_inode *pi)
 {
        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);
                return -ENOENT;
        }
@@ -95,7 +96,11 @@ int pohmelfs_path_length(struct pohmelfs_inode *pi)
        root = dget(current->fs->root.dentry);
        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))
                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 */
                d = d->d_parent;
        }
-       spin_unlock(&dcache_lock);
+       rcu_read_unlock();
+       if (read_seqretry(&rename_lock, seq))
+               goto rename_retry;
 
        dput(root);
        dput(first);
index dbb9865..f2a1323 100644 (file)
@@ -62,7 +62,7 @@ smb_invalidate_dircache_entries(struct dentry *parent)
        struct list_head *next;
        struct dentry *dentry;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        next = parent->d_subdirs.next;
        while (next != &parent->d_subdirs) {
                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);
                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. */
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        next = parent->d_subdirs.next;
        while (next != &parent->d_subdirs) {
                dent = list_entry(next, struct dentry, d_u.d_child);
                if ((unsigned long)dent->d_fsdata == fpos) {
                        if (dent->d_inode)
-                               dget_locked(dent);
+                               dget(dent);
                        else
                                dent = NULL;
                        goto out_unlock;
@@ -111,7 +111,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
        }
        dent = NULL;
 out_unlock:
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
        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);
 
        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;
 
        newdent = d_lookup(dentry, qname);
@@ -145,8 +145,8 @@ smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                        goto end_advance;
        } else {
                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) {
index f088ea2..dd612f5 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/ctype.h>
 #include <linux/net.h>
 #include <linux/sched.h>
+#include <linux/namei.h>
 
 #include "smb_fs.h"
 #include "smb_mount.h"
@@ -274,9 +275,13 @@ smb_dir_open(struct inode *dir, struct file *file)
  * Dentry operations routines
  */
 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 =
 {
@@ -297,13 +302,20 @@ static const struct dentry_operations smbfs_dentry_operations_case =
  * This is the callback when the dcache has a lookup hit.
  */
 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;
 
+       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:
         * 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 
-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;
        int i;
@@ -347,14 +360,17 @@ smb_hash_dentry(struct dentry *dir, struct qstr *this)
 }
 
 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;
 
-       if (a->len != b->len)
+       if (len != name->len)
                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;
        }
        result = 0;
@@ -367,7 +383,7 @@ out:
  * We use this to unhash dentries with bad inodes.
  */
 static int
-smb_delete_dentry(struct dentry * dentry)
+smb_delete_dentry(const struct dentry *dentry)
 {
        if (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);
 
        if (server->mnt->flags & SMB_MOUNT_CASE)
-               dentry->d_op = &smbfs_dentry_operations_case;
+               d_set_d_op(dentry, &smbfs_dentry_operations_case);
        else
-               dentry->d_op = &smbfs_dentry_operations;
+               d_set_d_op(dentry, &smbfs_dentry_operations);
        dentry->d_time = jiffies;
 }
 
@@ -454,9 +470,9 @@ smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        add_entry:
                        server = server_from_dentry(dentry);
                        if (server->mnt->flags & SMB_MOUNT_CASE)
-                               dentry->d_op = &smbfs_dentry_operations_case;
+                               d_set_d_op(dentry, &smbfs_dentry_operations_case);
                        else
-                               dentry->d_op = &smbfs_dentry_operations;
+                               d_set_d_op(dentry, &smbfs_dentry_operations);
 
                        d_add(dentry, inode);
                        smb_renew_times(dentry);
index 5dcd19c..31372e7 100644 (file)
@@ -407,11 +407,14 @@ smb_file_release(struct inode *inode, struct file * file)
  * privileges, so we need our own check for this.
  */
 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 error = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        VERBOSE("mode=%x, mask=%x\n", mode, mask);
 
        /* Look at user permissions */
index 540a984..244319d 100644 (file)
@@ -62,11 +62,18 @@ static struct inode *smb_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void smb_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, smb_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct smb_inode_info *ei = (struct smb_inode_info *) foo;
index b690aa3..1b125c2 100644 (file)
@@ -343,17 +343,19 @@ static int usbfs_empty (struct dentry *dentry)
 {
        struct list_head *list;
 
-       spin_lock(&dcache_lock);
-
+       spin_lock(&dentry->d_lock);
        list_for_each(list, &dentry->d_subdirs) {
                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)) {
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&de->d_lock);
+                       spin_unlock(&dentry->d_lock);
                        return 0;
                }
+               spin_unlock(&de->d_lock);
        }
-
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
        return 1;
 }
 
index 12d6023..6e58c4c 100644 (file)
@@ -91,11 +91,14 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
        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 v9fs_session_info *v9ses;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        v9ses = v9fs_inode2v9ses(inode);
        if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
                /*
index 59e18c2..7ef3ac9 100644 (file)
@@ -16,7 +16,7 @@
 
 #ifdef CONFIG_9P_FS_POSIX_ACL
 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_set_create_acl(struct dentry *,
                               struct posix_acl *, struct posix_acl *);
index cbf4e50..466d2a4 100644 (file)
@@ -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,
                                                                        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;
        P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
index 34bf71b..5978298 100644 (file)
@@ -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));
 }
+
+void v9fs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, v9fs_i_callback);
+}
 #endif
 
 /**
@@ -270,11 +277,11 @@ static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
 {
        struct dentry *dentry;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&inode->i_lock);
        /* Directory should have only one entry. */
        BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
        dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
        return dentry;
 }
 
@@ -628,9 +635,9 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        }
 
        if (v9ses->cache)
-               dentry->d_op = &v9fs_cached_dentry_operations;
+               d_set_d_op(dentry, &v9fs_cached_dentry_operations);
        else
-               dentry->d_op = &v9fs_dentry_operations;
+               d_set_d_op(dentry, &v9fs_dentry_operations);
 
        d_instantiate(dentry, inode);
        err = v9fs_fid_add(dentry, fid);
@@ -742,7 +749,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
                                err);
                        goto error;
                }
-               dentry->d_op = &v9fs_cached_dentry_operations;
+               d_set_d_op(dentry, &v9fs_cached_dentry_operations);
                d_instantiate(dentry, inode);
                err = v9fs_fid_add(dentry, fid);
                if (err < 0)
@@ -760,7 +767,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
                        err = PTR_ERR(inode);
                        goto error;
                }
-               dentry->d_op = &v9fs_dentry_operations;
+               d_set_d_op(dentry, &v9fs_dentry_operations);
                d_instantiate(dentry, inode);
        }
        /* Now set the ACL based on the default value */
@@ -949,7 +956,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
                                err);
                        goto error;
                }
-               dentry->d_op = &v9fs_cached_dentry_operations;
+               d_set_d_op(dentry, &v9fs_cached_dentry_operations);
                d_instantiate(dentry, inode);
                err = v9fs_fid_add(dentry, fid);
                if (err < 0)
@@ -966,7 +973,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
                        err = PTR_ERR(inode);
                        goto error;
                }
-               dentry->d_op = &v9fs_dentry_operations;
+               d_set_d_op(dentry, &v9fs_dentry_operations);
                d_instantiate(dentry, inode);
        }
        /* 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:
        if (v9ses->cache)
-               dentry->d_op = &v9fs_cached_dentry_operations;
+               d_set_d_op(dentry, &v9fs_cached_dentry_operations);
        else
-               dentry->d_op = &v9fs_dentry_operations;
+               d_set_d_op(dentry, &v9fs_dentry_operations);
 
        d_add(dentry, inode);
        return NULL;
@@ -1702,7 +1709,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
                                        err);
                        goto error;
                }
-               dentry->d_op = &v9fs_cached_dentry_operations;
+               d_set_d_op(dentry, &v9fs_cached_dentry_operations);
                d_instantiate(dentry, inode);
                err = v9fs_fid_add(dentry, fid);
                if (err < 0)
@@ -1715,7 +1722,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
                        err = PTR_ERR(inode);
                        goto error;
                }
-               dentry->d_op = &v9fs_dentry_operations;
+               d_set_d_op(dentry, &v9fs_dentry_operations);
                d_instantiate(dentry, inode);
        }
 
@@ -1849,7 +1856,7 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
                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);
 
        return err;
@@ -1973,7 +1980,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
                                err);
                        goto error;
                }
-               dentry->d_op = &v9fs_cached_dentry_operations;
+               d_set_d_op(dentry, &v9fs_cached_dentry_operations);
                d_instantiate(dentry, inode);
                err = v9fs_fid_add(dentry, fid);
                if (err < 0)
@@ -1989,7 +1996,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
                        err = PTR_ERR(inode);
                        goto error;
                }
-               dentry->d_op = &v9fs_dentry_operations;
+               d_set_d_op(dentry, &v9fs_dentry_operations);
                d_instantiate(dentry, inode);
        }
        /* Now set the ACL based on the default value */
index f4287e4..bf7693c 100644 (file)
@@ -201,7 +201,8 @@ const struct file_operations adfs_dir_operations = {
 };
 
 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 char *name;
@@ -237,17 +238,19 @@ adfs_hash(struct dentry *parent, struct qstr *qstr)
  * requirements of the underlying filesystem.
  */
 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;
 
-       if (entry->len != name->len)
+       if (len != name->len)
                return 1;
 
        for (i = 0; i < name->len; i++) {
                char a, b;
 
-               a = entry->name[i];
+               a = str[i];
                b = name->name[i];
 
                if (a >= 'A' && a <= 'Z')
@@ -273,7 +276,7 @@ adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        struct object_info obj;
        int error;
 
-       dentry->d_op = &adfs_dentry_operations; 
+       d_set_d_op(dentry, &adfs_dentry_operations);
        lock_kernel();
        error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
        if (error == 0) {
index 959dbff..a4041b5 100644 (file)
@@ -240,11 +240,18 @@ static struct inode *adfs_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void adfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, adfs_i_callback);
+}
+
 static void init_once(void *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");
                goto error;
        } else
-               sb->s_root->d_op = &adfs_dentry_operations;
+               d_set_d_op(sb->s_root, &adfs_dentry_operations);
        unlock_kernel();
        return 0;
 
index 7d0f0a3..3a4557e 100644 (file)
@@ -128,7 +128,7 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
        void *data = dentry->d_fsdata;
        struct list_head *head, *next;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&inode->i_lock);
        head = &inode->i_dentry;
        next = head->next;
        while (next != head) {
@@ -139,7 +139,7 @@ affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
                }
                next = next->next;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
 }
 
 
index 914d1c0..944a404 100644 (file)
 typedef int (*toupper_t)(int);
 
 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_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 = {
        .d_hash         = affs_hash_dentry,
@@ -58,13 +66,13 @@ affs_get_toupper(struct super_block *sb)
  * Note: the dentry argument is the parent dentry.
  */
 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;
        unsigned long hash;
        int i;
 
-       i = affs_check_name(qstr->name,qstr->len);
+       i = affs_check_name(qstr->name, qstr->len);
        if (i)
                return i;
 
@@ -78,39 +86,41 @@ __affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
 }
 
 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
-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;
 
-       /* 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.
         */
-       len = a->len;
        if (len >= 30) {
-               if (b->len < 30)
+               if (name->len < 30)
                        return 1;
                len = 30;
-       } else if (len != b->len)
+       } else if (len != name->len)
                return 1;
 
        for (; len > 0; len--)
@@ -121,14 +131,18 @@ __affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, tou
 }
 
 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
-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))
                        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);
        return NULL;
 }
index 0cf7f43..d39081b 100644 (file)
@@ -95,11 +95,18 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void affs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, affs_i_callback);
+}
+
 static void init_once(void *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");
                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);
        return 0;
index 5439e1b..34a3263 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/pagemap.h>
 #include <linux/ctype.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_readdir(struct file *file, void *dirent, filldir_t filldir);
 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 int afs_lookup_filldir(void *_cookie, const char *name, int nlen,
                                  loff_t fpos, u64 ino, unsigned dtype);
@@ -581,7 +582,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
        }
 
 success:
-       dentry->d_op = &afs_fs_dentry_operations;
+       d_set_d_op(dentry, &afs_fs_dentry_operations);
 
        d_add(dentry, inode);
        _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;
        int ret;
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
        vnode = AFS_FS_I(dentry->d_inode);
 
        if (dentry->d_inode)
@@ -730,7 +734,7 @@ out_bad:
  * - called from dput() when d_count is going to 0.
  * - 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);
 
index cca8eef..6d4bc1c 100644 (file)
@@ -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_zap_permits(struct rcu_head *);
 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
index bb4ed14..f44b9d3 100644 (file)
@@ -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
  *   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);
        afs_access_t uninitialized_var(access);
        struct key *key;
        int ret;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        _enter("{{%x:%u},%lx},%x,",
               vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
 
@@ -347,7 +350,7 @@ int afs_permission(struct inode *inode, int mask)
        }
 
        key_put(key);
-       ret = generic_permission(inode, mask, NULL);
+       ret = generic_permission(inode, mask, flags, NULL);
        _leave(" = %d", ret);
        return ret;
 
index 27201cf..f901a9d 100644 (file)
@@ -498,6 +498,14 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
        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
  */
@@ -511,7 +519,7 @@ static void afs_destroy_inode(struct inode *inode)
 
        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);
 }
 
index 57ce55b..5fd3811 100644 (file)
@@ -102,7 +102,7 @@ struct file *anon_inode_getfile(const char *name,
        this.name = name;
        this.len = strlen(name);
        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)
                goto err_module;
 
@@ -113,7 +113,7 @@ struct file *anon_inode_getfile(const char *name,
         */
        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);
 
        error = -ENFILE;
@@ -232,7 +232,7 @@ static int __init anon_inode_init(void)
        return 0;
 
 err_mntput:
-       mntput(anon_inode_mnt);
+       mntput_long(anon_inode_mnt);
 err_unregister_filesystem:
        unregister_filesystem(&anon_inode_fs_type);
 err_exit:
index 3d283ab..0fffe1c 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/auto_fs4.h>
 #include <linux/auto_dev-ioctl.h>
 #include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/list.h>
 
 /* This is the range of ioctl() numbers we claim as ours */
@@ -60,6 +61,8 @@ do {                                                  \
                current->pid, __func__, ##args);        \
 } while (0)
 
+extern spinlock_t autofs4_lock;
+
 /* Unified info structure.  This is pointed to by both the dentry and
    inode structures.  Each file in the filesystem has an instance of this
    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);
 }
 
-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)
index a796c94..cc1d013 100644 (file)
@@ -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) {
                while (1) {
-                       if (p == root)
+                       struct dentry *parent;
+
+                       if (p == root) {
+                               spin_unlock(&p->d_lock);
+                               spin_unlock(&autofs4_lock);
+                               dput(prev);
                                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;
-                       if (next != &p->d_parent->d_subdirs)
+                       p = parent;
+                       if (next != &parent->d_subdirs)
                                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))
                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",
                        p, (int) p->d_name.len, p->d_name.name);
 
-               p = dget(p);
-               spin_unlock(&dcache_lock);
-
                /*
                 * Is someone visiting anywhere in the subtree ?
                 * If there's no mount we need to check the usage
@@ -198,16 +231,13 @@ static int autofs4_tree_busy(struct vfsmount *mnt,
                        else
                                ino_count++;
 
-                       if (atomic_read(&p->d_count) > ino_count) {
+                       if (p->d_count > ino_count) {
                                top_ino->last_used = jiffies;
                                dput(p);
                                return 1;
                        }
                }
-               dput(p);
-               spin_lock(&dcache_lock);
        }
-       spin_unlock(&dcache_lock);
 
        /* Timeout of a tree mount is ultimately determined by its top dentry */
        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",
                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",
                        p, (int) p->d_name.len, p->d_name.name);
 
-               p = dget(p);
-               spin_unlock(&dcache_lock);
-
                if (d_mountpoint(p)) {
                        /* Can we umount this guy */
                        if (autofs4_mount_busy(mnt, p))
-                               goto cont;
+                               continue;
 
                        /* Can we expire this guy */
                        if (autofs4_can_expire(p, timeout, do_now))
                                return p;
                }
-cont:
-               dput(p);
-               spin_lock(&dcache_lock);
        }
-       spin_unlock(&dcache_lock);
        return NULL;
 }
 
@@ -276,7 +295,9 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
                struct autofs_info *ino = autofs4_dentry_ino(root);
                if (d_mountpoint(root)) {
                        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;
                init_completion(&ino->expire_complete);
@@ -302,8 +323,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 {
        unsigned long timeout;
        struct dentry *root = sb->s_root;
+       struct dentry *dentry;
        struct dentry *expired = NULL;
-       struct list_head *next;
        int do_now = how & AUTOFS_EXP_IMMEDIATE;
        int exp_leaves = how & AUTOFS_EXP_LEAVES;
        struct autofs_info *ino;
@@ -315,23 +336,8 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
        now = jiffies;
        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);
                ino = autofs4_dentry_ino(dentry);
 
@@ -347,7 +353,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
 
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 2;
-                       if (atomic_read(&dentry->d_count) > ino_count)
+                       if (dentry->d_count > ino_count)
                                goto next;
 
                        /* Can we umount this guy */
@@ -369,7 +375,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                if (!exp_leaves) {
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 1;
-                       if (atomic_read(&dentry->d_count) > ino_count)
+                       if (dentry->d_count > ino_count)
                                goto next;
 
                        if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) {
@@ -383,7 +389,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                } else {
                        /* Path walk currently on this dentry? */
                        ino_count = atomic_read(&ino->count) + 1;
-                       if (atomic_read(&dentry->d_count) > ino_count)
+                       if (dentry->d_count > ino_count)
                                goto next;
 
                        expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
@@ -394,11 +400,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
                }
 next:
                spin_unlock(&sbi->fs_lock);
-               dput(dentry);
-               spin_lock(&dcache_lock);
-               next = next->next;
        }
-       spin_unlock(&dcache_lock);
        return NULL;
 
 found:
@@ -408,9 +410,13 @@ found:
        ino->flags |= AUTOFS_INF_EXPIRING;
        init_completion(&ino->expire_complete);
        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);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&expired->d_lock);
+       spin_unlock(&expired->d_parent->d_lock);
+       spin_unlock(&autofs4_lock);
        return expired;
 }
 
@@ -499,7 +505,14 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
 
                spin_lock(&sbi->fs_lock);
                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_EXPIRING;
index ac87e49..a7bdb9d 100644 (file)
@@ -309,7 +309,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
                goto fail_iput;
        pipe = NULL;
 
-       root->d_op = &autofs4_sb_dentry_operations;
+       d_set_d_op(root, &autofs4_sb_dentry_operations);
        root->d_fsdata = ino;
 
        /* Can this call block? */
index d34896c..651e4ef 100644 (file)
@@ -23,6 +23,8 @@
 
 #include "autofs_i.h"
 
+DEFINE_SPINLOCK(autofs4_lock);
+
 static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
 static int autofs4_dir_unlink(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
         * it.
         */
-       spin_lock(&dcache_lock);
+       spin_lock(&autofs4_lock);
+       spin_lock(&dentry->d_lock);
        if (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
-               spin_unlock(&dcache_lock);
+               spin_unlock(&dentry->d_lock);
+               spin_unlock(&autofs4_lock);
                return -ENOENT;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&autofs4_lock);
 
 out:
        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 */
        lookup_type = autofs4_need_mount(nd->flags);
        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)) {
-               spin_unlock(&dcache_lock);
+               spin_unlock(&dentry->d_lock);
+               spin_unlock(&autofs4_lock);
                spin_unlock(&sbi->fs_lock);
                goto follow;
        }
@@ -266,7 +273,8 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
         */
        if (ino->flags & AUTOFS_INF_PENDING ||
            (!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);
 
                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;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&autofs4_lock);
        spin_unlock(&sbi->fs_lock);
 follow:
        /*
@@ -306,12 +315,19 @@ out_error:
  */
 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 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 */
        spin_lock(&sbi->fs_lock);
        if (autofs4_ispending(dentry)) {
@@ -346,12 +362,14 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
                return 0;
 
        /* 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) &&
            !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
                DPRINTK("dentry=%p %.*s, emptydir",
                         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 */
                if (oz_mode)
@@ -367,7 +385,8 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
 
                return status;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&autofs4_lock);
 
        return 1;
 }
@@ -422,7 +441,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
        const unsigned char *str = name->name;
        struct list_head *p, *head;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&autofs4_lock);
        spin_lock(&sbi->lookup_lock);
        head = &sbi->active_list;
        list_for_each(p, head) {
@@ -436,7 +455,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
                spin_lock(&active->d_lock);
 
                /* Already gone? */
-               if (atomic_read(&active->d_count) == 0)
+               if (active->d_count == 0)
                        goto next;
 
                qstr = &active->d_name;
@@ -452,17 +471,17 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
                        goto next;
 
                if (d_unhashed(active)) {
-                       dget(active);
+                       dget_dlock(active);
                        spin_unlock(&active->d_lock);
                        spin_unlock(&sbi->lookup_lock);
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&autofs4_lock);
                        return active;
                }
 next:
                spin_unlock(&active->d_lock);
        }
        spin_unlock(&sbi->lookup_lock);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&autofs4_lock);
 
        return NULL;
 }
@@ -477,7 +496,7 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
        const unsigned char *str = name->name;
        struct list_head *p, *head;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&autofs4_lock);
        spin_lock(&sbi->lookup_lock);
        head = &sbi->expiring_list;
        list_for_each(p, head) {
@@ -507,17 +526,17 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
                        goto next;
 
                if (d_unhashed(expiring)) {
-                       dget(expiring);
+                       dget_dlock(expiring);
                        spin_unlock(&expiring->d_lock);
                        spin_unlock(&sbi->lookup_lock);
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&autofs4_lock);
                        return expiring;
                }
 next:
                spin_unlock(&expiring->d_lock);
        }
        spin_unlock(&sbi->lookup_lock);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&autofs4_lock);
 
        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
                 * 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
@@ -698,9 +717,9 @@ static int autofs4_dir_symlink(struct inode *dir,
        d_add(dentry, 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
-               dentry->d_op = &autofs4_dentry_operations;
+               d_set_d_op(dentry, &autofs4_dentry_operations);
 
        dentry->d_fsdata = ino;
        ino->dentry = dget(dentry);
@@ -753,12 +772,12 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
 
        dir->i_mtime = CURRENT_TIME;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&autofs4_lock);
        autofs4_add_expiring(dentry);
        spin_lock(&dentry->d_lock);
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&autofs4_lock);
 
        return 0;
 }
@@ -775,16 +794,20 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
        if (!autofs4_oz_mode(sbi))
                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)) {
-               spin_unlock(&dcache_lock);
+               spin_unlock(&dentry->d_lock);
+               spin_unlock(&sbi->lookup_lock);
+               spin_unlock(&autofs4_lock);
                return -ENOTEMPTY;
        }
-       autofs4_add_expiring(dentry);
-       spin_lock(&dentry->d_lock);
+       __autofs4_add_expiring(dentry);
+       spin_unlock(&sbi->lookup_lock);
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&autofs4_lock);
 
        if (atomic_dec_and_test(&ino->count)) {
                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);
 
        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
-               dentry->d_op = &autofs4_dentry_operations;
+               d_set_d_op(dentry, &autofs4_dentry_operations);
 
        dentry->d_fsdata = ino;
        ino->dentry = dget(dentry);
index 2341375..c5f8459 100644 (file)
@@ -186,16 +186,26 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
 {
        struct dentry *root = sbi->sb->s_root;
        struct dentry *tmp;
-       char *buf = *name;
+       char *buf;
        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)
                len += tmp->d_name.len + 1;
 
        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;
        }
 
@@ -208,7 +218,10 @@ static int autofs4_getpath(struct autofs_sb_info *sbi,
                p -= 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;
 }
index f024d8a..9ad2369 100644 (file)
@@ -229,8 +229,11 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
        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;
 }
 
index aa4e7c7..de93581 100644 (file)
@@ -284,12 +284,18 @@ befs_alloc_inode(struct super_block *sb)
         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));
 }
 
+static void befs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, befs_i_callback);
+}
+
 static void init_once(void *foo)
 {
         struct befs_inode_info *bi = (struct befs_inode_info *) foo;
index 76db6d7..a8e37f8 100644 (file)
@@ -248,11 +248,18 @@ static struct inode *bfs_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void bfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, bfs_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct bfs_inode_info *bi = foo;
index 4230252..771f235 100644 (file)
@@ -409,13 +409,20 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
        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);
 
+       INIT_LIST_HEAD(&inode->i_dentry);
        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)
 {
        struct bdev_inode *ei = (struct bdev_inode *) foo;
index 2222d16..6ae2c8c 100644 (file)
@@ -185,18 +185,23 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
        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;
 
-       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;
index af52f6d..a142d20 100644 (file)
@@ -2544,7 +2544,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait);
 
 /* acl.c */
 #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
 #define btrfs_check_acl NULL
 #endif
index 659f532..0ccf9a8 100644 (file)
@@ -110,7 +110,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
 
        dentry = d_obtain_alias(inode);
        if (!IS_ERR(dentry))
-               dentry->d_op = &btrfs_dentry_operations;
+               d_set_d_op(dentry, &btrfs_dentry_operations);
        return dentry;
 fail:
        srcu_read_unlock(&fs_info->subvol_srcu, index);
@@ -225,7 +225,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
        key.offset = 0;
        dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL));
        if (!IS_ERR(dentry))
-               dentry->d_op = &btrfs_dentry_operations;
+               d_set_d_op(dentry, &btrfs_dentry_operations);
        return dentry;
 fail:
        btrfs_free_path(path);
index 72f31ec..a0ff46a 100644 (file)
@@ -4084,7 +4084,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
        int index;
        int ret;
 
-       dentry->d_op = &btrfs_dentry_operations;
+       d_set_d_op(dentry, &btrfs_dentry_operations);
 
        if (dentry->d_name.len > BTRFS_NAME_LEN)
                return ERR_PTR(-ENAMETOOLONG);
@@ -4127,7 +4127,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
        return inode;
 }
 
-static int btrfs_dentry_delete(struct dentry *dentry)
+static int btrfs_dentry_delete(const struct dentry *dentry)
 {
        struct btrfs_root *root;
 
@@ -6495,6 +6495,13 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
        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)
 {
        struct btrfs_ordered_extent *ordered;
@@ -6564,7 +6571,7 @@ void btrfs_destroy_inode(struct inode *inode)
        inode_tree_del(inode);
        btrfs_drop_extent_cache(inode, 0, (u64)-1, 0);
 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)
@@ -7204,11 +7211,11 @@ static int btrfs_set_page_dirty(struct page *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))
                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 = {
index d902948..fa7ca04 100644 (file)
@@ -42,11 +42,11 @@ int ceph_init_dentry(struct dentry *dentry)
 
        if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */
            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)
-               dentry->d_op = &ceph_snapdir_dentry_ops;
+               d_set_d_op(dentry, &ceph_snapdir_dentry_ops);
        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);
        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,
             last);
 
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
 
        /* start at beginning? */
        if (filp->f_pos == 2 || last == NULL ||
@@ -136,6 +136,7 @@ more:
                        fi->at_end = 1;
                        goto out_unlock;
                }
+               spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
                if (!d_unhashed(dentry) && dentry->d_inode &&
                    ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
                    ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
@@ -145,13 +146,15 @@ more:
                     dentry->d_name.len, dentry->d_name.name, di->offset,
                     filp->f_pos, d_unhashed(dentry) ? " unhashed" : "",
                     !dentry->d_inode ? " null" : "");
+               spin_unlock(&dentry->d_lock);
                p = p->prev;
                dentry = list_entry(p, struct dentry, d_u.d_child);
                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,
             dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
@@ -177,19 +180,19 @@ more:
 
        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)) {
                dout(" lost I_COMPLETE on %p; falling back to mds\n", dir);
                err = -EAGAIN;
                goto out;
        }
 
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        p = p->prev;    /* advance to next dentry */
        goto more;
 
 out_unlock:
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
 out:
        if (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)
 {
-       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,
             dentry->d_name.len, dentry->d_name.name, dentry->d_inode,
index bf12865..e61de4f 100644 (file)
@@ -368,6 +368,15 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        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)
 {
        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)
                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++;
        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);
        dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset,
             dn->d_u.d_child.prev, dn->d_u.d_child.next);
        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) {
                dout("dn %p (%d) spliced with %p (%d) "
                     "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));
                dput(dn);
                dn = realdn;
@@ -1231,11 +1240,11 @@ retry_lookup:
                        goto retry_lookup;
                } else {
                        /* 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);
                        spin_unlock(&dn->d_lock);
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&parent->d_lock);
                }
 
                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
  * 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)
-               err = generic_permission(inode, mask, NULL);
+               err = generic_permission(inode, mask, flags, NULL);
        return err;
 }
 
index 38800ea..a50fca1 100644 (file)
@@ -1486,7 +1486,7 @@ retry:
        *base = ceph_ino(temp->d_inode);
        *plen = len;
        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;
 }
 
index 7f01728..4553d88 100644 (file)
@@ -665,7 +665,7 @@ extern void ceph_queue_invalidate(struct inode *inode);
 extern void ceph_queue_writeback(struct inode *inode);
 
 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_getattr(struct vfsmount *mnt, struct dentry *dentry,
                        struct kstat *stat);
index 3936aa7..8e21e0f 100644 (file)
@@ -283,10 +283,13 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
        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;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        cifs_sb = CIFS_SB(inode->i_sb);
 
        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
                servers which do not support setting and viewing mode bits,
                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;
@@ -334,10 +337,17 @@ cifs_alloc_inode(struct super_block *sb)
        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
 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
index 3840edd..db2a58c 100644 (file)
@@ -135,9 +135,9 @@ static void setup_cifs_dentry(struct cifsTconInfo *tcon,
                              struct inode *newinode)
 {
        if (tcon->nocase)
-               direntry->d_op = &cifs_ci_dentry_ops;
+               d_set_d_op(direntry, &cifs_ci_dentry_ops);
        else
-               direntry->d_op = &cifs_dentry_ops;
+               d_set_d_op(direntry, &cifs_dentry_ops);
        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,
                                                inode->i_sb, xid);
                if (pTcon->nocase)
-                       direntry->d_op = &cifs_ci_dentry_ops;
+                       d_set_d_op(direntry, &cifs_ci_dentry_ops);
                else
-                       direntry->d_op = &cifs_dentry_ops;
+                       d_set_d_op(direntry, &cifs_dentry_ops);
 
                if (rc == 0)
                        d_instantiate(direntry, newinode);
@@ -604,9 +604,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
 
        if ((rc == 0) && (newInode != NULL)) {
                if (pTcon->nocase)
-                       direntry->d_op = &cifs_ci_dentry_ops;
+                       d_set_d_op(direntry, &cifs_ci_dentry_ops);
                else
-                       direntry->d_op = &cifs_dentry_ops;
+                       d_set_d_op(direntry, &cifs_dentry_ops);
                d_add(direntry, newInode);
                if (posix_open) {
                        filp = lookup_instantiate_filp(nd, direntry,
@@ -634,9 +634,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
                rc = 0;
                direntry->d_time = jiffies;
                if (pTcon->nocase)
-                       direntry->d_op = &cifs_ci_dentry_ops;
+                       d_set_d_op(direntry, &cifs_ci_dentry_ops);
                else
-                       direntry->d_op = &cifs_dentry_ops;
+                       d_set_d_op(direntry, &cifs_dentry_ops);
                d_add(direntry, NULL);
        /*      if it was once a directory (but how can we tell?) we could do
                shrink_dcache_parent(direntry); */
@@ -656,22 +656,37 @@ lookup_out:
 static int
 cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
 {
-       int isValid = 1;
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
 
        if (direntry->d_inode) {
                if (cifs_revalidate_dentry(direntry))
                        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)
@@ -688,9 +703,10 @@ const struct dentry_operations cifs_dentry_ops = {
 /* 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;
        int i;
 
@@ -703,21 +719,16 @@ static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
        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 1;
 }
 
index 589f3e3..a853a89 100644 (file)
@@ -809,14 +809,14 @@ inode_has_hashed_dentries(struct inode *inode)
 {
        struct dentry *dentry;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&inode->i_lock);
        list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
                if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&inode->i_lock);
                        return true;
                }
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
        return false;
 }
 
@@ -1319,9 +1319,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
        to set uid/gid */
                        inc_nlink(inode);
                        if (pTcon->nocase)
-                               direntry->d_op = &cifs_ci_dentry_ops;
+                               d_set_d_op(direntry, &cifs_ci_dentry_ops);
                        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_fill_uniqueid(inode->i_sb, &fattr);
@@ -1363,9 +1363,9 @@ mkdir_get_info:
                                                 inode->i_sb, xid, NULL);
 
                if (pTcon->nocase)
-                       direntry->d_op = &cifs_ci_dentry_ops;
+                       d_set_d_op(direntry, &cifs_ci_dentry_ops);
                else
-                       direntry->d_op = &cifs_dentry_ops;
+                       d_set_d_op(direntry, &cifs_dentry_ops);
                d_instantiate(direntry, newinode);
                 /* setting nlink not necessary except in cases where we
                  * failed to get it from the server or was set bogus */
index 85cdbf8..fe2f6a9 100644 (file)
@@ -525,9 +525,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
                              rc);
                } else {
                        if (pTcon->nocase)
-                               direntry->d_op = &cifs_ci_dentry_ops;
+                               d_set_d_op(direntry, &cifs_ci_dentry_ops);
                        else
-                               direntry->d_op = &cifs_dentry_ops;
+                               d_set_d_op(direntry, &cifs_dentry_ops);
                        d_instantiate(direntry, newinode);
                }
        }
index a73eb9f..ec5b68e 100644 (file)
@@ -79,7 +79,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
        cFYI(1, "For %s", name->name);
 
        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
                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)
-               dentry->d_op = &cifs_ci_dentry_ops;
+               d_set_d_op(dentry, &cifs_ci_dentry_ops);
        else
-               dentry->d_op = &cifs_dentry_ops;
+               d_set_d_op(dentry, &cifs_dentry_ops);
 
        alias = d_materialise_unique(dentry, inode);
        if (alias != NULL) {
index 9060f08..5525e1c 100644 (file)
@@ -93,7 +93,7 @@ static void coda_flag_children(struct dentry *parent, int flag)
        struct list_head *child;
        struct dentry *de;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        list_for_each(child, &parent->d_subdirs)
        {
                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;
                coda_flag_inode(de->d_inode, flag);
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
        return; 
 }
 
index 5d8b355..29badd9 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
+#include <linux/namei.h>
 
 #include <asm/uaccess.h>
 
@@ -47,7 +48,7 @@ static int coda_readdir(struct file *file, void *buf, filldir_t filldir);
 
 /* dentry ops */
 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 */
 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);
 
 exit:
-       entry->d_op = &coda_dentry_operations;
+       d_set_d_op(entry, &coda_dentry_operations);
 
        if (inode && (type & CODA_NOCACHE))
                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;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
        if (!mask)
@@ -541,9 +545,13 @@ out:
 /* called when a cache lookup succeeds */
 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;
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = de->d_inode;
        if (!inode || coda_isroot(inode))
                goto out;
        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) 
                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 */
                goto out;
 
@@ -577,7 +585,7 @@ out:
  * This is the callback from dput() when d_count is going to 0.
  * 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;
 
index 5ea57c8..50dc7d1 100644 (file)
@@ -56,11 +56,18 @@ static struct inode *coda_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void coda_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, coda_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct coda_inode_info *ei = (struct coda_inode_info *) foo;
index 2fd89b5..741f0bd 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/coda_psdev.h>
 
 /* 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,
                        unsigned long user_data);
 
@@ -41,8 +41,10 @@ const struct file_operations coda_ioctl_operations = {
 };
 
 /* 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;
 }
 
index da6061a..026cf68 100644 (file)
@@ -120,7 +120,7 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
 {
        struct config_item * item = NULL;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&dentry->d_lock);
        if (!d_unhashed(dentry)) {
                struct configfs_dirent * sd = dentry->d_fsdata;
                if (sd->s_type & CONFIGFS_ITEM_LINK) {
@@ -129,7 +129,7 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
                } else
                        item = config_item_get(sd->s_element);
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
 
        return item;
 }
index 0b502f8..36637a8 100644 (file)
@@ -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
  * 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;
 }
@@ -232,10 +232,8 @@ int configfs_make_dirent(struct configfs_dirent * parent_sd,
 
        sd->s_mode = mode;
        sd->s_dentry = dentry;
-       if (dentry) {
+       if (dentry)
                dentry->d_fsdata = configfs_get(sd);
-               dentry->d_op = &configfs_dentry_ops;
-       }
 
        return 0;
 }
@@ -278,7 +276,6 @@ static int create_dir(struct config_item * k, struct dentry * p,
                error = configfs_create(d, mode, init_dir);
                if (!error) {
                        inc_nlink(p->d_inode);
-                       (d)->d_op = &configfs_dentry_ops;
                } else {
                        struct configfs_dirent *sd = d->d_fsdata;
                        if (sd) {
@@ -371,9 +368,7 @@ int configfs_create_link(struct configfs_symlink *sl,
                                   CONFIGFS_ITEM_LINK);
        if (!err) {
                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;
                        if (sd) {
                                spin_lock(&configfs_dirent_lock);
@@ -399,8 +394,7 @@ static void remove_dir(struct dentry * d)
        if (d->d_inode)
                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);
 }
@@ -448,7 +442,7 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
                return error;
        }
 
-       dentry->d_op = &configfs_dentry_ops;
+       d_set_d_op(dentry, &configfs_dentry_ops);
        d_rehash(dentry);
 
        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,
                 * 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:
@@ -685,6 +683,7 @@ static int create_default_group(struct config_group *parent_group,
        ret = -ENOMEM;
        child = d_alloc(parent, &name);
        if (child) {
+               d_set_d_op(child, &configfs_dentry_ops);
                d_add(child, NULL);
 
                ret = configfs_attach_group(&parent_group->cg_item,
@@ -1682,6 +1681,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
        err = -ENOMEM;
        dentry = d_alloc(configfs_sb->s_root, &name);
        if (dentry) {
+               d_set_d_op(dentry, &configfs_dentry_ops);
                d_add(dentry, NULL);
 
                err = configfs_attach_group(sd->s_element, &group->cg_item,
index 253476d..c83f476 100644 (file)
@@ -250,18 +250,14 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
        struct dentry * dentry = sd->s_dentry;
 
        if (dentry) {
-               spin_lock(&dcache_lock);
                spin_lock(&dentry->d_lock);
                if (!(d_unhashed(dentry) && dentry->d_inode)) {
-                       dget_locked(dentry);
+                       dget_dlock(dentry);
                        __d_drop(dentry);
                        spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
                        simple_unlink(parent->d_inode, dentry);
-               } else {
+               } else
                        spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
-               }
        }
 }
 
index 23702a9..5699d4c 100644 (file)
 #include <linux/bootmem.h>
 #include <linux/fs_struct.h>
 #include <linux/hardirq.h>
+#include <linux/bit_spinlock.h>
+#include <linux/rculist_bl.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;
 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);
 
-EXPORT_SYMBOL(dcache_lock);
+EXPORT_SYMBOL(rename_lock);
 
 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
  * 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_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. */
 struct dentry_stat_t dentry_stat = {
        .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)
+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,
                   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);
 }
 #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)
 {
-       percpu_counter_dec(&nr_dentry);
+       BUG_ON(dentry->d_count);
+       this_cpu_dec(nr_dentry);
        if (dentry->d_op && dentry->d_op->d_release)
                dentry->d_op->d_release(dentry);
 
        /* 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);
        else
                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
- * d_iput() operation if defined.
+ * d_iput() operation if defined. Dentry has no refcount
+ * and is unhashed.
  */
 static void dentry_iput(struct dentry * dentry)
        __releases(dentry->d_lock)
-       __releases(dcache_lock)
+       __releases(dentry->d_inode->i_lock)
 {
        struct inode *inode = dentry->d_inode;
        if (inode) {
                dentry->d_inode = NULL;
                list_del_init(&dentry->d_alias);
                spin_unlock(&dentry->d_lock);
-               spin_unlock(&dcache_lock);
+               spin_unlock(&inode->i_lock);
                if (!inode->i_nlink)
                        fsnotify_inoderemove(inode);
                if (dentry->d_op && dentry->d_op->d_iput)
@@ -128,40 +210,72 @@ static void dentry_iput(struct dentry * dentry)
                        iput(inode);
        } else {
                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)
 {
        if (list_empty(&dentry->d_lru)) {
+               spin_lock(&dcache_lru_lock);
                list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
                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)
 {
        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)
 {
+       spin_lock(&dcache_lru_lock);
        if (list_empty(&dentry->d_lru)) {
                list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
                dentry->d_sb->s_nr_dentry_unused++;
-               percpu_counter_inc(&nr_dentry_unused);
+               dentry_stat.nr_unused++;
        } else {
                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.
  *
  * 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(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);
-       /*drops the locks, at that point nobody can reach this dentry */
+       if (parent)
+               spin_unlock(&parent->d_lock);
        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))
                parent = NULL;
        else
                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
  * releasing its resources. If the parent dentries were scheduled for release
  * they too may now get deleted.
- *
- * no dcache lock, please.
  */
-
 void dput(struct dentry *dentry)
 {
        if (!dentry)
                return;
 
 repeat:
-       if (atomic_read(&dentry->d_count) == 1)
+       if (dentry->d_count == 1)
                might_sleep();
-       if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
-               return;
-
        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(&dcache_lock);
                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))
-                       goto unhash_it;
+                       goto kill_it;
        }
 
        /* Unreachable? Get rid of it */
@@ -252,16 +451,12 @@ repeat:
        dentry->d_flags |= DCACHE_REFERENCED;
        dentry_lru_add(dentry);
 
-       spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
+       dentry->d_count--;
+       spin_unlock(&dentry->d_lock);
        return;
 
-unhash_it:
-       __d_drop(dentry);
 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)
                goto repeat;
 }
@@ -284,9 +479,9 @@ int d_invalidate(struct dentry * dentry)
        /*
         * If it's already been dropped, return OK.
         */
-       spin_lock(&dcache_lock);
+       spin_lock(&dentry->d_lock);
        if (d_unhashed(dentry)) {
-               spin_unlock(&dcache_lock);
+               spin_unlock(&dentry->d_lock);
                return 0;
        }
        /*
@@ -294,9 +489,9 @@ int d_invalidate(struct dentry * dentry)
         * to get rid of unused child entries.
         */
        if (!list_empty(&dentry->d_subdirs)) {
-               spin_unlock(&dcache_lock);
+               spin_unlock(&dentry->d_lock);
                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
         * 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)) {
                        spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
                        return -EBUSY;
                }
        }
 
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
        return 0;
 }
 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
@@ -355,42 +576,51 @@ EXPORT_SYMBOL(dget_locked);
  * any other hashed alias over that one unless @want_discon is set,
  * 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 (IS_ROOT(alias) &&
-                           (alias->d_flags & DCACHE_DISCONNECTED))
+                           (alias->d_flags & DCACHE_DISCONNECTED)) {
                                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;
                        }
                }
+               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;
 
        if (!list_empty(&inode->i_dentry)) {
-               spin_lock(&dcache_lock);
+               spin_lock(&inode->i_lock);
                de = __d_find_alias(inode, 0);
-               spin_unlock(&dcache_lock);
+               spin_unlock(&inode->i_lock);
        }
        return de;
 }
@@ -404,54 +634,61 @@ void d_prune_aliases(struct inode *inode)
 {
        struct dentry *dentry;
 restart:
-       spin_lock(&dcache_lock);
+       spin_lock(&inode->i_lock);
        list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
                spin_lock(&dentry->d_lock);
-               if (!atomic_read(&dentry->d_count)) {
-                       __dget_locked(dentry);
+               if (!dentry->d_count) {
+                       __dget_dlock(dentry);
                        __d_drop(dentry);
                        spin_unlock(&dentry->d_lock);
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&inode->i_lock);
                        dput(dentry);
                        goto restart;
                }
                spin_unlock(&dentry->d_lock);
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
 }
 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(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) {
-               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;
-
-               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;
 
-       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
                 * the LRU because of laziness during lookup.  Do not free
                 * 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);
                        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);
        int cnt = *count;
 
-       spin_lock(&dcache_lock);
+relock:
+       spin_lock(&dcache_lru_lock);
        while (!list_empty(&sb->s_dentry_lru)) {
                dentry = list_entry(sb->s_dentry_lru.prev,
                                struct dentry, d_lru);
                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
                 * dentry has this flag set, don't free it.  Clear the flag
                 * 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);
+               } 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))
                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;
        int w_count;
-       int unused = percpu_counter_sum_positive(&nr_dentry_unused);
+       int unused = dentry_stat.nr_unused;
        int prune_ratio;
        int pruned;
 
        if (unused == 0 || count == 0)
                return;
-       spin_lock(&dcache_lock);
        if (count >= unused)
                prune_ratio = 1;
        else
@@ -589,11 +838,9 @@ static void prune_dcache(int count)
                if (down_read_trylock(&sb->s_umount)) {
                        if ((sb->s_root != NULL) &&
                            (!list_empty(&sb->s_dentry_lru))) {
-                               spin_unlock(&dcache_lock);
                                __shrink_dcache_sb(sb, &w_count,
                                                DCACHE_REFERENCED);
                                pruned -= w_count;
-                               spin_lock(&dcache_lock);
                        }
                        up_read(&sb->s_umount);
                }
@@ -609,7 +856,6 @@ static void prune_dcache(int count)
        if (p)
                __put_super(p);
        spin_unlock(&sb_lock);
-       spin_unlock(&dcache_lock);
 }
 
 /**
@@ -623,12 +869,14 @@ void shrink_dcache_sb(struct super_block *sb)
 {
        LIST_HEAD(tmp);
 
-       spin_lock(&dcache_lock);
+       spin_lock(&dcache_lru_lock);
        while (!list_empty(&sb->s_dentry_lru)) {
                list_splice_init(&sb->s_dentry_lru, &tmp);
+               spin_unlock(&dcache_lru_lock);
                shrink_dentry_list(&tmp);
+               spin_lock(&dcache_lru_lock);
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dcache_lru_lock);
 }
 EXPORT_SYMBOL(shrink_dcache_sb);
 
@@ -645,10 +893,10 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
        BUG_ON(!IS_ROOT(dentry));
 
        /* detach this root from the system */
-       spin_lock(&dcache_lock);
+       spin_lock(&dentry->d_lock);
        dentry_lru_del(dentry);
        __d_drop(dentry);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
 
        for (;;) {
                /* 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
                         * from the system in one go */
-                       spin_lock(&dcache_lock);
+                       spin_lock(&dentry->d_lock);
                        list_for_each_entry(loop, &dentry->d_subdirs,
                                            d_u.d_child) {
+                               spin_lock_nested(&loop->d_lock,
+                                               DENTRY_D_LOCK_NESTED);
                                dentry_lru_del(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 */
                        dentry = list_entry(dentry->d_subdirs.next,
@@ -676,7 +926,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                do {
                        struct inode *inode;
 
-                       if (atomic_read(&dentry->d_count) != 0) {
+                       if (dentry->d_count != 0) {
                                printk(KERN_ERR
                                       "BUG: Dentry %p{i=%lx,n=%s}"
                                       " still in use (%d)"
@@ -685,20 +935,23 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                                       dentry->d_inode ?
                                       dentry->d_inode->i_ino : 0UL,
                                       dentry->d_name.name,
-                                      atomic_read(&dentry->d_count),
+                                      dentry->d_count,
                                       dentry->d_sb->s_type->name,
                                       dentry->d_sb->s_id);
                                BUG();
                        }
 
-                       if (IS_ROOT(dentry))
+                       if (IS_ROOT(dentry)) {
                                parent = NULL;
-                       else {
+                               list_del(&dentry->d_u.d_child);
+                       } else {
                                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++;
 
                        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
- * - 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
  *     dentry trees will not be rearranged by the VFS
  *   - 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;
        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);
 
-       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);
        }
 }
@@ -768,15 +1022,20 @@ void shrink_dcache_for_umount(struct super_block *sb)
  * Return true if the parent or its subdirectories contain
  * a mount point
  */
 int have_submounts(struct dentry *parent)
 {
-       struct dentry *this_parent = parent;
+       struct dentry *this_parent;
        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))
                goto positive;
+       spin_lock(&this_parent->d_lock);
 repeat:
        next = this_parent->d_subdirs.next;
 resume:
@@ -784,27 +1043,65 @@ resume:
                struct list_head *tmp = next;
                struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
                next = tmp->next;
+
+               spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
                /* 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;
+               }
                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;
+                       spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
                        goto repeat;
                }
+               spin_unlock(&dentry->d_lock);
        }
        /*
         * All done at this level ... ascend and resume the search.
         */
        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;
        }
-       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 */
 positive:
-       spin_unlock(&dcache_lock);
+       if (!locked && read_seqretry(&rename_lock, seq))
+               goto rename_retry;
+       if (locked)
+               write_sequnlock(&rename_lock);
        return 1;
+
+rename_retry:
+       locked = 1;
+       write_seqlock(&rename_lock);
+       goto again;
 }
 EXPORT_SYMBOL(have_submounts);
 
@@ -824,11 +1121,16 @@ EXPORT_SYMBOL(have_submounts);
  */
 static int select_parent(struct dentry * parent)
 {
-       struct dentry *this_parent = parent;
+       struct dentry *this_parent;
        struct list_head *next;
+       unsigned seq;
        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:
        next = this_parent->d_subdirs.next;
 resume:
@@ -837,11 +1139,13 @@ resume:
                struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
                next = tmp->next;
 
+               spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+
                /* 
                 * move only zero ref count dentries to the end 
                 * of the unused list for prune_dcache
                 */
-               if (!atomic_read(&dentry->d_count)) {
+               if (!dentry->d_count) {
                        dentry_lru_move_tail(dentry);
                        found++;
                } else {
@@ -853,28 +1157,63 @@ resume:
                 * ensures forward progress). We'll be coming back to find
                 * the rest.
                 */
-               if (found && need_resched())
+               if (found && need_resched()) {
+                       spin_unlock(&dentry->d_lock);
                        goto out;
+               }
 
                /*
                 * Descend a level if the d_subdirs list is non-empty.
                 */
                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;
+                       spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
                        goto repeat;
                }
+
+               spin_unlock(&dentry->d_lock);
        }
        /*
         * All done at this level ... ascend and resume the search.
         */
        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;
        }
 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;
+
+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)
 {
-       int nr_unused;
-
        if (nr) {
                if (!(gfp_mask & __GFP_FS))
                        return -1;
                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 = {
@@ -960,38 +1296,52 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
        memcpy(dname, name->name, name->len);
        dname[name->len] = 0;
 
-       atomic_set(&dentry->d_count, 1);
+       dentry->d_count = 1;
        dentry->d_flags = DCACHE_UNHASHED;
        spin_lock_init(&dentry->d_lock);
+       seqcount_init(&dentry->d_seq);
        dentry->d_inode = NULL;
        dentry->d_parent = NULL;
        dentry->d_sb = NULL;
        dentry->d_op = 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_subdirs);
        INIT_LIST_HEAD(&dentry->d_alias);
+       INIT_LIST_HEAD(&dentry->d_u.d_child);
 
        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;
-       } 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);
-       spin_unlock(&dcache_lock);
+               spin_unlock(&parent->d_lock);
+       }
 
-       percpu_counter_inc(&nr_dentry);
+       this_cpu_inc(nr_dentry);
 
        return dentry;
 }
 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 qstr q;
@@ -1003,12 +1353,36 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *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)
 {
+       spin_lock(&dentry->d_lock);
        if (inode)
                list_add(&dentry->d_alias, &inode->i_dentry);
        dentry->d_inode = inode;
+       dentry_rcuwalk_barrier(dentry);
+       spin_unlock(&dentry->d_lock);
        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)
 {
        BUG_ON(!list_empty(&entry->d_alias));
-       spin_lock(&dcache_lock);
+       if (inode)
+               spin_lock(&inode->i_lock);
        __d_instantiate(entry, inode);
-       spin_unlock(&dcache_lock);
+       if (inode)
+               spin_unlock(&inode->i_lock);
        security_d_instantiate(entry, inode);
 }
 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) {
                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)
                        continue;
                if (alias->d_parent != entry->d_parent)
                        continue;
-               if (qstr->len != len)
+               if (dentry_cmp(qstr->name, qstr->len, name, len))
                        continue;
-               if (memcmp(qstr->name, name, len))
-                       continue;
-               dget_locked(alias);
+               __dget(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));
 
-       spin_lock(&dcache_lock);
+       if (inode)
+               spin_lock(&inode->i_lock);
        result = __d_instantiate_unique(entry, inode);
-       spin_unlock(&dcache_lock);
+       if (inode)
+               spin_unlock(&inode->i_lock);
 
        if (!result) {
                security_d_instantiate(entry, inode);
@@ -1134,14 +1515,6 @@ struct dentry * d_alloc_root(struct inode * root_inode)
 }
 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
  * @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 */
 
-       spin_lock(&dcache_lock);
+
+       spin_lock(&inode->i_lock);
        res = __d_find_alias(inode, 0);
        if (res) {
-               spin_unlock(&dcache_lock);
+               spin_unlock(&inode->i_lock);
                dput(tmp);
                goto out_iput;
        }
@@ -1195,12 +1569,14 @@ struct dentry *d_obtain_alias(struct inode *inode)
        tmp->d_sb = inode->i_sb;
        tmp->d_inode = inode;
        tmp->d_flags |= DCACHE_DISCONNECTED;
-       tmp->d_flags &= ~DCACHE_UNHASHED;
        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(&inode->i_lock);
 
-       spin_unlock(&dcache_lock);
        return tmp;
 
  out_iput:
@@ -1230,18 +1606,18 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
        struct dentry *new = NULL;
 
        if (inode && S_ISDIR(inode->i_mode)) {
-               spin_lock(&dcache_lock);
+               spin_lock(&inode->i_lock);
                new = __d_find_alias(inode, 1);
                if (new) {
                        BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&inode->i_lock);
                        security_d_instantiate(new, inode);
                        d_move(new, dentry);
                        iput(inode);
                } else {
-                       /* already taking dcache_lock, so d_add() by hand */
+                       /* already taking inode->i_lock, so d_add() by hand */
                        __d_instantiate(dentry, inode);
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&inode->i_lock);
                        security_d_instantiate(dentry, inode);
                        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
         * already has a dentry.
         */
-       spin_lock(&dcache_lock);
+       spin_lock(&inode->i_lock);
        if (!S_ISDIR(inode->i_mode) || list_empty(&inode->i_dentry)) {
                __d_instantiate(found, inode);
-               spin_unlock(&dcache_lock);
+               spin_unlock(&inode->i_lock);
                security_d_instantiate(found, inode);
                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.
         */
        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);
        d_move(new, found);
        iput(inode);
@@ -1342,6 +1718,112 @@ err_out:
 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
  * @parent: parent dentry
  * @name: qstr of name we wish to find
@@ -1352,10 +1834,10 @@ EXPORT_SYMBOL(d_add_ci);
  * 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.
  */
-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 {
                 seq = read_seqbegin(&rename_lock);
@@ -1367,7 +1849,7 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
 }
 EXPORT_SYMBOL(d_lookup);
 
-/*
+/**
  * __d_lookup - search for a dentry (racy)
  * @parent: parent dentry
  * @name: qstr of name we wish to find
@@ -1382,17 +1864,24 @@ EXPORT_SYMBOL(d_lookup);
  *
  * __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 hash = name->hash;
        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 hlist_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.
         *
         * Take d_lock when comparing a candidate dentry, to avoid races
@@ -1407,25 +1896,16 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
         */
        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)
                        continue;
-               if (dentry->d_parent != parent)
-                       continue;
 
                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)
                        goto next;
-
-               /* non-existing due to RCU? */
                if (d_unhashed(dentry))
                        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
                 * 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;
                } else {
-                       if (qstr->len != len)
-                               goto next;
-                       if (memcmp(qstr->name, str, len))
+                       if (dentry_cmp(tname, tlen, str, len))
                                goto next;
                }
 
-               atomic_inc(&dentry->d_count);
+               dentry->d_count++;
                found = dentry;
                spin_unlock(&dentry->d_lock);
                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.
         */
        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;
        }
        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
  * @dparent: The parent dentry (known to be valid)
  *
  * An insecure source has sent us a dentry, here we verify it and dget() it.
  * This is used by ncpfs in its readdir implementation.
  * 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;
                }
        }
-       rcu_read_unlock();
+       spin_unlock(&dparent->d_lock);
+
        return 0;
 }
 EXPORT_SYMBOL(d_validate);
@@ -1538,16 +2017,23 @@ EXPORT_SYMBOL(d_validate);
  
 void d_delete(struct dentry * dentry)
 {
+       struct inode *inode;
        int isdir = 0;
        /*
         * Are we the only user?
         */
-       spin_lock(&dcache_lock);
+again:
        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_iput(dentry);
+               dentry_unlink_inode(dentry);
                fsnotify_nameremove(dentry, isdir);
                return;
        }
@@ -1556,17 +2042,18 @@ void d_delete(struct dentry * dentry)
                __d_drop(dentry);
 
        spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
 
        fsnotify_nameremove(dentry, isdir);
 }
 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;
-       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)
@@ -1583,25 +2070,39 @@ static void _d_rehash(struct dentry * entry)
  
 void d_rehash(struct dentry * entry)
 {
-       spin_lock(&dcache_lock);
        spin_lock(&entry->d_lock);
        _d_rehash(entry);
        spin_unlock(&entry->d_lock);
-       spin_unlock(&dcache_lock);
 }
 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)
 {
        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);
 }
 
+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
  * @target: new dentry
  *
  * Update the dcache to reflect the move of a file name. Negative
  * 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)
                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);
-       /*
-        * 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 */
        __d_drop(target);
@@ -1715,27 +2246,16 @@ already_unhashed:
        }
 
        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);
        fsnotify_d_move(dentry);
        spin_unlock(&dentry->d_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);
 
 /**
@@ -1761,13 +2281,13 @@ struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2)
  * This helper attempts to cope with remotely renamed directories
  *
  * 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
  * 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 dentry *ret;
@@ -1790,10 +2310,10 @@ static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias)
                goto out_err;
        m2 = &alias->d_parent->d_inode->i_mutex;
 out_unalias:
-       d_move_locked(alias, dentry);
+       d_move(alias, dentry);
        ret = alias;
 out_err:
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
        if (m2)
                mutex_unlock(m2);
        if (m1)
@@ -1804,17 +2324,23 @@ out_err:
 /*
  * Prepare an anonymous dentry for life in the superblock's dentry tree as a
  * 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)
 {
        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;
        aparent = anon->d_parent;
 
+       switch_names(dentry, anon);
+       swap(dentry->d_name.hash, anon->d_name.hash);
+
        dentry->d_parent = (aparent == anon) ? dentry : aparent;
        list_del(&dentry->d_u.d_child);
        if (!IS_ROOT(dentry))
@@ -1829,6 +2355,13 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
        else
                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;
 }
 
@@ -1846,14 +2379,15 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
 
        BUG_ON(!d_unhashed(dentry));
 
-       spin_lock(&dcache_lock);
-
        if (!inode) {
                actual = dentry;
                __d_instantiate(dentry, NULL);
-               goto found_lock;
+               d_rehash(actual);
+               goto out_nolock;
        }
 
+       spin_lock(&inode->i_lock);
+
        if (S_ISDIR(inode->i_mode)) {
                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
                         * into our tree? */
                        if (IS_ROOT(alias)) {
-                               spin_lock(&alias->d_lock);
                                __d_materialise_dentry(dentry, alias);
                                __d_drop(alias);
                                goto found;
                        }
                        /* Nope, but we must(!) avoid directory aliasing */
-                       actual = __d_unalias(dentry, alias);
+                       actual = __d_unalias(inode, dentry, alias);
                        if (IS_ERR(actual))
                                dput(alias);
                        goto out_nolock;
@@ -1881,15 +2414,14 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
        actual = __d_instantiate_unique(dentry, inode);
        if (!actual)
                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);
 found:
        _d_rehash(actual);
        spin_unlock(&actual->d_lock);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
 out_nolock:
        if (actual == dentry) {
                security_d_instantiate(dentry, inode);
@@ -1898,10 +2430,6 @@ out_nolock:
 
        iput(inode);
        return actual;
-
-shouldnt_be_hashed:
-       spin_unlock(&dcache_lock);
-       BUG();
 }
 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
  * @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
  * 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;
                prefetch(parent);
+               spin_lock(&dentry->d_lock);
                error = prepend_name(buffer, buflen, &dentry->d_name);
+               spin_unlock(&dentry->d_lock);
                if (!error)
                        error = prepend(buffer, buflen, "/", 1);
                if (error)
@@ -2012,9 +2542,9 @@ char *__d_path(const struct path *path, struct path *root,
        int error;
 
        prepend(&res, &buflen, "\0", 1);
-       spin_lock(&dcache_lock);
+       write_seqlock(&rename_lock);
        error = prepend_path(path, root, &res, &buflen);
-       spin_unlock(&dcache_lock);
+       write_sequnlock(&rename_lock);
 
        if (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);
 
        get_fs_root(current->fs, &root);
-       spin_lock(&dcache_lock);
+       write_seqlock(&rename_lock);
        tmp = root;
        error = path_with_deleted(path, &tmp, &res, &buflen);
        if (error)
                res = ERR_PTR(error);
-       spin_unlock(&dcache_lock);
+       write_sequnlock(&rename_lock);
        path_put(&root);
        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);
 
        get_fs_root(current->fs, &root);
-       spin_lock(&dcache_lock);
+       write_seqlock(&rename_lock);
        tmp = root;
        error = path_with_deleted(path, &tmp, &res, &buflen);
        if (!error && !path_equal(&tmp, &root))
                error = prepend_unreachable(&res, &buflen);
-       spin_unlock(&dcache_lock);
+       write_sequnlock(&rename_lock);
        path_put(&root);
        if (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.
  */
-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 *retval;
@@ -2158,10 +2688,13 @@ char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
 
        while (!IS_ROOT(dentry)) {
                struct dentry *parent = dentry->d_parent;
+               int error;
 
                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;
 
                retval = end;
@@ -2171,14 +2704,25 @@ char *__dentry_path(struct dentry *dentry, char *buf, int buflen)
 Elong:
        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 *p = NULL;
        char *retval;
 
-       spin_lock(&dcache_lock);
+       write_seqlock(&rename_lock);
        if (d_unlinked(dentry)) {
                p = buf + buflen;
                if (prepend(&p, &buflen, "//deleted", 10) != 0)
@@ -2186,12 +2730,11 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen)
                buflen++;
        }
        retval = __dentry_path(dentry, buf, buflen);
-       spin_unlock(&dcache_lock);
+       write_sequnlock(&rename_lock);
        if (!IS_ERR(retval) && p)
                *p = '/';       /* restore '/' overriden with '\0' */
        return retval;
 Elong:
-       spin_unlock(&dcache_lock);
        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);
 
        error = -ENOENT;
-       spin_lock(&dcache_lock);
+       write_seqlock(&rename_lock);
        if (!d_unlinked(pwd.dentry)) {
                unsigned long len;
                struct path tmp = root;
@@ -2234,7 +2777,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
 
                prepend(&cwd, &buflen, "\0", 1);
                error = prepend_path(&pwd, &tmp, &cwd, &buflen);
-               spin_unlock(&dcache_lock);
+               write_sequnlock(&rename_lock);
 
                if (error)
                        goto out;
@@ -2253,8 +2796,9 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
                        if (copy_to_user(buf, cwd, len))
                                error = -EFAULT;
                }
-       } else
-               spin_unlock(&dcache_lock);
+       } else {
+               write_sequnlock(&rename_lock);
+       }
 
 out:
        path_put(&pwd);
@@ -2282,25 +2826,25 @@ out:
 int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
 {
        int result;
-       unsigned long seq;
+       unsigned seq;
 
        if (new_dentry == old_dentry)
                return 1;
 
-       /*
-        * Need rcu_readlock to protect against the d_parent trashing
-        * due to d_move
-        */
-       rcu_read_lock();
        do {
                /* for restarting inner loop in case of seq retry */
                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))
                        result = 1;
                else
                        result = 0;
+               rcu_read_unlock();
        } while (read_seqretry(&rename_lock, seq));
-       rcu_read_unlock();
 
        return result;
 }
@@ -2332,10 +2876,15 @@ EXPORT_SYMBOL(path_is_under);
 
 void d_genocide(struct dentry *root)
 {
-       struct dentry *this_parent = root;
+       struct dentry *this_parent;
        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:
        next = this_parent->d_subdirs.next;
 resume:
@@ -2343,21 +2892,62 @@ resume:
                struct list_head *tmp = next;
                struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
                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;
+               }
                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;
+                       spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
                        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) {
-               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;
        }
-       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 =
                alloc_large_system_hash("Dentry cache",
-                                       sizeof(struct hlist_head),
+                                       sizeof(struct dcache_hash_bucket),
                                        dhash_entries,
                                        13,
                                        HASH_EARLY,
@@ -2420,16 +3010,13 @@ static void __init dcache_init_early(void)
                                        0);
 
        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)
 {
        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,
         * but it is probably not worth it because of the cache nature
@@ -2446,7 +3033,7 @@ static void __init dcache_init(void)
 
        dentry_hashtable =
                alloc_large_system_hash("Dentry cache",
-                                       sizeof(struct hlist_head),
+                                       sizeof(struct dcache_hash_bucket),
                                        dhash_entries,
                                        13,
                                        0,
@@ -2455,7 +3042,7 @@ static void __init dcache_init(void)
                                        0);
 
        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 */
index 906e803..6fc4f31 100644 (file)
  */
 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 vfsmount *vfsmount_save;
        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)
                goto out;
        dentry_save = nd->path.dentry;
index 9d1a22d..337352a 100644 (file)
@@ -260,7 +260,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
                                   ecryptfs_dentry->d_parent));
        lower_inode = lower_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,
                                    kmem_cache_alloc(ecryptfs_dentry_info_cache,
                                                     GFP_KERNEL));
@@ -441,7 +441,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
        struct qstr lower_name;
        int rc = 0;
 
-       ecryptfs_dentry->d_op = &ecryptfs_dops;
+       d_set_d_op(ecryptfs_dentry, &ecryptfs_dops);
        if ((ecryptfs_dentry->d_name.len == 1
             && !strcmp(ecryptfs_dentry->d_name.name, "."))
            || (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;
        if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
                rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
-                                                   &lower_name);
+                               lower_dir_dentry->d_inode, &lower_name);
                if (rc < 0)
                        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);
        if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) {
                rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry,
-                                                   &lower_name);
+                               lower_dir_dentry->d_inode, &lower_name);
                if (rc < 0)
                        goto out_d_drop;
        }
@@ -980,8 +980,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
 }
 
 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);
 }
 
index a9dbd62..3510386 100644 (file)
@@ -189,7 +189,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
        if (special_file(lower_inode->i_mode))
                init_special_inode(inode, lower_inode->i_mode,
                                   lower_inode->i_rdev);
-       dentry->d_op = &ecryptfs_dops;
+       d_set_d_op(dentry, &ecryptfs_dops);
        fsstack_copy_attr_all(inode, lower_inode);
        /* This size will be overwritten for real files w/ headers and
         * other metadata */
@@ -594,7 +594,7 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
                deactivate_locked_super(s);
                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_parent = s->s_root;
 
index 2720178..3042fe1 100644 (file)
@@ -62,6 +62,16 @@ out:
        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
  * @inode: The ecryptfs inode
@@ -88,7 +98,7 @@ static void ecryptfs_destroy_inode(struct inode *inode)
                }
        }
        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);
 }
 
 /**
index 5073a07..0f31acb 100644 (file)
@@ -65,11 +65,18 @@ static struct inode *efs_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void efs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, efs_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct efs_inode_info *ei = (struct efs_inode_info *) foo;
index 79c3ae6..8c6c466 100644 (file)
@@ -150,12 +150,19 @@ static struct inode *exofs_alloc_inode(struct super_block *sb)
        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
  */
 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);
 }
 
 /*
index 51b3040..4b68257 100644 (file)
@@ -43,24 +43,26 @@ find_acceptable_alias(struct dentry *result,
                void *context)
 {
        struct dentry *dentry, *toput = NULL;
+       struct inode *inode;
 
        if (acceptable(context, 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)
                        dput(toput);
                if (dentry != result && acceptable(context, dentry)) {
                        dput(result);
                        return dentry;
                }
-               spin_lock(&dcache_lock);
+               spin_lock(&inode->i_lock);
                toput = dentry;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
 
        if (toput)
                dput(toput);
index 2bcc043..7b41805 100644 (file)
@@ -232,10 +232,17 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 }
 
 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))
                return PTR_ERR(acl);
        if (acl) {
index 3ff6cbb..c939b7b 100644 (file)
@@ -54,7 +54,7 @@ static inline int ext2_acl_count(size_t size)
 #ifdef CONFIG_EXT2_FS_POSIX_ACL
 
 /* 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_init_acl (struct inode *, struct inode *);
 
index d89e0b6..e0c6380 100644 (file)
@@ -161,11 +161,18 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void ext2_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, ext2_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
index 8a11fe2..e4fa49e 100644 (file)
@@ -240,10 +240,17 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 
 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))
                return PTR_ERR(acl);
        if (acl) {
index 5973346..5faf804 100644 (file)
@@ -54,7 +54,7 @@ static inline int ext3_acl_count(size_t size)
 #ifdef CONFIG_EXT3_FS_POSIX_ACL
 
 /* 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_init_acl (handle_t *, struct inode *, struct inode *);
 
index acf8695..77ce161 100644 (file)
@@ -479,6 +479,13 @@ static struct inode *ext3_alloc_inode(struct super_block *sb)
        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)
 {
        if (!list_empty(&(EXT3_I(inode)->i_orphan))) {
@@ -489,7 +496,7 @@ static void ext3_destroy_inode(struct inode *inode)
                                false);
                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)
index 5e2ed45..e0270d1 100644 (file)
@@ -238,10 +238,17 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 
 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))
                return PTR_ERR(acl);
        if (acl) {
index 9d843d5..dec8211 100644 (file)
@@ -54,7 +54,7 @@ static inline int ext4_acl_count(size_t size)
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
 
 /* 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_init_acl(handle_t *, struct inode *, struct inode *);
 
index fb15c9c..cd37f9d 100644 (file)
@@ -841,6 +841,13 @@ static int ext4_drop_inode(struct inode *inode)
        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)
 {
        ext4_ioend_wait(inode);
@@ -853,7 +860,7 @@ static void ext4_destroy_inode(struct inode *inode)
                                true);
                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)
index ad6998a..206351a 100644 (file)
@@ -514,11 +514,18 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
        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));
 }
 
+static void fat_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, fat_i_callback);
+}
+
 static void init_once(void *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);
        if (!IS_ERR(result))
-               result->d_op = sb->s_root->d_op;
+               d_set_d_op(result, sb->s_root->d_op);
        return result;
 }
 
@@ -793,7 +800,7 @@ static struct dentry *fat_get_parent(struct dentry *child)
 
        parent = d_obtain_alias(inode);
        if (!IS_ERR(parent))
-               parent->d_op = sb->s_root->d_op;
+               d_set_d_op(parent, sb->s_root->d_op);
 out:
        unlock_super(sb);
 
index 3345aab..35ffe43 100644 (file)
@@ -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
  * 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;
        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,
  * 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];
        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)
                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)
                goto old_compare;
        error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
@@ -182,8 +185,8 @@ out:
 
 old_compare:
        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;
 }
 
@@ -224,10 +227,10 @@ static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
        }
 out:
        unlock_super(sb);
-       dentry->d_op = &msdos_dentry_operations;
+       d_set_d_op(dentry, &msdos_dentry_operations);
        dentry = d_splice_alias(inode, dentry);
        if (dentry)
-               dentry->d_op = &msdos_dentry_operations;
+               d_set_d_op(dentry, &msdos_dentry_operations);
        return dentry;
 
 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_root->d_op = &msdos_dentry_operations;
+       d_set_d_op(sb->s_root, &msdos_dentry_operations);
        unlock_super(sb);
        return 0;
 }
index b936703..e3ffc5e 100644 (file)
@@ -43,6 +43,9 @@ static int vfat_revalidate_shortname(struct dentry *dentry)
 
 static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
        /* This is not negative dentry. Always valid. */
        if (dentry->d_inode)
                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)
 {
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
        /*
         * 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 */
-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--;
        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.
  * 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
  * 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));
        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
  * 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;
        unsigned int len;
        unsigned long hash;
@@ -133,16 +144,18 @@ static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
 /*
  * 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;
 
        /* 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 (nls_strnicmp(t, a->name, b->name, alen) == 0)
+               if (nls_strnicmp(t, name->name, str, alen) == 0)
                        return 0;
        }
        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.
  */
-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;
 
        /* 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 (strncmp(a->name, b->name, alen) == 0)
+               if (strncmp(name->name, str, alen) == 0)
                        return 0;
        }
        return 1;
@@ -757,11 +772,11 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
 
 out:
        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_splice_alias(inode, 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;
        }
        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')
-               sb->s_root->d_op = &vfat_ci_dentry_ops;
+               d_set_d_op(sb->s_root, &vfat_ci_dentry_ops);
        else
-               sb->s_root->d_op = &vfat_dentry_ops;
+               d_set_d_op(sb->s_root, &vfat_dentry_ops);
 
        unlock_super(sb);
        return 0;
index 68ba492..751d6b2 100644 (file)
@@ -115,6 +115,9 @@ int unregister_filesystem(struct file_system_type * fs)
                tmp = &(*tmp)->next;
        }
        write_unlock(&file_systems_lock);
+
+       synchronize_rcu();
+
        return -EINVAL;
 }
 
index 8c04eac..2ba6719 100644 (file)
@@ -337,6 +337,13 @@ vxfs_iget(struct super_block *sbp, ino_t ino)
        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
  * @ip:                inode to discard.
@@ -350,5 +357,5 @@ vxfs_evict_inode(struct inode *ip)
 {
        truncate_inode_pages(&ip->i_data, 0);
        end_writeback(ip);
-       kmem_cache_free(vxfs_inode_cachep, ip->i_private);
+       call_rcu(&ip->i_rcu, vxfs_i_callback);
 }
index ed45a9c..68ca487 100644 (file)
@@ -14,12 +14,14 @@ void set_fs_root(struct fs_struct *fs, struct path *path)
        struct path old_root;
 
        spin_lock(&fs->lock);
+       write_seqcount_begin(&fs->seq);
        old_root = fs->root;
        fs->root = *path;
-       path_get(path);
+       path_get_long(path);
+       write_seqcount_end(&fs->seq);
        spin_unlock(&fs->lock);
        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;
 
        spin_lock(&fs->lock);
+       write_seqcount_begin(&fs->seq);
        old_pwd = fs->pwd;
        fs->pwd = *path;
-       path_get(path);
+       path_get_long(path);
+       write_seqcount_end(&fs->seq);
        spin_unlock(&fs->lock);
 
        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)
@@ -52,31 +56,33 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root)
                fs = p->fs;
                if (fs) {
                        spin_lock(&fs->lock);
+                       write_seqcount_begin(&fs->seq);
                        if (fs->root.dentry == old_root->dentry
                            && fs->root.mnt == old_root->mnt) {
-                               path_get(new_root);
+                               path_get_long(new_root);
                                fs->root = *new_root;
                                count++;
                        }
                        if (fs->pwd.dentry == old_root->dentry
                            && fs->pwd.mnt == old_root->mnt) {
-                               path_get(new_root);
+                               path_get_long(new_root);
                                fs->pwd = *new_root;
                                count++;
                        }
+                       write_seqcount_end(&fs->seq);
                        spin_unlock(&fs->lock);
                }
                task_unlock(p);
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
        while (count--)
-               path_put(old_root);
+               path_put_long(old_root);
 }
 
 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);
 }
 
@@ -88,8 +94,10 @@ void exit_fs(struct task_struct *tsk)
                int kill;
                task_lock(tsk);
                spin_lock(&fs->lock);
+               write_seqcount_begin(&fs->seq);
                tsk->fs = NULL;
                kill = !--fs->users;
+               write_seqcount_end(&fs->seq);
                spin_unlock(&fs->lock);
                task_unlock(tsk);
                if (kill)
@@ -105,8 +113,15 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
                fs->users = 1;
                fs->in_exec = 0;
                spin_lock_init(&fs->lock);
+               seqcount_init(&fs->seq);
                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;
 }
@@ -144,6 +159,7 @@ EXPORT_SYMBOL(current_umask);
 struct fs_struct init_fs = {
        .users          = 1,
        .lock           = __SPIN_LOCK_UNLOCKED(init_fs.lock),
+       .seq            = SEQCNT_ZERO,
        .umask          = 0022,
 };
 
index c9627c9..f738599 100644 (file)
@@ -156,8 +156,12 @@ u64 fuse_get_attr_version(struct fuse_conn *fc)
  */
 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))
                return 0;
        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->d_op = &fuse_dentry_operations;
+       d_set_d_op(entry, &fuse_dentry_operations);
        if (outarg_valid)
                fuse_change_entry_timeout(entry, &outarg);
        else
@@ -981,12 +985,15 @@ static int fuse_access(struct inode *inode, int mask)
  * access request is sent.  Execute permission is still checked
  * 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);
        bool refreshed = false;
        int err = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        if (!fuse_allow_task(fc, current))
                return -EACCES;
 
@@ -1001,7 +1008,7 @@ static int fuse_permission(struct inode *inode, int mask)
        }
 
        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
                   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) {
                        err = fuse_do_getattr(inode, NULL, NULL);
                        if (!err)
-                               err = generic_permission(inode, mask, NULL);
+                               err = generic_permission(inode, mask,
+                                                       flags, NULL);
                }
 
                /* Note: the opposite of the above test does not
index cfce3ad..a8b31da 100644 (file)
@@ -99,6 +99,13 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
        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)
 {
        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));
        if (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,
@@ -619,7 +626,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb,
 
        entry = d_obtain_alias(inode);
        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);
        }
 
@@ -721,7 +728,7 @@ static struct dentry *fuse_get_parent(struct dentry *child)
 
        parent = d_obtain_alias(inode);
        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);
        }
 
index 6bc9e3a..06c48a8 100644 (file)
@@ -190,14 +190,20 @@ generic_acl_chmod(struct inode *inode)
 }
 
 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;
 }
index 48171f4..7118f1a 100644 (file)
@@ -75,11 +75,14 @@ static struct posix_acl *gfs2_acl_get(struct gfs2_inode *ip, int type)
  * 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;
        int error;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        acl = gfs2_acl_get(GFS2_I(inode), ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
index b522b0c..a93907c 100644 (file)
@@ -16,7 +16,7 @@
 #define GFS2_POSIX_ACL_DEFAULT         "posix_acl_default"
 #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_chmod(struct gfs2_inode *ip, struct iattr *attr);
 extern const struct xattr_handler gfs2_xattr_system_handler;
index 6798755..4a45633 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/namei.h>
 #include <linux/crc32.h>
 
 #include "gfs2.h"
 
 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_inode *ip = NULL;
        int error;
        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 (is_bad_inode(inode))
                        goto invalid;
@@ -100,13 +109,14 @@ fail:
        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);
        return 0;
 }
 
-static int gfs2_dentry_delete(struct dentry *dentry)
+static int gfs2_dentry_delete(const struct dentry *dentry)
 {
        struct gfs2_inode *ginode;
 
index 5ab3839..97012ec 100644 (file)
@@ -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));
        if (!IS_ERR(dentry))
-               dentry->d_op = &gfs2_dops;
+               d_set_d_op(dentry, &gfs2_dops);
        return dentry;
 }
 
@@ -158,7 +158,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
 out_inode:
        dentry = d_obtain_alias(inode);
        if (!IS_ERR(dentry))
-               dentry->d_op = &gfs2_dops;
+               d_set_d_op(dentry, &gfs2_dops);
        return dentry;
 }
 
index aa99647..fca6689 100644 (file)
@@ -241,7 +241,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
            !capable(CAP_LINUX_IMMUTABLE))
                goto out;
        if (!IS_IMMUTABLE(inode)) {
-               error = gfs2_permission(inode, MAY_WRITE);
+               error = gfs2_permission(inode, MAY_WRITE, 0);
                if (error)
                        goto out;
        }
index 14e682d..2232b3c 100644 (file)
@@ -509,7 +509,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
        }
 
        if (!is_root) {
-               error = gfs2_permission(dir, MAY_EXEC);
+               error = gfs2_permission(dir, MAY_EXEC, 0);
                if (error)
                        goto out;
        }
@@ -539,7 +539,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
 {
        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)
                return error;
 
index d8499fa..732a183 100644 (file)
@@ -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,
                                  const struct qstr *name,
                                  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 struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
 extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
index 3eb1393..2aeabd4 100644 (file)
@@ -440,7 +440,7 @@ static int gfs2_lookup_root(struct super_block *sb, struct dentry **dptr,
                iput(inode);
                return -ENOMEM;
        }
-       dentry->d_op = &gfs2_dops;
+       d_set_d_op(dentry, &gfs2_dops);
        *dptr = dentry;
        return 0;
 }
index 1db6b73..1501db4 100644 (file)
@@ -106,7 +106,7 @@ static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
 {
        struct inode *inode = NULL;
 
-       dentry->d_op = &gfs2_dops;
+       d_set_d_op(dentry, &gfs2_dops);
 
        inode = gfs2_lookupi(dir, &dentry->d_name, 0);
        if (inode && IS_ERR(inode))
@@ -166,7 +166,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
        if (error)
                goto out_child;
 
-       error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC);
+       error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
        if (error)
                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))
                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)
                return error;
 
@@ -822,7 +822,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
                        }
                }
        } else {
-               error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC);
+               error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
                if (error)
                        goto out_gunlock;
 
@@ -857,7 +857,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
        /* Check out the dir to be renamed */
 
        if (dir_rename) {
-               error = gfs2_permission(odentry->d_inode, MAY_WRITE);
+               error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
                if (error)
                        goto out_gunlock;
        }
@@ -1041,13 +1041,17 @@ static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
  * 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;
        int error;
        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) {
                error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
                if (error)
@@ -1058,7 +1062,7 @@ int gfs2_permission(struct inode *inode, int mask)
        if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
                error = -EACCES;
        else
-               error = generic_permission(inode, mask, gfs2_check_acl);
+               error = generic_permission(inode, mask, flags, gfs2_check_acl);
        if (unlock)
                gfs2_glock_dq_uninit(&i_gh);
 
index 2b2c499..16c2eca 100644 (file)
@@ -1405,11 +1405,18 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb)
        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);
 }
 
+static void gfs2_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, gfs2_i_callback);
+}
+
 const struct super_operations gfs2_super_ops = {
        .alloc_inode            = gfs2_alloc_inode,
        .destroy_inode          = gfs2_destroy_inode,
index 2b3b861..ea4aefe 100644 (file)
@@ -25,7 +25,7 @@ static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
        struct inode *inode = NULL;
        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_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
index c8cffb8..ad97c2d 100644 (file)
@@ -213,10 +213,14 @@ extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
 /* string.c */
 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,
                      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 */
 extern void hfs_asc2mac(struct super_block *, struct hfs_name *, struct qstr *);
index 927a5af..495a976 100644 (file)
@@ -51,7 +51,8 @@ static unsigned char caseorder[256] = {
 /*
  * 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;
        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.
  * 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;
-       int len;
 
-       len = s1->len;
        if (len >= HFS_NAMELEN) {
-               if (s2->len < HFS_NAMELEN)
+               if (name->len < HFS_NAMELEN)
                        return 1;
                len = HFS_NAMELEN;
-       } else if (len != s2->len)
+       } else if (len != name->len)
                return 1;
 
-       n1 = s1->name;
-       n2 = s2->name;
+       n1 = str;
+       n2 = name->name;
        while (len--) {
                if (caseorder[*n1++] != caseorder[*n2++])
                        return 1;
index 4824c27..0bef62a 100644 (file)
@@ -167,11 +167,18 @@ static struct inode *hfs_alloc_inode(struct super_block *sb)
        return i ? &i->vfs_inode : NULL;
 }
 
-static void hfs_destroy_inode(struct inode *inode)
+static void hfs_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(hfs_inode_cachep, HFS_I(inode));
 }
 
+static void hfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, hfs_i_callback);
+}
+
 static const struct super_operations hfs_super_operations = {
        .alloc_inode    = hfs_alloc_inode,
        .destroy_inode  = hfs_destroy_inode,
@@ -427,7 +434,7 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent)
        if (!sb->s_root)
                goto bail_iput;
 
-       sb->s_root->d_op = &hfs_dentry_operations;
+       d_set_d_op(sb->s_root, &hfs_dentry_operations);
 
        /* everything's okay */
        return 0;
index 7478f5c..19cf291 100644 (file)
@@ -8,15 +8,20 @@
  * This file contains the code to do various system dependent things.
  */
 
+#include <linux/namei.h>
 #include "hfs_fs.h"
 
 /* dentry case-handling: just lowercase everything */
 
 static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode;
        int diff;
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
        if(!inode)
                return 1;
 
index 9d59c05..ccab871 100644 (file)
@@ -37,7 +37,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
 
        sb = dir->i_sb;
 
-       dentry->d_op = &hfsplus_dentry_operations;
+       d_set_d_op(dentry, &hfsplus_dentry_operations);
        dentry->d_fsdata = NULL;
        hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
        hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
index cb3653e..a5308f4 100644 (file)
@@ -379,8 +379,12 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *, const struct hfsplus_unist
 int hfsplus_strcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
 int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *);
 int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int);
-int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str);
-int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2);
+int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *str);
+int hfsplus_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);
 
 /* wrapper.c */
 int hfsplus_read_wrapper(struct super_block *);
index 52cc746..ddf712e 100644 (file)
@@ -419,7 +419,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
                err = -ENOMEM;
                goto cleanup;
        }
-       sb->s_root->d_op = &hfsplus_dentry_operations;
+       d_set_d_op(sb->s_root, &hfsplus_dentry_operations);
 
        str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
        str.name = HFSP_HIDDENDIR_NAME;
@@ -488,11 +488,19 @@ static struct inode *hfsplus_alloc_inode(struct super_block *sb)
        return i ? &i->vfs_inode : NULL;
 }
 
-static void hfsplus_destroy_inode(struct inode *inode)
+static void hfsplus_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(hfsplus_inode_cachep, HFSPLUS_I(inode));
 }
 
+static void hfsplus_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, hfsplus_i_callback);
+}
+
 #define HFSPLUS_INODE_SIZE     sizeof(struct hfsplus_inode_info)
 
 static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
index b66d67d..d800aa0 100644 (file)
@@ -320,7 +320,8 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr,
  * Composed unicode characters are decomposed and case-folding is performed
  * if the appropriate bits are (un)set on the superblock.
  */
-int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str)
+int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *str)
 {
        struct super_block *sb = dentry->d_sb;
        const char *astr;
@@ -363,9 +364,12 @@ int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str)
  * Composed unicode characters are decomposed and case-folding is performed
  * if the appropriate bits are (un)set on the superblock.
  */
-int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2)
+int hfsplus_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)
 {
-       struct super_block *sb = dentry->d_sb;
+       struct super_block *sb = parent->d_sb;
        int casefold, decompose, size;
        int dsize1, dsize2, len1, len2;
        const u16 *dstr1, *dstr2;
@@ -375,10 +379,10 @@ int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *
 
        casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
        decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
-       astr1 = s1->name;
-       len1 = s1->len;
-       astr2 = s2->name;
-       len2 = s2->len;
+       astr1 = str;
+       len1 = len;
+       astr2 = name->name;
+       len2 = name->len;
        dsize1 = dsize2 = 0;
        dstr1 = dstr2 = NULL;
 
index 2c0f148..d3244d9 100644 (file)
@@ -32,7 +32,7 @@ static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
 
 #define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_path.dentry->d_inode)
 
-static int hostfs_d_delete(struct dentry *dentry)
+static int hostfs_d_delete(const struct dentry *dentry)
 {
        return 1;
 }
@@ -92,12 +92,10 @@ __uml_setup("hostfs=", hostfs_args,
 
 static char *__dentry_name(struct dentry *dentry, char *name)
 {
-       char *p = __dentry_path(dentry, name, PATH_MAX);
+       char *p = dentry_path_raw(dentry, name, PATH_MAX);
        char *root;
        size_t len;
 
-       spin_unlock(&dcache_lock);
-
        root = dentry->d_sb->s_fs_info;
        len = strlen(root);
        if (IS_ERR(p)) {
@@ -123,25 +121,23 @@ static char *dentry_name(struct dentry *dentry)
        if (!name)
                return NULL;
 
-       spin_lock(&dcache_lock);
        return __dentry_name(dentry, name); /* will unlock */
 }
 
 static char *inode_name(struct inode *ino)
 {
        struct dentry *dentry;
-       char *name = __getname();
-       if (!name)
-               return NULL;
+       char *name;
 
-       spin_lock(&dcache_lock);
-       if (list_empty(&ino->i_dentry)) {
-               spin_unlock(&dcache_lock);
-               __putname(name);
+       dentry = d_find_alias(ino);
+       if (!dentry)
                return NULL;
-       }
-       dentry = list_first_entry(&ino->i_dentry, struct dentry, d_alias);
-       return __dentry_name(dentry, name); /* will unlock */
+
+       name = dentry_name(dentry);
+
+       dput(dentry);
+
+       return name;
 }
 
 static char *follow_link(char *link)
@@ -251,11 +247,18 @@ static void hostfs_evict_inode(struct inode *inode)
        }
 }
 
-static void hostfs_destroy_inode(struct inode *inode)
+static void hostfs_i_callback(struct rcu_head *head)
 {
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       INIT_LIST_HEAD(&inode->i_dentry);
        kfree(HOSTFS_I(inode));
 }
 
+static void hostfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, hostfs_i_callback);
+}
+
 static int hostfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
 {
        const char *root_path = vfs->mnt_sb->s_fs_info;
@@ -609,7 +612,7 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
                goto out_put;
 
        d_add(dentry, inode);
-       dentry->d_op = &hostfs_dentry_ops;
+       d_set_d_op(dentry, &hostfs_dentry_ops);
        return NULL;
 
  out_put:
@@ -746,11 +749,14 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
        return err;
 }
 
-int hostfs_permission(struct inode *ino, int desired)
+int hostfs_permission(struct inode *ino, int desired, unsigned int flags)
 {
        char *name;
        int r = 0, w = 0, x = 0, err;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        if (desired & MAY_READ) r = 1;
        if (desired & MAY_WRITE) w = 1;
        if (desired & MAY_EXEC) x = 1;
@@ -765,7 +771,7 @@ int hostfs_permission(struct inode *ino, int desired)
                err = access_file(name, r, w, x);
        __putname(name);
        if (!err)
-               err = generic_permission(ino, desired, NULL);
+               err = generic_permission(ino, desired, flags, NULL);
        return err;
 }
 
index 67d9d36..32c13a9 100644 (file)
@@ -12,7 +12,8 @@
  * Note: the dentry argument is the parent dentry.
  */
 
-static int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+static int hpfs_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
 {
        unsigned long    hash;
        int              i;
@@ -34,19 +35,25 @@ static int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
        return 0;
 }
 
-static int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+static int hpfs_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)
 {
-       unsigned al=a->len;
-       unsigned bl=b->len;
-       hpfs_adjust_length(a->name, &al);
+       unsigned al = len;
+       unsigned bl = name->len;
+
+       hpfs_adjust_length(str, &al);
        /*hpfs_adjust_length(b->name, &bl);*/
-       /* 'a' is the qstr of an already existing dentry, so the name
-        * must be valid. 'b' must be validated first.
+
+       /*
+        * 'str' is the nane of an already existing dentry, so the name
+        * must be valid. 'name' must be validated first.
         */
 
-       if (hpfs_chk_name(b->name, &bl))
+       if (hpfs_chk_name(name->name, &bl))
                return 1;
-       if (hpfs_compare_names(dentry->d_sb, a->name, al, b->name, bl, 0))
+       if (hpfs_compare_names(parent->d_sb, str, al, name->name, bl, 0))
                return 1;
        return 0;
 }
@@ -58,5 +65,5 @@ static const struct dentry_operations hpfs_dentry_operations = {
 
 void hpfs_set_dentry_operations(struct dentry *dentry)
 {
-       dentry->d_op = &hpfs_dentry_operations;
+       d_set_d_op(dentry, &hpfs_dentry_operations);
 }
index 11c2b40..f4ad9e3 100644 (file)
@@ -419,7 +419,7 @@ again:
                        unlock_kernel();
                        return -ENOSPC;
                }
-               if (generic_permission(inode, MAY_WRITE, NULL) ||
+               if (generic_permission(inode, MAY_WRITE, 0, NULL) ||
                    !S_ISREG(inode->i_mode) ||
                    get_write_access(inode)) {
                        d_rehash(dentry);
index 6c5f015..49935ba 100644 (file)
@@ -177,11 +177,18 @@ static struct inode *hpfs_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void hpfs_destroy_inode(struct inode *inode)
+static void hpfs_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(hpfs_inode_cachep, hpfs_i(inode));
 }
 
+static void hpfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, hpfs_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
index f702b5f..87ed48e 100644 (file)
@@ -632,11 +632,18 @@ void hppfs_evict_inode(struct inode *ino)
        mntput(ino->i_sb->s_fs_info);
 }
 
-static void hppfs_destroy_inode(struct inode *inode)
+static void hppfs_i_callback(struct rcu_head *head)
 {
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       INIT_LIST_HEAD(&inode->i_dentry);
        kfree(HPPFS_I(inode));
 }
 
+static void hppfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, hppfs_i_callback);
+}
+
 static const struct super_operations hppfs_sbops = {
        .alloc_inode    = hppfs_alloc_inode,
        .destroy_inode  = hppfs_destroy_inode,
index a5fe681..9885082 100644 (file)
@@ -663,11 +663,18 @@ static struct inode *hugetlbfs_alloc_inode(struct super_block *sb)
        return &p->vfs_inode;
 }
 
+static void hugetlbfs_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(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
+}
+
 static void hugetlbfs_destroy_inode(struct inode *inode)
 {
        hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb));
        mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy);
-       kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
+       call_rcu(&inode->i_rcu, hugetlbfs_i_callback);
 }
 
 static const struct address_space_operations hugetlbfs_aops = {
index ae2727a..da85e56 100644 (file)
@@ -102,26 +102,29 @@ static DECLARE_RWSEM(iprune_sem);
  */
 struct inodes_stat_t inodes_stat;
 
-static struct percpu_counter nr_inodes __cacheline_aligned_in_smp;
-static struct percpu_counter nr_inodes_unused __cacheline_aligned_in_smp;
+static DEFINE_PER_CPU(unsigned int, nr_inodes);
 
 static struct kmem_cache *inode_cachep __read_mostly;
 
-static inline int get_nr_inodes(void)
+static int get_nr_inodes(void)
 {
-       return percpu_counter_sum_positive(&nr_inodes);
+       int i;
+       int sum = 0;
+       for_each_possible_cpu(i)
+               sum += per_cpu(nr_inodes, i);
+       return sum < 0 ? 0 : sum;
 }
 
 static inline int get_nr_inodes_unused(void)
 {
-       return percpu_counter_sum_positive(&nr_inodes_unused);
+       return inodes_stat.nr_unused;
 }
 
 int get_nr_dirty_inodes(void)
 {
+       /* not actually dirty inodes, but a wild approximation */
        int nr_dirty = get_nr_inodes() - get_nr_inodes_unused();
        return nr_dirty > 0 ? nr_dirty : 0;
-
 }
 
 /*
@@ -132,7 +135,6 @@ int proc_nr_inodes(ctl_table *table, int write,
                   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        inodes_stat.nr_inodes = get_nr_inodes();
-       inodes_stat.nr_unused = get_nr_inodes_unused();
        return proc_dointvec(table, write, buffer, lenp, ppos);
 }
 #endif
@@ -224,7 +226,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
        inode->i_fsnotify_mask = 0;
 #endif
 
-       percpu_counter_inc(&nr_inodes);
+       this_cpu_inc(nr_inodes);
 
        return 0;
 out:
@@ -255,6 +257,12 @@ static struct inode *alloc_inode(struct super_block *sb)
        return inode;
 }
 
+void free_inode_nonrcu(struct inode *inode)
+{
+       kmem_cache_free(inode_cachep, inode);
+}
+EXPORT_SYMBOL(free_inode_nonrcu);
+
 void __destroy_inode(struct inode *inode)
 {
        BUG_ON(inode_has_buffers(inode));
@@ -266,10 +274,17 @@ void __destroy_inode(struct inode *inode)
        if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
                posix_acl_release(inode->i_default_acl);
 #endif
-       percpu_counter_dec(&nr_inodes);
+       this_cpu_dec(nr_inodes);
 }
 EXPORT_SYMBOL(__destroy_inode);
 
+static void 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(inode_cachep, inode);
+}
+
 static void destroy_inode(struct inode *inode)
 {
        BUG_ON(!list_empty(&inode->i_lru));
@@ -277,7 +292,7 @@ static void destroy_inode(struct inode *inode)
        if (inode->i_sb->s_op->destroy_inode)
                inode->i_sb->s_op->destroy_inode(inode);
        else
-               kmem_cache_free(inode_cachep, (inode));
+               call_rcu(&inode->i_rcu, i_callback);
 }
 
 /*
@@ -335,7 +350,7 @@ static void inode_lru_list_add(struct inode *inode)
 {
        if (list_empty(&inode->i_lru)) {
                list_add(&inode->i_lru, &inode_lru);
-               percpu_counter_inc(&nr_inodes_unused);
+               inodes_stat.nr_unused++;
        }
 }
 
@@ -343,7 +358,7 @@ static void inode_lru_list_del(struct inode *inode)
 {
        if (!list_empty(&inode->i_lru)) {
                list_del_init(&inode->i_lru);
-               percpu_counter_dec(&nr_inodes_unused);
+               inodes_stat.nr_unused--;
        }
 }
 
@@ -430,6 +445,7 @@ void end_writeback(struct inode *inode)
        BUG_ON(!(inode->i_state & I_FREEING));
        BUG_ON(inode->i_state & I_CLEAR);
        inode_sync_wait(inode);
+       /* don't need i_lock here, no concurrent mods to i_state */
        inode->i_state = I_FREEING | I_CLEAR;
 }
 EXPORT_SYMBOL(end_writeback);
@@ -513,7 +529,7 @@ void evict_inodes(struct super_block *sb)
                list_move(&inode->i_lru, &dispose);
                list_del_init(&inode->i_wb_list);
                if (!(inode->i_state & (I_DIRTY | I_SYNC)))
-                       percpu_counter_dec(&nr_inodes_unused);
+                       inodes_stat.nr_unused--;
        }
        spin_unlock(&inode_lock);
 
@@ -554,7 +570,7 @@ int invalidate_inodes(struct super_block *sb)
                list_move(&inode->i_lru, &dispose);
                list_del_init(&inode->i_wb_list);
                if (!(inode->i_state & (I_DIRTY | I_SYNC)))
-                       percpu_counter_dec(&nr_inodes_unused);
+                       inodes_stat.nr_unused--;
        }
        spin_unlock(&inode_lock);
 
@@ -616,7 +632,7 @@ static void prune_icache(int nr_to_scan)
                if (atomic_read(&inode->i_count) ||
                    (inode->i_state & ~I_REFERENCED)) {
                        list_del_init(&inode->i_lru);
-                       percpu_counter_dec(&nr_inodes_unused);
+                       inodes_stat.nr_unused--;
                        continue;
                }
 
@@ -650,7 +666,7 @@ static void prune_icache(int nr_to_scan)
                 */
                list_move(&inode->i_lru, &freeable);
                list_del_init(&inode->i_wb_list);
-               percpu_counter_dec(&nr_inodes_unused);
+               inodes_stat.nr_unused--;
        }
        if (current_is_kswapd())
                __count_vm_events(KSWAPD_INODESTEAL, reap);
@@ -1648,8 +1664,6 @@ void __init inode_init(void)
                                         SLAB_MEM_SPREAD),
                                         init_once);
        register_shrinker(&icache_shrinker);
-       percpu_counter_init(&nr_inodes, 0);
-       percpu_counter_init(&nr_inodes_unused, 0);
 
        /* Hash may have been set up in inode_init_early */
        if (!hashdist)
index e43b9a4..9687c2e 100644 (file)
@@ -63,6 +63,7 @@ extern int copy_mount_string(const void __user *, char **);
 
 extern void free_vfsmnt(struct vfsmount *);
 extern struct vfsmount *alloc_vfsmnt(const char *);
+extern unsigned int mnt_get_count(struct vfsmount *mnt);
 extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int);
 extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *,
                                struct vfsmount *);
index bfdeb82..844a790 100644 (file)
 
 #define BEQUIET
 
-static int isofs_hashi(struct dentry *parent, struct qstr *qstr);
-static int isofs_hash(struct dentry *parent, struct qstr *qstr);
-static int isofs_dentry_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
-static int isofs_dentry_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int isofs_hashi(const struct dentry *parent, const struct inode *inode,
+               struct qstr *qstr);
+static int isofs_hash(const struct dentry *parent, const struct inode *inode,
+               struct qstr *qstr);
+static int isofs_dentry_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);
+static int isofs_dentry_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);
 
 #ifdef CONFIG_JOLIET
-static int isofs_hashi_ms(struct dentry *parent, struct qstr *qstr);
-static int isofs_hash_ms(struct dentry *parent, struct qstr *qstr);
-static int isofs_dentry_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
-static int isofs_dentry_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int isofs_hashi_ms(const struct dentry *parent, const struct inode *inode,
+               struct qstr *qstr);
+static int isofs_hash_ms(const struct dentry *parent, const struct inode *inode,
+               struct qstr *qstr);
+static int isofs_dentry_cmpi_ms(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 isofs_dentry_cmp_ms(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);
 #endif
 
 static void isofs_put_super(struct super_block *sb)
@@ -65,11 +81,18 @@ static struct inode *isofs_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void isofs_destroy_inode(struct inode *inode)
+static void isofs_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(isofs_inode_cachep, ISOFS_I(inode));
 }
 
+static void isofs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, isofs_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct iso_inode_info *ei = foo;
@@ -160,7 +183,7 @@ struct iso9660_options{
  * Compute the hash for the isofs name corresponding to the dentry.
  */
 static int
-isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms)
+isofs_hash_common(const struct dentry *dentry, struct qstr *qstr, int ms)
 {
        const char *name;
        int len;
@@ -181,7 +204,7 @@ isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms)
  * Compute the hash for the isofs name corresponding to the dentry.
  */
 static int
-isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms)
+isofs_hashi_common(const struct dentry *dentry, struct qstr *qstr, int ms)
 {
        const char *name;
        int len;
@@ -206,100 +229,94 @@ isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms)
 }
 
 /*
- * Case insensitive compare of two isofs names.
- */
-static int isofs_dentry_cmpi_common(struct dentry *dentry, struct qstr *a,
-                               struct qstr *b, int ms)
-{
-       int alen, blen;
-
-       /* A filename cannot end in '.' or we treat it like it has none */
-       alen = a->len;
-       blen = b->len;
-       if (ms) {
-               while (alen && a->name[alen-1] == '.')
-                       alen--;
-               while (blen && b->name[blen-1] == '.')
-                       blen--;
-       }
-       if (alen == blen) {
-               if (strnicmp(a->name, b->name, alen) == 0)
-                       return 0;
-       }
-       return 1;
-}
-
-/*
- * Case sensitive compare of two isofs names.
+ * Compare of two isofs names.
  */
-static int isofs_dentry_cmp_common(struct dentry *dentry, struct qstr *a,
-                                       struct qstr *b, int ms)
+static int isofs_dentry_cmp_common(
+               unsigned int len, const char *str,
+               const struct qstr *name, int ms, int ci)
 {
        int alen, blen;
 
        /* A filename cannot end in '.' or we treat it like it has none */
-       alen = a->len;
-       blen = b->len;
+       alen = name->len;
+       blen = len;
        if (ms) {
-               while (alen && a->name[alen-1] == '.')
+               while (alen && name->name[alen-1] == '.')
                        alen--;
-               while (blen && b->name[blen-1] == '.')
+               while (blen && str[blen-1] == '.')
                        blen--;
        }
        if (alen == blen) {
-               if (strncmp(a->name, b->name, alen) == 0)
-                       return 0;
+               if (ci) {
+                       if (strnicmp(name->name, str, alen) == 0)
+                               return 0;
+               } else {
+                       if (strncmp(name->name, str, alen) == 0)
+                               return 0;
+               }
        }
        return 1;
 }
 
 static int
-isofs_hash(struct dentry *dentry, struct qstr *qstr)
+isofs_hash(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
 {
        return isofs_hash_common(dentry, qstr, 0);
 }
 
 static int
-isofs_hashi(struct dentry *dentry, struct qstr *qstr)
+isofs_hashi(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
 {
        return isofs_hashi_common(dentry, qstr, 0);
 }
 
 static int
-isofs_dentry_cmp(struct dentry *dentry,struct qstr *a,struct qstr *b)
+isofs_dentry_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)
 {
-       return isofs_dentry_cmp_common(dentry, a, b, 0);
+       return isofs_dentry_cmp_common(len, str, name, 0, 0);
 }
 
 static int
-isofs_dentry_cmpi(struct dentry *dentry,struct qstr *a,struct qstr *b)
+isofs_dentry_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)
 {
-       return isofs_dentry_cmpi_common(dentry, a, b, 0);
+       return isofs_dentry_cmp_common(len, str, name, 0, 1);
 }
 
 #ifdef CONFIG_JOLIET
 static int
-isofs_hash_ms(struct dentry *dentry, struct qstr *qstr)
+isofs_hash_ms(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
 {
        return isofs_hash_common(dentry, qstr, 1);
 }
 
 static int
-isofs_hashi_ms(struct dentry *dentry, struct qstr *qstr)
+isofs_hashi_ms(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
 {
        return isofs_hashi_common(dentry, qstr, 1);
 }
 
 static int
-isofs_dentry_cmp_ms(struct dentry *dentry,struct qstr *a,struct qstr *b)
+isofs_dentry_cmp_ms(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 isofs_dentry_cmp_common(dentry, a, b, 1);
+       return isofs_dentry_cmp_common(len, str, name, 1, 0);
 }
 
 static int
-isofs_dentry_cmpi_ms(struct dentry *dentry,struct qstr *a,struct qstr *b)
+isofs_dentry_cmpi_ms(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 isofs_dentry_cmpi_common(dentry, a, b, 1);
+       return isofs_dentry_cmp_common(len, str, name, 1, 1);
 }
 #endif
 
@@ -932,7 +949,7 @@ root_found:
                table += 2;
        if (opt.check == 'r')
                table++;
-       s->s_root->d_op = &isofs_dentry_ops[table];
+       d_set_d_op(s->s_root, &isofs_dentry_ops[table]);
 
        kfree(opt.iocharset);
 
index 0d23abf..679a849 100644 (file)
@@ -37,7 +37,8 @@ isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
 
        qstr.name = compare;
        qstr.len = dlen;
-       return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr);
+       return dentry->d_op->d_compare(NULL, NULL, NULL, NULL,
+                       dentry->d_name.len, dentry->d_name.name, &qstr);
 }
 
 /*
@@ -171,7 +172,7 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nam
        struct inode *inode;
        struct page *page;
 
-       dentry->d_op = dir->i_sb->s_root->d_op;
+       d_set_d_op(dentry, dir->i_sb->s_root->d_op);
 
        page = alloc_page(GFP_USER);
        if (!page)
index 54a92fd..95b7967 100644 (file)
@@ -259,11 +259,14 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
        return rc;
 }
 
-int jffs2_check_acl(struct inode *inode, int mask)
+int jffs2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        int rc;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
index 5e42de8..3119f59 100644 (file)
@@ -26,7 +26,7 @@ struct jffs2_acl_header {
 
 #ifdef CONFIG_JFFS2_FS_POSIX_ACL
 
-extern int jffs2_check_acl(struct inode *, int);
+extern int jffs2_check_acl(struct inode *, int, unsigned int);
 extern int jffs2_acl_chmod(struct inode *);
 extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *);
 extern int jffs2_init_acl_post(struct inode *);
index c86041b..853b8e3 100644 (file)
@@ -40,11 +40,18 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb)
        return &f->vfs_inode;
 }
 
-static void jffs2_destroy_inode(struct inode *inode)
+static void jffs2_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(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
 }
 
+static void jffs2_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, jffs2_i_callback);
+}
+
 static void jffs2_i_init_once(void *foo)
 {
        struct jffs2_inode_info *f = foo;
index 1057a49..e5de942 100644 (file)
@@ -114,10 +114,14 @@ out:
        return rc;
 }
 
-int jfs_check_acl(struct inode *inode, int mask)
+int jfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct posix_acl *acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
+       struct posix_acl *acl;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       acl = jfs_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index 54e0755..f9285c4 100644 (file)
@@ -20,7 +20,7 @@
 
 #ifdef CONFIG_JFS_POSIX_ACL
 
-int jfs_check_acl(struct inode *, int);
+int jfs_check_acl(struct inode *, int, unsigned int flags);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
 int jfs_acl_chmod(struct inode *inode);
 
index 231ca4a..4414e3a 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/ctype.h>
 #include <linux/quotaops.h>
 #include <linux/exportfs.h>
@@ -1465,7 +1466,7 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc
        jfs_info("jfs_lookup: name = %s", name);
 
        if (JFS_SBI(dip->i_sb)->mntflag & JFS_OS2)
-               dentry->d_op = &jfs_ci_dentry_operations;
+               d_set_d_op(dentry, &jfs_ci_dentry_operations);
 
        if ((name[0] == '.') && (len == 1))
                inum = dip->i_ino;
@@ -1494,7 +1495,7 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc
        dentry = d_splice_alias(ip, dentry);
 
        if (dentry && (JFS_SBI(dip->i_sb)->mntflag & JFS_OS2))
-               dentry->d_op = &jfs_ci_dentry_operations;
+               d_set_d_op(dentry, &jfs_ci_dentry_operations);
 
        return dentry;
 }
@@ -1573,7 +1574,8 @@ const struct file_operations jfs_dir_operations = {
        .llseek         = generic_file_llseek,
 };
 
-static int jfs_ci_hash(struct dentry *dir, struct qstr *this)
+static int jfs_ci_hash(const struct dentry *dir, const struct inode *inode,
+               struct qstr *this)
 {
        unsigned long hash;
        int i;
@@ -1586,32 +1588,63 @@ static int jfs_ci_hash(struct dentry *dir, struct qstr *this)
        return 0;
 }
 
-static int jfs_ci_compare(struct dentry *dir, struct qstr *a, struct qstr *b)
+static int jfs_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)
 {
        int i, result = 1;
 
-       if (a->len != b->len)
+       if (len != name->len)
                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;
        }
        result = 0;
+out:
+       return result;
+}
 
+static int jfs_ci_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
        /*
-        * We want creates to preserve case.  A negative dentry, a, that
-        * has a different case than b may cause a new entry to be created
-        * with the wrong case.  Since we can't tell if a comes from a negative
-        * dentry, we blindly replace it with b.  This should be harmless if
-        * a is not a negative dentry.
+        * This is not negative dentry. Always valid.
+        *
+        * Note, rename() to existing directory entry will have ->d_inode,
+        * and will use existing name which isn't specified name by user.
+        *
+        * We may be able to drop this positive dentry here. But dropping
+        * positive dentry isn't good idea. So it's unsupported like
+        * rename("filename", "FILENAME") for now.
         */
-       memcpy((unsigned char *)a->name, b->name, a->len);
-out:
-       return result;
+       if (dentry->d_inode)
+               return 1;
+
+       /*
+        * 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;
+       }
+       return 1;
 }
 
 const struct dentry_operations jfs_ci_dentry_operations =
 {
        .d_hash = jfs_ci_hash,
        .d_compare = jfs_ci_compare,
+       .d_revalidate = jfs_ci_revalidate,
 };
index 0669fc1..3150d76 100644 (file)
@@ -115,6 +115,14 @@ static struct inode *jfs_alloc_inode(struct super_block *sb)
        return &jfs_inode->vfs_inode;
 }
 
+static void jfs_i_callback(struct rcu_head *head)
+{
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       struct jfs_inode_info *ji = JFS_IP(inode);
+       INIT_LIST_HEAD(&inode->i_dentry);
+       kmem_cache_free(jfs_inode_cachep, ji);
+}
+
 static void jfs_destroy_inode(struct inode *inode)
 {
        struct jfs_inode_info *ji = JFS_IP(inode);
@@ -128,7 +136,7 @@ static void jfs_destroy_inode(struct inode *inode)
                ji->active_ag = -1;
        }
        spin_unlock_irq(&ji->ag_lock);
-       kmem_cache_free(jfs_inode_cachep, ji);
+       call_rcu(&inode->i_rcu, jfs_i_callback);
 }
 
 static int jfs_statfs(struct dentry *dentry, struct kstatfs *buf)
@@ -517,7 +525,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
                goto out_no_root;
 
        if (sbi->mntflag & JFS_OS2)
-               sb->s_root->d_op = &jfs_ci_dentry_operations;
+               d_set_d_op(sb->s_root, &jfs_ci_dentry_operations);
 
        /* logical blocks are represented by 40 bits in pxd_t, etc. */
        sb->s_maxbytes = ((u64) sb->s_blocksize) << 40;
index a3accdf..889311e 100644 (file)
 
 #include <asm/uaccess.h>
 
+static inline int simple_positive(struct dentry *dentry)
+{
+       return dentry->d_inode && !d_unhashed(dentry);
+}
+
 int simple_getattr(struct vfsmount *mnt, struct dentry *dentry,
                   struct kstat *stat)
 {
@@ -37,7 +42,7 @@ int simple_statfs(struct dentry *dentry, struct kstatfs *buf)
  * Retaining negative dentries for an in-memory filesystem just wastes
  * memory and lookup time: arrange for them to be deleted immediately.
  */
-static int simple_delete_dentry(struct dentry *dentry)
+static int simple_delete_dentry(const struct dentry *dentry)
 {
        return 1;
 }
@@ -54,7 +59,7 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, struct na
 
        if (dentry->d_name.len > NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
-       dentry->d_op = &simple_dentry_operations;
+       d_set_d_op(dentry, &simple_dentry_operations);
        d_add(dentry, NULL);
        return NULL;
 }
@@ -76,7 +81,8 @@ int dcache_dir_close(struct inode *inode, struct file *file)
 
 loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
 {
-       mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
+       struct dentry *dentry = file->f_path.dentry;
+       mutex_lock(&dentry->d_inode->i_mutex);
        switch (origin) {
                case 1:
                        offset += file->f_pos;
@@ -84,7 +90,7 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
                        if (offset >= 0)
                                break;
                default:
-                       mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
+                       mutex_unlock(&dentry->d_inode->i_mutex);
                        return -EINVAL;
        }
        if (offset != file->f_pos) {
@@ -94,21 +100,24 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
                        struct dentry *cursor = file->private_data;
                        loff_t n = file->f_pos - 2;
 
-                       spin_lock(&dcache_lock);
+                       spin_lock(&dentry->d_lock);
+                       /* d_lock not required for cursor */
                        list_del(&cursor->d_u.d_child);
-                       p = file->f_path.dentry->d_subdirs.next;
-                       while (n && p != &file->f_path.dentry->d_subdirs) {
+                       p = dentry->d_subdirs.next;
+                       while (n && p != &dentry->d_subdirs) {
                                struct dentry *next;
                                next = list_entry(p, struct dentry, d_u.d_child);
-                               if (!d_unhashed(next) && next->d_inode)
+                               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
+                               if (simple_positive(next))
                                        n--;
+                               spin_unlock(&next->d_lock);
                                p = p->next;
                        }
                        list_add_tail(&cursor->d_u.d_child, p);
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&dentry->d_lock);
                }
        }
-       mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
+       mutex_unlock(&dentry->d_inode->i_mutex);
        return offset;
 }
 
@@ -148,29 +157,35 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
                        i++;
                        /* fallthrough */
                default:
-                       spin_lock(&dcache_lock);
+                       spin_lock(&dentry->d_lock);
                        if (filp->f_pos == 2)
                                list_move(q, &dentry->d_subdirs);
 
                        for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
                                struct dentry *next;
                                next = list_entry(p, struct dentry, d_u.d_child);
-                               if (d_unhashed(next) || !next->d_inode)
+                               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
+                               if (!simple_positive(next)) {
+                                       spin_unlock(&next->d_lock);
                                        continue;
+                               }
 
-                               spin_unlock(&dcache_lock);
+                               spin_unlock(&next->d_lock);
+                               spin_unlock(&dentry->d_lock);
                                if (filldir(dirent, next->d_name.name, 
                                            next->d_name.len, filp->f_pos, 
                                            next->d_inode->i_ino, 
                                            dt_type(next->d_inode)) < 0)
                                        return 0;
-                               spin_lock(&dcache_lock);
+                               spin_lock(&dentry->d_lock);
+                               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
                                /* next is still alive */
                                list_move(q, p);
+                               spin_unlock(&next->d_lock);
                                p = q;
                                filp->f_pos++;
                        }
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&dentry->d_lock);
        }
        return 0;
 }
@@ -259,23 +274,23 @@ int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *den
        return 0;
 }
 
-static inline int simple_positive(struct dentry *dentry)
-{
-       return dentry->d_inode && !d_unhashed(dentry);
-}
-
 int simple_empty(struct dentry *dentry)
 {
        struct dentry *child;
        int ret = 0;
 
-       spin_lock(&dcache_lock);
-       list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child)
-               if (simple_positive(child))
+       spin_lock(&dentry->d_lock);
+       list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
+               spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
+               if (simple_positive(child)) {
+                       spin_unlock(&child->d_lock);
                        goto out;
+               }
+               spin_unlock(&child->d_lock);
+       }
        ret = 1;
 out:
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
        return ret;
 }
 
index 8729347..08415b2 100644 (file)
@@ -1389,7 +1389,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
                if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
                        goto out;
                if ((arg == F_WRLCK)
-                   && ((atomic_read(&dentry->d_count) > 1)
+                   && ((dentry->d_count > 1)
                        || (atomic_read(&inode->i_count) > 1)))
                        goto out;
        }
index 409dfd6..f9ddf0c 100644 (file)
@@ -555,9 +555,11 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,
        return __logfs_create(dir, dentry, inode, target, destlen);
 }
 
-static int logfs_permission(struct inode *inode, int mask)
+static int logfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       return generic_permission(inode, mask, NULL);
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+       return generic_permission(inode, mask, flags, NULL);
 }
 
 static int logfs_link(struct dentry *old_dentry, struct inode *dir,
index d8c71ec..03b8c24 100644 (file)
@@ -141,13 +141,20 @@ struct inode *logfs_safe_iget(struct super_block *sb, ino_t ino, int *is_cached)
        return __logfs_iget(sb, ino);
 }
 
+static void logfs_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(logfs_inode_cache, logfs_inode(inode));
+}
+
 static void __logfs_destroy_inode(struct inode *inode)
 {
        struct logfs_inode *li = logfs_inode(inode);
 
        BUG_ON(li->li_block);
        list_del(&li->li_freeing_list);
-       kmem_cache_free(logfs_inode_cache, li);
+       call_rcu(&inode->i_rcu, logfs_i_callback);
 }
 
 static void logfs_destroy_inode(struct inode *inode)
index fb20208..ae0b83f 100644 (file)
@@ -68,11 +68,18 @@ static struct inode *minix_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void minix_destroy_inode(struct inode *inode)
+static void minix_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(minix_inode_cachep, minix_i(inode));
 }
 
+static void minix_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, minix_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct minix_inode_info *ei = (struct minix_inode_info *) foo;
index c0d35a3..1b9e077 100644 (file)
@@ -23,7 +23,7 @@ static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry, st
        struct inode * inode = NULL;
        ino_t ino;
 
-       dentry->d_op = dir->i_sb->s_root->d_op;
+       d_set_d_op(dentry, dir->i_sb->s_root->d_op);
 
        if (dentry->d_name.len > minix_sb(dir->i_sb)->s_namelen)
                return ERR_PTR(-ENAMETOOLONG);
index 4ff7ca5..19433cd 100644 (file)
@@ -169,8 +169,8 @@ EXPORT_SYMBOL(putname);
 /*
  * This does basic POSIX ACL permission checking
  */
-static int acl_permission_check(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask))
+static int acl_permission_check(struct inode *inode, int mask, unsigned int flags,
+               int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
 {
        umode_t                 mode = inode->i_mode;
 
@@ -180,7 +180,7 @@ static int acl_permission_check(struct inode *inode, int mask,
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
-                       int error = check_acl(inode, mask);
+                       int error = check_acl(inode, mask, flags);
                        if (error != -EAGAIN)
                                return error;
                }
@@ -198,25 +198,30 @@ static int acl_permission_check(struct inode *inode, int mask,
 }
 
 /**
- * generic_permission  -  check for access rights on a Posix-like filesystem
+ * generic_permission -  check for access rights on a Posix-like filesystem
  * @inode:     inode to check access rights for
  * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  * @check_acl: optional callback to check for Posix ACLs
+ * @flags      IPERM_FLAG_ flags.
  *
  * Used to check for read/write/execute permissions on a file.
  * We use "fsuid" for this, letting us set arbitrary permissions
  * for filesystem access without changing the "normal" uids which
- * are used for other things..
+ * are used for other things.
+ *
+ * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk
+ * request cannot be satisfied (eg. requires blocking or too much complexity).
+ * It would then be called again in ref-walk mode.
  */
-int generic_permission(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask))
+int generic_permission(struct inode *inode, int mask, unsigned int flags,
+       int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
 {
        int ret;
 
        /*
         * Do the basic POSIX ACL permission checks.
         */
-       ret = acl_permission_check(inode, mask, check_acl);
+       ret = acl_permission_check(inode, mask, flags, check_acl);
        if (ret != -EACCES)
                return ret;
 
@@ -271,9 +276,10 @@ int inode_permission(struct inode *inode, int mask)
        }
 
        if (inode->i_op->permission)
-               retval = inode->i_op->permission(inode, mask);
+               retval = inode->i_op->permission(inode, mask, 0);
        else
-               retval = generic_permission(inode, mask, inode->i_op->check_acl);
+               retval = generic_permission(inode, mask, 0,
+                               inode->i_op->check_acl);
 
        if (retval)
                return retval;
@@ -362,6 +368,18 @@ void path_get(struct path *path)
 EXPORT_SYMBOL(path_get);
 
 /**
+ * path_get_long - get a long reference to a path
+ * @path: path to get the reference to
+ *
+ * Given a path increment the reference count to the dentry and the vfsmount.
+ */
+void path_get_long(struct path *path)
+{
+       mntget_long(path->mnt);
+       dget(path->dentry);
+}
+
+/**
  * path_put - put a reference to a path
  * @path: path to put the reference to
  *
@@ -375,6 +393,185 @@ void path_put(struct path *path)
 EXPORT_SYMBOL(path_put);
 
 /**
+ * path_put_long - put a long reference to a path
+ * @path: path to put the reference to
+ *
+ * Given a path decrement the reference count to the dentry and the vfsmount.
+ */
+void path_put_long(struct path *path)
+{
+       dput(path->dentry);
+       mntput_long(path->mnt);
+}
+
+/**
+ * nameidata_drop_rcu - drop this nameidata out of rcu-walk
+ * @nd: nameidata pathwalk data to drop
+ * @Returns: 0 on success, -ECHLID on failure
+ *
+ * Path walking has 2 modes, rcu-walk and ref-walk (see
+ * Documentation/filesystems/path-lookup.txt). __drop_rcu* functions attempt
+ * to drop out of rcu-walk mode and take normal reference counts on dentries
+ * and vfsmounts to transition to rcu-walk mode. __drop_rcu* functions take
+ * refcounts at the last known good point before rcu-walk got stuck, so
+ * ref-walk may continue from there. If this is not successful (eg. a seqcount
+ * has changed), then failure is returned and path walk restarts from the
+ * beginning in ref-walk mode.
+ *
+ * nameidata_drop_rcu attempts to drop the current nd->path and nd->root into
+ * ref-walk. Must be called from rcu-walk context.
+ */
+static int nameidata_drop_rcu(struct nameidata *nd)
+{
+       struct fs_struct *fs = current->fs;
+       struct dentry *dentry = nd->path.dentry;
+
+       BUG_ON(!(nd->flags & LOOKUP_RCU));
+       if (nd->root.mnt) {
+               spin_lock(&fs->lock);
+               if (nd->root.mnt != fs->root.mnt ||
+                               nd->root.dentry != fs->root.dentry)
+                       goto err_root;
+       }
+       spin_lock(&dentry->d_lock);
+       if (!__d_rcu_to_refcount(dentry, nd->seq))
+               goto err;
+       BUG_ON(nd->inode != dentry->d_inode);
+       spin_unlock(&dentry->d_lock);
+       if (nd->root.mnt) {
+               path_get(&nd->root);
+               spin_unlock(&fs->lock);
+       }
+       mntget(nd->path.mnt);
+
+       rcu_read_unlock();
+       br_read_unlock(vfsmount_lock);
+       nd->flags &= ~LOOKUP_RCU;
+       return 0;
+err:
+       spin_unlock(&dentry->d_lock);
+err_root:
+       if (nd->root.mnt)
+               spin_unlock(&fs->lock);
+       return -ECHILD;
+}
+
+/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing.  */
+static inline int nameidata_drop_rcu_maybe(struct nameidata *nd)
+{
+       if (nd->flags & LOOKUP_RCU)
+               return nameidata_drop_rcu(nd);
+       return 0;
+}
+
+/**
+ * nameidata_dentry_drop_rcu - drop nameidata and dentry out of rcu-walk
+ * @nd: nameidata pathwalk data to drop
+ * @dentry: dentry to drop
+ * @Returns: 0 on success, -ECHLID on failure
+ *
+ * nameidata_dentry_drop_rcu attempts to drop the current nd->path and nd->root,
+ * and dentry into ref-walk. @dentry must be a path found by a do_lookup call on
+ * @nd. Must be called from rcu-walk context.
+ */
+static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry)
+{
+       struct fs_struct *fs = current->fs;
+       struct dentry *parent = nd->path.dentry;
+
+       BUG_ON(!(nd->flags & LOOKUP_RCU));
+       if (nd->root.mnt) {
+               spin_lock(&fs->lock);
+               if (nd->root.mnt != fs->root.mnt ||
+                               nd->root.dentry != fs->root.dentry)
+                       goto err_root;
+       }
+       spin_lock(&parent->d_lock);
+       spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+       if (!__d_rcu_to_refcount(dentry, nd->seq))
+               goto err;
+       /*
+        * If the sequence check on the child dentry passed, then the child has
+        * not been removed from its parent. This means the parent dentry must
+        * be valid and able to take a reference at this point.
+        */
+       BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
+       BUG_ON(!parent->d_count);
+       parent->d_count++;
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&parent->d_lock);
+       if (nd->root.mnt) {
+               path_get(&nd->root);
+               spin_unlock(&fs->lock);
+       }
+       mntget(nd->path.mnt);
+
+       rcu_read_unlock();
+       br_read_unlock(vfsmount_lock);
+       nd->flags &= ~LOOKUP_RCU;
+       return 0;
+err:
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&parent->d_lock);
+err_root:
+       if (nd->root.mnt)
+               spin_unlock(&fs->lock);
+       return -ECHILD;
+}
+
+/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing.  */
+static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct dentry *dentry)
+{
+       if (nd->flags & LOOKUP_RCU)
+               return nameidata_dentry_drop_rcu(nd, dentry);
+       return 0;
+}
+
+/**
+ * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk
+ * @nd: nameidata pathwalk data to drop
+ * @Returns: 0 on success, -ECHLID on failure
+ *
+ * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk.
+ * nd->path should be the final element of the lookup, so nd->root is discarded.
+ * Must be called from rcu-walk context.
+ */
+static int nameidata_drop_rcu_last(struct nameidata *nd)
+{
+       struct dentry *dentry = nd->path.dentry;
+
+       BUG_ON(!(nd->flags & LOOKUP_RCU));
+       nd->flags &= ~LOOKUP_RCU;
+       nd->root.mnt = NULL;
+       spin_lock(&dentry->d_lock);
+       if (!__d_rcu_to_refcount(dentry, nd->seq))
+               goto err_unlock;
+       BUG_ON(nd->inode != dentry->d_inode);
+       spin_unlock(&dentry->d_lock);
+
+       mntget(nd->path.mnt);
+
+       rcu_read_unlock();
+       br_read_unlock(vfsmount_lock);
+
+       return 0;
+
+err_unlock:
+       spin_unlock(&dentry->d_lock);
+       rcu_read_unlock();
+       br_read_unlock(vfsmount_lock);
+       return -ECHILD;
+}
+
+/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing.  */
+static inline int nameidata_drop_rcu_last_maybe(struct nameidata *nd)
+{
+       if (likely(nd->flags & LOOKUP_RCU))
+               return nameidata_drop_rcu_last(nd);
+       return 0;
+}
+
+/**
  * release_open_intent - free up open intent resources
  * @nd: pointer to nameidata
  */
@@ -386,10 +583,26 @@ void release_open_intent(struct nameidata *nd)
                fput(nd->intent.open.file);
 }
 
+static int d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+       int status;
+
+       status = dentry->d_op->d_revalidate(dentry, nd);
+       if (status == -ECHILD) {
+               if (nameidata_dentry_drop_rcu(nd, dentry))
+                       return status;
+               status = dentry->d_op->d_revalidate(dentry, nd);
+       }
+
+       return status;
+}
+
 static inline struct dentry *
 do_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       int status = dentry->d_op->d_revalidate(dentry, nd);
+       int status;
+
+       status = d_revalidate(dentry, nd);
        if (unlikely(status <= 0)) {
                /*
                 * The dentry failed validation.
@@ -397,19 +610,36 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
                 * the dentry otherwise d_revalidate is asking us
                 * to return a fail status.
                 */
-               if (!status) {
+               if (status < 0) {
+                       /* If we're in rcu-walk, we don't have a ref */
+                       if (!(nd->flags & LOOKUP_RCU))
+                               dput(dentry);
+                       dentry = ERR_PTR(status);
+
+               } else {
+                       /* Don't d_invalidate in rcu-walk mode */
+                       if (nameidata_dentry_drop_rcu_maybe(nd, dentry))
+                               return ERR_PTR(-ECHILD);
                        if (!d_invalidate(dentry)) {
                                dput(dentry);
                                dentry = NULL;
                        }
-               } else {
-                       dput(dentry);
-                       dentry = ERR_PTR(status);
                }
        }
        return dentry;
 }
 
+static inline int need_reval_dot(struct dentry *dentry)
+{
+       if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE)))
+               return 0;
+
+       if (likely(!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)))
+               return 0;
+
+       return 1;
+}
+
 /*
  * force_reval_path - force revalidation of a dentry
  *
@@ -433,13 +663,12 @@ force_reval_path(struct path *path, struct nameidata *nd)
 
        /*
         * only check on filesystems where it's possible for the dentry to
-        * become stale. It's assumed that if this flag is set then the
-        * d_revalidate op will also be defined.
+        * become stale.
         */
-       if (!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT))
+       if (!need_reval_dot(dentry))
                return 0;
 
-       status = dentry->d_op->d_revalidate(dentry, nd);
+       status = d_revalidate(dentry, nd);
        if (status > 0)
                return 0;
 
@@ -459,26 +688,27 @@ force_reval_path(struct path *path, struct nameidata *nd)
  * short-cut DAC fails, then call ->permission() to do more
  * complete permission check.
  */
-static int exec_permission(struct inode *inode)
+static inline int exec_permission(struct inode *inode, unsigned int flags)
 {
        int ret;
 
        if (inode->i_op->permission) {
-               ret = inode->i_op->permission(inode, MAY_EXEC);
-               if (!ret)
-                       goto ok;
-               return ret;
+               ret = inode->i_op->permission(inode, MAY_EXEC, flags);
+       } else {
+               ret = acl_permission_check(inode, MAY_EXEC, flags,
+                               inode->i_op->check_acl);
        }
-       ret = acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl);
-       if (!ret)
+       if (likely(!ret))
                goto ok;
+       if (ret == -ECHILD)
+               return ret;
 
        if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
                goto ok;
 
        return ret;
 ok:
-       return security_inode_permission(inode, MAY_EXEC);
+       return security_inode_exec_permission(inode, flags);
 }
 
 static __always_inline void set_root(struct nameidata *nd)
@@ -489,8 +719,23 @@ static __always_inline void set_root(struct nameidata *nd)
 
 static int link_path_walk(const char *, struct nameidata *);
 
+static __always_inline void set_root_rcu(struct nameidata *nd)
+{
+       if (!nd->root.mnt) {
+               struct fs_struct *fs = current->fs;
+               unsigned seq;
+
+               do {
+                       seq = read_seqcount_begin(&fs->seq);
+                       nd->root = fs->root;
+               } while (read_seqcount_retry(&fs->seq, seq));
+       }
+}
+
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
+       int ret;
+
        if (IS_ERR(link))
                goto fail;
 
@@ -500,8 +745,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                nd->path = nd->root;
                path_get(&nd->root);
        }
+       nd->inode = nd->path.dentry->d_inode;
 
-       return link_path_walk(link, nd);
+       ret = link_path_walk(link, nd);
+       return ret;
 fail:
        path_put(&nd->path);
        return PTR_ERR(link);
@@ -516,11 +763,12 @@ static void path_put_conditional(struct path *path, struct nameidata *nd)
 
 static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
 {
-       dput(nd->path.dentry);
-       if (nd->path.mnt != path->mnt) {
-               mntput(nd->path.mnt);
-               nd->path.mnt = path->mnt;
+       if (!(nd->flags & LOOKUP_RCU)) {
+               dput(nd->path.dentry);
+               if (nd->path.mnt != path->mnt)
+                       mntput(nd->path.mnt);
        }
+       nd->path.mnt = path->mnt;
        nd->path.dentry = path->dentry;
 }
 
@@ -535,9 +783,11 @@ __do_follow_link(struct path *path, struct nameidata *nd, void **p)
 
        if (path->mnt != nd->path.mnt) {
                path_to_nameidata(path, nd);
+               nd->inode = nd->path.dentry->d_inode;
                dget(dentry);
        }
        mntget(path->mnt);
+
        nd->last_type = LAST_BIND;
        *p = dentry->d_inode->i_op->follow_link(dentry, nd);
        error = PTR_ERR(*p);
@@ -591,6 +841,20 @@ loop:
        return err;
 }
 
+static int follow_up_rcu(struct path *path)
+{
+       struct vfsmount *parent;
+       struct dentry *mountpoint;
+
+       parent = path->mnt->mnt_parent;
+       if (parent == path->mnt)
+               return 0;
+       mountpoint = path->mnt->mnt_mountpoint;
+       path->dentry = mountpoint;
+       path->mnt = parent;
+       return 1;
+}
+
 int follow_up(struct path *path)
 {
        struct vfsmount *parent;
@@ -612,9 +876,24 @@ int follow_up(struct path *path)
        return 1;
 }
 
-/* no need for dcache_lock, as serialization is taken care in
- * namespace.c
+/*
+ * serialization is taken care of in namespace.c
  */
+static void __follow_mount_rcu(struct nameidata *nd, struct path *path,
+                               struct inode **inode)
+{
+       while (d_mountpoint(path->dentry)) {
+               struct vfsmount *mounted;
+               mounted = __lookup_mnt(path->mnt, path->dentry, 1);
+               if (!mounted)
+                       return;
+               path->mnt = mounted;
+               path->dentry = mounted->mnt_root;
+               nd->seq = read_seqcount_begin(&path->dentry->d_seq);
+               *inode = path->dentry->d_inode;
+       }
+}
+
 static int __follow_mount(struct path *path)
 {
        int res = 0;
@@ -645,9 +924,6 @@ static void follow_mount(struct path *path)
        }
 }
 
-/* no need for dcache_lock, as serialization is taken care in
- * namespace.c
- */
 int follow_down(struct path *path)
 {
        struct vfsmount *mounted;
@@ -663,7 +939,42 @@ int follow_down(struct path *path)
        return 0;
 }
 
-static __always_inline void follow_dotdot(struct nameidata *nd)
+static int follow_dotdot_rcu(struct nameidata *nd)
+{
+       struct inode *inode = nd->inode;
+
+       set_root_rcu(nd);
+
+       while(1) {
+               if (nd->path.dentry == nd->root.dentry &&
+                   nd->path.mnt == nd->root.mnt) {
+                       break;
+               }
+               if (nd->path.dentry != nd->path.mnt->mnt_root) {
+                       struct dentry *old = nd->path.dentry;
+                       struct dentry *parent = old->d_parent;
+                       unsigned seq;
+
+                       seq = read_seqcount_begin(&parent->d_seq);
+                       if (read_seqcount_retry(&old->d_seq, nd->seq))
+                               return -ECHILD;
+                       inode = parent->d_inode;
+                       nd->path.dentry = parent;
+                       nd->seq = seq;
+                       break;
+               }
+               if (!follow_up_rcu(&nd->path))
+                       break;
+               nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
+               inode = nd->path.dentry->d_inode;
+       }
+       __follow_mount_rcu(nd, &nd->path, &inode);
+       nd->inode = inode;
+
+       return 0;
+}
+
+static void follow_dotdot(struct nameidata *nd)
 {
        set_root(nd);
 
@@ -684,6 +995,7 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
                        break;
        }
        follow_mount(&nd->path);
+       nd->inode = nd->path.dentry->d_inode;
 }
 
 /*
@@ -721,17 +1033,17 @@ static struct dentry *d_alloc_and_lookup(struct dentry *parent,
  *  It _is_ time-critical.
  */
 static int do_lookup(struct nameidata *nd, struct qstr *name,
-                    struct path *path)
+                       struct path *path, struct inode **inode)
 {
        struct vfsmount *mnt = nd->path.mnt;
-       struct dentry *dentry, *parent;
+       struct dentry *dentry, *parent = nd->path.dentry;
        struct inode *dir;
        /*
         * See if the low-level filesystem might want
         * to use its own hash..
         */
-       if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
-               int err = nd->path.dentry->d_op->d_hash(nd->path.dentry, name);
+       if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
+               int err = parent->d_op->d_hash(parent, nd->inode, name);
                if (err < 0)
                        return err;
        }
@@ -741,21 +1053,44 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
         * of a false negative due to a concurrent rename, we're going to
         * do the non-racy lookup, below.
         */
-       dentry = __d_lookup(nd->path.dentry, name);
-       if (!dentry)
-               goto need_lookup;
+       if (nd->flags & LOOKUP_RCU) {
+               unsigned seq;
+
+               *inode = nd->inode;
+               dentry = __d_lookup_rcu(parent, name, &seq, inode);
+               if (!dentry) {
+                       if (nameidata_drop_rcu(nd))
+                               return -ECHILD;
+                       goto need_lookup;
+               }
+               /* Memory barrier in read_seqcount_begin of child is enough */
+               if (__read_seqcount_retry(&parent->d_seq, nd->seq))
+                       return -ECHILD;
+
+               nd->seq = seq;
+               if (dentry->d_flags & DCACHE_OP_REVALIDATE)
+                       goto need_revalidate;
+               path->mnt = mnt;
+               path->dentry = dentry;
+               __follow_mount_rcu(nd, path, inode);
+       } else {
+               dentry = __d_lookup(parent, name);
+               if (!dentry)
+                       goto need_lookup;
 found:
-       if (dentry->d_op && dentry->d_op->d_revalidate)
-               goto need_revalidate;
+               if (dentry->d_flags & DCACHE_OP_REVALIDATE)
+                       goto need_revalidate;
 done:
-       path->mnt = mnt;
-       path->dentry = dentry;
-       __follow_mount(path);
+               path->mnt = mnt;
+               path->dentry = dentry;
+               __follow_mount(path);
+               *inode = path->dentry->d_inode;
+       }
        return 0;
 
 need_lookup:
-       parent = nd->path.dentry;
        dir = parent->d_inode;
+       BUG_ON(nd->inode != dir);
 
        mutex_lock(&dir->i_mutex);
        /*
@@ -817,7 +1152,6 @@ static inline int follow_on_final(struct inode *inode, unsigned lookup_flags)
 static int link_path_walk(const char *name, struct nameidata *nd)
 {
        struct path next;
-       struct inode *inode;
        int err;
        unsigned int lookup_flags = nd->flags;
        
@@ -826,18 +1160,28 @@ static int link_path_walk(const char *name, struct nameidata *nd)
        if (!*name)
                goto return_reval;
 
-       inode = nd->path.dentry->d_inode;
        if (nd->depth)
                lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
 
        /* At this point we know we have a real path component. */
        for(;;) {
+               struct inode *inode;
                unsigned long hash;
                struct qstr this;
                unsigned int c;
 
                nd->flags |= LOOKUP_CONTINUE;
-               err = exec_permission(inode);
+               if (nd->flags & LOOKUP_RCU) {
+                       err = exec_permission(nd->inode, IPERM_FLAG_RCU);
+                       if (err == -ECHILD) {
+                               if (nameidata_drop_rcu(nd))
+                                       return -ECHILD;
+                               goto exec_again;
+                       }
+               } else {
+exec_again:
+                       err = exec_permission(nd->inode, 0);
+               }
                if (err)
                        break;
 
@@ -868,37 +1212,44 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                if (this.name[0] == '.') switch (this.len) {
                        default:
                                break;
-                       case 2: 
+                       case 2:
                                if (this.name[1] != '.')
                                        break;
-                               follow_dotdot(nd);
-                               inode = nd->path.dentry->d_inode;
+                               if (nd->flags & LOOKUP_RCU) {
+                                       if (follow_dotdot_rcu(nd))
+                                               return -ECHILD;
+                               } else
+                                       follow_dotdot(nd);
                                /* fallthrough */
                        case 1:
                                continue;
                }
                /* This does the actual lookups.. */
-               err = do_lookup(nd, &this, &next);
+               err = do_lookup(nd, &this, &next, &inode);
                if (err)
                        break;
-
                err = -ENOENT;
-               inode = next.dentry->d_inode;
                if (!inode)
                        goto out_dput;
 
                if (inode->i_op->follow_link) {
+                       /* We commonly drop rcu-walk here */
+                       if (nameidata_dentry_drop_rcu_maybe(nd, next.dentry))
+                               return -ECHILD;
+                       BUG_ON(inode != next.dentry->d_inode);
                        err = do_follow_link(&next, nd);
                        if (err)
                                goto return_err;
+                       nd->inode = nd->path.dentry->d_inode;
                        err = -ENOENT;
-                       inode = nd->path.dentry->d_inode;
-                       if (!inode)
+                       if (!nd->inode)
                                break;
-               } else
+               } else {
                        path_to_nameidata(&next, nd);
+                       nd->inode = inode;
+               }
                err = -ENOTDIR; 
-               if (!inode->i_op->lookup)
+               if (!nd->inode->i_op->lookup)
                        break;
                continue;
                /* here ends the main loop */
@@ -913,32 +1264,39 @@ last_component:
                if (this.name[0] == '.') switch (this.len) {
                        default:
                                break;
-                       case 2: 
+                       case 2:
                                if (this.name[1] != '.')
                                        break;
-                               follow_dotdot(nd);
-                               inode = nd->path.dentry->d_inode;
+                               if (nd->flags & LOOKUP_RCU) {
+                                       if (follow_dotdot_rcu(nd))
+                                               return -ECHILD;
+                               } else
+                                       follow_dotdot(nd);
                                /* fallthrough */
                        case 1:
                                goto return_reval;
                }
-               err = do_lookup(nd, &this, &next);
+               err = do_lookup(nd, &this, &next, &inode);
                if (err)
                        break;
-               inode = next.dentry->d_inode;
                if (follow_on_final(inode, lookup_flags)) {
+                       if (nameidata_dentry_drop_rcu_maybe(nd, next.dentry))
+                               return -ECHILD;
+                       BUG_ON(inode != next.dentry->d_inode);
                        err = do_follow_link(&next, nd);
                        if (err)
                                goto return_err;
-                       inode = nd->path.dentry->d_inode;
-               } else
+                       nd->inode = nd->path.dentry->d_inode;
+               } else {
                        path_to_nameidata(&next, nd);
+                       nd->inode = inode;
+               }
                err = -ENOENT;
-               if (!inode)
+               if (!nd->inode)
                        break;
                if (lookup_flags & LOOKUP_DIRECTORY) {
                        err = -ENOTDIR; 
-                       if (!inode->i_op->lookup)
+                       if (!nd->inode->i_op->lookup)
                                break;
                }
                goto return_base;
@@ -958,25 +1316,43 @@ return_reval:
                 * We bypassed the ordinary revalidation routines.
                 * We may need to check the cached dentry for staleness.
                 */
-               if (nd->path.dentry && nd->path.dentry->d_sb &&
-                   (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
-                       err = -ESTALE;
+               if (need_reval_dot(nd->path.dentry)) {
                        /* Note: we do not d_invalidate() */
-                       if (!nd->path.dentry->d_op->d_revalidate(
-                                       nd->path.dentry, nd))
+                       err = d_revalidate(nd->path.dentry, nd);
+                       if (!err)
+                               err = -ESTALE;
+                       if (err < 0)
                                break;
                }
 return_base:
+               if (nameidata_drop_rcu_last_maybe(nd))
+                       return -ECHILD;
                return 0;
 out_dput:
-               path_put_conditional(&next, nd);
+               if (!(nd->flags & LOOKUP_RCU))
+                       path_put_conditional(&next, nd);
                break;
        }
-       path_put(&nd->path);
+       if (!(nd->flags & LOOKUP_RCU))
+               path_put(&nd->path);
 return_err:
        return err;
 }
 
+static inline int path_walk_rcu(const char *name, struct nameidata *nd)
+{
+       current->total_link_count = 0;
+
+       return link_path_walk(name, nd);
+}
+
+static inline int path_walk_simple(const char *name, struct nameidata *nd)
+{
+       current->total_link_count = 0;
+
+       return link_path_walk(name, nd);
+}
+
 static int path_walk(const char *name, struct nameidata *nd)
 {
        struct path save = nd->path;
@@ -1002,6 +1378,93 @@ static int path_walk(const char *name, struct nameidata *nd)
        return result;
 }
 
+static void path_finish_rcu(struct nameidata *nd)
+{
+       if (nd->flags & LOOKUP_RCU) {
+               /* RCU dangling. Cancel it. */
+               nd->flags &= ~LOOKUP_RCU;
+               nd->root.mnt = NULL;
+               rcu_read_unlock();
+               br_read_unlock(vfsmount_lock);
+       }
+       if (nd->file)
+               fput(nd->file);
+}
+
+static int path_init_rcu(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
+{
+       int retval = 0;
+       int fput_needed;
+       struct file *file;
+
+       nd->last_type = LAST_ROOT; /* if there are only slashes... */
+       nd->flags = flags | LOOKUP_RCU;
+       nd->depth = 0;
+       nd->root.mnt = NULL;
+       nd->file = NULL;
+
+       if (*name=='/') {
+               struct fs_struct *fs = current->fs;
+               unsigned seq;
+
+               br_read_lock(vfsmount_lock);
+               rcu_read_lock();
+
+               do {
+                       seq = read_seqcount_begin(&fs->seq);
+                       nd->root = fs->root;
+                       nd->path = nd->root;
+                       nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+               } while (read_seqcount_retry(&fs->seq, seq));
+
+       } else if (dfd == AT_FDCWD) {
+               struct fs_struct *fs = current->fs;
+               unsigned seq;
+
+               br_read_lock(vfsmount_lock);
+               rcu_read_lock();
+
+               do {
+                       seq = read_seqcount_begin(&fs->seq);
+                       nd->path = fs->pwd;
+                       nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+               } while (read_seqcount_retry(&fs->seq, seq));
+
+       } else {
+               struct dentry *dentry;
+
+               file = fget_light(dfd, &fput_needed);
+               retval = -EBADF;
+               if (!file)
+                       goto out_fail;
+
+               dentry = file->f_path.dentry;
+
+               retval = -ENOTDIR;
+               if (!S_ISDIR(dentry->d_inode->i_mode))
+                       goto fput_fail;
+
+               retval = file_permission(file, MAY_EXEC);
+               if (retval)
+                       goto fput_fail;
+
+               nd->path = file->f_path;
+               if (fput_needed)
+                       nd->file = file;
+
+               nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+               br_read_lock(vfsmount_lock);
+               rcu_read_lock();
+       }
+       nd->inode = nd->path.dentry->d_inode;
+       return 0;
+
+fput_fail:
+       fput_light(file, fput_needed);
+out_fail:
+       return retval;
+}
+
 static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
 {
        int retval = 0;
@@ -1042,6 +1505,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei
 
                fput_light(file, fput_needed);
        }
+       nd->inode = nd->path.dentry->d_inode;
        return 0;
 
 fput_fail:
@@ -1054,16 +1518,53 @@ out_fail:
 static int do_path_lookup(int dfd, const char *name,
                                unsigned int flags, struct nameidata *nd)
 {
-       int retval = path_init(dfd, name, flags, nd);
-       if (!retval)
-               retval = path_walk(name, nd);
-       if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
-                               nd->path.dentry->d_inode))
-               audit_inode(name, nd->path.dentry);
+       int retval;
+
+       /*
+        * Path walking is largely split up into 2 different synchronisation
+        * schemes, rcu-walk and ref-walk (explained in
+        * Documentation/filesystems/path-lookup.txt). These share much of the
+        * path walk code, but some things particularly setup, cleanup, and
+        * following mounts are sufficiently divergent that functions are
+        * duplicated. Typically there is a function foo(), and its RCU
+        * analogue, foo_rcu().
+        *
+        * -ECHILD is the error number of choice (just to avoid clashes) that
+        * is returned if some aspect of an rcu-walk fails. Such an error must
+        * be handled by restarting a traditional ref-walk (which will always
+        * be able to complete).
+        */
+       retval = path_init_rcu(dfd, name, flags, nd);
+       if (unlikely(retval))
+               return retval;
+       retval = path_walk_rcu(name, nd);
+       path_finish_rcu(nd);
        if (nd->root.mnt) {
                path_put(&nd->root);
                nd->root.mnt = NULL;
        }
+
+       if (unlikely(retval == -ECHILD || retval == -ESTALE)) {
+               /* slower, locked walk */
+               if (retval == -ESTALE)
+                       flags |= LOOKUP_REVAL;
+               retval = path_init(dfd, name, flags, nd);
+               if (unlikely(retval))
+                       return retval;
+               retval = path_walk(name, nd);
+               if (nd->root.mnt) {
+                       path_put(&nd->root);
+                       nd->root.mnt = NULL;
+               }
+       }
+
+       if (likely(!retval)) {
+               if (unlikely(!audit_dummy_context())) {
+                       if (nd->path.dentry && nd->inode)
+                               audit_inode(name, nd->path.dentry);
+               }
+       }
+
        return retval;
 }
 
@@ -1106,10 +1607,11 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
        path_get(&nd->path);
        nd->root = nd->path;
        path_get(&nd->root);
+       nd->inode = nd->path.dentry->d_inode;
 
        retval = path_walk(name, nd);
        if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
-                               nd->path.dentry->d_inode))
+                               nd->inode))
                audit_inode(name, nd->path.dentry);
 
        path_put(&nd->root);
@@ -1125,7 +1627,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
        struct dentry *dentry;
        int err;
 
-       err = exec_permission(inode);
+       err = exec_permission(inode, 0);
        if (err)
                return ERR_PTR(err);
 
@@ -1133,8 +1635,8 @@ static struct dentry *__lookup_hash(struct qstr *name,
         * See if the low-level filesystem might want
         * to use its own hash..
         */
-       if (base->d_op && base->d_op->d_hash) {
-               err = base->d_op->d_hash(base, name);
+       if (base->d_flags & DCACHE_OP_HASH) {
+               err = base->d_op->d_hash(base, inode, name);
                dentry = ERR_PTR(err);
                if (err < 0)
                        goto out;
@@ -1147,7 +1649,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
         */
        dentry = d_lookup(base, name);
 
-       if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
+       if (dentry && (dentry->d_flags & DCACHE_OP_REVALIDATE))
                dentry = do_revalidate(dentry, nd);
 
        if (!dentry)
@@ -1490,6 +1992,7 @@ out_unlock:
        mutex_unlock(&dir->d_inode->i_mutex);
        dput(nd->path.dentry);
        nd->path.dentry = path->dentry;
+
        if (error)
                return error;
        /* Don't check for write permission, don't truncate */
@@ -1584,6 +2087,9 @@ exit:
        return ERR_PTR(error);
 }
 
+/*
+ * Handle O_CREAT case for do_filp_open
+ */
 static struct file *do_last(struct nameidata *nd, struct path *path,
                            int open_flag, int acc_mode,
                            int mode, const char *pathname)
@@ -1597,50 +2103,25 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                follow_dotdot(nd);
                dir = nd->path.dentry;
        case LAST_DOT:
-               if (nd->path.mnt->mnt_sb->s_type->fs_flags & FS_REVAL_DOT) {
-                       if (!dir->d_op->d_revalidate(dir, nd)) {
+               if (need_reval_dot(dir)) {
+                       error = d_revalidate(nd->path.dentry, nd);
+                       if (!error)
                                error = -ESTALE;
+                       if (error < 0)
                                goto exit;
-                       }
                }
                /* fallthrough */
        case LAST_ROOT:
-               if (open_flag & O_CREAT)
-                       goto exit;
-               /* fallthrough */
+               goto exit;
        case LAST_BIND:
                audit_inode(pathname, dir);
                goto ok;
        }
 
        /* trailing slashes? */
-       if (nd->last.name[nd->last.len]) {
-               if (open_flag & O_CREAT)
-                       goto exit;
-               nd->flags |= LOOKUP_DIRECTORY | LOOKUP_FOLLOW;
-       }
-
-       /* just plain open? */
-       if (!(open_flag & O_CREAT)) {
-               error = do_lookup(nd, &nd->last, path);
-               if (error)
-                       goto exit;
-               error = -ENOENT;
-               if (!path->dentry->d_inode)
-                       goto exit_dput;
-               if (path->dentry->d_inode->i_op->follow_link)
-                       return NULL;
-               error = -ENOTDIR;
-               if (nd->flags & LOOKUP_DIRECTORY) {
-                       if (!path->dentry->d_inode->i_op->lookup)
-                               goto exit_dput;
-               }
-               path_to_nameidata(path, nd);
-               audit_inode(pathname, nd->path.dentry);
-               goto ok;
-       }
+       if (nd->last.name[nd->last.len])
+               goto exit;
 
-       /* OK, it's O_CREAT */
        mutex_lock(&dir->d_inode->i_mutex);
 
        path->dentry = lookup_hash(nd);
@@ -1711,8 +2192,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                return NULL;
 
        path_to_nameidata(path, nd);
+       nd->inode = path->dentry->d_inode;
        error = -EISDIR;
-       if (S_ISDIR(path->dentry->d_inode->i_mode))
+       if (S_ISDIR(nd->inode->i_mode))
                goto exit;
 ok:
        filp = finish_open(nd, open_flag, acc_mode);
@@ -1743,7 +2225,7 @@ struct file *do_filp_open(int dfd, const char *pathname,
        struct path path;
        int count = 0;
        int flag = open_to_namei_flags(open_flag);
-       int force_reval = 0;
+       int flags;
 
        if (!(open_flag & O_CREAT))
                mode = 0;
@@ -1772,54 +2254,84 @@ struct file *do_filp_open(int dfd, const char *pathname,
        if (open_flag & O_APPEND)
                acc_mode |= MAY_APPEND;
 
-       /* find the parent */
-reval:
-       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
+       flags = LOOKUP_OPEN;
+       if (open_flag & O_CREAT) {
+               flags |= LOOKUP_CREATE;
+               if (open_flag & O_EXCL)
+                       flags |= LOOKUP_EXCL;
+       }
+       if (open_flag & O_DIRECTORY)
+               flags |= LOOKUP_DIRECTORY;
+       if (!(open_flag & O_NOFOLLOW))
+               flags |= LOOKUP_FOLLOW;
+
+       filp = get_empty_filp();
+       if (!filp)
+               return ERR_PTR(-ENFILE);
+
+       filp->f_flags = open_flag;
+       nd.intent.open.file = filp;
+       nd.intent.open.flags = flag;
+       nd.intent.open.create_mode = mode;
+
+       if (open_flag & O_CREAT)
+               goto creat;
+
+       /* !O_CREAT, simple open */
+       error = do_path_lookup(dfd, pathname, flags, &nd);
+       if (unlikely(error))
+               goto out_filp;
+       error = -ELOOP;
+       if (!(nd.flags & LOOKUP_FOLLOW)) {
+               if (nd.inode->i_op->follow_link)
+                       goto out_path;
+       }
+       error = -ENOTDIR;
+       if (nd.flags & LOOKUP_DIRECTORY) {
+               if (!nd.inode->i_op->lookup)
+                       goto out_path;
+       }
+       audit_inode(pathname, nd.path.dentry);
+       filp = finish_open(&nd, open_flag, acc_mode);
+       return filp;
+
+creat:
+       /* OK, have to create the file. Find the parent. */
+       error = path_init_rcu(dfd, pathname,
+                       LOOKUP_PARENT | (flags & LOOKUP_REVAL), &nd);
        if (error)
-               return ERR_PTR(error);
-       if (force_reval)
-               nd.flags |= LOOKUP_REVAL;
+               goto out_filp;
+       error = path_walk_rcu(pathname, &nd);
+       path_finish_rcu(&nd);
+       if (unlikely(error == -ECHILD || error == -ESTALE)) {
+               /* slower, locked walk */
+               if (error == -ESTALE) {
+reval:
+                       flags |= LOOKUP_REVAL;
+               }
+               error = path_init(dfd, pathname,
+                               LOOKUP_PARENT | (flags & LOOKUP_REVAL), &nd);
+               if (error)
+                       goto out_filp;
 
-       current->total_link_count = 0;
-       error = link_path_walk(pathname, &nd);
-       if (error) {
-               filp = ERR_PTR(error);
-               goto out;
+               error = path_walk_simple(pathname, &nd);
        }
-       if (unlikely(!audit_dummy_context()) && (open_flag & O_CREAT))
+       if (unlikely(error))
+               goto out_filp;
+       if (unlikely(!audit_dummy_context()))
                audit_inode(pathname, nd.path.dentry);
 
        /*
         * We have the parent and last component.
         */
-
-       error = -ENFILE;
-       filp = get_empty_filp();
-       if (filp == NULL)
-               goto exit_parent;
-       nd.intent.open.file = filp;
-       filp->f_flags = open_flag;
-       nd.intent.open.flags = flag;
-       nd.intent.open.create_mode = mode;
-       nd.flags &= ~LOOKUP_PARENT;
-       nd.flags |= LOOKUP_OPEN;
-       if (open_flag & O_CREAT) {
-               nd.flags |= LOOKUP_CREATE;
-               if (open_flag & O_EXCL)
-                       nd.flags |= LOOKUP_EXCL;
-       }
-       if (open_flag & O_DIRECTORY)
-               nd.flags |= LOOKUP_DIRECTORY;
-       if (!(open_flag & O_NOFOLLOW))
-               nd.flags |= LOOKUP_FOLLOW;
+       nd.flags = flags;
        filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
        while (unlikely(!filp)) { /* trailing symlink */
                struct path holder;
-               struct inode *inode = path.dentry->d_inode;
                void *cookie;
                error = -ELOOP;
                /* S_ISDIR part is a temporary automount kludge */
-               if (!(nd.flags & LOOKUP_FOLLOW) && !S_ISDIR(inode->i_mode))
+               if (!(nd.flags & LOOKUP_FOLLOW) && !S_ISDIR(nd.inode->i_mode))
                        goto exit_dput;
                if (count++ == 32)
                        goto exit_dput;
@@ -1840,36 +2352,33 @@ reval:
                        goto exit_dput;
                error = __do_follow_link(&path, &nd, &cookie);
                if (unlikely(error)) {
+                       if (!IS_ERR(cookie) && nd.inode->i_op->put_link)
+                               nd.inode->i_op->put_link(path.dentry, &nd, cookie);
                        /* nd.path had been dropped */
-                       if (!IS_ERR(cookie) && inode->i_op->put_link)
-                               inode->i_op->put_link(path.dentry, &nd, cookie);
-                       path_put(&path);
-                       release_open_intent(&nd);
-                       filp = ERR_PTR(error);
-                       goto out;
+                       nd.path = path;
+                       goto out_path;
                }
                holder = path;
                nd.flags &= ~LOOKUP_PARENT;
                filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
-               if (inode->i_op->put_link)
-                       inode->i_op->put_link(holder.dentry, &nd, cookie);
+               if (nd.inode->i_op->put_link)
+                       nd.inode->i_op->put_link(holder.dentry, &nd, cookie);
                path_put(&holder);
        }
 out:
        if (nd.root.mnt)
                path_put(&nd.root);
-       if (filp == ERR_PTR(-ESTALE) && !force_reval) {
-               force_reval = 1;
+       if (filp == ERR_PTR(-ESTALE) && !(flags & LOOKUP_REVAL))
                goto reval;
-       }
        return filp;
 
 exit_dput:
        path_put_conditional(&path, &nd);
+out_path:
+       path_put(&nd.path);
+out_filp:
        if (!IS_ERR(nd.intent.open.file))
                release_open_intent(&nd);
-exit_parent:
-       path_put(&nd.path);
        filp = ERR_PTR(error);
        goto out;
 }
@@ -2130,12 +2639,10 @@ void dentry_unhash(struct dentry *dentry)
 {
        dget(dentry);
        shrink_dcache_parent(dentry);
-       spin_lock(&dcache_lock);
        spin_lock(&dentry->d_lock);
-       if (atomic_read(&dentry->d_count) == 2)
+       if (dentry->d_count == 2)
                __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
 }
 
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
index 3dbfc07..3ddfd90 100644 (file)
@@ -138,6 +138,64 @@ void mnt_release_group_id(struct vfsmount *mnt)
        mnt->mnt_group_id = 0;
 }
 
+/*
+ * vfsmount lock must be held for read
+ */
+static inline void mnt_add_count(struct vfsmount *mnt, int n)
+{
+#ifdef CONFIG_SMP
+       this_cpu_add(mnt->mnt_pcp->mnt_count, n);
+#else
+       preempt_disable();
+       mnt->mnt_count += n;
+       preempt_enable();
+#endif
+}
+
+static inline void mnt_set_count(struct vfsmount *mnt, int n)
+{
+#ifdef CONFIG_SMP
+       this_cpu_write(mnt->mnt_pcp->mnt_count, n);
+#else
+       mnt->mnt_count = n;
+#endif
+}
+
+/*
+ * vfsmount lock must be held for read
+ */
+static inline void mnt_inc_count(struct vfsmount *mnt)
+{
+       mnt_add_count(mnt, 1);
+}
+
+/*
+ * vfsmount lock must be held for read
+ */
+static inline void mnt_dec_count(struct vfsmount *mnt)
+{
+       mnt_add_count(mnt, -1);
+}
+
+/*
+ * vfsmount lock must be held for write
+ */
+unsigned int mnt_get_count(struct vfsmount *mnt)
+{
+#ifdef CONFIG_SMP
+       unsigned int count = atomic_read(&mnt->mnt_longrefs);
+       int cpu;
+
+       for_each_possible_cpu(cpu) {
+               count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
+       }
+
+       return count;
+#else
+       return mnt->mnt_count;
+#endif
+}
+
 struct vfsmount *alloc_vfsmnt(const char *name)
 {
        struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
@@ -154,7 +212,17 @@ struct vfsmount *alloc_vfsmnt(const char *name)
                                goto out_free_id;
                }
 
-               atomic_set(&mnt->mnt_count, 1);
+#ifdef CONFIG_SMP
+               mnt->mnt_pcp = alloc_percpu(struct mnt_pcp);
+               if (!mnt->mnt_pcp)
+                       goto out_free_devname;
+
+               atomic_set(&mnt->mnt_longrefs, 1);
+#else
+               mnt->mnt_count = 1;
+               mnt->mnt_writers = 0;
+#endif
+
                INIT_LIST_HEAD(&mnt->mnt_hash);
                INIT_LIST_HEAD(&mnt->mnt_child);
                INIT_LIST_HEAD(&mnt->mnt_mounts);
@@ -166,13 +234,6 @@ struct vfsmount *alloc_vfsmnt(const char *name)
 #ifdef CONFIG_FSNOTIFY
                INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
 #endif
-#ifdef CONFIG_SMP
-               mnt->mnt_writers = alloc_percpu(int);
-               if (!mnt->mnt_writers)
-                       goto out_free_devname;
-#else
-               mnt->mnt_writers = 0;
-#endif
        }
        return mnt;
 
@@ -216,32 +277,32 @@ int __mnt_is_readonly(struct vfsmount *mnt)
 }
 EXPORT_SYMBOL_GPL(__mnt_is_readonly);
 
-static inline void inc_mnt_writers(struct vfsmount *mnt)
+static inline void mnt_inc_writers(struct vfsmount *mnt)
 {
 #ifdef CONFIG_SMP
-       (*per_cpu_ptr(mnt->mnt_writers, smp_processor_id()))++;
+       this_cpu_inc(mnt->mnt_pcp->mnt_writers);
 #else
        mnt->mnt_writers++;
 #endif
 }
 
-static inline void dec_mnt_writers(struct vfsmount *mnt)
+static inline void mnt_dec_writers(struct vfsmount *mnt)
 {
 #ifdef CONFIG_SMP
-       (*per_cpu_ptr(mnt->mnt_writers, smp_processor_id()))--;
+       this_cpu_dec(mnt->mnt_pcp->mnt_writers);
 #else
        mnt->mnt_writers--;
 #endif
 }
 
-static unsigned int count_mnt_writers(struct vfsmount *mnt)
+static unsigned int mnt_get_writers(struct vfsmount *mnt)
 {
 #ifdef CONFIG_SMP
        unsigned int count = 0;
        int cpu;
 
        for_each_possible_cpu(cpu) {
-               count += *per_cpu_ptr(mnt->mnt_writers, cpu);
+               count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_writers;
        }
 
        return count;
@@ -273,9 +334,9 @@ int mnt_want_write(struct vfsmount *mnt)
        int ret = 0;
 
        preempt_disable();
-       inc_mnt_writers(mnt);
+       mnt_inc_writers(mnt);
        /*
-        * The store to inc_mnt_writers must be visible before we pass
+        * The store to mnt_inc_writers must be visible before we pass
         * MNT_WRITE_HOLD loop below, so that the slowpath can see our
         * incremented count after it has set MNT_WRITE_HOLD.
         */
@@ -289,7 +350,7 @@ int mnt_want_write(struct vfsmount *mnt)
         */
        smp_rmb();
        if (__mnt_is_readonly(mnt)) {
-               dec_mnt_writers(mnt);
+               mnt_dec_writers(mnt);
                ret = -EROFS;
                goto out;
        }
@@ -317,7 +378,7 @@ int mnt_clone_write(struct vfsmount *mnt)
        if (__mnt_is_readonly(mnt))
                return -EROFS;
        preempt_disable();
-       inc_mnt_writers(mnt);
+       mnt_inc_writers(mnt);
        preempt_enable();
        return 0;
 }
@@ -351,7 +412,7 @@ EXPORT_SYMBOL_GPL(mnt_want_write_file);
 void mnt_drop_write(struct vfsmount *mnt)
 {
        preempt_disable();
-       dec_mnt_writers(mnt);
+       mnt_dec_writers(mnt);
        preempt_enable();
 }
 EXPORT_SYMBOL_GPL(mnt_drop_write);
@@ -384,7 +445,7 @@ static int mnt_make_readonly(struct vfsmount *mnt)
         * MNT_WRITE_HOLD, so it can't be decremented by another CPU while
         * we're counting up here.
         */
-       if (count_mnt_writers(mnt) > 0)
+       if (mnt_get_writers(mnt) > 0)
                ret = -EBUSY;
        else
                mnt->mnt_flags |= MNT_READONLY;
@@ -418,7 +479,7 @@ void free_vfsmnt(struct vfsmount *mnt)
        kfree(mnt->mnt_devname);
        mnt_free_id(mnt);
 #ifdef CONFIG_SMP
-       free_percpu(mnt->mnt_writers);
+       free_percpu(mnt->mnt_pcp);
 #endif
        kmem_cache_free(mnt_cache, mnt);
 }
@@ -492,6 +553,27 @@ static void __touch_mnt_namespace(struct mnt_namespace *ns)
 }
 
 /*
+ * Clear dentry's mounted state if it has no remaining mounts.
+ * vfsmount_lock must be held for write.
+ */
+static void dentry_reset_mounted(struct vfsmount *mnt, struct dentry *dentry)
+{
+       unsigned u;
+
+       for (u = 0; u < HASH_SIZE; u++) {
+               struct vfsmount *p;
+
+               list_for_each_entry(p, &mount_hashtable[u], mnt_hash) {
+                       if (p->mnt_mountpoint == dentry)
+                               return;
+               }
+       }
+       spin_lock(&dentry->d_lock);
+       dentry->d_flags &= ~DCACHE_MOUNTED;
+       spin_unlock(&dentry->d_lock);
+}
+
+/*
  * vfsmount lock must be held for write
  */
 static void detach_mnt(struct vfsmount *mnt, struct path *old_path)
@@ -502,7 +584,7 @@ static void detach_mnt(struct vfsmount *mnt, struct path *old_path)
        mnt->mnt_mountpoint = mnt->mnt_root;
        list_del_init(&mnt->mnt_child);
        list_del_init(&mnt->mnt_hash);
-       old_path->dentry->d_mounted--;
+       dentry_reset_mounted(old_path->mnt, old_path->dentry);
 }
 
 /*
@@ -513,7 +595,9 @@ void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
 {
        child_mnt->mnt_parent = mntget(mnt);
        child_mnt->mnt_mountpoint = dget(dentry);
-       dentry->d_mounted++;
+       spin_lock(&dentry->d_lock);
+       dentry->d_flags |= DCACHE_MOUNTED;
+       spin_unlock(&dentry->d_lock);
 }
 
 /*
@@ -629,9 +713,10 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
        return NULL;
 }
 
-static inline void __mntput(struct vfsmount *mnt)
+static inline void mntfree(struct vfsmount *mnt)
 {
        struct super_block *sb = mnt->mnt_sb;
+
        /*
         * This probably indicates that somebody messed
         * up a mnt_want/drop_write() pair.  If this
@@ -639,38 +724,123 @@ static inline void __mntput(struct vfsmount *mnt)
         * to make r/w->r/o transitions.
         */
        /*
-        * atomic_dec_and_lock() used to deal with ->mnt_count decrements
-        * provides barriers, so count_mnt_writers() below is safe.  AV
+        * The locking used to deal with mnt_count decrement provides barriers,
+        * so mnt_get_writers() below is safe.
         */
-       WARN_ON(count_mnt_writers(mnt));
+       WARN_ON(mnt_get_writers(mnt));
        fsnotify_vfsmount_delete(mnt);
        dput(mnt->mnt_root);
        free_vfsmnt(mnt);
        deactivate_super(sb);
 }
 
-void mntput_no_expire(struct vfsmount *mnt)
-{
-repeat:
-       if (atomic_add_unless(&mnt->mnt_count, -1, 1))
-               return;
+#ifdef CONFIG_SMP
+static inline void __mntput(struct vfsmount *mnt, int longrefs)
+{
+       if (!longrefs) {
+put_again:
+               br_read_lock(vfsmount_lock);
+               if (likely(atomic_read(&mnt->mnt_longrefs))) {
+                       mnt_dec_count(mnt);
+                       br_read_unlock(vfsmount_lock);
+                       return;
+               }
+               br_read_unlock(vfsmount_lock);
+       } else {
+               BUG_ON(!atomic_read(&mnt->mnt_longrefs));
+               if (atomic_add_unless(&mnt->mnt_longrefs, -1, 1))
+                       return;
+       }
+
        br_write_lock(vfsmount_lock);
-       if (!atomic_dec_and_test(&mnt->mnt_count)) {
+       if (!longrefs)
+               mnt_dec_count(mnt);
+       else
+               atomic_dec(&mnt->mnt_longrefs);
+       if (mnt_get_count(mnt)) {
                br_write_unlock(vfsmount_lock);
                return;
        }
-       if (likely(!mnt->mnt_pinned)) {
+       if (unlikely(mnt->mnt_pinned)) {
+               mnt_add_count(mnt, mnt->mnt_pinned + 1);
+               mnt->mnt_pinned = 0;
                br_write_unlock(vfsmount_lock);
-               __mntput(mnt);
+               acct_auto_close_mnt(mnt);
+               goto put_again;
+       }
+       br_write_unlock(vfsmount_lock);
+       mntfree(mnt);
+}
+#else
+static inline void __mntput(struct vfsmount *mnt, int longrefs)
+{
+put_again:
+       mnt_dec_count(mnt);
+       if (likely(mnt_get_count(mnt)))
                return;
+       br_write_lock(vfsmount_lock);
+       if (unlikely(mnt->mnt_pinned)) {
+               mnt_add_count(mnt, mnt->mnt_pinned + 1);
+               mnt->mnt_pinned = 0;
+               br_write_unlock(vfsmount_lock);
+               acct_auto_close_mnt(mnt);
+               goto put_again;
        }
-       atomic_add(mnt->mnt_pinned + 1, &mnt->mnt_count);
-       mnt->mnt_pinned = 0;
        br_write_unlock(vfsmount_lock);
-       acct_auto_close_mnt(mnt);
-       goto repeat;
+       mntfree(mnt);
+}
+#endif
+
+static void mntput_no_expire(struct vfsmount *mnt)
+{
+       __mntput(mnt, 0);
+}
+
+void mntput(struct vfsmount *mnt)
+{
+       if (mnt) {
+               /* avoid cacheline pingpong, hope gcc doesn't get "smart" */
+               if (unlikely(mnt->mnt_expiry_mark))
+                       mnt->mnt_expiry_mark = 0;
+               __mntput(mnt, 0);
+       }
+}
+EXPORT_SYMBOL(mntput);
+
+struct vfsmount *mntget(struct vfsmount *mnt)
+{
+       if (mnt)
+               mnt_inc_count(mnt);
+       return mnt;
 }
-EXPORT_SYMBOL(mntput_no_expire);
+EXPORT_SYMBOL(mntget);
+
+void mntput_long(struct vfsmount *mnt)
+{
+#ifdef CONFIG_SMP
+       if (mnt) {
+               /* avoid cacheline pingpong, hope gcc doesn't get "smart" */
+               if (unlikely(mnt->mnt_expiry_mark))
+                       mnt->mnt_expiry_mark = 0;
+               __mntput(mnt, 1);
+       }
+#else
+       mntput(mnt);
+#endif
+}
+EXPORT_SYMBOL(mntput_long);
+
+struct vfsmount *mntget_long(struct vfsmount *mnt)
+{
+#ifdef CONFIG_SMP
+       if (mnt)
+               atomic_inc(&mnt->mnt_longrefs);
+       return mnt;
+#else
+       return mntget(mnt);
+#endif
+}
+EXPORT_SYMBOL(mntget_long);
 
 void mnt_pin(struct vfsmount *mnt)
 {
@@ -678,19 +848,17 @@ void mnt_pin(struct vfsmount *mnt)
        mnt->mnt_pinned++;
        br_write_unlock(vfsmount_lock);
 }
-
 EXPORT_SYMBOL(mnt_pin);
 
 void mnt_unpin(struct vfsmount *mnt)
 {
        br_write_lock(vfsmount_lock);
        if (mnt->mnt_pinned) {
-               atomic_inc(&mnt->mnt_count);
+               mnt_inc_count(mnt);
                mnt->mnt_pinned--;
        }
        br_write_unlock(vfsmount_lock);
 }
-
 EXPORT_SYMBOL(mnt_unpin);
 
 static inline void mangle(struct seq_file *m, const char *s)
@@ -985,12 +1153,13 @@ int may_umount_tree(struct vfsmount *mnt)
        int minimum_refs = 0;
        struct vfsmount *p;
 
-       br_read_lock(vfsmount_lock);
+       /* write lock needed for mnt_get_count */
+       br_write_lock(vfsmount_lock);
        for (p = mnt; p; p = next_mnt(p, mnt)) {
-               actual_refs += atomic_read(&p->mnt_count);
+               actual_refs += mnt_get_count(p);
                minimum_refs += 2;
        }
-       br_read_unlock(vfsmount_lock);
+       br_write_unlock(vfsmount_lock);
 
        if (actual_refs > minimum_refs)
                return 0;
@@ -1017,10 +1186,10 @@ int may_umount(struct vfsmount *mnt)
 {
        int ret = 1;
        down_read(&namespace_sem);
-       br_read_lock(vfsmount_lock);
+       br_write_lock(vfsmount_lock);
        if (propagate_mount_busy(mnt, 2))
                ret = 0;
-       br_read_unlock(vfsmount_lock);
+       br_write_unlock(vfsmount_lock);
        up_read(&namespace_sem);
        return ret;
 }
@@ -1047,7 +1216,7 @@ void release_mounts(struct list_head *head)
                        dput(dentry);
                        mntput(m);
                }
-               mntput(mnt);
+               mntput_long(mnt);
        }
 }
 
@@ -1073,7 +1242,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
                list_del_init(&p->mnt_child);
                if (p->mnt_parent != p) {
                        p->mnt_parent->mnt_ghosts++;
-                       p->mnt_mountpoint->d_mounted--;
+                       dentry_reset_mounted(p->mnt_parent, p->mnt_mountpoint);
                }
                change_mnt_propagation(p, MS_PRIVATE);
        }
@@ -1102,8 +1271,16 @@ static int do_umount(struct vfsmount *mnt, int flags)
                    flags & (MNT_FORCE | MNT_DETACH))
                        return -EINVAL;
 
-               if (atomic_read(&mnt->mnt_count) != 2)
+               /*
+                * probably don't strictly need the lock here if we examined
+                * all race cases, but it's a slowpath.
+                */
+               br_write_lock(vfsmount_lock);
+               if (mnt_get_count(mnt) != 2) {
+                       br_write_lock(vfsmount_lock);
                        return -EBUSY;
+               }
+               br_write_unlock(vfsmount_lock);
 
                if (!xchg(&mnt->mnt_expiry_mark, 1))
                        return -EAGAIN;
@@ -1792,7 +1969,7 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
 
 unlock:
        up_write(&namespace_sem);
-       mntput(newmnt);
+       mntput_long(newmnt);
        return err;
 }
 
@@ -2125,11 +2302,11 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
                if (fs) {
                        if (p == fs->root.mnt) {
                                rootmnt = p;
-                               fs->root.mnt = mntget(q);
+                               fs->root.mnt = mntget_long(q);
                        }
                        if (p == fs->pwd.mnt) {
                                pwdmnt = p;
-                               fs->pwd.mnt = mntget(q);
+                               fs->pwd.mnt = mntget_long(q);
                        }
                }
                p = next_mnt(p, mnt_ns->root);
@@ -2138,9 +2315,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
        up_write(&namespace_sem);
 
        if (rootmnt)
-               mntput(rootmnt);
+               mntput_long(rootmnt);
        if (pwdmnt)
-               mntput(pwdmnt);
+               mntput_long(pwdmnt);
 
        return new_ns;
 }
@@ -2327,6 +2504,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
        touch_mnt_namespace(current->nsproxy->mnt_ns);
        br_write_unlock(vfsmount_lock);
        chroot_fs_refs(&root, &new);
+
        error = 0;
        path_put(&root_parent);
        path_put(&parent_path);
@@ -2353,6 +2531,7 @@ static void __init init_mount_tree(void)
        mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
        if (IS_ERR(mnt))
                panic("Can't create rootfs");
+
        ns = create_mnt_ns(mnt);
        if (IS_ERR(ns))
                panic("Can't allocate initial namespace");
index f22b12e..28f136d 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
+#include <linux/namei.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
@@ -74,9 +75,12 @@ const struct inode_operations ncp_dir_inode_operations =
  * Dentry operations routines
  */
 static int ncp_lookup_validate(struct dentry *, struct nameidata *);
-static int ncp_hash_dentry(struct dentry *, struct qstr *);
-static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
-static int ncp_delete_dentry(struct dentry *);
+static int ncp_hash_dentry(const struct dentry *, const struct inode *,
+               struct qstr *);
+static int ncp_compare_dentry(const struct dentry *, const struct inode *,
+               const struct dentry *, const struct inode *,
+               unsigned int, const char *, const struct qstr *);
+static int ncp_delete_dentry(const struct dentry *);
 
 static const struct dentry_operations ncp_dentry_operations =
 {
@@ -113,10 +117,10 @@ static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator)
 
 #define ncp_preserve_case(i)   (ncp_namespace(i) != NW_NS_DOS)
 
-static inline int ncp_case_sensitive(struct dentry *dentry)
+static inline int ncp_case_sensitive(const struct inode *i)
 {
 #ifdef CONFIG_NCPFS_NFS_NS
-       return ncp_namespace(dentry->d_inode) == NW_NS_NFS;
+       return ncp_namespace(i) == NW_NS_NFS;
 #else
        return 0;
 #endif /* CONFIG_NCPFS_NFS_NS */
@@ -127,14 +131,16 @@ static inline int ncp_case_sensitive(struct dentry *dentry)
  * is case-sensitive.
  */
 static int 
-ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
+ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *this)
 {
-       if (!ncp_case_sensitive(dentry)) {
+       if (!ncp_case_sensitive(inode)) {
+               struct super_block *sb = dentry->d_sb;
                struct nls_table *t;
                unsigned long hash;
                int i;
 
-               t = NCP_IO_TABLE(dentry);
+               t = NCP_IO_TABLE(sb);
                hash = init_name_hash();
                for (i=0; i<this->len ; i++)
                        hash = partial_name_hash(ncp_tolower(t, this->name[i]),
@@ -145,15 +151,17 @@ ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
 }
 
 static int
-ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+ncp_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)
 {
-       if (a->len != b->len)
+       if (len != name->len)
                return 1;
 
-       if (ncp_case_sensitive(dentry))
-               return strncmp(a->name, b->name, a->len);
+       if (ncp_case_sensitive(pinode))
+               return strncmp(str, name->name, len);
 
-       return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len);
+       return ncp_strnicmp(NCP_IO_TABLE(pinode->i_sb), str, name->name, len);
 }
 
 /*
@@ -162,7 +170,7 @@ ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
  * Closing files can be safely postponed until iput() - it's done there anyway.
  */
 static int
-ncp_delete_dentry(struct dentry * dentry)
+ncp_delete_dentry(const struct dentry * dentry)
 {
        struct inode *inode = dentry->d_inode;
 
@@ -301,6 +309,9 @@ ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd)
        int res, val = 0, len;
        __u8 __name[NCP_MAXPATHLEN + 1];
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
        parent = dget_parent(dentry);
        dir = parent->d_inode;
 
@@ -384,21 +395,21 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
        }
 
        /* If a pointer is invalid, we search the dentry. */
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        next = parent->d_subdirs.next;
        while (next != &parent->d_subdirs) {
                dent = list_entry(next, struct dentry, d_u.d_child);
                if ((unsigned long)dent->d_fsdata == fpos) {
                        if (dent->d_inode)
-                               dget_locked(dent);
+                               dget(dent);
                        else
                                dent = NULL;
-                       spin_unlock(&dcache_lock);
+                       spin_unlock(&parent->d_lock);
                        goto out;
                }
                next = next->next;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
        return NULL;
 
 out:
@@ -592,7 +603,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
        qname.hash = full_name_hash(qname.name, qname.len);
 
        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, dentry->d_inode, &qname) != 0)
                        goto end_advance;
 
        newdent = d_lookup(dentry, &qname);
@@ -611,35 +622,12 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                        shrink_dcache_parent(newdent);
 
                /*
-                * It is not as dangerous as it looks.  NetWare's OS2 namespace is
-                * case preserving yet case insensitive.  So we update dentry's name
-                * as received from server.  We found dentry via d_lookup with our
-                * hash, so we know that hash does not change, and so replacing name
-                * should be reasonably safe.
+                * NetWare's OS2 namespace is case preserving yet case
+                * insensitive.  So we update dentry's name as received from
+                * server. Parent dir's i_mutex is locked because we're in
+                * readdir.
                 */
-               if (qname.len == newdent->d_name.len &&
-                   memcmp(newdent->d_name.name, qname.name, newdent->d_name.len)) {
-                       struct inode *inode = newdent->d_inode;
-
-                       /*
-                        * Inside ncpfs all uses of d_name are either for debugging,
-                        * or on functions which acquire inode mutex (mknod, creat,
-                        * lookup).  So grab i_mutex here, to be sure.  d_path
-                        * uses dcache_lock when generating path, so we should too.
-                        * And finally d_compare is protected by dentry's d_lock, so
-                        * here we go.
-                        */
-                       if (inode)
-                               mutex_lock(&inode->i_mutex);
-                       spin_lock(&dcache_lock);
-                       spin_lock(&newdent->d_lock);
-                       memcpy((char *) newdent->d_name.name, qname.name,
-                                                               newdent->d_name.len);
-                       spin_unlock(&newdent->d_lock);
-                       spin_unlock(&dcache_lock);
-                       if (inode)
-                               mutex_unlock(&inode->i_mutex);
-               }
+               dentry_update_name_case(newdent, &qname);
        }
 
        if (!newdent->d_inode) {
@@ -649,7 +637,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
                entry->ino = iunique(dir->i_sb, 2);
                inode = ncp_iget(dir->i_sb, entry);
                if (inode) {
-                       newdent->d_op = &ncp_dentry_operations;
+                       d_set_d_op(newdent, &ncp_dentry_operations);
                        d_instantiate(newdent, inode);
                        if (!hashed)
                                d_rehash(newdent);
@@ -657,7 +645,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
        } else {
                struct inode *inode = newdent->d_inode;
 
-               mutex_lock(&inode->i_mutex);
+               mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
                ncp_update_inode2(inode, entry);
                mutex_unlock(&inode->i_mutex);
        }
@@ -905,7 +893,7 @@ static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struc
        if (inode) {
                ncp_new_dentry(dentry);
 add_entry:
-               dentry->d_op = &ncp_dentry_operations;
+               d_set_d_op(dentry, &ncp_dentry_operations);
                d_add(dentry, inode);
                error = 0;
        }
index 8fb93b6..9531c05 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/vfs.h>
 #include <linux/mount.h>
 #include <linux/seq_file.h>
+#include <linux/namei.h>
 
 #include <linux/ncp_fs.h>
 
@@ -58,11 +59,18 @@ static struct inode *ncp_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void ncp_destroy_inode(struct inode *inode)
+static void ncp_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(ncp_inode_cachep, NCP_FINFO(inode));
 }
 
+static void ncp_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, ncp_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct ncp_inode_info *ei = (struct ncp_inode_info *) foo;
@@ -710,7 +718,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
        sb->s_root = d_alloc_root(root_inode);
         if (!sb->s_root)
                goto out_no_root;
-       sb->s_root->d_op = &ncp_root_dentry_operations;
+       d_set_d_op(sb->s_root, &ncp_root_dentry_operations);
        return 0;
 
 out_no_root:
index 3c57eca..1220df7 100644 (file)
@@ -135,7 +135,7 @@ int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *,
                                const unsigned char *, unsigned int, int);
 
 #define NCP_ESC                        ':'
-#define NCP_IO_TABLE(dentry)   (NCP_SERVER((dentry)->d_inode)->nls_io)
+#define NCP_IO_TABLE(sb)       (NCP_SBP(sb)->nls_io)
 #define ncp_tolower(t, c)      nls_tolower(t, c)
 #define ncp_toupper(t, c)      nls_toupper(t, c)
 #define ncp_strnicmp(t, s1, s2, len) \
@@ -150,15 +150,15 @@ int ncp__io2vol(unsigned char *, unsigned int *,
 int ncp__vol2io(unsigned char *, unsigned int *,
                                const unsigned char *, unsigned int, int);
 
-#define NCP_IO_TABLE(dentry)   NULL
+#define NCP_IO_TABLE(sb)       NULL
 #define ncp_tolower(t, c)      tolower(c)
 #define ncp_toupper(t, c)      toupper(c)
 #define ncp_io2vol(S,m,i,n,k,U)        ncp__io2vol(m,i,n,k,U)
 #define ncp_vol2io(S,m,i,n,k,U)        ncp__vol2io(m,i,n,k,U)
 
 
-static inline int ncp_strnicmp(struct nls_table *t, const unsigned char *s1,
-               const unsigned char *s2, int len)
+static inline int ncp_strnicmp(const struct nls_table *t,
+               const unsigned char *s1, const unsigned char *s2, int len)
 {
        while (len--) {
                if (tolower(*s1++) != tolower(*s2++))
@@ -193,7 +193,7 @@ ncp_renew_dentries(struct dentry *parent)
        struct list_head *next;
        struct dentry *dentry;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        next = parent->d_subdirs.next;
        while (next != &parent->d_subdirs) {
                dentry = list_entry(next, struct dentry, d_u.d_child);
@@ -205,7 +205,7 @@ ncp_renew_dentries(struct dentry *parent)
 
                next = next->next;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
 }
 
 static inline void
@@ -215,7 +215,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent)
        struct list_head *next;
        struct dentry *dentry;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&parent->d_lock);
        next = parent->d_subdirs.next;
        while (next != &parent->d_subdirs) {
                dentry = list_entry(next, struct dentry, d_u.d_child);
@@ -223,7 +223,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent)
                ncp_age_dentry(server, dentry);
                next = next->next;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&parent->d_lock);
 }
 
 struct ncp_cache_head {
index 996dd89..d33da53 100644 (file)
@@ -438,7 +438,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        if (dentry == NULL)
                return;
 
-       dentry->d_op = NFS_PROTO(dir)->dentry_ops;
+       d_set_d_op(dentry, NFS_PROTO(dir)->dentry_ops);
        inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
        if (IS_ERR(inode))
                goto out;
@@ -938,7 +938,8 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
  * component of the path.
  * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT.
  */
-static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigned int mask)
+static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd,
+                                               unsigned int mask)
 {
        if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT))
                return 0;
@@ -1018,7 +1019,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
  * If the parent directory is seen to have changed, we throw out the
  * cached dentry and do a new lookup.
  */
-static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
+static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
        struct inode *dir;
        struct inode *inode;
@@ -1027,6 +1028,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        struct nfs_fattr *fattr = NULL;
        int error;
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
        parent = dget_parent(dentry);
        dir = parent->d_inode;
        nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
@@ -1117,7 +1121,7 @@ out_error:
 /*
  * This is called from dput() when d_count is going to 0.
  */
-static int nfs_dentry_delete(struct dentry *dentry)
+static int nfs_dentry_delete(const struct dentry *dentry)
 {
        dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -1188,7 +1192,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
        if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
                goto out;
 
-       dentry->d_op = NFS_PROTO(dir)->dentry_ops;
+       d_set_d_op(dentry, NFS_PROTO(dir)->dentry_ops);
 
        /*
         * If we're doing an exclusive create, optimize away the lookup
@@ -1333,7 +1337,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
                res = ERR_PTR(-ENAMETOOLONG);
                goto out;
        }
-       dentry->d_op = NFS_PROTO(dir)->dentry_ops;
+       d_set_d_op(dentry, NFS_PROTO(dir)->dentry_ops);
 
        /* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash
         * the dentry. */
@@ -1718,11 +1722,9 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
        dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
                dir->i_ino, dentry->d_name.name);
 
-       spin_lock(&dcache_lock);
        spin_lock(&dentry->d_lock);
-       if (atomic_read(&dentry->d_count) > 1) {
+       if (dentry->d_count > 1) {
                spin_unlock(&dentry->d_lock);
-               spin_unlock(&dcache_lock);
                /* Start asynchronous writeout of the inode */
                write_inode_now(dentry->d_inode, 0);
                error = nfs_sillyrename(dir, dentry);
@@ -1733,7 +1735,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
                need_rehash = 1;
        }
        spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
        error = nfs_safe_remove(dentry);
        if (!error || error == -ENOENT) {
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
@@ -1868,7 +1869,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
                 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
-                atomic_read(&new_dentry->d_count));
+                new_dentry->d_count);
 
        /*
         * For non-directories, check whether the target is busy and if so,
@@ -1886,7 +1887,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        rehash = new_dentry;
                }
 
-               if (atomic_read(&new_dentry->d_count) > 2) {
+               if (new_dentry->d_count > 2) {
                        int err;
 
                        /* copy the target dentry's name */
@@ -2188,11 +2189,14 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
        return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
 }
 
-int nfs_permission(struct inode *inode, int mask)
+int nfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
        struct rpc_cred *cred;
        int res = 0;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
        if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
@@ -2240,7 +2244,7 @@ out:
 out_notsup:
        res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
        if (res == 0)
-               res = generic_permission(inode, mask, NULL);
+               res = generic_permission(inode, mask, flags, NULL);
        goto out;
 }
 
index ac7b814..5596c6a 100644 (file)
@@ -63,9 +63,11 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i
                 * This again causes shrink_dcache_for_umount_subtree() to
                 * Oops, since the test for IS_ROOT() will fail.
                 */
-               spin_lock(&dcache_lock);
+               spin_lock(&sb->s_root->d_inode->i_lock);
+               spin_lock(&sb->s_root->d_lock);
                list_del_init(&sb->s_root->d_alias);
-               spin_unlock(&dcache_lock);
+               spin_unlock(&sb->s_root->d_lock);
+               spin_unlock(&sb->s_root->d_inode->i_lock);
        }
        return 0;
 }
@@ -119,7 +121,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
        security_d_instantiate(ret, inode);
 
        if (ret->d_op == NULL)
-               ret->d_op = server->nfs_client->rpc_ops->dentry_ops;
+               d_set_d_op(ret, server->nfs_client->rpc_ops->dentry_ops);
 out:
        nfs_free_fattr(fsinfo.fattr);
        return ret;
@@ -226,7 +228,7 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
        security_d_instantiate(ret, inode);
 
        if (ret->d_op == NULL)
-               ret->d_op = server->nfs_client->rpc_ops->dentry_ops;
+               d_set_d_op(ret, server->nfs_client->rpc_ops->dentry_ops);
 
 out:
        nfs_free_fattr(fattr);
index e67e31c..017daa3 100644 (file)
@@ -1438,11 +1438,18 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
        return &nfsi->vfs_inode;
 }
 
-void nfs_destroy_inode(struct inode *inode)
+static void nfs_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(nfs_inode_cachep, NFS_I(inode));
 }
 
+void nfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, nfs_i_callback);
+}
+
 static inline void nfs4_init_once(struct nfs_inode *nfsi)
 {
 #ifdef CONFIG_NFS_V4
index db6aa36..74aaf39 100644 (file)
@@ -49,12 +49,17 @@ char *nfs_path(const char *base,
               const struct dentry *dentry,
               char *buffer, ssize_t buflen)
 {
-       char *end = buffer+buflen;
+       char *end;
        int namelen;
+       unsigned seq;
 
+rename_retry:
+       end = buffer+buflen;
        *--end = '\0';
        buflen--;
-       spin_lock(&dcache_lock);
+
+       seq = read_seqbegin(&rename_lock);
+       rcu_read_lock();
        while (!IS_ROOT(dentry) && dentry != droot) {
                namelen = dentry->d_name.len;
                buflen -= namelen + 1;
@@ -65,7 +70,9 @@ char *nfs_path(const char *base,
                *--end = '/';
                dentry = dentry->d_parent;
        }
-       spin_unlock(&dcache_lock);
+       rcu_read_unlock();
+       if (read_seqretry(&rename_lock, seq))
+               goto rename_retry;
        if (*end != '/') {
                if (--buflen < 0)
                        goto Elong;
@@ -82,7 +89,9 @@ char *nfs_path(const char *base,
        memcpy(end, base, namelen);
        return end;
 Elong_unlock:
-       spin_unlock(&dcache_lock);
+       rcu_read_unlock();
+       if (read_seqretry(&rename_lock, seq))
+               goto rename_retry;
 Elong:
        return ERR_PTR(-ENAMETOOLONG);
 }
index 7bdec85..8fe9eb4 100644 (file)
@@ -496,7 +496,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
 
        dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
                dentry->d_parent->d_name.name, dentry->d_name.name,
-               atomic_read(&dentry->d_count));
+               dentry->d_count);
        nfs_inc_stats(dir, NFSIOS_SILLYRENAME);
 
        /*
index 184938f..3a35902 100644 (file)
@@ -1756,8 +1756,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
                goto out_dput_new;
 
        if (svc_msnfs(ffhp) &&
-               ((atomic_read(&odentry->d_count) > 1)
-                || (atomic_read(&ndentry->d_count) > 1))) {
+               ((odentry->d_count > 1) || (ndentry->d_count > 1))) {
                        host_err = -EPERM;
                        goto out_dput_new;
        }
@@ -1843,7 +1842,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        if (type != S_IFDIR) { /* It's UNLINK */
 #ifdef MSNFS
                if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
-                       (atomic_read(&rdentry->d_count) > 1)) {
+                       (rdentry->d_count > 1)) {
                        host_err = -EPERM;
                } else
 #endif
index 71d4bc8..77b48c8 100644 (file)
@@ -785,15 +785,19 @@ out_err:
        return err;
 }
 
-int nilfs_permission(struct inode *inode, int mask)
+int nilfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       struct nilfs_root *root = NILFS_I(inode)->i_root;
+       struct nilfs_root *root;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
 
+       root = NILFS_I(inode)->i_root;
        if ((mask & MAY_WRITE) && root &&
            root->cno != NILFS_CPTREE_CURRENT_CNO)
                return -EROFS; /* snapshot is not writable */
 
-       return generic_permission(inode, mask, NULL);
+       return generic_permission(inode, mask, flags, NULL);
 }
 
 int nilfs_load_inode_block(struct nilfs_sb_info *sbi, struct inode *inode,
index f7560da..0ca9882 100644 (file)
@@ -256,7 +256,7 @@ extern void nilfs_update_inode(struct inode *, struct buffer_head *);
 extern void nilfs_truncate(struct inode *);
 extern void nilfs_evict_inode(struct inode *);
 extern int nilfs_setattr(struct dentry *, struct iattr *);
-int nilfs_permission(struct inode *inode, int mask);
+int nilfs_permission(struct inode *inode, int mask, unsigned int flags);
 extern int nilfs_load_inode_block(struct nilfs_sb_info *, struct inode *,
                                  struct buffer_head **);
 extern int nilfs_inode_dirty(struct inode *);
index f804d41..e2dcc9c 100644 (file)
@@ -162,10 +162,13 @@ struct inode *nilfs_alloc_inode(struct super_block *sb)
        return &ii->vfs_inode;
 }
 
-void nilfs_destroy_inode(struct inode *inode)
+static void nilfs_i_callback(struct rcu_head *head)
 {
+       struct inode *inode = container_of(head, struct inode, i_rcu);
        struct nilfs_mdt_info *mdi = NILFS_MDT(inode);
 
+       INIT_LIST_HEAD(&inode->i_dentry);
+
        if (mdi) {
                kfree(mdi->mi_bgl); /* kfree(NULL) is safe */
                kfree(mdi);
@@ -173,6 +176,11 @@ void nilfs_destroy_inode(struct inode *inode)
        kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode));
 }
 
+void nilfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, nilfs_i_callback);
+}
+
 static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag)
 {
        struct the_nilfs *nilfs = sbi->s_nilfs;
@@ -838,7 +846,7 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
 
 static int nilfs_tree_was_touched(struct dentry *root_dentry)
 {
-       return atomic_read(&root_dentry->d_count) > 1;
+       return root_dentry->d_count > 1;
 }
 
 /**
index 20dc218..79b47cb 100644 (file)
@@ -59,7 +59,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
        /* determine if the children should tell inode about their events */
        watched = fsnotify_inode_watches_children(inode);
 
-       spin_lock(&dcache_lock);
+       spin_lock(&inode->i_lock);
        /* run all of the dentries associated with this inode.  Since this is a
         * directory, there damn well better only be one item on this list */
        list_for_each_entry(alias, &inode->i_dentry, d_alias) {
@@ -68,19 +68,21 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
                /* run all of the children of the original inode and fix their
                 * d_flags to indicate parental interest (their parent is the
                 * original inode) */
+               spin_lock(&alias->d_lock);
                list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
                        if (!child->d_inode)
                                continue;
 
-                       spin_lock(&child->d_lock);
+                       spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
                        if (watched)
                                child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
                        else
                                child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED;
                        spin_unlock(&child->d_lock);
                }
+               spin_unlock(&alias->d_lock);
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
 }
 
 /* Notify this dentry's parent about a child's events. */
index 93622b1..a627ed8 100644 (file)
@@ -332,6 +332,13 @@ struct inode *ntfs_alloc_big_inode(struct super_block *sb)
        return NULL;
 }
 
+static void ntfs_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(ntfs_big_inode_cache, NTFS_I(inode));
+}
+
 void ntfs_destroy_big_inode(struct inode *inode)
 {
        ntfs_inode *ni = NTFS_I(inode);
@@ -340,7 +347,7 @@ void ntfs_destroy_big_inode(struct inode *inode)
        BUG_ON(ni->page);
        if (!atomic_dec_and_test(&ni->count))
                BUG();
-       kmem_cache_free(ntfs_big_inode_cache, NTFS_I(inode));
+       call_rcu(&inode->i_rcu, ntfs_i_callback);
 }
 
 static inline ntfs_inode *ntfs_alloc_extent_inode(void)
index 3919150..704f6b1 100644 (file)
@@ -291,13 +291,17 @@ static int ocfs2_set_acl(handle_t *handle,
        return ret;
 }
 
-int ocfs2_check_acl(struct inode *inode, int mask)
+int ocfs2_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_super *osb;
        struct buffer_head *di_bh = NULL;
        struct posix_acl *acl;
        int ret = -EAGAIN;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       osb = OCFS2_SB(inode->i_sb);
        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
                return ret;
 
index 5c5d31f..4fe7c9c 100644 (file)
@@ -26,7 +26,7 @@ struct ocfs2_acl_entry {
        __le32 e_id;
 };
 
-extern int ocfs2_check_acl(struct inode *, int);
+extern int ocfs2_check_acl(struct inode *, int, unsigned int);
 extern int ocfs2_acl_chmod(struct inode *);
 extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
                          struct buffer_head *, struct buffer_head *,
index 895532a..6d80ecc 100644 (file)
@@ -52,9 +52,15 @@ void ocfs2_dentry_attach_gen(struct dentry *dentry)
 static int ocfs2_dentry_revalidate(struct dentry *dentry,
                                   struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode;
        int ret = 0;    /* if all else fails, just return false */
-       struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
+       struct ocfs2_super *osb;
+
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
+       osb = OCFS2_SB(dentry->d_sb);
 
        mlog_entry("(0x%p, '%.*s')\n", dentry,
                   dentry->d_name.len, dentry->d_name.name);
@@ -169,23 +175,25 @@ struct dentry *ocfs2_find_local_alias(struct inode *inode,
        struct list_head *p;
        struct dentry *dentry = NULL;
 
-       spin_lock(&dcache_lock);
-
+       spin_lock(&inode->i_lock);
        list_for_each(p, &inode->i_dentry) {
                dentry = list_entry(p, struct dentry, d_alias);
 
+               spin_lock(&dentry->d_lock);
                if (ocfs2_match_dentry(dentry, parent_blkno, skip_unhashed)) {
                        mlog(0, "dentry found: %.*s\n",
                             dentry->d_name.len, dentry->d_name.name);
 
-                       dget_locked(dentry);
+                       dget_dlock(dentry);
+                       spin_unlock(&dentry->d_lock);
                        break;
                }
+               spin_unlock(&dentry->d_lock);
 
                dentry = NULL;
        }
 
-       spin_unlock(&dcache_lock);
+       spin_unlock(&inode->i_lock);
 
        return dentry;
 }
index b2df490..8c5c0ed 100644 (file)
@@ -351,11 +351,18 @@ static struct inode *dlmfs_alloc_inode(struct super_block *sb)
        return &ip->ip_vfs_inode;
 }
 
-static void dlmfs_destroy_inode(struct inode *inode)
+static void dlmfs_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(dlmfs_inode_cache, DLMFS_I(inode));
 }
 
+static void dlmfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, dlmfs_i_callback);
+}
+
 static void dlmfs_evict_inode(struct inode *inode)
 {
        int status;
index 19ad145..6adafa5 100644 (file)
@@ -138,7 +138,7 @@ check_gen:
 
        result = d_obtain_alias(inode);
        if (!IS_ERR(result))
-               result->d_op = &ocfs2_dentry_ops;
+               d_set_d_op(result, &ocfs2_dentry_ops);
        else
                mlog_errno(PTR_ERR(result));
 
@@ -176,7 +176,7 @@ static struct dentry *ocfs2_get_parent(struct dentry *child)
 
        parent = d_obtain_alias(ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0, 0));
        if (!IS_ERR(parent))
-               parent->d_op = &ocfs2_dentry_ops;
+               d_set_d_op(parent, &ocfs2_dentry_ops);
 
 bail_unlock:
        ocfs2_inode_unlock(dir, 0);
index f6cba56..bdadbae 100644 (file)
@@ -1307,10 +1307,13 @@ bail:
        return err;
 }
 
-int ocfs2_permission(struct inode *inode, int mask)
+int ocfs2_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int ret;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        mlog_entry_void();
 
        ret = ocfs2_inode_lock(inode, NULL, 0);
@@ -1320,7 +1323,7 @@ int ocfs2_permission(struct inode *inode, int mask)
                goto out;
        }
 
-       ret = generic_permission(inode, mask, ocfs2_check_acl);
+       ret = generic_permission(inode, mask, flags, ocfs2_check_acl);
 
        ocfs2_inode_unlock(inode, 0);
 out:
index 97bf761..f5afbbe 100644 (file)
@@ -61,7 +61,7 @@ int ocfs2_zero_extend(struct inode *inode, struct buffer_head *di_bh,
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat);
-int ocfs2_permission(struct inode *inode, int mask);
+int ocfs2_permission(struct inode *inode, int mask, unsigned int flags);
 
 int ocfs2_should_update_atime(struct inode *inode,
                              struct vfsmount *vfsmnt);
index ff5744e..d14cad6 100644 (file)
@@ -147,7 +147,7 @@ static struct dentry *ocfs2_lookup(struct inode *dir, struct dentry *dentry,
        spin_unlock(&oi->ip_lock);
 
 bail_add:
-       dentry->d_op = &ocfs2_dentry_ops;
+       d_set_d_op(dentry, &ocfs2_dentry_ops);
        ret = d_splice_alias(inode, dentry);
 
        if (inode) {
@@ -415,7 +415,7 @@ static int ocfs2_mknod(struct inode *dir,
                mlog_errno(status);
                goto leave;
        }
-       dentry->d_op = &ocfs2_dentry_ops;
+       d_set_d_op(dentry, &ocfs2_dentry_ops);
 
        status = ocfs2_add_entry(handle, dentry, inode,
                                 OCFS2_I(inode)->ip_blkno, parent_fe_bh,
@@ -743,7 +743,7 @@ static int ocfs2_link(struct dentry *old_dentry,
        }
 
        ihold(inode);
-       dentry->d_op = &ocfs2_dentry_ops;
+       d_set_d_op(dentry, &ocfs2_dentry_ops);
        d_instantiate(dentry, inode);
 
 out_commit:
@@ -1794,7 +1794,7 @@ static int ocfs2_symlink(struct inode *dir,
                mlog_errno(status);
                goto bail;
        }
-       dentry->d_op = &ocfs2_dentry_ops;
+       d_set_d_op(dentry, &ocfs2_dentry_ops);
 
        status = ocfs2_add_entry(handle, dentry, inode,
                                 le64_to_cpu(fe->i_blkno), parent_fe_bh,
@@ -2459,7 +2459,7 @@ int ocfs2_mv_orphaned_inode_to_new(struct inode *dir,
                goto out_commit;
        }
 
-       dentry->d_op = &ocfs2_dentry_ops;
+       d_set_d_op(dentry, &ocfs2_dentry_ops);
        d_instantiate(dentry, inode);
        status = 0;
 out_commit:
index cfeab7c..17ff46f 100644 (file)
@@ -569,11 +569,18 @@ static struct inode *ocfs2_alloc_inode(struct super_block *sb)
        return &oi->vfs_inode;
 }
 
-static void ocfs2_destroy_inode(struct inode *inode)
+static void ocfs2_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(ocfs2_inode_cachep, OCFS2_I(inode));
 }
 
+static void ocfs2_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, ocfs2_i_callback);
+}
+
 static unsigned long long ocfs2_max_file_offset(unsigned int bbits,
                                                unsigned int cbits)
 {
index 911e61f..a2a5bff 100644 (file)
@@ -343,11 +343,18 @@ static struct inode *openprom_alloc_inode(struct super_block *sb)
        return &oi->vfs_inode;
 }
 
-static void openprom_destroy_inode(struct inode *inode)
+static void openprom_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(op_inode_cachep, OP_I(inode));
 }
 
+static void openprom_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, openprom_i_callback);
+}
+
 static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
 {
        struct inode *inode;
index 04629f3..68f1f8e 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -999,12 +999,12 @@ struct file *create_write_pipe(int flags)
                goto err;
 
        err = -ENOMEM;
-       path.dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &name);
+       path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &name);
        if (!path.dentry)
                goto err_inode;
        path.mnt = mntget(pipe_mnt);
 
-       path.dentry->d_op = &pipefs_dentry_operations;
+       d_set_d_op(path.dentry, &pipefs_dentry_operations);
        d_instantiate(path.dentry, inode);
 
        err = -ENFILE;
@@ -1253,6 +1253,10 @@ out:
        return ret;
 }
 
+static const struct super_operations pipefs_ops = {
+       .destroy_inode = free_inode_nonrcu,
+};
+
 /*
  * pipefs should _never_ be mounted by userland - too much of security hassle,
  * no real gain from having the whole whorehouse mounted. So we don't need
@@ -1262,7 +1266,7 @@ out:
 static struct dentry *pipefs_mount(struct file_system_type *fs_type,
                         int flags, const char *dev_name, void *data)
 {
-       return mount_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC);
+       return mount_pseudo(fs_type, "pipe:", &pipefs_ops, PIPEFS_MAGIC);
 }
 
 static struct file_system_type pipe_fs_type = {
@@ -1288,7 +1292,7 @@ static int __init init_pipe_fs(void)
 static void __exit exit_pipe_fs(void)
 {
        unregister_filesystem(&pipe_fs_type);
-       mntput(pipe_mnt);
+       mntput_long(pipe_mnt);
 }
 
 fs_initcall(init_pipe_fs);
index 8066b8d..d42514e 100644 (file)
@@ -288,7 +288,7 @@ out:
  */
 static inline int do_refcount_check(struct vfsmount *mnt, int count)
 {
-       int mycount = atomic_read(&mnt->mnt_count) - mnt->mnt_ghosts;
+       int mycount = mnt_get_count(mnt) - mnt->mnt_ghosts;
        return (mycount > count);
 }
 
@@ -300,7 +300,7 @@ static inline int do_refcount_check(struct vfsmount *mnt, int count)
  * Check if any of these mounts that **do not have submounts**
  * have more references than 'refcnt'. If so return busy.
  *
- * vfsmount lock must be held for read or write
+ * vfsmount lock must be held for write
  */
 int propagate_mount_busy(struct vfsmount *mnt, int refcnt)
 {
index 08cba2c..b20962c 100644 (file)
@@ -1795,10 +1795,16 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat
  */
 static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
-       struct task_struct *task = get_proc_task(inode);
+       struct inode *inode;
+       struct task_struct *task;
        const struct cred *cred;
 
+       if (nd && nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
+       task = get_proc_task(inode);
+
        if (task) {
                if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
                    task_dumpable(task)) {
@@ -1820,7 +1826,7 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
        return 0;
 }
 
-static int pid_delete_dentry(struct dentry * dentry)
+static int pid_delete_dentry(const struct dentry * dentry)
 {
        /* Is the task we represent dead?
         * If so, then don't put the dentry on the lru list,
@@ -1964,12 +1970,19 @@ static int proc_fd_link(struct inode *inode, struct path *path)
 
 static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
-       struct task_struct *task = get_proc_task(inode);
-       int fd = proc_fd(inode);
+       struct inode *inode;
+       struct task_struct *task;
+       int fd;
        struct files_struct *files;
        const struct cred *cred;
 
+       if (nd && nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
+       task = get_proc_task(inode);
+       fd = proc_fd(inode);
+
        if (task) {
                files = get_files_struct(task);
                if (files) {
@@ -2045,7 +2058,7 @@ static struct dentry *proc_fd_instantiate(struct inode *dir,
        inode->i_op = &proc_pid_link_inode_operations;
        inode->i_size = 64;
        ei->op.proc_get_link = proc_fd_link;
-       dentry->d_op = &tid_fd_dentry_operations;
+       d_set_d_op(dentry, &tid_fd_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (tid_fd_revalidate(dentry, NULL))
@@ -2177,11 +2190,13 @@ static const struct file_operations proc_fd_operations = {
  * /proc/pid/fd needs a special permission handler so that a process can still
  * access /proc/self/fd after it has executed a setuid().
  */
-static int proc_fd_permission(struct inode *inode, int mask)
+static int proc_fd_permission(struct inode *inode, int mask, unsigned int flags)
 {
        int rv;
 
-       rv = generic_permission(inode, mask, NULL);
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+       rv = generic_permission(inode, mask, flags, NULL);
        if (rv == 0)
                return 0;
        if (task_pid(current) == proc_pid(inode))
@@ -2213,7 +2228,7 @@ static struct dentry *proc_fdinfo_instantiate(struct inode *dir,
        ei->fd = fd;
        inode->i_mode = S_IFREG | S_IRUSR;
        inode->i_fop = &proc_fdinfo_file_operations;
-       dentry->d_op = &tid_fd_dentry_operations;
+       d_set_d_op(dentry, &tid_fd_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (tid_fd_revalidate(dentry, NULL))
@@ -2272,7 +2287,7 @@ static struct dentry *proc_pident_instantiate(struct inode *dir,
        if (p->fop)
                inode->i_fop = p->fop;
        ei->op = p->op;
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
        if (pid_revalidate(dentry, NULL))
@@ -2639,8 +2654,14 @@ static const struct pid_entry proc_base_stuff[] = {
  */
 static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct inode *inode = dentry->d_inode;
-       struct task_struct *task = get_proc_task(inode);
+       struct inode *inode;
+       struct task_struct *task;
+
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       inode = dentry->d_inode;
+       task = get_proc_task(inode);
        if (task) {
                put_task_struct(task);
                return 1;
@@ -2691,7 +2712,7 @@ static struct dentry *proc_base_instantiate(struct inode *dir,
        if (p->fop)
                inode->i_fop = p->fop;
        ei->op = p->op;
-       dentry->d_op = &proc_base_dentry_operations;
+       d_set_d_op(dentry, &proc_base_dentry_operations);
        d_add(dentry, inode);
        error = NULL;
 out:
@@ -3005,7 +3026,7 @@ static struct dentry *proc_pid_instantiate(struct inode *dir,
        inode->i_nlink = 2 + pid_entry_count_dirs(tgid_base_stuff,
                ARRAY_SIZE(tgid_base_stuff));
 
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
 
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
@@ -3248,7 +3269,7 @@ static struct dentry *proc_task_instantiate(struct inode *dir,
        inode->i_nlink = 2 + pid_entry_count_dirs(tid_base_stuff,
                ARRAY_SIZE(tid_base_stuff));
 
-       dentry->d_op = &pid_dentry_operations;
+       d_set_d_op(dentry, &pid_dentry_operations);
 
        d_add(dentry, inode);
        /* Close the race of the process dying before we return the dentry */
index dd29f03..f766be2 100644 (file)
@@ -400,7 +400,7 @@ static const struct inode_operations proc_link_inode_operations = {
  * smarter: we could keep a "volatile" flag in the 
  * inode to indicate which ones to keep.
  */
-static int proc_delete_dentry(struct dentry * dentry)
+static int proc_delete_dentry(const struct dentry * dentry)
 {
        return 1;
 }
@@ -439,7 +439,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
 out_unlock:
 
        if (inode) {
-               dentry->d_op = &proc_dentry_operations;
+               d_set_d_op(dentry, &proc_dentry_operations);
                d_add(dentry, inode);
                return NULL;
        }
index 3ddb606..6bcb926 100644 (file)
@@ -65,11 +65,18 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
        return inode;
 }
 
-static void proc_destroy_inode(struct inode *inode)
+static void proc_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(proc_inode_cachep, PROC_I(inode));
 }
 
+static void proc_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, proc_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct proc_inode *ei = (struct proc_inode *) foo;
index b652cb0..09a1f92 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/sysctl.h>
 #include <linux/proc_fs.h>
 #include <linux/security.h>
+#include <linux/namei.h>
 #include "internal.h"
 
 static const struct dentry_operations proc_sys_dentry_operations;
@@ -120,7 +121,7 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
                goto out;
 
        err = NULL;
-       dentry->d_op = &proc_sys_dentry_operations;
+       d_set_d_op(dentry, &proc_sys_dentry_operations);
        d_add(dentry, inode);
 
 out:
@@ -201,7 +202,7 @@ static int proc_sys_fill_cache(struct file *filp, void *dirent,
                                dput(child);
                                return -ENOMEM;
                        } else {
-                               child->d_op = &proc_sys_dentry_operations;
+                               d_set_d_op(child, &proc_sys_dentry_operations);
                                d_add(child, inode);
                        }
                } else {
@@ -294,7 +295,7 @@ out:
        return ret;
 }
 
-static int proc_sys_permission(struct inode *inode, int mask)
+static int proc_sys_permission(struct inode *inode, int mask,unsigned int flags)
 {
        /*
         * sysctl entries that are not writeable,
@@ -304,6 +305,9 @@ static int proc_sys_permission(struct inode *inode, int mask)
        struct ctl_table *table;
        int error;
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        /* Executable files are not allowed under /proc/sys/ */
        if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))
                return -EACCES;
@@ -389,23 +393,30 @@ static const struct inode_operations proc_sys_dir_operations = {
 
 static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
        return !PROC_I(dentry->d_inode)->sysctl->unregistering;
 }
 
-static int proc_sys_delete(struct dentry *dentry)
+static int proc_sys_delete(const struct dentry *dentry)
 {
        return !!PROC_I(dentry->d_inode)->sysctl->unregistering;
 }
 
-static int proc_sys_compare(struct dentry *dir, struct qstr *qstr,
-                           struct qstr *name)
+static int proc_sys_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 dentry *dentry = container_of(qstr, struct dentry, d_name);
-       if (qstr->len != name->len)
+       /* Although proc doesn't have negative dentries, rcu-walk means
+        * that inode here can be NULL */
+       if (!inode)
+               return 0;
+       if (name->len != len)
                return 1;
-       if (memcmp(qstr->name, name->name, name->len))
+       if (memcmp(name->name, str, len))
                return 1;
-       return !sysctl_is_seen(PROC_I(dentry->d_inode)->sysctl);
+       return !sysctl_is_seen(PROC_I(inode)->sysctl);
 }
 
 static const struct dentry_operations proc_sys_dentry_operations = {
index fcada42..e63b417 100644 (file)
@@ -425,11 +425,18 @@ static struct inode *qnx4_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void qnx4_destroy_inode(struct inode *inode)
+static void qnx4_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(qnx4_inode_cachep, qnx4_i(inode));
 }
 
+static void qnx4_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, qnx4_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo;
index b243117..2575682 100644 (file)
@@ -529,11 +529,18 @@ static struct inode *reiserfs_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void reiserfs_destroy_inode(struct inode *inode)
+static void reiserfs_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(reiserfs_inode_cachep, REISERFS_I(inode));
 }
 
+static void reiserfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, reiserfs_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct reiserfs_inode_info *ei = (struct reiserfs_inode_info *)foo;
index 5d04a78..3cfb2e9 100644 (file)
@@ -870,11 +870,14 @@ out:
        return err;
 }
 
-static int reiserfs_check_acl(struct inode *inode, int mask)
+static int reiserfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
        struct posix_acl *acl;
        int error = -EAGAIN; /* do regular unix permission checks by default */
 
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
        acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
 
        if (acl) {
@@ -951,8 +954,10 @@ static int xattr_mount_check(struct super_block *s)
        return 0;
 }
 
-int reiserfs_permission(struct inode *inode, int mask)
+int reiserfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
        /*
         * We don't do permission checks on the internal objects.
         * Permissions are determined by the "owning" object.
@@ -965,13 +970,16 @@ int reiserfs_permission(struct inode *inode, int mask)
         * Stat data v1 doesn't support ACLs.
         */
        if (get_inode_sd_version(inode) != STAT_DATA_V1)
-               return generic_permission(inode, mask, reiserfs_check_acl);
+               return generic_permission(inode, mask, flags,
+                                       reiserfs_check_acl);
 #endif
-       return generic_permission(inode, mask, NULL);
+       return generic_permission(inode, mask, flags, NULL);
 }
 
 static int xattr_hide_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
        return -EPERM;
 }
 
@@ -990,7 +998,7 @@ int reiserfs_lookup_privroot(struct super_block *s)
                                strlen(PRIVROOT_NAME));
        if (!IS_ERR(dentry)) {
                REISERFS_SB(s)->priv_root = dentry;
-               dentry->d_op = &xattr_lookup_poison_ops;
+               d_set_d_op(dentry, &xattr_lookup_poison_ops);
                if (dentry->d_inode)
                        dentry->d_inode->i_flags |= S_PRIVATE;
        } else
index 6647f90..2305e31 100644 (file)
@@ -400,11 +400,18 @@ static struct inode *romfs_alloc_inode(struct super_block *sb)
 /*
  * return a spent inode to the slab cache
  */
-static void romfs_destroy_inode(struct inode *inode)
+static void romfs_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(romfs_inode_cachep, ROMFS_I(inode));
 }
 
+static void romfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, romfs_i_callback);
+}
+
 /*
  * get filesystem statistics
  */
index 24de30b..20700b9 100644 (file)
@@ -440,11 +440,18 @@ static struct inode *squashfs_alloc_inode(struct super_block *sb)
 }
 
 
-static void squashfs_destroy_inode(struct inode *inode)
+static void squashfs_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(squashfs_inode_cachep, squashfs_i(inode));
 }
 
+static void squashfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, squashfs_i_callback);
+}
+
 
 static struct file_system_type squashfs_fs_type = {
        .owner = THIS_MODULE,
index ca69615..823e061 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/idr.h>
 #include <linux/mutex.h>
 #include <linux/backing-dev.h>
+#include <linux/rculist_bl.h>
 #include "internal.h"
 
 
@@ -71,7 +72,7 @@ static struct super_block *alloc_super(struct file_system_type *type)
                INIT_LIST_HEAD(&s->s_files);
 #endif
                INIT_LIST_HEAD(&s->s_instances);
-               INIT_HLIST_HEAD(&s->s_anon);
+               INIT_HLIST_BL_HEAD(&s->s_anon);
                INIT_LIST_HEAD(&s->s_inodes);
                INIT_LIST_HEAD(&s->s_dentry_lru);
                init_rwsem(&s->s_umount);
@@ -1139,7 +1140,7 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
        return mnt;
 
  err:
-       mntput(mnt);
+       mntput_long(mnt);
        return ERR_PTR(err);
 }
 
index 7e54bac..ea9120a 100644 (file)
@@ -231,7 +231,7 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
                goto repeat;
 }
 
-static int sysfs_dentry_delete(struct dentry *dentry)
+static int sysfs_dentry_delete(const struct dentry *dentry)
 {
        struct sysfs_dirent *sd = dentry->d_fsdata;
        return !!(sd->s_flags & SYSFS_FLAG_REMOVED);
@@ -239,9 +239,13 @@ static int sysfs_dentry_delete(struct dentry *dentry)
 
 static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct sysfs_dirent *sd = dentry->d_fsdata;
+       struct sysfs_dirent *sd;
        int is_dir;
 
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       sd = dentry->d_fsdata;
        mutex_lock(&sysfs_mutex);
 
        /* The sysfs dirent has been deleted */
@@ -701,7 +705,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
        /* instantiate and hash dentry */
        ret = d_find_alias(inode);
        if (!ret) {
-               dentry->d_op = &sysfs_dentry_ops;
+               d_set_d_op(dentry, &sysfs_dentry_ops);
                dentry->d_fsdata = sysfs_get(sd);
                d_add(dentry, inode);
        } else {
index cffb1fd..30ac273 100644 (file)
@@ -348,13 +348,18 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const cha
                return -ENOENT;
 }
 
-int sysfs_permission(struct inode *inode, int mask)
+int sysfs_permission(struct inode *inode, int mask, unsigned int flags)
 {
-       struct sysfs_dirent *sd = inode->i_private;
+       struct sysfs_dirent *sd;
+
+       if (flags & IPERM_FLAG_RCU)
+               return -ECHILD;
+
+       sd = inode->i_private;
 
        mutex_lock(&sysfs_mutex);
        sysfs_refresh_inode(sd, inode);
        mutex_unlock(&sysfs_mutex);
 
-       return generic_permission(inode, mask, NULL);
+       return generic_permission(inode, mask, flags, NULL);
 }
index d9be60a..ffaaa81 100644 (file)
@@ -200,7 +200,7 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
 struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd);
 void sysfs_evict_inode(struct inode *inode);
 int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr);
-int sysfs_permission(struct inode *inode, int mask);
+int sysfs_permission(struct inode *inode, int mask, unsigned int flags);
 int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
 int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
 int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
index de44d06..0630eb9 100644 (file)
@@ -333,11 +333,18 @@ static struct inode *sysv_alloc_inode(struct super_block *sb)
        return &si->vfs_inode;
 }
 
-static void sysv_destroy_inode(struct inode *inode)
+static void sysv_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(sysv_inode_cachep, SYSV_I(inode));
 }
 
+static void sysv_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, sysv_i_callback);
+}
+
 static void init_once(void *p)
 {
        struct sysv_inode_info *si = (struct sysv_inode_info *)p;
index 11e7f7d..b5e68da 100644 (file)
@@ -27,7 +27,8 @@ static int add_nondir(struct dentry *dentry, struct inode *inode)
        return err;
 }
 
-static int sysv_hash(struct dentry *dentry, struct qstr *qstr)
+static int sysv_hash(const struct dentry *dentry, const struct inode *inode,
+               struct qstr *qstr)
 {
        /* Truncate the name in place, avoids having to define a compare
           function. */
@@ -47,7 +48,7 @@ static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry, st
        struct inode * inode = NULL;
        ino_t ino;
 
-       dentry->d_op = dir->i_sb->s_root->d_op;
+       d_set_d_op(dentry, dir->i_sb->s_root->d_op);
        if (dentry->d_name.len > SYSV_NAMELEN)
                return ERR_PTR(-ENAMETOOLONG);
        ino = sysv_inode_by_name(dentry);
index 3d9c62b..76712ae 100644 (file)
@@ -346,7 +346,7 @@ static int complete_read_super(struct super_block *sb, int silent, int size)
        if (sbi->s_forced_ro)
                sb->s_flags |= MS_RDONLY;
        if (sbi->s_truncate)
-               sb->s_root->d_op = &sysv_dentry_operations;
+               d_set_d_op(sb->s_root, &sysv_dentry_operations);
        return 1;
 }
 
index 91fac54..6e11c29 100644 (file)
@@ -272,12 +272,20 @@ static struct inode *ubifs_alloc_inode(struct super_block *sb)
        return &ui->vfs_inode;
 };
 
+static void ubifs_i_callback(struct rcu_head *head)
+{
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       struct ubifs_inode *ui = ubifs_inode(inode);
+       INIT_LIST_HEAD(&inode->i_dentry);
+       kmem_cache_free(ubifs_inode_slab, ui);
+}
+
 static void ubifs_destroy_inode(struct inode *inode)
 {
        struct ubifs_inode *ui = ubifs_inode(inode);
 
        kfree(ui->data);
-       kmem_cache_free(ubifs_inode_slab, inode);
+       call_rcu(&inode->i_rcu, ubifs_i_callback);
 }
 
 /*
index 4a5c7c6..b539d53 100644 (file)
@@ -139,11 +139,18 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void udf_destroy_inode(struct inode *inode)
+static void udf_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(udf_inode_cachep, UDF_I(inode));
 }
 
+static void udf_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, udf_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct udf_inode_info *ei = (struct udf_inode_info *)foo;
index 2c47dae..2c61ac5 100644 (file)
@@ -1412,11 +1412,18 @@ static struct inode *ufs_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void ufs_destroy_inode(struct inode *inode)
+static void ufs_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(ufs_inode_cachep, UFS_I(inode));
 }
 
+static void ufs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, ufs_i_callback);
+}
+
 static void init_once(void *foo)
 {
        struct ufs_inode_info *ei = (struct ufs_inode_info *) foo;
index b277186..39f4f80 100644 (file)
@@ -219,12 +219,13 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 }
 
 int
-xfs_check_acl(struct inode *inode, int mask)
+xfs_check_acl(struct inode *inode, int mask, unsigned int flags)
 {
-       struct xfs_inode *ip = XFS_I(inode);
+       struct xfs_inode *ip;
        struct posix_acl *acl;
        int error = -EAGAIN;
 
+       ip = XFS_I(inode);
        trace_xfs_check_acl(ip);
 
        /*
@@ -234,6 +235,12 @@ xfs_check_acl(struct inode *inode, int mask)
        if (!XFS_IFORK_Q(ip))
                return -EAGAIN;
 
+       if (flags & IPERM_FLAG_RCU) {
+               if (!negative_cached_acl(inode, ACL_TYPE_ACCESS))
+                       return -ECHILD;
+               return -EAGAIN;
+       }
+
        acl = xfs_get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
index 0135e2a..11dd720 100644 (file)
@@ -42,7 +42,7 @@ struct xfs_acl {
 #define SGI_ACL_DEFAULT_SIZE   (sizeof(SGI_ACL_DEFAULT)-1)
 
 #ifdef CONFIG_XFS_POSIX_ACL
-extern int xfs_check_acl(struct inode *inode, int mask);
+extern int xfs_check_acl(struct inode *inode, int mask, unsigned int flags);
 extern struct posix_acl *xfs_get_acl(struct inode *inode, int type);
 extern int xfs_inherit_acl(struct inode *inode, struct posix_acl *default_acl);
 extern int xfs_acl_chmod(struct inode *inode);
index 0cdd269..d7de5a3 100644 (file)
@@ -91,6 +91,17 @@ xfs_inode_alloc(
        return ip;
 }
 
+STATIC void
+xfs_inode_free_callback(
+       struct rcu_head         *head)
+{
+       struct inode            *inode = container_of(head, struct inode, i_rcu);
+       struct xfs_inode        *ip = XFS_I(inode);
+
+       INIT_LIST_HEAD(&inode->i_dentry);
+       kmem_zone_free(xfs_inode_zone, ip);
+}
+
 void
 xfs_inode_free(
        struct xfs_inode        *ip)
@@ -134,7 +145,7 @@ xfs_inode_free(
        ASSERT(!spin_is_locked(&ip->i_flags_lock));
        ASSERT(completion_done(&ip->i_flush));
 
-       kmem_zone_free(xfs_inode_zone, ip);
+       call_rcu(&ip->i_vnode.i_rcu, xfs_inode_free_callback);
 }
 
 /*
index 7113a32..e612575 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef __LINUX_BIT_SPINLOCK_H
 #define __LINUX_BIT_SPINLOCK_H
 
+#include <linux/kernel.h>
+#include <linux/preempt.h>
+#include <asm/atomic.h>
+
 /*
  *  bit-based spin_lock()
  *
index 2e914d0..4ccc59c 100644 (file)
@@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations;
 /* operations shared over more than one file */
 int coda_open(struct inode *i, struct file *f);
 int coda_release(struct inode *i, struct file *f);
-int coda_permission(struct inode *inode, int mask);
+int coda_permission(struct inode *inode, int mask, unsigned int flags);
 int coda_revalidate_inode(struct dentry *);
 int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 int coda_setattr(struct dentry *, struct iattr *);
index 6a4aea3..bd07758 100644 (file)
@@ -4,7 +4,9 @@
 #include <asm/atomic.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
+#include <linux/rculist_bl.h>
 #include <linux/spinlock.h>
+#include <linux/seqlock.h>
 #include <linux/cache.h>
 #include <linux/rcupdate.h>
 
@@ -45,6 +47,27 @@ struct dentry_stat_t {
 };
 extern struct dentry_stat_t dentry_stat;
 
+/*
+ * Compare 2 name strings, return 0 if they match, otherwise non-zero.
+ * The strings are both count bytes long, and count is non-zero.
+ */
+static inline int dentry_cmp(const unsigned char *cs, size_t scount,
+                               const unsigned char *ct, size_t tcount)
+{
+       int ret;
+       if (scount != tcount)
+               return 1;
+       do {
+               ret = (*cs != *ct);
+               if (ret)
+                       break;
+               cs++;
+               ct++;
+               tcount--;
+       } while (tcount);
+       return ret;
+}
+
 /* Name hashing routines. Initial hash value */
 /* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
 #define init_name_hash()               0
@@ -81,25 +104,33 @@ full_name_hash(const unsigned char *name, unsigned int len)
  * large memory footprint increase).
  */
 #ifdef CONFIG_64BIT
-#define DNAME_INLINE_LEN_MIN 32 /* 192 bytes */
+# define DNAME_INLINE_LEN 32 /* 192 bytes */
 #else
-#define DNAME_INLINE_LEN_MIN 40 /* 128 bytes */
+# ifdef CONFIG_SMP
+#  define DNAME_INLINE_LEN 36 /* 128 bytes */
+# else
+#  define DNAME_INLINE_LEN 40 /* 128 bytes */
+# endif
 #endif
 
 struct dentry {
-       atomic_t d_count;
+       /* RCU lookup touched fields */
        unsigned int d_flags;           /* protected by d_lock */
-       spinlock_t d_lock;              /* per dentry lock */
-       int d_mounted;
-       struct inode *d_inode;          /* Where the name belongs to - NULL is
-                                        * negative */
-       /*
-        * The next three fields are touched by __d_lookup.  Place them here
-        * so they all fit in a cache line.
-        */
-       struct hlist_node d_hash;       /* lookup hash list */
+       seqcount_t d_seq;               /* per dentry seqlock */
+       struct hlist_bl_node d_hash;    /* lookup hash list */
        struct dentry *d_parent;        /* parent directory */
        struct qstr d_name;
+       struct inode *d_inode;          /* Where the name belongs to - NULL is
+                                        * negative */
+       unsigned char d_iname[DNAME_INLINE_LEN];        /* small names */
+
+       /* Ref lookup also touches following */
+       unsigned int d_count;           /* protected by d_lock */
+       spinlock_t d_lock;              /* per dentry lock */
+       const struct dentry_operations *d_op;
+       struct super_block *d_sb;       /* The root of the dentry tree */
+       unsigned long d_time;           /* used by d_revalidate */
+       void *d_fsdata;                 /* fs-specific data */
 
        struct list_head d_lru;         /* LRU list */
        /*
@@ -111,12 +142,6 @@ struct dentry {
        } d_u;
        struct list_head d_subdirs;     /* our children */
        struct list_head d_alias;       /* inode alias list */
-       unsigned long d_time;           /* used by d_revalidate */
-       const struct dentry_operations *d_op;
-       struct super_block *d_sb;       /* The root of the dentry tree */
-       void *d_fsdata;                 /* fs-specific data */
-
-       unsigned char d_iname[DNAME_INLINE_LEN_MIN];    /* small names */
 };
 
 /*
@@ -133,96 +158,61 @@ enum dentry_d_lock_class
 
 struct dentry_operations {
        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_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
-};
-
-/* the dentry parameter passed to d_hash and d_compare is the parent
- * directory of the entries to be compared. It is used in case these
- * functions need any directory specific information for determining
- * equivalency classes.  Using the dentry itself might not work, as it
- * might be a negative dentry which has no information associated with
- * it */
+} ____cacheline_aligned;
 
 /*
-locking rules:
-               big lock        dcache_lock     d_lock   may block
-d_revalidate:  no              no              no       yes
-d_hash         no              no              no       yes
-d_compare:     no              yes             yes      no
-d_delete:      no              yes             no       no
-d_release:     no              no              no       yes
-d_iput:                no              no              no       yes
+ * Locking rules for dentry_operations callbacks are to be found in
+ * Documentation/filesystems/Locking. Keep it updated!
+ *
+ * FUrther descriptions are found in Documentation/filesystems/vfs.txt.
+ * Keep it updated too!
  */
 
 /* d_flags entries */
 #define DCACHE_AUTOFS_PENDING 0x0001    /* autofs: "under construction" */
-#define DCACHE_NFSFS_RENAMED  0x0002    /* this dentry has been "silly
-                                        * renamed" and has to be
-                                        * deleted on the last dput()
-                                        */
-#define        DCACHE_DISCONNECTED 0x0004
-     /* This dentry is possibly not currently connected to the dcache tree,
-      * in which case its parent will either be itself, or will have this
-      * flag as well.  nfsd will not use a dentry with this bit set, but will
-      * first endeavour to clear the bit either by discovering that it is
-      * connected, or by performing lookup operations.   Any filesystem which
-      * supports nfsd_operations MUST have a lookup function which, if it finds
-      * a directory inode with a DCACHE_DISCONNECTED dentry, will d_move
-      * that dentry into place and return that dentry rather than the passed one,
-      * typically using d_splice_alias.
-      */
+#define DCACHE_NFSFS_RENAMED  0x0002
+     /* this dentry has been "silly renamed" and has to be deleted on the last
+      * dput() */
+
+#define        DCACHE_DISCONNECTED     0x0004
+     /* This dentry is possibly not currently connected to the dcache tree, in
+      * which case its parent will either be itself, or will have this flag as
+      * well.  nfsd will not use a dentry with this bit set, but will first
+      * endeavour to clear the bit either by discovering that it is connected,
+      * or by performing lookup operations.   Any filesystem which supports
+      * nfsd_operations MUST have a lookup function which, if it finds a
+      * directory inode with a DCACHE_DISCONNECTED dentry, will d_move that
+      * dentry into place and return that dentry rather than the passed one,
+      * typically using d_splice_alias. */
 
 #define DCACHE_REFERENCED      0x0008  /* Recently used, don't discard. */
 #define DCACHE_UNHASHED                0x0010  
-
-#define DCACHE_INOTIFY_PARENT_WATCHED  0x0020 /* Parent inode is watched by inotify */
+#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020
+     /* Parent inode is watched by inotify */
 
 #define DCACHE_COOKIE          0x0040  /* For use by dcookie subsystem */
-
-#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */
+#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080
+     /* Parent inode is watched by some fsnotify listener */
 
 #define DCACHE_CANT_MOUNT      0x0100
+#define DCACHE_GENOCIDE                0x0200
+#define DCACHE_MOUNTED         0x0400  /* is a mountpoint */
 
-extern spinlock_t dcache_lock;
-extern seqlock_t rename_lock;
-
-/**
- * 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.
- */
-
-static inline void __d_drop(struct dentry *dentry)
-{
-       if (!(dentry->d_flags & DCACHE_UNHASHED)) {
-               dentry->d_flags |= DCACHE_UNHASHED;
-               hlist_del_rcu(&dentry->d_hash);
-       }
-}
+#define DCACHE_OP_HASH         0x1000
+#define DCACHE_OP_COMPARE      0x2000
+#define DCACHE_OP_REVALIDATE   0x4000
+#define DCACHE_OP_DELETE       0x8000
 
-static inline void d_drop(struct dentry *dentry)
-{
-       spin_lock(&dcache_lock);
-       spin_lock(&dentry->d_lock);
-       __d_drop(dentry);
-       spin_unlock(&dentry->d_lock);
-       spin_unlock(&dcache_lock);
-}
+extern seqlock_t rename_lock;
 
 static inline int dname_external(struct dentry *dentry)
 {
@@ -235,10 +225,14 @@ static inline int dname_external(struct dentry *dentry)
 extern void d_instantiate(struct dentry *, struct inode *);
 extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
 extern struct dentry * d_materialise_unique(struct dentry *, struct inode *);
+extern void __d_drop(struct dentry *dentry);
+extern void d_drop(struct dentry *dentry);
 extern void d_delete(struct dentry *);
+extern void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op);
 
 /* allocate/de-allocate */
 extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
+extern struct dentry * d_alloc_pseudo(struct super_block *, const struct qstr *);
 extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
 extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
 extern struct dentry * d_obtain_alias(struct inode *);
@@ -296,14 +290,40 @@ static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *in
        return res;
 }
 
+extern void dentry_update_name_case(struct dentry *, struct qstr *);
+
 /* used for rename() and baskets */
 extern void d_move(struct dentry *, struct dentry *);
 extern struct dentry *d_ancestor(struct dentry *, struct dentry *);
 
 /* appendix may either be NULL or be used for transname suffixes */
-extern struct dentry * d_lookup(struct dentry *, struct qstr *);
-extern struct dentry * __d_lookup(struct dentry *, struct qstr *);
-extern struct dentry * d_hash_and_lookup(struct dentry *, struct qstr *);
+extern struct dentry *d_lookup(struct dentry *, struct qstr *);
+extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
+extern struct dentry *__d_lookup(struct dentry *, struct qstr *);
+extern struct dentry *__d_lookup_rcu(struct dentry *parent, struct qstr *name,
+                               unsigned *seq, struct inode **inode);
+
+/**
+ * __d_rcu_to_refcount - take a refcount on dentry if sequence check is ok
+ * @dentry: dentry to take a ref on
+ * @seq: seqcount to verify against
+ * @Returns: 0 on failure, else 1.
+ *
+ * __d_rcu_to_refcount operates on a dentry,seq pair that was returned
+ * by __d_lookup_rcu, to get a reference on an rcu-walk dentry.
+ */
+static inline int __d_rcu_to_refcount(struct dentry *dentry, unsigned seq)
+{
+       int ret = 0;
+
+       assert_spin_locked(&dentry->d_lock);
+       if (!read_seqcount_retry(&dentry->d_seq, seq)) {
+               ret = 1;
+               dentry->d_count++;
+       }
+
+       return ret;
+}
 
 /* validate "insecure" dentry pointer */
 extern int d_validate(struct dentry *, struct dentry *);
@@ -316,34 +336,37 @@ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
 extern char *__d_path(const struct path *path, struct path *root, char *, int);
 extern char *d_path(const struct path *, char *, int);
 extern char *d_path_with_unreachable(const struct path *, char *, int);
-extern char *__dentry_path(struct dentry *, char *, int);
+extern char *dentry_path_raw(struct dentry *, char *, int);
 extern char *dentry_path(struct dentry *, char *, int);
 
 /* Allocation counts.. */
 
 /**
- *     dget, dget_locked       -       get a reference to a dentry
+ *     dget, dget_dlock -      get a reference to a dentry
  *     @dentry: dentry to get a reference to
  *
  *     Given a dentry or %NULL pointer increment the reference count
  *     if appropriate and return the dentry. A dentry will not be 
- *     destroyed when it has references. dget() should never be
- *     called for dentries with zero reference counter. For these cases
- *     (preferably none, functions in dcache.c are sufficient for normal
- *     needs and they take necessary precautions) you should hold dcache_lock
- *     and call dget_locked() instead of dget().
+ *     destroyed when it has references.
  */
+static inline struct dentry *dget_dlock(struct dentry *dentry)
+{
+       if (dentry)
+               dentry->d_count++;
+       return dentry;
+}
+
 static inline struct dentry *dget(struct dentry *dentry)
 {
        if (dentry) {
-               BUG_ON(!atomic_read(&dentry->d_count));
-               atomic_inc(&dentry->d_count);
+               spin_lock(&dentry->d_lock);
+               dget_dlock(dentry);
+               spin_unlock(&dentry->d_lock);
        }
        return dentry;
 }
 
-extern struct dentry * dget_locked(struct dentry *);
+extern struct dentry *dget_parent(struct dentry *dentry);
 
 /**
  *     d_unhashed -    is dentry hashed
@@ -374,21 +397,11 @@ static inline void dont_mount(struct dentry *dentry)
        spin_unlock(&dentry->d_lock);
 }
 
-static inline struct dentry *dget_parent(struct dentry *dentry)
-{
-       struct dentry *ret;
-
-       spin_lock(&dentry->d_lock);
-       ret = dget(dentry->d_parent);
-       spin_unlock(&dentry->d_lock);
-       return ret;
-}
-
 extern void dput(struct dentry *);
 
 static inline int d_mountpoint(struct dentry *dentry)
 {
-       return dentry->d_mounted;
+       return dentry->d_flags & DCACHE_MOUNTED;
 }
 
 extern struct vfsmount *lookup_mnt(struct path *);
index 090f0ea..baf3e55 100644 (file)
@@ -392,6 +392,7 @@ struct inodes_stat_t {
 #include <linux/capability.h>
 #include <linux/semaphore.h>
 #include <linux/fiemap.h>
+#include <linux/rculist_bl.h>
 
 #include <asm/atomic.h>
 #include <asm/byteorder.h>
@@ -733,16 +734,31 @@ struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
 
 struct inode {
+       /* RCU path lookup touches following: */
+       umode_t                 i_mode;
+       uid_t                   i_uid;
+       gid_t                   i_gid;
+       const struct inode_operations   *i_op;
+       struct super_block      *i_sb;
+
+       spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
+       unsigned int            i_flags;
+       struct mutex            i_mutex;
+
+       unsigned long           i_state;
+       unsigned long           dirtied_when;   /* jiffies of first dirtying */
+
        struct hlist_node       i_hash;
        struct list_head        i_wb_list;      /* backing dev IO list */
        struct list_head        i_lru;          /* inode LRU list */
        struct list_head        i_sb_list;
-       struct list_head        i_dentry;
+       union {
+               struct list_head        i_dentry;
+               struct rcu_head         i_rcu;
+       };
        unsigned long           i_ino;
        atomic_t                i_count;
        unsigned int            i_nlink;
-       uid_t                   i_uid;
-       gid_t                   i_gid;
        dev_t                   i_rdev;
        unsigned int            i_blkbits;
        u64                     i_version;
@@ -755,13 +771,8 @@ struct inode {
        struct timespec         i_ctime;
        blkcnt_t                i_blocks;
        unsigned short          i_bytes;
-       umode_t                 i_mode;
-       spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
-       struct mutex            i_mutex;
        struct rw_semaphore     i_alloc_sem;
-       const struct inode_operations   *i_op;
        const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
-       struct super_block      *i_sb;
        struct file_lock        *i_flock;
        struct address_space    *i_mapping;
        struct address_space    i_data;
@@ -782,11 +793,6 @@ struct inode {
        struct hlist_head       i_fsnotify_marks;
 #endif
 
-       unsigned long           i_state;
-       unsigned long           dirtied_when;   /* jiffies of first dirtying */
-
-       unsigned int            i_flags;
-
 #ifdef CONFIG_IMA
        /* protected by i_lock */
        unsigned int            i_readcount; /* struct files open RO */
@@ -1372,13 +1378,13 @@ struct super_block {
        const struct xattr_handler **s_xattr;
 
        struct list_head        s_inodes;       /* all inodes */
-       struct hlist_head       s_anon;         /* anonymous dentries for (nfs) exporting */
+       struct hlist_bl_head    s_anon;         /* anonymous dentries for (nfs) exporting */
 #ifdef CONFIG_SMP
        struct list_head __percpu *s_files;
 #else
        struct list_head        s_files;
 #endif
-       /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
+       /* s_dentry_lru, s_nr_dentry_unused protected by dcache.c lru locks */
        struct list_head        s_dentry_lru;   /* unused dentry lru */
        int                     s_nr_dentry_unused;     /* # of dentry on lru */
 
@@ -1545,9 +1551,18 @@ struct file_operations {
        int (*setlease)(struct file *, long, struct file_lock **);
 };
 
+#define IPERM_FLAG_RCU 0x0001
+
 struct inode_operations {
-       int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
        struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
+       void * (*follow_link) (struct dentry *, struct nameidata *);
+       int (*permission) (struct inode *, int, unsigned int);
+       int (*check_acl)(struct inode *, int, unsigned int);
+
+       int (*readlink) (struct dentry *, char __user *,int);
+       void (*put_link) (struct dentry *, struct nameidata *, void *);
+
+       int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
        int (*link) (struct dentry *,struct inode *,struct dentry *);
        int (*unlink) (struct inode *,struct dentry *);
        int (*symlink) (struct inode *,struct dentry *,const char *);
@@ -1556,12 +1571,7 @@ struct inode_operations {
        int (*mknod) (struct inode *,struct dentry *,int,dev_t);
        int (*rename) (struct inode *, struct dentry *,
                        struct inode *, struct dentry *);
-       int (*readlink) (struct dentry *, char __user *,int);
-       void * (*follow_link) (struct dentry *, struct nameidata *);
-       void (*put_link) (struct dentry *, struct nameidata *, void *);
        void (*truncate) (struct inode *);
-       int (*permission) (struct inode *, int);
-       int (*check_acl)(struct inode *, int);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -1573,7 +1583,7 @@ struct inode_operations {
                          loff_t len);
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
                      u64 len);
-};
+} ____cacheline_aligned;
 
 struct seq_file;
 
@@ -2158,8 +2168,8 @@ extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct iattr *);
 extern int inode_permission(struct inode *, int);
-extern int generic_permission(struct inode *, int,
-               int (*check_acl)(struct inode *, int));
+extern int generic_permission(struct inode *, int, unsigned int,
+               int (*check_acl)(struct inode *, int, unsigned int));
 
 static inline bool execute_ok(struct inode *inode)
 {
@@ -2230,6 +2240,7 @@ extern void iget_failed(struct inode *);
 extern void end_writeback(struct inode *);
 extern void __destroy_inode(struct inode *);
 extern struct inode *new_inode(struct super_block *);
+extern void free_inode_nonrcu(struct inode *inode);
 extern int should_remove_suid(struct dentry *);
 extern int file_remove_suid(struct file *);
 
@@ -2446,6 +2457,10 @@ static inline ino_t parent_ino(struct dentry *dentry)
 {
        ino_t res;
 
+       /*
+        * Don't strictly need d_lock here? If the parent ino could change
+        * then surely we'd have a deeper race in the caller?
+        */
        spin_lock(&dentry->d_lock);
        res = dentry->d_parent->d_inode->i_ino;
        spin_unlock(&dentry->d_lock);
index a42b5bf..003dc0f 100644 (file)
@@ -2,10 +2,13 @@
 #define _LINUX_FS_STRUCT_H
 
 #include <linux/path.h>
+#include <linux/spinlock.h>
+#include <linux/seqlock.h>
 
 struct fs_struct {
        int users;
        spinlock_t lock;
+       seqcount_t seq;
        int umask;
        int in_exec;
        struct path root, pwd;
index b10bcde..2a53f10 100644 (file)
@@ -17,7 +17,6 @@
 
 /*
  * fsnotify_d_instantiate - instantiate a dentry for inode
- * Called with dcache_lock held.
  */
 static inline void fsnotify_d_instantiate(struct dentry *dentry,
                                          struct inode *inode)
@@ -62,7 +61,6 @@ static inline int fsnotify_perm(struct file *file, int mask)
 
 /*
  * fsnotify_d_move - dentry has been moved
- * Called with dcache_lock and dentry->d_lock held.
  */
 static inline void fsnotify_d_move(struct dentry *dentry)
 {
index 7380763..69ad89b 100644 (file)
@@ -329,9 +329,15 @@ static inline void __fsnotify_update_dcache_flags(struct dentry *dentry)
 {
        struct dentry *parent;
 
-       assert_spin_locked(&dcache_lock);
        assert_spin_locked(&dentry->d_lock);
 
+       /*
+        * Serialisation of setting PARENT_WATCHED on the dentries is provided
+        * by d_lock. If inotify_inode_watched changes after we have taken
+        * d_lock, the following __fsnotify_update_child_dentry_flags call will
+        * find our entry, so it will spin until we complete here, and update
+        * us with the new state.
+        */
        parent = dentry->d_parent;
        if (parent->d_inode && fsnotify_inode_watches_children(parent->d_inode))
                dentry->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
@@ -341,15 +347,12 @@ static inline void __fsnotify_update_dcache_flags(struct dentry *dentry)
 
 /*
  * fsnotify_d_instantiate - instantiate a dentry for inode
- * Called with dcache_lock held.
  */
 static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode *inode)
 {
        if (!inode)
                return;
 
-       assert_spin_locked(&dcache_lock);
-
        spin_lock(&dentry->d_lock);
        __fsnotify_update_dcache_flags(dentry);
        spin_unlock(&dentry->d_lock);
index 574bea4..0437e37 100644 (file)
@@ -10,6 +10,6 @@ extern const struct xattr_handler generic_acl_default_handler;
 
 int generic_acl_init(struct inode *, struct inode *);
 int generic_acl_chmod(struct inode *);
-int generic_check_acl(struct inode *inode, int mask);
+int generic_check_acl(struct inode *inode, int mask, unsigned int flags);
 
 #endif /* LINUX_GENERIC_ACL_H */
diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h
new file mode 100644 (file)
index 0000000..9ee97e7
--- /dev/null
@@ -0,0 +1,144 @@
+#ifndef _LINUX_LIST_BL_H
+#define _LINUX_LIST_BL_H
+
+#include <linux/list.h>
+
+/*
+ * Special version of lists, where head of the list has a lock in the lowest
+ * bit. This is useful for scalable hash tables without increasing memory
+ * footprint overhead.
+ *
+ * For modification operations, the 0 bit of hlist_bl_head->first
+ * pointer must be set.
+ *
+ * With some small modifications, this can easily be adapted to store several
+ * arbitrary bits (not just a single lock bit), if the need arises to store
+ * some fast and compact auxiliary data.
+ */
+
+#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
+#define LIST_BL_LOCKMASK       1UL
+#else
+#define LIST_BL_LOCKMASK       0UL
+#endif
+
+#ifdef CONFIG_DEBUG_LIST
+#define LIST_BL_BUG_ON(x) BUG_ON(x)
+#else
+#define LIST_BL_BUG_ON(x)
+#endif
+
+
+struct hlist_bl_head {
+       struct hlist_bl_node *first;
+};
+
+struct hlist_bl_node {
+       struct hlist_bl_node *next, **pprev;
+};
+#define INIT_HLIST_BL_HEAD(ptr) \
+       ((ptr)->first = NULL)
+
+static inline void INIT_HLIST_BL_NODE(struct hlist_bl_node *h)
+{
+       h->next = NULL;
+       h->pprev = NULL;
+}
+
+#define hlist_bl_entry(ptr, type, member) container_of(ptr,type,member)
+
+static inline int hlist_bl_unhashed(const struct hlist_bl_node *h)
+{
+       return !h->pprev;
+}
+
+static inline struct hlist_bl_node *hlist_bl_first(struct hlist_bl_head *h)
+{
+       return (struct hlist_bl_node *)
+               ((unsigned long)h->first & ~LIST_BL_LOCKMASK);
+}
+
+static inline void hlist_bl_set_first(struct hlist_bl_head *h,
+                                       struct hlist_bl_node *n)
+{
+       LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
+       LIST_BL_BUG_ON(!((unsigned long)h->first & LIST_BL_LOCKMASK));
+       h->first = (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK);
+}
+
+static inline int hlist_bl_empty(const struct hlist_bl_head *h)
+{
+       return !((unsigned long)h->first & ~LIST_BL_LOCKMASK);
+}
+
+static inline void hlist_bl_add_head(struct hlist_bl_node *n,
+                                       struct hlist_bl_head *h)
+{
+       struct hlist_bl_node *first = hlist_bl_first(h);
+
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       n->pprev = &h->first;
+       hlist_bl_set_first(h, n);
+}
+
+static inline void __hlist_bl_del(struct hlist_bl_node *n)
+{
+       struct hlist_bl_node *next = n->next;
+       struct hlist_bl_node **pprev = n->pprev;
+
+       LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
+
+       /* pprev may be `first`, so be careful not to lose the lock bit */
+       *pprev = (struct hlist_bl_node *)
+                       ((unsigned long)next |
+                        ((unsigned long)*pprev & LIST_BL_LOCKMASK));
+       if (next)
+               next->pprev = pprev;
+}
+
+static inline void hlist_bl_del(struct hlist_bl_node *n)
+{
+       __hlist_bl_del(n);
+       n->next = LIST_POISON1;
+       n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_bl_del_init(struct hlist_bl_node *n)
+{
+       if (!hlist_bl_unhashed(n)) {
+               __hlist_bl_del(n);
+               INIT_HLIST_BL_NODE(n);
+       }
+}
+
+/**
+ * hlist_bl_for_each_entry     - iterate over list of given type
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ *
+ */
+#define hlist_bl_for_each_entry(tpos, pos, head, member)               \
+       for (pos = hlist_bl_first(head);                                \
+            pos &&                                                     \
+               ({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_bl_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @n:         another &struct hlist_node to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_bl_for_each_entry_safe(tpos, pos, n, head, member)        \
+       for (pos = hlist_bl_first(head);                                 \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+#endif
index 5e7a594..1869ea2 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/list.h>
 #include <linux/nodemask.h>
 #include <linux/spinlock.h>
+#include <linux/seqlock.h>
 #include <asm/atomic.h>
 
 struct super_block;
@@ -46,12 +47,24 @@ struct mnt_namespace;
 
 #define MNT_INTERNAL   0x4000
 
+struct mnt_pcp {
+       int mnt_count;
+       int mnt_writers;
+};
+
 struct vfsmount {
        struct list_head mnt_hash;
        struct vfsmount *mnt_parent;    /* fs we are mounted on */
        struct dentry *mnt_mountpoint;  /* dentry of mountpoint */
        struct dentry *mnt_root;        /* root of the mounted tree */
        struct super_block *mnt_sb;     /* pointer to superblock */
+#ifdef CONFIG_SMP
+       struct mnt_pcp __percpu *mnt_pcp;
+       atomic_t mnt_longrefs;
+#else
+       int mnt_count;
+       int mnt_writers;
+#endif
        struct list_head mnt_mounts;    /* list of children, anchored here */
        struct list_head mnt_child;     /* and going through their mnt_child */
        int mnt_flags;
@@ -70,57 +83,25 @@ struct vfsmount {
        struct mnt_namespace *mnt_ns;   /* containing namespace */
        int mnt_id;                     /* mount identifier */
        int mnt_group_id;               /* peer group identifier */
-       /*
-        * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
-        * to let these frequently modified fields in a separate cache line
-        * (so that reads of mnt_flags wont ping-pong on SMP machines)
-        */
-       atomic_t mnt_count;
        int mnt_expiry_mark;            /* true if marked for expiry */
        int mnt_pinned;
        int mnt_ghosts;
-#ifdef CONFIG_SMP
-       int __percpu *mnt_writers;
-#else
-       int mnt_writers;
-#endif
 };
 
-static inline int *get_mnt_writers_ptr(struct vfsmount *mnt)
-{
-#ifdef CONFIG_SMP
-       return mnt->mnt_writers;
-#else
-       return &mnt->mnt_writers;
-#endif
-}
-
-static inline struct vfsmount *mntget(struct vfsmount *mnt)
-{
-       if (mnt)
-               atomic_inc(&mnt->mnt_count);
-       return mnt;
-}
-
 struct file; /* forward dec */
 
 extern int mnt_want_write(struct vfsmount *mnt);
 extern int mnt_want_write_file(struct file *file);
 extern int mnt_clone_write(struct vfsmount *mnt);
 extern void mnt_drop_write(struct vfsmount *mnt);
-extern void mntput_no_expire(struct vfsmount *mnt);
+extern void mntput(struct vfsmount *mnt);
+extern struct vfsmount *mntget(struct vfsmount *mnt);
+extern void mntput_long(struct vfsmount *mnt);
+extern struct vfsmount *mntget_long(struct vfsmount *mnt);
 extern void mnt_pin(struct vfsmount *mnt);
 extern void mnt_unpin(struct vfsmount *mnt);
 extern int __mnt_is_readonly(struct vfsmount *mnt);
 
-static inline void mntput(struct vfsmount *mnt)
-{
-       if (mnt) {
-               mnt->mnt_expiry_mark = 0;
-               mntput_no_expire(mnt);
-       }
-}
-
 extern struct vfsmount *do_kern_mount(const char *fstype, int flags,
                                      const char *name, void *data);
 
index 05b441d..18d06ad 100644 (file)
@@ -19,7 +19,10 @@ struct nameidata {
        struct path     path;
        struct qstr     last;
        struct path     root;
+       struct file     *file;
+       struct inode    *inode; /* path.dentry.d_inode */
        unsigned int    flags;
+       unsigned        seq;
        int             last_type;
        unsigned        depth;
        char *saved_names[MAX_NESTED_LINKS + 1];
@@ -41,14 +44,15 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
  *  - require a directory
  *  - ending slashes ok even for nonexistent files
  *  - internal "there are more path components" flag
- *  - locked when lookup done with dcache_lock held
  *  - dentry cache is untrusted; force a real lookup
  */
-#define LOOKUP_FOLLOW           1
-#define LOOKUP_DIRECTORY        2
-#define LOOKUP_CONTINUE                 4
-#define LOOKUP_PARENT          16
-#define LOOKUP_REVAL           64
+#define LOOKUP_FOLLOW          0x0001
+#define LOOKUP_DIRECTORY       0x0002
+#define LOOKUP_CONTINUE                0x0004
+
+#define LOOKUP_PARENT          0x0010
+#define LOOKUP_REVAL           0x0020
+#define LOOKUP_RCU             0x0040
 /*
  * Intent data
  */
index ef66306..1c27f20 100644 (file)
@@ -184,13 +184,13 @@ struct ncp_entry_info {
        __u8                    file_handle[6];
 };
 
-static inline struct ncp_server *NCP_SBP(struct super_block *sb)
+static inline struct ncp_server *NCP_SBP(const struct super_block *sb)
 {
        return sb->s_fs_info;
 }
 
 #define NCP_SERVER(inode)      NCP_SBP((inode)->i_sb)
-static inline struct ncp_inode_info *NCP_FINFO(struct inode *inode)
+static inline struct ncp_inode_info *NCP_FINFO(const struct inode *inode)
 {
        return container_of(inode, struct ncp_inode_info, vfs_inode);
 }
index 29d504d..0779bb8 100644 (file)
@@ -351,7 +351,7 @@ extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
-extern int nfs_permission(struct inode *, int);
+extern int nfs_permission(struct inode *, int, unsigned int);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int nfs_attribute_timeout(struct inode *inode);
index edc98de..a581e8c 100644 (file)
@@ -10,7 +10,9 @@ struct path {
 };
 
 extern void path_get(struct path *);
+extern void path_get_long(struct path *);
 extern void path_put(struct path *);
+extern void path_put_long(struct path *);
 
 static inline int path_equal(const struct path *path1, const struct path *path2)
 {
index 6760816..d68283a 100644 (file)
@@ -108,6 +108,25 @@ static inline struct posix_acl *get_cached_acl(struct inode *inode, int type)
        return acl;
 }
 
+static inline int negative_cached_acl(struct inode *inode, int type)
+{
+       struct posix_acl **p, *acl;
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               p = &inode->i_acl;
+               break;
+       case ACL_TYPE_DEFAULT:
+               p = &inode->i_default_acl;
+               break;
+       default:
+               BUG();
+       }
+       acl = ACCESS_ONCE(*p);
+       if (acl)
+               return 0;
+       return 1;
+}
+
 static inline void set_cached_acl(struct inode *inode,
                                  int type,
                                  struct posix_acl *acl)
diff --git a/include/linux/rculist_bl.h b/include/linux/rculist_bl.h
new file mode 100644 (file)
index 0000000..b872b49
--- /dev/null
@@ -0,0 +1,127 @@
+#ifndef _LINUX_RCULIST_BL_H
+#define _LINUX_RCULIST_BL_H
+
+/*
+ * RCU-protected bl list version. See include/linux/list_bl.h.
+ */
+#include <linux/list_bl.h>
+#include <linux/rcupdate.h>
+
+static inline void hlist_bl_set_first_rcu(struct hlist_bl_head *h,
+                                       struct hlist_bl_node *n)
+{
+       LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
+       LIST_BL_BUG_ON(!((unsigned long)h->first & LIST_BL_LOCKMASK));
+       rcu_assign_pointer(h->first,
+               (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK));
+}
+
+static inline struct hlist_bl_node *hlist_bl_first_rcu(struct hlist_bl_head *h)
+{
+       return (struct hlist_bl_node *)
+               ((unsigned long)rcu_dereference(h->first) & ~LIST_BL_LOCKMASK);
+}
+
+/**
+ * hlist_bl_del_init_rcu - deletes entry from hash list with re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: hlist_bl_unhashed() on the node returns true after this. It is
+ * useful for RCU based read lockfree traversal if the writer side
+ * must know if the list entry is still hashed or already unhashed.
+ *
+ * In particular, it means that we can not poison the forward pointers
+ * that may still be used for walking the hash list and we can only
+ * zero the pprev pointer so list_unhashed() will return true after
+ * this.
+ *
+ * The caller must take whatever precautions are necessary (such as
+ * holding appropriate locks) to avoid racing with another
+ * list-mutation primitive, such as hlist_bl_add_head_rcu() or
+ * hlist_bl_del_rcu(), running on this same list.  However, it is
+ * perfectly legal to run concurrently with the _rcu list-traversal
+ * primitives, such as hlist_bl_for_each_entry_rcu().
+ */
+static inline void hlist_bl_del_init_rcu(struct hlist_bl_node *n)
+{
+       if (!hlist_bl_unhashed(n)) {
+               __hlist_bl_del(n);
+               n->pprev = NULL;
+       }
+}
+
+/**
+ * hlist_bl_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: hlist_bl_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_bl_add_head_rcu()
+ * or hlist_bl_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_bl_for_each_entry().
+ */
+static inline void hlist_bl_del_rcu(struct hlist_bl_node *n)
+{
+       __hlist_bl_del(n);
+       n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_bl_add_head_rcu
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * Description:
+ * Adds the specified element to the specified hlist_bl,
+ * while permitting racing traversals.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_bl_add_head_rcu()
+ * or hlist_bl_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_bl_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.  Regardless of the type of CPU, the
+ * list-traversal primitive must be guarded by rcu_read_lock().
+ */
+static inline void hlist_bl_add_head_rcu(struct hlist_bl_node *n,
+                                       struct hlist_bl_head *h)
+{
+       struct hlist_bl_node *first;
+
+       /* don't need hlist_bl_first_rcu because we're under lock */
+       first = hlist_bl_first(h);
+
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       n->pprev = &h->first;
+
+       /* need _rcu because we can have concurrent lock free readers */
+       hlist_bl_set_first_rcu(h, n);
+}
+/**
+ * hlist_bl_for_each_entry_rcu - iterate over rcu list of given type
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_bl_node to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_bl_node within the struct.
+ *
+ */
+#define hlist_bl_for_each_entry_rcu(tpos, pos, head, member)           \
+       for (pos = hlist_bl_first_rcu(head);                            \
+               pos &&                                                  \
+               ({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1; }); \
+               pos = rcu_dereference_raw(pos->next))
+
+#endif
index b2cf208..3b94c91 100644 (file)
@@ -41,7 +41,7 @@ int reiserfs_xattr_init(struct super_block *sb, int mount_flags);
 int reiserfs_lookup_privroot(struct super_block *sb);
 int reiserfs_delete_xattrs(struct inode *inode);
 int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs);
-int reiserfs_permission(struct inode *inode, int mask);
+int reiserfs_permission(struct inode *inode, int mask, unsigned int flags);
 
 #ifdef CONFIG_REISERFS_FS_XATTR
 #define has_xattr_dir(inode) (REISERFS_I(inode)->i_flags & i_has_xattr_dir)
index d47a4c2..1ac4247 100644 (file)
@@ -457,7 +457,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     called when the actual read/write operations are performed.
  *     @inode contains the inode structure to check.
  *     @mask contains the permission mask.
- *     @nd contains the nameidata (may be NULL).
  *     Return 0 if permission is granted.
  * @inode_setattr:
  *     Check permission before setting file attributes.  Note that the kernel
@@ -1713,6 +1712,7 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
 int security_inode_readlink(struct dentry *dentry);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
 int security_inode_permission(struct inode *inode, int mask);
+int security_inode_exec_permission(struct inode *inode, unsigned int flags);
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 int security_inode_setxattr(struct dentry *dentry, const char *name,
@@ -2102,6 +2102,12 @@ static inline int security_inode_permission(struct inode *inode, int mask)
        return 0;
 }
 
+static inline int security_inode_exec_permission(struct inode *inode,
+                                                 unsigned int flags)
+{
+       return 0;
+}
+
 static inline int security_inode_setattr(struct dentry *dentry,
                                          struct iattr *attr)
 {
index 632205c..e98cd2e 100644 (file)
@@ -107,7 +107,7 @@ static __always_inline int read_seqretry(const seqlock_t *sl, unsigned start)
 {
        smp_rmb();
 
-       return (sl->sequence != start);
+       return unlikely(sl->sequence != start);
 }
 
 
@@ -125,14 +125,25 @@ typedef struct seqcount {
 #define SEQCNT_ZERO { 0 }
 #define seqcount_init(x)       do { *(x) = (seqcount_t) SEQCNT_ZERO; } while (0)
 
-/* Start of read using pointer to a sequence counter only.  */
-static inline unsigned read_seqcount_begin(const seqcount_t *s)
+/**
+ * __read_seqcount_begin - begin a seq-read critical section (without barrier)
+ * @s: pointer to seqcount_t
+ * Returns: count to be passed to read_seqcount_retry
+ *
+ * __read_seqcount_begin is like read_seqcount_begin, but has no smp_rmb()
+ * barrier. Callers should ensure that smp_rmb() or equivalent ordering is
+ * provided before actually loading any of the variables that are to be
+ * protected in this critical section.
+ *
+ * Use carefully, only in critical code, and comment how the barrier is
+ * provided.
+ */
+static inline unsigned __read_seqcount_begin(const seqcount_t *s)
 {
        unsigned ret;
 
 repeat:
        ret = s->sequence;
-       smp_rmb();
        if (unlikely(ret & 1)) {
                cpu_relax();
                goto repeat;
@@ -140,14 +151,56 @@ repeat:
        return ret;
 }
 
-/*
- * Test if reader processed invalid data because sequence number has changed.
+/**
+ * read_seqcount_begin - begin a seq-read critical section
+ * @s: pointer to seqcount_t
+ * Returns: count to be passed to read_seqcount_retry
+ *
+ * read_seqcount_begin opens a read critical section of the given seqcount.
+ * Validity of the critical section is tested by checking read_seqcount_retry
+ * function.
+ */
+static inline unsigned read_seqcount_begin(const seqcount_t *s)
+{
+       unsigned ret = __read_seqcount_begin(s);
+       smp_rmb();
+       return ret;
+}
+
+/**
+ * __read_seqcount_retry - end a seq-read critical section (without barrier)
+ * @s: pointer to seqcount_t
+ * @start: count, from read_seqcount_begin
+ * Returns: 1 if retry is required, else 0
+ *
+ * __read_seqcount_retry is like read_seqcount_retry, but has no smp_rmb()
+ * barrier. Callers should ensure that smp_rmb() or equivalent ordering is
+ * provided before actually loading any of the variables that are to be
+ * protected in this critical section.
+ *
+ * Use carefully, only in critical code, and comment how the barrier is
+ * provided.
+ */
+static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
+{
+       return unlikely(s->sequence != start);
+}
+
+/**
+ * read_seqcount_retry - end a seq-read critical section
+ * @s: pointer to seqcount_t
+ * @start: count, from read_seqcount_begin
+ * Returns: 1 if retry is required, else 0
+ *
+ * read_seqcount_retry closes a read critical section of the given seqcount.
+ * If the critical section was invalid, it must be ignored (and typically
+ * retried).
  */
 static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
 {
        smp_rmb();
 
-       return s->sequence != start;
+       return __read_seqcount_retry(s, start);
 }
 
 
@@ -167,6 +220,19 @@ static inline void write_seqcount_end(seqcount_t *s)
        s->sequence++;
 }
 
+/**
+ * write_seqcount_barrier - invalidate in-progress read-side seq operations
+ * @s: pointer to seqcount_t
+ *
+ * After write_seqcount_barrier, no read-side seq operations will complete
+ * successfully and see data older than this.
+ */
+static inline void write_seqcount_barrier(seqcount_t *s)
+{
+       smp_wmb();
+       s->sequence+=2;
+}
+
 /*
  * Possible sw/hw IRQ protected versions of the interfaces.
  */
index 59260e2..fa90866 100644 (file)
@@ -106,8 +106,6 @@ int kmem_cache_shrink(struct kmem_cache *);
 void kmem_cache_free(struct kmem_cache *, void *);
 unsigned int kmem_cache_size(struct kmem_cache *);
 const char *kmem_cache_name(struct kmem_cache *);
-int kern_ptr_validate(const void *ptr, unsigned long size);
-int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr);
 
 /*
  * Please use this macro to create slab caches. Simply specify the
index 035f439..14fb6d6 100644 (file)
@@ -237,11 +237,18 @@ static struct inode *mqueue_alloc_inode(struct super_block *sb)
        return &ei->vfs_inode;
 }
 
-static void mqueue_destroy_inode(struct inode *inode)
+static void mqueue_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(mqueue_inode_cachep, MQUEUE_I(inode));
 }
 
+static void mqueue_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, mqueue_i_callback);
+}
+
 static void mqueue_evict_inode(struct inode *inode)
 {
        struct mqueue_inode_info *info;
index 66a416b..51cddc1 100644 (file)
@@ -763,6 +763,8 @@ EXPORT_SYMBOL_GPL(cgroup_unlock);
  * -> cgroup_mkdir.
  */
 
+static struct dentry *cgroup_lookup(struct inode *dir,
+                       struct dentry *dentry, struct nameidata *nd);
 static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, int mode);
 static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry);
 static int cgroup_populate_dir(struct cgroup *cgrp);
@@ -874,25 +876,29 @@ static void cgroup_clear_directory(struct dentry *dentry)
        struct list_head *node;
 
        BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
-       spin_lock(&dcache_lock);
+       spin_lock(&dentry->d_lock);
        node = dentry->d_subdirs.next;
        while (node != &dentry->d_subdirs) {
                struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
+
+               spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
                list_del_init(node);
                if (d->d_inode) {
                        /* This should never be called on a cgroup
                         * directory with child cgroups */
                        BUG_ON(d->d_inode->i_mode & S_IFDIR);
-                       d = dget_locked(d);
-                       spin_unlock(&dcache_lock);
+                       dget_dlock(d);
+                       spin_unlock(&d->d_lock);
+                       spin_unlock(&dentry->d_lock);
                        d_delete(d);
                        simple_unlink(dentry->d_inode, d);
                        dput(d);
-                       spin_lock(&dcache_lock);
-               }
+                       spin_lock(&dentry->d_lock);
+               } else
+                       spin_unlock(&d->d_lock);
                node = dentry->d_subdirs.next;
        }
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
 }
 
 /*
@@ -900,11 +906,16 @@ static void cgroup_clear_directory(struct dentry *dentry)
  */
 static void cgroup_d_remove_dir(struct dentry *dentry)
 {
+       struct dentry *parent;
+
        cgroup_clear_directory(dentry);
 
-       spin_lock(&dcache_lock);
+       parent = dentry->d_parent;
+       spin_lock(&parent->d_lock);
+       spin_lock(&dentry->d_lock);
        list_del_init(&dentry->d_u.d_child);
-       spin_unlock(&dcache_lock);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&parent->d_lock);
        remove_dir(dentry);
 }
 
@@ -2180,7 +2191,7 @@ static const struct file_operations cgroup_file_operations = {
 };
 
 static const struct inode_operations cgroup_dir_inode_operations = {
-       .lookup = simple_lookup,
+       .lookup = cgroup_lookup,
        .mkdir = cgroup_mkdir,
        .rmdir = cgroup_rmdir,
        .rename = cgroup_rename,
@@ -2196,13 +2207,29 @@ static inline struct cftype *__file_cft(struct file *file)
        return __d_cft(file->f_dentry);
 }
 
-static int cgroup_create_file(struct dentry *dentry, mode_t mode,
-                               struct super_block *sb)
+static int cgroup_delete_dentry(const struct dentry *dentry)
+{
+       return 1;
+}
+
+static struct dentry *cgroup_lookup(struct inode *dir,
+                       struct dentry *dentry, struct nameidata *nd)
 {
-       static const struct dentry_operations cgroup_dops = {
+       static const struct dentry_operations cgroup_dentry_operations = {
+               .d_delete = cgroup_delete_dentry,
                .d_iput = cgroup_diput,
        };
 
+       if (dentry->d_name.len > NAME_MAX)
+               return ERR_PTR(-ENAMETOOLONG);
+       d_set_d_op(dentry, &cgroup_dentry_operations);
+       d_add(dentry, NULL);
+       return NULL;
+}
+
+static int cgroup_create_file(struct dentry *dentry, mode_t mode,
+                               struct super_block *sb)
+{
        struct inode *inode;
 
        if (!dentry)
@@ -2228,7 +2255,6 @@ static int cgroup_create_file(struct dentry *dentry, mode_t mode,
                inode->i_size = 0;
                inode->i_fop = &cgroup_file_operations;
        }
-       dentry->d_op = &cgroup_dops;
        d_instantiate(dentry, inode);
        dget(dentry);   /* Extra count - pin the dentry in core */
        return 0;
@@ -3638,9 +3664,7 @@ again:
        list_del(&cgrp->sibling);
        cgroup_unlock_hierarchy(cgrp->root);
 
-       spin_lock(&cgrp->dentry->d_lock);
        d = dget(cgrp->dentry);
-       spin_unlock(&d->d_lock);
 
        cgroup_d_remove_dir(d);
        dput(d);
index 6b9aee2..ca38939 100644 (file)
  *    ->inode_lock             (zap_pte_range->set_page_dirty)
  *    ->private_lock           (zap_pte_range->__set_page_dirty_buffers)
  *
- *  ->task->proc_lock
- *    ->dcache_lock            (proc_pid_lookup)
- *
  *  (code doesn't rely on that order, so you could switch it around)
  *  ->tasklist_lock             (memory_failure, collect_procs_ao)
  *    ->i_mmap_lock
index 47fdeeb..5ee67c9 100644 (file)
@@ -2415,13 +2415,20 @@ static struct inode *shmem_alloc_inode(struct super_block *sb)
        return &p->vfs_inode;
 }
 
+static void shmem_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(shmem_inode_cachep, SHMEM_I(inode));
+}
+
 static void shmem_destroy_inode(struct inode *inode)
 {
        if ((inode->i_mode & S_IFMT) == S_IFREG) {
                /* only struct inode is valid if it's an inline symlink */
                mpol_free_shared_policy(&SHMEM_I(inode)->policy);
        }
-       kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
+       call_rcu(&inode->i_rcu, shmem_i_callback);
 }
 
 static void init_once(void *foo)
index b1e40da..6107f23 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2781,7 +2781,7 @@ static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
 /*
  * Map pages beginning at addr to the given cache and slab. This is required
  * for the slab allocator to be able to lookup the cache and slab of a
- * virtual address for kfree, ksize, kmem_ptr_validate, and slab debugging.
+ * virtual address for kfree, ksize, and slab debugging.
  */
 static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
                           void *addr)
@@ -3660,36 +3660,6 @@ void *kmem_cache_alloc_notrace(struct kmem_cache *cachep, gfp_t flags)
 EXPORT_SYMBOL(kmem_cache_alloc_notrace);
 #endif
 
-/**
- * kmem_ptr_validate - check if an untrusted pointer might be a slab entry.
- * @cachep: the cache we're checking against
- * @ptr: pointer to validate
- *
- * This verifies that the untrusted pointer looks sane;
- * it is _not_ a guarantee that the pointer is actually
- * part of the slab cache in question, but it at least
- * validates that the pointer can be dereferenced and
- * looks half-way sane.
- *
- * Currently only used for dentry validation.
- */
-int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr)
-{
-       unsigned long size = cachep->buffer_size;
-       struct page *page;
-
-       if (unlikely(!kern_ptr_validate(ptr, size)))
-               goto out;
-       page = virt_to_page(ptr);
-       if (unlikely(!PageSlab(page)))
-               goto out;
-       if (unlikely(page_get_cache(page) != cachep))
-               goto out;
-       return 1;
-out:
-       return 0;
-}
-
 #ifdef CONFIG_NUMA
 void *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid)
 {
index 617b6d6..3588eaa 100644 (file)
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -678,11 +678,6 @@ int kmem_cache_shrink(struct kmem_cache *d)
 }
 EXPORT_SYMBOL(kmem_cache_shrink);
 
-int kmem_ptr_validate(struct kmem_cache *a, const void *b)
-{
-       return 0;
-}
-
 static unsigned int slob_ready __read_mostly;
 
 int slab_is_available(void)
index bec0e35..a2fe172 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1917,17 +1917,6 @@ void kmem_cache_free(struct kmem_cache *s, void *x)
 }
 EXPORT_SYMBOL(kmem_cache_free);
 
-/* Figure out on which slab page the object resides */
-static struct page *get_object_page(const void *x)
-{
-       struct page *page = virt_to_head_page(x);
-
-       if (!PageSlab(page))
-               return NULL;
-
-       return page;
-}
-
 /*
  * Object placement in a slab is made very easy because we always start at
  * offset 0. If we tune the size of the object to the alignment then we can
@@ -2386,35 +2375,6 @@ error:
 }
 
 /*
- * Check if a given pointer is valid
- */
-int kmem_ptr_validate(struct kmem_cache *s, const void *object)
-{
-       struct page *page;
-
-       if (!kern_ptr_validate(object, s->size))
-               return 0;
-
-       page = get_object_page(object);
-
-       if (!page || s != page->slab)
-               /* No slab or wrong slab */
-               return 0;
-
-       if (!check_valid_pointer(s, page, object))
-               return 0;
-
-       /*
-        * We could also check if the object is on the slabs freelist.
-        * But this would be too expensive and it seems that the main
-        * purpose of kmem_ptr_valid() is to check if the object belongs
-        * to a certain slab.
-        */
-       return 1;
-}
-EXPORT_SYMBOL(kmem_ptr_validate);
-
-/*
  * Determine the size of a slab object
  */
 unsigned int kmem_cache_size(struct kmem_cache *s)
index 73dac81..f126975 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -186,27 +186,6 @@ void kzfree(const void *p)
 }
 EXPORT_SYMBOL(kzfree);
 
-int kern_ptr_validate(const void *ptr, unsigned long size)
-{
-       unsigned long addr = (unsigned long)ptr;
-       unsigned long min_addr = PAGE_OFFSET;
-       unsigned long align_mask = sizeof(void *) - 1;
-
-       if (unlikely(addr < min_addr))
-               goto out;
-       if (unlikely(addr > (unsigned long)high_memory - size))
-               goto out;
-       if (unlikely(addr & align_mask))
-               goto out;
-       if (unlikely(!kern_addr_valid(addr)))
-               goto out;
-       if (unlikely(!kern_addr_valid(addr + size - 1)))
-               goto out;
-       return 1;
-out:
-       return 0;
-}
-
 /*
  * strndup_user - duplicate an existing string from user space
  * @s: The string to duplicate
index c1663c0..ccc576a 100644 (file)
@@ -262,6 +262,7 @@ static struct inode *sock_alloc_inode(struct super_block *sb)
 }
 
 
+
 static void wq_free_rcu(struct rcu_head *head)
 {
        struct socket_wq *wq = container_of(head, struct socket_wq, rcu);
@@ -360,14 +361,14 @@ static int sock_alloc_file(struct socket *sock, struct file **f, int flags)
        if (unlikely(fd < 0))
                return fd;
 
-       path.dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name);
+       path.dentry = d_alloc_pseudo(sock_mnt->mnt_sb, &name);
        if (unlikely(!path.dentry)) {
                put_unused_fd(fd);
                return -ENOMEM;
        }
        path.mnt = mntget(sock_mnt);
 
-       path.dentry->d_op = &sockfs_dentry_operations;
+       d_set_d_op(path.dentry, &sockfs_dentry_operations);
        d_instantiate(path.dentry, SOCK_INODE(sock));
        SOCK_INODE(sock)->i_fop = &socket_file_ops;
 
@@ -2390,6 +2391,8 @@ EXPORT_SYMBOL(sock_unregister);
 
 static int __init sock_init(void)
 {
+       int err;
+
        /*
         *      Initialize sock SLAB cache.
         */
@@ -2406,8 +2409,15 @@ static int __init sock_init(void)
         */
 
        init_inodecache();
-       register_filesystem(&sock_fs_type);
+
+       err = register_filesystem(&sock_fs_type);
+       if (err)
+               goto out_fs;
        sock_mnt = kern_mount(&sock_fs_type);
+       if (IS_ERR(sock_mnt)) {
+               err = PTR_ERR(sock_mnt);
+               goto out_mount;
+       }
 
        /* The real protocol initialization is performed in later initcalls.
         */
@@ -2420,7 +2430,13 @@ static int __init sock_init(void)
        skb_timestamping_init();
 #endif
 
-       return 0;
+out:
+       return err;
+
+out_mount:
+       unregister_filesystem(&sock_fs_type);
+out_fs:
+       goto out;
 }
 
 core_initcall(sock_init);      /* early initcall */
index 10a17a3..09f01f4 100644 (file)
@@ -162,11 +162,19 @@ rpc_alloc_inode(struct super_block *sb)
 }
 
 static void
-rpc_destroy_inode(struct inode *inode)
+rpc_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(rpc_inode_cachep, RPC_I(inode));
 }
 
+static void
+rpc_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, rpc_i_callback);
+}
+
 static int
 rpc_pipe_open(struct inode *inode, struct file *filp)
 {
@@ -430,7 +438,7 @@ void rpc_put_mount(void)
 }
 EXPORT_SYMBOL_GPL(rpc_put_mount);
 
-static int rpc_delete_dentry(struct dentry *dentry)
+static int rpc_delete_dentry(const struct dentry *dentry)
 {
        return 1;
 }
@@ -583,7 +591,7 @@ static struct dentry *__rpc_lookup_create(struct dentry *parent,
                }
        }
        if (!dentry->d_inode)
-               dentry->d_op = &rpc_dentry_operations;
+               d_set_d_op(dentry, &rpc_dentry_operations);
 out_err:
        return dentry;
 }
index e5fb07a..739e403 100644 (file)
@@ -513,6 +513,15 @@ int security_inode_permission(struct inode *inode, int mask)
        return security_ops->inode_permission(inode, mask);
 }
 
+int security_inode_exec_permission(struct inode *inode, unsigned int flags)
+{
+       if (unlikely(IS_PRIVATE(inode)))
+               return 0;
+       if (flags)
+               return -ECHILD;
+       return security_ops->inode_permission(inode, MAY_EXEC);
+}
+
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 {
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
index 073fd5b..43deac2 100644 (file)
@@ -1145,24 +1145,28 @@ static void sel_remove_entries(struct dentry *de)
 {
        struct list_head *node;
 
-       spin_lock(&dcache_lock);
+       spin_lock(&de->d_lock);
        node = de->d_subdirs.next;
        while (node != &de->d_subdirs) {
                struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
+
+               spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
                list_del_init(node);
 
                if (d->d_inode) {
-                       d = dget_locked(d);
-                       spin_unlock(&dcache_lock);
+                       dget_dlock(d);
+                       spin_unlock(&de->d_lock);
+                       spin_unlock(&d->d_lock);
                        d_delete(d);
                        simple_unlink(de->d_inode, d);
                        dput(d);
-                       spin_lock(&dcache_lock);
-               }
+                       spin_lock(&de->d_lock);
+               } else
+                       spin_unlock(&d->d_lock);
                node = de->d_subdirs.next;
        }
 
-       spin_unlock(&dcache_lock);
+       spin_unlock(&de->d_lock);
 }
 
 #define BOOL_DIR_NAME "booleans"
index 1d0bf8f..d1e05b0 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <net/sock.h>
 #include "common.h"
+#include "../../fs/internal.h"
 
 /**
  * tomoyo_encode: Convert binary string to ascii string.