Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b4aae7a authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFS: Reduce the stack usage in NFSv3 create operations

parent 57dc9a57
Loading
Loading
Loading
Loading
+142 −129
Original line number Diff line number Diff line
@@ -248,6 +248,53 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page,
	return status;
}

struct nfs3_createdata {
	struct rpc_message msg;
	union {
		struct nfs3_createargs create;
		struct nfs3_mkdirargs mkdir;
		struct nfs3_symlinkargs symlink;
		struct nfs3_mknodargs mknod;
	} arg;
	struct nfs3_diropres res;
	struct nfs_fh fh;
	struct nfs_fattr fattr;
	struct nfs_fattr dir_attr;
};

static struct nfs3_createdata *nfs3_alloc_createdata(void)
{
	struct nfs3_createdata *data;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (data != NULL) {
		data->msg.rpc_argp = &data->arg;
		data->msg.rpc_resp = &data->res;
		data->res.fh = &data->fh;
		data->res.fattr = &data->fattr;
		data->res.dir_attr = &data->dir_attr;
		nfs_fattr_init(data->res.fattr);
		nfs_fattr_init(data->res.dir_attr);
	}
	return data;
}

static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_createdata *data)
{
	int status;

	status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
	nfs_post_op_update_inode(dir, data->res.dir_attr);
	if (status == 0)
		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
	return status;
}

static void nfs3_free_createdata(struct nfs3_createdata *data)
{
	kfree(data);
}

