mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
xfs: move files to orphanage instead of letting nlinks drop to zero
If we encounter an inode with a nonzero link count but zero observed links, move it to the orphanage. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
1e58a8ccf2
commit
e6c9e75fbe
@ -4789,7 +4789,8 @@ Orphaned files are adopted by the orphanage as follows:
|
||||
cache.
|
||||
|
||||
6. Call ``xrep_adoption_finish`` to commit any filesystem updates, release the
|
||||
orphanage ILOCK, and clean the scrub transaction.
|
||||
orphanage ILOCK, and clean the scrub transaction. Call
|
||||
``xrep_adoption_commit`` to commit the updates and the scrub transaction.
|
||||
|
||||
7. If a runtime error happens, call ``xrep_adoption_cancel`` to release all
|
||||
resources.
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "scrub/xfile.h"
|
||||
#include "scrub/xfarray.h"
|
||||
#include "scrub/iscan.h"
|
||||
#include "scrub/orphanage.h"
|
||||
#include "scrub/nlinks.h"
|
||||
#include "scrub/trace.h"
|
||||
#include "scrub/readdir.h"
|
||||
@ -44,11 +45,23 @@ int
|
||||
xchk_setup_nlinks(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
struct xchk_nlink_ctrs *xnc;
|
||||
int error;
|
||||
|
||||
xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
|
||||
|
||||
sc->buf = kzalloc(sizeof(struct xchk_nlink_ctrs), XCHK_GFP_FLAGS);
|
||||
if (!sc->buf)
|
||||
if (xchk_could_repair(sc)) {
|
||||
error = xrep_setup_nlinks(sc);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
xnc = kvzalloc(sizeof(struct xchk_nlink_ctrs), XCHK_GFP_FLAGS);
|
||||
if (!xnc)
|
||||
return -ENOMEM;
|
||||
xnc->xname.name = xnc->namebuf;
|
||||
xnc->sc = sc;
|
||||
sc->buf = xnc;
|
||||
|
||||
return xchk_setup_fs(sc);
|
||||
}
|
||||
@ -873,9 +886,6 @@ xchk_nlinks_setup_scan(
|
||||
xfs_agino_t first_agino, last_agino;
|
||||
int error;
|
||||
|
||||
ASSERT(xnc->sc == NULL);
|
||||
xnc->sc = sc;
|
||||
|
||||
mutex_init(&xnc->lock);
|
||||
|
||||
/* Retry iget every tenth of a second for up to 30 seconds. */
|
||||
|
@ -28,6 +28,13 @@ struct xchk_nlink_ctrs {
|
||||
* from other writer threads.
|
||||
*/
|
||||
struct xfs_dir_hook dhook;
|
||||
|
||||
/* Orphanage reparenting request. */
|
||||
struct xrep_adoption adoption;
|
||||
|
||||
/* Directory entry name, plus the trailing null. */
|
||||
struct xfs_name xname;
|
||||
char namebuf[MAXNAMELEN];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "scrub/xfile.h"
|
||||
#include "scrub/xfarray.h"
|
||||
#include "scrub/iscan.h"
|
||||
#include "scrub/orphanage.h"
|
||||
#include "scrub/nlinks.h"
|
||||
#include "scrub/trace.h"
|
||||
#include "scrub/tempfile.h"
|
||||
@ -38,6 +39,34 @@
|
||||
* inode is locked.
|
||||
*/
|
||||
|
||||
/* Set up to repair inode link counts. */
|
||||
int
|
||||
xrep_setup_nlinks(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
return xrep_orphanage_try_create(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inodes that aren't the root directory or the orphanage, have a nonzero link
|
||||
* count, and no observed parents should be moved to the orphanage.
|
||||
*/
|
||||
static inline bool
|
||||
xrep_nlinks_is_orphaned(
|
||||
struct xfs_scrub *sc,
|
||||
struct xfs_inode *ip,
|
||||
unsigned int actual_nlink,
|
||||
const struct xchk_nlink *obs)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
if (obs->parents != 0)
|
||||
return false;
|
||||
if (ip == mp->m_rootip || ip == sc->orphanage)
|
||||
return false;
|
||||
return actual_nlink != 0;
|
||||
}
|
||||
|
||||
/* Remove an inode from the unlinked list. */
|
||||
STATIC int
|
||||
xrep_nlinks_iunlink_remove(
|
||||
@ -66,6 +95,7 @@ xrep_nlinks_repair_inode(
|
||||
struct xfs_inode *ip = sc->ip;
|
||||
uint64_t total_links;
|
||||
uint64_t actual_nlink;
|
||||
bool orphanage_available = false;
|
||||
bool dirty = false;
|
||||
int error;
|
||||
|
||||
@ -77,14 +107,41 @@ xrep_nlinks_repair_inode(
|
||||
if (xrep_is_tempfile(ip))
|
||||
return 0;
|
||||
|
||||
xchk_ilock(sc, XFS_IOLOCK_EXCL);
|
||||
/*
|
||||
* If the filesystem has an orphanage attached to the scrub context,
|
||||
* prepare for a link count repair that could involve @ip being adopted
|
||||
* by the lost+found.
|
||||
*/
|
||||
if (xrep_orphanage_can_adopt(sc)) {
|
||||
error = xrep_orphanage_iolock_two(sc);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link, 0, 0, 0, &sc->tp);
|
||||
if (error)
|
||||
return error;
|
||||
error = xrep_adoption_trans_alloc(sc, &xnc->adoption);
|
||||
if (error) {
|
||||
xchk_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
} else {
|
||||
orphanage_available = true;
|
||||
}
|
||||
}
|
||||
|
||||
xchk_ilock(sc, XFS_ILOCK_EXCL);
|
||||
xfs_trans_ijoin(sc->tp, ip, 0);
|
||||
/*
|
||||
* Either there is no orphanage or we couldn't allocate resources for
|
||||
* that kind of update. Let's try again with only the resources we
|
||||
* need for a simple link count update, since that's much more common.
|
||||
*/
|
||||
if (!orphanage_available) {
|
||||
xchk_ilock(sc, XFS_IOLOCK_EXCL);
|
||||
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link, 0, 0, 0,
|
||||
&sc->tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xchk_ilock(sc, XFS_ILOCK_EXCL);
|
||||
xfs_trans_ijoin(sc->tp, ip, 0);
|
||||
}
|
||||
|
||||
mutex_lock(&xnc->lock);
|
||||
|
||||
@ -122,6 +179,41 @@ xrep_nlinks_repair_inode(
|
||||
goto out_trans;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide if we're going to move this file to the orphanage, and fix
|
||||
* up the incore link counts if we are.
|
||||
*/
|
||||
if (orphanage_available &&
|
||||
xrep_nlinks_is_orphaned(sc, ip, actual_nlink, &obs)) {
|
||||
/* Figure out what name we're going to use here. */
|
||||
error = xrep_adoption_compute_name(&xnc->adoption, &xnc->xname);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
/*
|
||||
* Reattach this file to the directory tree by moving it to
|
||||
* the orphanage per the adoption parameters that we already
|
||||
* computed.
|
||||
*/
|
||||
error = xrep_adoption_move(&xnc->adoption);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
/*
|
||||
* Re-read the link counts since the reparenting will have
|
||||
* updated our scan info.
|
||||
*/
|
||||
mutex_lock(&xnc->lock);
|
||||
error = xfarray_load_sparse(xnc->nlinks, ip->i_ino, &obs);
|
||||
mutex_unlock(&xnc->lock);
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
total_links = xchk_nlink_total(ip, &obs);
|
||||
actual_nlink = VFS_I(ip)->i_nlink;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this inode is linked from the directory tree and on the unlinked
|
||||
* list, remove it from the unlinked list.
|
||||
@ -165,14 +257,19 @@ xrep_nlinks_repair_inode(
|
||||
xfs_trans_log_inode(sc->tp, ip, XFS_ILOG_CORE);
|
||||
|
||||
error = xrep_trans_commit(sc);
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
goto out_unlock;
|
||||
|
||||
out_scanlock:
|
||||
mutex_unlock(&xnc->lock);
|
||||
out_trans:
|
||||
xchk_trans_cancel(sc);
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
|
||||
out_unlock:
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
if (orphanage_available) {
|
||||
xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
}
|
||||
xchk_iunlock(sc, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -205,10 +302,10 @@ xrep_nlinks(
|
||||
/*
|
||||
* We need ftype for an accurate count of the number of child
|
||||
* subdirectory links. Child subdirectories with a back link (dotdot
|
||||
* entry) but no forward link are unfixable, so we cannot repair the
|
||||
* link count of the parent directory based on the back link count
|
||||
* alone. Filesystems without ftype support are rare (old V4) so we
|
||||
* just skip out here.
|
||||
* entry) but no forward link are moved to the orphanage, so we cannot
|
||||
* repair the link count of the parent directory based on the back link
|
||||
* count alone. Filesystems without ftype support are rare (old V4) so
|
||||
* we just skip out here.
|
||||
*/
|
||||
if (!xfs_has_ftype(sc->mp))
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -93,6 +93,7 @@ int xrep_setup_ag_refcountbt(struct xfs_scrub *sc);
|
||||
int xrep_setup_xattr(struct xfs_scrub *sc);
|
||||
int xrep_setup_directory(struct xfs_scrub *sc);
|
||||
int xrep_setup_parent(struct xfs_scrub *sc);
|
||||
int xrep_setup_nlinks(struct xfs_scrub *sc);
|
||||
|
||||
/* Repair setup functions */
|
||||
int xrep_setup_ag_allocbt(struct xfs_scrub *sc);
|
||||
@ -201,6 +202,7 @@ xrep_setup_nothing(
|
||||
#define xrep_setup_xattr xrep_setup_nothing
|
||||
#define xrep_setup_directory xrep_setup_nothing
|
||||
#define xrep_setup_parent xrep_setup_nothing
|
||||
#define xrep_setup_nlinks xrep_setup_nothing
|
||||
|
||||
#define xrep_setup_inode(sc, imap) ((void)0)
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "scrub/xfarray.h"
|
||||
#include "scrub/quota.h"
|
||||
#include "scrub/iscan.h"
|
||||
#include "scrub/orphanage.h"
|
||||
#include "scrub/nlinks.h"
|
||||
#include "scrub/fscounters.h"
|
||||
|
||||
|
@ -2643,6 +2643,32 @@ DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_dir_salvaged_parent);
|
||||
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_findparent_dirent);
|
||||
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_findparent_from_dcache);
|
||||
|
||||
TRACE_EVENT(xrep_nlinks_set_record,
|
||||
TP_PROTO(struct xfs_mount *mp, xfs_ino_t ino,
|
||||
const struct xchk_nlink *obs),
|
||||
TP_ARGS(mp, ino, obs),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_ino_t, ino)
|
||||
__field(xfs_nlink_t, parents)
|
||||
__field(xfs_nlink_t, backrefs)
|
||||
__field(xfs_nlink_t, children)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = mp->m_super->s_dev;
|
||||
__entry->ino = ino;
|
||||
__entry->parents = obs->parents;
|
||||
__entry->backrefs = obs->backrefs;
|
||||
__entry->children = obs->children;
|
||||
),
|
||||
TP_printk("dev %d:%d ino 0x%llx parents %u backrefs %u children %u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino,
|
||||
__entry->parents,
|
||||
__entry->backrefs,
|
||||
__entry->children)
|
||||
);
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
|
||||
|
||||
#endif /* _TRACE_XFS_SCRUB_TRACE_H */
|
||||
|
Loading…
Reference in New Issue
Block a user