Fix state of dquot-less vnodes after failed quotaoff.
UFS quotaoff iterates over all mp vnodes, and derefences and clears the pointers to corresponding dquots. If SU work items transiently reference some of dquots,quotaoff() would eventually fail, but all processed vnodes are already stripped from dquots. The state is problematic, since quotas are left enabled, but there is no dquots where blocks and inodes can be accounted. The result is assertion failures and NULL pointer dereferences. Fix it by suspending writes around quotaoff() call. Since the filesystem is synced, no dandling references to dquots from SU workitems can left behind, which means that quotaoff succeeds. The complication there is that quotaoff VFS op is performed with the mount point busied, while to suspend, we need to start write on the mp. If vn_start_write() is called on busied mp, system might deadlock against parallel unmount request. Handle this by unbusy-ing mp before starting write, which in turn requires changing the quotaoff() interface to return with the mount point not busied, same as was done for quotaon(). Reviewed by: mckusick Reported and tested by: pho Sponsored by: The FreeBSD Foundation Approved by: re (gjb) MFC after: 1 week Differential revision: https://reviews.freebsd.org/D17208
This commit is contained in:
parent
f803ec1ef5
commit
bf94d6c78b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=338798
@ -190,7 +190,8 @@ sys_quotactl(struct thread *td, struct quotactl_args *uap)
|
||||
* Require that Q_QUOTAON handles the vfs_busy() reference on
|
||||
* its own, always returning with ubusied mount point.
|
||||
*/
|
||||
if ((uap->cmd >> SUBCMDSHIFT) != Q_QUOTAON)
|
||||
if ((uap->cmd >> SUBCMDSHIFT) != Q_QUOTAON &&
|
||||
(uap->cmd >> SUBCMDSHIFT) != Q_QUOTAOFF)
|
||||
vfs_unbusy(mp);
|
||||
return (error);
|
||||
}
|
||||
|
@ -712,6 +712,34 @@ quotaoff1(struct thread *td, struct mount *mp, int type)
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
quotaoff_inchange1(struct thread *td, struct mount *mp, int type)
|
||||
{
|
||||
int error;
|
||||
bool need_resume;
|
||||
|
||||
/*
|
||||
* mp is already suspended on unmount. If not, suspend it, to
|
||||
* avoid the situation where quotaoff operation eventually
|
||||
* failing due to SU structures still keeping references on
|
||||
* dquots, but vnode's references are already clean. This
|
||||
* would cause quota accounting leak and asserts otherwise.
|
||||
* Note that the thread has already called vn_start_write().
|
||||
*/
|
||||
if (mp->mnt_susp_owner == td) {
|
||||
need_resume = false;
|
||||
} else {
|
||||
error = vfs_write_suspend_umnt(mp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
need_resume = true;
|
||||
}
|
||||
error = quotaoff1(td, mp, type);
|
||||
if (need_resume)
|
||||
vfs_write_resume(mp, VR_START_WRITE);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turns off quotas, assumes that ump->um_qflags are already checked
|
||||
* and QTF_CLOSING is set to indicate operation in progress. Fixes
|
||||
@ -721,10 +749,9 @@ int
|
||||
quotaoff_inchange(struct thread *td, struct mount *mp, int type)
|
||||
{
|
||||
struct ufsmount *ump;
|
||||
int i;
|
||||
int error;
|
||||
int error, i;
|
||||
|
||||
error = quotaoff1(td, mp, type);
|
||||
error = quotaoff_inchange1(td, mp, type);
|
||||
|
||||
ump = VFSTOUFS(mp);
|
||||
UFS_LOCK(ump);
|
||||
|
@ -94,7 +94,8 @@ ufs_quotactl(mp, cmds, id, arg)
|
||||
void *arg;
|
||||
{
|
||||
#ifndef QUOTA
|
||||
if ((cmds >> SUBCMDSHIFT) == Q_QUOTAON)
|
||||
if ((cmds >> SUBCMDSHIFT) == Q_QUOTAON ||
|
||||
(cmds >> SUBCMDSHIFT) == Q_QUOTAOFF)
|
||||
vfs_unbusy(mp);
|
||||
|
||||
return (EOPNOTSUPP);
|
||||
@ -117,13 +118,13 @@ ufs_quotactl(mp, cmds, id, arg)
|
||||
break;
|
||||
|
||||
default:
|
||||
if (cmd == Q_QUOTAON)
|
||||
if (cmd == Q_QUOTAON || cmd == Q_QUOTAOFF)
|
||||
vfs_unbusy(mp);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
if ((u_int)type >= MAXQUOTAS) {
|
||||
if (cmd == Q_QUOTAON)
|
||||
if (cmd == Q_QUOTAON || cmd == Q_QUOTAOFF)
|
||||
vfs_unbusy(mp);
|
||||
return (EINVAL);
|
||||
}
|
||||
@ -134,7 +135,11 @@ ufs_quotactl(mp, cmds, id, arg)
|
||||
break;
|
||||
|
||||
case Q_QUOTAOFF:
|
||||
vfs_ref(mp);
|
||||
vfs_unbusy(mp);
|
||||
vn_start_write(NULL, &mp, V_WAIT | V_MNTREF);
|
||||
error = quotaoff(td, mp, type);
|
||||
vn_finished_write(mp);
|
||||
break;
|
||||
|
||||
case Q_SETQUOTA32:
|
||||
|
Loading…
Reference in New Issue
Block a user