/*
 * Create a regular file.
 * For now, we don't implement O_EXCL.
@@ -256,70 +303,60 @@ static int
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
		 int flags, struct nameidata *nd)
{
	struct nfs_fh		fhandle;
	struct nfs_fattr	fattr;
	struct nfs_fattr	dir_attr;
	struct nfs3_createargs	arg = {
		.fh		= NFS_FH(dir),
		.name		= dentry->d_name.name,
		.len		= dentry->d_name.len,
		.sattr		= sattr,
	};
	struct nfs3_diropres	res = {
		.dir_attr	= &dir_attr,
		.fh		= &fhandle,
		.fattr		= &fattr
	};
	struct rpc_message msg = {
		.rpc_proc	= &nfs3_procedures[NFS3PROC_CREATE],
		.rpc_argp	= &arg,
		.rpc_resp	= &res,
	};
	struct nfs3_createdata *data;
	mode_t mode = sattr->ia_mode;
	int status;
	int status = -ENOMEM;

	dprintk("NFS call  create %s\n", dentry->d_name.name);
	arg.createmode = NFS3_CREATE_UNCHECKED;

	data = nfs3_alloc_createdata();
	if (data == NULL)
		goto out;

	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_CREATE];
	data->arg.create.fh = NFS_FH(dir);
	data->arg.create.name = dentry->d_name.name;
	data->arg.create.len = dentry->d_name.len;
	data->arg.create.sattr = sattr;

	data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
	if (flags & O_EXCL) {
		arg.createmode  = NFS3_CREATE_EXCLUSIVE;
		arg.verifier[0] = jiffies;
		arg.verifier[1] = current->pid;
		data->arg.create.createmode  = NFS3_CREATE_EXCLUSIVE;
		data->arg.create.verifier[0] = jiffies;
		data->arg.create.verifier[1] = current->pid;
	}

	sattr->ia_mode &= ~current->fs->umask;

again:
	nfs_fattr_init(&dir_attr);
	nfs_fattr_init(&fattr);
	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
	nfs_refresh_inode(dir, &dir_attr);
	for (;;) {
		status = nfs3_do_create(dir, dentry, data);

	/* If the server doesn't support the exclusive creation semantics,
	 * try again with simple 'guarded' mode. */
	if (status == -ENOTSUPP) {
		switch (arg.createmode) {
		if (status != -ENOTSUPP)
			break;
		/* If the server doesn't support the exclusive creation
		 * semantics, try again with simple 'guarded' mode. */
		switch (data->arg.create.createmode) {
			case NFS3_CREATE_EXCLUSIVE:
				arg.createmode = NFS3_CREATE_GUARDED;
				data->arg.create.createmode = NFS3_CREATE_GUARDED;
				break;

			case NFS3_CREATE_GUARDED:
				arg.createmode = NFS3_CREATE_UNCHECKED;
				data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
				break;

			case NFS3_CREATE_UNCHECKED:
				goto out;
		}
		goto again;
		nfs_fattr_init(data->res.dir_attr);
		nfs_fattr_init(data->res.fattr);
	}

	if (status == 0)
		status = nfs_instantiate(dentry, &fhandle, &fattr);
	if (status != 0)
		goto out;

	/* When we created the file with exclusive semantics, make
	 * sure we set the attributes afterwards. */
	if (arg.createmode == NFS3_CREATE_EXCLUSIVE) {
	if (data->arg.create.createmode == NFS3_CREATE_EXCLUSIVE) {
		dprintk("NFS call  setattr (post-create)\n");

		if (!(sattr->ia_valid & ATTR_ATIME_SET))
@@ -330,14 +367,15 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
		/* Note: we could use a guarded setattr here, but I'm
		 * not sure this buys us anything (and I'd have
		 * to revamp the NFSv3 XDR code) */
		status = nfs3_proc_setattr(dentry, &fattr, sattr);
		nfs_post_op_update_inode(dentry->d_inode, &fattr);
		status = nfs3_proc_setattr(dentry, data->res.fattr, sattr);
		nfs_post_op_update_inode(dentry->d_inode, data->res.fattr);
		dprintk("NFS reply setattr (post-create): %d\n", status);
	}
		if (status != 0)
			goto out;
	}
	status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out:
	nfs3_free_createdata(data);
	dprintk("NFS reply create: %d\n", status);
	return status;
}
@@ -452,40 +490,28 @@ static int
nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
		  unsigned int len, struct iattr *sattr)
{
	struct nfs_fh fhandle;
	struct nfs_fattr fattr, dir_attr;
	struct nfs3_symlinkargs	arg = {
		.fromfh		= NFS_FH(dir),
		.fromname	= dentry->d_name.name,
		.fromlen	= dentry->d_name.len,
		.pages		= &page,
		.pathlen	= len,
		.sattr		= sattr
	};
	struct nfs3_diropres	res = {
		.dir_attr	= &dir_attr,
		.fh		= &fhandle,
		.fattr		= &fattr
	};
	struct rpc_message msg = {
		.rpc_proc	= &nfs3_procedures[NFS3PROC_SYMLINK],
		.rpc_argp	= &arg,
		.rpc_resp	= &res,
	};
	int			status;
	struct nfs3_createdata *data;
	int status = -ENOMEM;

	if (len > NFS3_MAXPATHLEN)
		return -ENAMETOOLONG;

	dprintk("NFS call  symlink %s\n", dentry->d_name.name);

	nfs_fattr_init(&dir_attr);
	nfs_fattr_init(&fattr);
	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
	nfs_post_op_update_inode(dir, &dir_attr);
	if (status != 0)
	data = nfs3_alloc_createdata();
	if (data == NULL)
		goto out;
	status = nfs_instantiate(dentry, &fhandle, &fattr);
	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK];
	data->arg.symlink.fromfh = NFS_FH(dir);
	data->arg.symlink.fromname = dentry->d_name.name;
	data->arg.symlink.fromlen = dentry->d_name.len;
	data->arg.symlink.pages = &page;
	data->arg.symlink.pathlen = len;
	data->arg.symlink.sattr = sattr;

	status = nfs3_do_create(dir, dentry, data);

	nfs3_free_createdata(data);
out:
	dprintk("NFS reply symlink: %d\n", status);
	return status;
