nfsd: Push mnt_want_write() outside of i_mutex
When mnt_want_write() starts to handle freezing it will get a full lock semantics requiring proper lock ordering. So push mnt_want_write() call consistently outside of i_mutex. CC: linux-nfs@vger.kernel.org CC: "J. Bruce Fields" <bfields@fieldses.org> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
e7848683ae
commit
4a55c1017b
|
@ -154,6 +154,10 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
status = mnt_want_write_file(rec_file);
|
||||||
|
if (status)
|
||||||
|
return;
|
||||||
|
|
||||||
dir = rec_file->f_path.dentry;
|
dir = rec_file->f_path.dentry;
|
||||||
/* lock the parent */
|
/* lock the parent */
|
||||||
mutex_lock(&dir->d_inode->i_mutex);
|
mutex_lock(&dir->d_inode->i_mutex);
|
||||||
|
@ -173,11 +177,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
|
||||||
* as well be forgiving and just succeed silently.
|
* as well be forgiving and just succeed silently.
|
||||||
*/
|
*/
|
||||||
goto out_put;
|
goto out_put;
|
||||||
status = mnt_want_write_file(rec_file);
|
|
||||||
if (status)
|
|
||||||
goto out_put;
|
|
||||||
status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
|
status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
|
||||||
mnt_drop_write_file(rec_file);
|
|
||||||
out_put:
|
out_put:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
|
@ -189,6 +189,7 @@ out_unlock:
|
||||||
" (err %d); please check that %s exists"
|
" (err %d); please check that %s exists"
|
||||||
" and is writeable", status,
|
" and is writeable", status,
|
||||||
user_recovery_dirname);
|
user_recovery_dirname);
|
||||||
|
mnt_drop_write_file(rec_file);
|
||||||
nfs4_reset_creds(original_cred);
|
nfs4_reset_creds(original_cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -635,6 +635,7 @@ fh_put(struct svc_fh *fhp)
|
||||||
fhp->fh_post_saved = 0;
|
fhp->fh_post_saved = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
fh_drop_write(fhp);
|
||||||
if (exp) {
|
if (exp) {
|
||||||
exp_put(exp);
|
exp_put(exp);
|
||||||
fhp->fh_export = NULL;
|
fhp->fh_export = NULL;
|
||||||
|
|
|
@ -196,6 +196,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
|
||||||
struct dentry *dchild;
|
struct dentry *dchild;
|
||||||
int type, mode;
|
int type, mode;
|
||||||
__be32 nfserr;
|
__be32 nfserr;
|
||||||
|
int hosterr;
|
||||||
dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size);
|
dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size);
|
||||||
|
|
||||||
dprintk("nfsd: CREATE %s %.*s\n",
|
dprintk("nfsd: CREATE %s %.*s\n",
|
||||||
|
@ -214,6 +215,12 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
|
||||||
nfserr = nfserr_exist;
|
nfserr = nfserr_exist;
|
||||||
if (isdotent(argp->name, argp->len))
|
if (isdotent(argp->name, argp->len))
|
||||||
goto done;
|
goto done;
|
||||||
|
hosterr = fh_want_write(dirfhp);
|
||||||
|
if (hosterr) {
|
||||||
|
nfserr = nfserrno(hosterr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
fh_lock_nested(dirfhp, I_MUTEX_PARENT);
|
fh_lock_nested(dirfhp, I_MUTEX_PARENT);
|
||||||
dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
|
dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
|
||||||
if (IS_ERR(dchild)) {
|
if (IS_ERR(dchild)) {
|
||||||
|
@ -330,7 +337,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
|
||||||
out_unlock:
|
out_unlock:
|
||||||
/* We don't really need to unlock, as fh_put does it. */
|
/* We don't really need to unlock, as fh_put does it. */
|
||||||
fh_unlock(dirfhp);
|
fh_unlock(dirfhp);
|
||||||
|
fh_drop_write(dirfhp);
|
||||||
done:
|
done:
|
||||||
fh_put(dirfhp);
|
fh_put(dirfhp);
|
||||||
return nfsd_return_dirop(nfserr, resp);
|
return nfsd_return_dirop(nfserr, resp);
|
||||||
|
|
|
@ -1276,6 +1276,10 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
* If it has, the parent directory should already be locked.
|
* If it has, the parent directory should already be locked.
|
||||||
*/
|
*/
|
||||||
if (!resfhp->fh_dentry) {
|
if (!resfhp->fh_dentry) {
|
||||||
|
host_err = fh_want_write(fhp);
|
||||||
|
if (host_err)
|
||||||
|
goto out_nfserr;
|
||||||
|
|
||||||
/* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
|
/* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
|
||||||
fh_lock_nested(fhp, I_MUTEX_PARENT);
|
fh_lock_nested(fhp, I_MUTEX_PARENT);
|
||||||
dchild = lookup_one_len(fname, dentry, flen);
|
dchild = lookup_one_len(fname, dentry, flen);
|
||||||
|
@ -1319,14 +1323,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
host_err = fh_want_write(fhp);
|
|
||||||
if (host_err)
|
|
||||||
goto out_nfserr;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the dir op function pointer.
|
* Get the dir op function pointer.
|
||||||
*/
|
*/
|
||||||
err = 0;
|
err = 0;
|
||||||
|
host_err = 0;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case S_IFREG:
|
case S_IFREG:
|
||||||
host_err = vfs_create(dirp, dchild, iap->ia_mode, true);
|
host_err = vfs_create(dirp, dchild, iap->ia_mode, true);
|
||||||
|
@ -1343,10 +1344,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
|
host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (host_err < 0) {
|
if (host_err < 0)
|
||||||
fh_drop_write(fhp);
|
|
||||||
goto out_nfserr;
|
goto out_nfserr;
|
||||||
}
|
|
||||||
|
|
||||||
err = nfsd_create_setattr(rqstp, resfhp, iap);
|
err = nfsd_create_setattr(rqstp, resfhp, iap);
|
||||||
|
|
||||||
|
@ -1358,7 +1357,6 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
err2 = nfserrno(commit_metadata(fhp));
|
err2 = nfserrno(commit_metadata(fhp));
|
||||||
if (err2)
|
if (err2)
|
||||||
err = err2;
|
err = err2;
|
||||||
fh_drop_write(fhp);
|
|
||||||
/*
|
/*
|
||||||
* Update the file handle to get the new inode info.
|
* Update the file handle to get the new inode info.
|
||||||
*/
|
*/
|
||||||
|
@ -1417,6 +1415,11 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
err = nfserr_notdir;
|
err = nfserr_notdir;
|
||||||
if (!dirp->i_op->lookup)
|
if (!dirp->i_op->lookup)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
host_err = fh_want_write(fhp);
|
||||||
|
if (host_err)
|
||||||
|
goto out_nfserr;
|
||||||
|
|
||||||
fh_lock_nested(fhp, I_MUTEX_PARENT);
|
fh_lock_nested(fhp, I_MUTEX_PARENT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1449,9 +1452,6 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
v_atime = verifier[1]&0x7fffffff;
|
v_atime = verifier[1]&0x7fffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
host_err = fh_want_write(fhp);
|
|
||||||
if (host_err)
|
|
||||||
goto out_nfserr;
|
|
||||||
if (dchild->d_inode) {
|
if (dchild->d_inode) {
|
||||||
err = 0;
|
err = 0;
|
||||||
|
|
||||||
|
@ -1522,7 +1522,6 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
if (!err)
|
if (!err)
|
||||||
err = nfserrno(commit_metadata(fhp));
|
err = nfserrno(commit_metadata(fhp));
|
||||||
|
|
||||||
fh_drop_write(fhp);
|
|
||||||
/*
|
/*
|
||||||
* Update the filehandle to get the new inode info.
|
* Update the filehandle to get the new inode info.
|
||||||
*/
|
*/
|
||||||
|
@ -1533,6 +1532,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
fh_unlock(fhp);
|
fh_unlock(fhp);
|
||||||
if (dchild && !IS_ERR(dchild))
|
if (dchild && !IS_ERR(dchild))
|
||||||
dput(dchild);
|
dput(dchild);
|
||||||
|
fh_drop_write(fhp);
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
out_nfserr:
|
out_nfserr:
|
||||||
|
@ -1613,6 +1613,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
|
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
host_err = fh_want_write(fhp);
|
||||||
|
if (host_err)
|
||||||
|
goto out_nfserr;
|
||||||
|
|
||||||
fh_lock(fhp);
|
fh_lock(fhp);
|
||||||
dentry = fhp->fh_dentry;
|
dentry = fhp->fh_dentry;
|
||||||
dnew = lookup_one_len(fname, dentry, flen);
|
dnew = lookup_one_len(fname, dentry, flen);
|
||||||
|
@ -1620,10 +1625,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||||
if (IS_ERR(dnew))
|
if (IS_ERR(dnew))
|
||||||
goto out_nfserr;
|
goto out_nfserr;
|
||||||
|
|
||||||
host_err = fh_want_write(fhp);
|
|
||||||
if (host_err)
|
|
||||||
goto out_nfserr;
|
|
||||||
|
|
||||||
if (unlikely(path[plen] != 0)) {
|
if (unlikely(path[plen] != 0)) {
|
||||||
char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
|
char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
|
||||||
if (path_alloced == NULL)
|
if (path_alloced == NULL)
|
||||||
|
@ -1683,6 +1684,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
||||||
if (isdotent(name, len))
|
if (isdotent(name, len))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
host_err = fh_want_write(tfhp);
|
||||||
|
if (host_err) {
|
||||||
|
err = nfserrno(host_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
fh_lock_nested(ffhp, I_MUTEX_PARENT);
|
fh_lock_nested(ffhp, I_MUTEX_PARENT);
|
||||||
ddir = ffhp->fh_dentry;
|
ddir = ffhp->fh_dentry;
|
||||||
dirp = ddir->d_inode;
|
dirp = ddir->d_inode;
|
||||||
|
@ -1694,18 +1701,13 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
||||||
|
|
||||||
dold = tfhp->fh_dentry;
|
dold = tfhp->fh_dentry;
|
||||||
|
|
||||||
host_err = fh_want_write(tfhp);
|
|
||||||
if (host_err) {
|
|
||||||
err = nfserrno(host_err);
|
|
||||||
goto out_dput;
|
|
||||||
}
|
|
||||||
err = nfserr_noent;
|
err = nfserr_noent;
|
||||||
if (!dold->d_inode)
|
if (!dold->d_inode)
|
||||||
goto out_drop_write;
|
goto out_dput;
|
||||||
host_err = nfsd_break_lease(dold->d_inode);
|
host_err = nfsd_break_lease(dold->d_inode);
|
||||||
if (host_err) {
|
if (host_err) {
|
||||||
err = nfserrno(host_err);
|
err = nfserrno(host_err);
|
||||||
goto out_drop_write;
|
goto out_dput;
|
||||||
}
|
}
|
||||||
host_err = vfs_link(dold, dirp, dnew);
|
host_err = vfs_link(dold, dirp, dnew);
|
||||||
if (!host_err) {
|
if (!host_err) {
|
||||||
|
@ -1718,12 +1720,11 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
||||||
else
|
else
|
||||||
err = nfserrno(host_err);
|
err = nfserrno(host_err);
|
||||||
}
|
}
|
||||||
out_drop_write:
|
|
||||||
fh_drop_write(tfhp);
|
|
||||||
out_dput:
|
out_dput:
|
||||||
dput(dnew);
|
dput(dnew);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
fh_unlock(ffhp);
|
fh_unlock(ffhp);
|
||||||
|
fh_drop_write(tfhp);
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1766,6 +1767,12 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
||||||
if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
|
if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
host_err = fh_want_write(ffhp);
|
||||||
|
if (host_err) {
|
||||||
|
err = nfserrno(host_err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* cannot use fh_lock as we need deadlock protective ordering
|
/* cannot use fh_lock as we need deadlock protective ordering
|
||||||
* so do it by hand */
|
* so do it by hand */
|
||||||
trap = lock_rename(tdentry, fdentry);
|
trap = lock_rename(tdentry, fdentry);
|
||||||
|
@ -1796,17 +1803,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
||||||
host_err = -EXDEV;
|
host_err = -EXDEV;
|
||||||
if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
|
if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
|
||||||
goto out_dput_new;
|
goto out_dput_new;
|
||||||
host_err = fh_want_write(ffhp);
|
|
||||||
if (host_err)
|
|
||||||
goto out_dput_new;
|
|
||||||
|
|
||||||
host_err = nfsd_break_lease(odentry->d_inode);
|
host_err = nfsd_break_lease(odentry->d_inode);
|
||||||
if (host_err)
|
if (host_err)
|
||||||
goto out_drop_write;
|
goto out_dput_new;
|
||||||
if (ndentry->d_inode) {
|
if (ndentry->d_inode) {
|
||||||
host_err = nfsd_break_lease(ndentry->d_inode);
|
host_err = nfsd_break_lease(ndentry->d_inode);
|
||||||
if (host_err)
|
if (host_err)
|
||||||
goto out_drop_write;
|
goto out_dput_new;
|
||||||
}
|
}
|
||||||
host_err = vfs_rename(fdir, odentry, tdir, ndentry);
|
host_err = vfs_rename(fdir, odentry, tdir, ndentry);
|
||||||
if (!host_err) {
|
if (!host_err) {
|
||||||
|
@ -1814,8 +1818,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
||||||
if (!host_err)
|
if (!host_err)
|
||||||
host_err = commit_metadata(ffhp);
|
host_err = commit_metadata(ffhp);
|
||||||
}
|
}
|
||||||
out_drop_write:
|
|
||||||
fh_drop_write(ffhp);
|
|
||||||
out_dput_new:
|
out_dput_new:
|
||||||
dput(ndentry);
|
dput(ndentry);
|
||||||
out_dput_old:
|
out_dput_old:
|
||||||
|
@ -1831,6 +1833,7 @@ out_drop_write:
|
||||||
fill_post_wcc(tfhp);
|
fill_post_wcc(tfhp);
|
||||||
unlock_rename(tdentry, fdentry);
|
unlock_rename(tdentry, fdentry);
|
||||||
ffhp->fh_locked = tfhp->fh_locked = 0;
|
ffhp->fh_locked = tfhp->fh_locked = 0;
|
||||||
|
fh_drop_write(ffhp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
|
@ -1856,6 +1859,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
host_err = fh_want_write(fhp);
|
||||||
|
if (host_err)
|
||||||
|
goto out_nfserr;
|
||||||
|
|
||||||
fh_lock_nested(fhp, I_MUTEX_PARENT);
|
fh_lock_nested(fhp, I_MUTEX_PARENT);
|
||||||
dentry = fhp->fh_dentry;
|
dentry = fhp->fh_dentry;
|
||||||
dirp = dentry->d_inode;
|
dirp = dentry->d_inode;
|
||||||
|
@ -1874,21 +1881,15 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||||
if (!type)
|
if (!type)
|
||||||
type = rdentry->d_inode->i_mode & S_IFMT;
|
type = rdentry->d_inode->i_mode & S_IFMT;
|
||||||
|
|
||||||
host_err = fh_want_write(fhp);
|
|
||||||
if (host_err)
|
|
||||||
goto out_put;
|
|
||||||
|
|
||||||
host_err = nfsd_break_lease(rdentry->d_inode);
|
host_err = nfsd_break_lease(rdentry->d_inode);
|
||||||
if (host_err)
|
if (host_err)
|
||||||
goto out_drop_write;
|
goto out_put;
|
||||||
if (type != S_IFDIR)
|
if (type != S_IFDIR)
|
||||||
host_err = vfs_unlink(dirp, rdentry);
|
host_err = vfs_unlink(dirp, rdentry);
|
||||||
else
|
else
|
||||||
host_err = vfs_rmdir(dirp, rdentry);
|
host_err = vfs_rmdir(dirp, rdentry);
|
||||||
if (!host_err)
|
if (!host_err)
|
||||||
host_err = commit_metadata(fhp);
|
host_err = commit_metadata(fhp);
|
||||||
out_drop_write:
|
|
||||||
fh_drop_write(fhp);
|
|
||||||
out_put:
|
out_put:
|
||||||
dput(rdentry);
|
dput(rdentry);
|
||||||
|
|
||||||
|
|
|
@ -110,12 +110,19 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
|
||||||
|
|
||||||
static inline int fh_want_write(struct svc_fh *fh)
|
static inline int fh_want_write(struct svc_fh *fh)
|
||||||
{
|
{
|
||||||
return mnt_want_write(fh->fh_export->ex_path.mnt);
|
int ret = mnt_want_write(fh->fh_export->ex_path.mnt);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
fh->fh_want_write = 1;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void fh_drop_write(struct svc_fh *fh)
|
static inline void fh_drop_write(struct svc_fh *fh)
|
||||||
{
|
{
|
||||||
mnt_drop_write(fh->fh_export->ex_path.mnt);
|
if (fh->fh_want_write) {
|
||||||
|
fh->fh_want_write = 0;
|
||||||
|
mnt_drop_write(fh->fh_export->ex_path.mnt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* LINUX_NFSD_VFS_H */
|
#endif /* LINUX_NFSD_VFS_H */
|
||||||
|
|
|
@ -143,6 +143,7 @@ typedef struct svc_fh {
|
||||||
int fh_maxsize; /* max size for fh_handle */
|
int fh_maxsize; /* max size for fh_handle */
|
||||||
|
|
||||||
unsigned char fh_locked; /* inode locked by us */
|
unsigned char fh_locked; /* inode locked by us */
|
||||||
|
unsigned char fh_want_write; /* remount protection taken */
|
||||||
|
|
||||||
#ifdef CONFIG_NFSD_V3
|
#ifdef CONFIG_NFSD_V3
|
||||||
unsigned char fh_post_saved; /* post-op attrs saved */
|
unsigned char fh_post_saved; /* post-op attrs saved */
|
||||||
|
|
Loading…
Reference in a new issue