Skip to content

Commit

Permalink
replace ->follow_link() with new method that could stay in RCU mode
Browse files Browse the repository at this point in the history
new method: ->get_link(); replacement of ->follow_link().  The differences
are:
	* inode and dentry are passed separately
	* might be called both in RCU and non-RCU mode;
the former is indicated by passing it a NULL dentry.
	* when called that way it isn't allowed to block
and should return ERR_PTR(-ECHILD) if it needs to be called
in non-RCU mode.

It's a flagday change - the old method is gone, all in-tree instances
converted.  Conversion isn't hard; said that, so far very few instances
do not immediately bail out when called in RCU mode.  That'll change
in the next commits.

Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Al Viro committed Dec 9, 2015
1 parent 21fc61c commit 6b25539
Show file tree
Hide file tree
Showing 45 changed files with 234 additions and 132 deletions.
4 changes: 2 additions & 2 deletions Documentation/filesystems/Locking
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ prototypes:
int (*rename2) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*readlink) (struct dentry *, char __user *,int);
const char *(*follow_link) (struct dentry *, void **);
const char *(*get_link) (struct dentry *, struct inode *, void **);
void (*put_link) (struct inode *, void *);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int, unsigned int);
Expand Down Expand Up @@ -83,7 +83,7 @@ rmdir: yes (both) (see below)
rename: yes (all) (see below)
rename2: yes (all) (see below)
readlink: no
follow_link: no
get_link: no
put_link: no
setattr: yes
permission: no (may not block if called in rcu-walk mode)
Expand Down
6 changes: 6 additions & 0 deletions Documentation/filesystems/porting
Original file line number Diff line number Diff line change
Expand Up @@ -509,3 +509,9 @@ in your dentry operations instead.
any symlink that might use page_follow_link_light/page_put_link() must
have inode_nohighmem(inode) called before anything might start playing with
its pagecache.
--
[mandatory]
->follow_link() is replaced with ->get_link(); same API, except that
* ->get_link() gets inode as a separate argument
* ->get_link() may be called in RCU mode - in that case NULL
dentry is passed
8 changes: 5 additions & 3 deletions drivers/staging/lustre/lustre/llite/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,14 @@ static int ll_readlink_internal(struct inode *inode,
return rc;
}

static const char *ll_follow_link(struct dentry *dentry, void **cookie)
static const char *ll_get_link(struct dentry *dentry,
struct inode *inode, void **cookie)
{
struct inode *inode = d_inode(dentry);
struct ptlrpc_request *request = NULL;
int rc;
char *symname = NULL;
if (!dentry)
return ERR_PTR(-ECHILD);

CDEBUG(D_VFSTRACE, "VFS Op\n");
ll_inode_size_lock(inode);
Expand All @@ -149,7 +151,7 @@ static void ll_put_link(struct inode *unused, void *cookie)
struct inode_operations ll_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.setattr = ll_setattr,
.follow_link = ll_follow_link,
.get_link = ll_get_link,
.put_link = ll_put_link,
.getattr = ll_getattr,
.permission = ll_inode_permission,
Expand Down
17 changes: 12 additions & 5 deletions fs/9p/vfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1223,18 +1223,25 @@ ino_t v9fs_qid2ino(struct p9_qid *qid)
}

/**
* v9fs_vfs_follow_link - follow a symlink path
* v9fs_vfs_get_link - follow a symlink path
* @dentry: dentry for symlink
* @inode: inode for symlink
* @cookie: place to pass the data to put_link()
*/

static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie)
static const char *v9fs_vfs_get_link(struct dentry *dentry,
struct inode *inode, void **cookie)
{
struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
struct p9_fid *fid = v9fs_fid_lookup(dentry);
struct v9fs_session_info *v9ses;
struct p9_fid *fid;
struct p9_wstat *st;
char *res;

if (!dentry)
return ERR_PTR(-ECHILD);

v9ses = v9fs_dentry2v9ses(dentry);
fid = v9fs_fid_lookup(dentry);
p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);

if (IS_ERR(fid))
Expand Down Expand Up @@ -1452,7 +1459,7 @@ static const struct inode_operations v9fs_file_inode_operations = {

static const struct inode_operations v9fs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = v9fs_vfs_follow_link,
.get_link = v9fs_vfs_get_link,
.put_link = kfree_put_link,
.getattr = v9fs_vfs_getattr,
.setattr = v9fs_vfs_setattr,
Expand Down
14 changes: 10 additions & 4 deletions fs/9p/vfs_inode_dotl.c
Original file line number Diff line number Diff line change
Expand Up @@ -899,20 +899,26 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
}

