aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJason A. Harmening <jah@FreeBSD.org>2021-05-11 15:54:58 +0000
committerJason A. Harmening <jah@FreeBSD.org>2021-05-29 21:05:39 +0000
commit6d3e78ad6c11849ad3d36feeb10c412c93b56073 (patch)
treea9449581c777f18a55907e1dcd5cdc33e3c293b7 /sys
parent811e645d286d0b97c02d1b66a59a76c327d46c35 (diff)
downloadsrc-6d3e78ad6c11849ad3d36feeb10c412c93b56073.tar.gz
src-6d3e78ad6c11849ad3d36feeb10c412c93b56073.zip
VFS_QUOTACTL(9): allow implementation to indicate busy state changes
Instead of requiring all implementations of vfs_quotactl to unbusy the mount for Q_QUOTAON and Q_QUOTAOFF, add an "mp_busy" in/out param to VFS_QUOTACTL(9). The implementation may then indicate to the caller whether it needed to unbusy the mount. Reviewed By: kib, markj Differential Revision: https://reviews.freebsd.org/D30218
Diffstat (limited to 'sys')
-rw-r--r--sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c15
-rw-r--r--sys/fs/nullfs/null_vfsops.c30
-rw-r--r--sys/fs/smbfs/smbfs_vfsops.c3
-rw-r--r--sys/fs/unionfs/union_vfsops.c28
-rw-r--r--sys/kern/vfs_default.c4
-rw-r--r--sys/kern/vfs_init.c6
-rw-r--r--sys/kern/vfs_syscalls.c19
-rw-r--r--sys/sys/mount.h7
-rw-r--r--sys/sys/param.h2
-rw-r--r--sys/ufs/ufs/quota.h2
-rw-r--r--sys/ufs/ufs/ufs_quota.c21
-rw-r--r--sys/ufs/ufs/ufs_vfsops.c19
12 files changed, 106 insertions, 50 deletions
diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c
index a537342f9678..4f2d7df87fc0 100644
--- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c
+++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c
@@ -102,7 +102,12 @@ SYSCTL_INT(_vfs_zfs_version, OID_AUTO, zpl, CTLFLAG_RD, &zfs_version_zpl, 0,
"ZPL_VERSION");
/* END CSTYLED */
+#if __FreeBSD_version >= 1400018
+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 @@ done:
}
static int
+#if __FreeBSD_version >= 1400018
+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 @@ zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg)
break;
default:
error = EINVAL;
+#if __FreeBSD_version < 1400018
if (cmd == Q_QUOTAON || cmd == Q_QUOTAOFF)
vfs_unbusy(vfsp);
+#endif
goto done;
}
}
@@ -351,11 +362,15 @@ zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg)
case Q_QUOTAON:
// As far as I can tell, you can't turn quotas on or off on zfs
error = 0;
+#if __FreeBSD_version < 1400018
vfs_unbusy(vfsp);
+#endif
break;
case Q_QUOTAOFF:
error = ENOTSUP;
+#if __FreeBSD_version < 1400018
vfs_unbusy(vfsp);
+#endif
break;
case Q_SETQUOTA:
error = copyin(arg, &dqblk, sizeof (dqblk));
diff --git a/sys/fs/nullfs/null_vfsops.c b/sys/fs/nullfs/null_vfsops.c
index 0bb98072edf4..0ad2385116a9 100644
--- a/sys/fs/nullfs/null_vfsops.c
+++ b/sys/fs/nullfs/null_vfsops.c
@@ -294,13 +294,39 @@ nullfs_root(mp, flags, vpp)
}
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 mount *lowermp;
+ struct null_mount *mntdata;
+ int error;
+ bool unbusy;
+
+ mntdata = MOUNTTONULLMOUNT(mp);
+ lowermp = atomic_load_ptr(&mntdata->nullm_vfs);
+ KASSERT(*mp_busy == true, ("upper mount not busy"));
+ /*
+ * See comment in sys_quotactl() for an explanation of why the
+ * lower mount needs to be busied by the caller of VFS_QUOTACTL()
+ * but may be unbusied by the implementation. We must unbusy
+ * the upper mount for the same reason; otherwise a namei lookup
+ * issued by the VFS_QUOTACTL() implementation could traverse the
+ * upper mount and deadlock.
+ */
+ vfs_unbusy(mp);
+ *mp_busy = false;
+ unbusy = true;
+ error = vfs_busy(lowermp, 0);
+ if (error == 0)
+ error = VFS_QUOTACTL(lowermp, cmd, uid, arg, &unbusy);
+ if (unbusy)
+ vfs_unbusy(lowermp);
+
+ return (error);
}
static int
diff --git a/sys/fs/smbfs/smbfs_vfsops.c b/sys/fs/smbfs/smbfs_vfsops.c
index d19816a7869c..a1ae565c6341 100644
--- a/sys/fs/smbfs/smbfs_vfsops.c
+++ b/sys/fs/smbfs/smbfs_vfsops.c
@@ -352,11 +352,12 @@ out:
*/
/* 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;
diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c
index ae95bd9c005c..bd264c7bcdb5 100644
--- a/sys/fs/unionfs/union_vfsops.c
+++ b/sys/fs/unionfs/union_vfsops.c
@@ -371,16 +371,38 @@ unionfs_root(struct mount *mp, int flags, struct vnode **vpp)
}
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 mount *uppermp;
struct unionfs_mount *ump;
+ int error;
+ bool unbusy;
ump = MOUNTTOUNIONFSMOUNT(mp);
-
+ uppermp = atomic_load_ptr(&ump->um_uppervp->v_mount);
+ KASSERT(*mp_busy == true, ("upper mount not busy"));
+ /*
+ * See comment in sys_quotactl() for an explanation of why the
+ * lower mount needs to be busied by the caller of VFS_QUOTACTL()
+ * but may be unbusied by the implementation. We must unbusy
+ * the upper mount for the same reason; otherwise a namei lookup
+ * issued by the VFS_QUOTACTL() implementation could traverse the
+ * upper mount and deadlock.
+ */
+ vfs_unbusy(mp);
+ *mp_busy = false;
+ unbusy = true;
+ error = vfs_busy(uppermp, 0);
/*
* Writing is always performed to upper vnode.
*/
- return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg));
+ if (error == 0)
+ error = VFS_QUOTACTL(uppermp, cmd, uid, arg, &unbusy);
+ if (unbusy)
+ vfs_unbusy(uppermp);
+
+ return (error);
}
static int
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c
index ace9ad1d37c3..63bca7810847 100644
--- a/sys/kern/vfs_default.c
+++ b/sys/kern/vfs_default.c
@@ -1350,13 +1350,13 @@ vfs_stdstatfs (mp, sbp)
}
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);
}
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c
index 3365ddb11474..112b4c76e575 100644
--- a/sys/kern/vfs_init.c
+++ b/sys/kern/vfs_init.c
@@ -212,12 +212,14 @@ vfs_cachedroot_sigdefer(struct mount *mp, int flags, struct vnode **vpp)
}
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);
}
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 55780b0474ee..2f4a6036ef88 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -89,8 +89,6 @@ __FBSDID("$FreeBSD$");
#include <fs/devfs/devfs.h>
-#include <ufs/ufs/quota.h>
-
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 @@ sys_quotactl(struct thread *td, struct quotactl_args *uap)
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 @@ sys_quotactl(struct thread *td, struct quotactl_args *uap)
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);
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index a1d4bfd15ddb..684d8c3eb780 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -759,7 +759,8 @@ struct mntarg;
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,
@@ -832,10 +833,10 @@ vfs_statfs_t __vfs_statfs;
_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) ({ \
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 959f0b94ca70..c63452973daf 100644
--- a/sys/sys/param.h
+++ b/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 1400017
+#define __FreeBSD_version 1400018
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
diff --git a/sys/ufs/ufs/quota.h b/sys/ufs/ufs/quota.h
index e154f8234705..eb3db9c300d0 100644
--- a/sys/ufs/ufs/quota.h
+++ b/sys/ufs/ufs/quota.h
@@ -232,7 +232,7 @@ int getinoquota(struct inode *);
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 *);
diff --git a/sys/ufs/ufs/ufs_quota.c b/sys/ufs/ufs/ufs_quota.c
index 4dff74f75945..143e0afbf1e3 100644
--- a/sys/ufs/ufs/ufs_quota.c
+++ b/sys/ufs/ufs/ufs_quota.c
@@ -492,7 +492,8 @@ chkdquot(struct inode *ip)
* 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 @@ quotaon(struct thread *td, struct mount *mp, int type, void *fname)
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 @@ quotaon(struct thread *td, struct mount *mp, int type, void *fname)
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 @@ quotaon(struct thread *td, struct mount *mp, int type, void *fname)
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 @@ quotaon(struct thread *td, struct mount *mp, int type, void *fname)
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 @@ quotaon(struct thread *td, struct mount *mp, int type, void *fname)
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 @@ again:
("quotaon: leaking flags"));
UFS_UNLOCK(ump);
- vfs_unbusy(mp);
return (error);
}
diff --git a/sys/ufs/ufs/ufs_vfsops.c b/sys/ufs/ufs/ufs_vfsops.c
index 0f45baed634f..33ef7bc2c3d1 100644
--- a/sys/ufs/ufs/ufs_vfsops.c
+++ b/sys/ufs/ufs/ufs_vfsops.c
@@ -87,17 +87,14 @@ ufs_root(mp, flags, vpp)
* 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 @@ ufs_quotactl(mp, cmds, id, arg)
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);