Subject: Re: linux-next: manual merge of the vfs tree with the nfsd tree

From: "J. Bruce Fields" <[email-masked]>
Date: Mon, 8 Jul 2019 08:45:10 -0400

On Mon, Jul 08, 2019 at 11:06:33AM +1000, Stephen Rothwell wrote:
> Today's linux-next merge of the vfs tree got a conflict in:
> 
>   fs/nfsd/nfsctl.c
> 
> between commits:
> 
>   e8a79fb14f6b ("nfsd: add nfsd/clients directory")
> 
> from the nfsd tree and commit:
> 
>   96a374a35f82 ("vfs: Convert nfsctl to use the new mount API")

I did a fetch of

	git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git

and looked at the "master" branch and couldn't find that vfs commit.  Am
I looking in the wrong place?

(I'm sure your resolution is fine, I just thought to be careful it might
be nice to run some tests on the merge.)

--b.

> 
> from the vfs tree.
> 
> I fixed it up (Maybe? see below) and can carry the fix as necessary.
> This is now fixed as far as linux-next is concerned, but any non trivial
> conflicts should be mentioned to your upstream maintainer when your tree
> is submitted for merging.  You may also want to consider cooperating
> with the maintainer of the conflicting tree to minimise any particularly
> complex conflicts.
> 
> -- 
> Cheers,
> Stephen Rothwell
> 
> diff --cc fs/nfsd/nfsctl.c
> index 4683ba5c69c7,bbff9c4ac49f..000000000000
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@@ -1149,201 -1148,8 +1150,201 @@@ static ssize_t write_v4_end_grace(struc
>    *	populating the filesystem.
>    */
>   
>  +/* Basically copying rpc_get_inode. */
>  +static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode)
>  +{
>  +	struct inode *inode = new_inode(sb);
>  +	if (!inode)
>  +		return NULL;
>  +	/* Following advice from simple_fill_super documentation: */
>  +	inode->i_ino = iunique(sb, NFSD_MaxReserved);
>  +	inode->i_mode = mode;
>  +	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
>  +	switch (mode & S_IFMT) {
>  +	case S_IFDIR:
>  +		inode->i_fop = &simple_dir_operations;
>  +		inode->i_op = &simple_dir_inode_operations;
>  +		inc_nlink(inode);
>  +	default:
>  +		break;
>  +	}
>  +	return inode;
>  +}
>  +
>  +static int __nfsd_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
>  +{
>  +	struct inode *inode;
>  +
>  +	inode = nfsd_get_inode(dir->i_sb, mode);
>  +	if (!inode)
>  +		return -ENOMEM;
>  +	d_add(dentry, inode);
>  +	inc_nlink(dir);
>  +	fsnotify_mkdir(dir, dentry);
>  +	return 0;
>  +}
>  +
>  +static struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *ncl, char *name)
>  +{
>  +	struct inode *dir = parent->d_inode;
>  +	struct dentry *dentry;
>  +	int ret = -ENOMEM;
>  +
>  +	inode_lock(dir);
>  +	dentry = d_alloc_name(parent, name);
>  +	if (!dentry)
>  +		goto out_err;
>  +	ret = __nfsd_mkdir(d_inode(parent), dentry, S_IFDIR | 0600);
>  +	if (ret)
>  +		goto out_err;
>  +	if (ncl) {
>  +		d_inode(dentry)->i_private = ncl;
>  +		kref_get(&ncl->cl_ref);
>  +	}
>  +out:
>  +	inode_unlock(dir);
>  +	return dentry;
>  +out_err:
>  +	dentry = ERR_PTR(ret);
>  +	goto out;
>  +}
>  +
>  +static void clear_ncl(struct inode *inode)
>  +{
>  +	struct nfsdfs_client *ncl = inode->i_private;
>  +
>  +	inode->i_private = NULL;
>  +	synchronize_rcu();
>  +	kref_put(&ncl->cl_ref, ncl->cl_release);
>  +}
>  +
>  +
>  +struct nfsdfs_client *__get_nfsdfs_client(struct inode *inode)
>  +{
>  +	struct nfsdfs_client *nc = inode->i_private;
>  +
>  +	if (nc)
>  +		kref_get(&nc->cl_ref);
>  +	return nc;
>  +}
>  +
>  +struct nfsdfs_client *get_nfsdfs_client(struct inode *inode)
>  +{
>  +	struct nfsdfs_client *nc;
>  +
>  +	rcu_read_lock();
>  +	nc = __get_nfsdfs_client(inode);
>  +	rcu_read_unlock();
>  +	return nc;
>  +}
>  +/* from __rpc_unlink */
>  +static void nfsdfs_remove_file(struct inode *dir, struct dentry *dentry)
>  +{
>  +	int ret;
>  +
>  +	clear_ncl(d_inode(dentry));
>  +	dget(dentry);
>  +	ret = simple_unlink(dir, dentry);
>  +	d_delete(dentry);
>  +	dput(dentry);
>  +	WARN_ON_ONCE(ret);
>  +}
>  +
>  +static void nfsdfs_remove_files(struct dentry *root)
>  +{
>  +	struct dentry *dentry, *tmp;
>  +
>  +	list_for_each_entry_safe(dentry, tmp, &root->d_subdirs, d_child) {
>  +		if (!simple_positive(dentry)) {
>  +			WARN_ON_ONCE(1); /* I think this can't happen? */
>  +			continue;
>  +		}
>  +		nfsdfs_remove_file(d_inode(root), dentry);
>  +	}
>  +}
>  +
>  +/* XXX: cut'n'paste from simple_fill_super; figure out if we could share
>  + * code instead. */
>  +static  int nfsdfs_create_files(struct dentry *root,
>  +					const struct tree_descr *files)
>  +{
>  +	struct inode *dir = d_inode(root);
>  +	struct inode *inode;
>  +	struct dentry *dentry;
>  +	int i;
>  +
>  +	inode_lock(dir);
>  +	for (i = 0; files->name && files->name[0]; i++, files++) {
>  +		if (!files->name)
>  +			continue;
>  +		dentry = d_alloc_name(root, files->name);
>  +		if (!dentry)
>  +			goto out;
>  +		inode = nfsd_get_inode(d_inode(root)->i_sb,
>  +					S_IFREG | files->mode);
>  +		if (!inode) {
>  +			dput(dentry);
>  +			goto out;
>  +		}
>  +		inode->i_fop = files->ops;
>  +		inode->i_private = __get_nfsdfs_client(dir);
>  +		d_add(dentry, inode);
>  +		fsnotify_create(dir, dentry);
>  +	}
>  +	inode_unlock(dir);
>  +	return 0;
>  +out:
>  +	nfsdfs_remove_files(root);
>  +	inode_unlock(dir);
>  +	return -ENOMEM;
>  +}
>  +
>  +/* on success, returns positive number unique to that client. */
>  +struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
>  +		struct nfsdfs_client *ncl, u32 id,
>  +		const struct tree_descr *files)
>  +{
>  +	struct dentry *dentry;
>  +	char name[11];
>  +	int ret;
>  +
>  +	sprintf(name, "%u", id);
>  +
>  +	dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name);
>  +	if (IS_ERR(dentry)) /* XXX: tossing errors? */
>  +		return NULL;
>  +	ret = nfsdfs_create_files(dentry, files);
>  +	if (ret) {
>  +		nfsd_client_rmdir(dentry);
>  +		return NULL;
>  +	}
>  +	return dentry;
>  +}
>  +
>  +/* Taken from __rpc_rmdir: */
>  +void nfsd_client_rmdir(struct dentry *dentry)
>  +{
>  +	struct inode *dir = d_inode(dentry->d_parent);
>  +	struct inode *inode = d_inode(dentry);
>  +	int ret;
>  +
>  +	inode_lock(dir);
>  +	nfsdfs_remove_files(dentry);
>  +	clear_ncl(inode);
>  +	dget(dentry);
>  +	ret = simple_rmdir(dir, dentry);
>  +	WARN_ON_ONCE(ret);
>  +	d_delete(dentry);
>  +	inode_unlock(dir);
>  +}
>  +
> - static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
> + static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
>   {
>  +	struct nfsd_net *nn = net_generic(current->nsproxy->net_ns,
>  +							nfsd_net_id);
>  +	struct dentry *dentry;
>  +	int ret;
>  +
>   	static const struct tree_descr nfsd_files[] = {
>   		[NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO},
>   		[NFSD_Export_features] = {"export_features",
> @@@ -1372,23 -1178,33 +1373,39 @@@
>   #endif
>   		/* last one */ {""}
>   	};
> - 	get_net(sb->s_fs_info);
>  -
>  -	return simple_fill_super(sb, 0x6e667364, nfsd_files);
>  +	ret = simple_fill_super(sb, 0x6e667364, nfsd_files);
>  +	if (ret)
>  +		return ret;
>  +	dentry = nfsd_mkdir(sb->s_root, NULL, "clients");
>  +	if (IS_ERR(dentry))
>  +		return PTR_ERR(dentry);
>  +	nn->nfsd_client_dir = dentry;
>  +	return 0;
> + }
> + 
> + static int nfsd_fs_get_tree(struct fs_context *fc)
> + {
> + 	fc->s_fs_info = get_net(fc->net_ns);
> + 	return vfs_get_super(fc, vfs_get_keyed_super, nfsd_fill_super);
> + }
>   
> + static void nfsd_fs_free_fc(struct fs_context *fc)
> + {
> + 	if (fc->s_fs_info)
> + 		put_net(fc->s_fs_info);
>   }
>   
> - static struct dentry *nfsd_mount(struct file_system_type *fs_type,
> - 	int flags, const char *dev_name, void *data)
> + static const struct fs_context_operations nfsd_fs_context_ops = {
> + 	.free		= nfsd_fs_free_fc,
> + 	.get_tree	= nfsd_fs_get_tree,
> + };
> + 
> + static int nfsd_init_fs_context(struct fs_context *fc)
>   {
> - 	struct net *net = current->nsproxy->net_ns;
> - 	return mount_ns(fs_type, flags, data, net, net->user_ns, nfsd_fill_super);
> + 	put_user_ns(fc->user_ns);
> + 	fc->user_ns = get_user_ns(fc->net_ns->user_ns);
> + 	fc->ops = &nfsd_fs_context_ops;
> + 	return 0;
>   }
>   
>   static void nfsd_umount(struct super_block *sb)