/**
* v9fs_vfs_follow_link_dotl - follow a symlink path
* v9fs_vfs_get_link_dotl - follow a symlink path
* @dentry: dentry for symlink
* @inode: inode for symlink
* @cookie: place to pass the data to put_link()
*/

static const char *
v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie)
v9fs_vfs_get_link_dotl(struct dentry *dentry,
struct inode *inode, void **cookie)
{
struct p9_fid *fid = v9fs_fid_lookup(dentry);
struct p9_fid *fid;
char *target;
int retval;

if (!dentry)
return ERR_PTR(-ECHILD);

p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);

fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid))
return ERR_CAST(fid);
retval = p9_client_readlink(fid, &target);
Expand Down Expand Up @@ -984,7 +990,7 @@ const struct inode_operations v9fs_file_inode_operations_dotl = {

const struct inode_operations v9fs_symlink_inode_operations_dotl = {
.readlink = generic_readlink,
.follow_link = v9fs_vfs_follow_link_dotl,
.get_link = v9fs_vfs_get_link_dotl,
.put_link = kfree_put_link,
.getattr = v9fs_vfs_getattr_dotl,
.setattr = v9fs_vfs_setattr_dotl,
Expand Down
4 changes: 2 additions & 2 deletions fs/affs/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
char c;
char lc;

pr_debug("follow_link(ino=%lu)\n", inode->i_ino);
pr_debug("get_link(ino=%lu)\n", inode->i_ino);

bh = affs_bread(inode->i_sb, inode->i_ino);
if (!bh)
Expand Down Expand Up @@ -71,7 +71,7 @@ const struct address_space_operations affs_symlink_aops = {

const struct inode_operations affs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.get_link = page_get_link,
.put_link = page_put_link,
.setattr = affs_notify_change,
};
13 changes: 9 additions & 4 deletions fs/autofs4/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@

#include "autofs_i.h"

static const char *autofs4_follow_link(struct dentry *dentry, void **cookie)
static const char *autofs4_get_link(struct dentry *dentry,
struct inode *inode, void **cookie)
{
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct autofs_sb_info *sbi;
struct autofs_info *ino;
if (!dentry)
return ERR_PTR(-ECHILD);
sbi = autofs4_sbi(dentry->d_sb);
ino = autofs4_dentry_ino(dentry);
if (ino && !autofs4_oz_mode(sbi))
ino->last_used = jiffies;
return d_inode(dentry)->i_private;
}

const struct inode_operations autofs4_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = autofs4_follow_link
.get_link = autofs4_get_link
};
2 changes: 1 addition & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -10096,7 +10096,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
};
static const struct inode_operations btrfs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.get_link = page_get_link,
.put_link = page_put_link,
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
Expand Down
2 changes: 1 addition & 1 deletion fs/ceph/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1756,7 +1756,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode)
*/
static const struct inode_operations ceph_symlink_iops = {
.readlink = generic_readlink,
.follow_link = simple_follow_link,
.get_link = simple_get_link,
.setattr = ceph_setattr,
.getattr = ceph_getattr,
.setxattr = ceph_setxattr,
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ const struct inode_operations cifs_file_inode_ops = {

const struct inode_operations cifs_symlink_inode_ops = {
.readlink = generic_readlink,
.follow_link = cifs_follow_link,
.get_link = cifs_get_link,
.put_link = kfree_put_link,
.permission = cifs_permission,
/* BB add the following two eventually */
Expand Down
4 changes: 1 addition & 3 deletions fs/cifs/cifsfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,7 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
#endif

/* Functions related to symlinks */
extern const char *cifs_follow_link(struct dentry *direntry, void **cookie);
extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
int buflen);
extern const char *cifs_get_link(struct dentry *, struct inode *, void **);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
extern int cifs_removexattr(struct dentry *, const char *);
Expand Down
6 changes: 4 additions & 2 deletions fs/cifs/link.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
}

const char *
cifs_follow_link(struct dentry *direntry, void **cookie)
cifs_get_link(struct dentry *direntry, struct inode *inode, void **cookie)
{
struct inode *inode = d_inode(direntry);
int rc = -ENOMEM;
unsigned int xid;
char *full_path = NULL;
Expand All @@ -639,6 +638,9 @@ cifs_follow_link(struct dentry *direntry, void **cookie)
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;

if (!direntry)
return ERR_PTR(-ECHILD);

xid = get_xid();

tlink = cifs_sb_tlink(cifs_sb);
Expand Down
2 changes: 1 addition & 1 deletion fs/coda/cnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)

static const struct inode_operations coda_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.get_link = page_get_link,
.put_link = page_put_link,
.setattr = coda_setattr,
};
Expand Down
11 changes: 8 additions & 3 deletions fs/configfs/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,16 @@ static int configfs_getlink(struct dentry *dentry, char * path)

}

