* finding or making overcommit--main--1.0--patch-51 * finding or making quotas--patch--1.0--patch-29 * auto-adding kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-29 to greedy revision library /usr/home/kostik/tla-libraries * found immediate ancestor revision in library (kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-28) * patching for this revision (kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-29) * computing changeset A/ {arch}/quotas A/ {arch}/quotas/quotas--patch A/ {arch}/quotas/quotas--patch/quotas--patch--1.0 A/ {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd A/ {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/base-0 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-1 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-10 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-11 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-12 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-13 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-14 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-15 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-16 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-17 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-18 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-19 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-2 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-20 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-21 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-22 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-23 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-24 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-25 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-26 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-27 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-28 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-29 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-3 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-4 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-5 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-6 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-7 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-8 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-9 M sbin/mount/mount.c M sys/kern/vfs_syscalls.c M sys/ufs/ffs/ffs_vfsops.c M sys/ufs/ufs/quota.h M sys/ufs/ufs/ufs_quota.c M sys/ufs/ufs/ufs_vnops.c * changeset report * added directories {arch}/quotas {arch}/quotas/quotas--patch {arch}/quotas/quotas--patch/quotas--patch--1.0 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log * added files {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/base-0 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-1 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-10 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-11 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-12 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-13 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-14 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-15 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-16 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-17 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-18 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-19 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-2 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-20 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-21 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-22 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-23 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-24 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-25 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-26 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-27 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-28 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-29 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-3 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-4 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-5 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-6 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-7 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-8 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-9 * modified files --- orig/sbin/mount/mount.c +++ mod/sbin/mount/mount.c @@ -120,6 +120,9 @@ 0 }; +static const char userquotaeq[] = "userquota="; +static const char groupquotaeq[] = "groupquota="; + static int use_mountprog(const char *vfstype) { @@ -634,8 +637,12 @@ continue; } else if (strcmp(p, "userquota") == 0) { continue; + } else if (strncmp(p, userquotaeq, sizeof(userquotaeq) - 1) == 0) { + continue; } else if (strcmp(p, "groupquota") == 0) { continue; + } else if (strncmp(p, groupquotaeq, sizeof(groupquotaeq) - 1) == 0) { + continue; } else if (*p == '-') { argv[argc++] = p; p = strchr(p, '='); --- orig/sys/kern/vfs_syscalls.c +++ mod/sys/kern/vfs_syscalls.c @@ -178,7 +178,7 @@ caddr_t arg; } */ *uap; { - struct mount *mp, *vmp; + struct mount *mp; int vfslocked; int error; struct nameidata nd; @@ -191,14 +191,9 @@ return (error); vfslocked = NDHASGIANT(&nd); NDFREE(&nd, NDF_ONLY_PNBUF); - error = vn_start_write(nd.ni_vp, &vmp, V_WAIT | PCATCH); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); - if (error) - goto out; error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, td); - vn_finished_write(vmp); -out: VFS_UNLOCK_GIANT(vfslocked); return (error); } --- orig/sys/ufs/ffs/ffs_vfsops.c +++ mod/sys/ufs/ffs/ffs_vfsops.c @@ -831,15 +831,7 @@ (void) ufs_extattr_autostart(mp, td); #endif /* !UFS_EXTATTR_AUTOSTART */ #endif /* !UFS_EXTATTR */ -#ifdef QUOTA - /* - * Our bufobj must require giant for snapshots when quotas are - * enabled. - */ - devvp->v_bufobj.bo_flag |= BO_NEEDSGIANT; -#else mp->mnt_kern_flag |= MNTK_MPSAFE; -#endif return (0); out: if (bp) @@ -1040,8 +1032,6 @@ if (error) return (error); for (i = 0; i < MAXQUOTAS; i++) { - if (ump->um_quotas[i] == NULLVP) - continue; quotaoff(td, mp, i); } /* --- orig/sys/ufs/ufs/quota.h +++ mod/sys/ufs/ufs/quota.h @@ -117,6 +117,7 @@ struct dquot { LIST_ENTRY(dquot) dq_hash; /* hash list */ TAILQ_ENTRY(dquot) dq_freelist; /* free list */ + struct mtx dq_lock; /* lock for concurrency */ u_int16_t dq_flags; /* flags, see below */ u_int16_t dq_type; /* quota type of this dquot */ u_int32_t dq_cnt; /* count of active references */ --- orig/sys/ufs/ufs/ufs_quota.c +++ mod/sys/ufs/ufs/ufs_quota.c @@ -75,6 +75,7 @@ u_long, struct ufsmount *, int, struct dquot **); static int dqsync(struct vnode *, struct dquot *); static void dqflush(struct vnode *); +static int quotaoff1(struct thread *td, struct mount *mp, int type); #ifdef DIAGNOSTIC static void dqref(struct dquot *); @@ -102,8 +103,7 @@ * Set up the user quota based on file uid. * EINVAL means that quotas are not enabled. */ - if (ip->i_dquot[USRQUOTA] == NODQUOT && - (error = + if ((error = dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && error != EINVAL) return (error); @@ -111,8 +111,7 @@ * Set up the group quota based on file gid. * EINVAL means that quotas are not enabled. */ - if (ip->i_dquot[GRPQUOTA] == NODQUOT && - (error = + if ((error = dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && error != EINVAL) return (error); @@ -143,9 +142,10 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkdq1", 0); + (void) msleep(dq, &(dq->dq_lock), PINOD+1, "chkdq1", 0); } ncurblocks = dq->dq_curblocks + change; if (ncurblocks >= 0) @@ -154,6 +154,7 @@ dq->dq_curblocks = 0; dq->dq_flags &= ~DQ_BLKS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); } return (0); } @@ -169,9 +170,10 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkdq2", 0); + (void) msleep(dq, &(dq->dq_lock), PINOD+1, "chkdq2", 0); } /* Reset timer when crossing soft limit */ if (dq->dq_curblocks + change >= dq->dq_bsoftlimit && @@ -180,6 +182,7 @@ VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i]; dq->dq_curblocks += change; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); } return (0); } @@ -197,6 +200,9 @@ { struct dquot *dq = ip->i_dquot[type]; ufs2_daddr_t ncurblocks = dq->dq_curblocks + change; + int unlocked = 0; + + mtx_lock(&(dq->dq_lock)); /* * If user would exceed their hard limit, disallow space allocation. @@ -204,11 +210,15 @@ if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { if ((dq->dq_flags & DQ_BLKS) == 0 && ip->i_uid == cred->cr_uid) { + dq->dq_flags |= DQ_BLKS; + mtx_unlock(&(dq->dq_lock)); + unlocked = 1; uprintf("\n%s: write failed, %s disk limit reached\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type]); - dq->dq_flags |= DQ_BLKS; } + if (!unlocked) + mtx_unlock(&(dq->dq_lock)); return (EDQUOT); } /* @@ -219,6 +229,7 @@ if (dq->dq_curblocks < dq->dq_bsoftlimit) { dq->dq_btime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type]; + mtx_unlock(&(dq->dq_lock)); if (ip->i_uid == cred->cr_uid) uprintf("\n%s: warning, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, @@ -228,15 +239,20 @@ if (time_second > dq->dq_btime) { if ((dq->dq_flags & DQ_BLKS) == 0 && ip->i_uid == cred->cr_uid) { + dq->dq_flags |= DQ_BLKS; + mtx_unlock(&(dq->dq_lock)); + unlocked = 1; uprintf("\n%s: write failed, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "disk quota exceeded for too long"); - dq->dq_flags |= DQ_BLKS; } + if (!unlocked) + mtx_unlock(&(dq->dq_lock)); return (EDQUOT); } } + mtx_unlock(&(dq->dq_lock)); return (0); } @@ -265,9 +281,10 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkiq1", 0); + (void) msleep(dq, &(dq->dq_lock), PINOD+1, "chkiq1", 0); } ncurinodes = dq->dq_curinodes + change; /* XXX: ncurinodes is unsigned */ @@ -277,6 +294,7 @@ dq->dq_curinodes = 0; dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); } return (0); } @@ -292,9 +310,10 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkiq2", 0); + (void) msleep(dq, &(dq->dq_lock), PINOD+1, "chkiq2", 0); } /* Reset timer when crossing soft limit */ if (dq->dq_curinodes + change >= dq->dq_isoftlimit && @@ -303,6 +322,7 @@ VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i]; dq->dq_curinodes += change; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); } return (0); } @@ -320,18 +340,24 @@ { struct dquot *dq = ip->i_dquot[type]; ino_t ncurinodes = dq->dq_curinodes + change; + int unlocked = 0; + mtx_lock(&(dq->dq_lock)); /* * If user would exceed their hard limit, disallow inode allocation. */ if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { if ((dq->dq_flags & DQ_INODS) == 0 && ip->i_uid == cred->cr_uid) { + dq->dq_flags |= DQ_INODS; + mtx_unlock(&(dq->dq_lock)); + unlocked = 1; uprintf("\n%s: write failed, %s inode limit reached\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type]); - dq->dq_flags |= DQ_INODS; } + if (!unlocked) + mtx_unlock(&(dq->dq_lock)); return (EDQUOT); } /* @@ -342,6 +368,7 @@ if (dq->dq_curinodes < dq->dq_isoftlimit) { dq->dq_itime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type]; + mtx_unlock(&(dq->dq_lock)); if (ip->i_uid == cred->cr_uid) uprintf("\n%s: warning, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, @@ -351,15 +378,20 @@ if (time_second > dq->dq_itime) { if ((dq->dq_flags & DQ_INODS) == 0 && ip->i_uid == cred->cr_uid) { + dq->dq_flags |= DQ_INODS; + mtx_unlock(&(dq->dq_lock)); + unlocked = 1; uprintf("\n%s: write failed, %s %s\n", ITOV(ip)->v_mount->mnt_stat.f_mntonname, quotatypes[type], "inode quota exceeded for too long"); - dq->dq_flags |= DQ_INODS; } + if (!unlocked) + mtx_unlock(&(dq->dq_lock)); return (EDQUOT); } } + mtx_unlock(&(dq->dq_lock)); return (0); } @@ -375,15 +407,18 @@ struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount); int i; + mtx_lock(&(ump->um_lock)); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP || (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) continue; if (ip->i_dquot[i] == NODQUOT) { + mtx_unlock(&(ump->um_lock)); vprint("chkdquot: missing dquot", ITOV(ip)); panic("chkdquot: missing dquot"); } } + mtx_unlock(&(ump->um_lock)); } #endif @@ -404,7 +439,7 @@ struct ufsmount *ump = VFSTOUFS(mp); struct vnode *vp, **vpp; struct vnode *mvp; - struct dquot *dq; + struct dquot *dq = NODQUOT; int error, flags; struct nameidata nd; @@ -412,7 +447,6 @@ if (error) return (error); - vpp = &ump->um_quotas[type]; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td); flags = FREAD | FWRITE; error = vn_open(&nd, &flags, 0, -1); @@ -425,10 +459,21 @@ (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); return (EACCES); } - if (*vpp != vp) - quotaoff(td, mp, type); + + mtx_lock(&(ump->um_lock)); + if ((ump->um_qflags[type] & (QTF_OPENING | QTF_CLOSING)) != 0) { + mtx_unlock(&(ump->um_lock)); + (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); + return (EALREADY); + } ump->um_qflags[type] |= QTF_OPENING; mp->mnt_flag |= MNT_QUOTA; + mtx_unlock(&(ump->um_lock)); + + vpp = &ump->um_quotas[type]; + if (*vpp != vp) + quotaoff1(td, mp, type); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); vp->v_vflag |= VV_SYSTEM; VOP_UNLOCK(vp, 0, td); @@ -478,17 +523,18 @@ } } MNT_IUNLOCK(mp); + + mtx_lock(&(ump->um_lock)); ump->um_qflags[type] &= ~QTF_OPENING; + mtx_unlock(&(ump->um_lock)); + if (error) quotaoff(td, mp, type); return (error); } -/* - * Q_QUOTAOFF - turn off disk quotas for a filesystem. - */ -int -quotaoff(td, mp, type) +static int +quotaoff1(td, mp, type) struct thread *td; struct mount *mp; int type; @@ -498,15 +544,19 @@ struct ufsmount *ump = VFSTOUFS(mp); struct dquot *dq; struct inode *ip; + struct ucred *cr; int error; - error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL); - if (error) - return (error); - - if ((qvp = ump->um_quotas[type]) == NULLVP) + mtx_lock(&(ump->um_lock)); + KASSERT((ump->um_qflags[type] & (QTF_OPENING | QTF_CLOSING)) != 0, + ("quotaoff1: flags are invalid")); + if ((qvp = ump->um_quotas[type]) == NULLVP) { + mtx_unlock(&(ump->um_lock)); return (0); - ump->um_qflags[type] |= QTF_CLOSING; + } + cr = ump->um_cred[type]; + mtx_unlock(&(ump->um_lock)); + /* * Search vnodes associated with this mount point, * deleting any references to quota file being closed. @@ -535,20 +585,61 @@ MNT_ILOCK(mp); } MNT_IUNLOCK(mp); + + /* Clear um_quotas before closing the quota vnode to prevent + * access to the closed vnode from dqget/dqsync + */ + mtx_lock(&(ump->um_lock)); + ump->um_quotas[type] = NULLVP; + ump->um_cred[type] = NOCRED; + mtx_unlock(&(ump->um_lock)); + dqflush(qvp); vn_lock(qvp, LK_EXCLUSIVE | LK_RETRY, td); qvp->v_vflag &= ~VV_SYSTEM; VOP_UNLOCK(qvp, 0, td); error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td); - ump->um_quotas[type] = NULLVP; - crfree(ump->um_cred[type]); - ump->um_cred[type] = NOCRED; + crfree(cr); + + return (error); +} + +/* + * Q_QUOTAOFF - turn off disk quotas for a filesystem. + */ +int +quotaoff(td, mp, type) + struct thread *td; + struct mount *mp; + int type; +{ + struct ufsmount *ump = VFSTOUFS(mp); + int i; + int error; + + error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL); + if (error) + return (error); + + mtx_lock(&(ump->um_lock)); + if ((ump->um_qflags[type] & (QTF_OPENING | QTF_CLOSING)) != 0) { + mtx_unlock(&(ump->um_lock)); + return (EALREADY); + } + ump->um_qflags[type] |= QTF_CLOSING; + mtx_unlock(&(ump->um_lock)); + + error = quotaoff1(td, mp, type); + + mtx_lock(&(ump->um_lock)); ump->um_qflags[type] &= ~QTF_CLOSING; - for (type = 0; type < MAXQUOTAS; type++) - if (ump->um_quotas[type] != NULLVP) + for (i = 0; i < MAXQUOTAS; i++) + if (ump->um_quotas[i] != NULLVP) break; - if (type == MAXQUOTAS) + if (i == MAXQUOTAS) mp->mnt_flag &= ~MNT_QUOTA; + mtx_unlock(&(ump->um_lock)); + return (error); } @@ -563,7 +654,7 @@ int type; void *addr; { - struct dquot *dq; + struct dquot *dq = NODQUOT; int error; switch (type) { @@ -607,7 +698,7 @@ void *addr; { struct dquot *dq; - struct dquot *ndq; + struct dquot *ndq = NODQUOT; struct ufsmount *ump = VFSTOUFS(mp); struct dqblk newlim; int error; @@ -623,9 +714,10 @@ if (error) return (error); dq = ndq; + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "setqta", 0); + (void) msleep(dq, &(dq->dq_lock), PINOD+1, "setqta", 0); } /* * Copy all but the current values. @@ -657,6 +749,7 @@ else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); dqrele(NULLVP, dq); return (0); } @@ -674,7 +767,7 @@ { struct dquot *dq; struct ufsmount *ump = VFSTOUFS(mp); - struct dquot *ndq; + struct dquot *ndq = NODQUOT; struct dqblk usage; int error; @@ -689,9 +782,10 @@ if (error) return (error); dq = ndq; + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "setuse", 0); + (void) msleep(dq, &(dq->dq_lock), PINOD+1, "setuse", 0); } /* * Reset time limit if have a soft limit and were @@ -710,6 +804,7 @@ if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); dqrele(NULLVP, dq); return (0); } @@ -731,9 +826,11 @@ * Check if the mount point has any quotas. * If not, simply return. */ + mtx_lock(&(ump->um_lock)); for (i = 0; i < MAXQUOTAS; i++) if (ump->um_quotas[i] != NULLVP) break; + mtx_unlock(&(ump->um_lock)); if (i == MAXQUOTAS) return (0); /* @@ -761,7 +858,7 @@ } for (i = 0; i < MAXQUOTAS; i++) { dq = VTOI(vp)->i_dquot[i]; - if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) + if (dq != NODQUOT) dqsync(vp, dq); } vput(vp); @@ -786,6 +883,12 @@ static TAILQ_HEAD(dqfreelist, dquot) dqfreelist; static long numdquot, desireddquot = DQUOTINC; +/* + * Lock to protect quota hash, dq free list and dq_cnt ref counters of + * _all_ dqs. + */ +struct mtx dqhlock; + /* * Initialize the quota system. */ @@ -793,6 +896,7 @@ dqinit() { + mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF); dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); TAILQ_INIT(&dqfreelist); } @@ -808,8 +912,10 @@ hashdestroy(dqhashtbl, M_DQUOT, dqhash); while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) { TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); + mtx_destroy(&(dq->dq_lock)); free(dq, M_DQUOT); } + mtx_destroy(&dqhlock); } /* @@ -832,14 +938,27 @@ struct uio auio; int error; +#ifdef DEBUG_VFS_LOCKS + if (vp != NULLVP) + ASSERT_VOP_ELOCKED(vp, "dqget"); +#endif + + if (vp != NULLVP && *dqp != NODQUOT) { + return (0); + } + + mtx_lock(&(ump->um_lock)); dqvp = ump->um_quotas[type]; if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { *dqp = NODQUOT; + mtx_unlock(&(ump->um_lock)); return (EINVAL); } + mtx_unlock(&(ump->um_lock)); /* * Check the cache first. */ + mtx_lock(&dqhlock); dqh = DQHASH(dqvp, id); LIST_FOREACH(dq, dqh, dq_hash) { if (dq->dq_id != id || @@ -852,6 +971,7 @@ if (dq->dq_cnt == 0) TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); DQREF(dq); + mtx_unlock(&dqhlock); *dqp = dq; return (0); } @@ -862,11 +982,14 @@ numdquot < MAXQUOTAS * desiredvnodes) desireddquot += DQUOTINC; if (numdquot < desireddquot) { + numdquot++; + mtx_unlock(&dqhlock); dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK | M_ZERO); - numdquot++; + mtx_init(&(dq->dq_lock), "dqlock", NULL, MTX_DEF); } else { if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) { + mtx_unlock(&dqhlock); tablefull("dquot"); *dqp = NODQUOT; return (EUSERS); @@ -876,18 +999,22 @@ TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); if (dq->dq_ump != NULL) LIST_REMOVE(dq, dq_hash); + mtx_unlock(&dqhlock); } /* * Initialize the contents of the dquot structure. */ if (vp != dqvp) vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); - LIST_INSERT_HEAD(dqh, dq, dq_hash); - DQREF(dq); dq->dq_flags = DQ_LOCK; dq->dq_id = id; dq->dq_ump = ump; dq->dq_type = type; + mtx_lock(&dqhlock); + LIST_INSERT_HEAD(dqh, dq, dq_hash); + DQREF(dq); + mtx_unlock(&dqhlock); + auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = &dq->dq_dqb; @@ -902,6 +1029,7 @@ bzero(&dq->dq_dqb, sizeof(struct dqblk)); if (vp != dqvp) VOP_UNLOCK(dqvp, 0, td); + mtx_lock(&(dq->dq_lock)); if (dq->dq_flags & DQ_WANT) wakeup(dq); dq->dq_flags = 0; @@ -910,7 +1038,10 @@ * quota structure and reflect problem to caller. */ if (error) { + mtx_unlock(&(dq->dq_lock)); + mtx_lock(&dqhlock); LIST_REMOVE(dq, dq_hash); + mtx_unlock(&dqhlock); dqrele(vp, dq); *dqp = NODQUOT; return (error); @@ -928,6 +1059,7 @@ if (dq->dq_itime == 0) dq->dq_itime = time_second + ump->um_itime[type]; } + mtx_unlock(&(dq->dq_lock)); *dqp = dq; return (0); } @@ -956,15 +1088,24 @@ if (dq == NODQUOT) return; + mtx_lock(&dqhlock); if (dq->dq_cnt > 1) { dq->dq_cnt--; + mtx_unlock(&dqhlock); return; } - if (dq->dq_flags & DQ_MOD) - (void) dqsync(vp, dq); + mtx_unlock(&dqhlock); + + (void) dqsync(vp, dq); + + mtx_lock(&dqhlock); if (--dq->dq_cnt > 0) + { + mtx_unlock(&dqhlock); return; + } TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); + mtx_unlock(&dqhlock); } /* @@ -985,24 +1126,24 @@ mp = NULL; if (dq == NODQUOT) panic("dqsync: dquot"); - if ((dq->dq_flags & DQ_MOD) == 0) - return (0); if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) panic("dqsync: file"); - (void) vn_start_secondary_write(dqvp, &mp, V_WAIT); - if (vp != dqvp) - vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); + mtx_lock(&(dq->dq_lock)); while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+2, "dqsync", 0); - if ((dq->dq_flags & DQ_MOD) == 0) { - if (vp != dqvp) - VOP_UNLOCK(dqvp, 0, td); - vn_finished_secondary_write(mp); - return (0); - } + (void) msleep(dq, &(dq->dq_lock), PINOD+2, "dqsync", 0); + } + if ((dq->dq_flags & DQ_MOD) == 0) { + mtx_unlock(&(dq->dq_lock)); + return (0); } dq->dq_flags |= DQ_LOCK; + mtx_unlock(&(dq->dq_lock)); + + (void) vn_start_secondary_write(dqvp, &mp, V_WAIT); + if (vp != dqvp) + vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); + auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = &dq->dq_dqb; @@ -1015,9 +1156,13 @@ error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); if (auio.uio_resid && error == 0) error = EIO; + + mtx_lock(&(dq->dq_lock)); if (dq->dq_flags & DQ_WANT) wakeup(dq); dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); + mtx_unlock(&(dq->dq_lock)); + if (vp != dqvp) VOP_UNLOCK(dqvp, 0, td); vn_finished_secondary_write(mp); @@ -1039,6 +1184,7 @@ * file off their hash chains (they will eventually * fall off the head of the free list and be re-used). */ + mtx_lock(&dqhlock); for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { for (dq = LIST_FIRST(dqh); dq; dq = nextdq) { nextdq = LIST_NEXT(dq, dq_hash); @@ -1050,4 +1196,5 @@ dq->dq_ump = (struct ufsmount *)0; } } + mtx_unlock(&dqhlock); } --- orig/sys/ufs/ufs/ufs_vnops.c +++ mod/sys/ufs/ufs/ufs_vnops.c @@ -287,6 +287,9 @@ struct inode *ip = VTOI(vp); mode_t mode = ap->a_mode; int error; +#ifdef QUOTA + int vp_lockstatus; +#endif #ifdef UFS_ACL struct acl *acl; #endif @@ -304,8 +307,13 @@ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); #ifdef QUOTA + vp_lockstatus = VOP_ISLOCKED(vp, ap->a_td); + if (vp_lockstatus == LK_SHARED) + vn_lock(vp, LK_UPGRADE|LK_RETRY, ap->a_td); if ((error = getinoquota(ip)) != 0) return (error); + if (vp_lockstatus == LK_SHARED) + vn_lock(vp, LK_DOWNGRADE, ap->a_td); #endif break; default: