* finding or making overcommit--main--1.0--patch-81 * finding or making quotas--patch--1.0--patch-51 * auto-adding kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-51 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-50) * patching for this revision (kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-51) * 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-30 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-31 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-32 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-33 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-34 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-35 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-36 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-37 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-38 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-39 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-40 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-41 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-42 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-43 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-44 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-45 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-46 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-47 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-48 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-49 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-50 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-51 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/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-30 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-31 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-32 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-33 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-34 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-35 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-36 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-37 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-38 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-39 {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-40 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-41 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-42 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-43 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-44 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-45 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-46 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-47 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-48 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-49 {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-50 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-51 {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/kern/vfs_syscalls.c +++ mod/sys/kern/vfs_syscalls.c @@ -184,7 +184,7 @@ caddr_t arg; } */ *uap; { - struct mount *mp, *vmp; + struct mount *mp; int vfslocked; int error; struct nameidata nd; @@ -199,14 +199,15 @@ 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; + if ((error = vfs_busy(mp, 0, NULL, td))) { + vrele(nd.ni_vp); + VFS_UNLOCK_GIANT(vfslocked); + return (error); + } 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_unbusy(mp, td); VFS_UNLOCK_GIANT(vfslocked); return (error); } --- orig/sys/ufs/ffs/ffs_vfsops.c +++ mod/sys/ufs/ffs/ffs_vfsops.c @@ -845,19 +845,9 @@ (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. - */ - BO_LOCK(&devvp->v_bufobj); - devvp->v_bufobj.bo_flag |= BO_NEEDSGIANT; - BO_UNLOCK(&devvp->v_bufobj); -#else MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_MPSAFE; MNT_IUNLOCK(mp); -#endif return (0); out: if (bp) @@ -1060,8 +1050,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 @@ -76,6 +76,8 @@ 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); +static int quotaoff_inchange(struct thread *td, struct mount *mp, int type); #ifdef DIAGNOSTIC static void dqref(struct dquot *); @@ -95,9 +97,11 @@ struct inode *ip; { struct ufsmount *ump; - struct vnode *vp = ITOV(ip); + struct vnode *vp; int error; + vp = ITOV(ip); + #ifndef NO_FFS_SNAPSHOT /* * Disk quotas must be turned off for snapshot files. @@ -110,8 +114,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); @@ -119,8 +122,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); @@ -151,9 +153,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) @@ -162,6 +165,7 @@ dq->dq_curblocks = 0; dq->dq_flags &= ~DQ_BLKS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&dq->dq_lock); } return (0); } @@ -177,9 +181,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 && @@ -188,6 +193,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); } @@ -205,6 +211,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. @@ -212,11 +221,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); } /* @@ -227,6 +240,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, @@ -236,15 +250,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); } @@ -273,9 +292,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 */ @@ -285,6 +305,7 @@ dq->dq_curinodes = 0; dq->dq_flags &= ~DQ_INODS; dq->dq_flags |= DQ_MOD; + mtx_unlock(&dq->dq_lock); } return (0); } @@ -300,9 +321,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 && @@ -311,6 +333,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); } @@ -328,18 +351,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); } /* @@ -350,6 +379,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, @@ -359,15 +389,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); } @@ -390,15 +425,18 @@ if ((ip->i_flags & SF_SNAPSHOT) != 0) return; #endif + UFS_LOCK(ump); 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) { + UFS_UNLOCK(ump); vprint("chkdquot: missing dquot", ITOV(ip)); panic("chkdquot: missing dquot"); } } + UFS_UNLOCK(ump); } #endif @@ -416,40 +454,57 @@ int type; void *fname; { - struct ufsmount *ump = VFSTOUFS(mp); + struct ufsmount *ump; struct vnode *vp, **vpp; struct vnode *mvp; struct dquot *dq; - int error, flags; + int error, flags, vfslocked; struct nameidata nd; error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL); if (error) return (error); - vpp = &ump->um_quotas[type]; - NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td); + ump = VFSTOUFS(mp); + dq = NODQUOT; + + NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_USERSPACE, fname, td); flags = FREAD | FWRITE; error = vn_open(&nd, &flags, 0, -1); if (error) return (error); + vfslocked = NDHASGIANT(&nd); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; VOP_UNLOCK(vp, 0, td); if (vp->v_type != VREG) { (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); + VFS_UNLOCK_GIANT(vfslocked); return (EACCES); } - if (*vpp != vp) - quotaoff(td, mp, type); - ump->um_qflags[type] |= QTF_OPENING; + + UFS_LOCK(ump); + if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) { + UFS_UNLOCK(ump); + (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); + VFS_UNLOCK_GIANT(vfslocked); + return (EALREADY); + } + ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING; MNT_ILOCK(mp); mp->mnt_flag |= MNT_QUOTA; MNT_IUNLOCK(mp); + UFS_UNLOCK(ump); + + 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); *vpp = vp; + VFS_UNLOCK_GIANT(vfslocked); /* * Save the credential of the process that turned on quotas. * Set up the time limits for this quota. @@ -465,6 +520,13 @@ dqrele(NULLVP, dq); } /* + * Allow the getdq from getinoquota below to read the quota + * from file. + */ + UFS_LOCK(ump); + ump->um_qflags[type] &= ~QTF_CLOSING; + UFS_UNLOCK(ump); + /* * Search vnodes associated with this mount point, * adding references to quota file being opened. * NB: only need to add dquot's for inodes being modified. @@ -495,35 +557,46 @@ } } MNT_IUNLOCK(mp); - ump->um_qflags[type] &= ~QTF_OPENING; + if (error) - quotaoff(td, mp, type); + quotaoff_inchange(td, mp, type); + UFS_LOCK(ump); + ump->um_qflags[type] &= ~QTF_OPENING; + KASSERT((ump->um_qflags[type] & QTF_CLOSING) == 0, + ("quotaon: leaking flags")); + UFS_UNLOCK(ump); + 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; { struct vnode *vp; struct vnode *qvp, *mvp; - struct ufsmount *ump = VFSTOUFS(mp); + struct ufsmount *ump; struct dquot *dq; struct inode *ip; + struct ucred *cr; + int vfslocked; int error; - error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL); - if (error) - return (error); - if ((qvp = ump->um_quotas[type]) == NULLVP) + ump = VFSTOUFS(mp); + + UFS_LOCK(ump); + KASSERT((ump->um_qflags[type] & QTF_CLOSING) != 0, + ("quotaoff1: flags are invalid")); + if ((qvp = ump->um_quotas[type]) == NULLVP) { + UFS_UNLOCK(ump); return (0); - ump->um_qflags[type] |= QTF_CLOSING; + } + cr = ump->um_cred[type]; + UFS_UNLOCK(ump); + /* * Search vnodes associated with this mount point, * deleting any references to quota file being closed. @@ -552,27 +625,83 @@ MNT_ILOCK(mp); } MNT_IUNLOCK(mp); + + /* Clear um_quotas before closing the quota vnode to prevent + * access to the closed vnode from dqget/dqsync + */ + UFS_LOCK(ump); + ump->um_quotas[type] = NULLVP; + ump->um_cred[type] = NOCRED; + UFS_UNLOCK(ump); + dqflush(qvp); + vfslocked = VFS_LOCK_GIANT(qvp->v_mount); 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; + VFS_UNLOCK_GIANT(vfslocked); + crfree(cr); + + return (error); +} + +int +quotaoff_inchange(td, mp, type) + struct thread *td; + struct mount *mp; + int type; +{ + struct ufsmount *ump; + int i; + int error; + + error = quotaoff1(td, mp, type); + + ump = VFSTOUFS(mp); + UFS_LOCK(ump); 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) { MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_QUOTA; MNT_IUNLOCK(mp); } + UFS_UNLOCK(ump); 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; + int error; + + error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL); + if (error) + return (error); + + ump = VFSTOUFS(mp); + UFS_LOCK(ump); + if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) { + UFS_UNLOCK(ump); + return (EALREADY); + } + ump->um_qflags[type] |= QTF_CLOSING; + UFS_UNLOCK(ump); + + return (quotaoff_inchange(td, mp, type)); +} + +/* * Q_GETQUOTA - return current values in a dqblk structure. */ int @@ -607,6 +736,7 @@ return (EINVAL); } + dq = NODQUOT; error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq); if (error) return (error); @@ -628,7 +758,7 @@ { struct dquot *dq; struct dquot *ndq; - struct ufsmount *ump = VFSTOUFS(mp); + struct ufsmount *ump; struct dqblk newlim; int error; @@ -639,13 +769,18 @@ error = copyin(addr, &newlim, sizeof (struct dqblk)); if (error) return (error); + + ndq = NODQUOT; + ump = VFSTOUFS(mp); + error = dqget(NULLVP, id, ump, type, &ndq); 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. @@ -677,6 +812,7 @@ else dq->dq_flags &= ~DQ_FAKE; dq->dq_flags |= DQ_MOD; + mtx_unlock(&dq->dq_lock); dqrele(NULLVP, dq); return (0); } @@ -693,7 +829,7 @@ void *addr; { struct dquot *dq; - struct ufsmount *ump = VFSTOUFS(mp); + struct ufsmount *ump; struct dquot *ndq; struct dqblk usage; int error; @@ -705,13 +841,18 @@ error = copyin(addr, &usage, sizeof (struct dqblk)); if (error) return (error); + + ump = VFSTOUFS(mp); + ndq = NODQUOT; + error = dqget(NULLVP, id, ump, type, &ndq); 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 @@ -730,6 +871,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); } @@ -751,9 +893,11 @@ * Check if the mount point has any quotas. * If not, simply return. */ + UFS_LOCK(ump); for (i = 0; i < MAXQUOTAS; i++) if (ump->um_quotas[i] != NULLVP) break; + UFS_UNLOCK(ump); if (i == MAXQUOTAS) return (0); /* @@ -781,7 +925,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); @@ -806,6 +950,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. */ @@ -813,6 +963,7 @@ dqinit() { + mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF); dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); TAILQ_INIT(&dqfreelist); } @@ -828,8 +979,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); } /* @@ -845,21 +998,35 @@ struct dquot **dqp; { struct thread *td = curthread; /* XXX */ - struct dquot *dq; + struct dquot *dq, *dq1; struct dqhash *dqh; struct vnode *dqvp; struct iovec aiov; struct uio auio; - int error; + int vfslocked, error; +#ifdef DEBUG_VFS_LOCKS + if (vp != NULLVP) + ASSERT_VOP_ELOCKED(vp, "dqget"); +#endif + + if (vp != NULLVP && *dqp != NODQUOT) { + return (0); + } + + UFS_LOCK(ump); dqvp = ump->um_quotas[type]; if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { *dqp = NODQUOT; + UFS_UNLOCK(ump); return (EINVAL); } + vref(dqvp); + UFS_UNLOCK(ump); /* * Check the cache first. */ + mtx_lock(&dqhlock); dqh = DQHASH(dqvp, id); LIST_FOREACH(dq, dqh, dq_hash) { if (dq->dq_id != id || @@ -872,7 +1039,9 @@ if (dq->dq_cnt == 0) TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); DQREF(dq); + mtx_unlock(&dqhlock); *dqp = dq; + vrele(dqvp); return (0); } /* @@ -882,13 +1051,37 @@ 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++; + /* + * During the sleep, other process may have allocated + * the dq for the id. Recheck the hash. + */ + mtx_lock(&dqhlock); + dqh = DQHASH(dqvp, id); + LIST_FOREACH(dq1, dqh, dq_hash) { + if (dq1->dq_id != id || + dq1->dq_ump->um_quotas[dq1->dq_type] != dqvp) + continue; + if (dq1->dq_cnt == 0) + TAILQ_REMOVE(&dqfreelist, dq1, dq_freelist); + DQREF(dq1); + numdquot--; + mtx_unlock(&dqhlock); + *dqp = dq1; + free(dq, M_DQUOT); + vrele(dqvp); + return (0); + } + mtx_init(&dq->dq_lock, "dqlock", NULL, MTX_DEF); } else { if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) { + mtx_unlock(&dqhlock); tablefull("dquot"); *dqp = NODQUOT; + vrele(dqvp); return (EUSERS); } if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) @@ -898,16 +1091,19 @@ LIST_REMOVE(dq, dq_hash); } /* - * Initialize the contents of the dquot structure. + * Initialize the contents of the dquot structure. dq will + * appear on the list already locked. This prevent other + * processes to perform simultaneous i/o on the fresh dq. */ - 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; + LIST_INSERT_HEAD(dqh, dq, dq_hash); + DQREF(dq); + /* Finally, unlock hash lock. */ + mtx_unlock(&dqhlock); + auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = &dq->dq_dqb; @@ -917,11 +1113,20 @@ auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_td = (struct thread *)0; + + vfslocked = VFS_LOCK_GIANT(dqvp->v_mount); + error = 0; + if (vp != dqvp) + vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, td); error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); if (auio.uio_resid == sizeof(struct dqblk) && error == 0) bzero(&dq->dq_dqb, sizeof(struct dqblk)); if (vp != dqvp) - VOP_UNLOCK(dqvp, 0, td); + vput(dqvp); + else + vrele(dqvp); + VFS_UNLOCK_GIANT(vfslocked); + mtx_lock(&dq->dq_lock); if (dq->dq_flags & DQ_WANT) wakeup(dq); dq->dq_flags = 0; @@ -930,7 +1135,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); @@ -948,6 +1156,7 @@ if (dq->dq_itime == 0) dq->dq_itime = time_second + ump->um_itime[type]; } + mtx_unlock(&dq->dq_lock); *dqp = dq; return (0); } @@ -976,15 +1185,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); } /* @@ -999,30 +1217,35 @@ struct vnode *dqvp; struct iovec aiov; struct uio auio; - int error; + int vfslocked, error; struct mount *mp; mp = NULL; if (dq == NODQUOT) panic("dqsync: dquot"); - if ((dq->dq_flags & DQ_MOD) == 0) - return (0); + UFS_LOCK(dq->dq_ump); 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); + vref(dqvp); + UFS_UNLOCK(dq->dq_ump); + 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); + vrele(dqvp); + return (0); } dq->dq_flags |= DQ_LOCK; + mtx_unlock(&dq->dq_lock); + + vfslocked = VFS_LOCK_GIANT(dqvp->v_mount); + (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; @@ -1035,12 +1258,19 @@ 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); + vput(dqvp); + else + vrele(dqvp); vn_finished_secondary_write(mp); + VFS_UNLOCK_GIANT(vfslocked); return (error); } @@ -1059,6 +1289,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); @@ -1070,4 +1301,5 @@ dq->dq_ump = (struct ufsmount *)0; } } + mtx_unlock(&dqhlock); } --- orig/sys/ufs/ufs/ufs_vnops.c +++ mod/sys/ufs/ufs/ufs_vnops.c @@ -324,10 +324,6 @@ case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); -#ifdef QUOTA - if ((error = getinoquota(ip)) != 0) - return (error); -#endif break; default: break;