* finding or making overcommit--main--1.0--patch-42 * finding or making quotas--patch--1.0--patch-15 * auto-adding kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-15 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-14) * patching for this revision (kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-15) * 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-2 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 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-2 {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/sys/ufs/ffs/ffs_vfsops.c +++ mod/sys/ufs/ffs/ffs_vfsops.c @@ -826,9 +826,7 @@ (void) ufs_extattr_autostart(mp, td); #endif /* !UFS_EXTATTR_AUTOSTART */ #endif /* !UFS_EXTATTR */ -#ifndef QUOTA mp->mnt_kern_flag |= MNTK_MPSAFE; -#endif return (0); out: if (bp) @@ -1029,8 +1027,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 @@ -102,8 +102,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 +110,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 +141,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 +153,7 @@ dq->dq_curblocks = 0; dq->dq_flags &= ~DQ_BLKS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); } return (0); } @@ -169,9 +169,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 +181,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 +199,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 +209,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 +228,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 +238,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 +280,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 +293,7 @@ dq->dq_curinodes = 0; dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); } return (0); } @@ -292,9 +309,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 +321,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 +339,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 +367,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 +377,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 +406,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 +438,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; @@ -504,9 +538,13 @@ if (error) return (error); - if ((qvp = ump->um_quotas[type]) == NULLVP) + mtx_lock(&(ump->um_lock)); + if ((qvp = ump->um_quotas[type]) == NULLVP) { + mtx_unlock(&(ump->um_lock)); return (0); + } ump->um_qflags[type] |= QTF_CLOSING; + mtx_unlock(&(ump->um_lock)); /* * Search vnodes associated with this mount point, * deleting any references to quota file being closed. @@ -540,13 +578,15 @@ 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; + mtx_lock(&(ump->um_lock)); ump->um_qflags[type] &= ~QTF_CLOSING; + ump->um_quotas[type] = NULLVP; for (type = 0; type < MAXQUOTAS; type++) if (ump->um_quotas[type] != NULLVP) break; + mtx_unlock(&(ump->um_lock)); if (type == MAXQUOTAS) mp->mnt_flag &= ~MNT_QUOTA; return (error); @@ -563,7 +603,7 @@ int type; void *addr; { - struct dquot *dq; + struct dquot *dq = NODQUOT; int error; switch (type) { @@ -607,7 +647,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 +663,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 +698,7 @@ else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; + mtx_unlock(&(dq->dq_lock)); dqrele(NULLVP, dq); return (0); } @@ -674,7 +716,7 @@ { struct dquot *dq; struct ufsmount *ump = VFSTOUFS(mp); - struct dquot *ndq; + struct dquot *ndq = NODQUOT; struct dqblk usage; int error; @@ -689,9 +731,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 +753,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 +775,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 +807,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 +832,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 +845,7 @@ dqinit() { + mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF); dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); TAILQ_INIT(&dqfreelist); } @@ -808,8 +861,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 +887,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 +920,7 @@ if (dq->dq_cnt == 0) TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); DQREF(dq); + mtx_unlock(&dqhlock); *dqp = dq; return (0); } @@ -862,11 +931,14 @@ numdquot < MAXQUOTAS * desiredvnodes) desireddquot += DQUOTINC; if (numdquot < desireddquot) { + 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 +948,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,15 +978,19 @@ 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; + mtx_unlock(&(dq->dq_lock)); /* * I/O error in reading quota file, release * quota structure and reflect problem to caller. */ if (error) { + mtx_lock(&dqhlock); LIST_REMOVE(dq, dq_hash); + mtx_unlock(&dqhlock); dqrele(vp, dq); *dqp = NODQUOT; return (error); @@ -956,15 +1036,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 +1074,27 @@ 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)); + if (vp != dqvp) + VOP_UNLOCK(dqvp, 0, td); + vn_finished_secondary_write(mp); + return (0); } dq->dq_flags |= DQ_LOCK; + mtx_unlock(&(dq->dq_lock)); + auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = &dq->dq_dqb; @@ -1015,9 +1107,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 +1135,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 +1147,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: