162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014 Red Hat, Inc.
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "xfs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_log_format.h"
1162306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1262306a36Sopenharmony_ci#include "xfs_sysfs.h"
1362306a36Sopenharmony_ci#include "xfs_log.h"
1462306a36Sopenharmony_ci#include "xfs_log_priv.h"
1562306a36Sopenharmony_ci#include "xfs_mount.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct xfs_sysfs_attr {
1862306a36Sopenharmony_ci	struct attribute attr;
1962306a36Sopenharmony_ci	ssize_t (*show)(struct kobject *kobject, char *buf);
2062306a36Sopenharmony_ci	ssize_t (*store)(struct kobject *kobject, const char *buf,
2162306a36Sopenharmony_ci			 size_t count);
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic inline struct xfs_sysfs_attr *
2562306a36Sopenharmony_cito_attr(struct attribute *attr)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	return container_of(attr, struct xfs_sysfs_attr, attr);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define XFS_SYSFS_ATTR_RW(name) \
3162306a36Sopenharmony_ci	static struct xfs_sysfs_attr xfs_sysfs_attr_##name = __ATTR_RW(name)
3262306a36Sopenharmony_ci#define XFS_SYSFS_ATTR_RO(name) \
3362306a36Sopenharmony_ci	static struct xfs_sysfs_attr xfs_sysfs_attr_##name = __ATTR_RO(name)
3462306a36Sopenharmony_ci#define XFS_SYSFS_ATTR_WO(name) \
3562306a36Sopenharmony_ci	static struct xfs_sysfs_attr xfs_sysfs_attr_##name = __ATTR_WO(name)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define ATTR_LIST(name) &xfs_sysfs_attr_##name.attr
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciSTATIC ssize_t
4062306a36Sopenharmony_cixfs_sysfs_object_show(
4162306a36Sopenharmony_ci	struct kobject		*kobject,
4262306a36Sopenharmony_ci	struct attribute	*attr,
4362306a36Sopenharmony_ci	char			*buf)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct xfs_sysfs_attr *xfs_attr = to_attr(attr);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return xfs_attr->show ? xfs_attr->show(kobject, buf) : 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciSTATIC ssize_t
5162306a36Sopenharmony_cixfs_sysfs_object_store(
5262306a36Sopenharmony_ci	struct kobject		*kobject,
5362306a36Sopenharmony_ci	struct attribute	*attr,
5462306a36Sopenharmony_ci	const char		*buf,
5562306a36Sopenharmony_ci	size_t			count)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct xfs_sysfs_attr *xfs_attr = to_attr(attr);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return xfs_attr->store ? xfs_attr->store(kobject, buf, count) : 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct sysfs_ops xfs_sysfs_ops = {
6362306a36Sopenharmony_ci	.show = xfs_sysfs_object_show,
6462306a36Sopenharmony_ci	.store = xfs_sysfs_object_store,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic struct attribute *xfs_mp_attrs[] = {
6862306a36Sopenharmony_ci	NULL,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ciATTRIBUTE_GROUPS(xfs_mp);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciconst struct kobj_type xfs_mp_ktype = {
7362306a36Sopenharmony_ci	.release = xfs_sysfs_release,
7462306a36Sopenharmony_ci	.sysfs_ops = &xfs_sysfs_ops,
7562306a36Sopenharmony_ci	.default_groups = xfs_mp_groups,
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#ifdef DEBUG
7962306a36Sopenharmony_ci/* debug */
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciSTATIC ssize_t
8262306a36Sopenharmony_cibug_on_assert_store(
8362306a36Sopenharmony_ci	struct kobject		*kobject,
8462306a36Sopenharmony_ci	const char		*buf,
8562306a36Sopenharmony_ci	size_t			count)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	int			ret;
8862306a36Sopenharmony_ci	int			val;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
9162306a36Sopenharmony_ci	if (ret)
9262306a36Sopenharmony_ci		return ret;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (val == 1)
9562306a36Sopenharmony_ci		xfs_globals.bug_on_assert = true;
9662306a36Sopenharmony_ci	else if (val == 0)
9762306a36Sopenharmony_ci		xfs_globals.bug_on_assert = false;
9862306a36Sopenharmony_ci	else
9962306a36Sopenharmony_ci		return -EINVAL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return count;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciSTATIC ssize_t
10562306a36Sopenharmony_cibug_on_assert_show(
10662306a36Sopenharmony_ci	struct kobject		*kobject,
10762306a36Sopenharmony_ci	char			*buf)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", xfs_globals.bug_on_assert);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(bug_on_assert);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciSTATIC ssize_t
11462306a36Sopenharmony_cilog_recovery_delay_store(
11562306a36Sopenharmony_ci	struct kobject	*kobject,
11662306a36Sopenharmony_ci	const char	*buf,
11762306a36Sopenharmony_ci	size_t		count)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int		ret;
12062306a36Sopenharmony_ci	int		val;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
12362306a36Sopenharmony_ci	if (ret)
12462306a36Sopenharmony_ci		return ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (val < 0 || val > 60)
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	xfs_globals.log_recovery_delay = val;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return count;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciSTATIC ssize_t
13562306a36Sopenharmony_cilog_recovery_delay_show(
13662306a36Sopenharmony_ci	struct kobject	*kobject,
13762306a36Sopenharmony_ci	char		*buf)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", xfs_globals.log_recovery_delay);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(log_recovery_delay);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ciSTATIC ssize_t
14462306a36Sopenharmony_cimount_delay_store(
14562306a36Sopenharmony_ci	struct kobject	*kobject,
14662306a36Sopenharmony_ci	const char	*buf,
14762306a36Sopenharmony_ci	size_t		count)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	int		ret;
15062306a36Sopenharmony_ci	int		val;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
15362306a36Sopenharmony_ci	if (ret)
15462306a36Sopenharmony_ci		return ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (val < 0 || val > 60)
15762306a36Sopenharmony_ci		return -EINVAL;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	xfs_globals.mount_delay = val;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return count;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciSTATIC ssize_t
16562306a36Sopenharmony_cimount_delay_show(
16662306a36Sopenharmony_ci	struct kobject	*kobject,
16762306a36Sopenharmony_ci	char		*buf)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", xfs_globals.mount_delay);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(mount_delay);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic ssize_t
17462306a36Sopenharmony_cialways_cow_store(
17562306a36Sopenharmony_ci	struct kobject	*kobject,
17662306a36Sopenharmony_ci	const char	*buf,
17762306a36Sopenharmony_ci	size_t		count)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	ssize_t		ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ret = kstrtobool(buf, &xfs_globals.always_cow);
18262306a36Sopenharmony_ci	if (ret < 0)
18362306a36Sopenharmony_ci		return ret;
18462306a36Sopenharmony_ci	return count;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic ssize_t
18862306a36Sopenharmony_cialways_cow_show(
18962306a36Sopenharmony_ci	struct kobject	*kobject,
19062306a36Sopenharmony_ci	char		*buf)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", xfs_globals.always_cow);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(always_cow);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci#ifdef DEBUG
19762306a36Sopenharmony_ci/*
19862306a36Sopenharmony_ci * Override how many threads the parallel work queue is allowed to create.
19962306a36Sopenharmony_ci * This has to be a debug-only global (instead of an errortag) because one of
20062306a36Sopenharmony_ci * the main users of parallel workqueues is mount time quotacheck.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_ciSTATIC ssize_t
20362306a36Sopenharmony_cipwork_threads_store(
20462306a36Sopenharmony_ci	struct kobject	*kobject,
20562306a36Sopenharmony_ci	const char	*buf,
20662306a36Sopenharmony_ci	size_t		count)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int		ret;
20962306a36Sopenharmony_ci	int		val;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		return ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (val < -1 || val > num_possible_cpus())
21662306a36Sopenharmony_ci		return -EINVAL;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	xfs_globals.pwork_threads = val;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return count;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ciSTATIC ssize_t
22462306a36Sopenharmony_cipwork_threads_show(
22562306a36Sopenharmony_ci	struct kobject	*kobject,
22662306a36Sopenharmony_ci	char		*buf)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", xfs_globals.pwork_threads);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(pwork_threads);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic ssize_t
23362306a36Sopenharmony_cilarp_store(
23462306a36Sopenharmony_ci	struct kobject	*kobject,
23562306a36Sopenharmony_ci	const char	*buf,
23662306a36Sopenharmony_ci	size_t		count)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	ssize_t		ret;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ret = kstrtobool(buf, &xfs_globals.larp);
24162306a36Sopenharmony_ci	if (ret < 0)
24262306a36Sopenharmony_ci		return ret;
24362306a36Sopenharmony_ci	return count;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciSTATIC ssize_t
24762306a36Sopenharmony_cilarp_show(
24862306a36Sopenharmony_ci	struct kobject	*kobject,
24962306a36Sopenharmony_ci	char		*buf)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", xfs_globals.larp);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(larp);
25462306a36Sopenharmony_ci#endif /* DEBUG */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic struct attribute *xfs_dbg_attrs[] = {
25762306a36Sopenharmony_ci	ATTR_LIST(bug_on_assert),
25862306a36Sopenharmony_ci	ATTR_LIST(log_recovery_delay),
25962306a36Sopenharmony_ci	ATTR_LIST(mount_delay),
26062306a36Sopenharmony_ci	ATTR_LIST(always_cow),
26162306a36Sopenharmony_ci#ifdef DEBUG
26262306a36Sopenharmony_ci	ATTR_LIST(pwork_threads),
26362306a36Sopenharmony_ci	ATTR_LIST(larp),
26462306a36Sopenharmony_ci#endif
26562306a36Sopenharmony_ci	NULL,
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ciATTRIBUTE_GROUPS(xfs_dbg);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ciconst struct kobj_type xfs_dbg_ktype = {
27062306a36Sopenharmony_ci	.release = xfs_sysfs_release,
27162306a36Sopenharmony_ci	.sysfs_ops = &xfs_sysfs_ops,
27262306a36Sopenharmony_ci	.default_groups = xfs_dbg_groups,
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci#endif /* DEBUG */
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/* stats */
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic inline struct xstats *
28062306a36Sopenharmony_cito_xstats(struct kobject *kobject)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct xfs_kobj *kobj = to_kobj(kobject);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return container_of(kobj, struct xstats, xs_kobj);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciSTATIC ssize_t
28862306a36Sopenharmony_cistats_show(
28962306a36Sopenharmony_ci	struct kobject	*kobject,
29062306a36Sopenharmony_ci	char		*buf)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct xstats	*stats = to_xstats(kobject);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return xfs_stats_format(stats->xs_stats, buf);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ciXFS_SYSFS_ATTR_RO(stats);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ciSTATIC ssize_t
29962306a36Sopenharmony_cistats_clear_store(
30062306a36Sopenharmony_ci	struct kobject	*kobject,
30162306a36Sopenharmony_ci	const char	*buf,
30262306a36Sopenharmony_ci	size_t		count)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	int		ret;
30562306a36Sopenharmony_ci	int		val;
30662306a36Sopenharmony_ci	struct xstats	*stats = to_xstats(kobject);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
30962306a36Sopenharmony_ci	if (ret)
31062306a36Sopenharmony_ci		return ret;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (val != 1)
31362306a36Sopenharmony_ci		return -EINVAL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	xfs_stats_clearall(stats->xs_stats);
31662306a36Sopenharmony_ci	return count;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ciXFS_SYSFS_ATTR_WO(stats_clear);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic struct attribute *xfs_stats_attrs[] = {
32162306a36Sopenharmony_ci	ATTR_LIST(stats),
32262306a36Sopenharmony_ci	ATTR_LIST(stats_clear),
32362306a36Sopenharmony_ci	NULL,
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ciATTRIBUTE_GROUPS(xfs_stats);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ciconst struct kobj_type xfs_stats_ktype = {
32862306a36Sopenharmony_ci	.release = xfs_sysfs_release,
32962306a36Sopenharmony_ci	.sysfs_ops = &xfs_sysfs_ops,
33062306a36Sopenharmony_ci	.default_groups = xfs_stats_groups,
33162306a36Sopenharmony_ci};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/* xlog */
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic inline struct xlog *
33662306a36Sopenharmony_cito_xlog(struct kobject *kobject)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct xfs_kobj *kobj = to_kobj(kobject);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return container_of(kobj, struct xlog, l_kobj);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ciSTATIC ssize_t
34462306a36Sopenharmony_cilog_head_lsn_show(
34562306a36Sopenharmony_ci	struct kobject	*kobject,
34662306a36Sopenharmony_ci	char		*buf)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	int cycle;
34962306a36Sopenharmony_ci	int block;
35062306a36Sopenharmony_ci	struct xlog *log = to_xlog(kobject);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	spin_lock(&log->l_icloglock);
35362306a36Sopenharmony_ci	cycle = log->l_curr_cycle;
35462306a36Sopenharmony_ci	block = log->l_curr_block;
35562306a36Sopenharmony_ci	spin_unlock(&log->l_icloglock);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return sysfs_emit(buf, "%d:%d\n", cycle, block);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ciXFS_SYSFS_ATTR_RO(log_head_lsn);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ciSTATIC ssize_t
36262306a36Sopenharmony_cilog_tail_lsn_show(
36362306a36Sopenharmony_ci	struct kobject	*kobject,
36462306a36Sopenharmony_ci	char		*buf)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	int cycle;
36762306a36Sopenharmony_ci	int block;
36862306a36Sopenharmony_ci	struct xlog *log = to_xlog(kobject);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	xlog_crack_atomic_lsn(&log->l_tail_lsn, &cycle, &block);
37162306a36Sopenharmony_ci	return sysfs_emit(buf, "%d:%d\n", cycle, block);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ciXFS_SYSFS_ATTR_RO(log_tail_lsn);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ciSTATIC ssize_t
37662306a36Sopenharmony_cireserve_grant_head_show(
37762306a36Sopenharmony_ci	struct kobject	*kobject,
37862306a36Sopenharmony_ci	char		*buf)
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int cycle;
38262306a36Sopenharmony_ci	int bytes;
38362306a36Sopenharmony_ci	struct xlog *log = to_xlog(kobject);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	xlog_crack_grant_head(&log->l_reserve_head.grant, &cycle, &bytes);
38662306a36Sopenharmony_ci	return sysfs_emit(buf, "%d:%d\n", cycle, bytes);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ciXFS_SYSFS_ATTR_RO(reserve_grant_head);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ciSTATIC ssize_t
39162306a36Sopenharmony_ciwrite_grant_head_show(
39262306a36Sopenharmony_ci	struct kobject	*kobject,
39362306a36Sopenharmony_ci	char		*buf)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	int cycle;
39662306a36Sopenharmony_ci	int bytes;
39762306a36Sopenharmony_ci	struct xlog *log = to_xlog(kobject);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	xlog_crack_grant_head(&log->l_write_head.grant, &cycle, &bytes);
40062306a36Sopenharmony_ci	return sysfs_emit(buf, "%d:%d\n", cycle, bytes);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ciXFS_SYSFS_ATTR_RO(write_grant_head);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic struct attribute *xfs_log_attrs[] = {
40562306a36Sopenharmony_ci	ATTR_LIST(log_head_lsn),
40662306a36Sopenharmony_ci	ATTR_LIST(log_tail_lsn),
40762306a36Sopenharmony_ci	ATTR_LIST(reserve_grant_head),
40862306a36Sopenharmony_ci	ATTR_LIST(write_grant_head),
40962306a36Sopenharmony_ci	NULL,
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ciATTRIBUTE_GROUPS(xfs_log);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ciconst struct kobj_type xfs_log_ktype = {
41462306a36Sopenharmony_ci	.release = xfs_sysfs_release,
41562306a36Sopenharmony_ci	.sysfs_ops = &xfs_sysfs_ops,
41662306a36Sopenharmony_ci	.default_groups = xfs_log_groups,
41762306a36Sopenharmony_ci};
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/*
42062306a36Sopenharmony_ci * Metadata IO error configuration
42162306a36Sopenharmony_ci *
42262306a36Sopenharmony_ci * The sysfs structure here is:
42362306a36Sopenharmony_ci *	...xfs/<dev>/error/<class>/<errno>/<error_attrs>
42462306a36Sopenharmony_ci *
42562306a36Sopenharmony_ci * where <class> allows us to discriminate between data IO and metadata IO,
42662306a36Sopenharmony_ci * and any other future type of IO (e.g. special inode or directory error
42762306a36Sopenharmony_ci * handling) we care to support.
42862306a36Sopenharmony_ci */
42962306a36Sopenharmony_cistatic inline struct xfs_error_cfg *
43062306a36Sopenharmony_cito_error_cfg(struct kobject *kobject)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct xfs_kobj *kobj = to_kobj(kobject);
43362306a36Sopenharmony_ci	return container_of(kobj, struct xfs_error_cfg, kobj);
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic inline struct xfs_mount *
43762306a36Sopenharmony_cierr_to_mp(struct kobject *kobject)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct xfs_kobj *kobj = to_kobj(kobject);
44062306a36Sopenharmony_ci	return container_of(kobj, struct xfs_mount, m_error_kobj);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic ssize_t
44462306a36Sopenharmony_cimax_retries_show(
44562306a36Sopenharmony_ci	struct kobject	*kobject,
44662306a36Sopenharmony_ci	char		*buf)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	int		retries;
44962306a36Sopenharmony_ci	struct xfs_error_cfg *cfg = to_error_cfg(kobject);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (cfg->max_retries == XFS_ERR_RETRY_FOREVER)
45262306a36Sopenharmony_ci		retries = -1;
45362306a36Sopenharmony_ci	else
45462306a36Sopenharmony_ci		retries = cfg->max_retries;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", retries);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic ssize_t
46062306a36Sopenharmony_cimax_retries_store(
46162306a36Sopenharmony_ci	struct kobject	*kobject,
46262306a36Sopenharmony_ci	const char	*buf,
46362306a36Sopenharmony_ci	size_t		count)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct xfs_error_cfg *cfg = to_error_cfg(kobject);
46662306a36Sopenharmony_ci	int		ret;
46762306a36Sopenharmony_ci	int		val;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
47062306a36Sopenharmony_ci	if (ret)
47162306a36Sopenharmony_ci		return ret;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (val < -1)
47462306a36Sopenharmony_ci		return -EINVAL;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (val == -1)
47762306a36Sopenharmony_ci		cfg->max_retries = XFS_ERR_RETRY_FOREVER;
47862306a36Sopenharmony_ci	else
47962306a36Sopenharmony_ci		cfg->max_retries = val;
48062306a36Sopenharmony_ci	return count;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(max_retries);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic ssize_t
48562306a36Sopenharmony_ciretry_timeout_seconds_show(
48662306a36Sopenharmony_ci	struct kobject	*kobject,
48762306a36Sopenharmony_ci	char		*buf)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int		timeout;
49062306a36Sopenharmony_ci	struct xfs_error_cfg *cfg = to_error_cfg(kobject);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (cfg->retry_timeout == XFS_ERR_RETRY_FOREVER)
49362306a36Sopenharmony_ci		timeout = -1;
49462306a36Sopenharmony_ci	else
49562306a36Sopenharmony_ci		timeout = jiffies_to_msecs(cfg->retry_timeout) / MSEC_PER_SEC;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", timeout);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic ssize_t
50162306a36Sopenharmony_ciretry_timeout_seconds_store(
50262306a36Sopenharmony_ci	struct kobject	*kobject,
50362306a36Sopenharmony_ci	const char	*buf,
50462306a36Sopenharmony_ci	size_t		count)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct xfs_error_cfg *cfg = to_error_cfg(kobject);
50762306a36Sopenharmony_ci	int		ret;
50862306a36Sopenharmony_ci	int		val;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
51162306a36Sopenharmony_ci	if (ret)
51262306a36Sopenharmony_ci		return ret;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* 1 day timeout maximum, -1 means infinite */
51562306a36Sopenharmony_ci	if (val < -1 || val > 86400)
51662306a36Sopenharmony_ci		return -EINVAL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (val == -1)
51962306a36Sopenharmony_ci		cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
52062306a36Sopenharmony_ci	else {
52162306a36Sopenharmony_ci		cfg->retry_timeout = msecs_to_jiffies(val * MSEC_PER_SEC);
52262306a36Sopenharmony_ci		ASSERT(msecs_to_jiffies(val * MSEC_PER_SEC) < LONG_MAX);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	return count;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(retry_timeout_seconds);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic ssize_t
52962306a36Sopenharmony_cifail_at_unmount_show(
53062306a36Sopenharmony_ci	struct kobject	*kobject,
53162306a36Sopenharmony_ci	char		*buf)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct xfs_mount	*mp = err_to_mp(kobject);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", mp->m_fail_unmount);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic ssize_t
53962306a36Sopenharmony_cifail_at_unmount_store(
54062306a36Sopenharmony_ci	struct kobject	*kobject,
54162306a36Sopenharmony_ci	const char	*buf,
54262306a36Sopenharmony_ci	size_t		count)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct xfs_mount	*mp = err_to_mp(kobject);
54562306a36Sopenharmony_ci	int		ret;
54662306a36Sopenharmony_ci	int		val;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	ret = kstrtoint(buf, 0, &val);
54962306a36Sopenharmony_ci	if (ret)
55062306a36Sopenharmony_ci		return ret;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (val < 0 || val > 1)
55362306a36Sopenharmony_ci		return -EINVAL;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	mp->m_fail_unmount = val;
55662306a36Sopenharmony_ci	return count;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ciXFS_SYSFS_ATTR_RW(fail_at_unmount);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic struct attribute *xfs_error_attrs[] = {
56162306a36Sopenharmony_ci	ATTR_LIST(max_retries),
56262306a36Sopenharmony_ci	ATTR_LIST(retry_timeout_seconds),
56362306a36Sopenharmony_ci	NULL,
56462306a36Sopenharmony_ci};
56562306a36Sopenharmony_ciATTRIBUTE_GROUPS(xfs_error);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic const struct kobj_type xfs_error_cfg_ktype = {
56862306a36Sopenharmony_ci	.release = xfs_sysfs_release,
56962306a36Sopenharmony_ci	.sysfs_ops = &xfs_sysfs_ops,
57062306a36Sopenharmony_ci	.default_groups = xfs_error_groups,
57162306a36Sopenharmony_ci};
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic const struct kobj_type xfs_error_ktype = {
57462306a36Sopenharmony_ci	.release = xfs_sysfs_release,
57562306a36Sopenharmony_ci	.sysfs_ops = &xfs_sysfs_ops,
57662306a36Sopenharmony_ci};
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/*
57962306a36Sopenharmony_ci * Error initialization tables. These need to be ordered in the same
58062306a36Sopenharmony_ci * order as the enums used to index the array. All class init tables need to
58162306a36Sopenharmony_ci * define a "default" behaviour as the first entry, all other entries can be
58262306a36Sopenharmony_ci * empty.
58362306a36Sopenharmony_ci */
58462306a36Sopenharmony_cistruct xfs_error_init {
58562306a36Sopenharmony_ci	char		*name;
58662306a36Sopenharmony_ci	int		max_retries;
58762306a36Sopenharmony_ci	int		retry_timeout;	/* in seconds */
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic const struct xfs_error_init xfs_error_meta_init[XFS_ERR_ERRNO_MAX] = {
59162306a36Sopenharmony_ci	{ .name = "default",
59262306a36Sopenharmony_ci	  .max_retries = XFS_ERR_RETRY_FOREVER,
59362306a36Sopenharmony_ci	  .retry_timeout = XFS_ERR_RETRY_FOREVER,
59462306a36Sopenharmony_ci	},
59562306a36Sopenharmony_ci	{ .name = "EIO",
59662306a36Sopenharmony_ci	  .max_retries = XFS_ERR_RETRY_FOREVER,
59762306a36Sopenharmony_ci	  .retry_timeout = XFS_ERR_RETRY_FOREVER,
59862306a36Sopenharmony_ci	},
59962306a36Sopenharmony_ci	{ .name = "ENOSPC",
60062306a36Sopenharmony_ci	  .max_retries = XFS_ERR_RETRY_FOREVER,
60162306a36Sopenharmony_ci	  .retry_timeout = XFS_ERR_RETRY_FOREVER,
60262306a36Sopenharmony_ci	},
60362306a36Sopenharmony_ci	{ .name = "ENODEV",
60462306a36Sopenharmony_ci	  .max_retries = 0,	/* We can't recover from devices disappearing */
60562306a36Sopenharmony_ci	  .retry_timeout = 0,
60662306a36Sopenharmony_ci	},
60762306a36Sopenharmony_ci};
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int
61062306a36Sopenharmony_cixfs_error_sysfs_init_class(
61162306a36Sopenharmony_ci	struct xfs_mount	*mp,
61262306a36Sopenharmony_ci	int			class,
61362306a36Sopenharmony_ci	const char		*parent_name,
61462306a36Sopenharmony_ci	struct xfs_kobj		*parent_kobj,
61562306a36Sopenharmony_ci	const struct xfs_error_init init[])
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct xfs_error_cfg	*cfg;
61862306a36Sopenharmony_ci	int			error;
61962306a36Sopenharmony_ci	int			i;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	ASSERT(class < XFS_ERR_CLASS_MAX);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	error = xfs_sysfs_init(parent_kobj, &xfs_error_ktype,
62462306a36Sopenharmony_ci				&mp->m_error_kobj, parent_name);
62562306a36Sopenharmony_ci	if (error)
62662306a36Sopenharmony_ci		return error;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	for (i = 0; i < XFS_ERR_ERRNO_MAX; i++) {
62962306a36Sopenharmony_ci		cfg = &mp->m_error_cfg[class][i];
63062306a36Sopenharmony_ci		error = xfs_sysfs_init(&cfg->kobj, &xfs_error_cfg_ktype,
63162306a36Sopenharmony_ci					parent_kobj, init[i].name);
63262306a36Sopenharmony_ci		if (error)
63362306a36Sopenharmony_ci			goto out_error;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		cfg->max_retries = init[i].max_retries;
63662306a36Sopenharmony_ci		if (init[i].retry_timeout == XFS_ERR_RETRY_FOREVER)
63762306a36Sopenharmony_ci			cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
63862306a36Sopenharmony_ci		else
63962306a36Sopenharmony_ci			cfg->retry_timeout = msecs_to_jiffies(
64062306a36Sopenharmony_ci					init[i].retry_timeout * MSEC_PER_SEC);
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	return 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ciout_error:
64562306a36Sopenharmony_ci	/* unwind the entries that succeeded */
64662306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
64762306a36Sopenharmony_ci		cfg = &mp->m_error_cfg[class][i];
64862306a36Sopenharmony_ci		xfs_sysfs_del(&cfg->kobj);
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	xfs_sysfs_del(parent_kobj);
65162306a36Sopenharmony_ci	return error;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ciint
65562306a36Sopenharmony_cixfs_error_sysfs_init(
65662306a36Sopenharmony_ci	struct xfs_mount	*mp)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	int			error;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/* .../xfs/<dev>/error/ */
66162306a36Sopenharmony_ci	error = xfs_sysfs_init(&mp->m_error_kobj, &xfs_error_ktype,
66262306a36Sopenharmony_ci				&mp->m_kobj, "error");
66362306a36Sopenharmony_ci	if (error)
66462306a36Sopenharmony_ci		return error;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	error = sysfs_create_file(&mp->m_error_kobj.kobject,
66762306a36Sopenharmony_ci				  ATTR_LIST(fail_at_unmount));
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (error)
67062306a36Sopenharmony_ci		goto out_error;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* .../xfs/<dev>/error/metadata/ */
67362306a36Sopenharmony_ci	error = xfs_error_sysfs_init_class(mp, XFS_ERR_METADATA,
67462306a36Sopenharmony_ci				"metadata", &mp->m_error_meta_kobj,
67562306a36Sopenharmony_ci				xfs_error_meta_init);
67662306a36Sopenharmony_ci	if (error)
67762306a36Sopenharmony_ci		goto out_error;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ciout_error:
68262306a36Sopenharmony_ci	xfs_sysfs_del(&mp->m_error_kobj);
68362306a36Sopenharmony_ci	return error;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_civoid
68762306a36Sopenharmony_cixfs_error_sysfs_del(
68862306a36Sopenharmony_ci	struct xfs_mount	*mp)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct xfs_error_cfg	*cfg;
69162306a36Sopenharmony_ci	int			i, j;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	for (i = 0; i < XFS_ERR_CLASS_MAX; i++) {
69462306a36Sopenharmony_ci		for (j = 0; j < XFS_ERR_ERRNO_MAX; j++) {
69562306a36Sopenharmony_ci			cfg = &mp->m_error_cfg[i][j];
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci			xfs_sysfs_del(&cfg->kobj);
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci	xfs_sysfs_del(&mp->m_error_meta_kobj);
70162306a36Sopenharmony_ci	xfs_sysfs_del(&mp->m_error_kobj);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistruct xfs_error_cfg *
70562306a36Sopenharmony_cixfs_error_get_cfg(
70662306a36Sopenharmony_ci	struct xfs_mount	*mp,
70762306a36Sopenharmony_ci	int			error_class,
70862306a36Sopenharmony_ci	int			error)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct xfs_error_cfg	*cfg;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (error < 0)
71362306a36Sopenharmony_ci		error = -error;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	switch (error) {
71662306a36Sopenharmony_ci	case EIO:
71762306a36Sopenharmony_ci		cfg = &mp->m_error_cfg[error_class][XFS_ERR_EIO];
71862306a36Sopenharmony_ci		break;
71962306a36Sopenharmony_ci	case ENOSPC:
72062306a36Sopenharmony_ci		cfg = &mp->m_error_cfg[error_class][XFS_ERR_ENOSPC];
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci	case ENODEV:
72362306a36Sopenharmony_ci		cfg = &mp->m_error_cfg[error_class][XFS_ERR_ENODEV];
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci	default:
72662306a36Sopenharmony_ci		cfg = &mp->m_error_cfg[error_class][XFS_ERR_DEFAULT];
72762306a36Sopenharmony_ci		break;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	return cfg;
73162306a36Sopenharmony_ci}
732