diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index 083dbbb0c3268..67a9908b5a4d9 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -2791,6 +2791,41 @@ void txLazyUnlock(struct tblock * tblk) LAZY_UNLOCK(flags); } + +/* + * txLazyDrain + * + * Wait for all pending lazy commit entries for this superblock + * to be processed by the jfsCommit thread. Must be called + * before freeing per-filesystem structures during unmount. + */ +void txLazyDrain(struct super_block *sb) +{ + struct jfs_sb_info *sbi = JFS_SBI(sb); + struct tblock *tblk; + unsigned long flags; + bool found; + + do { + found = false; + LAZY_LOCK(flags); + list_for_each_entry(tblk, &TxAnchor.unlock_queue, cqueue) { + if (tblk->sb == sb) { + found = true; + break; + } + } + if (!found && (sbi->commit_state & IN_LAZYCOMMIT)) + found = true; + LAZY_UNLOCK(flags); + + if (found) { + wake_up(&jfs_commit_thread_wait); + schedule_timeout_uninterruptible(1); + } + } while (found); +} + static void LogSyncRelease(struct metapage * mp) { struct jfs_log *log = mp->log; diff --git a/fs/jfs/jfs_txnmgr.h b/fs/jfs/jfs_txnmgr.h index ba71eb5ced567..80ce468eadde0 100644 --- a/fs/jfs/jfs_txnmgr.h +++ b/fs/jfs/jfs_txnmgr.h @@ -291,6 +291,7 @@ extern void txFreelock(struct inode *); extern int lmLog(struct jfs_log *, struct tblock *, struct lrd *, struct tlock *); extern void txQuiesce(struct super_block *); +extern void txLazyDrain(struct super_block *sb); extern void txResume(struct super_block *); extern void txLazyUnlock(struct tblock *); extern int jfs_lazycommit(void *); diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c index 18569f1eaabdb..657707361be2a 100644 --- a/fs/jfs/jfs_umount.c +++ b/fs/jfs/jfs_umount.c @@ -58,6 +58,13 @@ int jfs_umount(struct super_block *sb) */ jfs_flush_journal(log, 2); + /* + * Drain any pending lazy commit entries for this filesystem so + * the jfsCommit thread does not access freed structures. + */ + if (log) + txLazyDrain(sb); + /* * Hold log lock so write_special_inodes (lmLogSync) cannot see * this sbi with a NULL inode pointer while iterating log->sb_list. @@ -142,6 +149,7 @@ int jfs_umount_rw(struct super_block *sb) * remove file system from log active file system list. */ jfs_flush_journal(log, 2); + txLazyDrain(sb); /* * Make sure all metadata makes it to disk diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index 306165e61438c..95e95f71ec0fa 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1804,6 +1804,8 @@ static int lbmLogInit(struct jfs_log * log) * avoid deadlock here. */ init_waitqueue_head(&log->free_wait); + atomic_set(&log->io_count, 0); + init_waitqueue_head(&log->io_done_wait); log->lbuf_free = NULL; @@ -1855,6 +1857,9 @@ static void lbmLogShutdown(struct jfs_log * log) jfs_info("lbmLogShutdown: log:0x%p", log); + /* Wait for all in-flight log I/O to complete */ + wait_event(log->io_done_wait, !atomic_read(&log->io_count)); + lbuf = log->lbuf_free; while (lbuf) { struct lbuf *next = lbuf->l_freelist; @@ -1976,6 +1981,8 @@ static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp) bio->bi_end_io = lbmIODone; bio->bi_private = bp; + + atomic_inc(&log->io_count); /*check if journaling to disk has been disabled*/ if (log->no_integrity) { bio->bi_iter.bi_size = 0; @@ -2123,6 +2130,8 @@ static void lbmStartIO(struct lbuf * bp) bio->bi_end_io = lbmIODone; bio->bi_private = bp; + atomic_inc(&log->io_count); + /* check if journaling to disk has been disabled */ if (log->no_integrity) { bio->bi_iter.bi_size = 0; @@ -2299,6 +2308,9 @@ static void lbmIODone(struct bio *bio) out: bp->l_flag |= lbmDONE; LCACHE_UNLOCK(flags); + + if (atomic_dec_and_test(&bp->l_log->io_count)) + wake_up(&bp->l_log->io_done_wait); } int jfsIOWait(void *arg) diff --git a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h index 09e0ef6aeccef..cbf38ed27c950 100644 --- a/fs/jfs/jfs_logmgr.h +++ b/fs/jfs/jfs_logmgr.h @@ -367,6 +367,8 @@ struct jfs_log { struct lbuf *lbuf_free; /* 4: free lbufs */ wait_queue_head_t free_wait; /* 4: */ + atomic_t io_count; /* in-flight log I/O count */ + wait_queue_head_t io_done_wait; /* wait for io_count == 0 */ /* log write */ int logtid; /* 4: log tid */