ffs: clear MNT_SOFTDEP earlier when remounting rw to ro
Suppose that we remount rw->ro and in parallel some reader tries to instantiate a vnode, e.g. during lookup. Suppose that softdep_unmount() already started, but we did not cleared the MNT_SOFTDEP flag yet. Then ffs_vgetf() calls into softdep_load_inodeblock() which accessed destroyed hashes and freed memory. Set/clear fs_ronly simultaneously (WRT to files flush) with MNT_SOFTDEP. It might be reasonable to move the change of fs_ronly to under MNT_ILOCK, but no readers take it. Reported and tested by: pho Reviewed by: mckusick Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D29178
This commit is contained in:
parent
7f682bdcab
commit
7c7a6681fa
@ -1792,10 +1792,10 @@ softdep_process_worklist(mp, full)
|
||||
long starttime;
|
||||
|
||||
KASSERT(mp != NULL, ("softdep_process_worklist: NULL mp"));
|
||||
if (MOUNTEDSOFTDEP(mp) == 0)
|
||||
ump = VFSTOUFS(mp);
|
||||
if (ump->um_softdep == NULL)
|
||||
return (0);
|
||||
matchcnt = 0;
|
||||
ump = VFSTOUFS(mp);
|
||||
ACQUIRE_LOCK(ump);
|
||||
starttime = time_second;
|
||||
softdep_process_journal(mp, NULL, full ? MNT_WAIT : 0);
|
||||
@ -2134,6 +2134,8 @@ softdep_waitidle(struct mount *mp, int flags __unused)
|
||||
int error, i;
|
||||
|
||||
ump = VFSTOUFS(mp);
|
||||
KASSERT(ump->um_softdep != NULL,
|
||||
("softdep_waitidle called on non-softdep filesystem"));
|
||||
devvp = ump->um_devvp;
|
||||
td = curthread;
|
||||
error = 0;
|
||||
@ -2171,14 +2173,15 @@ softdep_flushfiles(oldmnt, flags, td)
|
||||
int flags;
|
||||
struct thread *td;
|
||||
{
|
||||
#ifdef QUOTA
|
||||
struct ufsmount *ump;
|
||||
#ifdef QUOTA
|
||||
int i;
|
||||
#endif
|
||||
int error, early, depcount, loopcnt, retry_flush_count, retry;
|
||||
int morework;
|
||||
|
||||
KASSERT(MOUNTEDSOFTDEP(oldmnt) != 0,
|
||||
ump = VFSTOUFS(oldmnt);
|
||||
KASSERT(ump->um_softdep != NULL,
|
||||
("softdep_flushfiles called on non-softdep filesystem"));
|
||||
loopcnt = 10;
|
||||
retry_flush_count = 3;
|
||||
@ -2222,7 +2225,6 @@ softdep_flushfiles(oldmnt, flags, td)
|
||||
MNT_ILOCK(oldmnt);
|
||||
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)
|
||||
@ -2783,7 +2785,7 @@ softdep_unmount(mp)
|
||||
("softdep_unmount called on non-softdep filesystem"));
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag &= ~MNT_SOFTDEP;
|
||||
if (MOUNTEDSUJ(mp) == 0) {
|
||||
if ((mp->mnt_flag & MNT_SUJ) == 0) {
|
||||
MNT_IUNLOCK(mp);
|
||||
} else {
|
||||
mp->mnt_flag &= ~MNT_SUJ;
|
||||
@ -3706,12 +3708,12 @@ softdep_process_journal(mp, needwk, flags)
|
||||
int off;
|
||||
int devbsize;
|
||||
|
||||
if (MOUNTEDSUJ(mp) == 0)
|
||||
ump = VFSTOUFS(mp);
|
||||
if (ump->um_softdep == NULL || ump->um_softdep->sd_jblocks == NULL)
|
||||
return;
|
||||
shouldflush = softdep_flushcache;
|
||||
bio = NULL;
|
||||
jseg = NULL;
|
||||
ump = VFSTOUFS(mp);
|
||||
LOCK_OWNED(ump);
|
||||
fs = ump->um_fs;
|
||||
jblocks = ump->softdep_jblocks;
|
||||
@ -14201,7 +14203,8 @@ check_clear_deps(mp)
|
||||
* causes deferred work to be done sooner.
|
||||
*/
|
||||
ump = VFSTOUFS(mp);
|
||||
suj_susp = MOUNTEDSUJ(mp) && ump->softdep_jblocks->jb_suspended;
|
||||
suj_susp = ump->um_softdep->sd_jblocks != NULL &&
|
||||
ump->softdep_jblocks->jb_suspended;
|
||||
if (req_clear_remove || req_clear_inodedeps || suj_susp) {
|
||||
FREE_LOCK(ump);
|
||||
softdep_send_speedup(ump, 0, BIO_SPEEDUP_TRIM | BIO_SPEEDUP_WRITE);
|
||||
|
@ -380,6 +380,7 @@ ffs_mount(struct mount *mp)
|
||||
accmode_t accmode;
|
||||
struct nameidata ndp;
|
||||
char *fspec;
|
||||
bool mounted_softdep;
|
||||
|
||||
td = curthread;
|
||||
if (vfs_filteropt(mp->mnt_optnew, ffs_opts))
|
||||
@ -491,6 +492,16 @@ ffs_mount(struct mount *mp)
|
||||
error = vfs_write_suspend_umnt(mp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
fs->fs_ronly = 1;
|
||||
if (MOUNTEDSOFTDEP(mp)) {
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag &= ~MNT_SOFTDEP;
|
||||
MNT_IUNLOCK(mp);
|
||||
mounted_softdep = true;
|
||||
} else
|
||||
mounted_softdep = false;
|
||||
|
||||
/*
|
||||
* Check for and optionally get rid of files open
|
||||
* for writing.
|
||||
@ -498,15 +509,22 @@ ffs_mount(struct mount *mp)
|
||||
flags = WRITECLOSE;
|
||||
if (mp->mnt_flag & MNT_FORCE)
|
||||
flags |= FORCECLOSE;
|
||||
if (MOUNTEDSOFTDEP(mp)) {
|
||||
if (mounted_softdep) {
|
||||
error = softdep_flushfiles(mp, flags, td);
|
||||
} else {
|
||||
error = ffs_flushfiles(mp, flags, td);
|
||||
}
|
||||
if (error) {
|
||||
fs->fs_ronly = 0;
|
||||
if (mounted_softdep) {
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= MNT_SOFTDEP;
|
||||
MNT_IUNLOCK(mp);
|
||||
}
|
||||
vfs_write_resume(mp, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (fs->fs_pendingblocks != 0 ||
|
||||
fs->fs_pendinginodes != 0) {
|
||||
printf("WARNING: %s Update error: blocks %jd "
|
||||
@ -521,10 +539,15 @@ ffs_mount(struct mount *mp)
|
||||
if ((error = ffs_sbupdate(ump, MNT_WAIT, 0)) != 0) {
|
||||
fs->fs_ronly = 0;
|
||||
fs->fs_clean = 0;
|
||||
if (mounted_softdep) {
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= MNT_SOFTDEP;
|
||||
MNT_IUNLOCK(mp);
|
||||
}
|
||||
vfs_write_resume(mp, 0);
|
||||
return (error);
|
||||
}
|
||||
if (MOUNTEDSOFTDEP(mp))
|
||||
if (mounted_softdep)
|
||||
softdep_unmount(mp);
|
||||
g_topology_lock();
|
||||
/*
|
||||
@ -532,7 +555,6 @@ ffs_mount(struct mount *mp)
|
||||
*/
|
||||
g_access(ump->um_cp, 0, -1, -1);
|
||||
g_topology_unlock();
|
||||
fs->fs_ronly = 1;
|
||||
MNT_ILOCK(mp);
|
||||
mp->mnt_flag |= MNT_RDONLY;
|
||||
MNT_IUNLOCK(mp);
|
||||
|
Loading…
Reference in New Issue
Block a user