NFSv4: Ensure that nfs4_do_close() doesn't race with umount
nfs4_do_close() does not currently have any way to ensure that the user won't attempt to unmount the partition while the asynchronous RPC call is completing. This again may cause Oopses in nfs_update_inode(). Add a vfsmount argument to nfs4_close_state to ensure that the partition remains mounted while we're closing the file. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
ad389da79f
commit
4a35bd41af
@ -490,7 +490,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx)
|
|||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
if (ctx->state != NULL)
|
if (ctx->state != NULL)
|
||||||
nfs4_close_state(ctx->state, ctx->mode);
|
nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
|
||||||
if (ctx->cred != NULL)
|
if (ctx->cred != NULL)
|
||||||
put_rpccred(ctx->cred);
|
put_rpccred(ctx->cred);
|
||||||
dput(ctx->path.dentry);
|
dput(ctx->path.dentry);
|
||||||
@ -1103,27 +1103,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
|||||||
*/
|
*/
|
||||||
void nfs4_clear_inode(struct inode *inode)
|
void nfs4_clear_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct nfs_inode *nfsi = NFS_I(inode);
|
|
||||||
|
|
||||||
/* If we are holding a delegation, return it! */
|
/* If we are holding a delegation, return it! */
|
||||||
nfs_inode_return_delegation(inode);
|
nfs_inode_return_delegation(inode);
|
||||||
/* First call standard NFS clear_inode() code */
|
/* First call standard NFS clear_inode() code */
|
||||||
nfs_clear_inode(inode);
|
nfs_clear_inode(inode);
|
||||||
/* Now clear out any remaining state */
|
|
||||||
while (!list_empty(&nfsi->open_states)) {
|
|
||||||
struct nfs4_state *state;
|
|
||||||
|
|
||||||
state = list_entry(nfsi->open_states.next,
|
|
||||||
struct nfs4_state,
|
|
||||||
inode_states);
|
|
||||||
dprintk("%s(%s/%Ld): found unclaimed NFSv4 state %p\n",
|
|
||||||
__FUNCTION__,
|
|
||||||
inode->i_sb->s_id,
|
|
||||||
(long long)NFS_FILEID(inode),
|
|
||||||
state);
|
|
||||||
BUG_ON(atomic_read(&state->count) != 1);
|
|
||||||
nfs4_close_state(state, state->state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc
|
|||||||
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
|
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
|
||||||
extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
|
extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
|
||||||
extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
|
extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
|
||||||
extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state);
|
extern int nfs4_do_close(struct path *path, struct nfs4_state *state);
|
||||||
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
|
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
|
||||||
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
|
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
|
||||||
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
|
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
|
||||||
@ -196,7 +196,7 @@ extern void nfs4_put_state_owner(struct nfs4_state_owner *);
|
|||||||
extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
|
extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
|
||||||
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
|
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
|
||||||
extern void nfs4_put_open_state(struct nfs4_state *);
|
extern void nfs4_put_open_state(struct nfs4_state *);
|
||||||
extern void nfs4_close_state(struct nfs4_state *, mode_t);
|
extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t);
|
||||||
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
|
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
|
||||||
extern void nfs4_schedule_state_recovery(struct nfs_client *);
|
extern void nfs4_schedule_state_recovery(struct nfs_client *);
|
||||||
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
|
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
|
||||||
@ -222,7 +222,7 @@ extern struct svc_version nfs4_callback_version1;
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define nfs4_close_state(a, b) do { } while (0)
|
#define nfs4_close_state(a, b, c) do { } while (0)
|
||||||
|
|
||||||
#endif /* CONFIG_NFS_V4 */
|
#endif /* CONFIG_NFS_V4 */
|
||||||
#endif /* __LINUX_FS_NFS_NFS4_FS.H */
|
#endif /* __LINUX_FS_NFS_NFS4_FS.H */
|
||||||
|
@ -453,7 +453,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
|
|||||||
opendata->owner->so_cred,
|
opendata->owner->so_cred,
|
||||||
&opendata->o_res);
|
&opendata->o_res);
|
||||||
}
|
}
|
||||||
nfs4_close_state(newstate, opendata->o_arg.open_flags);
|
nfs4_close_state(&opendata->path, newstate, opendata->o_arg.open_flags);
|
||||||
}
|
}
|
||||||
if (newstate != state)
|
if (newstate != state)
|
||||||
return -ESTALE;
|
return -ESTALE;
|
||||||
@ -603,7 +603,7 @@ static void nfs4_open_confirm_release(void *calldata)
|
|||||||
nfs_confirm_seqid(&data->owner->so_seqid, 0);
|
nfs_confirm_seqid(&data->owner->so_seqid, 0);
|
||||||
state = nfs4_opendata_to_nfs4_state(data);
|
state = nfs4_opendata_to_nfs4_state(data);
|
||||||
if (state != NULL)
|
if (state != NULL)
|
||||||
nfs4_close_state(state, data->o_arg.open_flags);
|
nfs4_close_state(&data->path, state, data->o_arg.open_flags);
|
||||||
out_free:
|
out_free:
|
||||||
nfs4_opendata_free(data);
|
nfs4_opendata_free(data);
|
||||||
}
|
}
|
||||||
@ -706,7 +706,7 @@ static void nfs4_open_release(void *calldata)
|
|||||||
nfs_confirm_seqid(&data->owner->so_seqid, 0);
|
nfs_confirm_seqid(&data->owner->so_seqid, 0);
|
||||||
state = nfs4_opendata_to_nfs4_state(data);
|
state = nfs4_opendata_to_nfs4_state(data);
|
||||||
if (state != NULL)
|
if (state != NULL)
|
||||||
nfs4_close_state(state, data->o_arg.open_flags);
|
nfs4_close_state(&data->path, state, data->o_arg.open_flags);
|
||||||
out_free:
|
out_free:
|
||||||
nfs4_opendata_free(data);
|
nfs4_opendata_free(data);
|
||||||
}
|
}
|
||||||
@ -1103,6 +1103,7 @@ static int nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct nfs4_closedata {
|
struct nfs4_closedata {
|
||||||
|
struct path path;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct nfs4_state *state;
|
struct nfs4_state *state;
|
||||||
struct nfs_closeargs arg;
|
struct nfs_closeargs arg;
|
||||||
@ -1119,6 +1120,8 @@ static void nfs4_free_closedata(void *data)
|
|||||||
nfs4_put_open_state(calldata->state);
|
nfs4_put_open_state(calldata->state);
|
||||||
nfs_free_seqid(calldata->arg.seqid);
|
nfs_free_seqid(calldata->arg.seqid);
|
||||||
nfs4_put_state_owner(sp);
|
nfs4_put_state_owner(sp);
|
||||||
|
dput(calldata->path.dentry);
|
||||||
|
mntput(calldata->path.mnt);
|
||||||
kfree(calldata);
|
kfree(calldata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1211,18 +1214,18 @@ static const struct rpc_call_ops nfs4_close_ops = {
|
|||||||
*
|
*
|
||||||
* NOTE: Caller must be holding the sp->so_owner semaphore!
|
* NOTE: Caller must be holding the sp->so_owner semaphore!
|
||||||
*/
|
*/
|
||||||
int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
|
int nfs4_do_close(struct path *path, struct nfs4_state *state)
|
||||||
{
|
{
|
||||||
struct nfs_server *server = NFS_SERVER(inode);
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
||||||
struct nfs4_closedata *calldata;
|
struct nfs4_closedata *calldata;
|
||||||
int status = -ENOMEM;
|
int status = -ENOMEM;
|
||||||
|
|
||||||
calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
|
calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
|
||||||
if (calldata == NULL)
|
if (calldata == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
calldata->inode = inode;
|
calldata->inode = state->inode;
|
||||||
calldata->state = state;
|
calldata->state = state;
|
||||||
calldata->arg.fh = NFS_FH(inode);
|
calldata->arg.fh = NFS_FH(state->inode);
|
||||||
calldata->arg.stateid = &state->stateid;
|
calldata->arg.stateid = &state->stateid;
|
||||||
/* Serialization for the sequence id */
|
/* Serialization for the sequence id */
|
||||||
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
|
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
|
||||||
@ -1231,6 +1234,8 @@ int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
|
|||||||
calldata->arg.bitmask = server->attr_bitmask;
|
calldata->arg.bitmask = server->attr_bitmask;
|
||||||
calldata->res.fattr = &calldata->fattr;
|
calldata->res.fattr = &calldata->fattr;
|
||||||
calldata->res.server = server;
|
calldata->res.server = server;
|
||||||
|
calldata->path.mnt = mntget(path->mnt);
|
||||||
|
calldata->path.dentry = dget(path->dentry);
|
||||||
|
|
||||||
status = nfs4_call_async(server->client, &nfs4_close_ops, calldata);
|
status = nfs4_call_async(server->client, &nfs4_close_ops, calldata);
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
@ -1243,18 +1248,18 @@ out:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
|
static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state)
|
||||||
{
|
{
|
||||||
struct file *filp;
|
struct file *filp;
|
||||||
|
|
||||||
filp = lookup_instantiate_filp(nd, dentry, NULL);
|
filp = lookup_instantiate_filp(nd, path->dentry, NULL);
|
||||||
if (!IS_ERR(filp)) {
|
if (!IS_ERR(filp)) {
|
||||||
struct nfs_open_context *ctx;
|
struct nfs_open_context *ctx;
|
||||||
ctx = (struct nfs_open_context *)filp->private_data;
|
ctx = (struct nfs_open_context *)filp->private_data;
|
||||||
ctx->state = state;
|
ctx->state = state;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
nfs4_close_state(state, nd->intent.open.flags);
|
nfs4_close_state(path, state, nd->intent.open.flags);
|
||||||
return PTR_ERR(filp);
|
return PTR_ERR(filp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1293,7 +1298,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
|||||||
res = d_add_unique(dentry, igrab(state->inode));
|
res = d_add_unique(dentry, igrab(state->inode));
|
||||||
if (res != NULL)
|
if (res != NULL)
|
||||||
dentry = res;
|
dentry = res;
|
||||||
nfs4_intent_set_file(nd, dentry, state);
|
nfs4_intent_set_file(nd, &path, state);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1328,10 +1333,10 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state->inode == dentry->d_inode) {
|
if (state->inode == dentry->d_inode) {
|
||||||
nfs4_intent_set_file(nd, dentry, state);
|
nfs4_intent_set_file(nd, &path, state);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
nfs4_close_state(state, openflags);
|
nfs4_close_state(&path, state, openflags);
|
||||||
out_drop:
|
out_drop:
|
||||||
d_drop(dentry);
|
d_drop(dentry);
|
||||||
return 0;
|
return 0;
|
||||||
@ -1789,9 +1794,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
|||||||
nfs_setattr_update_inode(state->inode, sattr);
|
nfs_setattr_update_inode(state->inode, sattr);
|
||||||
}
|
}
|
||||||
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
|
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
|
||||||
status = nfs4_intent_set_file(nd, dentry, state);
|
status = nfs4_intent_set_file(nd, &path, state);
|
||||||
else
|
else
|
||||||
nfs4_close_state(state, flags);
|
nfs4_close_state(&path, state, flags);
|
||||||
out:
|
out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ void nfs4_put_open_state(struct nfs4_state *state)
|
|||||||
/*
|
/*
|
||||||
* Close the current file.
|
* Close the current file.
|
||||||
*/
|
*/
|
||||||
void nfs4_close_state(struct nfs4_state *state, mode_t mode)
|
void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode)
|
||||||
{
|
{
|
||||||
struct inode *inode = state->inode;
|
struct inode *inode = state->inode;
|
||||||
struct nfs4_state_owner *owner = state->owner;
|
struct nfs4_state_owner *owner = state->owner;
|
||||||
@ -375,7 +375,7 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
|
|||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
spin_unlock(&owner->so_lock);
|
spin_unlock(&owner->so_lock);
|
||||||
|
|
||||||
if (oldstate != newstate && nfs4_do_close(inode, state) == 0)
|
if (oldstate != newstate && nfs4_do_close(path, state) == 0)
|
||||||
return;
|
return;
|
||||||
nfs4_put_open_state(state);
|
nfs4_put_open_state(state);
|
||||||
nfs4_put_state_owner(owner);
|
nfs4_put_state_owner(owner);
|
||||||
|
Loading…
Reference in New Issue
Block a user