From d8f2daca23d3b16ad916ea604e40ef2bd7db2ccd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 1 Jun 2018 19:07:45 +0200 Subject: [PATCH] 4.16-stable patches added patches: xfs-convert-xfs_agfl_size-to-a-helper-function.patch xfs-detect-agfl-count-corruption-and-reset-agfl.patch --- queue-4.16/series | 2 + ...t-xfs_agfl_size-to-a-helper-function.patch | 180 ++++++++++++++ ...agfl-count-corruption-and-reset-agfl.patch | 230 ++++++++++++++++++ 3 files changed, 412 insertions(+) create mode 100644 queue-4.16/xfs-convert-xfs_agfl_size-to-a-helper-function.patch create mode 100644 queue-4.16/xfs-detect-agfl-count-corruption-and-reset-agfl.patch diff --git a/queue-4.16/series b/queue-4.16/series index 9acd2f6adb3..481f20c34d6 100644 --- a/queue-4.16/series +++ b/queue-4.16/series @@ -8,3 +8,5 @@ x86-mce-amd-cache-smca-misc-block-addresses.patch drm-vmwgfx-use-kasprintf.patch drm-vmwgfx-fix-host-logging-guestinfo-reading-error-paths.patch revert-pinctrl-msm-use-dynamic-gpio-numbering.patch +xfs-convert-xfs_agfl_size-to-a-helper-function.patch +xfs-detect-agfl-count-corruption-and-reset-agfl.patch diff --git a/queue-4.16/xfs-convert-xfs_agfl_size-to-a-helper-function.patch b/queue-4.16/xfs-convert-xfs_agfl_size-to-a-helper-function.patch new file mode 100644 index 00000000000..d004982bc28 --- /dev/null +++ b/queue-4.16/xfs-convert-xfs_agfl_size-to-a-helper-function.patch @@ -0,0 +1,180 @@ +From a78ee256c325ecfaec13cafc41b315bd4e1dd518 Mon Sep 17 00:00:00 2001 +From: Dave Chinner +Date: Tue, 6 Mar 2018 17:08:32 -0800 +Subject: xfs: convert XFS_AGFL_SIZE to a helper function + +From: Dave Chinner + +commit a78ee256c325ecfaec13cafc41b315bd4e1dd518 upstream. + +The AGFL size calculation is about to get more complex, so lets turn +the macro into a function first and remove the macro. + +Signed-off-by: Dave Chinner +[darrick: forward port to newer kernel, simplify the helper] +Signed-off-by: Darrick J. Wong +Reviewed-by: Brian Foster +Signed-off-by: Greg Kroah-Hartman + +--- + fs/xfs/libxfs/xfs_alloc.c | 31 ++++++++++++++++++++++++------- + fs/xfs/libxfs/xfs_alloc.h | 2 ++ + fs/xfs/libxfs/xfs_format.h | 13 +------------ + fs/xfs/scrub/agheader.c | 6 +++--- + fs/xfs/xfs_fsops.c | 2 +- + 5 files changed, 31 insertions(+), 23 deletions(-) + +--- a/fs/xfs/libxfs/xfs_alloc.c ++++ b/fs/xfs/libxfs/xfs_alloc.c +@@ -53,6 +53,23 @@ STATIC int xfs_alloc_ag_vextent_size(xfs + STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *, + xfs_btree_cur_t *, xfs_agblock_t *, xfs_extlen_t *, int *); + ++/* ++ * Size of the AGFL. For CRC-enabled filesystes we steal a couple of slots in ++ * the beginning of the block for a proper header with the location information ++ * and CRC. ++ */ ++unsigned int ++xfs_agfl_size( ++ struct xfs_mount *mp) ++{ ++ unsigned int size = mp->m_sb.sb_sectsize; ++ ++ if (xfs_sb_version_hascrc(&mp->m_sb)) ++ size -= sizeof(struct xfs_agfl); ++ ++ return size / sizeof(xfs_agblock_t); ++} ++ + unsigned int + xfs_refc_block( + struct xfs_mount *mp) +@@ -550,7 +567,7 @@ xfs_agfl_verify( + if (bp->b_pag && be32_to_cpu(agfl->agfl_seqno) != bp->b_pag->pag_agno) + return __this_address; + +- for (i = 0; i < XFS_AGFL_SIZE(mp); i++) { ++ for (i = 0; i < xfs_agfl_size(mp); i++) { + if (be32_to_cpu(agfl->agfl_bno[i]) != NULLAGBLOCK && + be32_to_cpu(agfl->agfl_bno[i]) >= mp->m_sb.sb_agblocks) + return __this_address; +@@ -2266,7 +2283,7 @@ xfs_alloc_get_freelist( + bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]); + be32_add_cpu(&agf->agf_flfirst, 1); + xfs_trans_brelse(tp, agflbp); +- if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp)) ++ if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp)) + agf->agf_flfirst = 0; + + pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); +@@ -2377,7 +2394,7 @@ xfs_alloc_put_freelist( + be32_to_cpu(agf->agf_seqno), &agflbp))) + return error; + be32_add_cpu(&agf->agf_fllast, 1); +- if (be32_to_cpu(agf->agf_fllast) == XFS_AGFL_SIZE(mp)) ++ if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp)) + agf->agf_fllast = 0; + + pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); +@@ -2395,7 +2412,7 @@ xfs_alloc_put_freelist( + + xfs_alloc_log_agf(tp, agbp, logflags); + +- ASSERT(be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)); ++ ASSERT(be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp)); + + agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp); + blockp = &agfl_bno[be32_to_cpu(agf->agf_fllast)]; +@@ -2428,9 +2445,9 @@ xfs_agf_verify( + if (!(agf->agf_magicnum == cpu_to_be32(XFS_AGF_MAGIC) && + XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) && + be32_to_cpu(agf->agf_freeblks) <= be32_to_cpu(agf->agf_length) && +- be32_to_cpu(agf->agf_flfirst) < XFS_AGFL_SIZE(mp) && +- be32_to_cpu(agf->agf_fllast) < XFS_AGFL_SIZE(mp) && +- be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp))) ++ be32_to_cpu(agf->agf_flfirst) < xfs_agfl_size(mp) && ++ be32_to_cpu(agf->agf_fllast) < xfs_agfl_size(mp) && ++ be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp))) + return __this_address; + + if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 || +--- a/fs/xfs/libxfs/xfs_alloc.h ++++ b/fs/xfs/libxfs/xfs_alloc.h +@@ -26,6 +26,8 @@ struct xfs_trans; + + extern struct workqueue_struct *xfs_alloc_wq; + ++unsigned int xfs_agfl_size(struct xfs_mount *mp); ++ + /* + * Freespace allocation types. Argument to xfs_alloc_[v]extent. + */ +--- a/fs/xfs/libxfs/xfs_format.h ++++ b/fs/xfs/libxfs/xfs_format.h +@@ -803,24 +803,13 @@ typedef struct xfs_agi { + &(XFS_BUF_TO_AGFL(bp)->agfl_bno[0]) : \ + (__be32 *)(bp)->b_addr) + +-/* +- * Size of the AGFL. For CRC-enabled filesystes we steal a couple of +- * slots in the beginning of the block for a proper header with the +- * location information and CRC. +- */ +-#define XFS_AGFL_SIZE(mp) \ +- (((mp)->m_sb.sb_sectsize - \ +- (xfs_sb_version_hascrc(&((mp)->m_sb)) ? \ +- sizeof(struct xfs_agfl) : 0)) / \ +- sizeof(xfs_agblock_t)) +- + typedef struct xfs_agfl { + __be32 agfl_magicnum; + __be32 agfl_seqno; + uuid_t agfl_uuid; + __be64 agfl_lsn; + __be32 agfl_crc; +- __be32 agfl_bno[]; /* actually XFS_AGFL_SIZE(mp) */ ++ __be32 agfl_bno[]; /* actually xfs_agfl_size(mp) */ + } __attribute__((packed)) xfs_agfl_t; + + #define XFS_AGFL_CRC_OFF offsetof(struct xfs_agfl, agfl_crc) +--- a/fs/xfs/scrub/agheader.c ++++ b/fs/xfs/scrub/agheader.c +@@ -80,7 +80,7 @@ xfs_scrub_walk_agfl( + } + + /* first to the end */ +- for (i = flfirst; i < XFS_AGFL_SIZE(mp); i++) { ++ for (i = flfirst; i < xfs_agfl_size(mp); i++) { + error = fn(sc, be32_to_cpu(agfl_bno[i]), priv); + if (error) + return error; +@@ -664,7 +664,7 @@ xfs_scrub_agf( + if (agfl_last > agfl_first) + fl_count = agfl_last - agfl_first + 1; + else +- fl_count = XFS_AGFL_SIZE(mp) - agfl_first + agfl_last + 1; ++ fl_count = xfs_agfl_size(mp) - agfl_first + agfl_last + 1; + if (agfl_count != 0 && fl_count != agfl_count) + xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); + +@@ -791,7 +791,7 @@ xfs_scrub_agfl( + /* Allocate buffer to ensure uniqueness of AGFL entries. */ + agf = XFS_BUF_TO_AGF(sc->sa.agf_bp); + agflcount = be32_to_cpu(agf->agf_flcount); +- if (agflcount > XFS_AGFL_SIZE(sc->mp)) { ++ if (agflcount > xfs_agfl_size(sc->mp)) { + xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); + goto out; + } +--- a/fs/xfs/xfs_fsops.c ++++ b/fs/xfs/xfs_fsops.c +@@ -217,7 +217,7 @@ xfs_growfs_data_private( + } + + agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp); +- for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++) ++ for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++) + agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK); + + error = xfs_bwrite(bp); diff --git a/queue-4.16/xfs-detect-agfl-count-corruption-and-reset-agfl.patch b/queue-4.16/xfs-detect-agfl-count-corruption-and-reset-agfl.patch new file mode 100644 index 00000000000..5f96d138ba8 --- /dev/null +++ b/queue-4.16/xfs-detect-agfl-count-corruption-and-reset-agfl.patch @@ -0,0 +1,230 @@ +From a27ba2607e60312554cbcd43fc660b2c7f29dc9c Mon Sep 17 00:00:00 2001 +From: Brian Foster +Date: Thu, 15 Mar 2018 10:51:58 -0700 +Subject: xfs: detect agfl count corruption and reset agfl + +From: Brian Foster + +commit a27ba2607e60312554cbcd43fc660b2c7f29dc9c upstream. + +The struct xfs_agfl v5 header was originally introduced with +unexpected padding that caused the AGFL to operate with one less +slot than intended. The header has since been packed, but the fix +left an incompatibility for users who upgrade from an old kernel +with the unpacked header to a newer kernel with the packed header +while the AGFL happens to wrap around the end. The newer kernel +recognizes one extra slot at the physical end of the AGFL that the +previous kernel did not. The new kernel will eventually attempt to +allocate a block from that slot, which contains invalid data, and +cause a crash. + +This condition can be detected by comparing the active range of the +AGFL to the count. While this detects a padding mismatch, it can +also trigger false positives for unrelated flcount corruption. Since +we cannot distinguish a size mismatch due to padding from unrelated +corruption, we can't trust the AGFL enough to simply repopulate the +empty slot. + +Instead, avoid unnecessarily complex detection logic and and use a +solution that can handle any form of flcount corruption that slips +through read verifiers: distrust the entire AGFL and reset it to an +empty state. Any valid blocks within the AGFL are intentionally +leaked. This requires xfs_repair to rectify (which was already +necessary based on the state the AGFL was found in). The reset +mitigates the side effect of the padding mismatch problem from a +filesystem crash to a free space accounting inconsistency. The +generic approach also means that this patch can be safely backported +to kernels with or without a packed struct xfs_agfl. + +Check the AGF for an invalid freelist count on initial read from +disk. If detected, set a flag on the xfs_perag to indicate that a +reset is required before the AGFL can be used. In the first +transaction that attempts to use a flagged AGFL, reset it to empty, +warn the user about the inconsistency and allow the freelist fixup +code to repopulate the AGFL with new blocks. The xfs_perag flag is +cleared to eliminate the need for repeated checks on each block +allocation operation. + +This allows kernels that include the packing fix commit 96f859d52bcb +("libxfs: pack the agfl header structure so XFS_AGFL_SIZE is correct") +to handle older unpacked AGFL formats without a filesystem crash. + +Suggested-by: Dave Chinner +Signed-off-by: Brian Foster +Reviewed-by: Darrick J. Wong +Reviewed-by Dave Chiluk +Signed-off-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman + +--- + fs/xfs/libxfs/xfs_alloc.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++ + fs/xfs/xfs_mount.h | 1 + fs/xfs/xfs_trace.h | 9 +++- + 3 files changed, 103 insertions(+), 1 deletion(-) + +--- a/fs/xfs/libxfs/xfs_alloc.c ++++ b/fs/xfs/libxfs/xfs_alloc.c +@@ -2071,6 +2071,93 @@ xfs_alloc_space_available( + } + + /* ++ * Check the agfl fields of the agf for inconsistency or corruption. The purpose ++ * is to detect an agfl header padding mismatch between current and early v5 ++ * kernels. This problem manifests as a 1-slot size difference between the ++ * on-disk flcount and the active [first, last] range of a wrapped agfl. This ++ * may also catch variants of agfl count corruption unrelated to padding. Either ++ * way, we'll reset the agfl and warn the user. ++ * ++ * Return true if a reset is required before the agfl can be used, false ++ * otherwise. ++ */ ++static bool ++xfs_agfl_needs_reset( ++ struct xfs_mount *mp, ++ struct xfs_agf *agf) ++{ ++ uint32_t f = be32_to_cpu(agf->agf_flfirst); ++ uint32_t l = be32_to_cpu(agf->agf_fllast); ++ uint32_t c = be32_to_cpu(agf->agf_flcount); ++ int agfl_size = xfs_agfl_size(mp); ++ int active; ++ ++ /* no agfl header on v4 supers */ ++ if (!xfs_sb_version_hascrc(&mp->m_sb)) ++ return false; ++ ++ /* ++ * The agf read verifier catches severe corruption of these fields. ++ * Repeat some sanity checks to cover a packed -> unpacked mismatch if ++ * the verifier allows it. ++ */ ++ if (f >= agfl_size || l >= agfl_size) ++ return true; ++ if (c > agfl_size) ++ return true; ++ ++ /* ++ * Check consistency between the on-disk count and the active range. An ++ * agfl padding mismatch manifests as an inconsistent flcount. ++ */ ++ if (c && l >= f) ++ active = l - f + 1; ++ else if (c) ++ active = agfl_size - f + l + 1; ++ else ++ active = 0; ++ ++ return active != c; ++} ++ ++/* ++ * Reset the agfl to an empty state. Ignore/drop any existing blocks since the ++ * agfl content cannot be trusted. Warn the user that a repair is required to ++ * recover leaked blocks. ++ * ++ * The purpose of this mechanism is to handle filesystems affected by the agfl ++ * header padding mismatch problem. A reset keeps the filesystem online with a ++ * relatively minor free space accounting inconsistency rather than suffer the ++ * inevitable crash from use of an invalid agfl block. ++ */ ++static void ++xfs_agfl_reset( ++ struct xfs_trans *tp, ++ struct xfs_buf *agbp, ++ struct xfs_perag *pag) ++{ ++ struct xfs_mount *mp = tp->t_mountp; ++ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp); ++ ++ ASSERT(pag->pagf_agflreset); ++ trace_xfs_agfl_reset(mp, agf, 0, _RET_IP_); ++ ++ xfs_warn(mp, ++ "WARNING: Reset corrupted AGFL on AG %u. %d blocks leaked. " ++ "Please unmount and run xfs_repair.", ++ pag->pag_agno, pag->pagf_flcount); ++ ++ agf->agf_flfirst = 0; ++ agf->agf_fllast = cpu_to_be32(xfs_agfl_size(mp) - 1); ++ agf->agf_flcount = 0; ++ xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLFIRST | XFS_AGF_FLLAST | ++ XFS_AGF_FLCOUNT); ++ ++ pag->pagf_flcount = 0; ++ pag->pagf_agflreset = false; ++} ++ ++/* + * Decide whether to use this allocation group for this allocation. + * If so, fix up the btree freelist's size. + */ +@@ -2131,6 +2218,10 @@ xfs_alloc_fix_freelist( + } + } + ++ /* reset a padding mismatched agfl before final free space check */ ++ if (pag->pagf_agflreset) ++ xfs_agfl_reset(tp, agbp, pag); ++ + /* If there isn't enough total space or single-extent, reject it. */ + need = xfs_alloc_min_freelist(mp, pag); + if (!xfs_alloc_space_available(args, need, flags)) +@@ -2287,6 +2378,7 @@ xfs_alloc_get_freelist( + agf->agf_flfirst = 0; + + pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); ++ ASSERT(!pag->pagf_agflreset); + be32_add_cpu(&agf->agf_flcount, -1); + xfs_trans_agflist_delta(tp, -1); + pag->pagf_flcount--; +@@ -2398,6 +2490,7 @@ xfs_alloc_put_freelist( + agf->agf_fllast = 0; + + pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); ++ ASSERT(!pag->pagf_agflreset); + be32_add_cpu(&agf->agf_flcount, 1); + xfs_trans_agflist_delta(tp, 1); + pag->pagf_flcount++; +@@ -2605,6 +2698,7 @@ xfs_alloc_read_agf( + pag->pagb_count = 0; + pag->pagb_tree = RB_ROOT; + pag->pagf_init = 1; ++ pag->pagf_agflreset = xfs_agfl_needs_reset(mp, agf); + } + #ifdef DEBUG + else if (!XFS_FORCED_SHUTDOWN(mp)) { +--- a/fs/xfs/xfs_mount.h ++++ b/fs/xfs/xfs_mount.h +@@ -353,6 +353,7 @@ typedef struct xfs_perag { + char pagi_inodeok; /* The agi is ok for inodes */ + uint8_t pagf_levels[XFS_BTNUM_AGF]; + /* # of levels in bno & cnt btree */ ++ bool pagf_agflreset; /* agfl requires reset before use */ + uint32_t pagf_flcount; /* count of blocks in freelist */ + xfs_extlen_t pagf_freeblks; /* total free blocks */ + xfs_extlen_t pagf_longest; /* longest free space */ +--- a/fs/xfs/xfs_trace.h ++++ b/fs/xfs/xfs_trace.h +@@ -1477,7 +1477,7 @@ TRACE_EVENT(xfs_extent_busy_trim, + __entry->tlen) + ); + +-TRACE_EVENT(xfs_agf, ++DECLARE_EVENT_CLASS(xfs_agf_class, + TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags, + unsigned long caller_ip), + TP_ARGS(mp, agf, flags, caller_ip), +@@ -1533,6 +1533,13 @@ TRACE_EVENT(xfs_agf, + __entry->longest, + (void *)__entry->caller_ip) + ); ++#define DEFINE_AGF_EVENT(name) \ ++DEFINE_EVENT(xfs_agf_class, name, \ ++ TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags, \ ++ unsigned long caller_ip), \ ++ TP_ARGS(mp, agf, flags, caller_ip)) ++DEFINE_AGF_EVENT(xfs_agf); ++DEFINE_AGF_EVENT(xfs_agfl_reset); + + TRACE_EVENT(xfs_free_extent, + TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno, -- 2.47.3