Index: share/man/man9/VFS_QUOTACTL.9 =================================================================== --- share/man/man9/VFS_QUOTACTL.9 +++ share/man/man9/VFS_QUOTACTL.9 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 17, 2020 +.Dd May 11, 2021 .Dt VFS_QUOTACTL 9 .Os .Sh NAME @@ -39,12 +39,38 @@ .In sys/mount.h .In sys/vnode.h .Ft int -.Fn VFS_QUOTACTL "struct mount *mp" "int cmds" "uid_t uid" "void *arg" +.Fn VFS_QUOTACTL "struct mount *mp" "int cmds" "uid_t uid" "void *arg" "bool *mp_busy" .Sh DESCRIPTION Implement file system quotas. +.Pp +The +.Fa mp_busy +argument is an input/output parameter. +.Fn VFS_QUOTACTL +must be called with +.Fa mp +marked busy through +.Xr vfs_busy 9 +and +.Fa *mp_busy +set to true. +The filesystem implementation of +.Fn VFS_QUOTACTL +may then unbusy +.Fa mp +using +.Xr vfs_unbusy 9 +prior to performing quota file I/O. +In this case the implementation must set +.Fa *mp_busy +to false to indicate that the caller must not unbusy +.Fa mp +upon completion of +.Fn VFS_QUOTACTL . +.Pp See .Xr quotactl 2 -for a description of the arguments. +for a description of the remaining arguments. .Sh SEE ALSO .Xr quotactl 2 , .Xr vnode 9 Index: sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c =================================================================== --- sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c +++ sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c @@ -102,7 +102,12 @@ "ZPL_VERSION"); /* END CSTYLED */ +#if __FreeBSD_version >= 1400014 +static int zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg, + bool *mp_busy); +#else static int zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg); +#endif static int zfs_mount(vfs_t *vfsp); static int zfs_umount(vfs_t *vfsp, int fflag); static int zfs_root(vfs_t *vfsp, int flags, vnode_t **vpp); @@ -267,7 +272,11 @@ } static int +#if __FreeBSD_version >= 1400014 +zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg, bool *mp_busy) +#else zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg) +#endif { zfsvfs_t *zfsvfs = vfsp->vfs_data; struct thread *td; @@ -291,8 +300,10 @@ break; default: error = EINVAL; +#if __FreeBSD_version < 1400014 if (cmd == Q_QUOTAON || cmd == Q_QUOTAOFF) vfs_unbusy(vfsp); +#endif goto done; } } @@ -351,11 +362,15 @@ case Q_QUOTAON: // As far as I can tell, you can't turn quotas on or off on zfs error = 0; +#if __FreeBSD_version < 1400014 vfs_unbusy(vfsp); +#endif break; case Q_QUOTAOFF: error = ENOTSUP; +#if __FreeBSD_version < 1400014 vfs_unbusy(vfsp); +#endif break; case Q_SETQUOTA: error = copyin(arg, &dqblk, sizeof (dqblk)); Index: sys/fs/nullfs/null_vfsops.c =================================================================== --- sys/fs/nullfs/null_vfsops.c +++ sys/fs/nullfs/null_vfsops.c @@ -294,13 +294,30 @@ } static int -nullfs_quotactl(mp, cmd, uid, arg) +nullfs_quotactl(mp, cmd, uid, arg, mp_busy) struct mount *mp; int cmd; uid_t uid; void *arg; + bool *mp_busy; { - return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg); + struct null_mount *mntdata; + int error; + bool unbusy; + + mntdata = MOUNTTONULLMOUNT(mp); + unbusy = true; + + error = vfs_busy(mntdata->nullm_vfs, MBF_NOWAIT); + if (error != 0) + return (error); + + error = VFS_QUOTACTL(mntdata->nullm_vfs, cmd, uid, arg, &unbusy); + + if (unbusy) + vfs_unbusy(mntdata->nullm_vfs); + + return (error); } static int Index: sys/fs/smbfs/smbfs_vfsops.c =================================================================== --- sys/fs/smbfs/smbfs_vfsops.c +++ sys/fs/smbfs/smbfs_vfsops.c @@ -352,11 +352,12 @@ */ /* ARGSUSED */ static int -smbfs_quotactl(mp, cmd, uid, arg) +smbfs_quotactl(mp, cmd, uid, arg, mp_busy) struct mount *mp; int cmd; uid_t uid; void *arg; + bool *mp_busy; { SMBVDEBUG("return EOPNOTSUPP\n"); return EOPNOTSUPP; Index: sys/fs/unionfs/union_vfsops.c =================================================================== --- sys/fs/unionfs/union_vfsops.c +++ sys/fs/unionfs/union_vfsops.c @@ -371,16 +371,30 @@ } static int -unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg) +unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, + bool *mp_busy) { struct unionfs_mount *ump; + int error; + bool unbusy; + unbusy = true; ump = MOUNTTOUNIONFSMOUNT(mp); + error = vfs_busy(ump->um_uppervp->v_mount, MBF_NOWAIT); + if (error != 0) + return (error); + /* * Writing is always performed to upper vnode. */ - return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg)); + error = VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg, + &unbusy); + + if (unbusy) + vfs_unbusy(ump->um_uppervp->v_mount); + + return (error); } static int Index: sys/kern/vfs_default.c =================================================================== --- sys/kern/vfs_default.c +++ sys/kern/vfs_default.c @@ -1322,13 +1322,13 @@ } int -vfs_stdquotactl (mp, cmds, uid, arg) +vfs_stdquotactl (mp, cmds, uid, arg, mp_busy) struct mount *mp; int cmds; uid_t uid; void *arg; + bool *mp_busy; { - return (EOPNOTSUPP); } Index: sys/kern/vfs_init.c =================================================================== --- sys/kern/vfs_init.c +++ sys/kern/vfs_init.c @@ -212,12 +212,14 @@ } static int -vfs_quotactl_sigdefer(struct mount *mp, int cmd, uid_t uid, void *arg) +vfs_quotactl_sigdefer(struct mount *mp, int cmd, uid_t uid, void *arg, + bool *mp_busy) { int prev_stops, rc; prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT); - rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_quotactl)(mp, cmd, uid, arg); + rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_quotactl)(mp, cmd, uid, arg, + mp_busy); sigallowstop(prev_stops); return (rc); } Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -89,8 +89,6 @@ #include -#include - MALLOC_DEFINE(M_FADVISE, "fadvise", "posix_fadvise(2) information"); static int kern_chflagsat(struct thread *td, int fd, const char *path, @@ -195,6 +193,7 @@ struct mount *mp; struct nameidata nd; int error; + bool mp_busy; AUDIT_ARG_CMD(uap->cmd); AUDIT_ARG_UID(uap->uid); @@ -213,21 +212,21 @@ vfs_rel(mp); return (error); } - error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg); + mp_busy = true; + error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, &mp_busy); /* - * Since quota on operation typically needs to open quota - * file, the Q_QUOTAON handler needs to unbusy the mount point + * Since quota on/off operations typically need to open quota + * files, the implementation may need to unbusy the mount point * before calling into namei. Otherwise, unmount might be - * started between two vfs_busy() invocations (first is our, + * started between two vfs_busy() invocations (first is ours, * second is from mount point cross-walk code in lookup()), * causing deadlock. * - * Require that Q_QUOTAON handles the vfs_busy() reference on - * its own, always returning with ubusied mount point. + * Avoid unbusying mp if the implementation indicates it has + * already done so. */ - if ((uap->cmd >> SUBCMDSHIFT) != Q_QUOTAON && - (uap->cmd >> SUBCMDSHIFT) != Q_QUOTAOFF) + if (mp_busy) vfs_unbusy(mp); vfs_rel(mp); return (error); Index: sys/sys/mount.h =================================================================== --- sys/sys/mount.h +++ sys/sys/mount.h @@ -760,7 +760,8 @@ typedef int vfs_cmount_t(struct mntarg *ma, void *data, uint64_t flags); typedef int vfs_unmount_t(struct mount *mp, int mntflags); typedef int vfs_root_t(struct mount *mp, int flags, struct vnode **vpp); -typedef int vfs_quotactl_t(struct mount *mp, int cmds, uid_t uid, void *arg); +typedef int vfs_quotactl_t(struct mount *mp, int cmds, uid_t uid, void *arg, + bool *mp_busy); typedef int vfs_statfs_t(struct mount *mp, struct statfs *sbp); typedef int vfs_sync_t(struct mount *mp, int waitfor); typedef int vfs_vget_t(struct mount *mp, ino_t ino, int flags, @@ -833,10 +834,10 @@ _rc = (*(MP)->mnt_op->vfs_cachedroot)(MP, FLAGS, VPP); \ _rc; }) -#define VFS_QUOTACTL(MP, C, U, A) ({ \ +#define VFS_QUOTACTL(MP, C, U, A, MP_BUSY) ({ \ int _rc; \ \ - _rc = (*(MP)->mnt_op->vfs_quotactl)(MP, C, U, A); \ + _rc = (*(MP)->mnt_op->vfs_quotactl)(MP, C, U, A, MP_BUSY); \ _rc; }) #define VFS_STATFS(MP, SBP) ({ \ Index: sys/sys/param.h =================================================================== --- sys/sys/param.h +++ sys/sys/param.h @@ -76,7 +76,7 @@ * cannot include sys/param.h and should only be updated here. */ #undef __FreeBSD_version -#define __FreeBSD_version 1400013 +#define __FreeBSD_version 1400014 /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, Index: sys/ufs/ufs/quota.h =================================================================== --- sys/ufs/ufs/quota.h +++ sys/ufs/ufs/quota.h @@ -232,7 +232,7 @@ int qsync(struct mount *); int qsyncvp(struct vnode *); int quotaoff(struct thread *, struct mount *, int); -int quotaon(struct thread *, struct mount *, int, void *); +int quotaon(struct thread *, struct mount *, int, void *, bool *); int getquota32(struct thread *, struct mount *, u_long, int, void *); int setquota32(struct thread *, struct mount *, u_long, int, void *); int setuse32(struct thread *, struct mount *, u_long, int, void *); Index: sys/ufs/ufs/ufs_quota.c =================================================================== --- sys/ufs/ufs/ufs_quota.c +++ sys/ufs/ufs/ufs_quota.c @@ -492,7 +492,8 @@ * Q_QUOTAON - set up a quota file for a particular filesystem. */ int -quotaon(struct thread *td, struct mount *mp, int type, void *fname) +quotaon(struct thread *td, struct mount *mp, int type, void *fname, + bool *mp_busy) { struct ufsmount *ump; struct vnode *vp, **vpp; @@ -502,15 +503,11 @@ struct nameidata nd; error = priv_check(td, PRIV_UFS_QUOTAON); - if (error != 0) { - vfs_unbusy(mp); + if (error != 0) return (error); - } - if ((mp->mnt_flag & MNT_RDONLY) != 0) { - vfs_unbusy(mp); + if ((mp->mnt_flag & MNT_RDONLY) != 0) return (EROFS); - } ump = VFSTOUFS(mp); dq = NODQUOT; @@ -518,7 +515,9 @@ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td); flags = FREAD | FWRITE; vfs_ref(mp); + KASSERT(*mp_busy, ("%s called without busied mount", __func__)); vfs_unbusy(mp); + *mp_busy = false; error = vn_open(&nd, &flags, 0, NULL); if (error != 0) { vfs_rel(mp); @@ -529,10 +528,9 @@ error = vfs_busy(mp, MBF_NOWAIT); vfs_rel(mp); if (error == 0) { - if (vp->v_type != VREG) { + *mp_busy = true; + if (vp->v_type != VREG) error = EACCES; - vfs_unbusy(mp); - } } if (error != 0) { VOP_UNLOCK(vp); @@ -545,7 +543,6 @@ UFS_UNLOCK(ump); VOP_UNLOCK(vp); (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); - vfs_unbusy(mp); return (EALREADY); } ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING; @@ -556,7 +553,6 @@ ump->um_qflags[type] &= ~(QTF_OPENING|QTF_CLOSING); UFS_UNLOCK(ump); (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); - vfs_unbusy(mp); return (error); } VOP_UNLOCK(vp); @@ -640,7 +636,6 @@ ("quotaon: leaking flags")); UFS_UNLOCK(ump); - vfs_unbusy(mp); return (error); } Index: sys/ufs/ufs/ufs_vfsops.c =================================================================== --- sys/ufs/ufs/ufs_vfsops.c +++ sys/ufs/ufs/ufs_vfsops.c @@ -87,17 +87,14 @@ * Do operations associated with quotas */ int -ufs_quotactl(mp, cmds, id, arg) +ufs_quotactl(mp, cmds, id, arg, mp_busy) struct mount *mp; int cmds; uid_t id; void *arg; + bool *mp_busy; { #ifndef QUOTA - if ((cmds >> SUBCMDSHIFT) == Q_QUOTAON || - (cmds >> SUBCMDSHIFT) == Q_QUOTAOFF) - vfs_unbusy(mp); - return (EOPNOTSUPP); #else struct thread *td; @@ -117,25 +114,23 @@ break; default: - if (cmd == Q_QUOTAON || cmd == Q_QUOTAOFF) - vfs_unbusy(mp); return (EINVAL); } } - if ((u_int)type >= MAXQUOTAS) { - if (cmd == Q_QUOTAON || cmd == Q_QUOTAOFF) - vfs_unbusy(mp); + if ((u_int)type >= MAXQUOTAS) return (EINVAL); - } switch (cmd) { case Q_QUOTAON: - error = quotaon(td, mp, type, arg); + error = quotaon(td, mp, type, arg, mp_busy); break; case Q_QUOTAOFF: vfs_ref(mp); + KASSERT(*mp_busy, + ("%s called without busied mount", __func__)); vfs_unbusy(mp); + *mp_busy = false; vn_start_write(NULL, &mp, V_WAIT | V_MNTREF); error = quotaoff(td, mp, type); vn_finished_write(mp);