All of lore.kernel.org
 help / color / mirror / Atom feed
From: NeilBrown <neilb@suse.de>
To: Al Viro <viro@ZenIV.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 08/20] VFS: make all ->follow_link handlers aware for LOOKUP_RCU
Date: Mon, 23 Mar 2015 13:37:39 +1100	[thread overview]
Message-ID: <20150323023739.8161.87072.stgit@notabene.brown> (raw)
In-Reply-To: <20150323023258.8161.32467.stgit@notabene.brown>

Pass a the inode explicit as an argument, as dentry->d_inode is not
stable during RCU-walk, and also a new 'flags' argument
which may (after further patches) contain LOOKUP_RCU.

->follow_link methods which cannot complete atomically
must return -ECHILD when LOOKUP_RCU is set.
Those which can complete atomically must use 'inode'
rather than 'dentry->d_inode', and must only reference data
structures that are freed using rcu_free().

Later patches will make some of these handle LOOKUP_RCU
more gracefully.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 Documentation/filesystems/Locking             |    2 +-
 Documentation/filesystems/porting             |    9 +++++++++
 Documentation/filesystems/vfs.txt             |    2 +-
 drivers/staging/lustre/lustre/llite/symlink.c |    7 +++++--
 fs/9p/vfs_inode.c                             |   11 +++++++++--
 fs/9p/vfs_inode_dotl.c                        |   10 ++++++++--
 fs/autofs4/symlink.c                          |    6 ++++--
 fs/befs/linuxvfs.c                            |   15 +++++++++------
 fs/ceph/inode.c                               |    5 +++--
 fs/cifs/cifsfs.h                              |    3 ++-
 fs/cifs/link.c                                |    5 +++--
 fs/configfs/symlink.c                         |   10 ++++++++--
 fs/debugfs/file.c                             |    5 +++--
 fs/ecryptfs/inode.c                           |   12 +++++++++---
 fs/exofs/symlink.c                            |    6 +++---
 fs/ext2/symlink.c                             |    5 +++--
 fs/ext3/symlink.c                             |    5 +++--
 fs/ext4/symlink.c                             |    5 +++--
 fs/freevxfs/vxfs_immed.c                      |    9 ++++++---
 fs/fuse/dir.c                                 |    5 ++++-
 fs/gfs2/inode.c                               |    9 +++++++--
 fs/hostfs/hostfs_kern.c                       |   10 ++++++++--
 fs/hppfs/hppfs.c                              |    9 ++++++---
 fs/jffs2/symlink.c                            |    8 +++++---
 fs/jfs/symlink.c                              |    5 +++--
 fs/kernfs/symlink.c                           |   10 ++++++++--
 fs/namei.c                                    |   14 ++++++++++----
 fs/nfs/symlink.c                              |    6 ++++--
 fs/overlayfs/inode.c                          |    8 ++++++--
 fs/proc/base.c                                |    6 ++++--
 fs/proc/inode.c                               |    5 +++--
 fs/proc/namespaces.c                          |    7 +++++--
 fs/proc/self.c                                |   10 ++++++++--
 fs/proc/thread_self.c                         |   13 ++++++++++---
 fs/sysv/symlink.c                             |    5 +++--
 fs/ubifs/file.c                               |    5 +++--
 fs/ufs/symlink.c                              |    6 ++++--
 fs/xfs/xfs_iops.c                             |    8 ++++++--
 include/linux/fs.h                            |    4 ++--
 mm/shmem.c                                    |   14 ++++++++++----
 40 files changed, 211 insertions(+), 88 deletions(-)

diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index bbce4914d209..c0289bae848f 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -50,7 +50,7 @@ prototypes:
 	int (*rename2) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *, unsigned int);
 	int (*readlink) (struct dentry *, char __user *,int);
-	void * (*follow_link) (struct dentry *);
+	void * (*follow_link) (struct dentry *, struct inode *, int);
 	void (*put_link) (struct dentry *, char *, void *);
 	void (*truncate) (struct inode *);
 	int (*permission) (struct inode *, int, unsigned int);
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 9996b4631a87..eba8dd0a13e3 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -481,3 +481,12 @@ in your dentry operations instead.
 	->follow_link() no longer receives 'struct nameidata *'.
 	The nd is now attached to 'current' and nd_set_link()
 	accesses it directly.
