LCOV - code coverage report
Current view: top level - fs/xfs/scrub - stats.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-acha @ Mon Jul 31 20:08:06 PDT 2023 Lines: 71 150 47.3 %
Date: 2023-07-31 20:08:07 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             : };
      84             : 
      85             : /* Format the scrub stats into a text buffer, similar to pcp style. */
      86             : STATIC ssize_t
      87           0 : xchk_stats_format(
      88             :         struct xchk_stats       *cs,
      89             :         char                    *buf,
      90             :         size_t                  remaining)
      91             : {
      92           0 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
      93           0 :         unsigned int            i;
      94           0 :         ssize_t                 copied = 0;
      95           0 :         int                     ret = 0;
      96             : 
      97           0 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
      98           0 :                 if (!name_map[i])
      99           0 :                         continue;
     100             : 
     101           0 :                 ret = scnprintf(buf, remaining,
     102             :  "%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu\n",
     103             :                                 name_map[i],
     104           0 :                                 (unsigned int)css->invocations,
     105           0 :                                 (unsigned int)css->clean,
     106           0 :                                 (unsigned int)css->corrupt,
     107           0 :                                 (unsigned int)css->preen,
     108           0 :                                 (unsigned int)css->xfail,
     109           0 :                                 (unsigned int)css->xcorrupt,
     110           0 :                                 (unsigned int)css->incomplete,
     111           0 :                                 (unsigned int)css->warning,
     112           0 :                                 (unsigned int)css->retries,
     113           0 :                                 (unsigned long long)css->checktime_us,
     114           0 :                                 (unsigned int)css->repair_invocations,
     115           0 :                                 (unsigned int)css->repair_success,
     116           0 :                                 (unsigned long long)css->repairtime_us);
     117           0 :                 if (ret <= 0)
     118             :                         break;
     119             : 
     120           0 :                 remaining -= ret;
     121           0 :                 copied += ret;
     122           0 :                 buf +=  ret;
     123             :         }
     124             : 
     125           0 :         return copied > 0 ? copied : ret;
     126             : }
     127             : 
     128             : /* Estimate the worst case buffer size required to hold the whole report. */
     129             : STATIC size_t
     130           0 : xchk_stats_estimate_bufsize(
     131             :         struct xchk_stats       *cs)
     132             : {
     133           0 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
     134           0 :         unsigned int            i;
     135           0 :         size_t                  field_width;
     136           0 :         size_t                  ret = 0;
     137             : 
     138             :         /* 4294967296 plus one space for each u32 field */
     139           0 :         field_width = 11 * (offsetof(struct xchk_scrub_stats, checktime_us) /
     140             :                             sizeof(uint32_t));
     141             : 
     142             :         /* 18446744073709551615 plus one space for each u64 field */
     143           0 :         field_width += 21 * ((offsetof(struct xchk_scrub_stats, css_lock) -
     144             :                               offsetof(struct xchk_scrub_stats, checktime_us)) /
     145             :                              sizeof(uint64_t));
     146             : 
     147           0 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
     148           0 :                 if (!name_map[i])
     149           0 :                         continue;
     150             : 
     151             :                 /* name plus one space */
     152           0 :                 ret += 1 + strlen(name_map[i]);
     153             : 
     154             :                 /* all fields, plus newline */
     155           0 :                 ret += field_width + 1;
     156             :         }
     157             : 
     158           0 :         return ret;
     159             : }
     160             : 
     161             : /* Clear all counters. */
     162             : STATIC void
     163           0 : xchk_stats_clearall(
     164             :         struct xchk_stats       *cs)
     165             : {
     166           0 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
     167           0 :         unsigned int            i;
     168             : 
     169           0 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {
     170           0 :                 spin_lock(&css->css_lock);
     171           0 :                 memset(css, 0, offsetof(struct xchk_scrub_stats, css_lock));
     172           0 :                 spin_unlock(&css->css_lock);
     173             :         }
     174           0 : }
     175             : 
     176             : #define XFS_SCRUB_OFLAG_UNCLEAN (XFS_SCRUB_OFLAG_CORRUPT | \
     177             :                                  XFS_SCRUB_OFLAG_PREEN | \
     178             :                                  XFS_SCRUB_OFLAG_XFAIL | \
     179             :                                  XFS_SCRUB_OFLAG_XCORRUPT | \
     180             :                                  XFS_SCRUB_OFLAG_INCOMPLETE | \
     181             :                                  XFS_SCRUB_OFLAG_WARNING)
     182             : 
     183             : STATIC void
     184  1096735042 : xchk_stats_merge_one(
     185             :         struct xchk_stats               *cs,
     186             :         const struct xfs_scrub_metadata *sm,
     187             :         const struct xchk_stats_run     *run)
     188             : {
     189  1096735042 :         struct xchk_scrub_stats         *css;
     190             : 
     191  1096735042 :         ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR);
     192             : 
     193  1096735042 :         css = &cs->cs_stats[sm->sm_type];
     194  1096735042 :         spin_lock(&css->css_lock);
     195  1097014065 :         css->invocations++;
     196  1097014065 :         if (!(sm->sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))
     197  1089575413 :                 css->clean++;
     198  1097014065 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     199          36 :                 css->corrupt++;
     200  1097014065 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_PREEN)
     201     7437767 :                 css->preen++;
     202  1097014065 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL)
     203           0 :                 css->xfail++;
     204  1097014065 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
     205           4 :                 css->xcorrupt++;
     206  1097014065 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
     207          62 :                 css->incomplete++;
     208  1097014065 :         if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING)
     209          48 :                 css->warning++;
     210  1097014065 :         css->retries += run->retries;
     211  1097014065 :         css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC);
     212             : 
     213  1097014065 :         if (run->repair_attempted)
     214    37100154 :                 css->repair_invocations++;
     215  1097014065 :         if (run->repair_succeeded)
     216     8700118 :                 css->repair_success++;
     217  1097014065 :         css->repairtime_us += howmany_64(run->repair_ns, NSEC_PER_USEC);
     218  1097014065 :         spin_unlock(&css->css_lock);
     219  1096886117 : }
     220             : 
     221             : /* Merge these scrub-run stats into the global and mount stat data. */
     222             : void
     223   548610715 : xchk_stats_merge(
     224             :         struct xfs_mount                *mp,
     225             :         const struct xfs_scrub_metadata *sm,
     226             :         const struct xchk_stats_run     *run)
     227             : {
     228   548610715 :         xchk_stats_merge_one(&global_stats, sm, run);
     229   548627720 :         xchk_stats_merge_one(mp->m_scrub_stats, sm, run);
     230   548627864 : }
     231             : 
     232             : /* debugfs boilerplate */
     233             : 
     234             : static ssize_t
     235           0 : xchk_scrub_stats_read(
     236             :         struct file             *file,
     237             :         char __user             *ubuf,
     238             :         size_t                  count,
     239             :         loff_t                  *ppos)
     240             : {
     241           0 :         struct xchk_stats       *cs = file->private_data;
     242           0 :         char                    *buf;
     243           0 :         size_t                  bufsize;
     244           0 :         ssize_t                 avail, ret;
     245             : 
     246             :         /*
     247             :          * This generates stringly snapshot of all the scrub counters, so we
     248             :          * do not want userspace to receive garbled text from multiple calls.
     249             :          * If the file position is greater than 0, return a short read.
     250             :          */
     251           0 :         if (*ppos > 0)
     252             :                 return 0;
     253             : 
     254           0 :         bufsize = xchk_stats_estimate_bufsize(cs);
     255             : 
     256           0 :         buf = kvmalloc(bufsize, XCHK_GFP_FLAGS);
     257           0 :         if (!buf)
     258             :                 return -ENOMEM;
     259             : 
     260           0 :         avail = xchk_stats_format(cs, buf, bufsize);
     261           0 :         if (avail < 0) {
     262           0 :                 ret = avail;
     263           0 :                 goto out;
     264             :         }
     265             : 
     266           0 :         ret = simple_read_from_buffer(ubuf, count, ppos, buf, avail);
     267           0 : out:
     268           0 :         kvfree(buf);
     269           0 :         return ret;
     270             : }
     271             : 
     272             : static const struct file_operations scrub_stats_fops = {
     273             :         .open                   = simple_open,
     274             :         .read                   = xchk_scrub_stats_read,
     275             : };
     276             : 
     277             : static ssize_t
     278           0 : xchk_clear_scrub_stats_write(
     279             :         struct file             *file,
     280             :         const char __user       *ubuf,
     281             :         size_t                  count,
     282             :         loff_t                  *ppos)
     283             : {
     284           0 :         struct xchk_stats       *cs = file->private_data;
     285           0 :         unsigned int            val;
     286           0 :         int                     ret;
     287             : 
     288           0 :         ret = kstrtouint_from_user(ubuf, count, 0, &val);
     289           0 :         if (ret)
     290           0 :                 return ret;
     291             : 
     292           0 :         if (val != 1)
     293             :                 return -EINVAL;
     294             : 
     295           0 :         xchk_stats_clearall(cs);
     296           0 :         return count;
     297             : }
     298             : 
     299             : static const struct file_operations clear_scrub_stats_fops = {
     300             :         .open                   = simple_open,
     301             :         .write                  = xchk_clear_scrub_stats_write,
     302             : };
     303             : 
     304             : /* Initialize the stats object. */
     305             : STATIC int
     306       24169 : xchk_stats_init(
     307             :         struct xchk_stats       *cs,
     308             :         struct xfs_mount        *mp)
     309             : {
     310       24169 :         struct xchk_scrub_stats *css = &cs->cs_stats[0];
     311       24169 :         unsigned int            i;
     312             : 
     313      725070 :         for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++)
     314      700901 :                 spin_lock_init(&css->css_lock);
     315             : 
     316       24169 :         return 0;
     317             : }
     318             : 
     319             : /* Connect the stats object to debugfs. */
     320             : void
     321       24131 : xchk_stats_register(
     322             :         struct xchk_stats       *cs,
     323             :         struct dentry           *parent)
     324             : {
     325       24131 :         if (!parent)
     326             :                 return;
     327             : 
     328       24131 :         cs->cs_debugfs = xfs_debugfs_mkdir("scrub", parent);
     329       24131 :         if (!cs->cs_debugfs)
     330             :                 return;
     331             : 
     332       24131 :         debugfs_create_file("stats", 0644, cs->cs_debugfs, cs,
     333             :                         &scrub_stats_fops);
     334       24131 :         debugfs_create_file("clear_stats", 0400, cs->cs_debugfs, cs,
     335             :                         &clear_scrub_stats_fops);
     336             : }
     337             : 
     338             : /* Free all resources related to the stats object. */
     339             : STATIC int
     340             : xchk_stats_teardown(
     341             :         struct xchk_stats       *cs)
     342             : {
     343             :         return 0;
     344             : }
     345             : 
     346             : /* Disconnect the stats object from debugfs. */
     347             : void
     348       24126 : xchk_stats_unregister(
     349             :         struct xchk_stats       *cs)
     350             : {
     351       24126 :         debugfs_remove(cs->cs_debugfs);
     352       24126 : }
     353             : 
     354             : /* Initialize global stats and register them */
     355             : int __init
     356          12 : xchk_global_stats_setup(
     357             :         struct dentry           *parent)
     358             : {
     359          12 :         int                     error;
     360             : 
     361          12 :         error = xchk_stats_init(&global_stats, NULL);
     362          12 :         if (error)
     363             :                 return error;
     364             : 
     365          12 :         xchk_stats_register(&global_stats, parent);
     366          12 :         return 0;
     367             : }
     368             : 
     369             : /* Unregister global stats and tear them down */
     370             : void
     371          12 : xchk_global_stats_teardown(void)
     372             : {
     373          12 :         xchk_stats_unregister(&global_stats);
     374          12 :         xchk_stats_teardown(&global_stats);
     375          12 : }
     376             : 
     377             : /* Allocate per-mount stats */
     378             : int
     379       24157 : xchk_mount_stats_alloc(
     380             :         struct xfs_mount        *mp)
     381             : {
     382       24157 :         struct xchk_stats       *cs;
     383       24157 :         int                     error;
     384             : 
     385       24157 :         cs = kvzalloc(sizeof(struct xchk_stats), GFP_KERNEL);
     386       24157 :         if (!cs)
     387             :                 return -ENOMEM;
     388             : 
     389       24157 :         error = xchk_stats_init(cs, mp);
     390       24157 :         if (error)
     391           0 :                 goto out_free;
     392             : 
     393       24157 :         mp->m_scrub_stats = cs;
     394       24157 :         return 0;
     395             : out_free:
     396           0 :         kvfree(cs);
     397           0 :         return error;
     398             : }
     399             : 
     400             : /* Free per-mount stats */
     401             : void
     402       24164 : xchk_mount_stats_free(
     403             :         struct xfs_mount        *mp)
     404             : {
     405       24164 :         xchk_stats_teardown(mp->m_scrub_stats);
     406       24164 :         kvfree(mp->m_scrub_stats);
     407       24164 :         mp->m_scrub_stats = NULL;
     408       24164 : }

Generated by: LCOV version 1.14