diff options
Diffstat (limited to 'sys/ufs')
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 73 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_softdep.c | 23 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_vfsops.c | 33 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_quota.c | 23 |
4 files changed, 129 insertions, 23 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index abe4073..789a7cf 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -1790,6 +1790,17 @@ fail: return (0); } +static inline struct buf * +getinobuf(struct inode *ip, u_int cg, u_int32_t cginoblk, int gbflags) +{ + struct fs *fs; + + fs = ip->i_fs; + return (getblk(ip->i_devvp, fsbtodb(fs, ino_to_fsba(fs, + cg * fs->fs_ipg + cginoblk)), (int)fs->fs_bsize, 0, 0, + gbflags)); +} + /* * Determine whether an inode can be allocated. * @@ -1814,9 +1825,11 @@ ffs_nodealloccg(ip, cg, ipref, mode, unused) u_int8_t *inosused, *loc; struct ufs2_dinode *dp2; int error, start, len, i; + u_int32_t old_initediblk; fs = ip->i_fs; ump = ip->i_ump; +check_nifree: if (fs->fs_cs(fs, cg).cs_nifree == 0) return (0); UFS_UNLOCK(ump); @@ -1828,13 +1841,13 @@ ffs_nodealloccg(ip, cg, ipref, mode, unused) return (0); } cgp = (struct cg *)bp->b_data; +restart: if (!cg_chkmagic(cgp) || cgp->cg_cs.cs_nifree == 0) { brelse(bp); UFS_LOCK(ump); return (0); } bp->b_xflags |= BX_BKGRDWRITE; - cgp->cg_old_time = cgp->cg_time = time_second; inosused = cg_inosused(cgp); if (ipref) { ipref %= fs->fs_ipg; @@ -1856,7 +1869,6 @@ ffs_nodealloccg(ip, cg, ipref, mode, unused) } } ipref = (loc - inosused) * NBBY + ffs(~*loc) - 1; - cgp->cg_irotor = ipref; gotit: /* * Check to see if we need to initialize more inodes. @@ -1864,9 +1876,37 @@ gotit: if (fs->fs_magic == FS_UFS2_MAGIC && ipref + INOPB(fs) > cgp->cg_initediblk && cgp->cg_initediblk < cgp->cg_niblk) { - ibp = getblk(ip->i_devvp, fsbtodb(fs, - ino_to_fsba(fs, cg * fs->fs_ipg + cgp->cg_initediblk)), - (int)fs->fs_bsize, 0, 0, 0); + old_initediblk = cgp->cg_initediblk; + + /* + * Free the cylinder group lock before writing the + * initialized inode block. Entering the + * babarrierwrite() with the cylinder group lock + * causes lock order violation between the lock and + * snaplk. + * + * Another thread can decide to initialize the same + * inode block, but whichever thread first gets the + * cylinder group lock after writing the newly + * allocated inode block will update it and the other + * will realize that it has lost and leave the + * cylinder group unchanged. + */ + ibp = getinobuf(ip, cg, old_initediblk, GB_LOCK_NOWAIT); + brelse(bp); + if (ibp == NULL) { + /* + * The inode block buffer is already owned by + * another thread, which must initialize it. + * Wait on the buffer to allow another thread + * to finish the updates, with dropped cg + * buffer lock, then retry. + */ + ibp = getinobuf(ip, cg, old_initediblk, 0); + brelse(ibp); + UFS_LOCK(ump); + goto check_nifree; + } bzero(ibp->b_data, (int)fs->fs_bsize); dp2 = (struct ufs2_dinode *)(ibp->b_data); for (i = 0; i < INOPB(fs); i++) { @@ -1883,8 +1923,29 @@ gotit: * loading of newly created filesystems. */ babarrierwrite(ibp); - cgp->cg_initediblk += INOPB(fs); + + /* + * After the inode block is written, try to update the + * cg initediblk pointer. If another thread beat us + * to it, then leave it unchanged as the other thread + * has already set it correctly. + */ + error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), + (int)fs->fs_cgsize, NOCRED, &bp); + UFS_LOCK(ump); + ACTIVECLEAR(fs, cg); + UFS_UNLOCK(ump); + if (error != 0) { + brelse(bp); + return (error); + } + cgp = (struct cg *)bp->b_data; + if (cgp->cg_initediblk == old_initediblk) + cgp->cg_initediblk += INOPB(fs); + goto restart; } + cgp->cg_old_time = cgp->cg_time = time_second; + cgp->cg_irotor = ipref; UFS_LOCK(ump); ACTIVECLEAR(fs, cg); setbit(inosused, ipref); diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 16fe134..e39fd46 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -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 @@ retry_flush: * 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 @@ retry_flush: 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; diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 0204613..b3292d0 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -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. */ diff --git a/sys/ufs/ufs/ufs_quota.c b/sys/ufs/ufs/ufs_quota.c index 87ac9a1..a949898 100644 --- a/sys/ufs/ufs/ufs_quota.c +++ b/sys/ufs/ufs/ufs_quota.c @@ -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 @@ again: 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 @@ out: /* * 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); } /* |