Skip to content

Commit a83d5a8

Browse files
dgchinnerdchinner
authored andcommitted
xfs: introduce xfs_iunlink_lookup
When an inode is on an unlinked list during normal operation, it is guaranteed to be pinned in memory as it is either referenced by the current unlink operation or it has a open file descriptor that references it and has it pinned in memory. Hence to look up an inode on the unlinked list, we can do a direct inode cache lookup and always expect the lookup to succeed. Add a function to do this lookup based on the agino that we use to link the chain of unlinked inodes together so we can begin the conversion the unlinked list manipulations to use in-memory inodes rather than inode cluster buffers and remove the backref cache. Use this lookup function to replace the on-disk inode buffer walk when removing inodes from the unlinked list with an in-core inode unlinked list walk. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
1 parent 04755d2 commit a83d5a8

2 files changed

Lines changed: 66 additions & 96 deletions

File tree

fs/xfs/xfs_inode.c

Lines changed: 66 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,35 @@ xfs_iunlink_destroy(
19921992
ASSERT(freed_anything == false || xfs_is_shutdown(pag->pag_mount));
19931993
}
19941994

1995+
/*
1996+
* Find an inode on the unlinked list. This does not take references to the
1997+
* inode as we have existence guarantees by holding the AGI buffer lock and that
1998+
* only unlinked, referenced inodes can be on the unlinked inode list. If we
1999+
* don't find the inode in cache, then let the caller handle the situation.
2000+
*/
2001+
static struct xfs_inode *
2002+
xfs_iunlink_lookup(
2003+
struct xfs_perag *pag,
2004+
xfs_agino_t agino)
2005+
{
2006+
struct xfs_inode *ip;
2007+
2008+
rcu_read_lock();
2009+
ip = radix_tree_lookup(&pag->pag_ici_root, agino);
2010+
2011+
/*
2012+
* Inode not in memory or in RCU freeing limbo should not happen.
2013+
* Warn about this and let the caller handle the failure.
2014+
*/
2015+
if (WARN_ON_ONCE(!ip || !ip->i_ino)) {
2016+
rcu_read_unlock();
2017+
return NULL;
2018+
}
2019+
ASSERT(!xfs_iflags_test(ip, XFS_IRECLAIMABLE | XFS_IRECLAIM));
2020+
rcu_read_unlock();
2021+
return ip;
2022+
}
2023+
19952024
/*
19962025
* Point the AGI unlinked bucket at an inode and log the results. The caller
19972026
* is responsible for validating the old value.
@@ -2097,7 +2126,8 @@ xfs_iunlink_update_inode(
20972126
* current pointer is the same as the new value, unless we're
20982127
* terminating the list.
20992128
*/
2100-
*old_next_agino = old_value;
2129+
if (old_next_agino)
2130+
*old_next_agino = old_value;
21012131
if (old_value == next_agino) {
21022132
if (next_agino != NULLAGINO) {
21032133
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__,
@@ -2203,38 +2233,6 @@ xfs_iunlink(
22032233
return error;
22042234
}
22052235

2206-
/* Return the imap, dinode pointer, and buffer for an inode. */
2207-
STATIC int
2208-
xfs_iunlink_map_ino(
2209-
struct xfs_trans *tp,
2210-
xfs_agnumber_t agno,
2211-
xfs_agino_t agino,
2212-
struct xfs_imap *imap,
2213-
struct xfs_dinode **dipp,
2214-
struct xfs_buf **bpp)
2215-
{
2216-
struct xfs_mount *mp = tp->t_mountp;
2217-
int error;
2218-
2219-
imap->im_blkno = 0;
2220-
error = xfs_imap(mp, tp, XFS_AGINO_TO_INO(mp, agno, agino), imap, 0);
2221-
if (error) {
2222-
xfs_warn(mp, "%s: xfs_imap returned error %d.",
2223-
__func__, error);
2224-
return error;
2225-
}
2226-
2227-
error = xfs_imap_to_bp(mp, tp, imap, bpp);
2228-
if (error) {
2229-
xfs_warn(mp, "%s: xfs_imap_to_bp returned error %d.",
2230-
__func__, error);
2231-
return error;
2232-
}
2233-
2234-
*dipp = xfs_buf_offset(*bpp, imap->im_boffset);
2235-
return 0;
2236-
}
2237-
22382236
/*
22392237
* Walk the unlinked chain from @head_agino until we find the inode that
22402238
* points to @target_agino. Return the inode number, map, dinode pointer,
@@ -2245,77 +2243,49 @@ xfs_iunlink_map_ino(
22452243
*
22462244
* Do not call this function if @target_agino is the head of the list.
22472245
*/
2248-
STATIC int
2249-
xfs_iunlink_map_prev(
2250-
struct xfs_trans *tp,
2246+
static int
2247+
xfs_iunlink_lookup_prev(
22512248
struct xfs_perag *pag,
22522249
xfs_agino_t head_agino,
22532250
xfs_agino_t target_agino,
2254-
xfs_agino_t *agino,
2255-
struct xfs_imap *imap,
2256-
struct xfs_dinode **dipp,
2257-
struct xfs_buf **bpp)
2251+
struct xfs_inode **ipp)
22582252
{
2259-
struct xfs_mount *mp = tp->t_mountp;
2253+
struct xfs_inode *ip;
22602254
xfs_agino_t next_agino;
2261-
int error;
2262-
2263-
ASSERT(head_agino != target_agino);
2264-
*bpp = NULL;
22652255

2266-
/* See if our backref cache can find it faster. */
2267-
*agino = xfs_iunlink_lookup_backref(pag, target_agino);
2268-
if (*agino != NULLAGINO) {
2269-
error = xfs_iunlink_map_ino(tp, pag->pag_agno, *agino, imap,
2270-
dipp, bpp);
2271-
if (error)
2272-
return error;
2256+
*ipp = NULL;
22732257

2274-
if (be32_to_cpu((*dipp)->di_next_unlinked) == target_agino)
2258+
next_agino = xfs_iunlink_lookup_backref(pag, target_agino);
2259+
if (next_agino != NULLAGINO) {
2260+
ip = xfs_iunlink_lookup(pag, next_agino);
2261+
if (ip && ip->i_next_unlinked == target_agino) {
2262+
*ipp = ip;
22752263
return 0;
2276-
2277-
/*
2278-
* If we get here the cache contents were corrupt, so drop the
2279-
* buffer and fall back to walking the bucket list.
2280-
*/
2281-
xfs_trans_brelse(tp, *bpp);
2282-
*bpp = NULL;
2283-
WARN_ON_ONCE(1);
2264+
}
22842265
}
22852266

2286-
trace_xfs_iunlink_map_prev_fallback(mp, pag->pag_agno);
2287-
22882267
/* Otherwise, walk the entire bucket until we find it. */
22892268
next_agino = head_agino;
2290-
while (next_agino != target_agino) {
2291-
xfs_agino_t unlinked_agino;
2292-
2293-
if (*bpp)
2294-
xfs_trans_brelse(tp, *bpp);
2269+
while (next_agino != NULLAGINO) {
2270+
ip = xfs_iunlink_lookup(pag, next_agino);
2271+
if (!ip)
2272+
return -EFSCORRUPTED;
22952273

2296-
*agino = next_agino;
2297-
error = xfs_iunlink_map_ino(tp, pag->pag_agno, next_agino, imap,
2298-
dipp, bpp);
2299-
if (error)
2300-
return error;
2301-
2302-
unlinked_agino = be32_to_cpu((*dipp)->di_next_unlinked);
23032274
/*
23042275
* Make sure this pointer is valid and isn't an obvious
23052276
* infinite loop.
23062277
*/
2307-
if (!xfs_verify_agino(pag, unlinked_agino) ||
2308-
next_agino == unlinked_agino) {
2309-
XFS_CORRUPTION_ERROR(__func__,
2310-
XFS_ERRLEVEL_LOW, mp,
2311-
*dipp, sizeof(**dipp));
2312-
error = -EFSCORRUPTED;
2313-
return error;
2278+
if (!xfs_verify_agino(pag, ip->i_next_unlinked) ||
2279+
next_agino == ip->i_next_unlinked)
2280+
return -EFSCORRUPTED;
2281+
2282+
if (ip->i_next_unlinked == target_agino) {
2283+
*ipp = ip;
2284+
return 0;
23142285
}
2315-
next_agino = unlinked_agino;
2286+
next_agino = ip->i_next_unlinked;
23162287
}
2317-
2318-
return 0;
2288+
return -EFSCORRUPTED;
23192289
}
23202290

23212291
static int
@@ -2327,8 +2297,6 @@ xfs_iunlink_remove_inode(
23272297
{
23282298
struct xfs_mount *mp = tp->t_mountp;
23292299
struct xfs_agi *agi = agibp->b_addr;
2330-
struct xfs_buf *last_ibp;
2331-
struct xfs_dinode *last_dip = NULL;
23322300
xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
23332301
xfs_agino_t next_agino;
23342302
xfs_agino_t head_agino;
@@ -2356,7 +2324,6 @@ xfs_iunlink_remove_inode(
23562324
error = xfs_iunlink_update_inode(tp, ip, pag, NULLAGINO, &next_agino);
23572325
if (error)
23582326
return error;
2359-
ip->i_next_unlinked = NULLAGINO;
23602327

23612328
/*
23622329
* If there was a backref pointing from the next inode back to this
@@ -2372,18 +2339,21 @@ xfs_iunlink_remove_inode(
23722339
}
23732340

23742341
if (head_agino != agino) {
2375-
struct xfs_imap imap;
2376-
xfs_agino_t prev_agino;
2342+
struct xfs_inode *prev_ip;
23772343

2378-
/* We need to search the list for the inode being freed. */
2379-
error = xfs_iunlink_map_prev(tp, pag, head_agino, agino,
2380-
&prev_agino, &imap, &last_dip, &last_ibp);
2344+
error = xfs_iunlink_lookup_prev(pag, head_agino, agino,
2345+
&prev_ip);
23812346
if (error)
23822347
return error;
23832348

23842349
/* Point the previous inode on the list to the next inode. */
2385-
xfs_iunlink_update_dinode(tp, pag, prev_agino, last_ibp,
2386-
last_dip, &imap, next_agino);
2350+
error = xfs_iunlink_update_inode(tp, prev_ip, pag, next_agino,
2351+
NULL);
2352+
if (error)
2353+
return error;
2354+
2355+
prev_ip->i_next_unlinked = ip->i_next_unlinked;
2356+
ip->i_next_unlinked = NULLAGINO;
23872357

23882358
/*
23892359
* Now we deal with the backref for this inode. If this inode
@@ -2398,6 +2368,7 @@ xfs_iunlink_remove_inode(
23982368
}
23992369

24002370
/* Point the head of the list to the next unlinked inode. */
2371+
ip->i_next_unlinked = NULLAGINO;
24012372
return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index,
24022373
next_agino);
24032374
}

fs/xfs/xfs_trace.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3672,7 +3672,6 @@ DEFINE_EVENT(xfs_ag_inode_class, name, \
36723672
TP_ARGS(ip))
36733673
DEFINE_AGINODE_EVENT(xfs_iunlink);
36743674
DEFINE_AGINODE_EVENT(xfs_iunlink_remove);
3675-
DEFINE_AG_EVENT(xfs_iunlink_map_prev_fallback);
36763675

36773676
DECLARE_EVENT_CLASS(xfs_fs_corrupt_class,
36783677
TP_PROTO(struct xfs_mount *mp, unsigned int flags),

0 commit comments

Comments
 (0)