The softdep freeblks workitem might hold a reference on the dquot.
Current dqflush() panics when a dquot with with non-zero refcount is encountered. The situation is possible, because quotas are turned off before softdep workitem queue if flushed, due to the quota file writes might create softdep workitems. Make the encountering an active dquot in dqflush() not fatal, return the error from quotaoff() instead. Ignore the quotaoff() failures when ffs_flushfiles() is called in the course of softdep_flushfiles() loop, until the last iteration. At the last loop, the quotas must be closed, and because SU workitems should be already flushed, the references to dquot are gone. Sponsored by: The FreeBSD Foundation Reported and tested by: pho Reviewed by: mckusick MFC after: 2 weeks
This commit is contained in:
parent
94f4ac214c
commit
ba05dec5a4
@ -385,6 +385,7 @@ extern int vttoif_tab[];
|
||||
#define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */
|
||||
#define FORCECLOSE 0x0002 /* vflush: force file closure */
|
||||
#define WRITECLOSE 0x0004 /* vflush: only close writable files */
|
||||
#define EARLYFLUSH 0x0008 /* vflush: early call for ffs_flushfiles */
|
||||
#define V_SAVE 0x0001 /* vinvalbuf: sync file first */
|
||||
#define V_ALT 0x0002 /* vinvalbuf: invalidate only alternate bufs */
|
||||
#define V_NORMAL 0x0004 /* vinvalbuf: invalidate only regular bufs */
|
||||
|
@ -1908,7 +1908,12 @@ softdep_flushfiles(oldmnt, flags, td)
|
||||
int flags;
|
||||
struct thread *td;
|
||||
{
|
||||
int error, depcount, loopcnt, retry_flush_count, retry;
|
||||
#ifdef QUOTA
|
||||
struct ufsmount *ump;
|
||||
int i;
|
||||
#endif
|
||||
int error, early, depcount, loopcnt, retry_flush_count, retry;
|
||||
int morework;
|
||||
|
||||
loopcnt = 10;
|
||||
retry_flush_count = 3;
|
||||
@ -1926,7 +1931,9 @@ softdep_flushfiles(oldmnt, flags, td)
|
||||
* Do another flush in case any vnodes were brought in
|
||||
* as part of the cleanup operations.
|
||||
*/
|
||||
if ((error = ffs_flushfiles(oldmnt, flags, td)) != 0)
|
||||
early = retry_flush_count == 1 || (oldmnt->mnt_kern_flag &
|
||||
MNTK_UNMOUNT) == 0 ? 0 : EARLYFLUSH;
|
||||
if ((error = ffs_flushfiles(oldmnt, flags | early, td)) != 0)
|
||||
break;
|
||||
if ((error = softdep_flushworklist(oldmnt, &depcount, td)) != 0 ||
|
||||
depcount == 0)
|
||||
@ -1950,7 +1957,17 @@ softdep_flushfiles(oldmnt, flags, td)
|
||||
MNT_ILOCK(oldmnt);
|
||||
KASSERT((oldmnt->mnt_kern_flag & MNTK_NOINSMNTQ) != 0,
|
||||
("softdep_flushfiles: !MNTK_NOINSMNTQ"));
|
||||
if (oldmnt->mnt_nvnodelistsize > 0) {
|
||||
morework = oldmnt->mnt_nvnodelistsize > 0;
|
||||
#ifdef QUOTA
|
||||
ump = VFSTOUFS(oldmnt);
|
||||
UFS_LOCK(ump);
|
||||
for (i = 0; i < MAXQUOTAS; i++) {
|
||||
if (ump->um_quotas[i] != NULLVP)
|
||||
morework = 1;
|
||||
}
|
||||
UFS_UNLOCK(ump);
|
||||
#endif
|
||||
if (morework) {
|
||||
if (--retry_flush_count > 0) {
|
||||
retry = 1;
|
||||
loopcnt = 3;
|
||||
|
@ -1351,9 +1351,10 @@ ffs_flushfiles(mp, flags, td)
|
||||
struct thread *td;
|
||||
{
|
||||
struct ufsmount *ump;
|
||||
int error;
|
||||
int qerror, error;
|
||||
|
||||
ump = VFSTOUFS(mp);
|
||||
qerror = 0;
|
||||
#ifdef QUOTA
|
||||
if (mp->mnt_flag & MNT_QUOTA) {
|
||||
int i;
|
||||
@ -1361,11 +1362,19 @@ ffs_flushfiles(mp, flags, td)
|
||||
if (error)
|
||||
return (error);
|
||||
for (i = 0; i < MAXQUOTAS; i++) {
|
||||
quotaoff(td, mp, i);
|
||||
error = quotaoff(td, mp, i);
|
||||
if (error != 0) {
|
||||
if ((flags & EARLYFLUSH) == 0)
|
||||
return (error);
|
||||
else
|
||||
qerror = error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we fall through to vflush again to ensure
|
||||
* that we have gotten rid of all the system vnodes.
|
||||
* Here we fall through to vflush again to ensure that
|
||||
* we have gotten rid of all the system vnodes, unless
|
||||
* quotas must not be closed.
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
@ -1380,11 +1389,21 @@ ffs_flushfiles(mp, flags, td)
|
||||
* that we have gotten rid of all the system vnodes.
|
||||
*/
|
||||
}
|
||||
/*
|
||||
* Flush all the files.
|
||||
|
||||
/*
|
||||
* Do not close system files if quotas were not closed, to be
|
||||
* able to sync the remaining dquots. The freeblks softupdate
|
||||
* workitems might hold a reference on a dquot, preventing
|
||||
* quotaoff() from completing. Next round of
|
||||
* softdep_flushworklist() iteration should process the
|
||||
* blockers, allowing the next run of quotaoff() to finally
|
||||
* flush held dquots.
|
||||
*
|
||||
* Otherwise, flush all the files.
|
||||
*/
|
||||
if ((error = vflush(mp, 0, flags, td)) != 0)
|
||||
if (qerror == 0 && (error = vflush(mp, 0, flags, td)) != 0)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Flush filesystem metadata.
|
||||
*/
|
||||
|
@ -80,7 +80,7 @@ static int dqopen(struct vnode *, struct ufsmount *, int);
|
||||
static int dqget(struct vnode *,
|
||||
u_long, struct ufsmount *, int, struct dquot **);
|
||||
static int dqsync(struct vnode *, struct dquot *);
|
||||
static void dqflush(struct vnode *);
|
||||
static int 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);
|
||||
|
||||
@ -674,8 +674,12 @@ quotaoff1(struct thread *td, struct mount *mp, int type)
|
||||
vrele(vp);
|
||||
}
|
||||
|
||||
dqflush(qvp);
|
||||
/* Clear um_quotas before closing the quota vnode to prevent
|
||||
error = dqflush(qvp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Clear um_quotas before closing the quota vnode to prevent
|
||||
* access to the closed vnode from dqget/dqsync
|
||||
*/
|
||||
UFS_LOCK(ump);
|
||||
@ -1594,17 +1598,19 @@ dqsync(struct vnode *vp, struct dquot *dq)
|
||||
/*
|
||||
* Flush all entries from the cache for a particular vnode.
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
dqflush(struct vnode *vp)
|
||||
{
|
||||
struct dquot *dq, *nextdq;
|
||||
struct dqhash *dqh;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Move all dquot's that used to refer to this quota
|
||||
* file off their hash chains (they will eventually
|
||||
* fall off the head of the free list and be re-used).
|
||||
*/
|
||||
error = 0;
|
||||
DQH_LOCK();
|
||||
for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
|
||||
for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
|
||||
@ -1612,12 +1618,15 @@ dqflush(struct vnode *vp)
|
||||
if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
|
||||
continue;
|
||||
if (dq->dq_cnt)
|
||||
panic("dqflush: stray dquot");
|
||||
LIST_REMOVE(dq, dq_hash);
|
||||
dq->dq_ump = (struct ufsmount *)0;
|
||||
error = EBUSY;
|
||||
else {
|
||||
LIST_REMOVE(dq, dq_hash);
|
||||
dq->dq_ump = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
DQH_UNLOCK();
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user