Skip to content

Commit d4d12c0

Browse files
Dave Chinnerdchinner
authored andcommitted
xfs: collect errors from inodegc for unlinked inode recovery
Unlinked list recovery requires errors removing the inode the from the unlinked list get fed back to the main recovery loop. Now that we offload the unlinking to the inodegc work, we don't get errors being fed back when we trip over a corruption that prevents the inode from being removed from the unlinked list. This means we never clear the corrupt unlinked list bucket, resulting in runtime operations eventually tripping over it and shutting down. Fix this by collecting inodegc worker errors and feed them back to the flush caller. This is largely best effort - the only context that really cares is log recovery, and it only flushes a single inode at a time so we don't need complex synchronised handling. Essentially the inodegc workers will capture the first error that occurs and the next flush will gather them and clear them. The flush itself will only report the first gathered error. In the cases where callers can return errors, propagate the collected inodegc flush error up the error handling chain. In the case of inode unlinked list recovery, there are several superfluous calls to flush queued unlinked inodes - xlog_recover_iunlink_bucket() guarantees that it has flushed the inodegc and collected errors before it returns. Hence nothing in the calling path needs to run a flush, even when an error is returned. Signed-off-by: Dave Chinner <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Dave Chinner <[email protected]>
1 parent 7dfee17 commit d4d12c0

File tree

8 files changed

+60
-37
lines changed

8 files changed

+60
-37
lines changed

fs/xfs/xfs_icache.c

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,27 @@ xfs_inodegc_queue_all(
454454
return ret;
455455
}
456456

457+
/* Wait for all queued work and collect errors */
458+
static int
459+
xfs_inodegc_wait_all(
460+
struct xfs_mount *mp)
461+
{
462+
int cpu;
463+
int error = 0;
464+
465+
flush_workqueue(mp->m_inodegc_wq);
466+
for_each_online_cpu(cpu) {
467+
struct xfs_inodegc *gc;
468+
469+
gc = per_cpu_ptr(mp->m_inodegc, cpu);
470+
if (gc->error && !error)
471+
error = gc->error;
472+
gc->error = 0;
473+
}
474+
475+
return error;
476+
}
477+
457478
/*
458479
* Check the validity of the inode we just found it the cache
459480
*/
@@ -1491,15 +1512,14 @@ xfs_blockgc_free_space(
14911512
if (error)
14921513
return error;
14931514

1494-
xfs_inodegc_flush(mp);
1495-
return 0;
1515+
return xfs_inodegc_flush(mp);
14961516
}
14971517

