162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/ext4/sysfs.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1992, 1993, 1994, 1995
662306a36Sopenharmony_ci * Remy Card (card@masi.ibp.fr)
762306a36Sopenharmony_ci * Theodore Ts'o (tytso@mit.edu)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/time.h>
1262306a36Sopenharmony_ci#include <linux/fs.h>
1362306a36Sopenharmony_ci#include <linux/seq_file.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/proc_fs.h>
1662306a36Sopenharmony_ci#include <linux/part_stat.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "ext4.h"
1962306a36Sopenharmony_ci#include "ext4_jbd2.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_citypedef enum {
2262306a36Sopenharmony_ci	attr_noop,
2362306a36Sopenharmony_ci	attr_delayed_allocation_blocks,
2462306a36Sopenharmony_ci	attr_session_write_kbytes,
2562306a36Sopenharmony_ci	attr_lifetime_write_kbytes,
2662306a36Sopenharmony_ci	attr_reserved_clusters,
2762306a36Sopenharmony_ci	attr_sra_exceeded_retry_limit,
2862306a36Sopenharmony_ci	attr_inode_readahead,
2962306a36Sopenharmony_ci	attr_trigger_test_error,
3062306a36Sopenharmony_ci	attr_first_error_time,
3162306a36Sopenharmony_ci	attr_last_error_time,
3262306a36Sopenharmony_ci	attr_feature,
3362306a36Sopenharmony_ci	attr_pointer_ui,
3462306a36Sopenharmony_ci	attr_pointer_ul,
3562306a36Sopenharmony_ci	attr_pointer_u64,
3662306a36Sopenharmony_ci	attr_pointer_u8,
3762306a36Sopenharmony_ci	attr_pointer_string,
3862306a36Sopenharmony_ci	attr_pointer_atomic,
3962306a36Sopenharmony_ci	attr_journal_task,
4062306a36Sopenharmony_ci} attr_id_t;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_citypedef enum {
4362306a36Sopenharmony_ci	ptr_explicit,
4462306a36Sopenharmony_ci	ptr_ext4_sb_info_offset,
4562306a36Sopenharmony_ci	ptr_ext4_super_block_offset,
4662306a36Sopenharmony_ci} attr_ptr_t;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic const char proc_dirname[] = "fs/ext4";
4962306a36Sopenharmony_cistatic struct proc_dir_entry *ext4_proc_root;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct ext4_attr {
5262306a36Sopenharmony_ci	struct attribute attr;
5362306a36Sopenharmony_ci	short attr_id;
5462306a36Sopenharmony_ci	short attr_ptr;
5562306a36Sopenharmony_ci	unsigned short attr_size;
5662306a36Sopenharmony_ci	union {
5762306a36Sopenharmony_ci		int offset;
5862306a36Sopenharmony_ci		void *explicit_ptr;
5962306a36Sopenharmony_ci	} u;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic ssize_t session_write_kbytes_show(struct ext4_sb_info *sbi, char *buf)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct super_block *sb = sbi->s_buddy_cache->i_sb;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n",
6762306a36Sopenharmony_ci			(part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
6862306a36Sopenharmony_ci			 sbi->s_sectors_written_start) >> 1);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic ssize_t lifetime_write_kbytes_show(struct ext4_sb_info *sbi, char *buf)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct super_block *sb = sbi->s_buddy_cache->i_sb;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return sysfs_emit(buf, "%llu\n",
7662306a36Sopenharmony_ci			(unsigned long long)(sbi->s_kbytes_written +
7762306a36Sopenharmony_ci			((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
7862306a36Sopenharmony_ci			  EXT4_SB(sb)->s_sectors_written_start) >> 1)));
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic ssize_t inode_readahead_blks_store(struct ext4_sb_info *sbi,
8262306a36Sopenharmony_ci					  const char *buf, size_t count)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	unsigned long t;
8562306a36Sopenharmony_ci	int ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	ret = kstrtoul(skip_spaces(buf), 0, &t);
8862306a36Sopenharmony_ci	if (ret)
8962306a36Sopenharmony_ci		return ret;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (t && (!is_power_of_2(t) || t > 0x40000000))
9262306a36Sopenharmony_ci		return -EINVAL;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	sbi->s_inode_readahead_blks = t;
9562306a36Sopenharmony_ci	return count;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic ssize_t reserved_clusters_store(struct ext4_sb_info *sbi,
9962306a36Sopenharmony_ci				   const char *buf, size_t count)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	unsigned long long val;
10262306a36Sopenharmony_ci	ext4_fsblk_t clusters = (ext4_blocks_count(sbi->s_es) >>
10362306a36Sopenharmony_ci				 sbi->s_cluster_bits);
10462306a36Sopenharmony_ci	int ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = kstrtoull(skip_spaces(buf), 0, &val);
10762306a36Sopenharmony_ci	if (ret || val >= clusters)
10862306a36Sopenharmony_ci		return -EINVAL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	atomic64_set(&sbi->s_resv_clusters, val);
11162306a36Sopenharmony_ci	return count;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic ssize_t trigger_test_error(struct ext4_sb_info *sbi,
11562306a36Sopenharmony_ci				  const char *buf, size_t count)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int len = count;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
12062306a36Sopenharmony_ci		return -EPERM;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (len && buf[len-1] == '\n')
12362306a36Sopenharmony_ci		len--;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (len)
12662306a36Sopenharmony_ci		ext4_error(sbi->s_sb, "%.*s", len, buf);
12762306a36Sopenharmony_ci	return count;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic ssize_t journal_task_show(struct ext4_sb_info *sbi, char *buf)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	if (!sbi->s_journal)
13362306a36Sopenharmony_ci		return sysfs_emit(buf, "<none>\n");
13462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n",
13562306a36Sopenharmony_ci			task_pid_vnr(sbi->s_journal->j_task));
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define EXT4_ATTR(_name,_mode,_id)					\
13962306a36Sopenharmony_cistatic struct ext4_attr ext4_attr_##_name = {				\
14062306a36Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },		\
14162306a36Sopenharmony_ci	.attr_id = attr_##_id,						\
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#define EXT4_ATTR_FUNC(_name,_mode)  EXT4_ATTR(_name,_mode,_name)
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci#define EXT4_ATTR_FEATURE(_name)   EXT4_ATTR(_name, 0444, feature)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define EXT4_ATTR_OFFSET(_name,_mode,_id,_struct,_elname)	\
14962306a36Sopenharmony_cistatic struct ext4_attr ext4_attr_##_name = {			\
15062306a36Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },	\
15162306a36Sopenharmony_ci	.attr_id = attr_##_id,					\
15262306a36Sopenharmony_ci	.attr_ptr = ptr_##_struct##_offset,			\
15362306a36Sopenharmony_ci	.u = {							\
15462306a36Sopenharmony_ci		.offset = offsetof(struct _struct, _elname),\
15562306a36Sopenharmony_ci	},							\
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci#define EXT4_ATTR_STRING(_name,_mode,_size,_struct,_elname)	\
15962306a36Sopenharmony_cistatic struct ext4_attr ext4_attr_##_name = {			\
16062306a36Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },	\
16162306a36Sopenharmony_ci	.attr_id = attr_pointer_string,				\
16262306a36Sopenharmony_ci	.attr_size = _size,					\
16362306a36Sopenharmony_ci	.attr_ptr = ptr_##_struct##_offset,			\
16462306a36Sopenharmony_ci	.u = {							\
16562306a36Sopenharmony_ci		.offset = offsetof(struct _struct, _elname),\
16662306a36Sopenharmony_ci	},							\
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci#define EXT4_RO_ATTR_ES_UI(_name,_elname)				\
17062306a36Sopenharmony_ci	EXT4_ATTR_OFFSET(_name, 0444, pointer_ui, ext4_super_block, _elname)
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#define EXT4_RO_ATTR_ES_U8(_name,_elname)				\
17362306a36Sopenharmony_ci	EXT4_ATTR_OFFSET(_name, 0444, pointer_u8, ext4_super_block, _elname)
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define EXT4_RO_ATTR_ES_U64(_name,_elname)				\
17662306a36Sopenharmony_ci	EXT4_ATTR_OFFSET(_name, 0444, pointer_u64, ext4_super_block, _elname)
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#define EXT4_RO_ATTR_ES_STRING(_name,_elname,_size)			\
17962306a36Sopenharmony_ci	EXT4_ATTR_STRING(_name, 0444, _size, ext4_super_block, _elname)
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#define EXT4_RW_ATTR_SBI_UI(_name,_elname)	\
18262306a36Sopenharmony_ci	EXT4_ATTR_OFFSET(_name, 0644, pointer_ui, ext4_sb_info, _elname)
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#define EXT4_RW_ATTR_SBI_UL(_name,_elname)	\
18562306a36Sopenharmony_ci	EXT4_ATTR_OFFSET(_name, 0644, pointer_ul, ext4_sb_info, _elname)
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci#define EXT4_RO_ATTR_SBI_ATOMIC(_name,_elname)	\
18862306a36Sopenharmony_ci	EXT4_ATTR_OFFSET(_name, 0444, pointer_atomic, ext4_sb_info, _elname)
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#define EXT4_ATTR_PTR(_name,_mode,_id,_ptr) \
19162306a36Sopenharmony_cistatic struct ext4_attr ext4_attr_##_name = {			\
19262306a36Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode },	\
19362306a36Sopenharmony_ci	.attr_id = attr_##_id,					\
19462306a36Sopenharmony_ci	.attr_ptr = ptr_explicit,				\
19562306a36Sopenharmony_ci	.u = {							\
19662306a36Sopenharmony_ci		.explicit_ptr = _ptr,				\
19762306a36Sopenharmony_ci	},							\
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci#define ATTR_LIST(name) &ext4_attr_##name.attr
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciEXT4_ATTR_FUNC(delayed_allocation_blocks, 0444);
20362306a36Sopenharmony_ciEXT4_ATTR_FUNC(session_write_kbytes, 0444);
20462306a36Sopenharmony_ciEXT4_ATTR_FUNC(lifetime_write_kbytes, 0444);
20562306a36Sopenharmony_ciEXT4_ATTR_FUNC(reserved_clusters, 0644);
20662306a36Sopenharmony_ciEXT4_ATTR_FUNC(sra_exceeded_retry_limit, 0444);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ciEXT4_ATTR_OFFSET(inode_readahead_blks, 0644, inode_readahead,
20962306a36Sopenharmony_ci		 ext4_sb_info, s_inode_readahead_blks);
21062306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal);
21162306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats);
21262306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan);
21362306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan);
21462306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
21562306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
21662306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
21762306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_max_linear_groups, s_mb_max_linear_groups);
21862306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb);
21962306a36Sopenharmony_ciEXT4_ATTR(trigger_fs_error, 0200, trigger_test_error);
22062306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(err_ratelimit_interval_ms, s_err_ratelimit_state.interval);
22162306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(err_ratelimit_burst, s_err_ratelimit_state.burst);
22262306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.interval);
22362306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
22462306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
22562306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
22662306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_best_avail_max_trim_order, s_mb_best_avail_max_trim_order);
22762306a36Sopenharmony_ci#ifdef CONFIG_EXT4_DEBUG
22862306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UL(simulate_fail, s_simulate_fail);
22962306a36Sopenharmony_ci#endif
23062306a36Sopenharmony_ciEXT4_RO_ATTR_SBI_ATOMIC(warning_count, s_warning_count);
23162306a36Sopenharmony_ciEXT4_RO_ATTR_SBI_ATOMIC(msg_count, s_msg_count);
23262306a36Sopenharmony_ciEXT4_RO_ATTR_ES_UI(errors_count, s_error_count);
23362306a36Sopenharmony_ciEXT4_RO_ATTR_ES_U8(first_error_errcode, s_first_error_errcode);
23462306a36Sopenharmony_ciEXT4_RO_ATTR_ES_U8(last_error_errcode, s_last_error_errcode);
23562306a36Sopenharmony_ciEXT4_RO_ATTR_ES_UI(first_error_ino, s_first_error_ino);
23662306a36Sopenharmony_ciEXT4_RO_ATTR_ES_UI(last_error_ino, s_last_error_ino);
23762306a36Sopenharmony_ciEXT4_RO_ATTR_ES_U64(first_error_block, s_first_error_block);
23862306a36Sopenharmony_ciEXT4_RO_ATTR_ES_U64(last_error_block, s_last_error_block);
23962306a36Sopenharmony_ciEXT4_RO_ATTR_ES_UI(first_error_line, s_first_error_line);
24062306a36Sopenharmony_ciEXT4_RO_ATTR_ES_UI(last_error_line, s_last_error_line);
24162306a36Sopenharmony_ciEXT4_RO_ATTR_ES_STRING(first_error_func, s_first_error_func, 32);
24262306a36Sopenharmony_ciEXT4_RO_ATTR_ES_STRING(last_error_func, s_last_error_func, 32);
24362306a36Sopenharmony_ciEXT4_ATTR(first_error_time, 0444, first_error_time);
24462306a36Sopenharmony_ciEXT4_ATTR(last_error_time, 0444, last_error_time);
24562306a36Sopenharmony_ciEXT4_ATTR(journal_task, 0444, journal_task);
24662306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_prefetch, s_mb_prefetch);
24762306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UI(mb_prefetch_limit, s_mb_prefetch_limit);
24862306a36Sopenharmony_ciEXT4_RW_ATTR_SBI_UL(last_trim_minblks, s_last_trim_minblks);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic unsigned int old_bump_val = 128;
25162306a36Sopenharmony_ciEXT4_ATTR_PTR(max_writeback_mb_bump, 0444, pointer_ui, &old_bump_val);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic struct attribute *ext4_attrs[] = {
25462306a36Sopenharmony_ci	ATTR_LIST(delayed_allocation_blocks),
25562306a36Sopenharmony_ci	ATTR_LIST(session_write_kbytes),
25662306a36Sopenharmony_ci	ATTR_LIST(lifetime_write_kbytes),
25762306a36Sopenharmony_ci	ATTR_LIST(reserved_clusters),
25862306a36Sopenharmony_ci	ATTR_LIST(sra_exceeded_retry_limit),
25962306a36Sopenharmony_ci	ATTR_LIST(inode_readahead_blks),
26062306a36Sopenharmony_ci	ATTR_LIST(inode_goal),
26162306a36Sopenharmony_ci	ATTR_LIST(mb_stats),
26262306a36Sopenharmony_ci	ATTR_LIST(mb_max_to_scan),
26362306a36Sopenharmony_ci	ATTR_LIST(mb_min_to_scan),
26462306a36Sopenharmony_ci	ATTR_LIST(mb_order2_req),
26562306a36Sopenharmony_ci	ATTR_LIST(mb_stream_req),
26662306a36Sopenharmony_ci	ATTR_LIST(mb_group_prealloc),
26762306a36Sopenharmony_ci	ATTR_LIST(mb_max_linear_groups),
26862306a36Sopenharmony_ci	ATTR_LIST(max_writeback_mb_bump),
26962306a36Sopenharmony_ci	ATTR_LIST(extent_max_zeroout_kb),
27062306a36Sopenharmony_ci	ATTR_LIST(trigger_fs_error),
27162306a36Sopenharmony_ci	ATTR_LIST(err_ratelimit_interval_ms),
27262306a36Sopenharmony_ci	ATTR_LIST(err_ratelimit_burst),
27362306a36Sopenharmony_ci	ATTR_LIST(warning_ratelimit_interval_ms),
27462306a36Sopenharmony_ci	ATTR_LIST(warning_ratelimit_burst),
27562306a36Sopenharmony_ci	ATTR_LIST(msg_ratelimit_interval_ms),
27662306a36Sopenharmony_ci	ATTR_LIST(msg_ratelimit_burst),
27762306a36Sopenharmony_ci	ATTR_LIST(mb_best_avail_max_trim_order),
27862306a36Sopenharmony_ci	ATTR_LIST(errors_count),
27962306a36Sopenharmony_ci	ATTR_LIST(warning_count),
28062306a36Sopenharmony_ci	ATTR_LIST(msg_count),
28162306a36Sopenharmony_ci	ATTR_LIST(first_error_ino),
28262306a36Sopenharmony_ci	ATTR_LIST(last_error_ino),
28362306a36Sopenharmony_ci	ATTR_LIST(first_error_block),
28462306a36Sopenharmony_ci	ATTR_LIST(last_error_block),
28562306a36Sopenharmony_ci	ATTR_LIST(first_error_line),
28662306a36Sopenharmony_ci	ATTR_LIST(last_error_line),
28762306a36Sopenharmony_ci	ATTR_LIST(first_error_func),
28862306a36Sopenharmony_ci	ATTR_LIST(last_error_func),
28962306a36Sopenharmony_ci	ATTR_LIST(first_error_errcode),
29062306a36Sopenharmony_ci	ATTR_LIST(last_error_errcode),
29162306a36Sopenharmony_ci	ATTR_LIST(first_error_time),
29262306a36Sopenharmony_ci	ATTR_LIST(last_error_time),
29362306a36Sopenharmony_ci	ATTR_LIST(journal_task),
29462306a36Sopenharmony_ci#ifdef CONFIG_EXT4_DEBUG
29562306a36Sopenharmony_ci	ATTR_LIST(simulate_fail),
29662306a36Sopenharmony_ci#endif
29762306a36Sopenharmony_ci	ATTR_LIST(mb_prefetch),
29862306a36Sopenharmony_ci	ATTR_LIST(mb_prefetch_limit),
29962306a36Sopenharmony_ci	ATTR_LIST(last_trim_minblks),
30062306a36Sopenharmony_ci	NULL,
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ciATTRIBUTE_GROUPS(ext4);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/* Features this copy of ext4 supports */
30562306a36Sopenharmony_ciEXT4_ATTR_FEATURE(lazy_itable_init);
30662306a36Sopenharmony_ciEXT4_ATTR_FEATURE(batched_discard);
30762306a36Sopenharmony_ciEXT4_ATTR_FEATURE(meta_bg_resize);
30862306a36Sopenharmony_ci#ifdef CONFIG_FS_ENCRYPTION
30962306a36Sopenharmony_ciEXT4_ATTR_FEATURE(encryption);
31062306a36Sopenharmony_ciEXT4_ATTR_FEATURE(test_dummy_encryption_v2);
31162306a36Sopenharmony_ci#endif
31262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_UNICODE)
31362306a36Sopenharmony_ciEXT4_ATTR_FEATURE(casefold);
31462306a36Sopenharmony_ci#endif
31562306a36Sopenharmony_ci#ifdef CONFIG_FS_VERITY
31662306a36Sopenharmony_ciEXT4_ATTR_FEATURE(verity);
31762306a36Sopenharmony_ci#endif
31862306a36Sopenharmony_ciEXT4_ATTR_FEATURE(metadata_csum_seed);
31962306a36Sopenharmony_ciEXT4_ATTR_FEATURE(fast_commit);
32062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_FS_ENCRYPTION)
32162306a36Sopenharmony_ciEXT4_ATTR_FEATURE(encrypted_casefold);
32262306a36Sopenharmony_ci#endif
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic struct attribute *ext4_feat_attrs[] = {
32562306a36Sopenharmony_ci	ATTR_LIST(lazy_itable_init),
32662306a36Sopenharmony_ci	ATTR_LIST(batched_discard),
32762306a36Sopenharmony_ci	ATTR_LIST(meta_bg_resize),
32862306a36Sopenharmony_ci#ifdef CONFIG_FS_ENCRYPTION
32962306a36Sopenharmony_ci	ATTR_LIST(encryption),
33062306a36Sopenharmony_ci	ATTR_LIST(test_dummy_encryption_v2),
33162306a36Sopenharmony_ci#endif
33262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_UNICODE)
33362306a36Sopenharmony_ci	ATTR_LIST(casefold),
33462306a36Sopenharmony_ci#endif
33562306a36Sopenharmony_ci#ifdef CONFIG_FS_VERITY
33662306a36Sopenharmony_ci	ATTR_LIST(verity),
33762306a36Sopenharmony_ci#endif
33862306a36Sopenharmony_ci	ATTR_LIST(metadata_csum_seed),
33962306a36Sopenharmony_ci	ATTR_LIST(fast_commit),
34062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_FS_ENCRYPTION)
34162306a36Sopenharmony_ci	ATTR_LIST(encrypted_casefold),
34262306a36Sopenharmony_ci#endif
34362306a36Sopenharmony_ci	NULL,
34462306a36Sopenharmony_ci};
34562306a36Sopenharmony_ciATTRIBUTE_GROUPS(ext4_feat);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void *calc_ptr(struct ext4_attr *a, struct ext4_sb_info *sbi)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	switch (a->attr_ptr) {
35062306a36Sopenharmony_ci	case ptr_explicit:
35162306a36Sopenharmony_ci		return a->u.explicit_ptr;
35262306a36Sopenharmony_ci	case ptr_ext4_sb_info_offset:
35362306a36Sopenharmony_ci		return (void *) (((char *) sbi) + a->u.offset);
35462306a36Sopenharmony_ci	case ptr_ext4_super_block_offset:
35562306a36Sopenharmony_ci		return (void *) (((char *) sbi->s_es) + a->u.offset);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	return NULL;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic ssize_t __print_tstamp(char *buf, __le32 lo, __u8 hi)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	return sysfs_emit(buf, "%lld\n",
36362306a36Sopenharmony_ci			((time64_t)hi << 32) + le32_to_cpu(lo));
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci#define print_tstamp(buf, es, tstamp) \
36762306a36Sopenharmony_ci	__print_tstamp(buf, (es)->tstamp, (es)->tstamp ## _hi)
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic ssize_t ext4_attr_show(struct kobject *kobj,
37062306a36Sopenharmony_ci			      struct attribute *attr, char *buf)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
37362306a36Sopenharmony_ci						s_kobj);
37462306a36Sopenharmony_ci	struct ext4_attr *a = container_of(attr, struct ext4_attr, attr);
37562306a36Sopenharmony_ci	void *ptr = calc_ptr(a, sbi);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	switch (a->attr_id) {
37862306a36Sopenharmony_ci	case attr_delayed_allocation_blocks:
37962306a36Sopenharmony_ci		return sysfs_emit(buf, "%llu\n",
38062306a36Sopenharmony_ci				(s64) EXT4_C2B(sbi,
38162306a36Sopenharmony_ci		       percpu_counter_sum(&sbi->s_dirtyclusters_counter)));
38262306a36Sopenharmony_ci	case attr_session_write_kbytes:
38362306a36Sopenharmony_ci		return session_write_kbytes_show(sbi, buf);
38462306a36Sopenharmony_ci	case attr_lifetime_write_kbytes:
38562306a36Sopenharmony_ci		return lifetime_write_kbytes_show(sbi, buf);
38662306a36Sopenharmony_ci	case attr_reserved_clusters:
38762306a36Sopenharmony_ci		return sysfs_emit(buf, "%llu\n",
38862306a36Sopenharmony_ci				(unsigned long long)
38962306a36Sopenharmony_ci				atomic64_read(&sbi->s_resv_clusters));
39062306a36Sopenharmony_ci	case attr_sra_exceeded_retry_limit:
39162306a36Sopenharmony_ci		return sysfs_emit(buf, "%llu\n",
39262306a36Sopenharmony_ci				(unsigned long long)
39362306a36Sopenharmony_ci			percpu_counter_sum(&sbi->s_sra_exceeded_retry_limit));
39462306a36Sopenharmony_ci	case attr_inode_readahead:
39562306a36Sopenharmony_ci	case attr_pointer_ui:
39662306a36Sopenharmony_ci		if (!ptr)
39762306a36Sopenharmony_ci			return 0;
39862306a36Sopenharmony_ci		if (a->attr_ptr == ptr_ext4_super_block_offset)
39962306a36Sopenharmony_ci			return sysfs_emit(buf, "%u\n",
40062306a36Sopenharmony_ci					le32_to_cpup(ptr));
40162306a36Sopenharmony_ci		else
40262306a36Sopenharmony_ci			return sysfs_emit(buf, "%u\n",
40362306a36Sopenharmony_ci					*((unsigned int *) ptr));
40462306a36Sopenharmony_ci	case attr_pointer_ul:
40562306a36Sopenharmony_ci		if (!ptr)
40662306a36Sopenharmony_ci			return 0;
40762306a36Sopenharmony_ci		return sysfs_emit(buf, "%lu\n",
40862306a36Sopenharmony_ci				*((unsigned long *) ptr));
40962306a36Sopenharmony_ci	case attr_pointer_u8:
41062306a36Sopenharmony_ci		if (!ptr)
41162306a36Sopenharmony_ci			return 0;
41262306a36Sopenharmony_ci		return sysfs_emit(buf, "%u\n",
41362306a36Sopenharmony_ci				*((unsigned char *) ptr));
41462306a36Sopenharmony_ci	case attr_pointer_u64:
41562306a36Sopenharmony_ci		if (!ptr)
41662306a36Sopenharmony_ci			return 0;
41762306a36Sopenharmony_ci		if (a->attr_ptr == ptr_ext4_super_block_offset)
41862306a36Sopenharmony_ci			return sysfs_emit(buf, "%llu\n",
41962306a36Sopenharmony_ci					le64_to_cpup(ptr));
42062306a36Sopenharmony_ci		else
42162306a36Sopenharmony_ci			return sysfs_emit(buf, "%llu\n",
42262306a36Sopenharmony_ci					*((unsigned long long *) ptr));
42362306a36Sopenharmony_ci	case attr_pointer_string:
42462306a36Sopenharmony_ci		if (!ptr)
42562306a36Sopenharmony_ci			return 0;
42662306a36Sopenharmony_ci		return sysfs_emit(buf, "%.*s\n", a->attr_size,
42762306a36Sopenharmony_ci				(char *) ptr);
42862306a36Sopenharmony_ci	case attr_pointer_atomic:
42962306a36Sopenharmony_ci		if (!ptr)
43062306a36Sopenharmony_ci			return 0;
43162306a36Sopenharmony_ci		return sysfs_emit(buf, "%d\n",
43262306a36Sopenharmony_ci				atomic_read((atomic_t *) ptr));
43362306a36Sopenharmony_ci	case attr_feature:
43462306a36Sopenharmony_ci		return sysfs_emit(buf, "supported\n");
43562306a36Sopenharmony_ci	case attr_first_error_time:
43662306a36Sopenharmony_ci		return print_tstamp(buf, sbi->s_es, s_first_error_time);
43762306a36Sopenharmony_ci	case attr_last_error_time:
43862306a36Sopenharmony_ci		return print_tstamp(buf, sbi->s_es, s_last_error_time);
43962306a36Sopenharmony_ci	case attr_journal_task:
44062306a36Sopenharmony_ci		return journal_task_show(sbi, buf);
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic ssize_t ext4_attr_store(struct kobject *kobj,
44762306a36Sopenharmony_ci			       struct attribute *attr,
44862306a36Sopenharmony_ci			       const char *buf, size_t len)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
45162306a36Sopenharmony_ci						s_kobj);
45262306a36Sopenharmony_ci	struct ext4_attr *a = container_of(attr, struct ext4_attr, attr);
45362306a36Sopenharmony_ci	void *ptr = calc_ptr(a, sbi);
45462306a36Sopenharmony_ci	unsigned long t;
45562306a36Sopenharmony_ci	int ret;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	switch (a->attr_id) {
45862306a36Sopenharmony_ci	case attr_reserved_clusters:
45962306a36Sopenharmony_ci		return reserved_clusters_store(sbi, buf, len);
46062306a36Sopenharmony_ci	case attr_pointer_ui:
46162306a36Sopenharmony_ci		if (!ptr)
46262306a36Sopenharmony_ci			return 0;
46362306a36Sopenharmony_ci		ret = kstrtoul(skip_spaces(buf), 0, &t);
46462306a36Sopenharmony_ci		if (ret)
46562306a36Sopenharmony_ci			return ret;
46662306a36Sopenharmony_ci		if (a->attr_ptr == ptr_ext4_super_block_offset)
46762306a36Sopenharmony_ci			*((__le32 *) ptr) = cpu_to_le32(t);
46862306a36Sopenharmony_ci		else
46962306a36Sopenharmony_ci			*((unsigned int *) ptr) = t;
47062306a36Sopenharmony_ci		return len;
47162306a36Sopenharmony_ci	case attr_pointer_ul:
47262306a36Sopenharmony_ci		if (!ptr)
47362306a36Sopenharmony_ci			return 0;
47462306a36Sopenharmony_ci		ret = kstrtoul(skip_spaces(buf), 0, &t);
47562306a36Sopenharmony_ci		if (ret)
47662306a36Sopenharmony_ci			return ret;
47762306a36Sopenharmony_ci		*((unsigned long *) ptr) = t;
47862306a36Sopenharmony_ci		return len;
47962306a36Sopenharmony_ci	case attr_inode_readahead:
48062306a36Sopenharmony_ci		return inode_readahead_blks_store(sbi, buf, len);
48162306a36Sopenharmony_ci	case attr_trigger_test_error:
48262306a36Sopenharmony_ci		return trigger_test_error(sbi, buf, len);
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic void ext4_sb_release(struct kobject *kobj)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
49062306a36Sopenharmony_ci						s_kobj);
49162306a36Sopenharmony_ci	complete(&sbi->s_kobj_unregister);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void ext4_feat_release(struct kobject *kobj)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	kfree(kobj);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic const struct sysfs_ops ext4_attr_ops = {
50062306a36Sopenharmony_ci	.show	= ext4_attr_show,
50162306a36Sopenharmony_ci	.store	= ext4_attr_store,
50262306a36Sopenharmony_ci};
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic const struct kobj_type ext4_sb_ktype = {
50562306a36Sopenharmony_ci	.default_groups = ext4_groups,
50662306a36Sopenharmony_ci	.sysfs_ops	= &ext4_attr_ops,
50762306a36Sopenharmony_ci	.release	= ext4_sb_release,
50862306a36Sopenharmony_ci};
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic const struct kobj_type ext4_feat_ktype = {
51162306a36Sopenharmony_ci	.default_groups = ext4_feat_groups,
51262306a36Sopenharmony_ci	.sysfs_ops	= &ext4_attr_ops,
51362306a36Sopenharmony_ci	.release	= ext4_feat_release,
51462306a36Sopenharmony_ci};
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_civoid ext4_notify_error_sysfs(struct ext4_sb_info *sbi)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	sysfs_notify(&sbi->s_kobj, NULL, "errors_count");
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic struct kobject *ext4_root;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic struct kobject *ext4_feat;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciint ext4_register_sysfs(struct super_block *sb)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct ext4_sb_info *sbi = EXT4_SB(sb);
52862306a36Sopenharmony_ci	int err;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	init_completion(&sbi->s_kobj_unregister);
53162306a36Sopenharmony_ci	err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, ext4_root,
53262306a36Sopenharmony_ci				   "%s", sb->s_id);
53362306a36Sopenharmony_ci	if (err) {
53462306a36Sopenharmony_ci		kobject_put(&sbi->s_kobj);
53562306a36Sopenharmony_ci		wait_for_completion(&sbi->s_kobj_unregister);
53662306a36Sopenharmony_ci		return err;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (ext4_proc_root)
54062306a36Sopenharmony_ci		sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root);
54162306a36Sopenharmony_ci	if (sbi->s_proc) {
54262306a36Sopenharmony_ci		proc_create_single_data("options", S_IRUGO, sbi->s_proc,
54362306a36Sopenharmony_ci				ext4_seq_options_show, sb);
54462306a36Sopenharmony_ci		proc_create_single_data("es_shrinker_info", S_IRUGO,
54562306a36Sopenharmony_ci				sbi->s_proc, ext4_seq_es_shrinker_info_show,
54662306a36Sopenharmony_ci				sb);
54762306a36Sopenharmony_ci		proc_create_single_data("fc_info", 0444, sbi->s_proc,
54862306a36Sopenharmony_ci					ext4_fc_info_show, sb);
54962306a36Sopenharmony_ci		proc_create_seq_data("mb_groups", S_IRUGO, sbi->s_proc,
55062306a36Sopenharmony_ci				&ext4_mb_seq_groups_ops, sb);
55162306a36Sopenharmony_ci		proc_create_single_data("mb_stats", 0444, sbi->s_proc,
55262306a36Sopenharmony_ci				ext4_seq_mb_stats_show, sb);
55362306a36Sopenharmony_ci		proc_create_seq_data("mb_structs_summary", 0444, sbi->s_proc,
55462306a36Sopenharmony_ci				&ext4_mb_seq_structs_summary_ops, sb);
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_civoid ext4_unregister_sysfs(struct super_block *sb)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct ext4_sb_info *sbi = EXT4_SB(sb);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (sbi->s_proc)
56462306a36Sopenharmony_ci		remove_proc_subtree(sb->s_id, ext4_proc_root);
56562306a36Sopenharmony_ci	kobject_del(&sbi->s_kobj);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ciint __init ext4_init_sysfs(void)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	int ret;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ext4_root = kobject_create_and_add("ext4", fs_kobj);
57362306a36Sopenharmony_ci	if (!ext4_root)
57462306a36Sopenharmony_ci		return -ENOMEM;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ext4_feat = kzalloc(sizeof(*ext4_feat), GFP_KERNEL);
57762306a36Sopenharmony_ci	if (!ext4_feat) {
57862306a36Sopenharmony_ci		ret = -ENOMEM;
57962306a36Sopenharmony_ci		goto root_err;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	ret = kobject_init_and_add(ext4_feat, &ext4_feat_ktype,
58362306a36Sopenharmony_ci				   ext4_root, "features");
58462306a36Sopenharmony_ci	if (ret)
58562306a36Sopenharmony_ci		goto feat_err;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ext4_proc_root = proc_mkdir(proc_dirname, NULL);
58862306a36Sopenharmony_ci	return ret;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cifeat_err:
59162306a36Sopenharmony_ci	kobject_put(ext4_feat);
59262306a36Sopenharmony_ci	ext4_feat = NULL;
59362306a36Sopenharmony_ciroot_err:
59462306a36Sopenharmony_ci	kobject_put(ext4_root);
59562306a36Sopenharmony_ci	ext4_root = NULL;
59662306a36Sopenharmony_ci	return ret;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_civoid ext4_exit_sysfs(void)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	kobject_put(ext4_feat);
60262306a36Sopenharmony_ci	ext4_feat = NULL;
60362306a36Sopenharmony_ci	kobject_put(ext4_root);
60462306a36Sopenharmony_ci	ext4_root = NULL;
60562306a36Sopenharmony_ci	remove_proc_entry(proc_dirname, NULL);
60662306a36Sopenharmony_ci	ext4_proc_root = NULL;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
609