+--
+[mandatory]
+	->follow_link now receives 'struct inode *' and 'int flags' which
+	may contain LOOKUP_RCU.  In this case -ECHILD must be
+	returned if the operation cannot be completed under
+	rcu_read_lock() conditions.
+	The passed inode must be used rather than dentry->d_inode,
+	particularly if LOOKUP_RCU is set.
+	If s_fs_info is used, it must be freed using RCU.
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 11aac530931b..5557e9283d04 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -350,7 +350,7 @@ struct inode_operations {
 	int (*rename2) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *, unsigned int);
 	int (*readlink) (struct dentry *, char __user *,int);
-        void * (*follow_link) (struct dentry *);
+        void * (*follow_link) (struct dentry *, struct inode *, int);
 	void (*put_link) (struct dentry *, char *, void *);
 	int (*permission) (struct inode *, int);
 	int (*get_acl)(struct inode *, int);
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
index 63dd1a925c92..44d095c68ce7 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -118,14 +118,17 @@ failed:
 	return rc;
 }
 
-static void *ll_follow_link(struct dentry *dentry)
+static void *ll_follow_link(struct dentry *dentry, struct inode *inode,
+			    int flags)
 {
 	unsigned long avail_space;
-	struct inode *inode = dentry->d_inode;
 	struct ptlrpc_request *request = NULL;
 	int rc;
 	char *symname = NULL;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
 	CDEBUG(D_VFSTRACE, "VFS Op\n");
 	/* Limit the recursive symlink depth.
 	 * Previously limited to 5 instead of default 8 links when
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index ebf50c3e132c..112091a186a1 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1274,13 +1274,20 @@ done:
 /**
  * v9fs_vfs_follow_link - follow a symlink path
  * @dentry: dentry for symlink
+ * @inode:  inode for the symlink
+ * @flags: lookup flags
  *
  */
 
-static void *v9fs_vfs_follow_link(struct dentry *dentry)
+static void *v9fs_vfs_follow_link(struct dentry *dentry, struct inode *inode,
+				  int flags)
 {
 	int len = 0;
-	char *link = __getname();
+	char *link;
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+	link = __getname();
 
 	p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index dc35156aea6a..3971e265f788 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -905,17 +905,23 @@ error:
 /**
  * v9fs_vfs_follow_link_dotl - follow a symlink path
  * @dentry: dentry for symlink
+ * @inode: inode for symlink
+ * @flags: lookup flags
  *
  */
 
 static void *
-v9fs_vfs_follow_link_dotl(struct dentry *dentry)
+v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct inode *inode,
+			  int flags)
 {
 	int retval;
 	struct p9_fid *fid;
-	char *link = __getname();
+	char *link;
 	char *target;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+	link = __getname();
 	p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
 	if (!link) {
diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c
index 37b4b561faa3..e87885a6ef4e 100644
--- a/fs/autofs4/symlink.c
+++ b/fs/autofs4/symlink.c
@@ -12,13 +12,15 @@
 
 #include "autofs_i.h"
 
-static void *autofs4_follow_link(struct dentry *dentry)
+static void *autofs4_follow_link(struct dentry *dentry, struct inode *inode,
+				 int flags)
 {
 	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
 	struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
 	if (ino && !autofs4_oz_mode(sbi))
 		ino->last_used = jiffies;
-	nd_set_link(dentry->d_inode->i_private);
+	nd_set_link(inode->i_private);
 	return NULL;
 }
 
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 339ac02c0e17..1151a6fbc74e 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -42,8 +42,8 @@ static struct inode *befs_iget(struct super_block *, unsigned long);
 static struct inode *befs_alloc_inode(struct super_block *sb);
 static void befs_destroy_inode(struct inode *inode);
 static void befs_destroy_inodecache(void);
-static void *befs_follow_link(struct dentry *);
-static void *befs_fast_follow_link(struct dentry *);
+static void *befs_follow_link(struct dentry *, struct inode *, int);
+static void *befs_fast_follow_link(struct dentry *, struct inode *, int);
 static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
 			char **out, int *out_len);
 static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,
@@ -469,14 +469,17 @@ befs_destroy_inodecache(void)
  * flag is set.
  */
 static void *
-befs_follow_link(struct dentry *dentry)
+befs_follow_link(struct dentry *dentry, struct inode *inode, int flags)
 {
 	struct super_block *sb = dentry->d_sb;
-	befs_inode_info *befs_ino = BEFS_I(dentry->d_inode);
+	befs_inode_info *befs_ino = BEFS_I(inode);
 	befs_data_stream *data = &befs_ino->i_data.ds;
 	befs_off_t len = data->size;
 	char *link;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
 	if (len == 0) {
 		befs_error(sb, "Long symlink with illegal length");
 		link = ERR_PTR(-EIO);
@@ -500,9 +503,9 @@ befs_follow_link(struct dentry *dentry)
 
 
 static void *
-befs_fast_follow_link(struct dentry *dentry)
+befs_fast_follow_link(struct dentry *dentry, struct inode *inode, int flags)
 {
-	befs_inode_info *befs_ino = BEFS_I(dentry->d_inode);
+	befs_inode_info *befs_ino = BEFS_I(inode);
 	nd_set_link(befs_ino->i_data.symlink);
 	return NULL;
 }
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index f8212d6945de..761b55e73491 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1691,9 +1691,10 @@ retry:
 /*
  * symlinks
  */
-static void *ceph_sym_follow_link(struct dentry *dentry)
+static void *ceph_sym_follow_link(struct dentry *dentry, struct inode *inode,
+				  int flags)
 {
-	struct ceph_inode_info *ci = ceph_inode(dentry->d_inode);
+	struct ceph_inode_info *ci = ceph_inode(inode);
 	nd_set_link(ci->i_symlink);
 	return NULL;
 }
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index e3a6ef52a3e4..e58e685565d8 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -120,7 +120,8 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
 #endif
 
 /* Functions related to symlinks */
-extern void *cifs_follow_link(struct dentry *direntry);
+extern void *cifs_follow_link(struct dentry *direntry, struct inode *inode,
+			      int flags);
 extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
 			 int buflen);
 extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index ba3562198c33..0d3c14acbdfc 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -627,9 +627,8 @@ cifs_hl_exit:
 }
 
 void *
-cifs_follow_link(struct dentry *direntry)
+cifs_follow_link(struct dentry *direntry, struct inode *inode, int flags)
 {
-	struct inode *inode = direntry->d_inode;
 	int rc = -ENOMEM;
 	unsigned int xid;
 	char *full_path = NULL;
@@ -639,6 +638,8 @@ cifs_follow_link(struct dentry *direntry)
 	struct cifs_tcon *tcon;
 	struct TCP_Server_Info *server;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	xid = get_xid();
 
 	tlink = cifs_sb_tlink(cifs_sb);
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index ff41712ffddd..443b11251b84 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -279,10 +279,16 @@ static int configfs_getlink(struct dentry *dentry, char * path)
 
 }
 
-static void *configfs_follow_link(struct dentry *dentry)
+static void *configfs_follow_link(struct dentry *dentry, struct inode *inode,
+				  int flags)
 {
 	int error = -ENOMEM;
-	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	unsigned long page;
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
+	page = get_zeroed_page(GFP_KERNEL);
 
 	if (page) {
 		error = configfs_getlink(dentry, (char *)page);
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index eeed1f1fed4f..720dfb983b93 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -43,9 +43,10 @@ const struct file_operations debugfs_file_operations = {
 	.llseek =	noop_llseek,
 };
 
-static void *debugfs_follow_link(struct dentry *dentry)
+static void *debugfs_follow_link(struct dentry *dentry, struct inode *inode,
+				 int flags)
 {
-	nd_set_link(dentry->d_inode->i_private);
+	nd_set_link(inode->i_private);
 	return NULL;
 }
 
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 680cf30e9135..17c4321e6d40 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -673,13 +673,19 @@ out:
 	return rc ? ERR_PTR(rc) : buf;
 }
 
-static void *ecryptfs_follow_link(struct dentry *dentry)
+static void *ecryptfs_follow_link(struct dentry *dentry, struct inode *inode,
+				  int flags)
 {
 	size_t len;
-	char *buf = ecryptfs_readlink_lower(dentry, &len);
+	char *buf;
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
+	buf = ecryptfs_readlink_lower(dentry, &len);
 	if (IS_ERR(buf))
 		goto out;
-	fsstack_copy_attr_atime(dentry->d_inode,
+	fsstack_copy_attr_atime(inode,
 				ecryptfs_dentry_to_lower(dentry)->d_inode);
 	buf[len] = '\0';
 out:
diff --git a/fs/exofs/symlink.c b/fs/exofs/symlink.c
index e6d0467a0b5a..c8525b051811 100644
--- a/fs/exofs/symlink.c
+++ b/fs/exofs/symlink.c
@@ -35,10 +35,10 @@
 
 #include "exofs.h"
 
-static void *exofs_follow_link(struct dentry *dentry)
+static void *exofs_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags)
 {
-	struct exofs_i_info *oi = exofs_i(dentry->d_inode);
-
+	struct exofs_i_info *oi = exofs_i(inode);
 	nd_set_link((char *)oi->i_data);
 	return NULL;
 }
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 063852432bd4..eb1820a875c9 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -21,9 +21,10 @@
 #include "xattr.h"
 #include <linux/namei.h>
 
-static void *ext2_follow_link(struct dentry *dentry)
+static void *ext2_follow_link(struct dentry *dentry, struct inode *inode,
+			      int flags)
 {
-	struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
+	struct ext2_inode_info *ei = EXT2_I(inode);
 	nd_set_link((char *)ei->i_data);
 	return NULL;
 }
diff --git a/fs/ext3/symlink.c b/fs/ext3/symlink.c
index bf8acd9efaae..c048fedc13c4 100644
--- a/fs/ext3/symlink.c
+++ b/fs/ext3/symlink.c
@@ -21,9 +21,10 @@
 #include "ext3.h"
 #include "xattr.h"
 
-static void * ext3_follow_link(struct dentry *dentry)
+static void * ext3_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags)
 {
-	struct ext3_inode_info *ei = EXT3_I(dentry->d_inode);
+	struct ext3_inode_info *ei = EXT3_I(inode);
 	nd_set_link((char*)ei->i_data);
 	return NULL;
 }
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index 0015e7f53d0f..da0790514769 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -23,9 +23,10 @@
 #include "ext4.h"
 #include "xattr.h"
 
-static void *ext4_follow_link(struct dentry *dentry)
+static void *ext4_follow_link(struct dentry *dentry, struct inode *inode,
+			      int flags)
 {
-	struct ext4_inode_info *ei = EXT4_I(dentry->d_inode);
+	struct ext4_inode_info *ei = EXT4_I(inode);
 	nd_set_link((char *) ei->i_data);
 	return NULL;
 }
diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c
index 058acefeb11c..bc8f83f1ff7d 100644
--- a/fs/freevxfs/vxfs_immed.c
+++ b/fs/freevxfs/vxfs_immed.c
@@ -39,7 +39,7 @@
 #include "vxfs_inode.h"
 
 
-static void *	vxfs_immed_follow_link(struct dentry *);
+static void *	vxfs_immed_follow_link(struct dentry *, struct inode *, int);
 
 static int	vxfs_immed_readpage(struct file *, struct page *);
 
@@ -64,6 +64,8 @@ const struct address_space_operations vxfs_immed_aops = {
 /**
  * vxfs_immed_follow_link - follow immed symlink
  * @dp:		dentry for the link
+ * @inode:	inode for the link
+ * @flags:	lookup flags
  *
  * Description:
  *   vxfs_immed_follow_link restarts the pathname lookup with
@@ -73,9 +75,10 @@ const struct address_space_operations vxfs_immed_aops = {
  *   Zero on success, else a negative error code.
  */
 static void *
-vxfs_immed_follow_link(struct dentry *dp)
+vxfs_immed_follow_link(struct dentry *dp, struct inode *inode,
+		       int flags)
 {
-	struct vxfs_inode_info		*vip = VXFS_INO(dp->d_inode);
+	struct vxfs_inode_info		*vip = VXFS_INO(inode);
 	nd_set_link(vip->vii_immed.vi_immed);
 	return NULL;
 }
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 58e632be862b..e950b3c774c5 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1400,8 +1400,11 @@ static void free_link(char *link)
 		free_page((unsigned long) link);
 }
 
-static void *fuse_follow_link(struct dentry *dentry)
+static void *fuse_follow_link(struct dentry *dentry, struct inode *inode,
+			      int flags)
 {
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	nd_set_link(read_link(dentry));
 	return NULL;
 }
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 5b28009ea860..2e4caa76bf97 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1541,21 +1541,26 @@ out:
 /**
  * gfs2_follow_link - Follow a symbolic link
  * @dentry: The dentry of the link
+ * @inode: The inode of the link
+ * @flags: Lookup flags
  *
  * This can handle symlinks of any size.
  *
  * Returns: 0 on success or error code
  */
 
-static void *gfs2_follow_link(struct dentry *dentry)
+static void *gfs2_follow_link(struct dentry *dentry, struct inode *inode,
+			      int flags)
 {
-	struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
+	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_holder i_gh;
 	struct buffer_head *dibh;
 	unsigned int size;
 	char *buf;
 	int error;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
 	error = gfs2_glock_nq(&i_gh);
 	if (error) {
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index a862634bf98e..f40966f9fdaf 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -882,9 +882,15 @@ static const struct inode_operations hostfs_dir_iops = {
 	.setattr	= hostfs_setattr,
 };
 
-static void *hostfs_follow_link(struct dentry *dentry)
+static void *hostfs_follow_link(struct dentry *dentry, struct inode *inode,
+				int flags)
 {
-	char *link = __getname();
+	char *link;
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
+	link = __getname();
 	if (link) {
 		char *path = dentry_name(dentry);
 		int err = -ENOMEM;
diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c
index bf13cf24eec7..b6f68e22d0fb 100644
--- a/fs/hppfs/hppfs.c
+++ b/fs/hppfs/hppfs.c
@@ -642,11 +642,14 @@ static int hppfs_readlink(struct dentry *dentry, char __user *buffer,
 						    buflen);
 }
 
-static void *hppfs_follow_link(struct dentry *dentry)
+static void *hppfs_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags)
 {
-	struct dentry *proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry;
+	struct dentry *proc_dentry = HPPFS_I(inode)->proc_dentry;
 
-	return proc_dentry->d_inode->i_op->follow_link(proc_dentry);
+	return proc_dentry->d_inode->i_op->follow_link(proc_dentry,
+						       proc_dentry->d_inode,
+						       flags);
 }
 
 static void hppfs_put_link(struct dentry *dentry, char *link,
diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c
index 6b58d7659fbd..18c00fbd7060 100644
--- a/fs/jffs2/symlink.c
+++ b/fs/jffs2/symlink.c
@@ -16,7 +16,8 @@
 #include <linux/namei.h>
 #include "nodelist.h"
 
-static void *jffs2_follow_link(struct dentry *dentry);
+static void *jffs2_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags);
 
 const struct inode_operations jffs2_symlink_inode_operations =
 {
@@ -29,9 +30,10 @@ const struct inode_operations jffs2_symlink_inode_operations =
 	.removexattr =	jffs2_removexattr
 };
 
-static void *jffs2_follow_link(struct dentry *dentry)
+static void *jffs2_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags)
 {
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	char *p = (char *)f->target;
 
 	/*
diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c
index cefda45b8d3a..610f44fad05e 100644
--- a/fs/jfs/symlink.c
+++ b/fs/jfs/symlink.c
@@ -22,9 +22,10 @@
 #include "jfs_inode.h"
 #include "jfs_xattr.h"
 
-static void *jfs_follow_link(struct dentry *dentry)
+static void *jfs_follow_link(struct dentry *dentry, struct inode *inode,
+			     int flags)
 {
-	char *s = JFS_IP(dentry->d_inode)->i_inline;
+	char *s = JFS_IP(inode)->i_inline;
 	nd_set_link(s);
 	return NULL;
 }
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index 63d08ecbb72c..1a40d5e6ac71 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -112,10 +112,16 @@ static int kernfs_getlink(struct dentry *dentry, char *path)
 	return error;
 }
 
-static void *kernfs_iop_follow_link(struct dentry *dentry)
+static void *kernfs_iop_follow_link(struct dentry *dentry, struct inode *inode,
+				    int flags)
 {
 	int error = -ENOMEM;
-	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	unsigned long page;
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
+	page = get_zeroed_page(GFP_KERNEL);
 	if (page) {
 		error = kernfs_getlink(dentry, (char *) page);
 		if (error < 0)
diff --git a/fs/namei.c b/fs/namei.c
index e7fab6886e29..784fca0e6c70 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -876,6 +876,7 @@ static __always_inline int
 follow_link(struct path *link, struct nameidata *nd, void **p)
 {
 	struct dentry *dentry = link->dentry;
+	struct inode *inode = dentry->d_inode;
 	int error;
 	char *s;
 
@@ -894,12 +895,13 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
 	touch_atime(link);
 	nd_set_link(NULL);
 
-	error = security_inode_follow_link(link->dentry);
+	error = security_inode_follow_link(dentry);
 	if (error)
 		goto out_put_nd_path;
 
 	nd->last_type = LAST_BIND;
-	*p = dentry->d_inode->i_op->follow_link(dentry);
+	*p = inode->i_op->follow_link(dentry, inode,
+				      nd->flags & LOOKUP_RCU);
 	error = PTR_ERR(*p);
 	if (IS_ERR(*p))
 		goto out_put_nd_path;
@@ -4464,7 +4466,8 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 	int res;
 
 	nd.depth = 0;
-	cookie = dentry->d_inode->i_op->follow_link(dentry);
+	cookie = dentry->d_inode->i_op->follow_link(dentry,
+						    dentry->d_inode, 0);
 	if (IS_ERR(cookie))
 		res = PTR_ERR(cookie);
 	else {
@@ -4506,9 +4509,12 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 }
 EXPORT_SYMBOL(page_readlink);
 
-void *page_follow_link_light(struct dentry *dentry)
+void *page_follow_link_light(struct dentry *dentry, struct inode *inode,
+			     int flags)
 {
 	struct page *page = NULL;
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	nd_set_link(page_getlink(dentry, &page));
 	return page;
 }
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index f3c44616b615..32bbac1bb4bc 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -43,12 +43,14 @@ error:
 	return -EIO;
 }
 
-static void *nfs_follow_link(struct dentry *dentry)
+static void *nfs_follow_link(struct dentry *dentry, struct inode *inode,
+			     int flags)
 {
-	struct inode *inode = dentry->d_inode;
 	struct page *page;
 	void *err;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
 	if (err)
 		goto read_failed;
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 0de7b87bd025..675efccd5c84 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -8,6 +8,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/xattr.h>
 #include "overlayfs.h"
@@ -140,13 +141,16 @@ struct ovl_link_data {
 	void *cookie;
 };
 
-static void *ovl_follow_link(struct dentry *dentry)
+static void *ovl_follow_link(struct dentry *dentry, struct inode *inode,
+			     int flags)
 {
 	void *ret;
 	struct dentry *realdentry;
 	struct inode *realinode;
 	struct ovl_link_data *data = NULL;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	realdentry = ovl_dentry_real(dentry);
 	realinode = realdentry->d_inode;
 
@@ -160,7 +164,7 @@ static void *ovl_follow_link(struct dentry *dentry)
 		data->realdentry = realdentry;
 	}
 
-	ret = realinode->i_op->follow_link(realdentry);
+	ret = realinode->i_op->follow_link(realdentry, realinode, flags);
 	if (IS_ERR(ret)) {
 		kfree(data);
 		return ret;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a0c0b85aead3..203f8d8ceab1 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1371,12 +1371,14 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
 		return -ENOENT;
 }
 
-static void *proc_pid_follow_link(struct dentry *dentry)
+static void *proc_pid_follow_link(struct dentry *dentry, struct inode *inode,
+				  int flags)
 {
-	struct inode *inode = dentry->d_inode;
 	struct path path;
 	int error = -EACCES;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	/* Are we allowed to snoop on the tasks file descriptors? */
 	if (!proc_fd_access_allowed(inode))
 		goto out;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 7bdaf1040f98..faf2c5400437 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -394,9 +394,10 @@ static const struct file_operations proc_reg_file_ops_no_compat = {
 };
 #endif
 
-static void *proc_follow_link(struct dentry *dentry)
+static void *proc_follow_link(struct dentry *dentry, struct inode *inode,
+			      int flags)
 {
-	struct proc_dir_entry *pde = PDE(dentry->d_inode);
+	struct proc_dir_entry *pde = PDE(inode);
 	if (unlikely(!use_pde(pde)))
 		return ERR_PTR(-EINVAL);
 	nd_set_link(pde->data);
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 5e3394509c2c..a2578c44edeb 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -30,14 +30,17 @@ static const struct proc_ns_operations *ns_entries[] = {
 	&mntns_operations,
 };
 
-static void *proc_ns_follow_link(struct dentry *dentry)
+static void *proc_ns_follow_link(struct dentry *dentry, struct inode *inode,
+				 int flags)
 {
-	struct inode *inode = dentry->d_inode;
 	const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
 	struct task_struct *task;
 	struct path ns_path;
 	void *error = ERR_PTR(-EACCES);
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
 	task = get_proc_task(inode);
 	if (!task)
 		return error;
diff --git a/fs/proc/self.c b/fs/proc/self.c
index 639bd0afdc05..7fcb906c250a 100644
--- a/fs/proc/self.c
+++ b/fs/proc/self.c
@@ -19,11 +19,17 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
 	return readlink_copy(buffer, buflen, tmp);
 }
 
-static void *proc_self_follow_link(struct dentry *dentry)
+static void *proc_self_follow_link(struct dentry *dentry, struct inode *inode,
+				   int flags)
 {
 	struct pid_namespace *ns = dentry->d_sb->s_fs_info;
-	pid_t tgid = task_tgid_nr_ns(current, ns);
+	pid_t tgid;
 	char *name = ERR_PTR(-ENOENT);
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
+	tgid = task_tgid_nr_ns(current, ns);
 	if (tgid) {
 		/* 11 for max length of signed int in decimal + NULL term */
 		name = kmalloc(12, GFP_KERNEL);
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c
index 2036b051f53f..7a9af8a6baab 100644
--- a/fs/proc/thread_self.c
+++ b/fs/proc/thread_self.c
@@ -20,12 +20,19 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer,
 	return readlink_copy(buffer, buflen, tmp);
 }
 
-static void *proc_thread_self_follow_link(struct dentry *dentry)
+static void *proc_thread_self_follow_link(struct dentry *dentry,
+					  struct inode *inode, int flags)
 {
 	struct pid_namespace *ns = dentry->d_sb->s_fs_info;
-	pid_t tgid = task_tgid_nr_ns(current, ns);
-	pid_t pid = task_pid_nr_ns(current, ns);
+	pid_t tgid;
+	pid_t pid;
 	char *name = ERR_PTR(-ENOENT);
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+
+	tgid = task_tgid_nr_ns(current, ns);
+	pid = task_pid_nr_ns(current, ns);
 	if (pid) {
 		name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
 		if (!name)
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
index 3f8154e6b27e..bdddd74831ac 100644
--- a/fs/sysv/symlink.c
+++ b/fs/sysv/symlink.c
@@ -8,9 +8,10 @@
 #include "sysv.h"
 #include <linux/namei.h>
 
-static void *sysv_follow_link(struct dentry *dentry)
+static void *sysv_follow_link(struct dentry *dentry, struct inode *inode,
+			      int flags)
 {
-	nd_set_link((char *)SYSV_I(dentry->d_inode)->i_data);
+	nd_set_link((char *)SYSV_I(inode)->i_data);
 	return NULL;
 }
 
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 082958d62096..4872f14a88c1 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1300,9 +1300,10 @@ static void ubifs_invalidatepage(struct page *page, unsigned int offset,
 	ClearPageChecked(page);
 }
 
-static void *ubifs_follow_link(struct dentry *dentry)
+static void *ubifs_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags)
 {
-	struct ubifs_inode *ui = ubifs_inode(dentry->d_inode);
+	struct ubifs_inode *ui = ubifs_inode(inode);
 
 	nd_set_link(ui->data);
 	return NULL;
diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c
index a8266ff60b0f..3b690a020627 100644
--- a/fs/ufs/symlink.c
+++ b/fs/ufs/symlink.c
@@ -32,9 +32,11 @@
 #include "ufs.h"
 
 
-static void *ufs_follow_link(struct dentry *dentry)
+static void *ufs_follow_link(struct dentry *dentry, struct inode *inode,
+			     int flags)
 {
-	struct ufs_inode_info *p = UFS_I(dentry->d_inode);
+	struct ufs_inode_info *p = UFS_I(inode);
+
 	nd_set_link((char*)p->i_u1.i_symlink);
 	return NULL;
 }
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index ac915d09de29..c2c136ef3a50 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -411,16 +411,20 @@ xfs_vn_rename(
  */
 STATIC void *
 xfs_vn_follow_link(
-	struct dentry		*dentry)
+	struct dentry		*dentry,
+	struct inode		*inode,
+	int			flags)
 {
 	char			*link;
 	int			error = -ENOMEM;
 
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
 	link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
 	if (!link)
 		goto out_err;
 
-	error = xfs_readlink(XFS_I(dentry->d_inode), link);
+	error = xfs_readlink(XFS_I(inode), link);
 	if (unlikely(error))
 		goto out_kfree;
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index d78dd3ae1be9..dda92ac8ef41 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1573,7 +1573,7 @@ struct file_operations {
 
 struct inode_operations {
 	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
-	void * (*follow_link) (struct dentry *);
+	void * (*follow_link) (struct dentry *, struct inode *, int);
 	int (*permission) (struct inode *, int);
 	struct posix_acl * (*get_acl)(struct inode *, int);
 
@@ -2648,7 +2648,7 @@ extern const struct file_operations generic_ro_fops;
 
 extern int readlink_copy(char __user *, int, const char *);
 extern int page_readlink(struct dentry *, char __user *, int);
-extern void *page_follow_link_light(struct dentry *);
+extern void *page_follow_link_light(struct dentry *, struct inode *, int);
 extern void page_put_link(struct dentry *, char *, void *);
 extern int __page_symlink(struct inode *inode, const char *symname, int len,
 		int nofs);
diff --git a/mm/shmem.c b/mm/shmem.c
index 910b37f44a2b..1083e8c75536 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2474,16 +2474,22 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 	return 0;
 }
 
-static void *shmem_follow_short_symlink(struct dentry *dentry)
+static void *shmem_follow_short_symlink(struct dentry *dentry,
+					struct inode *inode, int flags)
 {
-	nd_set_link(SHMEM_I(dentry->d_inode)->symlink);
+	nd_set_link(SHMEM_I(inode)->symlink);
 	return NULL;
 }
 
-static void *shmem_follow_link(struct dentry *dentry)
+static void *shmem_follow_link(struct dentry *dentry, struct inode *inode,
+			       int flags)
 {
 	struct page *page = NULL;
-	int error = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
+	int error;
+
+	if (flags & LOOKUP_RCU)
+		return ERR_PTR(-ECHILD);
+	error = shmem_getpage(inode, 0, &page, SGP_READ, NULL);
 	nd_set_link(error ? ERR_PTR(error) : kmap(page));
 	if (page)
 		unlock_page(page);



  parent reply	other threads:[~2015-03-23  2:38 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-23  2:37 [PATCH 00/20] Support follow_link in RCU-walk - V3 NeilBrown
2015-03-23  2:37 ` [PATCH 02/20] STAGING/lustre: limit follow_link recursion using stack space NeilBrown
2015-04-18  3:01   ` Al Viro
2015-04-19 20:57     ` Andreas Dilger
2015-04-19 21:33       ` Al Viro
2015-04-20  2:29         ` Al Viro
2015-03-23  2:37 ` [PATCH 03/20] VFS: replace {, total_}link_count in task_struct with pointer to nameidata NeilBrown
2015-03-23  2:37 ` [PATCH 01/20] Documentation: remove outdated information from automount-support.txt NeilBrown
2015-03-23  2:37 ` [PATCH 10/20] security: make inode_follow_link RCU-walk aware NeilBrown
2015-03-23  2:37 ` [PATCH 04/20] ovl: rearrange ovl_follow_link to it doesn't need to call ->put_link NeilBrown
2015-03-23  2:37 ` [PATCH 07/20] VFS: remove nameidata args from ->follow_link NeilBrown
2015-03-23  2:37 ` [PATCH 06/20] SECURITY: remove nameidata arg from inode_follow_link NeilBrown
2015-03-23  2:37 ` [PATCH 05/20] VFS: replace nameidata arg to ->put_link with a char* NeilBrown
2015-03-23  2:37 ` [PATCH 09/20] security/selinux: pass 'flags' arg to avc_audit() and avc_has_perm_flags() NeilBrown
2015-03-23  2:37 ` [PATCH 11/20] VFS/namei: use terminate_walk when symlink lookup fails NeilBrown
2015-03-23  2:37 ` NeilBrown [this message]
2015-03-23  2:37 ` [PATCH 13/20] VFS/namei: abort RCU-walk on symlink if atime needs updating NeilBrown
2015-03-23  2:37 ` [PATCH 14/20] VFS/namei: add 'inode' arg to put_link() NeilBrown
2015-04-17 16:25   ` Al Viro
2015-04-17 19:09     ` Al Viro
2015-04-18  8:09       ` Al Viro
2015-03-23  2:37 ` [PATCH 16/20] VFS/namei: enable RCU-walk when following symlinks NeilBrown
2015-03-23  2:37 ` [PATCH 19/20] XFS: allow follow_link to often succeed in RCU-walk NeilBrown
2015-03-23  2:37 ` [PATCH 20/20] NFS: support LOOKUP_RCU in nfs_follow_link NeilBrown
2015-03-23  2:37 ` [PATCH 18/20] xfs: use RCU to free 'struct xfs_mount' NeilBrown
2015-03-23  2:37 ` [PATCH 17/20] VFS/namei: handle LOOKUP_RCU in page_follow_link_light NeilBrown
2015-03-23  2:37 ` [PATCH 12/20] VFS/namei: new flag to support RCU symlinks: LOOKUP_LINK_RCU NeilBrown
2015-03-23  2:37 ` [PATCH 15/20] VFS/namei: enhance follow_link to support RCU-walk NeilBrown
2015-03-25 23:23 ` [PATCH 00/20] Support follow_link in RCU-walk - V3 NeilBrown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150323023739.8161.87072.stgit@notabene.brown \
    --to=neilb@suse.de \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=viro@ZenIV.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.