14981518
/*
14991519
* Reclaim all the free space that we can by scheduling the background blockgc
15001520
* and inodegc workers immediately and waiting for them all to clear.
15011521
*/
1502-
void
1522+
int
15031523
xfs_blockgc_flush_all(
15041524
struct xfs_mount *mp)
15051525
{
@@ -1520,7 +1540,7 @@ xfs_blockgc_flush_all(
15201540
for_each_perag_tag(mp, agno, pag, XFS_ICI_BLOCKGC_TAG)
15211541
flush_delayed_work(&pag->pag_blockgc_work);
15221542

1523-
xfs_inodegc_flush(mp);
1543+
return xfs_inodegc_flush(mp);
15241544
}
15251545

15261546
/*
@@ -1842,13 +1862,17 @@ xfs_inodegc_set_reclaimable(
18421862
* This is the last chance to make changes to an otherwise unreferenced file
18431863
* before incore reclamation happens.
18441864
*/
1845-
static void
1865+
static int
18461866
xfs_inodegc_inactivate(
18471867
struct xfs_inode *ip)
18481868
{
1869+
int error;
1870+
18491871
trace_xfs_inode_inactivating(ip);
1850-
xfs_inactive(ip);
1872+
error = xfs_inactive(ip);
18511873
xfs_inodegc_set_reclaimable(ip);
1874+
return error;
1875+
18521876
}
18531877

18541878
void
@@ -1880,8 +1904,12 @@ xfs_inodegc_worker(
18801904

18811905
WRITE_ONCE(gc->shrinker_hits, 0);
18821906
llist_for_each_entry_safe(ip, n, node, i_gclist) {
1907+
int error;
1908+
18831909
xfs_iflags_set(ip, XFS_INACTIVATING);
1884-
xfs_inodegc_inactivate(ip);
1910+
error = xfs_inodegc_inactivate(ip);
1911+
if (error && !gc->error)
1912+
gc->error = error;
18851913
}
18861914

18871915
memalloc_nofs_restore(nofs_flag);
@@ -1905,13 +1933,13 @@ xfs_inodegc_push(
19051933
* Force all currently queued inode inactivation work to run immediately and
19061934
* wait for the work to finish.
19071935
*/
1908-
void
1936+
int
19091937
xfs_inodegc_flush(
19101938
struct xfs_mount *mp)
19111939
{
19121940
xfs_inodegc_push(mp);
19131941
trace_xfs_inodegc_flush(mp, __return_address);
1914-
flush_workqueue(mp->m_inodegc_wq);
1942+
return xfs_inodegc_wait_all(mp);
19151943
}
19161944

19171945
/*

fs/xfs/xfs_icache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ int xfs_blockgc_free_dquots(struct xfs_mount *mp, struct xfs_dquot *udqp,
6262
unsigned int iwalk_flags);
6363
int xfs_blockgc_free_quota(struct xfs_inode *ip, unsigned int iwalk_flags);
6464
int xfs_blockgc_free_space(struct xfs_mount *mp, struct xfs_icwalk *icm);
65-
void xfs_blockgc_flush_all(struct xfs_mount *mp);
65+
int xfs_blockgc_flush_all(struct xfs_mount *mp);
6666

6767
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
6868
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
@@ -80,7 +80,7 @@ void xfs_blockgc_start(struct xfs_mount *mp);
8080

8181
void xfs_inodegc_worker(struct work_struct *work);
8282
void xfs_inodegc_push(struct xfs_mount *mp);
83-
void xfs_inodegc_flush(struct xfs_mount *mp);
83+
int xfs_inodegc_flush(struct xfs_mount *mp);
8484
void xfs_inodegc_stop(struct xfs_mount *mp);
8585
void xfs_inodegc_start(struct xfs_mount *mp);
8686
void xfs_inodegc_cpu_dead(struct xfs_mount *mp, unsigned int cpu);

fs/xfs/xfs_inode.c

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,16 +1620,7 @@ xfs_inactive_ifree(
16201620
*/
16211621
xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_ICOUNT, -1);
16221622

1623-
/*
1624-
* Just ignore errors at this point. There is nothing we can do except
1625-
* to try to keep going. Make sure it's not a silent error.
1626-
*/
1627-
error = xfs_trans_commit(tp);
1628-
if (error)
1629-
xfs_notice(mp, "%s: xfs_trans_commit returned error %d",
1630-
__func__, error);
1631-
1632-
return 0;
1623+
return xfs_trans_commit(tp);
16331624
}
16341625

16351626
/*
@@ -1693,12 +1684,12 @@ xfs_inode_needs_inactive(
16931684
* now be truncated. Also, we clear all of the read-ahead state
16941685
* kept for the inode here since the file is now closed.
16951686
*/
1696-
void
1687+
int
16971688
xfs_inactive(
16981689
xfs_inode_t *ip)
16991690
{
17001691
struct xfs_mount *mp;
1701-
int error;
1692+
int error = 0;
17021693
int truncate = 0;
17031694

17041695
/*
@@ -1736,7 +1727,7 @@ xfs_inactive(
17361727
* reference to the inode at this point anyways.
17371728
*/
17381729
if (xfs_can_free_eofblocks(ip, true))
1739-
xfs_free_eofblocks(ip);
1730+
error = xfs_free_eofblocks(ip);
17401731

17411732
goto out;
17421733
}
@@ -1773,14 +1764,15 @@ xfs_inactive(
17731764
/*
17741765
* Free the inode.
17751766
*/
1776-
xfs_inactive_ifree(ip);
1767+
error = xfs_inactive_ifree(ip);
17771768

17781769
out:
17791770
/*
17801771
* We're done making metadata updates for this inode, so we can release
17811772
* the attached dquots.
17821773
*/
17831774
xfs_qm_dqdetach(ip);
1775+
return error;
17841776
}
17851777

17861778
/*

fs/xfs/xfs_inode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ enum layout_break_reason {
470470
(xfs_has_grpid((pip)->i_mount) || (VFS_I(pip)->i_mode & S_ISGID))
471471

472472
int xfs_release(struct xfs_inode *ip);
473-
void xfs_inactive(struct xfs_inode *ip);
473+
int xfs_inactive(struct xfs_inode *ip);
474474
int xfs_lookup(struct xfs_inode *dp, const struct xfs_name *name,
475475
struct xfs_inode **ipp, struct xfs_name *ci_name);
476476
int xfs_create(struct mnt_idmap *idmap,

fs/xfs/xfs_log_recover.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,18 +2711,25 @@ xlog_recover_iunlink_bucket(
27112711
* just to flush the inodegc queue and wait for it to
27122712
* complete.
27132713
*/
2714-
xfs_inodegc_flush(mp);
2714+
error = xfs_inodegc_flush(mp);
2715+
if (error)
2716+
break;
27152717
}
27162718

