summaryrefslogtreecommitdiffstats
path: root/sys/ufs
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2013-02-27 18:17:34 +0000
committerattilio <attilio@FreeBSD.org>2013-02-27 18:17:34 +0000
commit52c57fbbdb554a7ce0cdbb6bf27051ef70834bdf (patch)
treed0908474209a17865e044675940a2f62f9ff2493 /sys/ufs
parentc74a3afc6a5d7d1ced989c36d4ba0a7d2bbc43b9 (diff)
downloadFreeBSD-src-52c57fbbdb554a7ce0cdbb6bf27051ef70834bdf.zip
FreeBSD-src-52c57fbbdb554a7ce0cdbb6bf27051ef70834bdf.tar.gz
MFC
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_alloc.c73
-rw-r--r--sys/ufs/ffs/ffs_softdep.c23
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c33
-rw-r--r--sys/ufs/ufs/ufs_quota.c23
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);
}
/*
OpenPOWER on IntegriCloud