diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_alloc.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_alloc.c | 41 | 
1 files changed, 33 insertions, 8 deletions
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 7839efe050bf..000cc7f4a3ce 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -3444,16 +3444,41 @@ xfs_alloc_read_agf(  		set_bit(XFS_AGSTATE_AGF_INIT, &pag->pag_opstate);  	} +  #ifdef DEBUG -	else if (!xfs_is_shutdown(mp)) { -		ASSERT(pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks)); -		ASSERT(pag->pagf_btreeblks == be32_to_cpu(agf->agf_btreeblks)); -		ASSERT(pag->pagf_flcount == be32_to_cpu(agf->agf_flcount)); -		ASSERT(pag->pagf_longest == be32_to_cpu(agf->agf_longest)); -		ASSERT(pag->pagf_bno_level == be32_to_cpu(agf->agf_bno_level)); -		ASSERT(pag->pagf_cnt_level == be32_to_cpu(agf->agf_cnt_level)); +	/* +	 * It's possible for the AGF to be out of sync if the block device is +	 * silently dropping writes. This can happen in fstests with dmflakey +	 * enabled, which allows the buffer to be cleaned and reclaimed by +	 * memory pressure and then re-read from disk here. We will get a +	 * stale version of the AGF from disk, and nothing good can happen from +	 * here. Hence if we detect this situation, immediately shut down the +	 * filesystem. +	 * +	 * This can also happen if we are already in the middle of a forced +	 * shutdown, so don't bother checking if we are already shut down. +	 */ +	if (!xfs_is_shutdown(pag_mount(pag))) { +		bool	ok = true; + +		ok &= pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks); +		ok &= pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks); +		ok &= pag->pagf_btreeblks == be32_to_cpu(agf->agf_btreeblks); +		ok &= pag->pagf_flcount == be32_to_cpu(agf->agf_flcount); +		ok &= pag->pagf_longest == be32_to_cpu(agf->agf_longest); +		ok &= pag->pagf_bno_level == be32_to_cpu(agf->agf_bno_level); +		ok &= pag->pagf_cnt_level == be32_to_cpu(agf->agf_cnt_level); + +		if (XFS_IS_CORRUPT(pag_mount(pag), !ok)) { +			xfs_ag_mark_sick(pag, XFS_SICK_AG_AGF); +			xfs_trans_brelse(tp, agfbp); +			xfs_force_shutdown(pag_mount(pag), +					SHUTDOWN_CORRUPT_ONDISK); +			return -EFSCORRUPTED; +		}  	} -#endif +#endif /* DEBUG */ +  	if (agfbpp)  		*agfbpp = agfbp;  	else  | 
