]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 1 Jun 2018 17:07:28 +0000 (19:07 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 1 Jun 2018 17:07:28 +0000 (19:07 +0200)
added patches:
pci-hv-fix-2-hang-issues-in-hv_compose_msi_msg.patch
xfs-convert-xfs_agfl_size-to-a-helper-function.patch
xfs-detect-agfl-count-corruption-and-reset-agfl.patch

queue-4.14/pci-hv-fix-2-hang-issues-in-hv_compose_msi_msg.patch [new file with mode: 0644]
queue-4.14/series
queue-4.14/xfs-convert-xfs_agfl_size-to-a-helper-function.patch [new file with mode: 0644]
queue-4.14/xfs-detect-agfl-count-corruption-and-reset-agfl.patch [new file with mode: 0644]

diff --git a/queue-4.14/pci-hv-fix-2-hang-issues-in-hv_compose_msi_msg.patch b/queue-4.14/pci-hv-fix-2-hang-issues-in-hv_compose_msi_msg.patch
new file mode 100644 (file)
index 0000000..8520ce3
--- /dev/null
@@ -0,0 +1,142 @@
+From de0aa7b2f97d348ba7d1e17a00744c989baa0cb6 Mon Sep 17 00:00:00 2001
+From: Dexuan Cui <decui@microsoft.com>
+Date: Thu, 15 Mar 2018 14:21:08 +0000
+Subject: PCI: hv: Fix 2 hang issues in hv_compose_msi_msg()
+
+From: Dexuan Cui <decui@microsoft.com>
+
+commit de0aa7b2f97d348ba7d1e17a00744c989baa0cb6 upstream.
+
+1. With the patch "x86/vector/msi: Switch to global reservation mode",
+the recent v4.15 and newer kernels always hang for 1-vCPU Hyper-V VM
+with SR-IOV. This is because when we reach hv_compose_msi_msg() by
+request_irq() -> request_threaded_irq() ->__setup_irq()->irq_startup()
+-> __irq_startup() -> irq_domain_activate_irq() -> ... ->
+msi_domain_activate() -> ... -> hv_compose_msi_msg(), local irq is
+disabled in __setup_irq().
+
+Note: when we reach hv_compose_msi_msg() by another code path:
+pci_enable_msix_range() -> ... -> irq_domain_activate_irq() -> ... ->
+hv_compose_msi_msg(), local irq is not disabled.
+
+hv_compose_msi_msg() depends on an interrupt from the host.
+With interrupts disabled, a UP VM always hangs in the busy loop in
+the function, because the interrupt callback hv_pci_onchannelcallback()
+can not be called.
+
+We can do nothing but work it around by polling the channel. This
+is ugly, but we don't have any other choice.
+
+2. If the host is ejecting the VF device before we reach
+hv_compose_msi_msg(), in a UP VM, we can hang in hv_compose_msi_msg()
+forever, because at this time the host doesn't respond to the
+CREATE_INTERRUPT request. This issue exists the first day the
+pci-hyperv driver appears in the kernel.
+
+Luckily, this can also by worked around by polling the channel
+for the PCI_EJECT message and hpdev->state, and by checking the
+PCI vendor ID.
+
+Note: actually the above 2 issues also happen to a SMP VM, if
+"hbus->hdev->channel->target_cpu == smp_processor_id()" is true.
+
+Fixes: 4900be83602b ("x86/vector/msi: Switch to global reservation mode")
+Tested-by: Adrian Suhov <v-adsuho@microsoft.com>
+Tested-by: Chris Valean <v-chvale@microsoft.com>
+Signed-off-by: Dexuan Cui <decui@microsoft.com>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Michael Kelley <mikelley@microsoft.com>
+Acked-by: Haiyang Zhang <haiyangz@microsoft.com>
+Cc: <stable@vger.kernel.org>
+Cc: Stephen Hemminger <sthemmin@microsoft.com>
+Cc: K. Y. Srinivasan <kys@microsoft.com>
+Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
+Cc: Jack Morgenstein <jackm@mellanox.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/pci/host/pci-hyperv.c |   58 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 57 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/host/pci-hyperv.c
++++ b/drivers/pci/host/pci-hyperv.c
+@@ -531,6 +531,8 @@ struct hv_pci_compl {
+       s32 completion_status;
+ };
++static void hv_pci_onchannelcallback(void *context);
++
+ /**
+  * hv_pci_generic_compl() - Invoked for a completion packet
+  * @context:          Set up by the sender of the packet.
+@@ -675,6 +677,31 @@ static void _hv_pcifront_read_config(str
+       }
+ }
++static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
++{
++      u16 ret;
++      unsigned long flags;
++      void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET +
++                           PCI_VENDOR_ID;
++
++      spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
++
++      /* Choose the function to be read. (See comment above) */
++      writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
++      /* Make sure the function was chosen before we start reading. */
++      mb();
++      /* Read from that function's config space. */
++      ret = readw(addr);
++      /*
++       * mb() is not required here, because the spin_unlock_irqrestore()
++       * is a barrier.
++       */
++
++      spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
++
++      return ret;
++}
++
+ /**
+  * _hv_pcifront_write_config() - Internal PCI config write
+  * @hpdev:    The PCI driver's representation of the device
+@@ -1121,8 +1148,37 @@ static void hv_compose_msi_msg(struct ir
+        * Since this function is called with IRQ locks held, can't
+        * do normal wait for completion; instead poll.
+        */
+-      while (!try_wait_for_completion(&comp.comp_pkt.host_event))
++      while (!try_wait_for_completion(&comp.comp_pkt.host_event)) {
++              /* 0xFFFF means an invalid PCI VENDOR ID. */
++              if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) {
++                      dev_err_once(&hbus->hdev->device,
++                                   "the device has gone\n");
++                      goto free_int_desc;
++              }
++
++              /*
++               * When the higher level interrupt code calls us with
++               * interrupt disabled, we must poll the channel by calling
++               * the channel callback directly when channel->target_cpu is
++               * the current CPU. When the higher level interrupt code
++               * calls us with interrupt enabled, let's add the
++               * local_bh_disable()/enable() to avoid race.
++               */
++              local_bh_disable();
++
++              if (hbus->hdev->channel->target_cpu == smp_processor_id())
++                      hv_pci_onchannelcallback(hbus);
++
++              local_bh_enable();
++
++              if (hpdev->state == hv_pcichild_ejecting) {
++                      dev_err_once(&hbus->hdev->device,
++                                   "the device is being ejected\n");
++                      goto free_int_desc;
++              }
++
+               udelay(100);
++      }
+       if (comp.comp_pkt.completion_status < 0) {
+               dev_err(&hbus->hdev->device,
index ea2296a4a2cb213ac7e0dd8c0532812f4fe4e32e..5230e3bcb9f153500a54dceac7a2ce6a15e8f272 100644 (file)
@@ -6,3 +6,6 @@ objtool-fix-noreturn-detection-for-recursive-sibling-calls.patch
 x86-mce-amd-carve-out-smca-get_block_address-code.patch
 x86-mce-amd-cache-smca-misc-block-addresses.patch
 revert-pinctrl-msm-use-dynamic-gpio-numbering.patch
+pci-hv-fix-2-hang-issues-in-hv_compose_msi_msg.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.14/xfs-convert-xfs_agfl_size-to-a-helper-function.patch b/queue-4.14/xfs-convert-xfs_agfl_size-to-a-helper-function.patch
new file mode 100644 (file)
index 0000000..491cc0a
--- /dev/null
@@ -0,0 +1,150 @@
+From a78ee256c325ecfaec13cafc41b315bd4e1dd518 Mon Sep 17 00:00:00 2001
+From: Dave Chinner <dchinner@redhat.com>
+Date: Tue, 6 Mar 2018 17:08:32 -0800
+Subject: xfs: convert XFS_AGFL_SIZE to a helper function
+
+From: Dave Chinner <dchinner@redhat.com>
+
+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 <dchinner@redhat.com>
+[darrick: forward port to newer kernel, simplify the helper]
+Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
+Reviewed-by: Brian Foster <bfoster@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xfs/libxfs/xfs_alloc.c  |   31 ++++++++++++++++++++++++-------
+ fs/xfs/libxfs/xfs_alloc.h  |    2 ++
+ fs/xfs/libxfs/xfs_format.h |   13 +------------
+ fs/xfs/xfs_fsops.c         |    2 +-
+ 4 files changed, 28 insertions(+), 20 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_alloc.c
++++ b/fs/xfs/libxfs/xfs_alloc.c
+@@ -52,6 +52,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)
+@@ -540,7 +557,7 @@ xfs_agfl_verify(
+       if (bp->b_pag && be32_to_cpu(agfl->agfl_seqno) != bp->b_pag->pag_agno)
+               return false;
+-      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 false;
+@@ -2252,7 +2269,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));
+@@ -2363,7 +2380,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));
+@@ -2381,7 +2398,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)];
+@@ -2414,9 +2431,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 false;
+       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
+@@ -798,24 +798,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/xfs_fsops.c
++++ b/fs/xfs/xfs_fsops.c
+@@ -294,7 +294,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.14/xfs-detect-agfl-count-corruption-and-reset-agfl.patch b/queue-4.14/xfs-detect-agfl-count-corruption-and-reset-agfl.patch
new file mode 100644 (file)
index 0000000..a772de6
--- /dev/null
@@ -0,0 +1,230 @@
+From a27ba2607e60312554cbcd43fc660b2c7f29dc9c Mon Sep 17 00:00:00 2001
+From: Brian Foster <bfoster@redhat.com>
+Date: Thu, 15 Mar 2018 10:51:58 -0700
+Subject: xfs: detect agfl count corruption and reset agfl
+
+From: Brian Foster <bfoster@redhat.com>
+
+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 <david@fromorbit.com>
+Signed-off-by: Brian Foster <bfoster@redhat.com>
+Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
+Reviewed-by Dave Chiluk <chiluk+linuxxfs@indeed.com>
+Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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
+@@ -2057,6 +2057,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.
+  */
+@@ -2117,6 +2204,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))
+@@ -2273,6 +2364,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--;
+@@ -2384,6 +2476,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++;
+@@ -2589,6 +2682,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
+@@ -1513,7 +1513,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),
+@@ -1569,6 +1569,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,