From 2922abc9279fe5365073dd3cdd8f1922488fae52 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 6 Nov 2007 17:11:38 +0000 Subject: [PATCH] When the quotacheck has not been run the quota code may have to allocate blocks in the userquota file itself. This will deadlock the quota system. Disallow adjustments of quotas related to operations on the userquota file itself, and generate a warning to the console. Reported-by: David W --- sys/vfs/ufs/quota.h | 4 ++-- sys/vfs/ufs/ufs_quota.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/sys/vfs/ufs/quota.h b/sys/vfs/ufs/quota.h index 4ffcfbfb15..7ede91eaec 100644 --- a/sys/vfs/ufs/quota.h +++ b/sys/vfs/ufs/quota.h @@ -35,7 +35,7 @@ * * @(#)quota.h 8.3 (Berkeley) 8/19/94 * $FreeBSD: src/sys/ufs/ufs/quota.h,v 1.15.2.1 2003/02/27 12:04:13 das Exp $ - * $DragonFly: src/sys/vfs/ufs/quota.h,v 1.8 2006/05/06 18:48:53 dillon Exp $ + * $DragonFly: src/sys/vfs/ufs/quota.h,v 1.9 2007/11/06 17:11:38 dillon Exp $ */ #ifndef _VFS_UFS_QUOTA_H_ @@ -125,7 +125,7 @@ struct ufs_dquot { uint16_t dq_flags; /* flags, see below */ uint16_t dq_type; /* quota type of this dquot */ uint32_t dq_cnt; /* count of active references */ - uint32_t dq_id; /* identifier this applies to */ + uint32_t dq_id; /* identifier this applies to */ struct ufsmount *dq_ump; /* filesystem that this is taken from */ struct ufs_dqblk dq_dqb; /* actual usage & quotas */ }; diff --git a/sys/vfs/ufs/ufs_quota.c b/sys/vfs/ufs/ufs_quota.c index daf8852962..58c8c31ed3 100644 --- a/sys/vfs/ufs/ufs_quota.c +++ b/sys/vfs/ufs/ufs_quota.c @@ -35,7 +35,7 @@ * * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 * $FreeBSD: src/sys/ufs/ufs/ufs_quota.c,v 1.27.2.3 2002/01/15 10:33:32 phk Exp $ - * $DragonFly: src/sys/vfs/ufs/ufs_quota.c,v 1.24 2006/09/05 00:55:51 dillon Exp $ + * $DragonFly: src/sys/vfs/ufs/ufs_quota.c,v 1.25 2007/11/06 17:11:38 dillon Exp $ */ #include @@ -66,6 +66,7 @@ static int ufs_dqget (struct vnode *, u_long, struct ufsmount *, int, struct ufs_dquot **); static int ufs_dqsync (struct vnode *, struct ufs_dquot *); static void ufs_dqflush (struct vnode *); +static void ufs_quotawarn(struct ufs_dquot *dq); #ifdef DIAGNOSTIC static void ufs_dqref (struct ufs_dquot *); @@ -127,6 +128,10 @@ ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) { + ufs_quotawarn(dq); + continue; + } while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, 0, "chkdq1", 0); @@ -145,6 +150,10 @@ ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) { + ufs_quotawarn(dq); + continue; + } error = ufs_chkdqchg(ip, change, cred, i); if (error) return (error); @@ -153,6 +162,10 @@ ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) { + ufs_quotawarn(dq); + continue; + } while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, 0, "chkdq2", 0); @@ -240,6 +253,10 @@ ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) { + ufs_quotawarn(dq); + continue; + } while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, 0, "chkiq1", 0); @@ -258,6 +275,10 @@ ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) { + ufs_quotawarn(dq); + continue; + } error = ufs_chkiqchg(ip, change, cred, i); if (error) return (error); @@ -266,6 +287,10 @@ ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags) for (i = 0; i < MAXQUOTAS; i++) { if ((dq = ip->i_dquot[i]) == NODQUOT) continue; + if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) { + ufs_quotawarn(dq); + continue; + } while (dq->dq_flags & DQ_LOCK) { dq->dq_flags |= DQ_WANT; (void) tsleep((caddr_t)dq, 0, "chkiq2", 0); @@ -333,6 +358,23 @@ ufs_chkiqchg(struct inode *ip, long change, struct ucred *cred, int type) return (0); } +/* + * To avoid a deadlock we disallow quota operations on the quota file itself. + * This generally means that quotacheck was not run on the filesystem. + */ +static +void +ufs_quotawarn(struct ufs_dquot *dq) +{ + static int dqticks; + if (dqticks / hz != ticks / hz) { + dqticks = ticks / hz; + uprintf("%s: warning, quota file expanded, quotacheck " + "was not run!\n", + dq->dq_ump->um_mountp->mnt_stat.f_mntfromname); + } +} + #ifdef DIAGNOSTIC /* * On filesystems with quotas enabled, it is an error for a file to change -- 2.11.4.GIT