@@ -494,42 +520,31 @@ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
static int
nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
{
	struct nfs_fh fhandle;
	struct nfs_fattr fattr, dir_attr;
	struct nfs3_mkdirargs	arg = {
		.fh		= NFS_FH(dir),
		.name		= dentry->d_name.name,
		.len		= dentry->d_name.len,
		.sattr		= sattr
	};
	struct nfs3_diropres	res = {
		.dir_attr	= &dir_attr,
		.fh		= &fhandle,
		.fattr		= &fattr
	};
	struct rpc_message msg = {
		.rpc_proc	= &nfs3_procedures[NFS3PROC_MKDIR],
		.rpc_argp	= &arg,
		.rpc_resp	= &res,
	};
	struct nfs3_createdata *data;
	int mode = sattr->ia_mode;
	int status;
	int status = -ENOMEM;

	dprintk("NFS call  mkdir %s\n", dentry->d_name.name);

	sattr->ia_mode &= ~current->fs->umask;

	nfs_fattr_init(&dir_attr);
	nfs_fattr_init(&fattr);
	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
	nfs_post_op_update_inode(dir, &dir_attr);
	if (status != 0)
	data = nfs3_alloc_createdata();
	if (data == NULL)
		goto out;
	status = nfs_instantiate(dentry, &fhandle, &fattr);

	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR];
	data->arg.mkdir.fh = NFS_FH(dir);
	data->arg.mkdir.name = dentry->d_name.name;
	data->arg.mkdir.len = dentry->d_name.len;
	data->arg.mkdir.sattr = sattr;

	status = nfs3_do_create(dir, dentry, data);
	if (status != 0)
		goto out;

	status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out:
	nfs3_free_createdata(data);
	dprintk("NFS reply mkdir: %d\n", status);
	return status;
}
@@ -615,52 +630,50 @@ static int
nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
		dev_t rdev)
{
	struct nfs_fh fh;
	struct nfs_fattr fattr, dir_attr;
	struct nfs3_mknodargs	arg = {
		.fh		= NFS_FH(dir),
		.name		= dentry->d_name.name,
		.len		= dentry->d_name.len,
		.sattr		= sattr,
		.rdev		= rdev
	};
	struct nfs3_diropres	res = {
		.dir_attr	= &dir_attr,
		.fh		= &fh,
		.fattr		= &fattr
	};
	struct rpc_message msg = {
		.rpc_proc	= &nfs3_procedures[NFS3PROC_MKNOD],
		.rpc_argp	= &arg,
		.rpc_resp	= &res,
	};
	struct nfs3_createdata *data;
	mode_t mode = sattr->ia_mode;
	int status;

	switch (sattr->ia_mode & S_IFMT) {
	case S_IFBLK:	arg.type = NF3BLK;  break;
	case S_IFCHR:	arg.type = NF3CHR;  break;
	case S_IFIFO:	arg.type = NF3FIFO; break;
	case S_IFSOCK:	arg.type = NF3SOCK; break;
	default:	return -EINVAL;
	}
	int status = -ENOMEM;

	dprintk("NFS call  mknod %s %u:%u\n", dentry->d_name.name,
			MAJOR(rdev), MINOR(rdev));

	sattr->ia_mode &= ~current->fs->umask;

	nfs_fattr_init(&dir_attr);
	nfs_fattr_init(&fattr);
	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
	nfs_post_op_update_inode(dir, &dir_attr);
	if (status != 0)
	data = nfs3_alloc_createdata();
	if (data == NULL)
		goto out;

	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKNOD];
	data->arg.mknod.fh = NFS_FH(dir);
	data->arg.mknod.name = dentry->d_name.name;
	data->arg.mknod.len = dentry->d_name.len;
	data->arg.mknod.sattr = sattr;
	data->arg.mknod.rdev = rdev;

	switch (sattr->ia_mode & S_IFMT) {
	case S_IFBLK:
		data->arg.mknod.type = NF3BLK;
		break;
	case S_IFCHR:
		data->arg.mknod.type = NF3CHR;
		break;
	case S_IFIFO:
		data->arg.mknod.type = NF3FIFO;
		break;
	case S_IFSOCK:
		data->arg.mknod.type = NF3SOCK;
		break;
	default:
		status = -EINVAL;
		goto out;
	status = nfs_instantiate(dentry, &fh, &fattr);
	}

	status = nfs3_do_create(dir, dentry, data);
	if (status != 0)
		goto out;
	status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out:
	nfs3_free_createdata(data);
	dprintk("NFS reply mknod: %d\n", status);
	return status;
}