->get_link(), ->put_link() and cookies
Subsystems (39)
Al Viro
maintainer In cases when we need to pin the symlink body in some manner, we
need to undo whatever we'd done once the caller is done with the body. That went through several variants, the latest (in -next right now) being "have non-NULL ->put_link() and leave an argument for it in void *cookie, address of which is passed to ->get_link()".
Linus suggested getting rid of the cookie thing by deriving it
from the address of symlink body. That breaks in some cases, though - procfs and lustre, to name a couple. Symlink body may be not enough. Moreover, inode argument of ->put_link() is never used.
AFAICS, the following ends up a lot cleaner:
1) struct delayed_call {
void (*fn)(void *);
void *arg;
} is defined. Primitives: DEFINE_DELAYED_CALL(name) - similar to DEFINE_MUTEX, et.al.
set_delayed_call(p, fn, arg) - sets fn and arg
do_delayed_call(p) - if fn is set, calls fn(arg)
clear_delayed_call(p) - clears fn
2) in struct saved replace void *cookie; with struct delayed_call done; pass its address to ->get_link() and when/if it needs something to release the body, let it do set_delayed_call(done, fn, arg).
3) remove ->put_link entirely
4) get rid of allocations in overlayfs ->get_link() - just let the ->get_link() of underlying fs arrange the call it needs done and that's it; we don't need to keep a reference to inode of underlying symlink around, which removes the reason to allocate anything in there.
5) compensation of stack footprint changes: struct saved ->inode had been used for two things - storing the inode between the pick_link() and get_link() and keeping it until we decide to call its ->put_link(). Since the latter is gone now, we don't need to store that thing in nd->stack[...] - we can move it to nd->link_inode and be done with that. That compensates the increase of struct saved size, so we are only left with one extra word per struct nameidata instance.
The reasons why I like it more than the variant proposed by Linus:
* it allows to avoid convolutions in things like procfs and lustre
->get_link() instances; sure, we could stash a reference to the structure we'll need somewhere near the symlink body, use container_of(), etc., but it's a lot more convoluted
* it doesn't need to keep an inode reference around, only for the
purpose of locating ->put_link(). Seeing that ->put_link() instances do not give a damn about the inode, that's rather pointless (note, BTW, that all we are promised in RCU mode is that memory of this struct inode hadn't been reused yet).
I'd done that on top of vfs.git#work.symlinks; it's pretty much the same as incremental I'd posted three weeks ago, except for the switch of three instances from get_free_page/free_page_link to kmalloc/kfree_link.
It's in vfs.git#proposed.symlinks. It survives the local beating. Please, review and comment.
Shortlog:
Al Viro (13):
switch befs long symlinks to page_symlink_operations
logfs: don't duplicate page_symlink_inode_operations
udf: don't duplicate page_symlink_inode_operations
ufs: get rid of ->setattr() for symlinks
namei: page_getlink() and page_follow_link_light() are the same thing don't put symlink bodies in pagecache into highmem
replace ->follow_link() with new method that could stay in RCU mode teach page_get_link() to work in RCU mode
teach shmem_get_link() to work in RCU mode
teach proc_self_get_link()/proc_thread_self_get_link() to work in RCU mode teach nfs_get_link() to work in RCU mode
kill free_page_put_link()
switch ->get_link() to delayed_call, kill ->put_link()
Diffstat:
Documentation/filesystems/Locking | 6 +- Documentation/filesystems/porting | 17 ++++ Documentation/filesystems/vfs.txt | 21 ++--- drivers/staging/lustre/lustre/llite/symlink.c | 24 ++--- fs/9p/vfs_inode.c | 24 +++-- fs/9p/vfs_inode_dotl.c | 21 +++-- fs/affs/inode.c | 1 + fs/affs/namei.c | 1 + fs/affs/symlink.c | 9 +- fs/afs/inode.c | 1 + fs/autofs4/symlink.c | 14 ++- fs/befs/linuxvfs.c | 40 ++++---- fs/btrfs/inode.c | 5 +- fs/ceph/inode.c | 2 +- fs/cifs/cifsfs.c | 3 +- fs/cifs/cifsfs.h | 5 +- fs/cifs/link.c | 10 +- fs/coda/cnode.c | 5 +- fs/coda/symlink.c | 4 +- fs/configfs/symlink.c | 22 +++-- fs/cramfs/inode.c | 1 + fs/dcache.c | 2 +- fs/ecryptfs/inode.c | 17 +++- fs/efs/inode.c | 1 + fs/efs/symlink.c | 4 +- fs/exofs/inode.c | 1 + fs/exofs/namei.c | 1 + fs/ext2/inode.c | 1 + fs/ext2/namei.c | 1 + fs/ext2/symlink.c | 5 +- fs/ext4/inode.c | 1 + fs/ext4/namei.c | 1 + fs/ext4/symlink.c | 29 +++--- fs/f2fs/inode.c | 1 + fs/f2fs/namei.c | 31 ++++--- fs/freevxfs/vxfs_inode.c | 1 + fs/fuse/dir.c | 17 ++-- fs/gfs2/inode.c | 19 ++-- fs/hfsplus/inode.c | 2 + fs/hostfs/hostfs_kern.c | 22 ++--- fs/hpfs/inode.c | 1 + fs/hpfs/namei.c | 5 +- fs/hugetlbfs/inode.c | 1 + fs/inode.c | 6 ++ fs/isofs/inode.c | 1 + fs/isofs/rock.c | 4 +- fs/jffs2/symlink.c | 2 +- fs/jfs/inode.c | 1 + fs/jfs/namei.c | 1 + fs/jfs/symlink.c | 5 +- fs/kernfs/symlink.c | 24 +++-- fs/libfs.c | 22 ++--- fs/logfs/dir.c | 9 +- fs/logfs/inode.c | 3 +- fs/logfs/logfs.h | 1 - fs/minix/inode.c | 4 +- fs/namei.c | 126 +++++++++++++------------- fs/ncpfs/inode.c | 4 +- fs/nfs/inode.c | 26 +++++- fs/nfs/symlink.c | 39 +++++--- fs/nilfs2/inode.c | 1 + fs/nilfs2/namei.c | 4 +- fs/ocfs2/inode.c | 1 + fs/ocfs2/namei.c | 1 + fs/ocfs2/symlink.c | 3 +- fs/overlayfs/inode.c | 53 ++--------- fs/proc/base.c | 24 +++-- fs/proc/inode.c | 21 +++-- fs/proc/namespaces.c | 10 +- fs/proc/self.c | 18 ++-- fs/proc/thread_self.c | 19 ++-- fs/qnx4/inode.c | 1 + fs/qnx6/inode.c | 1 + fs/ramfs/inode.c | 1 + fs/reiserfs/inode.c | 1 + fs/reiserfs/namei.c | 4 +- fs/romfs/super.c | 1 + fs/squashfs/inode.c | 2 + fs/squashfs/symlink.c | 3 +- fs/sysv/inode.c | 4 +- fs/ubifs/file.c | 2 +- fs/udf/inode.c | 3 +- fs/udf/namei.c | 8 +- fs/udf/symlink.c | 4 +- fs/udf/udfdecl.h | 1 - fs/ufs/Makefile | 2 +- fs/ufs/inode.c | 5 +- fs/ufs/namei.c | 5 +- fs/ufs/symlink.c | 42 --------- fs/ufs/ufs.h | 4 - fs/xfs/xfs_iops.c | 14 ++- include/linux/delayed_call.h | 34 +++++++ include/linux/fs.h | 16 ++-- include/linux/nfs_fs.h | 1 + mm/shmem.c | 48 ++++++---- 95 files changed, 570 insertions(+), 470 deletions(-) delete mode 100644 fs/ufs/symlink.c create mode 100644 include/linux/delayed_call.h
Al Viro
maintainerPATCH 1/13 switch befs long symlinks to page_symlink_operations
From: Al Viro <[email protected]>
just give them the right ->readpage()...
--- fs/befs/linuxvfs.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-)
fs/befs/linuxvfs.c +22 -19
@@ -42,7 +42,7 @@ 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 const char *befs_follow_link (struct dentry *,void **); +static int befs_symlink_readpage (struct file *,struct page *);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,
@@ -79,10 +79,8 @@ static const struct address_space_operations befs_aops = { .bmap = befs_bmap, }; -static const struct inode_operations befs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = befs_follow_link, - .put_link = kfree_put_link, +static const struct address_space_operations befs_symlink_aops = { + .readpage = befs_symlink_readpage, };/*
@@ -398,7 +396,8 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &befs_dir_operations; }else if (S_ISLNK (inode->i_mode)) {if (befs_ino->i_flags &BEFS_LONG_SYMLINK ) { - inode->i_op = &befs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &befs_symlink_aops; }else { inode->i_link = befs_ino->i_data.symlink; inode->i_op = &simple_symlink_inode_operations;
@@ -463,31 +462,35 @@ befs_destroy_inodecache(void)* The data stream become link name. Unless the LONG_SYMLINK * flag is set. */ -static const char * -befs_follow_link (struct dentry *dentry,void **cookie) +static int befs_symlink_readpage (struct file *unused,struct page *page) { -struct super_block *sb = dentry->d_sb; -struct befs_inode_info *befs_ino =BEFS_I (d_inode (dentry)); +struct inode *inode = page->mapping->host; +struct super_block *sb = inode->i_sb; +struct 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; +char *link =kmap (page); -if (len ==0 ) { +if (len ==0 || len >PAGE_SIZE ) {befs_error (sb,"Long symlink with illegal length" ); -return ERR_PTR (-EIO ); + goto fail; }befs_debug (sb,"Follow long symlink" ); - link =kmalloc (len,GFP_NOFS ); -if (!link) -return ERR_PTR (-ENOMEM );if (befs_read_lsymlink (sb, data, link, len) != len) { -kfree (link);befs_error (sb,"Failed to read entire long symlink" ); -return ERR_PTR (-EIO ); + goto fail; } link[len -1 ] ='\0' ; -return *cookie = link; +SetPageUptodate (page); +kunmap (page); +unlock_page (page); +return 0 ; +fail: +SetPageError (page); +kunmap (page); +unlock_page (page); +return -EIO ; }/*
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 2/13 logfs: don't duplicate page_symlink_inode_operations
In reply to Al ViroFrom: Al Viro <[email protected]>
--- fs/logfs/dir.c | 8 +------- fs/logfs/inode.c | 2 +- fs/logfs/logfs.h | 1 - 3 files changed, 2 insertions(+), 9 deletions(-)
fs/logfs/dir.c +1 -7
@@ -528,7 +528,7 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,if (IS_ERR (inode))return PTR_ERR (inode); - inode->i_op = &logfs_symlink_iops; + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &logfs_reg_aops;return __logfs_create (dir, dentry, inode, target, destlen);
@@ -776,12 +776,6 @@ fail:return -EIO ; } -const struct inode_operations logfs_symlink_iops = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -}; -const struct inode_operations logfs_dir_iops = { .create = logfs_create, .link = logfs_link,
fs/logfs/inode.c +1 -1
@@ -64,7 +64,7 @@ static void logfs_inode_setops(struct inode *inode) inode->i_mapping->a_ops = &logfs_reg_aops;break ;case S_IFLNK : - inode->i_op = &logfs_symlink_iops; + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &logfs_reg_aops;break ;case S_IFSOCK :/* fall through */
fs/logfs/logfs.h +0 -1
@@ -495,7 +495,6 @@ static inline int logfs_get_sb_mtd(struct logfs_super *s, int mtdnr)#endif /* dir.c */ -extern const struct inode_operations logfs_symlink_iops;extern const struct inode_operations logfs_dir_iops;extern const struct file_operations logfs_dir_fops;int logfs_replay_journal (struct super_block *sb);
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 3/13 udf: don't duplicate page_symlink_inode_operations
In reply to Al ViroFrom: Al Viro <[email protected]>
--- fs/udf/inode.c | 2 +- fs/udf/namei.c | 7 +------ fs/udf/udfdecl.h | 1 - 3 files changed, 2 insertions(+), 8 deletions(-)
fs/udf/inode.c +1 -1
@@ -1540,7 +1540,7 @@ reread:break ;case ICBTAG_FILE_TYPE_SYMLINK : inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &udf_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; inode->i_mode =S_IFLNK |S_IRWXUGO ;break ;case ICBTAG_FILE_TYPE_MAIN :
fs/udf/namei.c +1 -6
@@ -921,7 +921,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, } inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &udf_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations;if (iinfo->i_alloc_type !=ICBTAG_FLAG_AD_IN_ICB ) {struct kernel_lb_addr eloc;
@@ -1344,8 +1344,3 @@ const struct inode_operations udf_dir_inode_operations = { .rename = udf_rename, .tmpfile = udf_tmpfile, }; -const struct inode_operations udf_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -};
fs/udf/udfdecl.h +0 -1
@@ -85,7 +85,6 @@ extern const struct inode_operations udf_dir_inode_operations;extern const struct file_operations udf_dir_operations;extern const struct inode_operations udf_file_inode_operations;extern const struct file_operations udf_file_operations; -extern const struct inode_operations udf_symlink_inode_operations;extern const struct address_space_operations udf_aops;extern const struct address_space_operations udf_adinicb_aops;extern const struct address_space_operations udf_symlink_aops;
Signature · 1 line
2.1.4
Re: [PATCH 03/13] udf: don't duplicate page_symlink_inode_operations
Quoted 3 lines · Al Viro
From: Al Viro <[email protected]>
Signed-off-by Al Viro <[email protected]>
Looks good. I guess it's more convenient for you to take this change through your tree so:
Honza
Quoted 60 lines
--- fs/udf/inode.c | 2 +- fs/udf/namei.c | 7 +------ fs/udf/udfdecl.h | 1 - 3 files changed, 2 insertions(+), 8 deletions(-)fs/udf/inode.c +1
-1@@ -1540,7 +1540,7 @@ reread:break ;case ICBTAG_FILE_TYPE_SYMLINK : inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &udf_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; inode->i_mode =S_IFLNK |S_IRWXUGO ;break ;case ICBTAG_FILE_TYPE_MAIN :fs/udf/namei.c +1
-6@@ -921,7 +921,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, } inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &udf_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations;if (iinfo->i_alloc_type !=ICBTAG_FLAG_AD_IN_ICB ) {struct kernel_lb_addr eloc;@@ -1344,8 +1344,3 @@ const struct inode_operations udf_dir_inode_operations = { .rename = udf_rename, .tmpfile = udf_tmpfile, }; -const struct inode_operations udf_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -};fs/udf/udfdecl.h +0
-1@@ -85,7 +85,6 @@ extern const struct inode_operations udf_dir_inode_operations;extern const struct file_operations udf_dir_operations;extern const struct inode_operations udf_file_inode_operations;extern const struct file_operations udf_file_operations; -extern const struct inode_operations udf_symlink_inode_operations;extern const struct address_space_operations udf_aops;extern const struct address_space_operations udf_adinicb_aops;extern const struct address_space_operations udf_symlink_aops;Signature · 6 lines
2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Al Viro
maintainerPATCH 4/13 ufs: get rid of ->setattr() for symlinks
In reply to Al ViroFrom: Al Viro <[email protected]>
It was to needed for a couple of months in 2010, until UFS quota support got dropped. Since then it's equivalent to simple_setattr() (i.e. the default) for everything except the regular files. And dropping it there allows to convert all UFS symlinks to {page,simple}_symlink_inode_operations, getting rid of fs/ufs/symlink.c completely.
--- fs/ufs/Makefile | 2 +- fs/ufs/inode.c | 4 ++-- fs/ufs/namei.c | 4 ++-- fs/ufs/symlink.c | 42 ------------------------------------------ fs/ufs/ufs.h | 4 ---- 5 files changed, 5 insertions(+), 51 deletions(-) delete mode 100644 fs/ufs/symlink.c
fs/ufs/Makefile +1 -1
@@ -5,5 +5,5 @@ obj-$(CONFIG_UFS_FS) += ufs.o ufs-objs := balloc.o cylinder.o dir.o file.o ialloc.o inode.o \ - namei.o super.o symlink.o util.o + namei.o super.o util.o ccflags-$(CONFIG_UFS_DEBUG) += -DDEBUG
fs/ufs/inode.c +2 -2
@@ -528,11 +528,11 @@ static void ufs_set_inode_ops(struct inode *inode) inode->i_mapping->a_ops = &ufs_aops; }else if (S_ISLNK (inode->i_mode)) {if (!inode->i_blocks) { - inode->i_op = &ufs_fast_symlink_inode_operations; inode->i_link = (char *)UFS_I (inode)->i_u1.i_symlink; + inode->i_op = &simple_symlink_inode_operations; }else { - inode->i_op = &ufs_symlink_inode_operations; inode->i_mapping->a_ops = &ufs_aops; + inode->i_op = &page_symlink_inode_operations; } }else init_special_inode(inode, inode->i_mode,
fs/ufs/namei.c +2 -2
@@ -123,14 +123,14 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,if (l >UFS_SB (sb)->s_uspi->s_maxsymlinklen) {/* slow symlink */ - inode->i_op = &ufs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &ufs_aops; err =page_symlink (inode, symname, l);if (err) goto out_fail; }else {/* fast symlink */ - inode->i_op = &ufs_fast_symlink_inode_operations; + inode->i_op = &simple_symlink_inode_operations; inode->i_link = (char *)UFS_I (inode)->i_u1.i_symlink;memcpy (inode->i_link, symname, l); inode->i_size = l-1 ;
fs/ufs/symlink.c deleted +0 -42
42 lines not on this page · raw message
fs/ufs/ufs.h +0 -4
@@ -136,10 +136,6 @@ extern __printf(3, 4)void ufs_panic (struct super_block *,const char *,const char *, ...);void ufs_mark_sb_dirty (struct super_block *sb); -/* symlink.c */ -extern const struct inode_operations ufs_fast_symlink_inode_operations; -extern const struct inode_operations ufs_symlink_inode_operations; -static inline struct ufs_sb_info *UFS_SB (struct super_block *sb) {return sb->s_fs_info;
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 5/13 namei: page_getlink() and page_follow_link_light() are the same thing
In reply to Al ViroFrom: Al Viro <[email protected]>
--- fs/namei.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-)
fs/namei.c +13 -19
@@ -4518,7 +4518,7 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)EXPORT_SYMBOL (generic_readlink);/* get the link contents into pagecache */ -static char *page_getlink (struct dentry * dentry,struct page **ppage) +static const char *page_getlink (struct dentry * dentry,void **cookie) {char *kaddr;struct page *page;
@@ -4526,31 +4526,15 @@ static char *page_getlink(struct dentry * dentry, struct page **ppage) page =read_mapping_page (mapping,0 ,NULL );if (IS_ERR (page))return (char *)page; - *ppage = page; + *cookie = page; kaddr =kmap (page);nd_terminate_link (kaddr, dentry->d_inode->i_size,PAGE_SIZE -1 );return kaddr; } -int page_readlink (struct dentry *dentry,char __user *buffer,int buflen) -{ -struct page *page =NULL ; -int res =readlink_copy (buffer, buflen,page_getlink (dentry, &page)); -if (page) { -kunmap (page); -page_cache_release (page); - } -return res; -} -EXPORT_SYMBOL (page_readlink); -const char *page_follow_link_light (struct dentry *dentry,void **cookie) { -struct page *page =NULL ; -char *res =page_getlink (dentry, &page); -if (!IS_ERR (res)) - *cookie = page; -return res; +return page_getlink (dentry, cookie); }EXPORT_SYMBOL (page_follow_link_light);
@@ -4562,6 +4546,16 @@ void page_put_link(struct inode *unused, void *cookie) }EXPORT_SYMBOL (page_put_link); +int page_readlink (struct dentry *dentry,char __user *buffer,int buflen) +{ +void *cookie =NULL ; +int res =readlink_copy (buffer, buflen,page_getlink (dentry, &cookie)); +if (cookie) +page_put_link (NULL , cookie); +return res; +} +EXPORT_SYMBOL (page_readlink); +/* * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS */
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 6/13 don't put symlink bodies in pagecache into highmem
In reply to Al ViroFrom: Al Viro <[email protected]>
kmap() in page_follow_link_light() needed to go - allowing to hold an arbitrary number of kmaps for long is a great way to deadlocking the system.
new helper (inode_nohighmem(inode)) needs to be used for pagecache symlinks inodes; done for all in-tree cases. page_follow_link_light() instrumented to yell about anything missed.
--- Documentation/filesystems/porting | 5 +++++ fs/affs/inode.c | 1 + fs/affs/namei.c | 1 + fs/affs/symlink.c | 4 +--- fs/afs/inode.c | 1 + fs/befs/linuxvfs.c | 5 ++--- fs/btrfs/inode.c | 2 ++ fs/coda/cnode.c | 2 ++ fs/coda/symlink.c | 4 +--- fs/cramfs/inode.c | 1 + fs/efs/inode.c | 1 + fs/efs/symlink.c | 4 +--- fs/exofs/inode.c | 1 + fs/exofs/namei.c | 1 + fs/ext2/inode.c | 1 + fs/ext2/namei.c | 1 + fs/ext4/inode.c | 1 + fs/ext4/namei.c | 1 + fs/ext4/symlink.c | 10 +++------- fs/f2fs/inode.c | 1 + fs/f2fs/namei.c | 5 ++--- fs/freevxfs/vxfs_inode.c | 1 + fs/hfsplus/inode.c | 2 ++ fs/hpfs/inode.c | 1 + fs/hpfs/namei.c | 5 ++--- fs/hugetlbfs/inode.c | 1 + fs/inode.c | 6 ++++++ fs/isofs/inode.c | 1 + fs/isofs/rock.c | 4 +--- fs/jfs/inode.c | 1 + fs/jfs/namei.c | 1 + fs/logfs/dir.c | 1 + fs/logfs/inode.c | 1 + fs/minix/inode.c | 1 + fs/namei.c | 9 +++------ fs/ncpfs/inode.c | 1 + fs/nfs/inode.c | 5 +++-- fs/nfs/symlink.c | 2 +- fs/nilfs2/inode.c | 1 + fs/nilfs2/namei.c | 1 + fs/ocfs2/inode.c | 1 + fs/ocfs2/namei.c | 1 + fs/qnx4/inode.c | 1 + fs/qnx6/inode.c | 1 + fs/ramfs/inode.c | 1 + fs/reiserfs/inode.c | 1 + fs/reiserfs/namei.c | 1 + fs/romfs/super.c | 1 + fs/squashfs/inode.c | 2 ++ fs/sysv/inode.c | 1 + fs/udf/inode.c | 1 + fs/udf/namei.c | 1 + fs/udf/symlink.c | 4 +--- fs/ufs/inode.c | 1 + fs/ufs/namei.c | 1 + include/linux/fs.h | 1 + mm/shmem.c | 9 +++------ 57 files changed, 81 insertions(+), 46 deletions(-)
Documentation/filesystems/porting +5 -0
@@ -504,3 +504,8 @@ in your dentry operations instead. [mandatory] __fd_install() & fd_install() can now sleep. Callers should not hold a spinlock or other resources that do not allow a schedule. +-- +[mandatory] + 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.
fs/affs/inode.c +1 -0
@@ -140,6 +140,7 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)break ;case ST_SOFTLINK : inode->i_mode |=S_IFLNK ; +inode_nohighmem (inode); inode->i_op = &affs_symlink_inode_operations; inode->i_data.a_ops = &affs_symlink_aops;break ;
fs/affs/namei.c +1 -0
@@ -344,6 +344,7 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)return -ENOSPC ; inode->i_op = &affs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_data.a_ops = &affs_symlink_aops; inode->i_mode =S_IFLNK |0777 ;mode_to_prot (inode);
fs/affs/symlink.c +1 -3
@@ -14,7 +14,7 @@ static int affs_symlink_readpage(struct file *file, struct page *page) {struct buffer_head *bh;struct inode *inode = page->mapping->host; -char *link =kmap (page); +char *link =page_address (page);struct slink_front *lf;int i, j;char c;
@@ -57,12 +57,10 @@ static int affs_symlink_readpage(struct file *file, struct page *page) link[i] ='\0' ;affs_brelse (bh);SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ; fail:SetPageError (page); -kunmap (page);unlock_page (page);return -EIO ; }
fs/afs/inode.c +1 -0
@@ -56,6 +56,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)case AFS_FTYPE_SYMLINK : inode->i_mode =S_IFLNK | vnode->status.mode; inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode);break ;default :printk ("kAFS: AFS vnode with undefined type\n" );
fs/befs/linuxvfs.c +2 -3
@@ -397,6 +397,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) }else if (S_ISLNK (inode->i_mode)) {if (befs_ino->i_flags &BEFS_LONG_SYMLINK ) { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &befs_symlink_aops; }else { inode->i_link = befs_ino->i_data.symlink;
@@ -469,7 +470,7 @@ static int befs_symlink_readpage(struct file *unused, struct page *page)struct 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 =kmap (page); +char *link =page_address (page);if (len ==0 || len >PAGE_SIZE ) {befs_error (sb,"Long symlink with illegal length" );
@@ -483,12 +484,10 @@ static int befs_symlink_readpage(struct file *unused, struct page *page) } link[len -1 ] ='\0' ;SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ; fail:SetPageError (page); -kunmap (page);unlock_page (page);return -EIO ; }
fs/btrfs/inode.c +2 -0
@@ -3774,6 +3774,7 @@ cache_acl:break ;case S_IFLNK : inode->i_op = &btrfs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &btrfs_symlink_aops;break ;default :
@@ -9705,6 +9706,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,btrfs_free_path (path); inode->i_op = &btrfs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &btrfs_symlink_aops;inode_set_bytes (inode, name_len);btrfs_i_size_write (inode, name_len);
fs/coda/cnode.c +2 -0
@@ -8,6 +8,7 @@#include <linux/coda.h> #include <linux/coda_psdev.h> +#include <linux/pagemap.h> #include "coda_linux.h" static inline int coda_fideq (struct CodaFid *fid1,struct CodaFid *fid2)
@@ -35,6 +36,7 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) inode->i_fop = &coda_dir_operations; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &coda_symlink_inode_operations; +inode_nohighmem (inode); inode->i_data.a_ops = &coda_symlink_aops; inode->i_mapping = &inode->i_data; }else
fs/coda/symlink.c +1 -3
@@ -26,7 +26,7 @@ static int coda_symlink_filler(struct file *file, struct page *page)int error;struct coda_inode_info *cii;unsigned int len =PAGE_SIZE ; -char *p =kmap (page); +char *p =page_address (page); cii =ITOC (inode);
@@ -34,13 +34,11 @@ static int coda_symlink_filler(struct file *file, struct page *page)if (error) goto fail;SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ; fail:SetPageError (page); -kunmap (page);unlock_page (page);return error; }
fs/cramfs/inode.c +1 -0
@@ -100,6 +100,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb,break ;case S_IFLNK : inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_data.a_ops = &cramfs_aops;break ;default :
fs/efs/inode.c +1 -0
@@ -151,6 +151,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino)break ;case S_IFLNK : inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_data.a_ops = &efs_symlink_aops;break ;case S_IFCHR :
fs/efs/symlink.c +1 -3
@@ -13,7 +13,7 @@static int efs_symlink_readpage (struct file *file,struct page *page) { -char *link =kmap (page); +char *link =page_address (page);struct buffer_head * bh;struct inode * inode = page->mapping->host;efs_block_t size = inode->i_size;
@@ -39,12 +39,10 @@ static int efs_symlink_readpage(struct file *file, struct page *page) } link[size] ='\0' ;SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ; fail:SetPageError (page); -kunmap (page);unlock_page (page);return err; }
fs/exofs/inode.c +1 -0
@@ -1227,6 +1227,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino) inode->i_link = (char *)oi->i_data; }else { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &exofs_aops; } }else {
fs/exofs/namei.c +1 -0
@@ -111,6 +111,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry,if (l >sizeof (oi->i_data)) {/* slow symlink */ inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &exofs_aops;memset (oi->i_data,0 ,sizeof (oi->i_data));
fs/ext2/inode.c +1 -0
@@ -1420,6 +1420,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) sizeof(ei->i_data) - 1); }else { inode->i_op = &ext2_symlink_inode_operations; +inode_nohighmem (inode);if (test_opt (inode->i_sb,NOBH )) inode->i_mapping->a_ops = &ext2_nobh_aops;else
fs/ext2/namei.c +1 -0
@@ -183,6 +183,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,if (l >sizeof (EXT2_I (inode)->i_data)) {/* slow symlink */ inode->i_op = &ext2_symlink_inode_operations; +inode_nohighmem (inode);if (test_opt (inode->i_sb,NOBH )) inode->i_mapping->a_ops = &ext2_nobh_aops;else
fs/ext4/inode.c +1 -0
@@ -4283,6 +4283,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_symlink_inode_operations;ext4_set_aops (inode); } +inode_nohighmem (inode); }else if (S_ISCHR (inode->i_mode) ||S_ISBLK (inode->i_mode) ||S_ISFIFO (inode->i_mode) ||S_ISSOCK (inode->i_mode)) { inode->i_op = &ext4_special_inode_operations;
fs/ext4/namei.c +1 -0
@@ -3132,6 +3132,7 @@ static int ext4_symlink(struct inode *dir,if ((disk_link.len >EXT4_N_BLOCKS *4 )) {if (!encryption_required) inode->i_op = &ext4_symlink_inode_operations; +inode_nohighmem (inode);ext4_set_aops (inode);/* * We cannot call page_symlink() with transaction started
fs/ext4/symlink.c +3 -7
@@ -45,7 +45,7 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook cpage =read_mapping_page (inode->i_mapping,0 ,NULL );if (IS_ERR (cpage))return ERR_CAST (cpage); - caddr =kmap (cpage); + caddr =page_address (cpage); caddr[size] =0 ; }
@@ -75,16 +75,12 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook/* Null-terminate the name */ if (res <= plen) paddr[res] ='\0' ; -if (cpage) { -kunmap (cpage); +if (cpage)page_cache_release (cpage); - }return *cookie = paddr; errout: -if (cpage) { -kunmap (cpage); +if (cpage)page_cache_release (cpage); - }kfree (paddr);return ERR_PTR (res); }
fs/f2fs/inode.c +1 -0
@@ -202,6 +202,7 @@ make_now: inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &f2fs_dblock_aops; }else if (S_ISCHR (inode->i_mode) ||S_ISBLK (inode->i_mode) ||S_ISFIFO (inode->i_mode) ||S_ISSOCK (inode->i_mode)) {
fs/f2fs/namei.c +2 -3
@@ -351,6 +351,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode->i_op = &f2fs_encrypted_symlink_inode_operations; else inode->i_op = &f2fs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &f2fs_dblock_aops;f2fs_lock_op (sbi);
@@ -942,7 +943,7 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook cpage =read_mapping_page (inode->i_mapping,0 ,NULL );if (IS_ERR (cpage))return ERR_CAST (cpage); - caddr =kmap (cpage); + caddr =page_address (cpage); caddr[size] =0 ;/* Symlink is encrypted */
@@ -982,13 +983,11 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook/* Null-terminate the name */ paddr[res] ='\0' ; -kunmap (cpage);page_cache_release (cpage);return *cookie = paddr; errout:kfree (cstr.name);f2fs_fname_crypto_free_buffer (&pstr); -kunmap (cpage);page_cache_release (cpage);return ERR_PTR (res); }
fs/freevxfs/vxfs_inode.c +1 -0
@@ -326,6 +326,7 @@ vxfs_iget(struct super_block *sbp, ino_t ino) }else if (S_ISLNK (ip->i_mode)) {if (!VXFS_ISIMMED (vip)) { ip->i_op = &page_symlink_inode_operations; +inode_nohighmem (ip); ip->i_mapping->a_ops = &vxfs_aops; }else { ip->i_op = &simple_symlink_inode_operations;
fs/hfsplus/inode.c +2 -0
@@ -403,6 +403,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode) }else if (S_ISLNK (inode->i_mode)) { sbi->file_count++; inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &hfsplus_aops; hip->clump_blocks =1 ; }else
@@ -526,6 +527,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) inode->i_mapping->a_ops = &hfsplus_aops; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &hfsplus_aops; }else { init_special_inode(inode, inode->i_mode,
fs/hpfs/inode.c +1 -0
@@ -77,6 +77,7 @@ void hpfs_read_inode(struct inode *i)kfree (ea); i->i_mode =S_IFLNK |0777 ; i->i_op = &page_symlink_inode_operations; +inode_nohighmem (i); i->i_data.a_ops = &hpfs_symlink_aops;set_nlink (i,1 ); i->i_size = ea_size;
fs/hpfs/namei.c +2 -3
@@ -332,6 +332,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy result->i_blocks =1 ;set_nlink (result,1 ); result->i_size =strlen (symlink); +inode_nohighmem (result); result->i_op = &page_symlink_inode_operations; result->i_data.a_ops = &hpfs_symlink_aops;
@@ -500,7 +501,7 @@ out:static int hpfs_symlink_readpage (struct file *file,struct page *page) { -char *link =kmap (page); +char *link =page_address (page);struct inode *i = page->mapping->host;struct fnode *fnode;struct buffer_head *bh;
@@ -516,14 +517,12 @@ static int hpfs_symlink_readpage(struct file *file, struct page *page) goto fail;hpfs_unlock (i->i_sb);SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ; fail:hpfs_unlock (i->i_sb);SetPageError (page); -kunmap (page);unlock_page (page);return err; }
fs/hugetlbfs/inode.c +1 -0
@@ -760,6 +760,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,break ;case S_IFLNK : inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode);break ; }lockdep_annotate_inode_mutex_key (inode);
fs/inode.c +6 -0
@@ -2028,3 +2028,9 @@ void inode_set_flags(struct inode *inode, unsigned int flags, new_flags) != old_flags)); }EXPORT_SYMBOL (inode_set_flags); + +void inode_nohighmem (struct inode *inode) +{ +mapping_set_gfp_mask (inode->i_mapping,GFP_USER ); +} +EXPORT_SYMBOL (inode_nohighmem);
fs/isofs/inode.c +1 -0
@@ -1417,6 +1417,7 @@ static int isofs_read_inode(struct inode *inode, int relocated) inode->i_fop = &isofs_dir_operations; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_data.a_ops = &isofs_symlink_aops; }else /* XXX - parse_rock_ridge_inode() had already set i_rdev. */
fs/isofs/rock.c +1 -3
@@ -687,7 +687,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page)struct inode *inode = page->mapping->host;struct iso_inode_info *ei =ISOFS_I (inode);struct isofs_sb_info *sbi =ISOFS_SB (inode->i_sb); -char *link =kmap (page); +char *link =page_address (page);unsigned long bufsize =ISOFS_BUFFER_SIZE (inode);struct buffer_head *bh;char *rpnt = link;
@@ -774,7 +774,6 @@ repeat:brelse (bh); *rpnt ='\0' ;SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ;
@@ -791,7 +790,6 @@ fail:brelse (bh); error:SetPageError (page); -kunmap (page);unlock_page (page);return -EIO ; }
fs/jfs/inode.c +1 -0
@@ -60,6 +60,7 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino) }else if (S_ISLNK (inode->i_mode)) {if (inode->i_size >=IDATASIZE ) { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &jfs_aops; }else { inode->i_op = &jfs_fast_symlink_inode_operations;
fs/jfs/namei.c +1 -0
@@ -983,6 +983,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,jfs_info ("jfs_symlink: allocate extent ip:0x%p" , ip); ip->i_op = &jfs_symlink_inode_operations; +inode_nohighmem (ip); ip->i_mapping->a_ops = &jfs_aops;/*
fs/logfs/dir.c +1 -0
@@ -529,6 +529,7 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,return PTR_ERR (inode); inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &logfs_reg_aops;return __logfs_create (dir, dentry, inode, target, destlen);
fs/logfs/inode.c +1 -0
@@ -65,6 +65,7 @@ static void logfs_inode_setops(struct inode *inode)break ;case S_IFLNK : inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &logfs_reg_aops;break ;case S_IFSOCK :/* fall through */
fs/minix/inode.c +1 -0
@@ -452,6 +452,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev) inode->i_mapping->a_ops = &minix_aops; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &minix_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &minix_aops; }else init_special_inode (inode, inode->i_mode, rdev);
fs/namei.c +3 -6
@@ -4527,7 +4527,8 @@ static const char *page_getlink(struct dentry * dentry, void **cookie)if (IS_ERR (page))return (char *)page; *cookie = page; - kaddr =kmap (page); +BUG_ON (mapping_gfp_mask (mapping) & __GFP_HIGHMEM); + kaddr =page_address (page);nd_terminate_link (kaddr, dentry->d_inode->i_size,PAGE_SIZE -1 );return kaddr; }
@@ -4541,7 +4542,6 @@ EXPORT_SYMBOL(page_follow_link_light);void page_put_link (struct inode *unused,void *cookie) {struct page *page = cookie; -kunmap (page);page_cache_release (page); }EXPORT_SYMBOL (page_put_link);
@@ -4565,7 +4565,6 @@ int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)struct page *page;void *fsdata;int err; -char *kaddr;unsigned int flags =AOP_FLAG_UNINTERRUPTIBLE ;if (nofs) flags |=AOP_FLAG_NOFS ;
@@ -4576,9 +4575,7 @@ retry:if (err) goto fail; - kaddr =kmap_atomic (page); -memcpy (kaddr, symname, len-1 ); -kunmap_atomic (kaddr); +memcpy (page_address (page), symname, len-1 ); err =pagecache_write_end (NULL , mapping,0 , len-1 , len-1 , page, fsdata);
fs/ncpfs/inode.c +1 -0
@@ -283,6 +283,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info)#if defined(CONFIG_NCPFS_EXTRAS ) || defined(CONFIG_NCPFS_NFS_NS ) } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &ncp_symlink_inode_operations; +inode_nohighmem (inode); inode->i_data.a_ops = &ncp_symlink_aops;#endif }else {
fs/nfs/inode.c +3 -2
@@ -408,9 +408,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st inode->i_fop =NULL ; inode->i_flags |=S_AUTOMOUNT ; } - }else if (S_ISLNK (inode->i_mode)) + }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &nfs_symlink_inode_operations; -else +inode_nohighmem (inode); + }else init_special_inode (inode, inode->i_mode, fattr->rdev);memset (&inode->i_atime,0 ,sizeof (inode->i_atime));
fs/nfs/symlink.c +1 -1
@@ -56,7 +56,7 @@ static const char *nfs_follow_link(struct dentry *dentry, void **cookie)if (IS_ERR (page))return ERR_CAST (page); *cookie = page; -return kmap (page); +return page_address (page); }/*
fs/nilfs2/inode.c +1 -0
@@ -510,6 +510,7 @@ static int __nilfs_read_inode(struct super_block *sb, inode->i_mapping->a_ops = &nilfs_aops; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &nilfs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &nilfs_aops; }else { inode->i_op = &nilfs_special_inode_operations;
fs/nilfs2/namei.c +1 -0
@@ -161,6 +161,7 @@ static int nilfs_symlink(struct inode *dir, struct dentry *dentry,/* slow symlink */ inode->i_op = &nilfs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &nilfs_aops; err =page_symlink (inode, symname, l);if (err)
fs/ocfs2/inode.c +1 -0
@@ -361,6 +361,7 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,break ;case S_IFLNK : inode->i_op = &ocfs2_symlink_inode_operations; +inode_nohighmem (inode);i_size_write (inode,le64_to_cpu (fe->i_size));break ;default :
fs/ocfs2/namei.c +1 -0
@@ -1960,6 +1960,7 @@ static int ocfs2_symlink(struct inode *dir, inode->i_rdev =0 ; newsize = l -1 ; inode->i_op = &ocfs2_symlink_inode_operations; +inode_nohighmem (inode);if (l >ocfs2_fast_symlink_chars (sb)) {u32 offset =0 ;
fs/qnx4/inode.c +1 -0
@@ -316,6 +316,7 @@ struct inode *qnx4_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &qnx4_dir_operations; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &qnx4_aops;qnx4_i (inode)->mmu_private = inode->i_size; }else {
fs/qnx6/inode.c +1 -0
@@ -582,6 +582,7 @@ struct inode *qnx6_iget(struct super_block *sb, unsigned ino) inode->i_mapping->a_ops = &qnx6_aops; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &qnx6_aops; }else init_special_inode (inode, inode->i_mode,0 );
fs/ramfs/inode.c +1 -0
@@ -79,6 +79,7 @@ struct inode *ramfs_get_inode(struct super_block *sb,break ;case S_IFLNK : inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode);break ; } }
fs/reiserfs/inode.c +1 -0
@@ -1361,6 +1361,7 @@ static void init_inode(struct inode *inode, struct treepath *path) inode->i_fop = &reiserfs_dir_operations; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &reiserfs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &reiserfs_address_space_operations; }else { inode->i_blocks =0 ;
fs/reiserfs/namei.c +1 -0
@@ -1170,6 +1170,7 @@ static int reiserfs_symlink(struct inode *parent_dir,reiserfs_update_inode_transaction (parent_dir); inode->i_op = &reiserfs_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &reiserfs_address_space_operations; retval = reiserfs_add_entry(&th, parent_dir, dentry->d_name.name,
fs/romfs/super.c +1 -0
@@ -360,6 +360,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)break ;case ROMFH_SYM : i->i_op = &page_symlink_inode_operations; +inode_nohighmem (i); i->i_data.a_ops = &romfs_aops; mode |=S_IRWXUGO ;break ;
fs/squashfs/inode.c +2 -0
@@ -41,6 +41,7 @@#include <linux/fs.h> #include <linux/vfs.h> #include <linux/xattr.h> +#include <linux/pagemap.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h"
@@ -291,6 +292,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)set_nlink (inode,le32_to_cpu (sqsh_ino->nlink)); inode->i_size =le32_to_cpu (sqsh_ino->symlink_size); inode->i_op = &squashfs_symlink_inode_ops; +inode_nohighmem (inode); inode->i_data.a_ops = &squashfs_symlink_aops; inode->i_mode |=S_IFLNK ;squashfs_i (inode)->start = block;
fs/sysv/inode.c +1 -0
@@ -163,6 +163,7 @@ void sysv_set_inode(struct inode *inode, dev_t rdev) inode->i_mapping->a_ops = &sysv_aops; }else if (S_ISLNK (inode->i_mode)) { inode->i_op = &sysv_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &sysv_aops; }else init_special_inode (inode, inode->i_mode, rdev);
fs/udf/inode.c +1 -0
@@ -1541,6 +1541,7 @@ reread:case ICBTAG_FILE_TYPE_SYMLINK : inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mode =S_IFLNK |S_IRWXUGO ;break ;case ICBTAG_FILE_TYPE_MAIN :
fs/udf/namei.c +1 -0
@@ -922,6 +922,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode);if (iinfo->i_alloc_type !=ICBTAG_FLAG_AD_IN_ICB ) {struct kernel_lb_addr eloc;
fs/udf/symlink.c +1 -3
@@ -107,7 +107,7 @@ static int udf_symlink_filler(struct file *file, struct page *page)struct buffer_head *bh =NULL ;unsigned char *symlink;int err; -unsigned char *p =kmap (page); +unsigned char *p =page_address (page);struct udf_inode_info *iinfo;uint32_t pos;
@@ -141,7 +141,6 @@ static int udf_symlink_filler(struct file *file, struct page *page)up_read (&iinfo->i_data_sem);SetPageUptodate (page); -kunmap (page);unlock_page (page);return 0 ;
@@ -149,7 +148,6 @@ out_unlock_inode:up_read (&iinfo->i_data_sem);SetPageError (page); out_unmap: -kunmap (page);unlock_page (page);return err; }
fs/ufs/inode.c +1 -0
@@ -533,6 +533,7 @@ static void ufs_set_inode_ops(struct inode *inode) }else { inode->i_mapping->a_ops = &ufs_aops; inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); } }else init_special_inode(inode, inode->i_mode,
fs/ufs/namei.c +1 -0
@@ -124,6 +124,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,if (l >UFS_SB (sb)->s_uspi->s_maxsymlinklen) {/* slow symlink */ inode->i_op = &page_symlink_inode_operations; +inode_nohighmem (inode); inode->i_mapping->a_ops = &ufs_aops; err =page_symlink (inode, symname, l);if (err)
include/linux/fs.h +1 -0
@@ -3025,5 +3025,6 @@ static inline bool dir_relax(struct inode *inode) }extern bool path_noexec (const struct path *path); +extern void inode_nohighmem (struct inode *inode);#endif /* _LINUX_FS_H */
mm/shmem.c +3 -6
@@ -2444,7 +2444,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *sint len;struct inode *inode;struct page *page; -char *kaddr;struct shmem_inode_info *info; len =strlen (symname) +1 ;
@@ -2483,9 +2482,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s } inode->i_mapping->a_ops = &shmem_aops; inode->i_op = &shmem_symlink_inode_operations; - kaddr =kmap_atomic (page); -memcpy (kaddr, symname, len); -kunmap_atomic (kaddr); +inode_nohighmem (inode); +memcpy (page_address (page), symname, len);SetPageUptodate (page);set_page_dirty (page);unlock_page (page);
@@ -2506,13 +2504,12 @@ static const char *shmem_follow_link(struct dentry *dentry, void **cookie)return ERR_PTR (error);unlock_page (page); *cookie = page; -return kmap (page); +return page_address (page); }static void shmem_put_link (struct inode *unused,void *cookie) {struct page *page = cookie; -kunmap (page);mark_page_accessed (page);page_cache_release (page); }
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 7/13 replace ->follow_link() with new method that could stay in RCU mode
In reply to Al ViroFrom: Al Viro <[email protected]>
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.
--- Documentation/filesystems/Locking | 4 +-- Documentation/filesystems/porting | 6 ++++ drivers/staging/lustre/lustre/llite/symlink.c | 8 +++-- fs/9p/vfs_inode.c | 17 +++++++--- fs/9p/vfs_inode_dotl.c | 14 ++++++--- fs/affs/symlink.c | 4 +-- fs/autofs4/symlink.c | 13 +++++--- fs/btrfs/inode.c | 2 +- fs/ceph/inode.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/cifs/cifsfs.h | 4 +-- fs/cifs/link.c | 6 ++-- fs/coda/cnode.c | 2 +- fs/configfs/symlink.c | 11 +++++-- fs/dcache.c | 2 +- fs/ecryptfs/inode.c | 12 +++++-- fs/ext2/symlink.c | 4 +-- fs/ext4/symlink.c | 13 +++++--- fs/f2fs/namei.c | 16 ++++++---- fs/fuse/dir.c | 9 ++++-- fs/gfs2/inode.c | 15 ++++++--- fs/hostfs/hostfs_kern.c | 10 ++++-- fs/jffs2/symlink.c | 2 +- fs/jfs/symlink.c | 4 +-- fs/kernfs/symlink.c | 11 +++++-- fs/libfs.c | 9 +++--- fs/minix/inode.c | 2 +- fs/namei.c | 45 +++++++++++++++++---------- fs/ncpfs/inode.c | 2 +- fs/nfs/symlink.c | 9 ++++-- fs/nilfs2/namei.c | 2 +- fs/ocfs2/symlink.c | 2 +- fs/overlayfs/inode.c | 12 ++++--- fs/proc/base.c | 22 +++++++------ fs/proc/inode.c | 7 +++-- fs/proc/namespaces.c | 9 ++++-- fs/proc/self.c | 9 ++++-- fs/proc/thread_self.c | 9 ++++-- fs/reiserfs/namei.c | 2 +- fs/squashfs/symlink.c | 2 +- fs/sysv/inode.c | 2 +- fs/ubifs/file.c | 2 +- fs/xfs/xfs_iops.c | 8 +++-- include/linux/fs.h | 6 ++-- mm/shmem.c | 12 ++++--- 45 files changed, 234 insertions(+), 132 deletions(-)
Documentation/filesystems/Locking +2 -2
@@ -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);
@@ -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)
Documentation/filesystems/porting +6 -0
@@ -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
drivers/staging/lustre/lustre/llite/symlink.c +5 -3
@@ -118,12 +118,14 @@ failed: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);
@@ -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,
fs/9p/vfs_inode.c +12 -5
@@ -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))
@@ -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,
fs/9p/vfs_inode_dotl.c +10 -4
@@ -899,20 +899,26 @@ error: }/** -* 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);
@@ -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,
fs/affs/symlink.c +2 -2
@@ -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)
@@ -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, };
fs/autofs4/symlink.c +9 -4
@@ -12,10 +12,15 @@#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;
@@ -23,5 +28,5 @@ static const char *autofs4_follow_link(struct dentry *dentry, void **cookie)const struct inode_operations autofs4_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = autofs4_follow_link + .get_link = autofs4_get_link };
fs/btrfs/inode.c +1 -1
@@ -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,
fs/ceph/inode.c +1 -1
@@ -1756,7 +1756,7 @@ retry:*/ 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,
fs/cifs/cifsfs.c +1 -1
@@ -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 */
fs/cifs/cifsfs.h +1 -3
@@ -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 *);
fs/cifs/link.c +4 -2
@@ -627,9 +627,8 @@ cifs_hl_exit: }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 ;
@@ -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);
fs/coda/cnode.c +1 -1
@@ -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, };
fs/configfs/symlink.c +8 -3
@@ -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 );
@@ -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,
fs/dcache.c +1 -1
@@ -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; }
fs/ecryptfs/inode.c +9 -3
@@ -674,10 +674,16 @@ out: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),
@@ -1095,7 +1101,7 @@ out: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,
fs/ext2/symlink.c +2 -2
@@ -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
@@ -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,
fs/ext4/symlink.c +8 -5
@@ -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);
@@ -87,7 +90,7 @@ errout: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,
@@ -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,
@@ -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,
fs/f2fs/namei.c +10 -6
@@ -315,9 +315,10 @@ fail:return err; } -static const char *f2fs_follow_link (struct dentry *dentry,void **cookie) +static const char *f2fs_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -const char *link =page_follow_link_light (dentry, cookie); +const char *link =page_get_link (dentry, inode, cookie);if (!IS_ERR (link) && !*link) {/* this is broken symlink case */ page_put_link (NULL , *cookie);
@@ -924,18 +925,21 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, }#ifdef CONFIG_F2FS_FS_ENCRYPTION -static const char *f2fs_encrypted_follow_link (struct dentry *dentry,void **cookie) +static const char *f2fs_encrypted_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) {struct page *cpage =NULL ;char *caddr, *paddr =NULL ;struct f2fs_str cstr;struct f2fs_str pstr =FSTR_INIT (NULL ,0 ); -struct inode *inode =d_inode (dentry);struct f2fs_encrypted_symlink_data *sd;loff_t size =min_t (loff_t,i_size_read (inode),PAGE_SIZE -1 );u32 max_size = inode->i_sb->s_blocksize;int res; +if (!dentry) +return ERR_PTR (-ECHILD ); + res =f2fs_get_encryption_info (inode);if (res)return ERR_PTR (res);
@@ -994,7 +998,7 @@ errout:const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = f2fs_encrypted_follow_link, + .get_link = f2fs_encrypted_get_link, .put_link = kfree_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr,
@@ -1030,7 +1034,7 @@ const struct inode_operations f2fs_dir_inode_operations = {const struct inode_operations f2fs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = f2fs_follow_link, + .get_link = f2fs_get_link, .put_link = page_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr,
fs/fuse/dir.c +6 -3
@@ -1365,14 +1365,17 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)return err; } -static const char *fuse_follow_link (struct dentry *dentry,void **cookie) +static const char *fuse_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct inode *inode =d_inode (dentry);struct fuse_conn *fc =get_fuse_conn (inode);FUSE_ARGS (args);char *link;ssize_t ret; +if (!dentry) +return ERR_PTR (-ECHILD ); + link = (char *)__get_free_page (GFP_KERNEL );if (!link)return ERR_PTR (-ENOMEM );
@@ -1909,7 +1912,7 @@ static const struct inode_operations fuse_common_inode_operations = {static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, - .follow_link = fuse_follow_link, + .get_link = fuse_get_link, .put_link = free_page_put_link, .readlink = generic_readlink, .getattr = fuse_getattr,
fs/gfs2/inode.c +10 -5
@@ -1712,24 +1712,29 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry, }/** -* gfs2_follow_link - Follow a symbolic link +* gfs2_get_link - Follow a symbolic link * @dentry: The dentry of the link -* @nd: Data that we pass to vfs_follow_link() +* @inode: The inode of the link +* @cookie: place to store the information for ->put_link() * * This can handle symlinks of any size. * * Returns: 0 on success or error code */ -static const char *gfs2_follow_link (struct dentry *dentry,void **cookie) +static const char *gfs2_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct gfs2_inode *ip =GFS2_I (d_inode (dentry)); +struct gfs2_inode *ip =GFS2_I (inode);struct gfs2_holder i_gh;struct buffer_head *dibh;unsigned int size;char *buf;int error; +if (!dentry) +return ERR_PTR (-ECHILD ); +gfs2_holder_init (ip->i_gl,LM_ST_SHARED ,0 , &i_gh); error =gfs2_glock_nq (&i_gh);if (error) {
@@ -2132,7 +2137,7 @@ const struct inode_operations gfs2_dir_iops = {const struct inode_operations gfs2_symlink_iops = { .readlink = generic_readlink, - .follow_link = gfs2_follow_link, + .get_link = gfs2_get_link, .put_link = kfree_put_link, .permission = gfs2_permission, .setattr = gfs2_setattr,
fs/hostfs/hostfs_kern.c +7 -3
@@ -892,9 +892,13 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -static const char *hostfs_follow_link (struct dentry *dentry,void **cookie) +static const char *hostfs_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -char *link =__getname (); +char *link; +if (!dentry) +return ERR_PTR (-ECHILD ); + link =__getname ();if (link) {char *path =dentry_name (dentry);int err = -ENOMEM ;
@@ -922,7 +926,7 @@ static void hostfs_put_link(struct inode *unused, void *cookie)static const struct inode_operations hostfs_link_iops = { .readlink = generic_readlink, - .follow_link = hostfs_follow_link, + .get_link = hostfs_get_link, .put_link = hostfs_put_link, };
fs/jffs2/symlink.c +1 -1
@@ -14,7 +14,7 @@const struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = jffs2_setattr, .setxattr = jffs2_setxattr, .getxattr = jffs2_getxattr,
fs/jfs/symlink.c +2 -2
@@ -23,7 +23,7 @@const struct inode_operations jfs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr,
@@ -33,7 +33,7 @@ const struct inode_operations jfs_fast_symlink_inode_operations = {const struct inode_operations jfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr,
fs/kernfs/symlink.c +8 -3
@@ -112,10 +112,15 @@ static int kernfs_getlink(struct dentry *dentry, char *path)return error; } -static const char *kernfs_iop_follow_link (struct dentry *dentry,void **cookie) +static const char *kernfs_iop_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) {int error = -ENOMEM ; -unsigned long page =get_zeroed_page (GFP_KERNEL ); +unsigned long page; + +if (!dentry) +return ERR_PTR (-ECHILD ); + page =get_zeroed_page (GFP_KERNEL );if (!page)return ERR_PTR (-ENOMEM ); error =kernfs_getlink (dentry, (char *)page);
@@ -132,7 +137,7 @@ const struct inode_operations kernfs_symlink_iops = { .getxattr = kernfs_iop_getxattr, .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, - .follow_link = kernfs_iop_follow_link, + .get_link = kernfs_iop_get_link, .put_link = free_page_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr,
fs/libfs.c +5 -4
@@ -1092,14 +1092,15 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, }EXPORT_SYMBOL (simple_nosetlease); -const char *simple_follow_link (struct dentry *dentry,void **cookie) +const char *simple_get_link (struct dentry *dentry,struct inode *inode, +void **cookie) { -return d_inode (dentry)->i_link; +return inode->i_link; } -EXPORT_SYMBOL (simple_follow_link); +EXPORT_SYMBOL (simple_get_link);const struct inode_operations simple_symlink_inode_operations = { - .follow_link = simple_follow_link, + .get_link = simple_get_link, .readlink = generic_readlink };EXPORT_SYMBOL (simple_symlink_inode_operations);
fs/minix/inode.c +1 -1
@@ -435,7 +435,7 @@ static const struct address_space_operations minix_aops = {static const struct inode_operations minix_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = minix_getattr, };
fs/namei.c +28 -17
100 lines not on this page · raw message
fs/ncpfs/inode.c +1 -1
@@ -244,7 +244,7 @@ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)#if defined(CONFIG_NCPFS_EXTRAS ) || defined(CONFIG_NCPFS_NFS_NS )static const struct inode_operations ncp_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = ncp_notify_change, };
fs/nfs/symlink.c +6 -3
@@ -42,12 +42,15 @@ error:return -EIO ; } -static const char *nfs_follow_link (struct dentry *dentry,void **cookie) +static const char *nfs_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct inode *inode =d_inode (dentry);struct page *page;void *err; +if (!dentry) +return ERR_PTR (-ECHILD ); + err =ERR_PTR (nfs_revalidate_mapping (inode, inode->i_mapping));if (err)return err;
@@ -64,7 +67,7 @@ static const char *nfs_follow_link(struct dentry *dentry, void **cookie)*/ const struct inode_operations nfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = nfs_follow_link, + .get_link = nfs_get_link, .put_link = page_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr,
fs/nilfs2/namei.c +1 -1
@@ -569,7 +569,7 @@ const struct inode_operations nilfs_special_inode_operations = {const struct inode_operations nilfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .permission = nilfs_permission, };
fs/ocfs2/symlink.c +1 -1
@@ -88,7 +88,7 @@ const struct address_space_operations ocfs2_fast_symlink_aops = {const struct inode_operations ocfs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr,
fs/overlayfs/inode.c +8 -4
@@ -137,17 +137,21 @@ struct ovl_link_data {void *cookie; }; -static const char *ovl_follow_link (struct dentry *dentry,void **cookie) +static const char *ovl_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) {struct dentry *realdentry;struct inode *realinode;struct ovl_link_data *data =NULL ;const char *ret; +if (!dentry) +return ERR_PTR (-ECHILD ); + realdentry =ovl_dentry_real (dentry); realinode = realdentry->d_inode; -if (WARN_ON (!realinode->i_op->follow_link)) +if (WARN_ON (!realinode->i_op->get_link))return ERR_PTR (-EPERM );if (realinode->i_op->put_link) {
@@ -157,7 +161,7 @@ static const char *ovl_follow_link(struct dentry *dentry, void **cookie) data->realdentry = realdentry; } - ret = realinode->i_op->follow_link (realdentry, cookie); + ret = realinode->i_op->get_link (realdentry, realinode, cookie);if (IS_ERR_OR_NULL (ret)) {kfree (data);return ret;
@@ -378,7 +382,7 @@ static const struct inode_operations ovl_file_inode_operations = {static const struct inode_operations ovl_symlink_inode_operations = { .setattr = ovl_setattr, - .follow_link = ovl_follow_link, + .get_link = ovl_get_link, .put_link = ovl_put_link, .readlink = ovl_readlink, .getattr = ovl_getattr,
fs/proc/base.c +13 -9
@@ -1564,12 +1564,15 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)return -ENOENT ; } -static const char *proc_pid_follow_link (struct dentry *dentry,void **cookie) +static const char *proc_pid_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct inode *inode =d_inode (dentry);struct path path;int error = -EACCES ; +if (!dentry) +return ERR_PTR (-ECHILD ); +/* Are we allowed to snoop on the tasks file descriptors? */ if (!proc_fd_access_allowed (inode)) goto out;
@@ -1630,7 +1633,7 @@ out:const struct inode_operations proc_pid_link_inode_operations = { .readlink = proc_pid_readlink, - .follow_link = proc_pid_follow_link, + .get_link = proc_pid_get_link, .setattr = proc_setattr, };
@@ -1895,7 +1898,7 @@ static const struct dentry_operations tid_map_files_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int proc_map_files_get_link (struct dentry *dentry,struct path *path) +static int map_files_get_link (struct dentry *dentry,struct path *path) {unsigned long vm_start, vm_end;struct vm_area_struct *vma;
@@ -1945,20 +1948,21 @@ struct map_files_info {* path to the file in question. */ static const char * -proc_map_files_follow_link (struct dentry *dentry,void **cookie) +proc_map_files_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) {if (!capable (CAP_SYS_ADMIN ))return ERR_PTR (-EPERM ); -return proc_pid_follow_link (dentry,NULL ); +return proc_pid_get_link (dentry, inode,NULL ); }/* -* Identical to proc_pid_link_inode_operations except for follow_link() +* Identical to proc_pid_link_inode_operations except for get_link() */ static const struct inode_operations proc_map_files_link_inode_operations = { .readlink = proc_pid_readlink, - .follow_link = proc_map_files_follow_link, + .get_link = proc_map_files_get_link, .setattr = proc_setattr, };
@@ -1975,7 +1979,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,return -ENOENT ; ei =PROC_I (inode); - ei->op.proc_get_link = proc_map_files_get_link; + ei->op.proc_get_link = map_files_get_link; inode->i_op = &proc_map_files_link_inode_operations; inode->i_size =64 ;
fs/proc/inode.c +4 -3
@@ -393,9 +393,10 @@ static const struct file_operations proc_reg_file_ops_no_compat = { };#endif -static const char *proc_follow_link (struct dentry *dentry,void **cookie) +static const char *proc_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct proc_dir_entry *pde =PDE (d_inode (dentry)); +struct proc_dir_entry *pde =PDE (inode);if (unlikely (!use_pde (pde)))return ERR_PTR (-EINVAL ); *cookie = pde;
@@ -409,7 +410,7 @@ static void proc_put_link(struct inode *unused, void *p)const struct inode_operations proc_link_inode_operations = { .readlink = generic_readlink, - .follow_link = proc_follow_link, + .get_link = proc_get_link, .put_link = proc_put_link, };
fs/proc/namespaces.c +6 -3
@@ -30,14 +30,17 @@ static const struct proc_ns_operations *ns_entries[] = { &mntns_operations, }; -static const char *proc_ns_follow_link (struct dentry *dentry,void **cookie) +static const char *proc_ns_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct inode *inode =d_inode (dentry);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 (!dentry) +return ERR_PTR (-ECHILD ); + task =get_proc_task (inode);if (!task)return error;
@@ -74,7 +77,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflstatic const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, - .follow_link = proc_ns_follow_link, + .get_link = proc_ns_get_link, .setattr = proc_setattr, };
fs/proc/self.c +6 -3
@@ -18,12 +18,15 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer,return readlink_copy (buffer, buflen, tmp); } -static const char *proc_self_follow_link (struct dentry *dentry,void **cookie) +static const char *proc_self_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct pid_namespace *ns = dentry->d_sb->s_fs_info; +struct pid_namespace *ns = inode->i_sb->s_fs_info;pid_t tgid =task_tgid_nr_ns (current, ns);char *name; +if (!dentry) +return ERR_PTR (-ECHILD );if (!tgid)return ERR_PTR (-ENOENT );/* 11 for max length of signed int in decimal + NULL term */
@@ -36,7 +39,7 @@ static const char *proc_self_follow_link(struct dentry *dentry, void **cookie)static const struct inode_operations proc_self_inode_operations = { .readlink = proc_self_readlink, - .follow_link = proc_self_follow_link, + .get_link = proc_self_get_link, .put_link = kfree_put_link, };
fs/proc/thread_self.c +6 -3
@@ -19,13 +19,16 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer,return readlink_copy (buffer, buflen, tmp); } -static const char *proc_thread_self_follow_link (struct dentry *dentry,void **cookie) +static const char *proc_thread_self_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) { -struct pid_namespace *ns = dentry->d_sb->s_fs_info; +struct pid_namespace *ns = inode->i_sb->s_fs_info;pid_t tgid =task_tgid_nr_ns (current, ns);pid_t pid =task_pid_nr_ns (current, ns);char *name; +if (!dentry) +return ERR_PTR (-ECHILD );if (!pid)return ERR_PTR (-ENOENT ); name =kmalloc (PROC_NUMBUF +6 +PROC_NUMBUF ,GFP_KERNEL );
@@ -37,7 +40,7 @@ static const char *proc_thread_self_follow_link(struct dentry *dentry, void **costatic const struct inode_operations proc_thread_self_inode_operations = { .readlink = proc_thread_self_readlink, - .follow_link = proc_thread_self_follow_link, + .get_link = proc_thread_self_get_link, .put_link = kfree_put_link, };
fs/reiserfs/namei.c +1 -1
@@ -1665,7 +1665,7 @@ const struct inode_operations reiserfs_dir_inode_operations = {*/ const struct inode_operations reiserfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = reiserfs_setattr, .setxattr = reiserfs_setxattr,
fs/squashfs/symlink.c +1 -1
@@ -119,7 +119,7 @@ const struct address_space_operations squashfs_symlink_aops = {const struct inode_operations squashfs_symlink_inode_ops = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getxattr = generic_getxattr, .listxattr = squashfs_listxattr
fs/sysv/inode.c +1 -1
@@ -146,7 +146,7 @@ static inline void write3byte(struct sysv_sb_info *sbi,static const struct inode_operations sysv_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = sysv_getattr, };
fs/ubifs/file.c +1 -1
@@ -1608,7 +1608,7 @@ const struct inode_operations ubifs_file_inode_operations = {const struct inode_operations ubifs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = ubifs_setxattr,
fs/xfs/xfs_iops.c +6 -2
@@ -414,13 +414,17 @@ xfs_vn_rename(* uio is kmalloced for this reason... */ STATIC const char * -xfs_vn_follow_link ( +xfs_vn_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) {char *link;int error = -ENOMEM ; +if (!dentry) +return ERR_PTR (-ECHILD ); + link =kmalloc (MAXPATHLEN +1 ,GFP_KERNEL );if (!link) goto out_err;
@@ -1172,7 +1176,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {static const struct inode_operations xfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = xfs_vn_follow_link, + .get_link = xfs_vn_get_link, .put_link = kfree_put_link, .getattr = xfs_vn_getattr, .setattr = xfs_vn_setattr,
include/linux/fs.h +3 -3
@@ -1633,7 +1633,7 @@ struct file_operations {struct inode_operations {struct dentry * (*lookup) (struct inode *,struct dentry *,unsigned int ); -const char * (*follow_link) (struct dentry *,void **); +const char * (*get_link) (struct dentry *,struct inode *,void **);int (*permission) (struct inode *,int );struct posix_acl * (*get_acl)(struct inode *,int );
@@ -2736,7 +2736,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 const char *page_follow_link_light (struct dentry *,void **); +extern const char *page_get_link (struct dentry *,struct inode *,void **);extern void page_put_link (struct inode *,void *);extern int __page_symlink (struct inode *inode,const char *symname,int len,int nofs);
@@ -2754,7 +2754,7 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes);void inode_sub_bytes (struct inode *inode,loff_t bytes);loff_t inode_get_bytes (struct inode *inode);void inode_set_bytes (struct inode *inode,loff_t bytes); -const char *simple_follow_link (struct dentry *,void **); +const char *simple_get_link (struct dentry *,struct inode *,void **);extern const struct inode_operations simple_symlink_inode_operations;extern int iterate_dir (struct file *,struct dir_context *);
mm/shmem.c +8 -4
@@ -2496,10 +2496,14 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *sreturn 0 ; } -static const char *shmem_follow_link (struct dentry *dentry,void **cookie) +static const char *shmem_get_link (struct dentry *dentry, +struct inode *inode,void **cookie) {struct page *page =NULL ; -int error =shmem_getpage (d_inode (dentry),0 , &page,SGP_READ ,NULL ); +int error; +if (!dentry) +return ERR_PTR (-ECHILD ); + error =shmem_getpage (inode,0 , &page,SGP_READ ,NULL );if (error)return ERR_PTR (error);unlock_page (page);
@@ -2656,7 +2660,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)static const struct inode_operations shmem_short_symlink_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr,
@@ -2667,7 +2671,7 @@ static const struct inode_operations shmem_short_symlink_operations = {static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = shmem_follow_link, + .get_link = shmem_get_link, .put_link = shmem_put_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr,
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 8/13 teach page_get_link() to work in RCU mode
In reply to Al ViroFrom: Al Viro <[email protected]>
more or less along the lines of Neil's patchset, sans the insanity around kmap().
--- fs/namei.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
fs/namei.c +13 -6
@@ -4533,12 +4533,19 @@ const char *page_get_link(struct dentry *dentry, struct inode *inode,struct page *page;struct address_space *mapping = inode->i_mapping; -if (!dentry) -return ERR_PTR (-ECHILD ); - - page =read_mapping_page (mapping,0 ,NULL ); -if (IS_ERR (page)) -return (char *)page; +if (!dentry) { + page =find_get_page (mapping,0 ); +if (!page) +return ERR_PTR (-ECHILD ); +if (!PageUptodate (page)) { +put_page (page); +return ERR_PTR (-ECHILD ); + } + }else { + page =read_mapping_page (mapping,0 ,NULL ); +if (IS_ERR (page)) +return (char *)page; + } *cookie = page;BUG_ON (mapping_gfp_mask (mapping) & __GFP_HIGHMEM); kaddr =page_address (page);
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 9/13 teach shmem_get_link() to work in RCU mode
In reply to Al ViroFrom: Al Viro <[email protected]>
--- mm/shmem.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
mm/shmem.c +14 -6
@@ -2501,12 +2501,20 @@ static const char *shmem_get_link(struct dentry *dentry, {struct page *page =NULL ;int error; -if (!dentry) -return ERR_PTR (-ECHILD ); - error =shmem_getpage (inode,0 , &page,SGP_READ ,NULL ); -if (error) -return ERR_PTR (error); -unlock_page (page); +if (!dentry) { + page =find_get_page (inode->i_mapping,0 ); +if (!page) +return ERR_PTR (-ECHILD ); +if (!PageUptodate (page)) { +put_page (page); +return ERR_PTR (-ECHILD ); + } + }else { + error =shmem_getpage (inode,0 , &page,SGP_READ ,NULL ); +if (error) +return ERR_PTR (error); +unlock_page (page); + } *cookie = page;return page_address (page); }
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 10/13 teach proc_self_get_link()/proc_thread_self_get_link() to work in RCU mode
In reply to Al ViroFrom: Al Viro <[email protected]>
--- fs/proc/self.c | 8 +++----- fs/proc/thread_self.c | 9 ++++----- 2 files changed, 7 insertions(+), 10 deletions(-)
fs/proc/self.c +3 -5
@@ -25,14 +25,12 @@ static const char *proc_self_get_link(struct dentry *dentry,pid_t tgid =task_tgid_nr_ns (current, ns);char *name; -if (!dentry) -return ERR_PTR (-ECHILD );if (!tgid)return ERR_PTR (-ENOENT );/* 11 for max length of signed int in decimal + NULL term */ - name =kmalloc (12 ,GFP_KERNEL ); -if (!name) -return ERR_PTR (-ENOMEM ); + name =kmalloc (12 , dentry ?GFP_KERNEL :GFP_ATOMIC ); +if (unlikely (!name)) +return dentry ?ERR_PTR (-ENOMEM ) :ERR_PTR (-ECHILD );sprintf (name,"%d" , tgid);return *cookie = name; }
fs/proc/thread_self.c +4 -5
@@ -27,13 +27,12 @@ static const char *proc_thread_self_get_link(struct dentry *dentry,pid_t pid =task_pid_nr_ns (current, ns);char *name; -if (!dentry) -return ERR_PTR (-ECHILD );if (!pid)return ERR_PTR (-ENOENT ); - name =kmalloc (PROC_NUMBUF +6 +PROC_NUMBUF ,GFP_KERNEL ); -if (!name) -return ERR_PTR (-ENOMEM ); + name =kmalloc (PROC_NUMBUF +6 +PROC_NUMBUF , + dentry ?GFP_KERNEL :GFP_ATOMIC ); +if (unlikely (!name)) +return dentry ?ERR_PTR (-ENOMEM ) :ERR_PTR (-ECHILD );sprintf (name,"%d/task/%d" , tgid, pid);return *cookie = name; }
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 11/13 teach nfs_get_link() to work in RCU mode
In reply to Al ViroFrom: Al Viro <[email protected]>
based upon the corresponding patch from Neil's March patchset, again with kmap-related horrors removed.
--- fs/nfs/inode.c | 21 +++++++++++++++++++++ fs/nfs/symlink.c | 30 ++++++++++++++++++++---------- include/linux/nfs_fs.h | 1 + 3 files changed, 42 insertions(+), 10 deletions(-)
fs/nfs/inode.c +21 -0
@@ -1087,6 +1087,27 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode) || NFS_STALE(inode); } +int nfs_revalidate_mapping_rcu (struct inode *inode) +{ +struct nfs_inode *nfsi =NFS_I (inode); +unsigned long *bitlock = &nfsi->flags; +int ret =0 ; + +if (IS_SWAPFILE (inode)) + goto out; +if (nfs_mapping_need_revalidate_inode (inode)) { + ret = -ECHILD ; + goto out; + } +spin_lock (&inode->i_lock); +if (test_bit (NFS_INO_INVALIDATING , bitlock) || + (nfsi->cache_validity &NFS_INO_INVALID_DATA )) + ret = -ECHILD ; +spin_unlock (&inode->i_lock); +out: +return ret; +} +/** * __nfs_revalidate_mapping - Revalidate the pagecache * @inode - pointer to host inode
fs/nfs/symlink.c +20 -10
@@ -48,16 +48,26 @@ static const char *nfs_get_link(struct dentry *dentry,struct page *page;void *err; -if (!dentry) -return ERR_PTR (-ECHILD ); - - err =ERR_PTR (nfs_revalidate_mapping (inode, inode->i_mapping)); -if (err) -return err; - page =read_cache_page (&inode->i_data,0 , - (filler_t *)nfs_symlink_filler, inode); -if (IS_ERR (page)) -return ERR_CAST (page); +if (!dentry) { + err =ERR_PTR (nfs_revalidate_mapping_rcu (inode)); +if (err) +return err; + page =find_get_page (inode->i_mapping,0 ); +if (!page) +return ERR_PTR (-ECHILD ); +if (!PageUptodate (page)) { +put_page (page); +return ERR_PTR (-ECHILD ); + } + }else { + err =ERR_PTR (nfs_revalidate_mapping (inode, inode->i_mapping)); +if (err) +return err; + page =read_cache_page (&inode->i_data,0 , + (filler_t *)nfs_symlink_filler, inode); +if (IS_ERR (page)) +return ERR_CAST (page); + } *cookie = page;return page_address (page); }
include/linux/nfs_fs.h +1 -0
@@ -359,6 +359,7 @@ extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);extern int nfs_revalidate_inode_rcu (struct nfs_server *server,struct inode *inode);extern int __nfs_revalidate_inode (struct nfs_server *,struct inode *);extern int nfs_revalidate_mapping (struct inode *inode,struct address_space *mapping); +extern int nfs_revalidate_mapping_rcu (struct inode *inode);extern int nfs_revalidate_mapping_protected (struct inode *inode,struct address_space *mapping);extern int nfs_setattr (struct dentry *,struct iattr *);extern void nfs_setattr_update_inode (struct inode *inode,struct iattr *attr,struct nfs_fattr *);
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 12/13 kill free_page_put_link()
In reply to Al ViroFrom: Al Viro <[email protected]>
all callers are better off with kfree_put_link()
--- fs/configfs/symlink.c | 12 ++++++------ fs/fuse/dir.c | 6 +++--- fs/kernfs/symlink.c | 12 ++++++------ fs/libfs.c | 6 ------ include/linux/fs.h | 1 - 5 files changed, 15 insertions(+), 22 deletions(-)
fs/configfs/symlink.c +6 -6
@@ -282,29 +282,29 @@ static int configfs_getlink(struct dentry *dentry, char * path)static const char *configfs_get_link (struct dentry *dentry,struct inode *inode,void **cookie) { -unsigned long page; +char *page;int error;if (!dentry)return ERR_PTR (-ECHILD ); - page =get_zeroed_page (GFP_KERNEL ); + page =kzalloc (PAGE_SIZE ,GFP_KERNEL );if (!page)return ERR_PTR (-ENOMEM ); - error =configfs_getlink (dentry, (char *)page); + error =configfs_getlink (dentry, page);if (!error) { -return *cookie = (void *)page; +return *cookie = page; } -free_page (page); +kfree (page);return ERR_PTR (error); }const struct inode_operations configfs_symlink_inode_operations = { .get_link = configfs_get_link, .readlink = generic_readlink, - .put_link = free_page_put_link, + .put_link = kfree_put_link, .setattr = configfs_setattr, };
fs/fuse/dir.c +3 -3
@@ -1376,7 +1376,7 @@ static const char *fuse_get_link(struct dentry *dentry,if (!dentry)return ERR_PTR (-ECHILD ); - link = (char *)__get_free_page (GFP_KERNEL ); + link =kmalloc (PAGE_SIZE ,GFP_KERNEL );if (!link)return ERR_PTR (-ENOMEM );
@@ -1388,7 +1388,7 @@ static const char *fuse_get_link(struct dentry *dentry, args.out.args[0 ].value = link; ret =fuse_simple_request (fc, &args);if (ret <0 ) { -free_page ((unsigned long ) link); +kfree (link); link =ERR_PTR (ret); }else { link[ret] ='\0' ;
@@ -1913,7 +1913,7 @@ static const struct inode_operations fuse_common_inode_operations = {static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, .get_link = fuse_get_link, - .put_link = free_page_put_link, + .put_link = kfree_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, .setxattr = fuse_setxattr,
fs/kernfs/symlink.c +6 -6
@@ -116,19 +116,19 @@ static const char *kernfs_iop_get_link(struct dentry *dentry, struct inode *inode, void **cookie) { int error = -ENOMEM; -unsigned long page; +char *page;if (!dentry)return ERR_PTR (-ECHILD ); - page =get_zeroed_page (GFP_KERNEL ); + page =kzalloc (PAGE_SIZE ,GFP_KERNEL );if (!page)return ERR_PTR (-ENOMEM ); - error =kernfs_getlink (dentry, (char *)page); + error =kernfs_getlink (dentry, page);if (unlikely (error <0 )) { -free_page ((unsigned long )page); +kfree (page);return ERR_PTR (error); } -return *cookie = (char *)page; +return *cookie = page; } const struct inode_operations kernfs_symlink_iops = {
@@ -138,7 +138,7 @@ const struct inode_operations kernfs_symlink_iops = { .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, .get_link = kernfs_iop_get_link, - .put_link = free_page_put_link, + .put_link = kfree_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, .permission = kernfs_iop_permission,
fs/libfs.c +0 -6
@@ -1025,12 +1025,6 @@ void kfree_put_link(struct inode *unused, void *cookie) }EXPORT_SYMBOL (kfree_put_link); -void free_page_put_link (struct inode *unused,void *cookie) -{ -free_page ((unsigned long ) cookie); -} -EXPORT_SYMBOL (free_page_put_link); -/* * nop .set_page_dirty method so that people can use .page_mkwrite on * anon inodes.
include/linux/fs.h +0 -1
@@ -2743,7 +2743,6 @@ extern int __page_symlink(struct inode *inode, const char *symname, int len,extern int page_symlink (struct inode *inode,const char *symname,int len);extern const struct inode_operations page_symlink_inode_operations;extern void kfree_put_link (struct inode *,void *); -extern void free_page_put_link (struct inode *,void *);extern int generic_readlink (struct dentry *,char __user *,int );extern void generic_fillattr (struct inode *,struct kstat *);int vfs_getattr_nosec (struct path *path,struct kstat *stat);
Signature · 1 line
2.1.4
Al Viro
maintainerPATCH 13/13 switch ->get_link() to delayed_call, kill ->put_link()
In reply to Al ViroFrom: Al Viro <[email protected]>
--- Documentation/filesystems/Locking | 2 - Documentation/filesystems/porting | 6 +++ Documentation/filesystems/vfs.txt | 21 +++++---- drivers/staging/lustre/lustre/llite/symlink.c | 18 ++++---- fs/9p/vfs_inode.c | 9 ++-- fs/9p/vfs_inode_dotl.c | 9 ++-- fs/affs/symlink.c | 1 - fs/autofs4/symlink.c | 3 +- fs/btrfs/inode.c | 1 - fs/cifs/cifsfs.c | 1 - fs/cifs/cifsfs.h | 3 +- fs/cifs/link.c | 6 ++- fs/coda/cnode.c | 1 - fs/configfs/symlink.c | 17 ++++---- fs/ecryptfs/inode.c | 7 +-- fs/ext2/symlink.c | 1 - fs/ext4/symlink.c | 8 ++-- fs/f2fs/namei.c | 16 ++++--- fs/fuse/dir.c | 6 +-- fs/gfs2/inode.c | 8 ++-- fs/hostfs/hostfs_kern.c | 16 +++---- fs/jfs/symlink.c | 1 - fs/kernfs/symlink.c | 19 ++++---- fs/libfs.c | 9 ++-- fs/minix/inode.c | 1 - fs/namei.c | 63 +++++++++++---------------- fs/ncpfs/inode.c | 1 - fs/nfs/symlink.c | 6 +-- fs/nilfs2/namei.c | 1 - fs/ocfs2/symlink.c | 1 - fs/overlayfs/inode.c | 45 ++----------------- fs/proc/base.c | 8 ++-- fs/proc/inode.c | 16 +++---- fs/proc/namespaces.c | 3 +- fs/proc/self.c | 7 +-- fs/proc/thread_self.c | 7 +-- fs/reiserfs/namei.c | 1 - fs/squashfs/symlink.c | 1 - fs/sysv/inode.c | 1 - fs/xfs/xfs_iops.c | 6 +-- include/linux/delayed_call.h | 34 +++++++++++++++ include/linux/fs.h | 14 +++--- mm/shmem.c | 19 ++++---- 43 files changed, 206 insertions(+), 218 deletions(-) create mode 100644 include/linux/delayed_call.h
Documentation/filesystems/Locking +0 -2
@@ -51,7 +51,6 @@ prototypes: struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); 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); int (*get_acl)(struct inode *, int);
@@ -84,7 +83,6 @@ rename: yes (all) (see below) rename2: yes (all) (see below) readlink: no get_link: no -put_link: no setattr: yes permission: no (may not block if called in rcu-walk mode) get_acl: no
Documentation/filesystems/porting +6 -0
@@ -515,3 +515,9 @@ in your dentry operations instead. * ->get_link() gets inode as a separate argument * ->get_link() may be called in RCU mode - in that case NULL dentry is passed +-- +[mandatory] + ->get_link() gets struct delayed_call *done now, and should do + set_delayed_call() where it used to set *cookie. + ->put_link() is gone - just give the destructor to set_delayed_call() + in ->get_link().
Documentation/filesystems/vfs.txt +10 -11
@@ -350,8 +350,8 @@ struct inode_operations { 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 **); - void (*put_link) (struct inode *, void *); + const char *(*get_link) (struct dentry *, struct inode *, + struct delayed_call *); int (*permission) (struct inode *, int); int (*get_acl)(struct inode *, int); int (*setattr) (struct dentry *, struct iattr *);
@@ -434,20 +434,19 @@ otherwise noted. readlink: called by the readlink(2) system call. Only required if you want to support reading symbolic links - follow_link: called by the VFS to follow a symbolic link to the + get_link: called by the VFS to follow a symbolic link to the inode it points to. Only required if you want to support symbolic links. This method returns the symlink body to traverse (and possibly resets the current position with nd_jump_link()). If the body won't go away until the inode is gone, nothing else is needed; if it needs to be otherwise - pinned, the data needed to release whatever we'd grabbed - is to be stored in void * variable passed by address to - follow_link() instance. - - put_link: called by the VFS to release resources allocated by - follow_link(). The cookie stored by follow_link() is passed - to this method as the last parameter; only called when - cookie isn't NULL. + pinned, arrange for its release by having get_link(..., ..., done) + do set_delayed_call(done, destructor, argument). + In that case destructor(argument) will be called once VFS is + done with the body you've returned. + May be called in RCU mode; that is indicated by NULL dentry + argument. If request can't be handled without leaving RCU mode, + have it return ERR_PTR(-ECHILD). permission: called by the VFS to check for access rights on a POSIX-like filesystem.
drivers/staging/lustre/lustre/llite/symlink.c +9 -9
@@ -118,8 +118,14 @@ failed:return rc; } +static void ll_put_link (void *p) +{ +ptlrpc_req_finished (p); +} +static const char *ll_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct ptlrpc_request *request =NULL ;int rc;
@@ -137,22 +143,16 @@ static const char *ll_get_link(struct dentry *dentry, }/* symname may contain a pointer to the request message buffer, -* we delay request releasing until ll_put_link then. +* we delay request releasing then. */ - *cookie = request; +set_delayed_call (done, ll_put_link, request);return symname; } -static void ll_put_link (struct inode *unused,void *cookie) -{ -ptlrpc_req_finished (cookie); -} -struct inode_operations ll_fast_symlink_inode_operations = { .readlink = generic_readlink, .setattr = ll_setattr, .get_link = ll_get_link, - .put_link = ll_put_link, .getattr = ll_getattr, .permission = ll_inode_permission, .setxattr = ll_setxattr,
fs/9p/vfs_inode.c +5 -4
@@ -1226,11 +1226,12 @@ ino_t v9fs_qid2ino(struct p9_qid *qid)* 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() +* @done: delayed call for when we are done with the return value */ static const char *v9fs_vfs_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct v9fs_session_info *v9ses;struct p9_fid *fid;
@@ -1266,7 +1267,8 @@ static const char *v9fs_vfs_get_link(struct dentry *dentry,p9stat_free (st);kfree (st); -return *cookie = res; +set_delayed_call (done, kfree_link, res); +return res; }/**
@@ -1460,7 +1462,6 @@ static const struct inode_operations v9fs_file_inode_operations = {static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = v9fs_vfs_get_link, - .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, };
fs/9p/vfs_inode_dotl.c +5 -4
@@ -902,12 +902,13 @@ error:* 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() +* @done: destructor for return value */ static const char *v9fs_vfs_get_link_dotl (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct p9_fid *fid;char *target;
@@ -924,7 +925,8 @@ v9fs_vfs_get_link_dotl(struct dentry *dentry, retval =p9_client_readlink (fid, &target);if (retval)return ERR_PTR (retval); -return *cookie = target; +set_delayed_call (done, kfree_link, target); +return target; }int v9fs_refresh_inode_dotl (struct p9_fid *fid,struct inode *inode)
@@ -991,7 +993,6 @@ const struct inode_operations v9fs_file_inode_operations_dotl = {const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, .get_link = v9fs_vfs_get_link_dotl, - .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, .setxattr = generic_setxattr,
fs/affs/symlink.c +0 -1
@@ -72,6 +72,5 @@ const struct address_space_operations affs_symlink_aops = {const struct inode_operations affs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = affs_notify_change, };
fs/autofs4/symlink.c +2 -1
@@ -13,7 +13,8 @@#include "autofs_i.h" static const char *autofs4_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct autofs_sb_info *sbi;struct autofs_info *ino;
fs/btrfs/inode.c +0 -1
@@ -10097,7 +10097,6 @@ static const struct inode_operations btrfs_special_inode_operations = {static const struct inode_operations btrfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = btrfs_getattr, .setattr = btrfs_setattr, .permission = btrfs_permission,
fs/cifs/cifsfs.c +0 -1
@@ -901,7 +901,6 @@ const struct inode_operations cifs_file_inode_ops = {const struct inode_operations cifs_symlink_inode_ops = { .readlink = generic_readlink, .get_link = cifs_get_link, - .put_link = kfree_put_link, .permission = cifs_permission,/* BB add the following two eventually */ /* revalidate: cifs_revalidate,
fs/cifs/cifsfs.h +2 -1
@@ -120,7 +120,8 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path);#endif /* Functions related to symlinks */ -extern const char *cifs_get_link (struct dentry *,struct inode *,void **); +extern const char *cifs_get_link (struct dentry *,struct inode *, +struct delayed_call *);extern int cifs_symlink (struct inode *inode,struct dentry *direntry,const char *symname);extern int cifs_removexattr (struct dentry *,const char *);
fs/cifs/link.c +4 -2
@@ -627,7 +627,8 @@ cifs_hl_exit: }const char * -cifs_get_link (struct dentry *direntry,struct inode *inode,void **cookie) +cifs_get_link (struct dentry *direntry,struct inode *inode, +struct delayed_call *done) {int rc = -ENOMEM ;unsigned int xid;
@@ -680,7 +681,8 @@ cifs_get_link(struct dentry *direntry, struct inode *inode, void **cookie)kfree (target_path);return ERR_PTR (rc); } -return *cookie = target_path; +set_delayed_call (done, kfree_link, target_path); +return target_path; }int
fs/coda/cnode.c +0 -1
@@ -19,7 +19,6 @@ static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)static const struct inode_operations coda_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = coda_setattr, };
fs/configfs/symlink.c +9 -8
@@ -280,31 +280,32 @@ static int configfs_getlink(struct dentry *dentry, char * path) }static const char *configfs_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) { -char *page; +char *body;int error;if (!dentry)return ERR_PTR (-ECHILD ); - page =kzalloc (PAGE_SIZE ,GFP_KERNEL ); -if (!page) + body =kzalloc (PAGE_SIZE ,GFP_KERNEL ); +if (!body)return ERR_PTR (-ENOMEM ); - error =configfs_getlink (dentry, page); + error =configfs_getlink (dentry, body);if (!error) { -return *cookie = page; +set_delayed_call (done, kfree_link, body); +return body; } -kfree (page); +kfree (body);return ERR_PTR (error); }const struct inode_operations configfs_symlink_inode_operations = { .get_link = configfs_get_link, .readlink = generic_readlink, - .put_link = kfree_put_link, .setattr = configfs_setattr, };
fs/ecryptfs/inode.c +4 -3
@@ -675,7 +675,8 @@ out: }static const char *ecryptfs_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {size_t len;char *buf;
@@ -689,7 +690,8 @@ static const char *ecryptfs_get_link(struct dentry *dentry,fsstack_copy_attr_atime (d_inode (dentry),d_inode (ecryptfs_dentry_to_lower (dentry))); buf[len] ='\0' ; -return *cookie = buf; +set_delayed_call (done, kfree_link, buf); +return buf; }/**
@@ -1102,7 +1104,6 @@ out:const struct inode_operations ecryptfs_symlink_iops = { .readlink = generic_readlink, .get_link = ecryptfs_get_link, - .put_link = kfree_put_link, .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, .getattr = ecryptfs_getattr_link,
fs/ext2/symlink.c +0 -1
@@ -23,7 +23,6 @@const struct inode_operations ext2_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr,
fs/ext4/symlink.c +4 -4
@@ -24,7 +24,8 @@#ifdef CONFIG_EXT4_FS_ENCRYPTION static const char *ext4_encrypted_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct page *cpage =NULL ;char *caddr, *paddr =NULL ;
@@ -80,7 +81,8 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry, paddr[res] ='\0' ;if (cpage)page_cache_release (cpage); -return *cookie = paddr; +set_delayed_call (done, kfree_link, paddr); +return paddr; errout:if (cpage)page_cache_release (cpage);
@@ -91,7 +93,6 @@ errout:const struct inode_operations ext4_encrypted_symlink_inode_operations = { .readlink = generic_readlink, .get_link = ext4_encrypted_get_link, - .put_link = kfree_put_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr,
@@ -103,7 +104,6 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = {const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr,
fs/f2fs/namei.c +9 -7
@@ -316,12 +316,14 @@ fail: }static const char *f2fs_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) { -const char *link =page_get_link (dentry, inode, cookie); +const char *link =page_get_link (dentry, inode, done);if (!IS_ERR (link) && !*link) {/* this is broken symlink case */ -page_put_link (NULL , *cookie); +do_delayed_call (done); +clear_delayed_call (done); link =ERR_PTR (-ENOENT ); }return link;
@@ -926,7 +928,8 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,#ifdef CONFIG_F2FS_FS_ENCRYPTION static const char *f2fs_encrypted_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct page *cpage =NULL ;char *caddr, *paddr =NULL ;
@@ -988,7 +991,8 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry, paddr[res] ='\0' ;page_cache_release (cpage); -return *cookie = paddr; +set_delayed_call (done, kfree_link, paddr); +return paddr; errout:kfree (cstr.name);f2fs_fname_crypto_free_buffer (&pstr);
@@ -999,7 +1003,6 @@ errout:const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .readlink = generic_readlink, .get_link = f2fs_encrypted_get_link, - .put_link = kfree_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, .setxattr = generic_setxattr,
@@ -1035,7 +1038,6 @@ const struct inode_operations f2fs_dir_inode_operations = {const struct inode_operations f2fs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = f2fs_get_link, - .put_link = page_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, #ifdef CONFIG_F2FS_FS_XATTR
fs/fuse/dir.c +3 -3
@@ -1366,7 +1366,8 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) }static const char *fuse_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct fuse_conn *fc =get_fuse_conn (inode);FUSE_ARGS (args);
@@ -1392,7 +1393,7 @@ static const char *fuse_get_link(struct dentry *dentry, link =ERR_PTR (ret); }else { link[ret] ='\0' ; - *cookie = link; +set_delayed_call (done, kfree_link, link); }fuse_invalidate_atime (inode);return link;
@@ -1913,7 +1914,6 @@ static const struct inode_operations fuse_common_inode_operations = {static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, .get_link = fuse_get_link, - .put_link = kfree_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, .setxattr = fuse_setxattr,
fs/gfs2/inode.c +4 -4
@@ -1715,7 +1715,7 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry,* gfs2_get_link - Follow a symbolic link * @dentry: The dentry of the link * @inode: The inode of the link -* @cookie: place to store the information for ->put_link() +* @done: destructor for return value * * This can handle symlinks of any size. *
@@ -1723,7 +1723,8 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry,*/ static const char *gfs2_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct gfs2_inode *ip =GFS2_I (inode);struct gfs2_holder i_gh;
@@ -1764,7 +1765,7 @@ static const char *gfs2_get_link(struct dentry *dentry, out:gfs2_glock_dq_uninit (&i_gh);if (!IS_ERR (buf)) - *cookie = buf; +set_delayed_call (done, kfree_link, buf);return buf; }
@@ -2138,7 +2139,6 @@ const struct inode_operations gfs2_dir_iops = {const struct inode_operations gfs2_symlink_iops = { .readlink = generic_readlink, .get_link = gfs2_get_link, - .put_link = kfree_put_link, .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr,
fs/hostfs/hostfs_kern.c +6 -10
@@ -893,12 +893,13 @@ static const struct inode_operations hostfs_dir_iops = { };static const char *hostfs_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {char *link;if (!dentry)return ERR_PTR (-ECHILD ); - link =__getname (); + link =kmalloc (PATH_MAX ,GFP_KERNEL );if (link) {char *path =dentry_name (dentry);int err = -ENOMEM ;
@@ -909,25 +910,20 @@ static const char *hostfs_get_link(struct dentry *dentry,__putname (path); }if (err <0 ) { -__putname (link); +kfree (link);return ERR_PTR (err); } }else {return ERR_PTR (-ENOMEM ); } -return *cookie = link; -} - -static void hostfs_put_link (struct inode *unused,void *cookie) -{ -__putname (cookie); +set_delayed_call (done, kfree_link, link); +return link; }static const struct inode_operations hostfs_link_iops = { .readlink = generic_readlink, .get_link = hostfs_get_link, - .put_link = hostfs_put_link, };static int hostfs_fill_sb_common (struct super_block *sb,void *d,int silent)
fs/jfs/symlink.c +0 -1
@@ -34,7 +34,6 @@ const struct inode_operations jfs_fast_symlink_inode_operations = {const struct inode_operations jfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr,
fs/kernfs/symlink.c +10 -9
@@ -113,22 +113,24 @@ static int kernfs_getlink(struct dentry *dentry, char *path) }static const char *kernfs_iop_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) { -int error = -ENOMEM ; -char *page; +char *body; +int error;if (!dentry)return ERR_PTR (-ECHILD ); - page =kzalloc (PAGE_SIZE ,GFP_KERNEL ); -if (!page) + body =kzalloc (PAGE_SIZE ,GFP_KERNEL ); +if (!body)return ERR_PTR (-ENOMEM ); - error =kernfs_getlink (dentry, page); + error =kernfs_getlink (dentry, body);if (unlikely (error <0 )) { -kfree (page); +kfree (body);return ERR_PTR (error); } -return *cookie = page; +set_delayed_call (done, kfree_link, body); +return body; }const struct inode_operations kernfs_symlink_iops = {
@@ -138,7 +140,6 @@ const struct inode_operations kernfs_symlink_iops = { .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, .get_link = kernfs_iop_get_link, - .put_link = kfree_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, .permission = kernfs_iop_permission,
fs/libfs.c +5 -4
@@ -1019,11 +1019,12 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync) }EXPORT_SYMBOL (noop_fsync); -void kfree_put_link (struct inode *unused,void *cookie) +/* Because kfree isn't assignment-compatible with void(void*) ;-/ */ +void kfree_link (void *p) { -kfree (cookie); +kfree (p); } -EXPORT_SYMBOL (kfree_put_link); +EXPORT_SYMBOL (kfree_link);/* * nop .set_page_dirty method so that people can use .page_mkwrite on
@@ -1087,7 +1088,7 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp,EXPORT_SYMBOL (simple_nosetlease);const char *simple_get_link (struct dentry *dentry,struct inode *inode, -void **cookie) +struct delayed_call *done) {return inode->i_link; }
fs/minix/inode.c +0 -1
@@ -436,7 +436,6 @@ static const struct address_space_operations minix_aops = {static const struct inode_operations minix_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = minix_getattr, };
fs/namei.c +26 -37
164 lines not on this page · raw message
fs/ncpfs/inode.c +0 -1
@@ -245,7 +245,6 @@ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)static const struct inode_operations ncp_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = ncp_notify_change, };#endif
fs/nfs/symlink.c +3 -3
@@ -43,7 +43,8 @@ error: }static const char *nfs_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct page *page;void *err;
@@ -68,7 +69,7 @@ static const char *nfs_get_link(struct dentry *dentry,if (IS_ERR (page))return ERR_CAST (page); } - *cookie = page; +set_delayed_call (done, page_put_link, page);return page_address (page); }
@@ -78,7 +79,6 @@ static const char *nfs_get_link(struct dentry *dentry,const struct inode_operations nfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = nfs_get_link, - .put_link = page_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr, };
fs/nilfs2/namei.c +0 -1
@@ -570,7 +570,6 @@ const struct inode_operations nilfs_special_inode_operations = {const struct inode_operations nilfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .permission = nilfs_permission, };
fs/ocfs2/symlink.c +0 -1
@@ -89,7 +89,6 @@ const struct address_space_operations ocfs2_fast_symlink_aops = {const struct inode_operations ocfs2_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, .setxattr = generic_setxattr,
fs/overlayfs/inode.c +3 -42
67 lines not on this page · raw message
fs/proc/base.c +5 -3
@@ -1565,7 +1565,8 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) }static const char *proc_pid_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct path path;int error = -EACCES ;
@@ -1949,12 +1950,13 @@ struct map_files_info {*/ static const char *proc_map_files_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {if (!capable (CAP_SYS_ADMIN ))return ERR_PTR (-EPERM ); -return proc_pid_get_link (dentry, inode,NULL ); +return proc_pid_get_link (dentry, inode, done); }/*
fs/proc/inode.c +8 -8
@@ -393,25 +393,25 @@ static const struct file_operations proc_reg_file_ops_no_compat = { };#endif +static void proc_put_link (void *p) +{ +unuse_pde (p); +} +static const char *proc_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct proc_dir_entry *pde =PDE (inode);if (unlikely (!use_pde (pde)))return ERR_PTR (-EINVAL ); - *cookie = pde; +set_delayed_call (done, proc_put_link, pde);return pde->data; } -static void proc_put_link (struct inode *unused,void *p) -{ -unuse_pde (p); -} -const struct inode_operations proc_link_inode_operations = { .readlink = generic_readlink, .get_link = proc_get_link, - .put_link = proc_put_link, };struct inode *proc_get_inode (struct super_block *sb,struct proc_dir_entry *de)
fs/proc/namespaces.c +2 -1
@@ -31,7 +31,8 @@ static const struct proc_ns_operations *ns_entries[] = { };static const char *proc_ns_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {const struct proc_ns_operations *ns_ops =PROC_I (inode)->ns_ops;struct task_struct *task;
fs/proc/self.c +4 -3
@@ -19,7 +19,8 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, }static const char *proc_self_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct pid_namespace *ns = inode->i_sb->s_fs_info;pid_t tgid =task_tgid_nr_ns (current, ns);
@@ -32,13 +33,13 @@ static const char *proc_self_get_link(struct dentry *dentry,if (unlikely (!name))return dentry ?ERR_PTR (-ENOMEM ) :ERR_PTR (-ECHILD );sprintf (name,"%d" , tgid); -return *cookie = name; +set_delayed_call (done, kfree_link, name); +return name; }static const struct inode_operations proc_self_inode_operations = { .readlink = proc_self_readlink, .get_link = proc_self_get_link, - .put_link = kfree_put_link, };static unsigned self_inum;
fs/proc/thread_self.c +4 -3
@@ -20,7 +20,8 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, }static const char *proc_thread_self_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct pid_namespace *ns = inode->i_sb->s_fs_info;pid_t tgid =task_tgid_nr_ns (current, ns);
@@ -34,13 +35,13 @@ static const char *proc_thread_self_get_link(struct dentry *dentry,if (unlikely (!name))return dentry ?ERR_PTR (-ENOMEM ) :ERR_PTR (-ECHILD );sprintf (name,"%d/task/%d" , tgid, pid); -return *cookie = name; +set_delayed_call (done, kfree_link, name); +return name; }static const struct inode_operations proc_thread_self_inode_operations = { .readlink = proc_thread_self_readlink, .get_link = proc_thread_self_get_link, - .put_link = kfree_put_link, };static unsigned thread_self_inum;
fs/reiserfs/namei.c +0 -1
@@ -1666,7 +1666,6 @@ const struct inode_operations reiserfs_dir_inode_operations = {const struct inode_operations reiserfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = reiserfs_setattr, .setxattr = reiserfs_setxattr, .getxattr = reiserfs_getxattr,
fs/squashfs/symlink.c +0 -1
@@ -120,7 +120,6 @@ const struct address_space_operations squashfs_symlink_aops = {const struct inode_operations squashfs_symlink_inode_ops = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getxattr = generic_getxattr, .listxattr = squashfs_listxattr };
fs/sysv/inode.c +0 -1
@@ -147,7 +147,6 @@ static inline void write3byte(struct sysv_sb_info *sbi,static const struct inode_operations sysv_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = sysv_getattr, };
fs/xfs/xfs_iops.c +3 -3
@@ -417,7 +417,7 @@ STATIC const char *xfs_vn_get_link ( struct dentry *dentry, struct inode *inode, - void **cookie) + struct delayed_call *done) { char *link;int error = -ENOMEM ;
@@ -433,7 +433,8 @@ xfs_vn_get_link(if (unlikely (error)) goto out_kfree; -return *cookie = link; +set_delayed_call (done, kfree_link, link); +return link; out_kfree:kfree (link);
@@ -1177,7 +1178,6 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {static const struct inode_operations xfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = xfs_vn_get_link, - .put_link = kfree_put_link, .getattr = xfs_vn_getattr, .setattr = xfs_vn_setattr, .setxattr = generic_setxattr,
include/linux/delayed_call.h new file +34 -0
@@ -0,0 +1,34 @@ +#ifndef _DELAYED_CALL_H +#define _DELAYED_CALL_H + +/* +* Poor man's closures; I wish we could've done them sanely polymorphic, +* but... +*/ + +struct delayed_call { +void (*fn)(void *); +void *arg; +}; + +#define DEFINE_DELAYED_CALL (name) struct delayed_call name = {NULL, NULL} + +/* I really wish we had closures with sane typechecking... */ +static inline void set_delayed_call (struct delayed_call *call, +void (*fn)(void *),void *arg) +{ + call->fn = fn; + call->arg = arg; +} + +static inline void do_delayed_call (struct delayed_call *call) +{ +if (call->fn) + call->fn (call->arg); +} + +static inline void clear_delayed_call (struct delayed_call *call) +{ + call->fn =NULL ; +} +#endif
include/linux/fs.h +8 -6
@@ -31,6 +31,7 @@#include <linux/blk_types.h> #include <linux/workqueue.h> #include <linux/percpu-rwsem.h> +#include <linux/delayed_call.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h>
@@ -1633,12 +1634,11 @@ struct file_operations {struct inode_operations {struct dentry * (*lookup) (struct inode *,struct dentry *,unsigned int ); -const char * (*get_link) (struct dentry *,struct inode *,void **); +const char * (*get_link) (struct dentry *,struct inode *,struct delayed_call *);int (*permission) (struct inode *,int );struct posix_acl * (*get_acl)(struct inode *,int );int (*readlink) (struct dentry *,char __user *,int ); -void (*put_link) (struct inode *,void *);int (*create) (struct inode *,struct dentry *,umode_t ,bool );int (*link) (struct dentry *,struct inode *,struct dentry *);
@@ -2736,13 +2736,14 @@ 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 const char *page_get_link (struct dentry *,struct inode *,void **); -extern void page_put_link (struct inode *,void *); +extern const char *page_get_link (struct dentry *,struct inode *, +struct delayed_call *); +extern void page_put_link (void *);extern int __page_symlink (struct inode *inode,const char *symname,int len,int nofs);extern int page_symlink (struct inode *inode,const char *symname,int len);extern const struct inode_operations page_symlink_inode_operations; -extern void kfree_put_link (struct inode *,void *); +extern void kfree_link (void *);extern int generic_readlink (struct dentry *,char __user *,int );extern void generic_fillattr (struct inode *,struct kstat *);int vfs_getattr_nosec (struct path *path,struct kstat *stat);
@@ -2753,7 +2754,8 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes);void inode_sub_bytes (struct inode *inode,loff_t bytes);loff_t inode_get_bytes (struct inode *inode);void inode_set_bytes (struct inode *inode,loff_t bytes); -const char *simple_get_link (struct dentry *,struct inode *,void **); +const char *simple_get_link (struct dentry *,struct inode *, +struct delayed_call *);extern const struct inode_operations simple_symlink_inode_operations;extern int iterate_dir (struct file *,struct dir_context *);
mm/shmem.c +9 -10
@@ -2496,8 +2496,15 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *sreturn 0 ; } +static void shmem_put_link (void *arg) +{ +mark_page_accessed (arg); +put_page (arg); +} +static const char *shmem_get_link (struct dentry *dentry, -struct inode *inode,void **cookie) +struct inode *inode, +struct delayed_call *done) {struct page *page =NULL ;int error;
@@ -2515,17 +2522,10 @@ static const char *shmem_get_link(struct dentry *dentry,return ERR_PTR (error);unlock_page (page); } - *cookie = page; +set_delayed_call (done, shmem_put_link, page);return page_address (page); } -static void shmem_put_link (struct inode *unused,void *cookie) -{ -struct page *page = cookie; -mark_page_accessed (page); -page_cache_release (page); -} -#ifdef CONFIG_TMPFS_XATTR /* * Superblocks without xattr inode operations may get some security.* xattr
@@ -2680,7 +2680,6 @@ static const struct inode_operations shmem_short_symlink_operations = {static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, .get_link = shmem_get_link, - .put_link = shmem_put_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr,
Signature · 1 line
2.1.4
Quoted 5 lines · Al Viro
In cases when we need to pin the symlink body in some manner, we
need to undo whatever we'd done once the caller is done with the body. That went through several variants, the latest (in -next right now) being "have non-NULL ->put_link() and leave an argument for it in void *cookie, address of which is passed to ->get_link()".
The series looks ok to me, even if I still am not a fan of the cookie. I suspect the remaining users could easily embed the returned string at the end of a structure, and get their data with container_of(). It would complicate their unusual behavior for sure, but make the common case much more understandable.
Oh well. I won't insist - it may be too painful to be worth it. And it's a fairly separate issue anyway.
So no objections to this series.
Linus
Al Viro
maintainerQuoted 13 lines · Linus Torvalds
Quoted 5 lines · Al Viro
In cases when we need to pin the symlink body in some manner, we
need to undo whatever we'd done once the caller is done with the body. That went through several variants, the latest (in -next right now) being "have non-NULL ->put_link() and leave an argument for it in void *cookie, address of which is passed to ->get_link()".The series looks ok to me, even if I still am not a fan of the cookie. I suspect the remaining users could easily embed the returned string at the end of a structure, and get their data with
container_of(). It would complicate their unusual behavior for sure, but make the common case much more understandable.
Quoted 4 lines
Oh well. I won't insist - it may be too painful to be worth it. And it's a fairly separate issue anyway.
So no objections to this series.
Just to make sure - that does include 13/13, presumably? IOW, ->put_link() is gone and the final calling conventions for ->get_link() are
const char *(*get_link)(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done);
with dentry == NULL <=> call in RCU mode, ERR_PTR(-E...) returned on error and set_delayed_call(done, destructor, arg) done if destructor(arg) should be done once pathname resolution is through with the body returned by ->get_link().
Quoted 2 lines · Al Viro
Just to make sure - that does include 13/13, presumably?
Ugh, no, I had set that aside and then forgot all about it.
I'm not sure about 13/13. I'm ok with it, but I'm not sure it's any less confusing than the cookie was.
I like how it removes "put_link()" as a callback, but at the same time I think it's even more abstract than the cookie was.
The main worry I have is that the naming is generic, but there's only a single very specialized use for it. Do we expect other uses?
Because if not, I think it would be clearer if it was named to be more concretely about putlink, and avoid the fact that it feels very abstract.
Don't get me wrong - abstract generalized helper functions are cool. But people aren't very abstract, and it tends to make for confusing code when you aren't intimately familiar with the rules.
Linus
Al Viro
maintainerQuoted 5 lines · Linus Torvalds
I like how it removes "
put_link()" as a callback, but at the same time I think it's even more abstract than the cookie was.The main worry I have is that the naming is generic, but there's only a single very specialized use for it. Do we expect other uses?
Quite possibly - I don't have anything specific planned right now, but I would be surprised if there hadn't been open-coded equivalents of that thing in many places, and not only "too many instances to touch" kind of holy cows, at that (timers are obviously very similar, but these _do_ have way too many instances to even think of converting them).
Quoted 7 lines
Because if not, I think it would be clearer if it was named to be more concretely about putlink, and avoid the fact that it feels very abstract.
Don't get me wrong - abstract generalized helper functions are cool. But people aren't very abstract, and it tends to make for confusing code when you aren't intimately familiar with the rules.
The rules are pretty simple - there are 4 primitives total.
1) DEFINE_DELAYED_CALL(name) - usual DEFINE_MUTEX, etc. style initialized declaration.
2) set_delayed_call(delayed_call, function, argument) - function is void(void *), argument is void *. A-la setup_timer().
3) clear_delayed_call(delayed_call) - resets.
4) do_delayed_call(delayed_call) - evaluates.
That's it. If you have better suggestions re names, I'd be glad to take them, but the set of primitives and the rules to go with those are as simple as it gets.
Sure, we can give it a less generic name for now and just do what we'd done for another generic thing (rcu_head, aka callback_head) when unrelated uses show up, but I don't see much point going that way, TBH...