mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
NFS: Reduce the stack usage in NFSv3 create operations
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
57dc9a5747
commit
0b4aae7aad
@ -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 @@ again:
|
||||
/* 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;
|
||||
}
|
||||
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 @@ out:
|
||||
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)
|
||||
goto out;
|
||||
status = nfs_instantiate(dentry, &fhandle, &fattr);
|
||||
data = nfs3_alloc_createdata();
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
|
||||
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;
|
||||
status = nfs_instantiate(dentry, &fh, &fattr);
|
||||
|
||||
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 = 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user