static const char *configfs_follow_link(struct dentry *dentry, void **cookie)
static const char *configfs_get_link(struct dentry *dentry,
struct inode *inode, void **cookie)
{
unsigned long page = get_zeroed_page(GFP_KERNEL);
unsigned long page;
int error;

if (!dentry)
return ERR_PTR(-ECHILD);

page = get_zeroed_page(GFP_KERNEL);
if (!page)
return ERR_PTR(-ENOMEM);

Expand All @@ -297,7 +302,7 @@ static const char *configfs_follow_link(struct dentry *dentry, void **cookie)
}

const struct inode_operations configfs_symlink_inode_operations = {
.follow_link = configfs_follow_link,
.get_link = configfs_get_link,
.readlink = generic_readlink,
.put_link = free_page_put_link,
.setattr = configfs_setattr,
Expand Down
2 changes: 1 addition & 1 deletion fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1734,7 +1734,7 @@ static unsigned d_flags_for_inode(struct inode *inode)
}

if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) {
if (unlikely(inode->i_op->follow_link)) {
if (unlikely(inode->i_op->get_link)) {
add_flags = DCACHE_SYMLINK_TYPE;
goto type_determined;
}
Expand Down
12 changes: 9 additions & 3 deletions fs/ecryptfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -674,10 +674,16 @@ static char *ecryptfs_readlink_lower(struct dentry *dentry, size_t *bufsiz)
return rc ? ERR_PTR(rc) : buf;
}

static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie)
static const char *ecryptfs_get_link(struct dentry *dentry,
struct inode *inode, void **cookie)
{
size_t len;
char *buf = ecryptfs_readlink_lower(dentry, &len);
char *buf;

if (!dentry)
return ERR_PTR(-ECHILD);

buf = ecryptfs_readlink_lower(dentry, &len);
if (IS_ERR(buf))
return buf;
fsstack_copy_attr_atime(d_inode(dentry),
Expand Down Expand Up @@ -1095,7 +1101,7 @@ static int ecryptfs_removexattr(struct dentry *dentry, const char *name)

const struct inode_operations ecryptfs_symlink_iops = {
.readlink = generic_readlink,
.follow_link = ecryptfs_follow_link,
.get_link = ecryptfs_get_link,
.put_link = kfree_put_link,
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
Expand Down
4 changes: 2 additions & 2 deletions fs/ext2/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

const struct inode_operations ext2_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.get_link = page_get_link,
.put_link = page_put_link,
.setattr = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
Expand All @@ -35,7 +35,7 @@ const struct inode_operations ext2_symlink_inode_operations = {

const struct inode_operations ext2_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = simple_follow_link,
.get_link = simple_get_link,
.setattr = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
Expand Down
13 changes: 8 additions & 5 deletions fs/ext4/symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@
#include "xattr.h"

#ifdef CONFIG_EXT4_FS_ENCRYPTION
static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cookie)
static const char *ext4_encrypted_get_link(struct dentry *dentry,
struct inode *inode, void **cookie)
{
struct page *cpage = NULL;
char *caddr, *paddr = NULL;
struct ext4_str cstr, pstr;
struct inode *inode = d_inode(dentry);
struct ext4_encrypted_symlink_data *sd;
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
int res;
u32 plen, max_size = inode->i_sb->s_blocksize;

if (!dentry)
return ERR_PTR(-ECHILD);

res = ext4_get_encryption_info(inode);
if (res)
return ERR_PTR(res);
Expand Down Expand Up @@ -87,7 +90,7 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook

const struct inode_operations ext4_encrypted_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = ext4_encrypted_follow_link,
.get_link = ext4_encrypted_get_link,
.put_link = kfree_put_link,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
Expand All @@ -99,7 +102,7 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = {

const struct inode_operations ext4_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.get_link = page_get_link,
.put_link = page_put_link,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
Expand All @@ -110,7 +113,7 @@ const struct inode_operations ext4_symlink_inode_operations = {

const struct inode_operations ext4_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = simple_follow_link,
.get_link = simple_get_link,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
Expand Down
Loading

0 comments on commit 6b25539

Please sign in to comment.