[PATCH 3/4] dcache: change rename_lock to a sequence read/write lock
The d_path() and related kernel functions currently take a writer lock on rename_lock because they need to follow pointers. By changing rename_lock to be the new sequence read/write lock, a reader lock can be taken and multiple d_path() threads can proceed concurrently without blocking each other. It is unlikely that the frequency of filesystem changes and d_path() name lookup will be high enough to cause writer starvation, the current limitation of the read/write lock should be acceptable in that case. All the sites where rename_lock is referenced were modified to use the sequence read/write lock declaration and access functions. Signed-off-by: Waiman Long --- fs/autofs4/waitq.c |6 ++-- fs/ceph/mds_client.c |4 +- fs/cifs/dir.c |4 +- fs/dcache.c| 87 --- fs/nfs/namespace.c |6 ++-- include/linux/dcache.h |4 +- kernel/auditsc.c |5 ++- 7 files changed, 59 insertions(+), 57 deletions(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 03bc1d3..95eee02 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -199,7 +199,7 @@ rename_retry: buf = *name; len = 0; - seq = read_seqbegin(_lock); + seq = read_seqrwbegin(_lock); rcu_read_lock(); spin_lock(>fs_lock); for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) @@ -208,7 +208,7 @@ rename_retry: if (!len || --len > NAME_MAX) { spin_unlock(>fs_lock); rcu_read_unlock(); - if (read_seqretry(_lock, seq)) + if (read_seqrwretry(_lock, seq)) goto rename_retry; return 0; } @@ -224,7 +224,7 @@ rename_retry: } spin_unlock(>fs_lock); rcu_read_unlock(); - if (read_seqretry(_lock, seq)) + if (read_seqrwretry(_lock, seq)) goto rename_retry; return len; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9165eb8..da6bd2c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1458,7 +1458,7 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, retry: len = 0; - seq = read_seqbegin(_lock); + seq = read_seqrwbegin(_lock); rcu_read_lock(); for (temp = dentry; !IS_ROOT(temp);) { struct inode *inode = temp->d_inode; @@ -1508,7 +1508,7 @@ retry: temp = temp->d_parent; } rcu_read_unlock(); - if (pos != 0 || read_seqretry(_lock, seq)) { + if (pos != 0 || read_seqrwretry(_lock, seq)) { pr_err("build_path did not end path lookup where " "expected, namelen is %d, pos is %d\n", len, pos); /* presumably this is only possible if racing with a diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 8719bbe..4842523 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -96,7 +96,7 @@ build_path_from_dentry(struct dentry *direntry) dfsplen = 0; cifs_bp_rename_retry: namelen = dfsplen; - seq = read_seqbegin(_lock); + seq = read_seqrwbegin(_lock); rcu_read_lock(); for (temp = direntry; !IS_ROOT(temp);) { namelen += (1 + temp->d_name.len); @@ -136,7 +136,7 @@ cifs_bp_rename_retry: } } rcu_read_unlock(); - if (namelen != dfsplen || read_seqretry(_lock, seq)) { + if (namelen != dfsplen || read_seqrwretry(_lock, seq)) { cFYI(1, "did not end path lookup where expected. namelen=%d " "dfsplen=%d", namelen, dfsplen); /* presumably this is only possible if racing with a rename diff --git a/fs/dcache.c b/fs/dcache.c index 20cc789..b1487e2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -82,7 +83,7 @@ int sysctl_vfs_cache_pressure __read_mostly = 100; EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lru_lock); -__cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock); +__cacheline_aligned_in_smp DEFINE_SEQRWLOCK(rename_lock); EXPORT_SYMBOL(rename_lock); @@ -1030,7 +1031,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq */ if (new != old->d_parent || (old->d_flags & DCACHE_DENTRY_KILLED) || -(!locked && read_seqretry(_lock, seq))) { +(!locked && read_seqrwretry(_lock, seq))) { spin_unlock(>d_lock); new = NULL; } @@ -1059,7 +1060,7 @@ int have_submounts(struct dentry *parent) unsigned seq; int locked = 0; - seq = read_seqbegin(_lock); + seq = read_seqrwbegin(_lock); again: this_parent = parent; @@ -1102,23 +1103,23 @@ resume: goto resume; }
[PATCH 3/4] dcache: change rename_lock to a sequence read/write lock
The d_path() and related kernel functions currently take a writer lock on rename_lock because they need to follow pointers. By changing rename_lock to be the new sequence read/write lock, a reader lock can be taken and multiple d_path() threads can proceed concurrently without blocking each other. It is unlikely that the frequency of filesystem changes and d_path() name lookup will be high enough to cause writer starvation, the current limitation of the read/write lock should be acceptable in that case. All the sites where rename_lock is referenced were modified to use the sequence read/write lock declaration and access functions. Signed-off-by: Waiman Long waiman.l...@hp.com --- fs/autofs4/waitq.c |6 ++-- fs/ceph/mds_client.c |4 +- fs/cifs/dir.c |4 +- fs/dcache.c| 87 --- fs/nfs/namespace.c |6 ++-- include/linux/dcache.h |4 +- kernel/auditsc.c |5 ++- 7 files changed, 59 insertions(+), 57 deletions(-) diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 03bc1d3..95eee02 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -199,7 +199,7 @@ rename_retry: buf = *name; len = 0; - seq = read_seqbegin(rename_lock); + seq = read_seqrwbegin(rename_lock); rcu_read_lock(); spin_lock(sbi-fs_lock); for (tmp = dentry ; tmp != root ; tmp = tmp-d_parent) @@ -208,7 +208,7 @@ rename_retry: if (!len || --len NAME_MAX) { spin_unlock(sbi-fs_lock); rcu_read_unlock(); - if (read_seqretry(rename_lock, seq)) + if (read_seqrwretry(rename_lock, seq)) goto rename_retry; return 0; } @@ -224,7 +224,7 @@ rename_retry: } spin_unlock(sbi-fs_lock); rcu_read_unlock(); - if (read_seqretry(rename_lock, seq)) + if (read_seqrwretry(rename_lock, seq)) goto rename_retry; return len; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9165eb8..da6bd2c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1458,7 +1458,7 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, retry: len = 0; - seq = read_seqbegin(rename_lock); + seq = read_seqrwbegin(rename_lock); rcu_read_lock(); for (temp = dentry; !IS_ROOT(temp);) { struct inode *inode = temp-d_inode; @@ -1508,7 +1508,7 @@ retry: temp = temp-d_parent; } rcu_read_unlock(); - if (pos != 0 || read_seqretry(rename_lock, seq)) { + if (pos != 0 || read_seqrwretry(rename_lock, seq)) { pr_err(build_path did not end path lookup where expected, namelen is %d, pos is %d\n, len, pos); /* presumably this is only possible if racing with a diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 8719bbe..4842523 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -96,7 +96,7 @@ build_path_from_dentry(struct dentry *direntry) dfsplen = 0; cifs_bp_rename_retry: namelen = dfsplen; - seq = read_seqbegin(rename_lock); + seq = read_seqrwbegin(rename_lock); rcu_read_lock(); for (temp = direntry; !IS_ROOT(temp);) { namelen += (1 + temp-d_name.len); @@ -136,7 +136,7 @@ cifs_bp_rename_retry: } } rcu_read_unlock(); - if (namelen != dfsplen || read_seqretry(rename_lock, seq)) { + if (namelen != dfsplen || read_seqrwretry(rename_lock, seq)) { cFYI(1, did not end path lookup where expected. namelen=%d dfsplen=%d, namelen, dfsplen); /* presumably this is only possible if racing with a rename diff --git a/fs/dcache.c b/fs/dcache.c index 20cc789..b1487e2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -29,6 +29,7 @@ #include asm/uaccess.h #include linux/security.h #include linux/seqlock.h +#include linux/seqrwlock.h #include linux/swap.h #include linux/bootmem.h #include linux/fs_struct.h @@ -82,7 +83,7 @@ int sysctl_vfs_cache_pressure __read_mostly = 100; EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lru_lock); -__cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock); +__cacheline_aligned_in_smp DEFINE_SEQRWLOCK(rename_lock); EXPORT_SYMBOL(rename_lock); @@ -1030,7 +1031,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq */ if (new != old-d_parent || (old-d_flags DCACHE_DENTRY_KILLED) || -(!locked read_seqretry(rename_lock, seq))) { +(!locked read_seqrwretry(rename_lock, seq))) { spin_unlock(new-d_lock); new = NULL; } @@ -1059,7 +1060,7 @@ int have_submounts(struct dentry *parent) unsigned seq;