xfs: online repair of inode unlinked state [v30.3 08/16]
This series adds some logic to the inode scrubbers so that they can detect and deal with consistency errors between the link count and the per-inode unlinked list state. The helpers needed to do this are presented here because they are a prequisite for rebuildng directories, since we need to get a rebuilt non-empty directory off the unlinked list. Note that this patchset does not provide comprehensive reconstruction of the AGI unlinked list; that is coming in a subsequent patchset. This has been running on the djcloud for months with no problems. Enjoy! Signed-off-by: Darrick J. Wong <djwong@kernel.org> -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQ2qTKExjcn+O1o2YRKO3ySh0YRpgUCZh23UwAKCRBKO3ySh0YR pt9pAQDbOMQ/9Y3Iyywkf9jTj9EvXEOpRlFPMd0F4gmtO9rhcAD/cQH9tctLcpeY DuAHqtmR3o2elpoXZrR1b+mAkS26Twc= =5oDH -----END PGP SIGNATURE----- Merge tag 'repair-unlinked-inode-state-6.10_2024-04-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.10-mergeA xfs: online repair of inode unlinked state This series adds some logic to the inode scrubbers so that they can detect and deal with consistency errors between the link count and the per-inode unlinked list state. The helpers needed to do this are presented here because they are a prequisite for rebuildng directories, since we need to get a rebuilt non-empty directory off the unlinked list. Note that this patchset does not provide comprehensive reconstruction of the AGI unlinked list; that is coming in a subsequent patchset. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Chandan Babu R <chandanbabu@kernel.org> * tag 'repair-unlinked-inode-state-6.10_2024-04-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: update the unlinked list when repairing link counts xfs: ensure unlinked list state is consistent with nlink during scrub
This commit is contained in:
commit
902603bfa1
@ -739,6 +739,23 @@ xchk_inode_check_reflink_iflag(
|
||||
xchk_ino_set_corrupt(sc, ino);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this inode has zero link count, it must be on the unlinked list. If
|
||||
* it has nonzero link count, it must not be on the unlinked list.
|
||||
*/
|
||||
STATIC void
|
||||
xchk_inode_check_unlinked(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
if (VFS_I(sc->ip)->i_nlink == 0) {
|
||||
if (!xfs_inode_on_unlinked_list(sc->ip))
|
||||
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
|
||||
} else {
|
||||
if (xfs_inode_on_unlinked_list(sc->ip))
|
||||
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scrub an inode. */
|
||||
int
|
||||
xchk_inode(
|
||||
@ -771,6 +788,8 @@ xchk_inode(
|
||||
if (S_ISREG(VFS_I(sc->ip)->i_mode))
|
||||
xchk_inode_check_reflink_iflag(sc, sc->ip->i_ino);
|
||||
|
||||
xchk_inode_check_unlinked(sc);
|
||||
|
||||
xchk_inode_xref(sc, sc->ip->i_ino, &di);
|
||||
out:
|
||||
return error;
|
||||
|
@ -1745,6 +1745,46 @@ xrep_inode_problems(
|
||||
return xrep_roll_trans(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure this inode's unlinked list pointers are consistent with its
|
||||
* link count.
|
||||
*/
|
||||
STATIC int
|
||||
xrep_inode_unlinked(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
unsigned int nlink = VFS_I(sc->ip)->i_nlink;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* If this inode is linked from the directory tree and on the unlinked
|
||||
* list, remove it from the unlinked list.
|
||||
*/
|
||||
if (nlink > 0 && xfs_inode_on_unlinked_list(sc->ip)) {
|
||||
struct xfs_perag *pag;
|
||||
int error;
|
||||
|
||||
pag = xfs_perag_get(sc->mp,
|
||||
XFS_INO_TO_AGNO(sc->mp, sc->ip->i_ino));
|
||||
error = xfs_iunlink_remove(sc->tp, pag, sc->ip);
|
||||
xfs_perag_put(pag);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this inode is not linked from the directory tree yet not on the
|
||||
* unlinked list, put it on the unlinked list.
|
||||
*/
|
||||
if (nlink == 0 && !xfs_inode_on_unlinked_list(sc->ip)) {
|
||||
error = xfs_iunlink(sc->tp, sc->ip);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Repair an inode's fields. */
|
||||
int
|
||||
xrep_inode(
|
||||
@ -1794,5 +1834,10 @@ xrep_inode(
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Reconnect incore unlinked list */
|
||||
error = xrep_inode_unlinked(sc);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return xrep_defer_finish(sc);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "xfs_iwalk.h"
|
||||
#include "xfs_ialloc.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/repair.h"
|
||||
@ -36,6 +37,20 @@
|
||||
* inode is locked.
|
||||
*/
|
||||
|
||||
/* Remove an inode from the unlinked list. */
|
||||
STATIC int
|
||||
xrep_nlinks_iunlink_remove(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
struct xfs_perag *pag;
|
||||
int error;
|
||||
|
||||
pag = xfs_perag_get(sc->mp, XFS_INO_TO_AGNO(sc->mp, sc->ip->i_ino));
|
||||
error = xfs_iunlink_remove(sc->tp, pag, sc->ip);
|
||||
xfs_perag_put(pag);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Correct the link count of the given inode. Because we have to grab locks
|
||||
* and resources in a certain order, it's possible that this will be a no-op.
|
||||
@ -99,16 +114,25 @@ xrep_nlinks_repair_inode(
|
||||
}
|
||||
|
||||
/*
|
||||
* We did not find any links to this inode. If the inode agrees, we
|
||||
* have nothing further to do. If not, the inode has a nonzero link
|
||||
* count and we don't have anywhere to graft the child onto. Dropping
|
||||
* a live inode's link count to zero can cause unexpected shutdowns in
|
||||
* inactivation, so leave it alone.
|
||||
* If this inode is linked from the directory tree and on the unlinked
|
||||
* list, remove it from the unlinked list.
|
||||
*/
|
||||
if (total_links == 0) {
|
||||
if (actual_nlink != 0)
|
||||
trace_xrep_nlinks_unfixable_inode(mp, ip, &obs);
|
||||
goto out_trans;
|
||||
if (total_links > 0 && xfs_inode_on_unlinked_list(ip)) {
|
||||
error = xrep_nlinks_iunlink_remove(sc);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this inode is not linked from the directory tree yet not on the
|
||||
* unlinked list, put it on the unlinked list.
|
||||
*/
|
||||
if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) {
|
||||
error = xfs_iunlink(sc->tp, ip);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/* Commit the new link count if it changed. */
|
||||
|
@ -42,9 +42,6 @@
|
||||
|
||||
struct kmem_cache *xfs_inode_cache;
|
||||
|
||||
STATIC int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
|
||||
struct xfs_inode *);
|
||||
|
||||
/*
|
||||
* helper function to extract extent size hint from inode
|
||||
*/
|
||||
@ -2252,7 +2249,7 @@ xfs_iunlink_remove_inode(
|
||||
/*
|
||||
* Pull the on-disk inode from the AGI unlinked list.
|
||||
*/
|
||||
STATIC int
|
||||
int
|
||||
xfs_iunlink_remove(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_perag *pag,
|
||||
|
@ -617,6 +617,8 @@ extern struct kmem_cache *xfs_inode_cache;
|
||||
bool xfs_inode_needs_inactive(struct xfs_inode *ip);
|
||||
|
||||
int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
|
||||
int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
|
||||
struct xfs_inode *ip);
|
||||
|
||||
void xfs_end_io(struct work_struct *work);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user