Skip to content

Commit 83771c5

Browse files
author
Darrick J. Wong
committed
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>
1 parent f12b966 commit 83771c5

File tree

5 files changed

+144
-0
lines changed

5 files changed

+144
-0
lines changed

fs/xfs/xfs_export.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ xfs_nfs_get_inode(
146146
return ERR_PTR(error);
147147
}
148148

149+
error = xfs_inode_reload_unlinked(ip);
150+
if (error) {
151+
xfs_irele(ip);
152+
return ERR_PTR(error);
153+
}
154+
149155
if (VFS_I(ip)->i_generation != generation) {
150156
xfs_irele(ip);
151157
return ERR_PTR(-ESTALE);

fs/xfs/xfs_inode.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3606,3 +3606,103 @@ xfs_iunlock2_io_mmap(
36063606
if (ip1 != ip2)
36073607
inode_unlock(VFS_I(ip1));
36083608
}
3609+
3610+
/*
3611+
* Reload the incore inode list for this inode. Caller should ensure that
3612+
* the link count cannot change, either by taking ILOCK_SHARED or otherwise
3613+
* preventing other threads from executing.
3614+
*/
3615+
int
3616+
xfs_inode_reload_unlinked_bucket(
3617+
struct xfs_trans *tp,
3618+
struct xfs_inode *ip)
3619+
{
3620+
struct xfs_mount *mp = tp->t_mountp;
3621+
struct xfs_buf *agibp;
3622+
struct xfs_agi *agi;
3623+
struct xfs_perag *pag;
3624+
xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
3625+
xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
3626+
xfs_agino_t prev_agino, next_agino;
3627+
unsigned int bucket;
3628+
bool foundit = false;
3629+
int error;
3630+
3631+
/* Grab the first inode in the list */
3632+
pag = xfs_perag_get(mp, agno);
3633+
error = xfs_ialloc_read_agi(pag, tp, &agibp);
3634+
xfs_perag_put(pag);
3635+
if (error)
3636+
return error;
3637+
3638+
bucket = agino % XFS_AGI_UNLINKED_BUCKETS;
3639+
agi = agibp->b_addr;
3640+
3641+
trace_xfs_inode_reload_unlinked_bucket(ip);
3642+
3643+
xfs_info_ratelimited(mp,
3644+
"Found unrecovered unlinked inode 0x%x in AG 0x%x. Initiating list recovery.",
3645+
agino, agno);
3646+
3647+
prev_agino = NULLAGINO;
3648+
next_agino = be32_to_cpu(agi->agi_unlinked[bucket]);
3649+
while (next_agino != NULLAGINO) {
3650+
struct xfs_inode *next_ip = NULL;
3651+
3652+
if (next_agino == agino) {
3653+
/* Found this inode, set its backlink. */
3654+
next_ip = ip;
3655+
next_ip->i_prev_unlinked = prev_agino;
3656+
foundit = true;
3657+
}
3658+
if (!next_ip) {
3659+
/* Inode already in memory. */
3660+
next_ip = xfs_iunlink_lookup(pag, next_agino);
3661+
}
3662+
if (!next_ip) {
3663+
/* Inode not in memory, reload. */
3664+
error = xfs_iunlink_reload_next(tp, agibp, prev_agino,
3665+
next_agino);
3666+
if (error)
3667+
break;
3668+
3669+
next_ip = xfs_iunlink_lookup(pag, next_agino);
3670+
}
3671+
if (!next_ip) {
3672+
/* No incore inode at all? We reloaded it... */
3673+
ASSERT(next_ip != NULL);
3674+
error = -EFSCORRUPTED;
3675+
break;
3676+
}
3677+
3678+
prev_agino = next_agino;
3679+
next_agino = next_ip->i_next_unlinked;
3680+
}
3681+
3682+
xfs_trans_brelse(tp, agibp);
3683+
/* Should have found this inode somewhere in the iunlinked bucket. */
3684+
if (!error && !foundit)
3685+
error = -EFSCORRUPTED;
3686+
return error;
3687+
}
3688+
3689+
/* Decide if this inode is missing its unlinked list and reload it. */
3690+
int
3691+
xfs_inode_reload_unlinked(
3692+
struct xfs_inode *ip)
3693+
{
3694+
struct xfs_trans *tp;
3695+
int error;
3696+
3697+
error = xfs_trans_alloc_empty(ip->i_mount, &tp);
3698+
if (error)
3699+
return error;
3700+
3701+
xfs_ilock(ip, XFS_ILOCK_SHARED);
3702+
if (xfs_inode_unlinked_incomplete(ip))
3703+
error = xfs_inode_reload_unlinked_bucket(tp, ip);
3704+
xfs_iunlock(ip, XFS_ILOCK_SHARED);
3705+
xfs_trans_cancel(tp);
3706+
3707+
return error;
3708+
}

fs/xfs/xfs_inode.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,4 +593,13 @@ void xfs_end_io(struct work_struct *work);
593593
int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
594594
void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
595595

596+
static inline bool
597+
xfs_inode_unlinked_incomplete(
598+
struct xfs_inode *ip)
599+
{
600+
return VFS_I(ip)->i_nlink == 0 && !xfs_inode_on_unlinked_list(ip);
601+
}
602+
int xfs_inode_reload_unlinked_bucket(struct xfs_trans *tp, struct xfs_inode *ip);
603+
int xfs_inode_reload_unlinked(struct xfs_inode *ip);
604+
596605
#endif /* __XFS_INODE_H__ */

fs/xfs/xfs_itable.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ xfs_bulkstat_one_int(
8080
if (error)
8181
goto out;
8282

83+
if (xfs_inode_unlinked_incomplete(ip)) {
84+
error = xfs_inode_reload_unlinked_bucket(tp, ip);
85+
if (error) {
86+
xfs_iunlock(ip, XFS_ILOCK_SHARED);
87+
xfs_irele(ip);
88+
return error;
89+
}
90+
}
91+
8392
ASSERT(ip != NULL);
8493
ASSERT(ip->i_imap.im_blkno != 0);
8594
inode = VFS_I(ip);

fs/xfs/xfs_trace.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3849,6 +3849,26 @@ TRACE_EVENT(xfs_iunlink_reload_next,
38493849
__entry->next_agino)
38503850
);
38513851

3852+
TRACE_EVENT(xfs_inode_reload_unlinked_bucket,
3853+
TP_PROTO(struct xfs_inode *ip),
3854+
TP_ARGS(ip),
3855+
TP_STRUCT__entry(
3856+
__field(dev_t, dev)
3857+
__field(xfs_agnumber_t, agno)
3858+
__field(xfs_agino_t, agino)
3859+
),
3860+
TP_fast_assign(
3861+
__entry->dev = ip->i_mount->m_super->s_dev;
3862+
__entry->agno = XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino);
3863+
__entry->agino = XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino);
3864+
),
3865+
TP_printk("dev %d:%d agno 0x%x agino 0x%x bucket %u",
3866+
MAJOR(__entry->dev), MINOR(__entry->dev),
3867+
__entry->agno,
3868+
__entry->agino,
3869+
__entry->agino % XFS_AGI_UNLINKED_BUCKETS)
3870+
);
3871+
38523872
DECLARE_EVENT_CLASS(xfs_ag_inode_class,
38533873
TP_PROTO(struct xfs_inode *ip),
38543874
TP_ARGS(ip),

0 commit comments

Comments
 (0)