xref: /openbmc/linux/fs/ext4/sysfs.c (revision ebd173be)
1b5799018STheodore Ts'o /*
2b5799018STheodore Ts'o  *  linux/fs/ext4/sysfs.c
3b5799018STheodore Ts'o  *
4b5799018STheodore Ts'o  * Copyright (C) 1992, 1993, 1994, 1995
5b5799018STheodore Ts'o  * Remy Card (card@masi.ibp.fr)
6b5799018STheodore Ts'o  * Theodore Ts'o (tytso@mit.edu)
7b5799018STheodore Ts'o  *
8b5799018STheodore Ts'o  */
9b5799018STheodore Ts'o 
10b5799018STheodore Ts'o #include <linux/time.h>
11b5799018STheodore Ts'o #include <linux/fs.h>
12b5799018STheodore Ts'o #include <linux/seq_file.h>
13b5799018STheodore Ts'o #include <linux/proc_fs.h>
14b5799018STheodore Ts'o 
15b5799018STheodore Ts'o #include "ext4.h"
16b5799018STheodore Ts'o #include "ext4_jbd2.h"
17b5799018STheodore Ts'o 
1876d33bcaSTheodore Ts'o typedef enum {
1976d33bcaSTheodore Ts'o 	attr_noop,
2076d33bcaSTheodore Ts'o 	attr_delayed_allocation_blocks,
2176d33bcaSTheodore Ts'o 	attr_session_write_kbytes,
2276d33bcaSTheodore Ts'o 	attr_lifetime_write_kbytes,
2376d33bcaSTheodore Ts'o 	attr_reserved_clusters,
2476d33bcaSTheodore Ts'o 	attr_inode_readahead,
2576d33bcaSTheodore Ts'o 	attr_trigger_test_error,
2676d33bcaSTheodore Ts'o 	attr_feature,
2776d33bcaSTheodore Ts'o 	attr_pointer_ui,
2876d33bcaSTheodore Ts'o 	attr_pointer_atomic,
2976d33bcaSTheodore Ts'o } attr_id_t;
3076d33bcaSTheodore Ts'o 
3176d33bcaSTheodore Ts'o typedef enum {
3276d33bcaSTheodore Ts'o 	ptr_explicit,
3376d33bcaSTheodore Ts'o 	ptr_ext4_sb_info_offset,
3476d33bcaSTheodore Ts'o 	ptr_ext4_super_block_offset,
3576d33bcaSTheodore Ts'o } attr_ptr_t;
3676d33bcaSTheodore Ts'o 
37ebd173beSTheodore Ts'o static const char *proc_dirname = "fs/ext4";
38ebd173beSTheodore Ts'o static struct proc_dir_entry *ext4_proc_root;
39ebd173beSTheodore Ts'o 
40b5799018STheodore Ts'o struct ext4_attr {
41b5799018STheodore Ts'o 	struct attribute attr;
4276d33bcaSTheodore Ts'o 	short attr_id;
4376d33bcaSTheodore Ts'o 	short attr_ptr;
44b5799018STheodore Ts'o 	union {
45b5799018STheodore Ts'o 		int offset;
4676d33bcaSTheodore Ts'o 		void *explicit_ptr;
47b5799018STheodore Ts'o 	} u;
48b5799018STheodore Ts'o };
49b5799018STheodore Ts'o 
50b5799018STheodore Ts'o static ssize_t session_write_kbytes_show(struct ext4_attr *a,
51b5799018STheodore Ts'o 					 struct ext4_sb_info *sbi, char *buf)
52b5799018STheodore Ts'o {
53b5799018STheodore Ts'o 	struct super_block *sb = sbi->s_buddy_cache->i_sb;
54b5799018STheodore Ts'o 
55b5799018STheodore Ts'o 	if (!sb->s_bdev->bd_part)
56b5799018STheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "0\n");
57b5799018STheodore Ts'o 	return snprintf(buf, PAGE_SIZE, "%lu\n",
58b5799018STheodore Ts'o 			(part_stat_read(sb->s_bdev->bd_part, sectors[1]) -
59b5799018STheodore Ts'o 			 sbi->s_sectors_written_start) >> 1);
60b5799018STheodore Ts'o }
61b5799018STheodore Ts'o 
62b5799018STheodore Ts'o static ssize_t lifetime_write_kbytes_show(struct ext4_attr *a,
63b5799018STheodore Ts'o 					  struct ext4_sb_info *sbi, char *buf)
64b5799018STheodore Ts'o {
65b5799018STheodore Ts'o 	struct super_block *sb = sbi->s_buddy_cache->i_sb;
66b5799018STheodore Ts'o 
67b5799018STheodore Ts'o 	if (!sb->s_bdev->bd_part)
68b5799018STheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "0\n");
69b5799018STheodore Ts'o 	return snprintf(buf, PAGE_SIZE, "%llu\n",
70b5799018STheodore Ts'o 			(unsigned long long)(sbi->s_kbytes_written +
71b5799018STheodore Ts'o 			((part_stat_read(sb->s_bdev->bd_part, sectors[1]) -
72b5799018STheodore Ts'o 			  EXT4_SB(sb)->s_sectors_written_start) >> 1)));
73b5799018STheodore Ts'o }
74b5799018STheodore Ts'o 
75b5799018STheodore Ts'o static ssize_t inode_readahead_blks_store(struct ext4_attr *a,
76b5799018STheodore Ts'o 					  struct ext4_sb_info *sbi,
77b5799018STheodore Ts'o 					  const char *buf, size_t count)
78b5799018STheodore Ts'o {
79b5799018STheodore Ts'o 	unsigned long t;
80b5799018STheodore Ts'o 	int ret;
81b5799018STheodore Ts'o 
82b5799018STheodore Ts'o 	ret = kstrtoul(skip_spaces(buf), 0, &t);
83b5799018STheodore Ts'o 	if (ret)
84b5799018STheodore Ts'o 		return ret;
85b5799018STheodore Ts'o 
86b5799018STheodore Ts'o 	if (t && (!is_power_of_2(t) || t > 0x40000000))
87b5799018STheodore Ts'o 		return -EINVAL;
88b5799018STheodore Ts'o 
89b5799018STheodore Ts'o 	sbi->s_inode_readahead_blks = t;
90b5799018STheodore Ts'o 	return count;
91b5799018STheodore Ts'o }
92b5799018STheodore Ts'o 
93b5799018STheodore Ts'o static ssize_t reserved_clusters_store(struct ext4_attr *a,
94b5799018STheodore Ts'o 				   struct ext4_sb_info *sbi,
95b5799018STheodore Ts'o 				   const char *buf, size_t count)
96b5799018STheodore Ts'o {
97b5799018STheodore Ts'o 	unsigned long long val;
98b5799018STheodore Ts'o 	ext4_fsblk_t clusters = (ext4_blocks_count(sbi->s_es) >>
99b5799018STheodore Ts'o 				 sbi->s_cluster_bits);
10076d33bcaSTheodore Ts'o 	int ret;
101b5799018STheodore Ts'o 
10276d33bcaSTheodore Ts'o 	ret = kstrtoull(skip_spaces(buf), 0, &val);
10376d33bcaSTheodore Ts'o 	if (!ret || val >= clusters)
104b5799018STheodore Ts'o 		return -EINVAL;
105b5799018STheodore Ts'o 
106b5799018STheodore Ts'o 	atomic64_set(&sbi->s_resv_clusters, val);
107b5799018STheodore Ts'o 	return count;
108b5799018STheodore Ts'o }
109b5799018STheodore Ts'o 
110b5799018STheodore Ts'o static ssize_t trigger_test_error(struct ext4_attr *a,
111b5799018STheodore Ts'o 				  struct ext4_sb_info *sbi,
112b5799018STheodore Ts'o 				  const char *buf, size_t count)
113b5799018STheodore Ts'o {
114b5799018STheodore Ts'o 	int len = count;
115b5799018STheodore Ts'o 
116b5799018STheodore Ts'o 	if (!capable(CAP_SYS_ADMIN))
117b5799018STheodore Ts'o 		return -EPERM;
118b5799018STheodore Ts'o 
119b5799018STheodore Ts'o 	if (len && buf[len-1] == '\n')
120b5799018STheodore Ts'o 		len--;
121b5799018STheodore Ts'o 
122b5799018STheodore Ts'o 	if (len)
123b5799018STheodore Ts'o 		ext4_error(sbi->s_sb, "%.*s", len, buf);
124b5799018STheodore Ts'o 	return count;
125b5799018STheodore Ts'o }
126b5799018STheodore Ts'o 
12776d33bcaSTheodore Ts'o #define EXT4_ATTR(_name,_mode,_id)					\
128b5799018STheodore Ts'o static struct ext4_attr ext4_attr_##_name = {				\
129b5799018STheodore Ts'o 	.attr = {.name = __stringify(_name), .mode = _mode },		\
13076d33bcaSTheodore Ts'o 	.attr_id = attr_##_id,						\
13176d33bcaSTheodore Ts'o }
13276d33bcaSTheodore Ts'o 
13376d33bcaSTheodore Ts'o #define EXT4_ATTR_FUNC(_name,_mode)  EXT4_ATTR(_name,_mode,_name)
13476d33bcaSTheodore Ts'o 
13576d33bcaSTheodore Ts'o #define EXT4_ATTR_FEATURE(_name)   EXT4_ATTR(_name, 0444, feature)
13676d33bcaSTheodore Ts'o 
13776d33bcaSTheodore Ts'o #define EXT4_ATTR_OFFSET(_name,_mode,_id,_struct,_elname)	\
13876d33bcaSTheodore Ts'o static struct ext4_attr ext4_attr_##_name = {			\
13976d33bcaSTheodore Ts'o 	.attr = {.name = __stringify(_name), .mode = _mode },	\
14076d33bcaSTheodore Ts'o 	.attr_id = attr_##_id,					\
14176d33bcaSTheodore Ts'o 	.attr_ptr = ptr_##_struct##_offset,			\
142b5799018STheodore Ts'o 	.u = {							\
14376d33bcaSTheodore Ts'o 		.offset = offsetof(struct _struct, _elname),\
144b5799018STheodore Ts'o 	},							\
145b5799018STheodore Ts'o }
146b5799018STheodore Ts'o 
14776d33bcaSTheodore Ts'o #define EXT4_RO_ATTR_ES_UI(_name,_elname)				\
14876d33bcaSTheodore Ts'o 	EXT4_ATTR_OFFSET(_name, 0444, pointer_ui, ext4_super_block, _elname)
14976d33bcaSTheodore Ts'o 
15076d33bcaSTheodore Ts'o #define EXT4_RW_ATTR_SBI_UI(_name,_elname)	\
15176d33bcaSTheodore Ts'o 	EXT4_ATTR_OFFSET(_name, 0644, pointer_ui, ext4_sb_info, _elname)
15276d33bcaSTheodore Ts'o 
15376d33bcaSTheodore Ts'o #define EXT4_ATTR_PTR(_name,_mode,_id,_ptr) \
154b5799018STheodore Ts'o static struct ext4_attr ext4_attr_##_name = {			\
155b5799018STheodore Ts'o 	.attr = {.name = __stringify(_name), .mode = _mode },	\
15676d33bcaSTheodore Ts'o 	.attr_id = attr_##_id,					\
15776d33bcaSTheodore Ts'o 	.attr_ptr = ptr_explicit,				\
158b5799018STheodore Ts'o 	.u = {							\
15976d33bcaSTheodore Ts'o 		.explicit_ptr = _ptr,				\
160b5799018STheodore Ts'o 	},							\
161b5799018STheodore Ts'o }
162b5799018STheodore Ts'o 
163b5799018STheodore Ts'o #define ATTR_LIST(name) &ext4_attr_##name.attr
164b5799018STheodore Ts'o 
16576d33bcaSTheodore Ts'o EXT4_ATTR_FUNC(delayed_allocation_blocks, 0444);
16676d33bcaSTheodore Ts'o EXT4_ATTR_FUNC(session_write_kbytes, 0444);
16776d33bcaSTheodore Ts'o EXT4_ATTR_FUNC(lifetime_write_kbytes, 0444);
16876d33bcaSTheodore Ts'o EXT4_ATTR_FUNC(reserved_clusters, 0644);
16976d33bcaSTheodore Ts'o 
17076d33bcaSTheodore Ts'o EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, inode_readahead,
17176d33bcaSTheodore Ts'o 		 ext4_sb_info, s_inode_readahead_blks);
172b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal);
173b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats);
174b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan);
175b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan);
176b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
177b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
178b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
179b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb);
18076d33bcaSTheodore Ts'o EXT4_ATTR(trigger_fs_error, 0200, trigger_test_error);
181b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(err_ratelimit_interval_ms, s_err_ratelimit_state.interval);
182b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(err_ratelimit_burst, s_err_ratelimit_state.burst);
183b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.interval);
184b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
185b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
186b5799018STheodore Ts'o EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
187b5799018STheodore Ts'o EXT4_RO_ATTR_ES_UI(errors_count, s_error_count);
188b5799018STheodore Ts'o EXT4_RO_ATTR_ES_UI(first_error_time, s_first_error_time);
189b5799018STheodore Ts'o EXT4_RO_ATTR_ES_UI(last_error_time, s_last_error_time);
190b5799018STheodore Ts'o 
19176d33bcaSTheodore Ts'o static unsigned int old_bump_val = 128;
19276d33bcaSTheodore Ts'o EXT4_ATTR_PTR(max_writeback_mb_bump, 0444, pointer_ui, &old_bump_val);
19376d33bcaSTheodore Ts'o 
194b5799018STheodore Ts'o static struct attribute *ext4_attrs[] = {
195b5799018STheodore Ts'o 	ATTR_LIST(delayed_allocation_blocks),
196b5799018STheodore Ts'o 	ATTR_LIST(session_write_kbytes),
197b5799018STheodore Ts'o 	ATTR_LIST(lifetime_write_kbytes),
198b5799018STheodore Ts'o 	ATTR_LIST(reserved_clusters),
199b5799018STheodore Ts'o 	ATTR_LIST(inode_readahead_blks),
200b5799018STheodore Ts'o 	ATTR_LIST(inode_goal),
201b5799018STheodore Ts'o 	ATTR_LIST(mb_stats),
202b5799018STheodore Ts'o 	ATTR_LIST(mb_max_to_scan),
203b5799018STheodore Ts'o 	ATTR_LIST(mb_min_to_scan),
204b5799018STheodore Ts'o 	ATTR_LIST(mb_order2_req),
205b5799018STheodore Ts'o 	ATTR_LIST(mb_stream_req),
206b5799018STheodore Ts'o 	ATTR_LIST(mb_group_prealloc),
207b5799018STheodore Ts'o 	ATTR_LIST(max_writeback_mb_bump),
208b5799018STheodore Ts'o 	ATTR_LIST(extent_max_zeroout_kb),
209b5799018STheodore Ts'o 	ATTR_LIST(trigger_fs_error),
210b5799018STheodore Ts'o 	ATTR_LIST(err_ratelimit_interval_ms),
211b5799018STheodore Ts'o 	ATTR_LIST(err_ratelimit_burst),
212b5799018STheodore Ts'o 	ATTR_LIST(warning_ratelimit_interval_ms),
213b5799018STheodore Ts'o 	ATTR_LIST(warning_ratelimit_burst),
214b5799018STheodore Ts'o 	ATTR_LIST(msg_ratelimit_interval_ms),
215b5799018STheodore Ts'o 	ATTR_LIST(msg_ratelimit_burst),
216b5799018STheodore Ts'o 	ATTR_LIST(errors_count),
217b5799018STheodore Ts'o 	ATTR_LIST(first_error_time),
218b5799018STheodore Ts'o 	ATTR_LIST(last_error_time),
219b5799018STheodore Ts'o 	NULL,
220b5799018STheodore Ts'o };
221b5799018STheodore Ts'o 
222b5799018STheodore Ts'o /* Features this copy of ext4 supports */
22376d33bcaSTheodore Ts'o EXT4_ATTR_FEATURE(lazy_itable_init);
22476d33bcaSTheodore Ts'o EXT4_ATTR_FEATURE(batched_discard);
22576d33bcaSTheodore Ts'o EXT4_ATTR_FEATURE(meta_bg_resize);
22676d33bcaSTheodore Ts'o EXT4_ATTR_FEATURE(encryption);
227b5799018STheodore Ts'o 
228b5799018STheodore Ts'o static struct attribute *ext4_feat_attrs[] = {
229b5799018STheodore Ts'o 	ATTR_LIST(lazy_itable_init),
230b5799018STheodore Ts'o 	ATTR_LIST(batched_discard),
231b5799018STheodore Ts'o 	ATTR_LIST(meta_bg_resize),
232b5799018STheodore Ts'o 	ATTR_LIST(encryption),
233b5799018STheodore Ts'o 	NULL,
234b5799018STheodore Ts'o };
235b5799018STheodore Ts'o 
23676d33bcaSTheodore Ts'o static void *calc_ptr(struct ext4_attr *a, struct ext4_sb_info *sbi)
23776d33bcaSTheodore Ts'o {
23876d33bcaSTheodore Ts'o 	switch (a->attr_ptr) {
23976d33bcaSTheodore Ts'o 	case ptr_explicit:
24076d33bcaSTheodore Ts'o 		return a->u.explicit_ptr;
24176d33bcaSTheodore Ts'o 	case ptr_ext4_sb_info_offset:
24276d33bcaSTheodore Ts'o 		return (void *) (((char *) sbi) + a->u.offset);
24376d33bcaSTheodore Ts'o 	case ptr_ext4_super_block_offset:
24476d33bcaSTheodore Ts'o 		return (void *) (((char *) sbi->s_es) + a->u.offset);
24576d33bcaSTheodore Ts'o 	}
24676d33bcaSTheodore Ts'o 	return NULL;
24776d33bcaSTheodore Ts'o }
24876d33bcaSTheodore Ts'o 
249b5799018STheodore Ts'o static ssize_t ext4_attr_show(struct kobject *kobj,
250b5799018STheodore Ts'o 			      struct attribute *attr, char *buf)
251b5799018STheodore Ts'o {
252b5799018STheodore Ts'o 	struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
253b5799018STheodore Ts'o 						s_kobj);
254b5799018STheodore Ts'o 	struct ext4_attr *a = container_of(attr, struct ext4_attr, attr);
25576d33bcaSTheodore Ts'o 	void *ptr = calc_ptr(a, sbi);
256b5799018STheodore Ts'o 
25776d33bcaSTheodore Ts'o 	switch (a->attr_id) {
25876d33bcaSTheodore Ts'o 	case attr_delayed_allocation_blocks:
25976d33bcaSTheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "%llu\n",
26076d33bcaSTheodore Ts'o 				(s64) EXT4_C2B(sbi,
26176d33bcaSTheodore Ts'o 		       percpu_counter_sum(&sbi->s_dirtyclusters_counter)));
26276d33bcaSTheodore Ts'o 	case attr_session_write_kbytes:
26376d33bcaSTheodore Ts'o 		return session_write_kbytes_show(a, sbi, buf);
26476d33bcaSTheodore Ts'o 	case attr_lifetime_write_kbytes:
26576d33bcaSTheodore Ts'o 		return lifetime_write_kbytes_show(a, sbi, buf);
26676d33bcaSTheodore Ts'o 	case attr_reserved_clusters:
26776d33bcaSTheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "%llu\n",
26876d33bcaSTheodore Ts'o 				(unsigned long long)
26976d33bcaSTheodore Ts'o 				atomic64_read(&sbi->s_resv_clusters));
27076d33bcaSTheodore Ts'o 	case attr_inode_readahead:
27176d33bcaSTheodore Ts'o 	case attr_pointer_ui:
27276d33bcaSTheodore Ts'o 		if (!ptr)
27376d33bcaSTheodore Ts'o 			return 0;
27476d33bcaSTheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "%u\n",
27576d33bcaSTheodore Ts'o 				*((unsigned int *) ptr));
27676d33bcaSTheodore Ts'o 	case attr_pointer_atomic:
27776d33bcaSTheodore Ts'o 		if (!ptr)
27876d33bcaSTheodore Ts'o 			return 0;
27976d33bcaSTheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "%d\n",
28076d33bcaSTheodore Ts'o 				atomic_read((atomic_t *) ptr));
28176d33bcaSTheodore Ts'o 	case attr_feature:
28276d33bcaSTheodore Ts'o 		return snprintf(buf, PAGE_SIZE, "supported\n");
28376d33bcaSTheodore Ts'o 	}
28476d33bcaSTheodore Ts'o 
28576d33bcaSTheodore Ts'o 	return 0;
286b5799018STheodore Ts'o }
287b5799018STheodore Ts'o 
288b5799018STheodore Ts'o static ssize_t ext4_attr_store(struct kobject *kobj,
289b5799018STheodore Ts'o 			       struct attribute *attr,
290b5799018STheodore Ts'o 			       const char *buf, size_t len)
291b5799018STheodore Ts'o {
292b5799018STheodore Ts'o 	struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
293b5799018STheodore Ts'o 						s_kobj);
294b5799018STheodore Ts'o 	struct ext4_attr *a = container_of(attr, struct ext4_attr, attr);
29576d33bcaSTheodore Ts'o 	void *ptr = calc_ptr(a, sbi);
29676d33bcaSTheodore Ts'o 	unsigned long t;
29776d33bcaSTheodore Ts'o 	int ret;
298b5799018STheodore Ts'o 
29976d33bcaSTheodore Ts'o 	switch (a->attr_id) {
30076d33bcaSTheodore Ts'o 	case attr_reserved_clusters:
30176d33bcaSTheodore Ts'o 		return reserved_clusters_store(a, sbi, buf, len);
30276d33bcaSTheodore Ts'o 	case attr_pointer_ui:
30376d33bcaSTheodore Ts'o 		if (!ptr)
30476d33bcaSTheodore Ts'o 			return 0;
30576d33bcaSTheodore Ts'o 		ret = kstrtoul(skip_spaces(buf), 0, &t);
30676d33bcaSTheodore Ts'o 		if (ret)
30776d33bcaSTheodore Ts'o 			return ret;
30876d33bcaSTheodore Ts'o 		*((unsigned int *) ptr) = t;
30976d33bcaSTheodore Ts'o 		return len;
31076d33bcaSTheodore Ts'o 	case attr_inode_readahead:
31176d33bcaSTheodore Ts'o 		return inode_readahead_blks_store(a, sbi, buf, len);
31276d33bcaSTheodore Ts'o 	case attr_trigger_test_error:
31376d33bcaSTheodore Ts'o 		return trigger_test_error(a, sbi, buf, len);
31476d33bcaSTheodore Ts'o 	}
31576d33bcaSTheodore Ts'o 	return 0;
316b5799018STheodore Ts'o }
317b5799018STheodore Ts'o 
318b5799018STheodore Ts'o static void ext4_sb_release(struct kobject *kobj)
319b5799018STheodore Ts'o {
320b5799018STheodore Ts'o 	struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
321b5799018STheodore Ts'o 						s_kobj);
322b5799018STheodore Ts'o 	complete(&sbi->s_kobj_unregister);
323b5799018STheodore Ts'o }
324b5799018STheodore Ts'o 
325b5799018STheodore Ts'o static const struct sysfs_ops ext4_attr_ops = {
326b5799018STheodore Ts'o 	.show	= ext4_attr_show,
327b5799018STheodore Ts'o 	.store	= ext4_attr_store,
328b5799018STheodore Ts'o };
329b5799018STheodore Ts'o 
330b5799018STheodore Ts'o static struct kobj_type ext4_sb_ktype = {
331b5799018STheodore Ts'o 	.default_attrs	= ext4_attrs,
332b5799018STheodore Ts'o 	.sysfs_ops	= &ext4_attr_ops,
333b5799018STheodore Ts'o 	.release	= ext4_sb_release,
334b5799018STheodore Ts'o };
335b5799018STheodore Ts'o 
336b5799018STheodore Ts'o static struct kobj_type ext4_ktype = {
337b5799018STheodore Ts'o 	.sysfs_ops	= &ext4_attr_ops,
338b5799018STheodore Ts'o };
339b5799018STheodore Ts'o 
340b5799018STheodore Ts'o static struct kset ext4_kset = {
341b5799018STheodore Ts'o 	.kobj   = {.ktype = &ext4_ktype},
342b5799018STheodore Ts'o };
343b5799018STheodore Ts'o 
344b5799018STheodore Ts'o static struct kobj_type ext4_feat_ktype = {
345b5799018STheodore Ts'o 	.default_attrs	= ext4_feat_attrs,
34676d33bcaSTheodore Ts'o 	.sysfs_ops	= &ext4_attr_ops,
347b5799018STheodore Ts'o };
348b5799018STheodore Ts'o 
349b5799018STheodore Ts'o static struct kobject ext4_feat = {
350b5799018STheodore Ts'o 	.kset	= &ext4_kset,
351b5799018STheodore Ts'o };
352b5799018STheodore Ts'o 
353ebd173beSTheodore Ts'o #define PROC_FILE_SHOW_DEFN(name) \
354ebd173beSTheodore Ts'o static int name##_open(struct inode *inode, struct file *file) \
355ebd173beSTheodore Ts'o { \
356ebd173beSTheodore Ts'o 	return single_open(file, ext4_seq_##name##_show, PDE_DATA(inode)); \
357ebd173beSTheodore Ts'o } \
358ebd173beSTheodore Ts'o \
359ebd173beSTheodore Ts'o const struct file_operations ext4_seq_##name##_fops = { \
360ebd173beSTheodore Ts'o 	.owner		= THIS_MODULE, \
361ebd173beSTheodore Ts'o 	.open		= name##_open, \
362ebd173beSTheodore Ts'o 	.read		= seq_read, \
363ebd173beSTheodore Ts'o 	.llseek		= seq_lseek, \
364ebd173beSTheodore Ts'o 	.release	= single_release, \
365ebd173beSTheodore Ts'o }
366ebd173beSTheodore Ts'o 
367ebd173beSTheodore Ts'o #define PROC_FILE_LIST(name) \
368ebd173beSTheodore Ts'o 	{ __stringify(name), &ext4_seq_##name##_fops }
369ebd173beSTheodore Ts'o 
370ebd173beSTheodore Ts'o PROC_FILE_SHOW_DEFN(es_shrinker_info);
371ebd173beSTheodore Ts'o PROC_FILE_SHOW_DEFN(options);
372ebd173beSTheodore Ts'o 
373ebd173beSTheodore Ts'o static struct ext4_proc_files {
374ebd173beSTheodore Ts'o 	const char *name;
375ebd173beSTheodore Ts'o 	const struct file_operations *fops;
376ebd173beSTheodore Ts'o } proc_files[] = {
377ebd173beSTheodore Ts'o 	PROC_FILE_LIST(options),
378ebd173beSTheodore Ts'o 	PROC_FILE_LIST(es_shrinker_info),
379ebd173beSTheodore Ts'o 	PROC_FILE_LIST(mb_groups),
380ebd173beSTheodore Ts'o 	{ NULL, NULL },
381ebd173beSTheodore Ts'o };
382ebd173beSTheodore Ts'o 
383b5799018STheodore Ts'o int ext4_register_sysfs(struct super_block *sb)
384b5799018STheodore Ts'o {
385b5799018STheodore Ts'o 	struct ext4_sb_info *sbi = EXT4_SB(sb);
386ebd173beSTheodore Ts'o 	struct ext4_proc_files *p;
387ebd173beSTheodore Ts'o 	int err;
388b5799018STheodore Ts'o 
389b5799018STheodore Ts'o 	sbi->s_kobj.kset = &ext4_kset;
390b5799018STheodore Ts'o 	init_completion(&sbi->s_kobj_unregister);
391ebd173beSTheodore Ts'o 	err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, NULL,
392b5799018STheodore Ts'o 				   "%s", sb->s_id);
393ebd173beSTheodore Ts'o 	if (err)
394ebd173beSTheodore Ts'o 		return err;
395ebd173beSTheodore Ts'o 
396ebd173beSTheodore Ts'o 	if (ext4_proc_root)
397ebd173beSTheodore Ts'o 		sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root);
398ebd173beSTheodore Ts'o 
399ebd173beSTheodore Ts'o 	if (sbi->s_proc) {
400ebd173beSTheodore Ts'o 		for (p = proc_files; p->name; p++)
401ebd173beSTheodore Ts'o 			proc_create_data(p->name, S_IRUGO, sbi->s_proc,
402ebd173beSTheodore Ts'o 					 p->fops, sb);
403ebd173beSTheodore Ts'o 	}
404ebd173beSTheodore Ts'o 	return 0;
405ebd173beSTheodore Ts'o }
406ebd173beSTheodore Ts'o 
407ebd173beSTheodore Ts'o void ext4_unregister_sysfs(struct super_block *sb)
408ebd173beSTheodore Ts'o {
409ebd173beSTheodore Ts'o 	struct ext4_sb_info *sbi = EXT4_SB(sb);
410ebd173beSTheodore Ts'o 	struct ext4_proc_files *p;
411ebd173beSTheodore Ts'o 
412ebd173beSTheodore Ts'o 	if (sbi->s_proc) {
413ebd173beSTheodore Ts'o 		for (p = proc_files; p->name; p++)
414ebd173beSTheodore Ts'o 			remove_proc_entry(p->name, sbi->s_proc);
415ebd173beSTheodore Ts'o 		remove_proc_entry(sb->s_id, ext4_proc_root);
416ebd173beSTheodore Ts'o 	}
417ebd173beSTheodore Ts'o 	kobject_del(&sbi->s_kobj);
418b5799018STheodore Ts'o }
419b5799018STheodore Ts'o 
420b5799018STheodore Ts'o int __init ext4_init_sysfs(void)
421b5799018STheodore Ts'o {
422b5799018STheodore Ts'o 	int ret;
423b5799018STheodore Ts'o 
424b5799018STheodore Ts'o 	kobject_set_name(&ext4_kset.kobj, "ext4");
425b5799018STheodore Ts'o 	ext4_kset.kobj.parent = fs_kobj;
426b5799018STheodore Ts'o 	ret = kset_register(&ext4_kset);
427b5799018STheodore Ts'o 	if (ret)
428b5799018STheodore Ts'o 		return ret;
429b5799018STheodore Ts'o 
430b5799018STheodore Ts'o 	ret = kobject_init_and_add(&ext4_feat, &ext4_feat_ktype,
431b5799018STheodore Ts'o 				   NULL, "features");
432b5799018STheodore Ts'o 	if (ret)
433b5799018STheodore Ts'o 		kset_unregister(&ext4_kset);
434ebd173beSTheodore Ts'o 	else
435ebd173beSTheodore Ts'o 		ext4_proc_root = proc_mkdir(proc_dirname, NULL);
436b5799018STheodore Ts'o 	return ret;
437b5799018STheodore Ts'o }
438b5799018STheodore Ts'o 
439b5799018STheodore Ts'o void ext4_exit_sysfs(void)
440b5799018STheodore Ts'o {
441b5799018STheodore Ts'o 	kobject_put(&ext4_feat);
442b5799018STheodore Ts'o 	kset_unregister(&ext4_kset);
443ebd173beSTheodore Ts'o 	remove_proc_entry(proc_dirname, NULL);
444ebd173beSTheodore Ts'o 	ext4_proc_root = NULL;
445b5799018STheodore Ts'o }
446b5799018STheodore Ts'o 
447