diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/xfs_health.c | 9 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.c | 50 |
2 files changed, 50 insertions, 9 deletions
diff --git a/fs/xfs/xfs_health.c b/fs/xfs/xfs_health.c index 5de3195f6cb2..eb10eacabc8f 100644 --- a/fs/xfs/xfs_health.c +++ b/fs/xfs/xfs_health.c @@ -229,6 +229,15 @@ xfs_inode_mark_sick( ip->i_sick |= mask; ip->i_checked |= mask; spin_unlock(&ip->i_flags_lock); + + /* + * Keep this inode around so we don't lose the sickness report. Scrub + * grabs inodes with DONTCACHE assuming that most inode are ok, which + * is not the case here. + */ + spin_lock(&VFS_I(ip)->i_lock); + VFS_I(ip)->i_state &= ~I_DONTCACHE; + spin_unlock(&VFS_I(ip)->i_lock); } /* Mark parts of an inode healed. */ diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 0cd29a2f9da5..6f1383bf706a 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -71,10 +71,13 @@ static int xfs_icwalk_ag(struct xfs_perag *pag, /* Stop scanning after icw_scan_limit inodes. */ #define XFS_ICWALK_FLAG_SCAN_LIMIT (1U << 28) +#define XFS_ICWALK_FLAG_RECLAIM_SICK (1U << 27) + #define XFS_ICWALK_PRIVATE_FLAGS (XFS_ICWALK_FLAG_DROP_UDQUOT | \ XFS_ICWALK_FLAG_DROP_GDQUOT | \ XFS_ICWALK_FLAG_DROP_PDQUOT | \ - XFS_ICWALK_FLAG_SCAN_LIMIT) + XFS_ICWALK_FLAG_SCAN_LIMIT | \ + XFS_ICWALK_FLAG_RECLAIM_SICK) /* * Allocate and initialise an xfs_inode. @@ -523,9 +526,6 @@ xfs_iget_cache_hit( XFS_INO_TO_AGINO(pag->pag_mount, ino), XFS_ICI_RECLAIM_TAG); inode->i_state = I_NEW; - ip->i_sick = 0; - ip->i_checked = 0; - spin_unlock(&ip->i_flags_lock); spin_unlock(&pag->pag_ici_lock); } else { @@ -913,7 +913,8 @@ xfs_dqrele_all_inodes( */ static bool xfs_reclaim_igrab( - struct xfs_inode *ip) + struct xfs_inode *ip, + struct xfs_eofblocks *eofb) { ASSERT(rcu_read_lock_held()); @@ -924,6 +925,14 @@ xfs_reclaim_igrab( spin_unlock(&ip->i_flags_lock); return false; } + + /* Don't reclaim a sick inode unless the caller asked for it. */ + if (ip->i_sick && + (!eofb || !(eofb->eof_flags & XFS_ICWALK_FLAG_RECLAIM_SICK))) { + spin_unlock(&ip->i_flags_lock); + return false; + } + __xfs_iflags_set(ip, XFS_IRECLAIM); spin_unlock(&ip->i_flags_lock); return true; @@ -979,6 +988,8 @@ reclaim: spin_lock(&ip->i_flags_lock); ip->i_flags = XFS_IRECLAIM; ip->i_ino = 0; + ip->i_sick = 0; + ip->i_checked = 0; spin_unlock(&ip->i_flags_lock); xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -1022,13 +1033,30 @@ out: xfs_iflags_clear(ip, XFS_IRECLAIM); } +/* Reclaim sick inodes if we're unmounting or the fs went down. */ +static inline bool +xfs_want_reclaim_sick( + struct xfs_mount *mp) +{ + return (mp->m_flags & XFS_MOUNT_UNMOUNTING) || + (mp->m_flags & XFS_MOUNT_NORECOVERY) || + XFS_FORCED_SHUTDOWN(mp); +} + void xfs_reclaim_inodes( struct xfs_mount *mp) { + struct xfs_eofblocks eofb = { + .eof_flags = 0, + }; + + if (xfs_want_reclaim_sick(mp)) + eofb.eof_flags |= XFS_ICWALK_FLAG_RECLAIM_SICK; + while (radix_tree_tagged(&mp->m_perag_tree, XFS_ICI_RECLAIM_TAG)) { xfs_ail_push_all_sync(mp->m_ail); - xfs_icwalk(mp, XFS_ICWALK_RECLAIM, NULL); + xfs_icwalk(mp, XFS_ICWALK_RECLAIM, &eofb); } } @@ -1049,6 +1077,9 @@ xfs_reclaim_inodes_nr( .icw_scan_limit = nr_to_scan, }; + if (xfs_want_reclaim_sick(mp)) + eofb.eof_flags |= XFS_ICWALK_FLAG_RECLAIM_SICK; + /* kick background reclaimer and push the AIL */ xfs_reclaim_work_queue(mp); xfs_ail_push_all(mp->m_ail); @@ -1598,7 +1629,8 @@ xfs_blockgc_free_quota( static inline bool xfs_icwalk_igrab( enum xfs_icwalk_goal goal, - struct xfs_inode *ip) + struct xfs_inode *ip, + struct xfs_eofblocks *eofb) { switch (goal) { case XFS_ICWALK_DQRELE: @@ -1606,7 +1638,7 @@ xfs_icwalk_igrab( case XFS_ICWALK_BLOCKGC: return xfs_blockgc_igrab(ip); case XFS_ICWALK_RECLAIM: - return xfs_reclaim_igrab(ip); + return xfs_reclaim_igrab(ip, eofb); default: return false; } @@ -1695,7 +1727,7 @@ restart: for (i = 0; i < nr_found; i++) { struct xfs_inode *ip = batch[i]; - if (done || !xfs_icwalk_igrab(goal, ip)) + if (done || !xfs_icwalk_igrab(goal, ip, eofb)) batch[i] = NULL; /* |