mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
xfs: reload entire unlinked bucket lists
The previous patch to reload unrecovered unlinked inodes when adding a newly created inode to the unlinked list is missing a key piece of functionality. It doesn't handle the case that someone calls xfs_iget on an inode that is not the last item in the incore list. For example, if at mount time the ondisk iunlink bucket looks like this: AGI -> 7 -> 22 -> 3 -> NULL None of these three inodes are cached in memory. Now let's say that someone tries to open inode 3 by handle. We need to walk the list to make sure that inodes 7 and 22 get loaded cold, and that the i_prev_unlinked of inode 3 gets set to 22. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
This commit is contained in:
parent
f12b96683d
commit
83771c50e4
@ -146,6 +146,12 @@ xfs_nfs_get_inode(
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
error = xfs_inode_reload_unlinked(ip);
|
||||
if (error) {
|
||||
xfs_irele(ip);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
if (VFS_I(ip)->i_generation != generation) {
|
||||
xfs_irele(ip);
|
||||
return ERR_PTR(-ESTALE);
|
||||
|
@ -3606,3 +3606,103 @@ xfs_iunlock2_io_mmap(
|
||||
if (ip1 != ip2)
|
||||
inode_unlock(VFS_I(ip1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reload the incore inode list for this inode. Caller should ensure that
|
||||
* the link count cannot change, either by taking ILOCK_SHARED or otherwise
|
||||
* preventing other threads from executing.
|
||||
*/
|
||||
int
|
||||
xfs_inode_reload_unlinked_bucket(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode *ip)
|
||||
{
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
struct xfs_buf *agibp;
|
||||
struct xfs_agi *agi;
|
||||
struct xfs_perag *pag;
|
||||
xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
|
||||
xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
|
||||
xfs_agino_t prev_agino, next_agino;
|
||||
unsigned int bucket;
|
||||
bool foundit = false;
|
||||
int error;
|
||||
|
||||
/* Grab the first inode in the list */
|
||||
pag = xfs_perag_get(mp, agno);
|
||||
error = xfs_ialloc_read_agi(pag, tp, &agibp);
|
||||
xfs_perag_put(pag);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
bucket = agino % XFS_AGI_UNLINKED_BUCKETS;
|
||||
agi = agibp->b_addr;
|
||||
|
||||
trace_xfs_inode_reload_unlinked_bucket(ip);
|
||||
|
||||
xfs_info_ratelimited(mp,
|
||||
"Found unrecovered unlinked inode 0x%x in AG 0x%x. Initiating list recovery.",
|
||||
agino, agno);
|
||||
|
||||
prev_agino = NULLAGINO;
|
||||
next_agino = be32_to_cpu(agi->agi_unlinked[bucket]);
|
||||
while (next_agino != NULLAGINO) {
|
||||
struct xfs_inode *next_ip = NULL;
|
||||
|
||||
if (next_agino == agino) {
|
||||
/* Found this inode, set its backlink. */
|
||||
next_ip = ip;
|
||||
next_ip->i_prev_unlinked = prev_agino;
|
||||
foundit = true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (!next_ip) {
|
||||
/* No incore inode at all? We reloaded it... */
|
||||
ASSERT(next_ip != NULL);
|
||||
error = -EFSCORRUPTED;
|
||||
break;
|
||||
}
|
||||
|
||||
prev_agino = next_agino;
|
||||
next_agino = next_ip->i_next_unlinked;
|
||||
}
|
||||
|
||||
xfs_trans_brelse(tp, agibp);
|
||||
/* Should have found this inode somewhere in the iunlinked bucket. */
|
||||
if (!error && !foundit)
|
||||
error = -EFSCORRUPTED;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Decide if this inode is missing its unlinked list and reload it. */
|
||||
int
|
||||
xfs_inode_reload_unlinked(
|
||||
struct xfs_inode *ip)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc_empty(ip->i_mount, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
||||
if (xfs_inode_unlinked_incomplete(ip))
|
||||
error = xfs_inode_reload_unlinked_bucket(tp, ip);
|
||||
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
||||
xfs_trans_cancel(tp);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -593,4 +593,13 @@ void xfs_end_io(struct work_struct *work);
|
||||
int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
|
||||
void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
|
||||
|
||||
static inline bool
|
||||
xfs_inode_unlinked_incomplete(
|
||||
struct xfs_inode *ip)
|
||||
{
|
||||
return VFS_I(ip)->i_nlink == 0 && !xfs_inode_on_unlinked_list(ip);
|
||||
}
|
||||
int xfs_inode_reload_unlinked_bucket(struct xfs_trans *tp, struct xfs_inode *ip);
|
||||
int xfs_inode_reload_unlinked(struct xfs_inode *ip);
|
||||
|
||||
#endif /* __XFS_INODE_H__ */
|
||||
|
@ -80,6 +80,15 @@ xfs_bulkstat_one_int(
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (xfs_inode_unlinked_incomplete(ip)) {
|
||||
error = xfs_inode_reload_unlinked_bucket(tp, ip);
|
||||
if (error) {
|
||||
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
||||
xfs_irele(ip);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(ip != NULL);
|
||||
ASSERT(ip->i_imap.im_blkno != 0);
|
||||
inode = VFS_I(ip);
|
||||
|
@ -3849,6 +3849,26 @@ TRACE_EVENT(xfs_iunlink_reload_next,
|
||||
__entry->next_agino)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xfs_inode_reload_unlinked_bucket,
|
||||
TP_PROTO(struct xfs_inode *ip),
|
||||
TP_ARGS(ip),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_agnumber_t, agno)
|
||||
__field(xfs_agino_t, agino)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = ip->i_mount->m_super->s_dev;
|
||||
__entry->agno = XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino);
|
||||
__entry->agino = XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino);
|
||||
),
|
||||
TP_printk("dev %d:%d agno 0x%x agino 0x%x bucket %u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->agno,
|
||||
__entry->agino,
|
||||
__entry->agino % XFS_AGI_UNLINKED_BUCKETS)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xfs_ag_inode_class,
|
||||
TP_PROTO(struct xfs_inode *ip),
|
||||
TP_ARGS(ip),
|
||||
|
Loading…
Reference in New Issue
Block a user