LCOV - code coverage report
Current view: top level - fs/xfs/scrub - stats.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 71 150 47.3 %
Date: 2023-07-31 20:08:27 Functions: 9 14 64.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 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_trans_resv.h"
      11             : #include "xfs_mount.h"
      12             : #include "xfs_sysfs.h"
      13             : #include "xfs_btree.h"
      14             : #include "xfs_super.h"
      15             : #include "scrub/scrub.h"
      16             : #include "scrub/stats.h"
      17             : #include "scrub/trace.h"
      18             : 
      19             : struct xchk_scrub_stats {
      20             :         /* all 32-bit counters here */
      21             : 
      22             :         /* checking stats */
      23             :         uint32_t                invocations;
      24             :         uint32_t                clean;
      25             :         uint32_t                corrupt;
      26             :         uint32_t                preen;
      27             :         uint32_t                xfail;
      28             :         uint32_t                xcorrupt;
      29             :         uint32_t                incomplete;
      30             :         uint32_t                warning;
      31             :         uint32_t                retries;
      32             : 
      33             :         /* repair stats */
      34             :         uint32_t                repair_invocations;
      35             :         uint32_t                repair_success;
      36             : 
      37             :         /* all 64-bit items here */
      38             : 
      39             :         /* runtimes */
      40             :         uint64_t                checktime_us;
      41             :         uint64_t                repairtime_us;
      42             : 
      43             :         /* non-counter state must go at the end for clearall */
      44             :         spinlock_t              css_lock;
      45             : };
      46             : 
      47             : struct xchk_stats {
      48             :         struct dentry           *cs_debugfs;
      49             :         struct xchk_scrub_stats cs_stats[XFS_SCRUB_TYPE_NR];
      50             : };
      51             : 
      52             : 
      53             : static struct xchk_stats        global_stats;
      54             : 
      55             : static const char *name_map[XFS_SCRUB_TYPE_NR] = {
      56             :         [XFS_SCRUB_TYPE_SB]             = "sb",
      57             :         [XFS_SCRUB_TYPE_AGF]            = "agf",
      58             :         [XFS_SCRUB_TYPE_AGFL]           = "agfl",
      59             :         [XFS_SCRUB_TYPE_AGI]            = "agi",
      60             :         [XFS_SCRUB_TYPE_BNOBT]          = "bnobt",
      61             :         [XFS_SCRUB_TYPE_CNTBT]          = "cntbt",
      62             :         [XFS_SCRUB_TYPE_INOBT]          = "inobt",
      63             :         [XFS_SCRUB_TYPE_FINOBT]         = "finobt",
      64             :         [XFS_SCRUB_TYPE_RMAPBT]         = "rmapbt",
      65             :         [XFS_SCRUB_TYPE_REFCNTBT]       = "refcountbt",
      66             :         [XFS_SCRUB_TYPE_INODE]          = "inode",
      67             :         [XFS_SCRUB_TYPE_BMBTD]          = "bmapbtd",
      68             :         [XFS_SCRUB_TYPE_BMBTA]          = "bmapbta",
      69             :         [XFS_SCRUB_TYPE_BMBTC]          = "bmapbtc",
      70             :         [XFS_SCRUB_TYPE_DIR]            = "directory",
      71             :         [XFS_SCRUB_TYPE_XATTR]          = "xattr",
      72             :         [XFS_SCRUB_TYPE_SYMLINK]        = "symlink",
      73             :         [XFS_SCRUB_TYPE_PARENT]         = "parent",
      74             :         [XFS_SCRUB_TYPE_RTBITMAP]       = "rtbitmap",
      75             :         [XFS_SCRUB_TYPE_RTSUM]          = "rtsummary",
      76             :         [XFS_SCRUB_TYPE_UQUOTA]         = "usrquota",
      77             :         [XFS_SCRUB_TYPE_GQUOTA]         = "grpquota",
      78             :         [XFS_SCRUB_TYPE_PQUOTA]         = "prjquota",
      79             :         [XFS_SCRUB_TYPE_FSCOUNTERS]     = "fscounters",
      80             :         [XFS_SCRUB_TYPE_QUOTACHECK]     = "quotacheck",
      81             :         [XFS_SCRUB_TYPE_NLINKS]         = "nlinks",
      82             :         [XFS_SCRUB_TYPE_DIRTREE]        = "dirtree",
      83             :         [XFS_SCRUB_TYPE_RGSUPER]        = "rgsuper",
      84             :         [XFS_SCRUB_TYPE_RGBITMAP]       = "rgbitmap",
      85             :         [XFS_SCRUB_TYPE_RTRMAPBT]       = "rtrmapbt",
      86             :         [XFS_SCRUB_TYPE_RTREFCBT]       = "rtrefcountbt",
      87             : };
      88             : 
      89             : /* Format the scrub stats into a text buffer, similar to pcp style. */
      90             : STATIC ssize_t
      91           0 : xchk_stats_format(
      92             :         struct xchk_stats       *cs,
      93             :         char                    *buf,
      94             :         size_t                  remaining)
      95             : {
      96           0 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
      97           0 :         unsigned int            i;
      98           0 :         ssize_t                 copied = 0;
      99           0 :         int                     ret = 0;
     100             : 
     101           0 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
     102           0 :                 if (!name_map[i])
     103           0 :                         continue;
     104             : 
     105           0 :                 ret = scnprintf(buf, remaining,
     106             :  "%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu\n",
     107             :                                 name_map[i],
     108           0 :                                 (unsigned int)css->invocations,
     109           0 :                                 (unsigned int)css->clean,
     110           0 :                                 (unsigned int)css->corrupt,
     111           0 :                                 (unsigned int)css->preen,
     112           0 :                                 (unsigned int)css->xfail,
     113           0 :                                 (unsigned int)css->xcorrupt,
     114           0 :                                 (unsigned int)css->incomplete,
     115           0 :                                 (unsigned int)css->warning,
     116           0 :                                 (unsigned int)css->retries,
     117           0 :                                 (unsigned long long)css->checktime_us,
     118           0 :                                 (unsigned int)css->repair_invocations,
     119           0 :                                 (unsigned int)css->repair_success,
     120           0 :                                 (unsigned long long)css->repairtime_us);
     121           0 :                 if (ret <= 0)
     122             :                         break;
     123             : 
     124           0 :                 remaining -= ret;
     125           0 :                 copied += ret;
     126           0 :                 buf +=  ret;
     127             :         }
     128             : 
     129           0 :         return copied > 0 ? copied : ret;
     130             : }
     131             : 
     132             : /* Estimate the worst case buffer size required to hold the whole report. */
     133             : STATIC size_t
     134           0 : xchk_stats_estimate_bufsize(
     135             :         struct xchk_stats       *cs)
     136             : {
     137           0 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
     138           0 :         unsigned int            i;
     139           0 :         size_t                  field_width;
     140           0 :         size_t                  ret = 0;
     141             : 
     142             :         /* 4294967296 plus one space for each u32 field */
     143           0 :         field_width = 11 * (offsetof(struct xchk_scrub_stats, checktime_us) /
     144             :                             sizeof(uint32_t));
     145             : 
     146             :         /* 18446744073709551615 plus one space for each u64 field */
     147           0 :         field_width += 21 * ((offsetof(struct xchk_scrub_stats, css_lock) -
     148             :                               offsetof(struct xchk_scrub_stats, checktime_us)) /
     149             :                              sizeof(uint64_t));
     150             : 
     151           0 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
     152           0 :                 if (!name_map[i])
     153           0 :                         continue;
     154             : 
     155             :                 /* name plus one space */
     156           0 :                 ret += 1 + strlen(name_map[i]);
     157             : 
     158             :                 /* all fields, plus newline */
     159           0 :                 ret += field_width + 1;
     160             :         }
     161             : 
     162           0 :         return ret;
     163             : }
     164             : 
     165             : /* Clear all counters. */
     166             : STATIC void
     167           0 : xchk_stats_clearall(
     168             :         struct xchk_stats       *cs)
     169             : {
     170           0 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
     171           0 :         unsigned int            i;
     172             : 
     173           0 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
     174           0 :                 spin_lock(&css->css_lock);
     175           0 :                 memset(css, 0, offsetof(struct xchk_scrub_stats, css_lock));
     176           0 :                 spin_unlock(&css->css_lock);
     177             :         }
     178           0 : }
     179             : 
     180             : #define XFS_SCRUB_OFLAG_UNCLEAN (XFS_SCRUB_OFLAG_CORRUPT | \
     181             :                                  XFS_SCRUB_OFLAG_PREEN | \
     182             :                                  XFS_SCRUB_OFLAG_XFAIL | \
     183             :                                  XFS_SCRUB_OFLAG_XCORRUPT | \
     184             :                                  XFS_SCRUB_OFLAG_INCOMPLETE | \
     185             :                                  XFS_SCRUB_OFLAG_WARNING)
     186             : 
     187             : STATIC void
     188   857070886 : xchk_stats_merge_one(
     189             :         struct xchk_stats               *cs,
     190             :         const struct xfs_scrub_metadata *sm,
     191             :         const struct xchk_stats_run     *run)
     192             : {
     193   857070886 :         struct xchk_scrub_stats         *css;
     194             : 
     195   857070886 :         ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR);
     196             : 
     197   857070886 :         css = &cs->cs_stats[sm->sm_type];
     198   857070886 :         spin_lock(&css->css_lock);
     199   857065341 :         css->invocations++;
     200   857065341 :         if (!(sm->sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))
     201   847213195 :                 css->clean++;
     202   857065341 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     203          36 :                 css->corrupt++;
     204   857065341 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_PREEN)
     205     9867966 :                 css->preen++;
     206   857065341 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL)
     207           0 :                 css->xfail++;
     208   857065341 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
     209           4 :                 css->xcorrupt++;
     210   857065341 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
     211          58 :                 css->incomplete++;
     212   857065341 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING)
     213          54 :                 css->warning++;
     214   857065341 :         css->retries += run->retries;
     215   857065341 :         css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC);
     216             : 
     217   857065341 :         if (run->repair_attempted)
     218     3487116 :                 css->repair_invocations++;
     219   857065341 :         if (run->repair_succeeded)
     220     3481647 :                 css->repair_success++;
     221   857065341 :         css->repairtime_us += howmany_64(run->repair_ns, NSEC_PER_USEC);
     222   857065341 :         spin_unlock(&css->css_lock);
     223   857084639 : }
     224             : 
     225             : /* Merge these scrub-run stats into the global and mount stat data. */
     226             : void
     227   428752544 : xchk_stats_merge(
     228             :         struct xfs_mount                *mp,
     229             :         const struct xfs_scrub_metadata *sm,
     230             :         const struct xchk_stats_run     *run)
     231             : {
     232   428752544 :         xchk_stats_merge_one(&global_stats, sm, run);
     233   428746199 :         xchk_stats_merge_one(mp->m_scrub_stats, sm, run);
     234   428745019 : }
     235             : 
     236             : /* debugfs boilerplate */
     237             : 
     238             : static ssize_t
     239           0 : xchk_scrub_stats_read(
     240             :         struct file             *file,
     241             :         char __user             *ubuf,
     242             :         size_t                  count,
     243             :         loff_t                  *ppos)
     244             : {
     245           0 :         struct xchk_stats       *cs = file->private_data;
     246           0 :         char                    *buf;
     247           0 :         size_t                  bufsize;
     248           0 :         ssize_t                 avail, ret;
     249             : 
     250             :         /*
     251             :          * This generates stringly snapshot of all the scrub counters, so we
     252             :          * do not want userspace to receive garbled text from multiple calls.
     253             :          * If the file position is greater than 0, return a short read.
     254             :          */
     255           0 :         if (*ppos > 0)
     256             :                 return 0;
     257             : 
     258           0 :         bufsize = xchk_stats_estimate_bufsize(cs);
     259             : 
     260           0 :         buf = kvmalloc(bufsize, XCHK_GFP_FLAGS);
     261           0 :         if (!buf)
     262             :                 return -ENOMEM;
     263             : 
     264           0 :         avail = xchk_stats_format(cs, buf, bufsize);
     265           0 :         if (avail < 0) {
     266           0 :                 ret = avail;
     267           0 :                 goto out;
     268             :         }
     269             : 
     270           0 :         ret = simple_read_from_buffer(ubuf, count, ppos, buf, avail);
     271           0 : out:
     272           0 :         kvfree(buf);
     273           0 :         return ret;
     274             : }
     275             : 
     276             : static const struct file_operations scrub_stats_fops = {
     277             :         .open                   = simple_open,
     278             :         .read                   = xchk_scrub_stats_read,
     279             : };
     280             : 
     281             : static ssize_t
     282           0 : xchk_clear_scrub_stats_write(
     283             :         struct file             *file,
     284             :         const char __user       *ubuf,
     285             :         size_t                  count,
     286             :         loff_t                  *ppos)
     287             : {
     288           0 :         struct xchk_stats       *cs = file->private_data;
     289           0 :         unsigned int            val;
     290           0 :         int                     ret;
     291             : 
     292           0 :         ret = kstrtouint_from_user(ubuf, count, 0, &val);
     293           0 :         if (ret)
     294           0 :                 return ret;
     295             : 
     296           0 :         if (val != 1)
     297             :                 return -EINVAL;
     298             : 
     299           0 :         xchk_stats_clearall(cs);
     300           0 :         return count;
     301             : }
     302             : 
     303             : static const struct file_operations clear_scrub_stats_fops = {
     304             :         .open                   = simple_open,
     305             :         .write                  = xchk_clear_scrub_stats_write,
     306             : };
     307             : 
     308             : /* Initialize the stats object. */
     309             : STATIC int
     310       24387 : xchk_stats_init(
     311             :         struct xchk_stats       *cs,
     312             :         struct xfs_mount        *mp)
     313             : {
     314       24387 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
     315       24387 :         unsigned int            i;
     316             : 
     317      829158 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++)
     318      804771 :                 spin_lock_init(&css->css_lock);
     319             : 
     320       24387 :         return 0;
     321             : }
     322             : 
     323             : /* Connect the stats object to debugfs. */
     324             : void
     325       24349 : xchk_stats_register(
     326             :         struct xchk_stats       *cs,
     327             :         struct dentry           *parent)
     328             : {
     329       24349 :         if (!parent)
     330             :                 return;
     331             : 
     332       24349 :         cs->cs_debugfs = xfs_debugfs_mkdir("scrub", parent);
     333       24349 :         if (!cs->cs_debugfs)
     334             :                 return;
     335             : 
     336       24349 :         debugfs_create_file("stats", 0644, cs->cs_debugfs, cs,
     337             :                         &scrub_stats_fops);
     338       24349 :         debugfs_create_file("clear_stats", 0400, cs->cs_debugfs, cs,
     339             :                         &clear_scrub_stats_fops);
     340             : }
     341             : 
     342             : /* Free all resources related to the stats object. */
     343             : STATIC int
     344             : xchk_stats_teardown(
     345             :         struct xchk_stats       *cs)
     346             : {
     347             :         return 0;
     348             : }
     349             : 
     350             : /* Disconnect the stats object from debugfs. */
     351             : void
     352       24343 : xchk_stats_unregister(
     353             :         struct xchk_stats       *cs)
     354             : {
     355       24343 :         debugfs_remove(cs->cs_debugfs);
     356       24343 : }
     357             : 
     358             : /* Initialize global stats and register them */
     359             : int __init
     360          12 : xchk_global_stats_setup(
     361             :         struct dentry           *parent)
     362             : {
     363          12 :         int                     error;
     364             : 
     365          12 :         error = xchk_stats_init(&global_stats, NULL);
     366          12 :         if (error)
     367             :                 return error;
     368             : 
     369          12 :         xchk_stats_register(&global_stats, parent);
     370          12 :         return 0;
     371             : }
     372             : 
     373             : /* Unregister global stats and tear them down */
     374             : void
     375          12 : xchk_global_stats_teardown(void)
     376             : {
     377          12 :         xchk_stats_unregister(&global_stats);
     378          12 :         xchk_stats_teardown(&global_stats);
     379          12 : }
     380             : 
     381             : /* Allocate per-mount stats */
     382             : int
     383       24375 : xchk_mount_stats_alloc(
     384             :         struct xfs_mount        *mp)
     385             : {
     386       24375 :         struct xchk_stats       *cs;
     387       24375 :         int                     error;
     388             : 
     389       24375 :         cs = kvzalloc(sizeof(struct xchk_stats), GFP_KERNEL);
     390       24375 :         if (!cs)
     391             :                 return -ENOMEM;
     392             : 
     393       24375 :         error = xchk_stats_init(cs, mp);
     394       24375 :         if (error)
     395           0 :                 goto out_free;
     396             : 
     397       24375 :         mp->m_scrub_stats = cs;
     398       24375 :         return 0;
     399             : out_free:
     400           0 :         kvfree(cs);
     401           0 :         return error;
     402             : }
     403             : 
     404             : /* Free per-mount stats */
     405             : void
     406       24381 : xchk_mount_stats_free(
     407             :         struct xfs_mount        *mp)
     408             : {
     409       24381 :         xchk_stats_teardown(mp->m_scrub_stats);
     410       24381 :         kvfree(mp->m_scrub_stats);
     411       24381 :         mp->m_scrub_stats = NULL;
     412       24381 : }

Generated by: LCOV version 1.14