* finding or making overcommit--main--1.0--patch-85 * finding or making quotas--patch--1.0--patch-55 * auto-adding kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-55 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-54) * patching for this revision (kostikbel@ukr.net--2005-freebsd/quotas--patch--1.0--patch-55) * 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-52 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-53 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-54 A {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-55 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_softdep.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-52 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-53 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-54 {arch}/quotas/quotas--patch/quotas--patch--1.0/kostikbel@ukr.net--2005-freebsd/patch-log/patch-55 {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 @@ -185,7 +185,7 @@ caddr_t arg; } */ *uap; { - struct mount *mp, *vmp; + struct mount *mp; int vfslocked; int error; struct nameidata nd; @@ -200,14 +200,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_softdep.c +++ mod/sys/ufs/ffs/ffs_softdep.c @@ -722,16 +722,12 @@ struct ufsmount *ump; struct thread *td; int remaining; - int vfslocked; td = curthread; td->td_pflags |= TDP_NORUNNINGBUF; for (;;) { kthread_suspend_check(softdepproc); -#ifdef QUOTA - mtx_lock(&Giant); -#endif ACQUIRE_LOCK(&lk); /* * If requested, try removing inode or removal dependencies. @@ -747,9 +743,6 @@ wakeup_one(&proc_waiting); } FREE_LOCK(&lk); -#ifdef QUOTA - mtx_unlock(&Giant); -#endif remaining = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { @@ -758,12 +751,10 @@ continue; if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, td)) continue; - vfslocked = VFS_LOCK_GIANT(mp); softdep_process_worklist(mp, 0); ump = VFSTOUFS(mp); remaining += ump->softdep_on_worklist - ump->softdep_on_worklist_inprogress; - VFS_UNLOCK_GIANT(vfslocked); mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, td); --- orig/sys/ufs/ffs/ffs_vfsops.c +++ mod/sys/ufs/ffs/ffs_vfsops.c @@ -877,19 +877,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) @@ -1100,8 +1090,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 @@ -113,23 +113,25 @@ * filesystem. There is one allocated for each quota that exists on any * filesystem for the current user or group. A cache is kept of recently * used entries. + * (h) protected by dqhlock */ struct dquot { - LIST_ENTRY(dquot) dq_hash; /* hash list */ - TAILQ_ENTRY(dquot) dq_freelist; /* free list */ + LIST_ENTRY(dquot) dq_hash; /* (h) hash list */ + TAILQ_ENTRY(dquot) dq_freelist; /* (h) 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 */ + u_int32_t dq_cnt; /* (h) count of active references */ u_int32_t dq_id; /* identifier this applies to */ + u_int32_t dq_cno; /* dq change number */ + u_int32_t dq_cno_wrtn; /* dq change number last + written to quota file */ struct ufsmount *dq_ump; /* filesystem that this is taken from */ struct dqblk dq_dqb; /* actual usage & quotas */ }; /* * Flag values. */ -#define DQ_LOCK 0x01 /* this quota locked (no MODS) */ -#define DQ_WANT 0x02 /* wakeup on unlock */ -#define DQ_MOD 0x04 /* this quota modified since read */ #define DQ_FAKE 0x08 /* no limits here, just usage */ #define DQ_BLKS 0x10 /* has been warned about blk limit */ #define DQ_INODS 0x20 /* has been warned about inode limit */ --- orig/sys/ufs/ufs/ufs_quota.c +++ mod/sys/ufs/ufs/ufs_quota.c @@ -77,6 +77,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 *); @@ -96,9 +98,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. @@ -111,8 +115,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); @@ -120,8 +123,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); @@ -152,17 +154,15 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkdq1", 0); - } + mtx_lock(&dq->dq_lock); ncurblocks = dq->dq_curblocks + change; if (ncurblocks >= 0) dq->dq_curblocks = ncurblocks; else dq->dq_curblocks = 0; dq->dq_flags &= ~DQ_BLKS; - dq->dq_flags |= DQ_MOD; + dq->dq_cno++; + mtx_unlock(&dq->dq_lock); } return (0); } @@ -179,17 +179,15 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkdq2", 0); - } + mtx_lock(&dq->dq_lock); /* Reset timer when crossing soft limit */ if (dq->dq_curblocks + change >= dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit) dq->dq_btime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i]; dq->dq_curblocks += change; - dq->dq_flags |= DQ_MOD; + dq->dq_cno++; + mtx_unlock(&dq->dq_lock); } return (0); } @@ -207,6 +205,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. @@ -214,11 +215,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); } /* @@ -229,6 +234,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, @@ -238,15 +244,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); } @@ -275,10 +286,7 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkiq1", 0); - } + mtx_lock(&dq->dq_lock); ncurinodes = dq->dq_curinodes + change; /* XXX: ncurinodes is unsigned */ if (ncurinodes >= 0) @@ -286,7 +294,8 @@ else dq->dq_curinodes = 0; dq->dq_flags &= ~DQ_INODS; - dq->dq_flags |= DQ_MOD; + dq->dq_cno++; + mtx_unlock(&dq->dq_lock); } return (0); } @@ -303,17 +312,15 @@ for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "chkiq2", 0); - } + mtx_lock(&dq->dq_lock); /* Reset timer when crossing soft limit */ if (dq->dq_curinodes + change >= dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_itime = time_second + VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i]; dq->dq_curinodes += change; - dq->dq_flags |= DQ_MOD; + dq->dq_cno++; + mtx_unlock(&dq->dq_lock); } return (0); } @@ -331,18 +338,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); } /* @@ -353,6 +366,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, @@ -362,15 +376,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); } @@ -393,15 +412,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 @@ -419,11 +441,11 @@ 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; /* @@ -434,29 +456,46 @@ 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); - NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; + vfslocked = NDHASGIANT(&nd); + NDFREE(&nd, NDF_ONLY_PNBUF); 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. @@ -472,6 +511,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. @@ -502,39 +548,45 @@ } } 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; - /* - * XXXRW: This also seems wrong to allow in a jail? - */ - error = priv_check_cred(td->td_ucred, PRIV_UFS_QUOTAOFF, - SUSER_ALLOWJAIL); - if (error) - return (error); + ump = VFSTOUFS(mp); - if ((qvp = ump->um_quotas[type]) == NULLVP) + 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. @@ -563,27 +615,91 @@ 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; + + /* + * XXXRW: This also seems wrong to allow in a jail? + */ + error = priv_check_cred(td->td_ucred, PRIV_UFS_QUOTAOFF, + SUSER_ALLOWJAIL); + if (error) + return (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 @@ -621,6 +737,7 @@ return (EINVAL); } + dq = NODQUOT; error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq); if (error) return (error); @@ -642,7 +759,7 @@ { struct dquot *dq; struct dquot *ndq; - struct ufsmount *ump = VFSTOUFS(mp); + struct ufsmount *ump; struct dqblk newlim; int error; @@ -654,14 +771,15 @@ 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; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "setqta", 0); - } + mtx_lock(&dq->dq_lock); /* * Copy all but the current values. * Reset time limit if previously had no soft limit or were @@ -691,7 +809,8 @@ dq->dq_flags |= DQ_FAKE; else dq->dq_flags &= ~DQ_FAKE; - dq->dq_flags |= DQ_MOD; + dq->dq_cno++; + mtx_unlock(&dq->dq_lock); dqrele(NULLVP, dq); return (0); } @@ -708,7 +827,7 @@ void *addr; { struct dquot *dq; - struct ufsmount *ump = VFSTOUFS(mp); + struct ufsmount *ump; struct dquot *ndq; struct dqblk usage; int error; @@ -721,14 +840,15 @@ 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; - while (dq->dq_flags & DQ_LOCK) { - dq->dq_flags |= DQ_WANT; - (void) tsleep(dq, PINOD+1, "setuse", 0); - } + mtx_lock(&dq->dq_lock); /* * Reset time limit if have a soft limit and were * previously under it, but are now over it. @@ -745,7 +865,8 @@ dq->dq_flags &= ~DQ_BLKS; if (dq->dq_curinodes < dq->dq_isoftlimit) dq->dq_flags &= ~DQ_INODS; - dq->dq_flags |= DQ_MOD; + dq->dq_cno++; + mtx_unlock(&dq->dq_lock); dqrele(NULLVP, dq); return (0); } @@ -767,9 +888,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); /* @@ -797,7 +920,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); @@ -822,6 +945,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. */ @@ -829,6 +958,7 @@ dqinit() { + mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF); dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); TAILQ_INIT(&dqfreelist); } @@ -844,8 +974,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); } /* @@ -861,21 +993,36 @@ 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; + struct dqblk dqb1; + 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 || @@ -888,70 +1035,112 @@ if (dq->dq_cnt == 0) TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); DQREF(dq); + mtx_unlock(&dqhlock); *dqp = dq; + vrele(dqvp); return (0); } + mtx_unlock(&dqhlock); /* - * Not in cache, allocate a new one. + * Not in cache, first read the quotas from file. + */ + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + aiov.iov_base = &dqb1; + aiov.iov_len = sizeof (struct dqblk); + auio.uio_resid = sizeof (struct dqblk); + auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); + 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(&dqb1, sizeof(struct dqblk)); + if (vp != dqvp) + vput(dqvp); + else + vrele(dqvp); + VFS_UNLOCK_GIANT(vfslocked); + /* + * I/O error in reading quota file, reflect problem to caller. + */ + if (error) { + *dqp = NODQUOT; + return (error); + } + + dq1 = NULL; + mtx_lock(&dqhlock); + /* + * Try to allocate new dq. */ if (TAILQ_FIRST(&dqfreelist) == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes) desireddquot += DQUOTINC; if (numdquot < desireddquot) { - dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, - M_WAITOK | M_ZERO); numdquot++; + mtx_unlock(&dqhlock); + dq1 = (struct dquot *)malloc(sizeof *dq, M_DQUOT, + M_WAITOK | M_ZERO); + mtx_lock(&dqhlock); + } + /* + * During the sleep (caused by I/O and malloc), other process + * may have allocated the dq for the id. Recheck the hash. + */ + dqh = DQHASH(dqvp, id); + LIST_FOREACH(dq, dqh, dq_hash) { + if (dq->dq_id != id || + dq->dq_ump->um_quotas[dq->dq_type] != dqvp) + continue; + if (dq->dq_cnt == 0) + TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); + DQREF(dq); + if (dq1 != NULL) + numdquot--; + mtx_unlock(&dqhlock); + *dqp = dq; + free(dq1, M_DQUOT); + return (0); + } + if (dq1 != NULL) { + /* Prepare the newly allocated dq */ + mtx_init(&dq1->dq_lock, "dqlock", NULL, MTX_DEF); + dq = dq1; } else { + /* Try to take dq from the free list */ if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) { + mtx_unlock(&dqhlock); tablefull("dquot"); *dqp = NODQUOT; return (EUSERS); } - if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) + if (dq->dq_cnt || dq->dq_cno != dq->dq_cno_wrtn) panic("dqget: free dquot isn't"); TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); if (dq->dq_ump != NULL) LIST_REMOVE(dq, dq_hash); } + + /* * 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; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - aiov.iov_base = &dq->dq_dqb; - aiov.iov_len = sizeof (struct dqblk); - auio.uio_resid = sizeof (struct dqblk); - auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); - auio.uio_segflg = UIO_SYSSPACE; - auio.uio_rw = UIO_READ; - auio.uio_td = (struct thread *)0; - 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); - if (dq->dq_flags & DQ_WANT) - wakeup(dq); + dq->dq_dqb = dqb1; + LIST_INSERT_HEAD(dqh, dq, dq_hash); + DQREF(dq); + /* Finally, unlock hash lock. */ + mtx_lock(&dq->dq_lock); + mtx_unlock(&dqhlock); dq->dq_flags = 0; /* - * I/O error in reading quota file, release - * quota structure and reflect problem to caller. - */ - if (error) { - LIST_REMOVE(dq, dq_hash); - dqrele(vp, dq); - *dqp = NODQUOT; - return (error); - } - /* * Check for no limit to enforce. * Initialize time values if necessary. */ @@ -964,6 +1153,7 @@ if (dq->dq_itime == 0) dq->dq_itime = time_second + ump->um_itime[type]; } + mtx_unlock(&dq->dq_lock); *dqp = dq; return (0); } @@ -992,15 +1182,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); } /* @@ -1015,33 +1214,37 @@ struct vnode *dqvp; struct iovec aiov; struct uio auio; - int error; + int vfslocked, error; struct mount *mp; + struct dqblk dqb1; + u_int32_t cno1; 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"); + vref(dqvp); + UFS_UNLOCK(dq->dq_ump); + mtx_lock(&dq->dq_lock); + if (dq->dq_cno == dq->dq_cno_wrtn) { + mtx_unlock(&dq->dq_lock); + vrele(dqvp); + return (0); + } + dqb1 = dq->dq_dqb; + cno1 = dq->dq_cno; + 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); - 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); - } - } - dq->dq_flags |= DQ_LOCK; + auio.uio_iov = &aiov; auio.uio_iovcnt = 1; - aiov.iov_base = &dq->dq_dqb; + aiov.iov_base = &dqb1; aiov.iov_len = sizeof (struct dqblk); auio.uio_resid = sizeof (struct dqblk); auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); @@ -1051,12 +1254,18 @@ error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); if (auio.uio_resid && error == 0) error = EIO; - if (dq->dq_flags & DQ_WANT) - wakeup(dq); - dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); + else { + mtx_lock(&dq->dq_lock); + dq->dq_cno_wrtn = cno1; + 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); } @@ -1075,6 +1284,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); @@ -1086,4 +1296,5 @@ dq->dq_ump = (struct ufsmount *)0; } } + mtx_unlock(&dqhlock); } --- orig/sys/ufs/ufs/ufs_vnops.c +++ mod/sys/ufs/ufs/ufs_vnops.c @@ -328,10 +328,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;