LCOV - code coverage report
Current view: top level - fs/xfs - xfs_fsrefs.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 221 400 55.2 %
Date: 2023-07-31 20:08:27 Functions: 10 16 62.5 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Copyright (C) 2021-2023 Oracle.  All Rights Reserved.
       4             :  * Author: Darrick J. Wong <djwong@kernel.org>
       5             :  */
       6             : #include "xfs.h"
       7             : #include "xfs_fs.h"
       8             : #include "xfs_shared.h"
       9             : #include "xfs_format.h"
      10             : #include "xfs_log_format.h"
      11             : #include "xfs_trans_resv.h"
      12             : #include "xfs_mount.h"
      13             : #include "xfs_inode.h"
      14             : #include "xfs_trans.h"
      15             : #include "xfs_btree.h"
      16             : #include "xfs_trace.h"
      17             : #include "xfs_alloc.h"
      18             : #include "xfs_bit.h"
      19             : #include "xfs_fsrefs.h"
      20             : #include "xfs_refcount.h"
      21             : #include "xfs_refcount_btree.h"
      22             : #include "xfs_alloc_btree.h"
      23             : #include "xfs_rtalloc.h"
      24             : #include "xfs_rtrefcount_btree.h"
      25             : #include "xfs_ag.h"
      26             : #include "xfs_rtbitmap.h"
      27             : #include "xfs_rtgroup.h"
      28             : 
      29             : /* getfsrefs query state */
      30             : struct xfs_fsrefs_info {
      31             :         struct xfs_fsrefs_head  *head;
      32             :         struct xfs_getfsrefs    *fsrefs_recs;   /* mapping records */
      33             : 
      34             :         struct xfs_btree_cur    *refc_cur;      /* refcount btree cursor */
      35             :         struct xfs_btree_cur    *bno_cur;       /* bnobt btree cursor */
      36             : 
      37             :         struct xfs_buf          *agf_bp;        /* AGF, for refcount queries */
      38             :         struct xfs_perag        *pag;           /* perag structure */
      39             :         struct xfs_rtgroup      *rtg;
      40             : 
      41             :         xfs_daddr_t             next_daddr;     /* next daddr we expect */
      42             :         /* daddr of low fsmap key when we're using the rtbitmap */
      43             :         xfs_daddr_t             low_daddr;
      44             : 
      45             :         /*
      46             :          * Low refcount key for the query.  If low.rc_blockcount is nonzero,
      47             :          * this is the second (or later) call to retrieve the recordset in
      48             :          * pieces.  xfs_getfsrefs_rec_before_start will compare all records
      49             :          * retrieved by the refcountbt query to filter out any records that
      50             :          * start before the last record.
      51             :          */
      52             :         struct xfs_refcount_irec low;
      53             :         struct xfs_refcount_irec high;          /* high refcount key */
      54             : 
      55             :         u32                     dev;            /* device id */
      56             :         bool                    last;           /* last extent? */
      57             : };
      58             : 
      59             : /* Associate a device with a getfsrefs handler. */
      60             : struct xfs_fsrefs_dev {
      61             :         u32                     dev;
      62             :         int                     (*fn)(struct xfs_trans *tp,
      63             :                                       const struct xfs_fsrefs *keys,
      64             :                                       struct xfs_fsrefs_info *info);
      65             : };
      66             : 
      67             : /* Convert an xfs_fsrefs to an fsrefs. */
      68             : static void
      69             : xfs_fsrefs_from_internal(
      70             :         struct xfs_getfsrefs    *dest,
      71             :         struct xfs_fsrefs       *src)
      72             : {
      73        2545 :         dest->fcr_device = src->fcr_device;
      74        2545 :         dest->fcr_flags = src->fcr_flags;
      75        2545 :         dest->fcr_physical = BBTOB(src->fcr_physical);
      76        2545 :         dest->fcr_owners = src->fcr_owners;
      77        2545 :         dest->fcr_length = BBTOB(src->fcr_length);
      78        2545 :         dest->fcr_reserved[0] = 0;
      79        2545 :         dest->fcr_reserved[1] = 0;
      80        2545 :         dest->fcr_reserved[2] = 0;
      81        2545 :         dest->fcr_reserved[3] = 0;
      82             : }
      83             : 
      84             : /* Convert an fsrefs to an xfs_fsrefs. */
      85             : void
      86        3033 : xfs_fsrefs_to_internal(
      87             :         struct xfs_fsrefs       *dest,
      88             :         struct xfs_getfsrefs    *src)
      89             : {
      90        3033 :         dest->fcr_device = src->fcr_device;
      91        3033 :         dest->fcr_flags = src->fcr_flags;
      92        3033 :         dest->fcr_physical = BTOBBT(src->fcr_physical);
      93        3033 :         dest->fcr_owners = src->fcr_owners;
      94        3033 :         dest->fcr_length = BTOBBT(src->fcr_length);
      95        3033 : }
      96             : 
      97             : /* Compare two getfsrefs device handlers. */
      98             : static int
      99        3033 : xfs_fsrefs_dev_compare(
     100             :         const void                      *p1,
     101             :         const void                      *p2)
     102             : {
     103        3033 :         const struct xfs_fsrefs_dev     *d1 = p1;
     104        3033 :         const struct xfs_fsrefs_dev     *d2 = p2;
     105             : 
     106        3033 :         return d1->dev - d2->dev;
     107             : }
     108             : 
     109             : static inline bool
     110             : xfs_fsrefs_rec_before_start(
     111             :         struct xfs_fsrefs_info          *info,
     112             :         const struct xfs_refcount_irec  *rec,
     113             :         xfs_daddr_t                     rec_daddr)
     114             : {
     115        4209 :         if (info->low_daddr != -1ULL)
     116           0 :                 return rec_daddr < info->low_daddr;
     117        4209 :         if (info->low.rc_blockcount)
     118        2635 :                 return rec->rc_startblock < info->low.rc_startblock;
     119             :         return false;
     120             : }
     121             : 
     122             : /*
     123             :  * Format a refcount record for fsrefs, having translated rc_startblock into
     124             :  * the appropriate daddr units.
     125             :  */
     126             : STATIC int
     127        4209 : xfs_fsrefs_helper(
     128             :         struct xfs_trans                *tp,
     129             :         struct xfs_fsrefs_info          *info,
     130             :         const struct xfs_refcount_irec  *rec,
     131             :         xfs_daddr_t                     rec_daddr,
     132             :         xfs_daddr_t                     len_daddr)
     133             : {
     134        4209 :         struct xfs_fsrefs               fcr;
     135        4209 :         struct xfs_getfsrefs            *row;
     136        4209 :         struct xfs_mount                *mp = tp->t_mountp;
     137             : 
     138        4209 :         if (fatal_signal_pending(current))
     139             :                 return -EINTR;
     140             : 
     141        4209 :         if (len_daddr == 0)
     142        4209 :                 len_daddr = XFS_FSB_TO_BB(mp, rec->rc_blockcount);
     143             : 
     144             :         /*
     145             :          * Filter out records that start before our startpoint, if the
     146             :          * caller requested that.
     147             :          */
     148        6844 :         if (xfs_fsrefs_rec_before_start(info, rec, rec_daddr))
     149             :                 return 0;
     150             : 
     151             :         /* Are we just counting mappings? */
     152        4209 :         if (info->head->fch_count == 0) {
     153           0 :                 if (info->head->fch_entries == UINT_MAX)
     154             :                         return -ECANCELED;
     155             : 
     156           0 :                 info->head->fch_entries++;
     157           0 :                 return 0;
     158             :         }
     159             : 
     160             :         /* Fill out the extent we found */
     161        4209 :         if (info->head->fch_entries >= info->head->fch_count)
     162             :                 return -ECANCELED;
     163             : 
     164        2545 :         if (info->pag)
     165        2545 :                 trace_xfs_fsrefs_mapping(mp, info->dev, info->pag->pag_agno,
     166             :                                 rec);
     167           0 :         else if (info->rtg)
     168           0 :                 trace_xfs_fsrefs_mapping(mp, info->dev, info->rtg->rtg_rgno,
     169             :                                 rec);
     170             :         else
     171           0 :                 trace_xfs_fsrefs_mapping(mp, info->dev, NULLAGNUMBER, rec);
     172             : 
     173        2545 :         fcr.fcr_device = info->dev;
     174        2545 :         fcr.fcr_flags = 0;
     175        2545 :         fcr.fcr_physical = rec_daddr;
     176        2545 :         fcr.fcr_owners = rec->rc_refcount;
     177        2545 :         fcr.fcr_length = len_daddr;
     178             : 
     179        2545 :         trace_xfs_getfsrefs_mapping(mp, &fcr);
     180             : 
     181        2545 :         row = &info->fsrefs_recs[info->head->fch_entries++];
     182        2545 :         xfs_fsrefs_from_internal(row, &fcr);
     183        2545 :         return 0;
     184             : }
     185             : 
     186             : /* Synthesize fsrefs records from free space data. */
     187             : STATIC int
     188        5012 : xfs_fsrefs_ddev_bnobt_helper(
     189             :         struct xfs_btree_cur            *cur,
     190             :         const struct xfs_alloc_rec_incore *rec,
     191             :         void                            *priv)
     192             : {
     193        5012 :         struct xfs_refcount_irec        irec;
     194        5012 :         struct xfs_mount                *mp = cur->bc_mp;
     195        5012 :         struct xfs_fsrefs_info          *info = priv;
     196        5012 :         xfs_agnumber_t                  next_agno;
     197        5012 :         xfs_agblock_t                   next_agbno;
     198        5012 :         xfs_daddr_t                     rec_daddr;
     199             : 
     200             :         /*
     201             :          * Figure out if there's a gap between the last fsrefs record we
     202             :          * emitted and this free extent.  If there is, report the gap as a
     203             :          * refcount==1 record.
     204             :          */
     205        5012 :         next_agno = xfs_daddr_to_agno(mp, info->next_daddr);
     206        5012 :         next_agbno = xfs_daddr_to_agbno(mp, info->next_daddr);
     207             : 
     208        5012 :         ASSERT(next_agno >= cur->bc_ag.pag->pag_agno);
     209        5012 :         ASSERT(rec->ar_startblock >= next_agbno);
     210             : 
     211             :         /*
     212             :          * If we've already moved on to the next AG, we don't have any fsrefs
     213             :          * records to synthesize.
     214             :          */
     215        5012 :         if (next_agno > cur->bc_ag.pag->pag_agno)
     216             :                 return 0;
     217             : 
     218        4982 :         info->next_daddr = XFS_AGB_TO_DADDR(mp, cur->bc_ag.pag->pag_agno,
     219             :                         rec->ar_startblock + rec->ar_blockcount);
     220             : 
     221        4982 :         if (rec->ar_startblock == next_agbno)
     222             :                 return 0;
     223             : 
     224             :         /* Emit a record for the in-use space */
     225        4062 :         irec.rc_startblock = next_agbno;
     226        4062 :         irec.rc_blockcount = rec->ar_startblock - next_agbno;
     227        4062 :         irec.rc_refcount = 1;
     228        4062 :         irec.rc_domain = XFS_REFC_DOMAIN_SHARED;
     229        4062 :         rec_daddr = XFS_AGB_TO_DADDR(mp, cur->bc_ag.pag->pag_agno,
     230             :                         irec.rc_startblock);
     231             : 
     232        4062 :         return xfs_fsrefs_helper(cur->bc_tp, info, &irec, rec_daddr, 0);
     233             : }
     234             : 
     235             : /* Emit records to fill a gap in the refcount btree with singly-owned blocks. */
     236             : STATIC int
     237        1793 : xfs_fsrefs_ddev_fill_refcount_gap(
     238             :         struct xfs_trans                *tp,
     239             :         struct xfs_fsrefs_info          *info,
     240             :         xfs_agblock_t                   agbno)
     241             : {
     242        1793 :         struct xfs_alloc_rec_incore     low = {0};
     243        1793 :         struct xfs_alloc_rec_incore     high = {0};
     244        1793 :         struct xfs_mount                *mp = tp->t_mountp;
     245        1793 :         struct xfs_btree_cur            *cur = info->bno_cur;
     246        1793 :         struct xfs_agf                  *agf;
     247        1793 :         int                             error;
     248             : 
     249        1793 :         ASSERT(xfs_daddr_to_agno(mp, info->next_daddr) ==
     250             :                         cur->bc_ag.pag->pag_agno);
     251             : 
     252        1793 :         low.ar_startblock = xfs_daddr_to_agbno(mp, info->next_daddr);
     253        1793 :         if (low.ar_startblock >= agbno)
     254             :                 return 0;
     255             : 
     256        1742 :         high.ar_startblock = agbno;
     257        1742 :         error = xfs_alloc_query_range(cur, &low, &high,
     258             :                         xfs_fsrefs_ddev_bnobt_helper, info);
     259        1742 :         if (error)
     260             :                 return error;
     261             : 
     262             :         /*
     263             :          * Synthesize records for single-owner extents between the last
     264             :          * fsrefcount record emitted and the end of the query range.
     265             :          */
     266         170 :         agf = cur->bc_ag.agbp->b_addr;
     267         170 :         low.ar_startblock = min_t(xfs_agblock_t, agbno,
     268             :                                   be32_to_cpu(agf->agf_length));
     269         170 :         if (xfs_daddr_to_agbno(mp, info->next_daddr) > low.ar_startblock)
     270             :                 return 0;
     271             : 
     272         170 :         info->last = true;
     273         170 :         return xfs_fsrefs_ddev_bnobt_helper(cur, &low, info);
     274             : }
     275             : 
     276             : /* Transform a refcountbt irec into a fsrefs */
     277             : STATIC int
     278         758 : xfs_fsrefs_ddev_refcountbt_helper(
     279             :         struct xfs_btree_cur            *cur,
     280             :         const struct xfs_refcount_irec  *rec,
     281             :         void                            *priv)
     282             : {
     283         758 :         struct xfs_mount                *mp = cur->bc_mp;
     284         758 :         struct xfs_fsrefs_info          *info = priv;
     285         758 :         xfs_daddr_t                     rec_daddr;
     286         758 :         int                             error;
     287             : 
     288             :         /*
     289             :          * Stop once we get to the CoW staging extents; they're all shoved to
     290             :          * the right side of the btree and were already covered by the bnobt
     291             :          * scan.
     292             :          */
     293         758 :         if (rec->rc_domain != XFS_REFC_DOMAIN_SHARED)
     294             :                 return -ECANCELED;
     295             : 
     296             :         /* Report on any gaps first */
     297         758 :         error = xfs_fsrefs_ddev_fill_refcount_gap(cur->bc_tp, info,
     298         758 :                         rec->rc_startblock);
     299         758 :         if (error)
     300             :                 return error;
     301             : 
     302         147 :         rec_daddr = XFS_AGB_TO_DADDR(mp, cur->bc_ag.pag->pag_agno,
     303             :                         rec->rc_startblock);
     304         147 :         info->next_daddr = XFS_AGB_TO_DADDR(mp, cur->bc_ag.pag->pag_agno,
     305             :                         rec->rc_startblock + rec->rc_blockcount);
     306             : 
     307         147 :         return xfs_fsrefs_helper(cur->bc_tp, info, rec, rec_daddr, 0);
     308             : }
     309             : 
     310             : /* Execute a getfsrefs query against the regular data device. */
     311             : STATIC int
     312        1011 : xfs_fsrefs_ddev(
     313             :         struct xfs_trans        *tp,
     314             :         const struct xfs_fsrefs *keys,
     315             :         struct xfs_fsrefs_info  *info)
     316             : {
     317        1011 :         struct xfs_mount        *mp = tp->t_mountp;
     318        1011 :         struct xfs_buf          *agf_bp = NULL;
     319        1011 :         struct xfs_perag        *pag = NULL;
     320        1011 :         xfs_fsblock_t           start_fsb;
     321        1011 :         xfs_fsblock_t           end_fsb;
     322        1011 :         xfs_agnumber_t          start_ag;
     323        1011 :         xfs_agnumber_t          end_ag;
     324        1011 :         xfs_agnumber_t          agno;
     325        1011 :         uint64_t                eofs;
     326        1011 :         int                     error = 0;
     327             : 
     328        1011 :         eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
     329        1011 :         if (keys[0].fcr_physical >= eofs)
     330             :                 return 0;
     331        1011 :         start_fsb = XFS_DADDR_TO_FSB(mp, keys[0].fcr_physical);
     332        1011 :         end_fsb = XFS_DADDR_TO_FSB(mp, min(eofs - 1, keys[1].fcr_physical));
     333             : 
     334        1011 :         info->refc_cur = info->bno_cur = NULL;
     335             : 
     336             :         /*
     337             :          * Convert the fsrefs low/high keys to AG based keys.  Initialize
     338             :          * low to the fsrefs low key and max out the high key to the end
     339             :          * of the AG.
     340             :          */
     341        1011 :         info->low.rc_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb);
     342        1011 :         info->low.rc_blockcount = XFS_BB_TO_FSBT(mp, keys[0].fcr_length);
     343        1011 :         info->low.rc_refcount = 0;
     344        1011 :         info->low.rc_domain = XFS_REFC_DOMAIN_SHARED;
     345             : 
     346             :         /* Adjust the low key if we are continuing from where we left off. */
     347        1011 :         if (info->low.rc_blockcount > 0) {
     348        1005 :                 info->low.rc_startblock += info->low.rc_blockcount;
     349             : 
     350        1005 :                 start_fsb += info->low.rc_blockcount;
     351        1005 :                 if (XFS_FSB_TO_DADDR(mp, start_fsb) >= eofs)
     352             :                         return 0;
     353             :         }
     354             : 
     355        1011 :         info->high.rc_startblock = -1U;
     356        1011 :         info->high.rc_blockcount = 0;
     357        1011 :         info->high.rc_refcount = 0;
     358        1011 :         info->high.rc_domain = XFS_REFC_DOMAIN_SHARED;
     359             : 
     360        1011 :         start_ag = XFS_FSB_TO_AGNO(mp, start_fsb);
     361        1011 :         end_ag = XFS_FSB_TO_AGNO(mp, end_fsb);
     362             : 
     363             :         /* Query each AG */
     364        1011 :         agno = start_ag;
     365        1041 :         for_each_perag_range(mp, agno, end_ag, pag) {
     366             :                 /*
     367             :                  * Set the AG high key from the fsrefs high key if this
     368             :                  * is the last AG that we're querying.
     369             :                  */
     370        1035 :                 info->pag = pag;
     371        1035 :                 if (pag->pag_agno == end_ag)
     372         254 :                         info->high.rc_startblock = XFS_FSB_TO_AGBNO(mp,
     373             :                                         end_fsb);
     374             : 
     375        1035 :                 if (info->refc_cur) {
     376          24 :                         xfs_btree_del_cursor(info->refc_cur, XFS_BTREE_NOERROR);
     377          24 :                         info->refc_cur = NULL;
     378             :                 }
     379        1035 :                 if (info->bno_cur) {
     380          24 :                         xfs_btree_del_cursor(info->bno_cur, XFS_BTREE_NOERROR);
     381          24 :                         info->bno_cur = NULL;
     382             :                 }
     383        1035 :                 if (agf_bp) {
     384          24 :                         xfs_trans_brelse(tp, agf_bp);
     385          24 :                         agf_bp = NULL;
     386             :                 }
     387             : 
     388        1035 :                 error = xfs_alloc_read_agf(pag, tp, 0, &agf_bp);
     389        1035 :                 if (error)
     390             :                         break;
     391             : 
     392        1035 :                 trace_xfs_fsrefs_low_key(mp, info->dev, pag->pag_agno,
     393        1035 :                                 &info->low);
     394        1035 :                 trace_xfs_fsrefs_high_key(mp, info->dev, pag->pag_agno,
     395        1035 :                                 &info->high);
     396             : 
     397        1035 :                 info->bno_cur = xfs_allocbt_init_cursor(mp, tp, agf_bp, pag,
     398             :                                                 XFS_BTNUM_BNO);
     399             : 
     400        1035 :                 if (xfs_has_reflink(mp)) {
     401        1035 :                         info->refc_cur = xfs_refcountbt_init_cursor(mp, tp,
     402             :                                                         agf_bp, pag);
     403             : 
     404             :                         /*
     405             :                          * Fill the query with refcount records and synthesize
     406             :                          * singly-owned block records from free space data.
     407             :                          */
     408        1035 :                         error = xfs_refcount_query_range(info->refc_cur,
     409             :                                         &info->low, &info->high,
     410             :                                         xfs_fsrefs_ddev_refcountbt_helper,
     411             :                                         info);
     412        1035 :                         if (error && error != -ECANCELED)
     413             :                                 break;
     414             :                 }
     415             : 
     416             :                 /*
     417             :                  * Synthesize refcount==1 records from the free space data
     418             :                  * between the end of the last fsrefs record reported and the
     419             :                  * end of the range.  If we don't have refcount support, the
     420             :                  * starting point will be the start of the query range.
     421             :                  */
     422        1035 :                 error = xfs_fsrefs_ddev_fill_refcount_gap(tp, info,
     423             :                                 info->high.rc_startblock);
     424        1035 :                 if (error)
     425             :                         break;
     426             : 
     427             :                 /*
     428             :                  * Set the AG low key to the start of the AG prior to
     429             :                  * moving on to the next AG.
     430             :                  */
     431          30 :                 if (pag->pag_agno == start_ag)
     432          36 :                         memset(&info->low, 0, sizeof(info->low));
     433             : 
     434          30 :                 info->pag = NULL;
     435             :         }
     436             : 
     437        1011 :         if (info->refc_cur) {
     438        1011 :                 xfs_btree_del_cursor(info->refc_cur, error);
     439        1011 :                 info->refc_cur = NULL;
     440             :         }
     441        1011 :         if (info->bno_cur) {
     442        1011 :                 xfs_btree_del_cursor(info->bno_cur, error);
     443        1011 :                 info->bno_cur = NULL;
     444             :         }
     445        1011 :         if (agf_bp)
     446        1011 :                 xfs_trans_brelse(tp, agf_bp);
     447        1011 :         if (info->pag) {
     448        1005 :                 xfs_perag_rele(info->pag);
     449        1005 :                 info->pag = NULL;
     450           6 :         } else if (pag) {
     451             :                 /* loop termination case */
     452           0 :                 xfs_perag_rele(pag);
     453             :         }
     454             : 
     455             :         return error;
     456             : }
     457             : 
     458             : /* Execute a getfsrefs query against the log device. */
     459             : STATIC int
     460           0 : xfs_fsrefs_logdev(
     461             :         struct xfs_trans                *tp,
     462             :         const struct xfs_fsrefs         *keys,
     463             :         struct xfs_fsrefs_info          *info)
     464             : {
     465           0 :         struct xfs_mount                *mp = tp->t_mountp;
     466           0 :         struct xfs_refcount_irec        refc;
     467           0 :         xfs_daddr_t                     rec_daddr, len_daddr;
     468           0 :         xfs_fsblock_t                   start_fsb, end_fsb;
     469           0 :         uint64_t                        eofs;
     470             : 
     471           0 :         eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
     472           0 :         if (keys[0].fcr_physical >= eofs)
     473             :                 return 0;
     474           0 :         start_fsb = XFS_BB_TO_FSBT(mp,
     475             :                                 keys[0].fcr_physical + keys[0].fcr_length);
     476           0 :         end_fsb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fcr_physical));
     477             : 
     478             :         /* Adjust the low key if we are continuing from where we left off. */
     479           0 :         if (keys[0].fcr_length > 0)
     480           0 :                 info->low_daddr = XFS_FSB_TO_BB(mp, start_fsb);
     481             : 
     482           0 :         trace_xfs_fsrefs_low_key_linear(mp, info->dev, start_fsb);
     483           0 :         trace_xfs_fsrefs_high_key_linear(mp, info->dev, end_fsb);
     484             : 
     485           0 :         if (start_fsb > 0)
     486             :                 return 0;
     487             : 
     488             :         /* Fabricate an refc entry for the external log device. */
     489           0 :         refc.rc_startblock = 0;
     490           0 :         refc.rc_blockcount = mp->m_sb.sb_logblocks;
     491           0 :         refc.rc_refcount = 1;
     492           0 :         refc.rc_domain = XFS_REFC_DOMAIN_SHARED;
     493             : 
     494           0 :         rec_daddr = XFS_FSB_TO_BB(mp, refc.rc_startblock);
     495           0 :         len_daddr = XFS_FSB_TO_BB(mp, refc.rc_blockcount);
     496           0 :         return xfs_fsrefs_helper(tp, info, &refc, rec_daddr, len_daddr);
     497             : }
     498             : 
     499             : #ifdef CONFIG_XFS_RT
     500             : /* Synthesize fsrefs records from rtbitmap records. */
     501             : STATIC int
     502           0 : xfs_fsrefs_rtdev_bitmap_helper(
     503             :         struct xfs_mount                *mp,
     504             :         struct xfs_trans                *tp,
     505             :         const struct xfs_rtalloc_rec    *rec,
     506             :         void                            *priv)
     507             : {
     508           0 :         struct xfs_refcount_irec        irec;
     509           0 :         struct xfs_fsrefs_info          *info = priv;
     510           0 :         xfs_rtblock_t                   rt_startblock;
     511           0 :         xfs_rtblock_t                   rec_rtlen;
     512           0 :         xfs_rtblock_t                   next_rtbno;
     513           0 :         xfs_daddr_t                     rec_daddr, len_daddr;
     514             : 
     515             :         /*
     516             :          * Figure out if there's a gap between the last fsrefs record we
     517             :          * emitted and this free extent.  If there is, report the gap as a
     518             :          * refcount==1 record.
     519             :          */
     520           0 :         next_rtbno = XFS_BB_TO_FSBT(mp, info->next_daddr);
     521           0 :         rec_daddr = XFS_FSB_TO_BB(mp, next_rtbno);
     522           0 :         irec.rc_startblock = next_rtbno;
     523             : 
     524           0 :         rt_startblock = xfs_rtx_to_rtb(mp, rec->ar_startext);
     525           0 :         rec_rtlen = xfs_rtx_to_rtb(mp, rec->ar_extcount);
     526             : 
     527           0 :         ASSERT(rt_startblock >= next_rtbno);
     528             : 
     529           0 :         info->next_daddr = XFS_FSB_TO_BB(mp, rt_startblock + rec_rtlen);
     530             : 
     531           0 :         if (rt_startblock == next_rtbno)
     532             :                 return 0;
     533             : 
     534             :         /* Emit a record for the in-use space */
     535           0 :         irec.rc_blockcount = rt_startblock - next_rtbno;
     536           0 :         len_daddr = XFS_FSB_TO_BB(mp, rt_startblock - next_rtbno);
     537             : 
     538           0 :         irec.rc_refcount = 1;
     539           0 :         irec.rc_domain = XFS_REFC_DOMAIN_SHARED;
     540             : 
     541           0 :         return xfs_fsrefs_helper(tp, info, &irec, rec_daddr, len_daddr);
     542             : }
     543             : 
     544             : /* Emit records to fill a gap in the refcount btree with singly-owned blocks. */
     545             : STATIC int
     546           0 : xfs_fsrefs_rtdev_fill_refcount_gap(
     547             :         struct xfs_trans        *tp,
     548             :         struct xfs_fsrefs_info  *info,
     549             :         xfs_rtblock_t           start_rtb,
     550             :         xfs_rtblock_t           end_rtb)
     551             : {
     552           0 :         struct xfs_rtalloc_rec  low = { 0 };
     553           0 :         struct xfs_rtalloc_rec  high = { 0 };
     554           0 :         struct xfs_mount        *mp = tp->t_mountp;
     555           0 :         xfs_daddr_t             rec_daddr;
     556           0 :         xfs_extlen_t            mod;
     557           0 :         int                     error;
     558             : 
     559             :         /*
     560             :          * Set up query parameters to return free extents covering the range we
     561             :          * want.
     562             :          */
     563           0 :         low.ar_startext = xfs_rtb_to_rtxt(mp, start_rtb);
     564           0 :         high.ar_startext = xfs_rtb_to_rtx(mp, end_rtb, &mod);
     565           0 :         if (mod)
     566           0 :                 high.ar_startext++;
     567             : 
     568           0 :         error = xfs_rtalloc_query_range(mp, tp, &low, &high,
     569             :                         xfs_fsrefs_rtdev_bitmap_helper, info);
     570           0 :         if (error)
     571             :                 return error;
     572             : 
     573             :         /*
     574             :          * Synthesize records for single-owner extents between the last
     575             :          * fsrefcount record emitted and the end of the query range.
     576             :          */
     577           0 :         high.ar_startext = min(mp->m_sb.sb_rextents, high.ar_startext);
     578           0 :         rec_daddr = XFS_FSB_TO_BB(mp, xfs_rtx_to_rtb(mp, high.ar_startext));
     579           0 :         if (info->next_daddr > rec_daddr)
     580             :                 return 0;
     581             : 
     582           0 :         info->last = true;
     583           0 :         return xfs_fsrefs_rtdev_bitmap_helper(mp, tp, &high, info);
     584             : }
     585             : 
     586             : /* Transform a absolute-startblock refcount (rtdev, logdev) into a fsrefs */
     587             : STATIC int
     588           0 : xfs_fsrefs_rtdev_refcountbt_helper(
     589             :         struct xfs_btree_cur            *cur,
     590             :         const struct xfs_refcount_irec  *rec,
     591             :         void                            *priv)
     592             : {
     593           0 :         struct xfs_mount                *mp = cur->bc_mp;
     594           0 :         struct xfs_fsrefs_info          *info = priv;
     595           0 :         xfs_rtblock_t                   rec_rtbno, next_rtbno;
     596           0 :         int                             error;
     597             : 
     598             :         /*
     599             :          * Stop once we get to the CoW staging extents; they're all shoved to
     600             :          * the right side of the btree and were already covered by the rtbitmap
     601             :          * scan.
     602             :          */
     603           0 :         if (rec->rc_domain != XFS_REFC_DOMAIN_SHARED)
     604             :                 return -ECANCELED;
     605             : 
     606             :         /* Report on any gaps first */
     607           0 :         rec_rtbno = xfs_rgbno_to_rtb(mp, cur->bc_ino.rtg->rtg_rgno,
     608           0 :                         rec->rc_startblock);
     609           0 :         next_rtbno = XFS_BB_TO_FSBT(mp, info->next_daddr);
     610           0 :         error = xfs_fsrefs_rtdev_fill_refcount_gap(cur->bc_tp, info,
     611             :                         next_rtbno, rec_rtbno);
     612           0 :         if (error)
     613             :                 return error;
     614             : 
     615             :         /* Report this refcount extent. */
     616           0 :         info->next_daddr = XFS_FSB_TO_BB(mp, rec_rtbno + rec->rc_blockcount);
     617           0 :         return xfs_fsrefs_helper(cur->bc_tp, info, rec,
     618           0 :                         XFS_FSB_TO_BB(mp, rec_rtbno), 0);
     619             : }
     620             : 
     621             : /* Execute a getfsrefs query against the realtime bitmap. */
     622             : STATIC int
     623           0 : xfs_fsrefs_rtdev_rtbitmap(
     624             :         struct xfs_trans        *tp,
     625             :         const struct xfs_fsrefs *keys,
     626             :         struct xfs_fsrefs_info  *info)
     627             : {
     628           0 :         struct xfs_mount        *mp = tp->t_mountp;
     629           0 :         xfs_rtblock_t           start_rtb;
     630           0 :         xfs_rtblock_t           end_rtb;
     631           0 :         uint64_t                eofs;
     632           0 :         int                     error;
     633             : 
     634           0 :         eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
     635           0 :         if (keys[0].fcr_physical >= eofs)
     636             :                 return 0;
     637           0 :         start_rtb = XFS_BB_TO_FSBT(mp,
     638             :                                 keys[0].fcr_physical + keys[0].fcr_length);
     639           0 :         end_rtb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fcr_physical));
     640             : 
     641           0 :         info->refc_cur = NULL;
     642             : 
     643             :         /* Adjust the low key if we are continuing from where we left off. */
     644           0 :         if (keys[0].fcr_length > 0) {
     645           0 :                 info->low_daddr = XFS_FSB_TO_BB(mp, start_rtb);
     646           0 :                 if (info->low_daddr >= eofs)
     647             :                         return 0;
     648             :         }
     649             : 
     650           0 :         trace_xfs_fsrefs_low_key_linear(mp, info->dev, start_rtb);
     651           0 :         trace_xfs_fsrefs_high_key_linear(mp, info->dev, end_rtb);
     652             : 
     653             :         /* Synthesize refcount==1 records from the free space data. */
     654           0 :         xfs_rtbitmap_lock_shared(mp, XFS_RBMLOCK_BITMAP);
     655           0 :         error = xfs_fsrefs_rtdev_fill_refcount_gap(tp, info, start_rtb,
     656             :                         end_rtb);
     657           0 :         xfs_rtbitmap_unlock_shared(mp, XFS_RBMLOCK_BITMAP);
     658           0 :         return error;
     659             : }
     660             : 
     661             : #define XFS_RTGLOCK_FSREFS      (XFS_RTGLOCK_BITMAP_SHARED | \
     662             :                                  XFS_RTGLOCK_REFCOUNT)
     663             : 
     664             : /* Execute a getfsrefs query against the realtime device. */
     665             : STATIC int
     666           0 : xfs_fsrefs_rtdev(
     667             :         struct xfs_trans        *tp,
     668             :         const struct xfs_fsrefs *keys,
     669             :         struct xfs_fsrefs_info  *info)
     670             : {
     671           0 :         struct xfs_mount        *mp = tp->t_mountp;
     672           0 :         struct xfs_rtgroup      *rtg;
     673           0 :         xfs_rtblock_t           start_rtb;
     674           0 :         xfs_rtblock_t           end_rtb;
     675           0 :         uint64_t                eofs;
     676           0 :         xfs_rgnumber_t          start_rg, end_rg;
     677           0 :         int                     error = 0;
     678             : 
     679           0 :         eofs = XFS_FSB_TO_BB(mp, xfs_rtx_to_rtb(mp, mp->m_sb.sb_rextents));
     680           0 :         if (keys[0].fcr_physical >= eofs)
     681             :                 return 0;
     682           0 :         start_rtb = XFS_BB_TO_FSBT(mp, keys[0].fcr_physical);
     683           0 :         end_rtb = XFS_BB_TO_FSB(mp, min(eofs - 1, keys[1].fcr_physical));
     684             : 
     685           0 :         info->refc_cur = NULL;
     686             : 
     687             :         /*
     688             :          * Convert the fsrefs low/high keys to rtgroup based keys.  Initialize
     689             :          * low to the fsrefs low key and max out the high key to the end of the
     690             :          * rtgroup.
     691             :          */
     692           0 :         info->low.rc_startblock = xfs_rtb_to_rgbno(mp, start_rtb, &start_rg);
     693           0 :         info->low.rc_blockcount = XFS_BB_TO_FSBT(mp, keys[0].fcr_length);
     694           0 :         info->low.rc_refcount = 0;
     695           0 :         info->low.rc_domain = XFS_REFC_DOMAIN_SHARED;
     696             : 
     697             :         /* Adjust the low key if we are continuing from where we left off. */
     698           0 :         if (info->low.rc_blockcount > 0) {
     699           0 :                 info->low.rc_startblock += info->low.rc_blockcount;
     700             : 
     701           0 :                 start_rtb += info->low.rc_blockcount;
     702           0 :                 if (xfs_rtb_to_daddr(mp, start_rtb) >= eofs)
     703             :                         return 0;
     704             :         }
     705             : 
     706           0 :         info->high.rc_startblock = -1U;
     707           0 :         info->high.rc_blockcount = 0;
     708           0 :         info->high.rc_refcount = 0;
     709           0 :         info->high.rc_domain = XFS_REFC_DOMAIN_SHARED;
     710             : 
     711           0 :         end_rg = xfs_rtb_to_rgno(mp, end_rtb);
     712             : 
     713           0 :         for_each_rtgroup_range(mp, start_rg, end_rg, rtg) {
     714             :                 /*
     715             :                  * Set the rtgroup high key from the fsrefs high key if this
     716             :                  * is the last rtgroup that we're querying.
     717             :                  */
     718           0 :                 info->rtg = rtg;
     719           0 :                 if (rtg->rtg_rgno == end_rg) {
     720           0 :                         xfs_rgnumber_t  junk;
     721             : 
     722           0 :                         info->high.rc_startblock = xfs_rtb_to_rgbno(mp,
     723             :                                         end_rtb, &junk);
     724             :                 }
     725             : 
     726           0 :                 if (info->refc_cur) {
     727           0 :                         xfs_rtgroup_unlock(info->refc_cur->bc_ino.rtg,
     728             :                                         XFS_RTGLOCK_FSREFS);
     729           0 :                         xfs_btree_del_cursor(info->refc_cur, XFS_BTREE_NOERROR);
     730           0 :                         info->refc_cur = NULL;
     731             :                 }
     732             : 
     733           0 :                 trace_xfs_fsrefs_low_key(mp, info->dev, rtg->rtg_rgno,
     734           0 :                                 &info->low);
     735           0 :                 trace_xfs_fsrefs_high_key(mp, info->dev, rtg->rtg_rgno,
     736           0 :                                 &info->high);
     737             : 
     738           0 :                 xfs_rtgroup_lock(NULL, rtg, XFS_RTGLOCK_FSREFS);
     739           0 :                 info->refc_cur = xfs_rtrefcountbt_init_cursor(mp, tp, rtg,
     740             :                                                 rtg->rtg_refcountip);
     741             : 
     742             :                 /*
     743             :                  * Fill the query with refcount records and synthesize
     744             :                  * singly-owned block records from free space data.
     745             :                  */
     746           0 :                 error = xfs_refcount_query_range(info->refc_cur,
     747             :                                 &info->low, &info->high,
     748             :                                 xfs_fsrefs_rtdev_refcountbt_helper, info);
     749           0 :                 if (error && error != -ECANCELED)
     750             :                         break;
     751             : 
     752             :                 /*
     753             :                  * Set the rtgroup low key to the start of the rtgroup prior to
     754             :                  * moving on to the next rtgroup.
     755             :                  */
     756           0 :                 if (rtg->rtg_rgno == start_rg)
     757           0 :                         memset(&info->low, 0, sizeof(info->low));
     758             : 
     759             :                 /*
     760             :                  * If this is the last rtgroup, report any gap at the end of it
     761             :                  * before we drop the reference to the perag when the loop
     762             :                  * terminates.
     763             :                  */
     764           0 :                 if (rtg->rtg_rgno == end_rg) {
     765           0 :                         xfs_rtblock_t   next_rtbno;
     766             : 
     767           0 :                         info->last = true;
     768           0 :                         next_rtbno = XFS_BB_TO_FSBT(mp, info->next_daddr);
     769           0 :                         error = xfs_fsrefs_rtdev_fill_refcount_gap(tp, info,
     770             :                                         next_rtbno, end_rtb);
     771           0 :                         if (error)
     772             :                                 break;
     773             :                 }
     774           0 :                 info->rtg = NULL;
     775             :         }
     776             : 
     777           0 :         if (info->refc_cur) {
     778           0 :                 xfs_rtgroup_unlock(info->refc_cur->bc_ino.rtg,
     779             :                                 XFS_RTGLOCK_FSREFS);
     780           0 :                 xfs_btree_del_cursor(info->refc_cur, error);
     781           0 :                 info->refc_cur = NULL;
     782             :         }
     783           0 :         if (info->rtg) {
     784           0 :                 xfs_rtgroup_rele(info->rtg);
     785           0 :                 info->rtg = NULL;
     786           0 :         } else if (rtg) {
     787             :                 /* loop termination case */
     788           0 :                 xfs_rtgroup_rele(rtg);
     789             :         }
     790             : 
     791             :         return error;
     792             : }
     793             : #endif
     794             : 
     795             : /* Do we recognize the device? */
     796             : STATIC bool
     797        2022 : xfs_fsrefs_is_valid_device(
     798             :         struct xfs_mount        *mp,
     799             :         struct xfs_fsrefs       *fcr)
     800             : {
     801        2022 :         if (fcr->fcr_device == 0 || fcr->fcr_device == UINT_MAX ||
     802        1005 :             fcr->fcr_device == new_encode_dev(mp->m_ddev_targp->bt_dev))
     803             :                 return true;
     804           0 :         if (mp->m_logdev_targp &&
     805           0 :             fcr->fcr_device == new_encode_dev(mp->m_logdev_targp->bt_dev))
     806             :                 return true;
     807           0 :         if (mp->m_rtdev_targp &&
     808           0 :             fcr->fcr_device == new_encode_dev(mp->m_rtdev_targp->bt_dev))
     809           0 :                 return true;
     810             :         return false;
     811             : }
     812             : 
     813             : /* Ensure that the low key is less than the high key. */
     814             : STATIC bool
     815        1011 : xfs_fsrefs_check_keys(
     816             :         struct xfs_fsrefs       *low_key,
     817             :         struct xfs_fsrefs       *high_key)
     818             : {
     819        1011 :         if (low_key->fcr_device > high_key->fcr_device)
     820             :                 return false;
     821        1011 :         if (low_key->fcr_device < high_key->fcr_device)
     822             :                 return true;
     823             : 
     824           0 :         if (low_key->fcr_physical > high_key->fcr_physical)
     825             :                 return false;
     826           0 :         if (low_key->fcr_physical < high_key->fcr_physical)
     827           0 :                 return true;
     828             : 
     829             :         return false;
     830             : }
     831             : 
     832             : /*
     833             :  * There are only two devices if we didn't configure RT devices at build time.
     834             :  */
     835             : #ifdef CONFIG_XFS_RT
     836             : #define XFS_GETFSREFS_DEVS      3
     837             : #else
     838             : #define XFS_GETFSREFS_DEVS      2
     839             : #endif /* CONFIG_XFS_RT */
     840             : 
     841             : /*
     842             :  * Get filesystem's extent refcounts as described in head, and format for
     843             :  * output. Fills in the supplied records array until there are no more reverse
     844             :  * mappings to return or head.fch_entries == head.fch_count.  In the second
     845             :  * case, this function returns -ECANCELED to indicate that more records would
     846             :  * have been returned.
     847             :  *
     848             :  * Key to Confusion
     849             :  * ----------------
     850             :  * There are multiple levels of keys and counters at work here:
     851             :  * xfs_fsrefs_head.fch_keys     -- low and high fsrefs keys passed in;
     852             :  *                                 these reflect fs-wide sector addrs.
     853             :  * dkeys                        -- fch_keys used to query each device;
     854             :  *                                 these are fch_keys but w/ the low key
     855             :  *                                 bumped up by fcr_length.
     856             :  * xfs_fsrefs_info.next_daddr-- next disk addr we expect to see; this
     857             :  *                                 is how we detect gaps in the fsrefs
     858             :  *                                 records and report them.
     859             :  * xfs_fsrefs_info.low/high     -- per-AG low/high keys computed from
     860             :  *                                 dkeys; used to query the metadata.
     861             :  */
     862             : int
     863        1011 : xfs_getfsrefs(
     864             :         struct xfs_mount        *mp,
     865             :         struct xfs_fsrefs_head  *head,
     866             :         struct xfs_getfsrefs    *fsrefs_recs)
     867             : {
     868        1011 :         struct xfs_trans        *tp = NULL;
     869        1011 :         struct xfs_fsrefs       dkeys[2];       /* per-dev keys */
     870        1011 :         struct xfs_fsrefs_dev   handlers[XFS_GETFSREFS_DEVS];
     871        1011 :         struct xfs_fsrefs_info  info = { NULL };
     872        1011 :         int                     i;
     873        1011 :         int                     error = 0;
     874             : 
     875        1011 :         if (head->fch_iflags & ~FCH_IF_VALID)
     876             :                 return -EINVAL;
     877        1011 :         if (!xfs_fsrefs_is_valid_device(mp, &head->fch_keys[0]) ||
     878        1011 :             !xfs_fsrefs_is_valid_device(mp, &head->fch_keys[1]))
     879             :                 return -EINVAL;
     880        1011 :         if (!xfs_fsrefs_check_keys(&head->fch_keys[0], &head->fch_keys[1]))
     881             :                 return -EINVAL;
     882             : 
     883        1011 :         head->fch_entries = 0;
     884             : 
     885             :         /* Set up our device handlers. */
     886        1011 :         memset(handlers, 0, sizeof(handlers));
     887        1011 :         handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
     888        1011 :         handlers[0].fn = xfs_fsrefs_ddev;
     889        1011 :         if (mp->m_logdev_targp != mp->m_ddev_targp) {
     890           0 :                 handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
     891           0 :                 handlers[1].fn = xfs_fsrefs_logdev;
     892             :         }
     893             : #ifdef CONFIG_XFS_RT
     894        1011 :         if (mp->m_rtdev_targp) {
     895           0 :                 handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev);
     896           0 :                 if (xfs_has_rtreflink(mp))
     897           0 :                         handlers[2].fn = xfs_fsrefs_rtdev;
     898             :                 else
     899           0 :                         handlers[2].fn = xfs_fsrefs_rtdev_rtbitmap;
     900             :         }
     901             : #endif /* CONFIG_XFS_RT */
     902             : 
     903        1011 :         xfs_sort(handlers, XFS_GETFSREFS_DEVS, sizeof(struct xfs_fsrefs_dev),
     904             :                         xfs_fsrefs_dev_compare);
     905             : 
     906             :         /*
     907             :          * To continue where we left off, we allow userspace to use the last
     908             :          * mapping from a previous call as the low key of the next.  This is
     909             :          * identified by a non-zero length in the low key. We have to increment
     910             :          * the low key in this scenario to ensure we don't return the same
     911             :          * mapping again, and instead return the very next mapping.  Bump the
     912             :          * physical offset as there can be no other mapping for the same
     913             :          * physical block range.
     914             :          *
     915             :          * Each fsrefs backend is responsible for making this adjustment as
     916             :          * appropriate for the backend.
     917             :          */
     918        1011 :         dkeys[0] = head->fch_keys[0];
     919        1011 :         memset(&dkeys[1], 0xFF, sizeof(struct xfs_fsrefs));
     920             : 
     921        1011 :         info.next_daddr = head->fch_keys[0].fcr_physical +
     922        1011 :                           head->fch_keys[0].fcr_length;
     923        1011 :         info.fsrefs_recs = fsrefs_recs;
     924        1011 :         info.head = head;
     925             : 
     926             :         /* For each device we support... */
     927        3039 :         for (i = 0; i < XFS_GETFSREFS_DEVS; i++) {
     928             :                 /* Is this device within the range the user asked for? */
     929        3033 :                 if (!handlers[i].fn)
     930        2022 :                         continue;
     931        1011 :                 if (head->fch_keys[0].fcr_device > handlers[i].dev)
     932           0 :                         continue;
     933        1011 :                 if (head->fch_keys[1].fcr_device < handlers[i].dev)
     934             :                         break;
     935             : 
     936             :                 /*
     937             :                  * If this device number matches the high key, we have to pass
     938             :                  * the high key to the handler to limit the query results.  If
     939             :                  * the device number exceeds the low key, zero out the low key
     940             :                  * so that we get everything from the beginning.
     941             :                  */
     942        1011 :                 if (handlers[i].dev == head->fch_keys[1].fcr_device)
     943           0 :                         dkeys[1] = head->fch_keys[1];
     944        1011 :                 if (handlers[i].dev > head->fch_keys[0].fcr_device)
     945           6 :                         memset(&dkeys[0], 0, sizeof(struct xfs_fsrefs));
     946             : 
     947             :                 /*
     948             :                  * Grab an empty transaction so that we can use its recursive
     949             :                  * buffer locking abilities to detect cycles in the refcountbt
     950             :                  * without deadlocking.
     951             :                  */
     952        1011 :                 error = xfs_trans_alloc_empty(mp, &tp);
     953        1011 :                 if (error)
     954             :                         break;
     955             : 
     956        1011 :                 info.dev = handlers[i].dev;
     957        1011 :                 info.last = false;
     958        1011 :                 info.pag = NULL;
     959        1011 :                 info.rtg = NULL;
     960        1011 :                 info.low_daddr = -1ULL;
     961        1011 :                 info.low.rc_blockcount = 0;
     962        1011 :                 error = handlers[i].fn(tp, dkeys, &info);
     963        1011 :                 if (error)
     964             :                         break;
     965           6 :                 xfs_trans_cancel(tp);
     966           6 :                 tp = NULL;
     967           6 :                 info.next_daddr = 0;
     968             :         }
     969             : 
     970        1011 :         if (tp)
     971        1005 :                 xfs_trans_cancel(tp);
     972        1011 :         head->fch_oflags = FCH_OF_DEV_T;
     973        1011 :         return error;
     974             : }

Generated by: LCOV version 1.14