diff options
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/libxfs/xfs_rmap.c | 4 | ||||
-rw-r--r-- | fs/xfs/scrub/rtrmap_repair.c | 124 | ||||
-rw-r--r-- | fs/xfs/scrub/trace.h | 33 | ||||
-rw-r--r-- | fs/xfs/xfs_mount.h | 5 | ||||
-rw-r--r-- | fs/xfs/xfs_super.c | 3 |
5 files changed, 163 insertions, 6 deletions
diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c index a6be6c7fe6e5..abd0f3fd4e28 100644 --- a/fs/xfs/libxfs/xfs_rmap.c +++ b/fs/xfs/libxfs/xfs_rmap.c @@ -827,6 +827,10 @@ xfs_rmap_update_hook( if (pag) xfs_hook_call(&pag->pag_rmap_update_hooks, op, &p); +#ifdef CONFIG_XFS_RT + else + xfs_hook_call(&tp->t_mountp->m_rtrmap_update_hooks, op, &p); +#endif } #else # define xfs_rmap_update_hook(t, p, o, s, b, u, oi) do { } while(0) diff --git a/fs/xfs/scrub/rtrmap_repair.c b/fs/xfs/scrub/rtrmap_repair.c index b8e53895dfa4..513aef875554 100644 --- a/fs/xfs/scrub/rtrmap_repair.c +++ b/fs/xfs/scrub/rtrmap_repair.c @@ -67,6 +67,9 @@ struct xrep_rtrmap { struct xrep_newbt new_btree_info; struct xfs_btree_bload rtrmap_bload; + /* lock for the xfbtree and xfile */ + struct mutex lock; + /* rmap records generated from primary metadata */ struct xfbtree *rtrmap_btree; /* in-memory btree cursor for the ->get_blocks walk */ @@ -77,6 +80,9 @@ struct xrep_rtrmap { /* bitmap of old rtrmapbt blocks */ struct xbitmap old_rtrmapbt_blocks; + /* Hooks into rtrmap update code. */ + struct notifier_block rtrmap_update_hook; + /* inode scan cursor */ struct xchk_iscan iscan; @@ -128,13 +134,16 @@ xrep_rtrmap_stash( if (xchk_should_terminate(sc, &error)) return error; + if (xchk_iscan_aborted(&rr->iscan)) + return -EFSCORRUPTED; + trace_xrep_rtrmap_found(sc->mp, &rmap); - /* Add entry to in-memory btree. */ + mutex_lock(&rr->lock); error = xfs_btree_mem_head_read_buf(rr->rtrmap_btree->target, sc->tp, &mhead_bp); if (error) - return error; + goto out_abort; mcur = xfs_rtrmapbt_mem_cursor(sc->mp, sc->tp, mhead_bp, rr->rtrmap_btree); @@ -143,10 +152,18 @@ xrep_rtrmap_stash( if (error) goto out_cancel; - return xfbtree_trans_commit(rr->rtrmap_btree, sc->tp); + error = xfbtree_trans_commit(rr->rtrmap_btree, sc->tp); + if (error) + goto out_abort; + + mutex_unlock(&rr->lock); + return 0; out_cancel: xfbtree_trans_cancel(rr->rtrmap_btree, sc->tp); +out_abort: + xchk_iscan_abort(&rr->iscan); + mutex_unlock(&rr->lock); return error; } @@ -567,6 +584,13 @@ xrep_rtrmap_find_rmaps( if (error) return error; + /* + * If a hook failed to update the in-memory btree, we lack the data to + * continue the repair. + */ + if (xchk_iscan_aborted(&rr->iscan)) + return -EFSCORRUPTED; + /* Scan for old rtrmap blocks. */ for_each_perag(sc->mp, agno, pag) { error = xrep_rtrmap_scan_ag(rr, pag); @@ -817,6 +841,78 @@ xrep_rtrmap_remove_old_tree( return xrep_reset_imeta_reservation(rr->sc); } +static inline bool +xrep_rtrmapbt_want_live_update( + struct xchk_iscan *iscan, + const struct xfs_owner_info *oi) +{ + /* + * We scanned the CoW staging extents before we started the iscan, so + * we need all the updates. + */ + if (XFS_RMAP_NON_INODE_OWNER(oi->oi_owner)) + return true; + + /* Ignore updates to files that the scanner hasn't visited yet. */ + return xchk_iscan_want_live_update(iscan, oi->oi_owner); +} + +/* + * Apply a rtrmapbt update from the regular filesystem into our shadow btree. + * We're running from the thread that owns the rtrmap ILOCK and is generating + * the update, so we must be careful about which parts of the struct + * xrep_rtrmap that we change. + */ +static int +xrep_rtrmapbt_live_update( + struct notifier_block *nb, + unsigned long op, + void *data) +{ + struct xfs_rmap_update_params *p = data; + struct xrep_rtrmap *rr; + struct xfs_mount *mp; + struct xfs_btree_cur *mcur; + struct xfs_buf *mhead_bp; + int error; + + rr = container_of(nb, struct xrep_rtrmap, rtrmap_update_hook); + mp = rr->sc->mp; + + if (!xrep_rtrmapbt_want_live_update(&rr->iscan, &p->oinfo)) + goto out_unlock; + + trace_xrep_rtrmap_live_update(mp, op, p); + + mutex_lock(&rr->lock); + error = xfs_btree_mem_head_read_buf(rr->rtrmap_btree->target, p->tp, + &mhead_bp); + if (error) + goto out_abort; + + mcur = xfs_rtrmapbt_mem_cursor(mp, p->tp, mhead_bp, rr->rtrmap_btree); + error = __xfs_rmap_finish_intent(mcur, op, p->startblock, + p->blockcount, &p->oinfo, p->unwritten); + xfs_btree_del_cursor(mcur, error); + if (error) + goto out_cancel; + + error = xfbtree_trans_commit(rr->rtrmap_btree, p->tp); + if (error) + goto out_abort; + + mutex_unlock(&rr->lock); + return NOTIFY_DONE; + +out_cancel: + xfbtree_trans_cancel(rr->rtrmap_btree, p->tp); +out_abort: + xchk_iscan_abort(&rr->iscan); + mutex_unlock(&rr->lock); +out_unlock: + return NOTIFY_DONE; +} + /* Repair the realtime rmap btree. */ int xrep_rtrmapbt( @@ -830,6 +926,7 @@ xrep_rtrmapbt( return -ENOMEM; rr->sc = sc; + mutex_init(&rr->lock); xbitmap_init(&rr->old_rtrmapbt_blocks); /* Set up some storage */ @@ -842,29 +939,44 @@ xrep_rtrmapbt( rr->iscan.iget_retry_delay = HZ / 10; xchk_iscan_start(&rr->iscan); + /* + * Hook into live rtrmap operations so that we can update our in-memory + * btree to reflect live changes on the filesystem. Since we drop the + * rtrmap ILOCK to scan all the inodes, we need this piece to avoid + * installing a stale btree. + */ + error = xfs_hook_add(&sc->mp->m_rtrmap_update_hooks, + &rr->rtrmap_update_hook, xrep_rtrmapbt_live_update); + if (error) + goto out_records; + /* Collect rmaps for realtime files. */ error = xrep_rtrmap_find_rmaps(rr); if (error) - goto out_records; + goto out_hook; xfs_trans_ijoin(sc->tp, sc->ip, 0); error = xrep_ino_dqattach(sc); if (error) - goto out_records; + goto out_hook; /* Rebuild the rtrmap information. */ error = xrep_rtrmap_build_new_tree(rr); if (error) - goto out_records; + goto out_hook; /* Kill the old tree. */ error = xrep_rtrmap_remove_old_tree(rr); +out_hook: + xchk_iscan_abort(&rr->iscan); + xfs_hook_del(&sc->mp->m_rtrmap_update_hooks, &rr->rtrmap_update_hook); out_records: xchk_iscan_finish(&rr->iscan); xfbtree_destroy(rr->rtrmap_btree); out_bitmap: xbitmap_destroy(&rr->old_rtrmapbt_blocks); + mutex_destroy(&rr->lock); kmem_free(rr); return error; } diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 8a7c31e3855b..25fa1eda4162 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -2252,6 +2252,39 @@ TRACE_EVENT(xrep_rmap_live_update, __entry->flags) ); +TRACE_EVENT(xrep_rtrmap_live_update, + TP_PROTO(struct xfs_mount *mp, unsigned int op, + const struct xfs_rmap_update_params *p), + TP_ARGS(mp, op, p), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, op) + __field(xfs_rtblock_t, rtbno) + __field(xfs_filblks_t, len) + __field(uint64_t, owner) + __field(uint64_t, offset) + __field(unsigned int, flags) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->op = op; + __entry->rtbno = p->startblock; + __entry->len = p->blockcount; + xfs_owner_info_unpack(&p->oinfo, &__entry->owner, + &__entry->offset, &__entry->flags); + if (p->unwritten) + __entry->flags |= XFS_RMAP_UNWRITTEN; + ), + TP_printk("dev %d:%d op %s rtbno 0x%llx fsbcount 0x%llx owner 0x%llx fileoff 0x%llx flags 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __print_symbolic(__entry->op, XFS_RMAP_INTENT_STRINGS), + __entry->rtbno, + __entry->len, + __entry->owner, + __entry->offset, + __entry->flags) +); + #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 05b791aa24d1..8a2e2c132e39 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -275,6 +275,11 @@ typedef struct xfs_mount { /* online file link count check stuff */ struct xfs_hook_chain m_nlink_delta_hooks; +#ifdef CONFIG_XFS_RT + /* online rt rmap repair stuff */ + struct xfs_hook_chain m_rtrmap_update_hooks; +#endif + #if IS_ENABLED(CONFIG_XFS_ONLINE_SCRUB) && IS_ENABLED(CONFIG_XFS_RT) /* * Counter of live intents. We track the number of log intent items diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 3786ee4420ac..f2f00fe7d4dd 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -2010,6 +2010,9 @@ static int xfs_init_fs_context( mp->m_allocsize_log = 16; /* 64k */ xfs_hook_init(&mp->m_nlink_delta_hooks); +#ifdef CONFIG_XFS_RT + xfs_hook_init(&mp->m_rtrmap_update_hooks); +#endif /* * Copy binary VFS mount flags we are interested in. |