@@ -146,10 +146,18 @@ xfs_nfs_get_inode(
return ERR_PTR(error);
}
- error = xfs_inode_reload_unlinked(ip);
- if (error) {
- xfs_irele(ip);
- return ERR_PTR(error);
+ /*
+ * Reload the incore unlinked list to avoid failure in inodegc.
+ * Use an unlocked check here because unrecovered unlinked inodes
+ * should be somewhat rare.
+ */
+ if (xfs_inode_unlinked_incomplete(ip)) {
+ error = xfs_inode_reload_unlinked(ip);
+ if (error) {
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+ xfs_irele(ip);
+ return ERR_PTR(error);
+ }
}
if (VFS_I(ip)->i_generation != generation) {
@@ -1744,6 +1744,14 @@ xfs_inactive(
truncate = 1;
if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) {
+ /*
+ * If this inode is being inactivated during a quotacheck and
+ * has not yet been scanned by quotacheck, we /must/ remove
+ * the dquots from the inode before inactivation changes the
+ * block and inode counts. Most probably this is a result of
+ * reloading the incore iunlinked list to purge unrecovered
+ * unlinked inodes.
+ */
xfs_qm_dqdetach(ip);
} else {
error = xfs_qm_dqattach(ip);
@@ -3657,6 +3665,16 @@ xfs_inode_reload_unlinked_bucket(
if (error)
return error;
+ /*
+ * We've taken ILOCK_SHARED and the AGI buffer lock to stabilize the
+ * incore unlinked list pointers for this inode. Check once more to
+ * see if we raced with anyone else to reload the unlinked list.
+ */
+ if (!xfs_inode_unlinked_incomplete(ip)) {
+ foundit = true;
+ goto out_agibp;
+ }
+
bucket = agino % XFS_AGI_UNLINKED_BUCKETS;
agi = agibp->b_addr;
@@ -3671,25 +3689,27 @@ xfs_inode_reload_unlinked_bucket(
while (next_agino != NULLAGINO) {
struct xfs_inode *next_ip = NULL;
+ /* Found this caller's inode, set its backlink. */
if (next_agino == agino) {
- /* Found this inode, set its backlink. */
next_ip = ip;
next_ip->i_prev_unlinked = prev_agino;
foundit = true;
+ goto next_inode;
}
- if (!next_ip) {
- /* Inode already in memory. */
- next_ip = xfs_iunlink_lookup(pag, next_agino);
- }
- if (!next_ip) {
- /* Inode not in memory, reload. */
- error = xfs_iunlink_reload_next(tp, agibp, prev_agino,
- next_agino);
- if (error)
- break;
- next_ip = xfs_iunlink_lookup(pag, next_agino);
- }
+ /* Try in-memory lookup first. */
+ next_ip = xfs_iunlink_lookup(pag, next_agino);
+ if (next_ip)
+ goto next_inode;
+
+ /* Inode not in memory, try reloading it. */
+ error = xfs_iunlink_reload_next(tp, agibp, prev_agino,
+ next_agino);
+ if (error)
+ break;
+
+ /* Grab the reloaded inode. */
+ next_ip = xfs_iunlink_lookup(pag, next_agino);
if (!next_ip) {
/* No incore inode at all? We reloaded it... */
ASSERT(next_ip != NULL);
@@ -3697,10 +3717,12 @@ xfs_inode_reload_unlinked_bucket(
break;
}
+next_inode:
prev_agino = next_agino;
next_agino = next_ip->i_next_unlinked;
}
+out_agibp:
xfs_trans_brelse(tp, agibp);
/* Should have found this inode somewhere in the iunlinked bucket. */
if (!error && !foundit)
@@ -80,10 +80,12 @@ xfs_bulkstat_one_int(
if (error)
goto out;
+ /* Reload the incore unlinked list to avoid failure in inodegc. */
if (xfs_inode_unlinked_incomplete(ip)) {
error = xfs_inode_reload_unlinked_bucket(tp, ip);
if (error) {
xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
xfs_irele(ip);
return error;
}
@@ -1160,9 +1160,18 @@ xfs_qm_dqusage_adjust(
if (error)
return error;
- error = xfs_inode_reload_unlinked(ip);
- if (error)
- goto error0;
+ /*
+ * Reload the incore unlinked list to avoid failure in inodegc.
+ * Use an unlocked check here because unrecovered unlinked inodes
+ * should be somewhat rare.
+ */
+ if (xfs_inode_unlinked_incomplete(ip)) {
+ error = xfs_inode_reload_unlinked(ip);
+ if (error) {
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+ goto error0;
+ }
+ }
ASSERT(ip->i_delayed_blks == 0);