27172719
prev_agino = agino;
27182720
prev_ip = ip;
27192721
}
27202722

27212723
if (prev_ip) {
2724+
int error2;
2725+
27222726
ip->i_prev_unlinked = prev_agino;
27232727
xfs_irele(prev_ip);
2728+
2729+
error2 = xfs_inodegc_flush(mp);
2730+
if (error2 && !error)
2731+
return error2;
27242732
}
2725-
xfs_inodegc_flush(mp);
27262733
return error;
27272734
}
27282735

@@ -2789,7 +2796,6 @@ xlog_recover_iunlink_ag(
27892796
* bucket and remaining inodes on it unreferenced and
27902797
* unfreeable.
27912798
*/
2792-
xfs_inodegc_flush(pag->pag_mount);
27932799
xlog_recover_clear_agi_bucket(pag, bucket);
27942800
}
27952801
}
@@ -2806,13 +2812,6 @@ xlog_recover_process_iunlinks(
28062812

28072813
for_each_perag(log->l_mp, agno, pag)
28082814
xlog_recover_iunlink_ag(pag);
2809-
2810-
/*
2811-
* Flush the pending unlinked inodes to ensure that the inactivations
2812-
* are fully completed on disk and the incore inodes can be reclaimed
2813-
* before we signal that recovery is complete.
2814-
*/
2815-
xfs_inodegc_flush(log->l_mp);
28162815
}
28172816

28182817
STATIC void

fs/xfs/xfs_mount.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct xfs_error_cfg {
6262
struct xfs_inodegc {
6363
struct llist_head list;
6464
struct delayed_work work;
65+
int error;
6566

6667
/* approximate count of inodes in the list */
6768
unsigned int items;

fs/xfs/xfs_super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ xfs_inodegc_init_percpu(
11001100
#endif
11011101
init_llist_head(&gc->list);
11021102
gc->items = 0;
1103+
gc->error = 0;
11031104
INIT_DELAYED_WORK(&gc->work, xfs_inodegc_worker);
11041105
}
11051106
return 0;

fs/xfs/xfs_trans.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,9 @@ xfs_trans_alloc(
290290
* Do not perform a synchronous scan because callers can hold
291291
* other locks.
292292
*/
293-
xfs_blockgc_flush_all(mp);
293+
error = xfs_blockgc_flush_all(mp);
294+
if (error)
295+
return error;
294296
want_retry = false;
295297
goto retry;
296298
}

0 commit comments

Comments
 (0)