Skip to content

Commit

Permalink
fs: rcu-walk aware d_revalidate method
Browse files Browse the repository at this point in the history
Require filesystems be aware of .d_revalidate being called in rcu-walk
mode (nd->flags & LOOKUP_RCU). For now do a simple push down, returning
-ECHILD from all implementations.

Signed-off-by: Nick Piggin <[email protected]>
  • Loading branch information
Nick Piggin committed Jan 7, 2011
1 parent 44a7d7a commit 34286d6
Show file tree
Hide file tree
Showing 27 changed files with 215 additions and 61 deletions.
18 changes: 9 additions & 9 deletions Documentation/filesystems/Locking
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ be able to use diff(1).

--------------------------- dentry_operations --------------------------
prototypes:
int (*d_revalidate)(struct dentry *, int);
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 *,
Expand All @@ -21,14 +21,14 @@ prototypes:
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);

locking rules:
rename_lock ->d_lock may block
d_revalidate: no no yes
d_hash no no no
d_compare: yes no no
d_delete: no yes no
d_release: no no yes
d_iput: no no yes
d_dname: no no no
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 ---------------------------
prototypes:
Expand Down
5 changes: 2 additions & 3 deletions Documentation/filesystems/path-lookup.txt
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,10 @@ The detailed design for rcu-walk is like this:
The cases where rcu-walk cannot continue are:
* NULL dentry (ie. any uncached path element)
* parent with d_inode->i_op->permission or ACLs
* dentries with d_revalidate
* Following links

In future patches, permission checks and d_revalidate become rcu-walk aware. It
may be possible eventually to make following links rcu-walk aware.
In future patches, permission checks become rcu-walk aware. 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.
Expand Down
20 changes: 20 additions & 0 deletions Documentation/filesystems/porting
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,23 @@ 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.
9 changes: 9 additions & 0 deletions Documentation/filesystems/vfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,15 @@ struct dentry_operations {
dcache. Most filesystems leave this as NULL, because all their
dentries in the dcache are valid

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.
Expand Down
5 changes: 4 additions & 1 deletion drivers/staging/autofs/root.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 12 additions & 4 deletions drivers/staging/smbfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -301,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
Expand Down
4 changes: 4 additions & 0 deletions fs/afs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 10 additions & 3 deletions fs/autofs4/root.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,19 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
*/
static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
{
struct inode *dir = dentry->d_parent->d_inode;
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
int oz_mode = autofs4_oz_mode(sbi);
struct inode *dir;
struct autofs_sb_info *sbi;
int oz_mode;
int flags = nd ? nd->flags : 0;
int 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)) {
Expand Down
7 changes: 6 additions & 1 deletion fs/ceph/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -990,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,
Expand Down
3 changes: 3 additions & 0 deletions fs/cifs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
static int
cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU)
return -ECHILD;

if (direntry->d_inode) {
if (cifs_revalidate_dentry(direntry))
return 0;
Expand Down
7 changes: 6 additions & 1 deletion fs/coda/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/namei.h>

#include <asm/uaccess.h>

Expand Down Expand Up @@ -541,9 +542,13 @@ static int coda_venus_readdir(struct file *coda_file, void *buf,
/* 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))
Expand Down
9 changes: 7 additions & 2 deletions fs/ecryptfs/dentry.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,17 @@
*/
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;
Expand Down
6 changes: 6 additions & 0 deletions fs/fat/namei_vfat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
*
Expand Down
6 changes: 5 additions & 1 deletion fs/fuse/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
17 changes: 13 additions & 4 deletions fs/gfs2/dentry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -34,15 +35,23 @@

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;
Expand Down
7 changes: 6 additions & 1 deletion fs/hfs/sysdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions fs/jfs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,8 @@ static int jfs_ci_compare(const struct dentry *parent,

static int jfs_ci_revalidate(struct dentry *dentry, struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU)
return -ECHILD;
/*
* This is not negative dentry. Always valid.
*
Expand Down
Loading

0 comments on commit 34286d6

Please sign in to comment.