brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · 7996c23 Raw
412 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 19struct 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 47struct xchk_stats {48	struct dentry		*cs_debugfs;49	struct xchk_scrub_stats	cs_stats[XFS_SCRUB_TYPE_NR];50};51 52 53static struct xchk_stats	global_stats;54 55static 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. */86STATIC ssize_t87xchk_stats_format(88	struct xchk_stats	*cs,89	char			*buf,90	size_t			remaining)91{92	struct xchk_scrub_stats	*css = &cs->cs_stats[0];93	unsigned int		i;94	ssize_t			copied = 0;95	int			ret = 0;96 97	for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {98		if (!name_map[i])99			continue;100 101		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				(unsigned int)css->invocations,105				(unsigned int)css->clean,106				(unsigned int)css->corrupt,107				(unsigned int)css->preen,108				(unsigned int)css->xfail,109				(unsigned int)css->xcorrupt,110				(unsigned int)css->incomplete,111				(unsigned int)css->warning,112				(unsigned int)css->retries,113				(unsigned long long)css->checktime_us,114				(unsigned int)css->repair_invocations,115				(unsigned int)css->repair_success,116				(unsigned long long)css->repairtime_us);117		if (ret <= 0)118			break;119 120		remaining -= ret;121		copied += ret;122		buf +=  ret;123	}124 125	return copied > 0 ? copied : ret;126}127 128/* Estimate the worst case buffer size required to hold the whole report. */129STATIC size_t130xchk_stats_estimate_bufsize(131	struct xchk_stats	*cs)132{133	struct xchk_scrub_stats	*css = &cs->cs_stats[0];134	unsigned int		i;135	size_t			field_width;136	size_t			ret = 0;137 138	/* 4294967296 plus one space for each u32 field */139	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	field_width += 21 * ((offsetof(struct xchk_scrub_stats, css_lock) -144			      offsetof(struct xchk_scrub_stats, checktime_us)) /145			     sizeof(uint64_t));146 147	for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {148		if (!name_map[i])149			continue;150 151		/* name plus one space */152		ret += 1 + strlen(name_map[i]);153 154		/* all fields, plus newline */155		ret += field_width + 1;156	}157 158	return ret;159}160 161/* Clear all counters. */162STATIC void163xchk_stats_clearall(164	struct xchk_stats	*cs)165{166	struct xchk_scrub_stats	*css = &cs->cs_stats[0];167	unsigned int		i;168 169	for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) {170		spin_lock(&css->css_lock);171		memset(css, 0, offsetof(struct xchk_scrub_stats, css_lock));172		spin_unlock(&css->css_lock);173	}174}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 183STATIC void184xchk_stats_merge_one(185	struct xchk_stats		*cs,186	const struct xfs_scrub_metadata	*sm,187	const struct xchk_stats_run	*run)188{189	struct xchk_scrub_stats		*css;190 191	if (sm->sm_type >= XFS_SCRUB_TYPE_NR) {192		ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR);193		return;194	}195 196	css = &cs->cs_stats[sm->sm_type];197	spin_lock(&css->css_lock);198	css->invocations++;199	if (!(sm->sm_flags & XFS_SCRUB_OFLAG_UNCLEAN))200		css->clean++;201	if (sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)202		css->corrupt++;203	if (sm->sm_flags & XFS_SCRUB_OFLAG_PREEN)204		css->preen++;205	if (sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL)206		css->xfail++;207	if (sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)208		css->xcorrupt++;209	if (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)210		css->incomplete++;211	if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING)212		css->warning++;213	css->retries += run->retries;214	css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC);215 216	if (run->repair_attempted)217		css->repair_invocations++;218	if (run->repair_succeeded)219		css->repair_success++;220	css->repairtime_us += howmany_64(run->repair_ns, NSEC_PER_USEC);221	spin_unlock(&css->css_lock);222}223 224/* Merge these scrub-run stats into the global and mount stat data. */225void226xchk_stats_merge(227	struct xfs_mount		*mp,228	const struct xfs_scrub_metadata	*sm,229	const struct xchk_stats_run	*run)230{231	xchk_stats_merge_one(&global_stats, sm, run);232	xchk_stats_merge_one(mp->m_scrub_stats, sm, run);233}234 235/* debugfs boilerplate */236 237static ssize_t238xchk_scrub_stats_read(239	struct file		*file,240	char __user		*ubuf,241	size_t			count,242	loff_t			*ppos)243{244	struct xchk_stats	*cs = file->private_data;245	char			*buf;246	size_t			bufsize;247	ssize_t			avail, ret;248 249	/*250	 * This generates stringly snapshot of all the scrub counters, so we251	 * do not want userspace to receive garbled text from multiple calls.252	 * If the file position is greater than 0, return a short read.253	 */254	if (*ppos > 0)255		return 0;256 257	bufsize = xchk_stats_estimate_bufsize(cs);258 259	buf = kvmalloc(bufsize, XCHK_GFP_FLAGS);260	if (!buf)261		return -ENOMEM;262 263	avail = xchk_stats_format(cs, buf, bufsize);264	if (avail < 0) {265		ret = avail;266		goto out;267	}268 269	ret = simple_read_from_buffer(ubuf, count, ppos, buf, avail);270out:271	kvfree(buf);272	return ret;273}274 275static const struct file_operations scrub_stats_fops = {276	.open			= simple_open,277	.read			= xchk_scrub_stats_read,278};279 280static ssize_t281xchk_clear_scrub_stats_write(282	struct file		*file,283	const char __user	*ubuf,284	size_t			count,285	loff_t			*ppos)286{287	struct xchk_stats	*cs = file->private_data;288	unsigned int		val;289	int			ret;290 291	ret = kstrtouint_from_user(ubuf, count, 0, &val);292	if (ret)293		return ret;294 295	if (val != 1)296		return -EINVAL;297 298	xchk_stats_clearall(cs);299	return count;300}301 302static const struct file_operations clear_scrub_stats_fops = {303	.open			= simple_open,304	.write			= xchk_clear_scrub_stats_write,305};306 307/* Initialize the stats object. */308STATIC int309xchk_stats_init(310	struct xchk_stats	*cs,311	struct xfs_mount	*mp)312{313	struct xchk_scrub_stats	*css = &cs->cs_stats[0];314	unsigned int		i;315 316	for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++)317		spin_lock_init(&css->css_lock);318 319	return 0;320}321 322/* Connect the stats object to debugfs. */323void324xchk_stats_register(325	struct xchk_stats	*cs,326	struct dentry		*parent)327{328	if (!parent)329		return;330 331	cs->cs_debugfs = xfs_debugfs_mkdir("scrub", parent);332	if (!cs->cs_debugfs)333		return;334 335	debugfs_create_file("stats", 0444, cs->cs_debugfs, cs,336			&scrub_stats_fops);337	debugfs_create_file("clear_stats", 0200, cs->cs_debugfs, cs,338			&clear_scrub_stats_fops);339}340 341/* Free all resources related to the stats object. */342STATIC int343xchk_stats_teardown(344	struct xchk_stats	*cs)345{346	return 0;347}348 349/* Disconnect the stats object from debugfs. */350void351xchk_stats_unregister(352	struct xchk_stats	*cs)353{354	debugfs_remove(cs->cs_debugfs);355}356 357/* Initialize global stats and register them */358int __init359xchk_global_stats_setup(360	struct dentry		*parent)361{362	int			error;363 364	error = xchk_stats_init(&global_stats, NULL);365	if (error)366		return error;367 368	xchk_stats_register(&global_stats, parent);369	return 0;370}371 372/* Unregister global stats and tear them down */373void374xchk_global_stats_teardown(void)375{376	xchk_stats_unregister(&global_stats);377	xchk_stats_teardown(&global_stats);378}379 380/* Allocate per-mount stats */381int382xchk_mount_stats_alloc(383	struct xfs_mount	*mp)384{385	struct xchk_stats	*cs;386	int			error;387 388	cs = kvzalloc(sizeof(struct xchk_stats), GFP_KERNEL);389	if (!cs)390		return -ENOMEM;391 392	error = xchk_stats_init(cs, mp);393	if (error)394		goto out_free;395 396	mp->m_scrub_stats = cs;397	return 0;398out_free:399	kvfree(cs);400	return error;401}402 403/* Free per-mount stats */404void405xchk_mount_stats_free(406	struct xfs_mount	*mp)407{408	xchk_stats_teardown(mp->m_scrub_stats);409	kvfree(mp->m_scrub_stats);410	mp->m_scrub_stats = NULL;411}412