| 1 | |
| 2 | |
| 3 | * linux/fs/ext4/sysfs.c
|
| 4 | *
|
| 5 | * Copyright (C) 1992, 1993, 1994, 1995
|
| 6 | * Remy Card ([email protected])
|
| 7 | * Theodore Ts'o ([email protected])
|
| 8 | *
|
| 9 |
|
| 10 |
|
| 11 | #include <linux/time.h>
|
| 12 | #include <linux/fs.h>
|
| 13 | #include <linux/seq_file.h>
|
| 14 | #include <linux/slab.h>
|
| 15 | #include <linux/proc_fs.h>
|
| 16 | #include <linux/part_stat.h>
|
| 17 |
|
| 18 | #include "ext4.h"
|
| 19 | #include "ext4_jbd2.h"
|
| 20 |
|
| 21 | typedef enum {
|
| 22 | attr_noop,
|
| 23 | attr_delayed_allocation_blocks,
|
| 24 | attr_session_write_kbytes,
|
| 25 | attr_lifetime_write_kbytes,
|
| 26 | attr_reserved_clusters,
|
| 27 | attr_sra_exceeded_retry_limit,
|
| 28 | attr_inode_readahead,
|
| 29 | attr_trigger_test_error,
|
| 30 | attr_first_error_time,
|
| 31 | attr_last_error_time,
|
| 32 | attr_clusters_in_group,
|
| 33 | attr_mb_order,
|
| 34 | attr_feature,
|
| 35 | attr_pointer_pi,
|
| 36 | attr_pointer_ui,
|
| 37 | attr_pointer_ul,
|
| 38 | attr_pointer_u64,
|
| 39 | attr_pointer_u8,
|
| 40 | attr_pointer_string,
|
| 41 | attr_pointer_atomic,
|
| 42 | attr_journal_task,
|
| 43 | attr_err_report_sec,
|
| 44 | } attr_id_t;
|
| 45 |
|
| 46 | typedef enum {
|
| 47 | ptr_explicit,
|
| 48 | ptr_ext4_sb_info_offset,
|
| 49 | ptr_ext4_super_block_offset,
|
| 50 | } attr_ptr_t;
|
| 51 |
|
| 52 | static const char proc_dirname[] = "fs/ext4";
|
| 53 | static struct proc_dir_entry *ext4_proc_root;
|
| 54 |
|
| 55 | struct ext4_attr {
|
| 56 | struct attribute attr;
|
| 57 | short attr_id;
|
| 58 | short attr_ptr;
|
| 59 | unsigned short attr_size;
|
| 60 | union {
|
| 61 | int offset;
|
| 62 | void *explicit_ptr;
|
| 63 | } u;
|
| 64 | };
|
| 65 |
|
| 66 | static ssize_t session_write_kbytes_show(struct ext4_sb_info *sbi, char *buf)
|
| 67 | {
|
| 68 | struct super_block *sb = sbi->s_buddy_cache->i_sb;
|
| 69 |
|
| 70 | return sysfs_emit(buf, "%lu\n",
|
| 71 | (part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
|
| 72 | sbi->s_sectors_written_start) >> 1);
|
| 73 | }
|
| 74 |
|
| 75 | static ssize_t lifetime_write_kbytes_show(struct ext4_sb_info *sbi, char *buf)
|
| 76 | {
|
| 77 | struct super_block *sb = sbi->s_buddy_cache->i_sb;
|
| 78 |
|
| 79 | return sysfs_emit(buf, "%llu\n",
|
| 80 | (unsigned long long)(sbi->s_kbytes_written +
|
| 81 | ((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
|
| 82 | EXT4_SB(sb)->s_sectors_written_start) >> 1)));
|
| 83 | }
|
| 84 |
|
| 85 | static ssize_t inode_readahead_blks_store(struct ext4_sb_info *sbi,
|
| 86 | const char *buf, size_t count)
|
| 87 | {
|
| 88 | unsigned long t;
|
| 89 | int ret;
|
| 90 |
|
| 91 | ret = kstrtoul(skip_spaces(buf), 0, &t);
|
| 92 | if (ret)
|
| 93 | return ret;
|
| 94 |
|
| 95 | if (t && (!is_power_of_2(t) || t > 0x40000000))
|
| 96 | return -EINVAL;
|
| 97 |
|
| 98 | sbi->s_inode_readahead_blks = t;
|
| 99 | return count;
|
| 100 | }
|
| 101 |
|
| 102 | static ssize_t reserved_clusters_store(struct ext4_sb_info *sbi,
|
| 103 | const char *buf, size_t count)
|
| 104 | {
|
| 105 | unsigned long long val;
|
| 106 | ext4_fsblk_t clusters = (ext4_blocks_count(sbi->s_es) >>
|
| 107 | sbi->s_cluster_bits);
|
| 108 | int ret;
|
| 109 |
|
| 110 | ret = kstrtoull(skip_spaces(buf), 0, &val);
|
| 111 | if (ret || val >= clusters || (s64)val < 0)
|
| 112 | return -EINVAL;
|
| 113 |
|
| 114 | atomic64_set(&sbi->s_resv_clusters, val);
|
| 115 | return count;
|
| 116 | }
|
| 117 |
|
| 118 | static ssize_t trigger_test_error(struct ext4_sb_info *sbi,
|
| 119 | const char *buf, size_t count)
|
| 120 | {
|
| 121 | int len = count;
|
| 122 |
|
| 123 | if (!capable(CAP_SYS_ADMIN))
|
| 124 | return -EPERM;
|
| 125 |
|
| 126 | if (len && buf[len-1] == '\n')
|
| 127 | len--;
|
| 128 |
|
| 129 | if (len)
|
| 130 | ext4_error(sbi->s_sb, "%.*s", len, buf);
|
| 131 | return count;
|
| 132 | }
|
| 133 |
|
| 134 | static ssize_t err_report_sec_store(struct ext4_sb_info *sbi,
|
| 135 | const char *buf, size_t count)
|
| 136 | {
|
| 137 | unsigned long t;
|
| 138 | int ret;
|
| 139 |
|
| 140 | ret = kstrtoul(skip_spaces(buf), 0, &t);
|
| 141 | if (ret)
|
| 142 | return ret;
|
| 143 |
|
| 144 |
|
| 145 | if (t > (365*24*60*60))
|
| 146 | return -EINVAL;
|
| 147 |
|
| 148 | if (sbi->s_err_report_sec == t)
|
| 149 | goto out;
|
| 150 | else if (!sbi->s_err_report_sec && t) {
|
| 151 | timer_setup(&sbi->s_err_report, print_daily_error_info, 0);
|
| 152 | } else if (sbi->s_err_report_sec && !t) {
|
| 153 | timer_delete_sync(&sbi->s_err_report);
|
| 154 | goto out;
|
| 155 | }
|
| 156 |
|
| 157 | sbi->s_err_report_sec = t;
|
| 158 | mod_timer(&sbi->s_err_report, jiffies + secs_to_jiffies(sbi->s_err_report_sec));
|
| 159 |
|
| 160 | out:
|
| 161 | return count;
|
| 162 | }
|
| 163 |
|
| 164 | static ssize_t journal_task_show(struct ext4_sb_info *sbi, char *buf)
|
| 165 | {
|
| 166 | if (!sbi->s_journal)
|
| 167 | return sysfs_emit(buf, "<none>\n");
|
| 168 | return sysfs_emit(buf, "%d\n",
|
| 169 | task_pid_vnr(sbi->s_journal->j_task));
|
| 170 | }
|
| 171 |
|
| 172 | #define EXT4_ATTR(_name,_mode,_id) \
|
| 173 | static struct ext4_attr ext4_attr_##_name = { \
|
| 174 | .attr = {.name = __stringify(_name), .mode = _mode }, \
|
| 175 | .attr_id = attr_##_id, \
|
| 176 | }
|
| 177 |
|
| 178 | #define EXT4_ATTR_FUNC(_name,_mode) EXT4_ATTR(_name,_mode,_name)
|
| 179 |
|
| 180 | #define EXT4_ATTR_FEATURE(_name) EXT4_ATTR(_name, 0444, feature)
|
| 181 |
|
| 182 | #define EXT4_ATTR_OFFSET(_name,_mode,_id,_struct,_elname) \
|
| 183 | static struct ext4_attr ext4_attr_##_name = { \
|
| 184 | .attr = {.name = __stringify(_name), .mode = _mode }, \
|
| 185 | .attr_id = attr_##_id, \
|
| 186 | .attr_ptr = ptr_##_struct##_offset, \
|
| 187 | .u = { \
|
| 188 | .offset = offsetof(struct _struct, _elname),\
|
| 189 | }, \
|
| 190 | }
|
| 191 |
|
| 192 | #define EXT4_ATTR_STRING(_name,_mode,_size,_struct,_elname) \
|
| 193 | static struct ext4_attr ext4_attr_##_name = { \
|
| 194 | .attr = {.name = __stringify(_name), .mode = _mode }, \
|
| 195 | .attr_id = attr_pointer_string, \
|
| 196 | .attr_size = _size, \
|
| 197 | .attr_ptr = ptr_##_struct##_offset, \
|
| 198 | .u = { \
|
| 199 | .offset = offsetof(struct _struct, _elname),\
|
| 200 | }, \
|
| 201 | }
|
| 202 |
|
| 203 | #define EXT4_RO_ATTR_ES_UI(_name,_elname) \
|
| 204 | EXT4_ATTR_OFFSET(_name, 0444, pointer_ui, ext4_super_block, _elname)
|
| 205 |
|
| 206 | #define EXT4_RO_ATTR_ES_U8(_name,_elname) \
|
| 207 | EXT4_ATTR_OFFSET(_name, 0444, pointer_u8, ext4_super_block, _elname)
|
| 208 |
|
| 209 | #define EXT4_RO_ATTR_ES_U64(_name,_elname) \
|
| 210 | EXT4_ATTR_OFFSET(_name, 0444, pointer_u64, ext4_super_block, _elname)
|
| 211 |
|
| 212 | #define EXT4_RO_ATTR_ES_STRING(_name,_elname,_size) \
|
| 213 | EXT4_ATTR_STRING(_name, 0444, _size, ext4_super_block, _elname)
|
| 214 |
|
| 215 | #define EXT4_RW_ATTR_SBI_PI(_name,_elname) \
|
| 216 | EXT4_ATTR_OFFSET(_name, 0644, pointer_pi, ext4_sb_info, _elname)
|
| 217 |
|
| 218 | #define EXT4_RW_ATTR_SBI_UI(_name,_elname) \
|
| 219 | EXT4_ATTR_OFFSET(_name, 0644, pointer_ui, ext4_sb_info, _elname)
|
| 220 |
|
| 221 | #define EXT4_RW_ATTR_SBI_UL(_name,_elname) \
|
| 222 | EXT4_ATTR_OFFSET(_name, 0644, pointer_ul, ext4_sb_info, _elname)
|
| 223 |
|
| 224 | #define EXT4_RO_ATTR_SBI_ATOMIC(_name,_elname) \
|
| 225 | EXT4_ATTR_OFFSET(_name, 0444, pointer_atomic, ext4_sb_info, _elname)
|
| 226 |
|
| 227 | #define EXT4_ATTR_PTR(_name,_mode,_id,_ptr) \
|
| 228 | static struct ext4_attr ext4_attr_##_name = { \
|
| 229 | .attr = {.name = __stringify(_name), .mode = _mode }, \
|
| 230 | .attr_id = attr_##_id, \
|
| 231 | .attr_ptr = ptr_explicit, \
|
| 232 | .u = { \
|
| 233 | .explicit_ptr = _ptr, \
|
| 234 | }, \
|
| 235 | }
|
| 236 |
|
| 237 | #define ATTR_LIST(name) &ext4_attr_##name.attr
|
| 238 |
|
| 239 | EXT4_ATTR_FUNC(delayed_allocation_blocks, 0444);
|
| 240 | EXT4_ATTR_FUNC(session_write_kbytes, 0444);
|
| 241 | EXT4_ATTR_FUNC(lifetime_write_kbytes, 0444);
|
| 242 | EXT4_ATTR_FUNC(reserved_clusters, 0644);
|
| 243 | EXT4_ATTR_FUNC(sra_exceeded_retry_limit, 0444);
|
| 244 |
|
| 245 | EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, inode_readahead,
|
| 246 | ext4_sb_info, s_inode_readahead_blks);
|
| 247 | EXT4_ATTR_OFFSET(mb_group_prealloc, 0644, clusters_in_group,
|
| 248 | ext4_sb_info, s_mb_group_prealloc);
|
| 249 | EXT4_ATTR_OFFSET(mb_best_avail_max_trim_order, 0644, mb_order,
|
| 250 | ext4_sb_info, s_mb_best_avail_max_trim_order);
|
| 251 | EXT4_ATTR_OFFSET(err_report_sec, 0644, err_report_sec, ext4_sb_info, s_err_report_sec);
|
| 252 | EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal);
|
| 253 | EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats);
|
| 254 | EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan);
|
| 255 | EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan);
|
| 256 | EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
|
| 257 | EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
|
| 258 | EXT4_RW_ATTR_SBI_UI(mb_max_linear_groups, s_mb_max_linear_groups);
|
| 259 | EXT4_RW_ATTR_SBI_UI(extent_max_zeroout_kb, s_extent_max_zeroout_kb);
|
| 260 | EXT4_ATTR(trigger_fs_error, 0200, trigger_test_error);
|
| 261 | EXT4_RW_ATTR_SBI_PI(err_ratelimit_interval_ms, s_err_ratelimit_state.interval);
|
| 262 | EXT4_RW_ATTR_SBI_PI(err_ratelimit_burst, s_err_ratelimit_state.burst);
|
| 263 | EXT4_RW_ATTR_SBI_PI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.interval);
|
| 264 | EXT4_RW_ATTR_SBI_PI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
|
| 265 | EXT4_RW_ATTR_SBI_PI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
|
| 266 | EXT4_RW_ATTR_SBI_PI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
|
| 267 | #ifdef CONFIG_EXT4_DEBUG
|
| 268 | EXT4_RW_ATTR_SBI_UL(simulate_fail, s_simulate_fail);
|
| 269 | #endif
|
| 270 | EXT4_RO_ATTR_SBI_ATOMIC(warning_count, s_warning_count);
|
| 271 | EXT4_RO_ATTR_SBI_ATOMIC(msg_count, s_msg_count);
|
| 272 | EXT4_RO_ATTR_ES_UI(errors_count, s_error_count);
|
| 273 | EXT4_RO_ATTR_ES_U8(first_error_errcode, s_first_error_errcode);
|
| 274 | EXT4_RO_ATTR_ES_U8(last_error_errcode, s_last_error_errcode);
|
| 275 | EXT4_RO_ATTR_ES_UI(first_error_ino, s_first_error_ino);
|
| 276 | EXT4_RO_ATTR_ES_UI(last_error_ino, s_last_error_ino);
|
| 277 | EXT4_RO_ATTR_ES_U64(first_error_block, s_first_error_block);
|
| 278 | EXT4_RO_ATTR_ES_U64(last_error_block, s_last_error_block);
|
| 279 | EXT4_RO_ATTR_ES_UI(first_error_line, s_first_error_line);
|
| 280 | EXT4_RO_ATTR_ES_UI(last_error_line, s_last_error_line);
|
| 281 | EXT4_RO_ATTR_ES_STRING(first_error_func, s_first_error_func, 32);
|
| 282 | EXT4_RO_ATTR_ES_STRING(last_error_func, s_last_error_func, 32);
|
| 283 | EXT4_ATTR(first_error_time, 0444, first_error_time);
|
| 284 | EXT4_ATTR(last_error_time, 0444, last_error_time);
|
| 285 | EXT4_ATTR(journal_task, 0444, journal_task);
|
| 286 | EXT4_RW_ATTR_SBI_UI(mb_prefetch, s_mb_prefetch);
|
| 287 | EXT4_RW_ATTR_SBI_UI(mb_prefetch_limit, s_mb_prefetch_limit);
|
| 288 | EXT4_RW_ATTR_SBI_UL(last_trim_minblks, s_last_trim_minblks);
|
| 289 | EXT4_RW_ATTR_SBI_UI(sb_update_sec, s_sb_update_sec);
|
| 290 | EXT4_RW_ATTR_SBI_UI(sb_update_kb, s_sb_update_kb);
|
| 291 |
|
| 292 | static unsigned int old_bump_val = 128;
|
| 293 | EXT4_ATTR_PTR(max_writeback_mb_bump, 0444, pointer_ui, &old_bump_val);
|
| 294 |
|
| 295 | static struct attribute *ext4_attrs[] = {
|
| 296 | ATTR_LIST(delayed_allocation_blocks),
|
| 297 | ATTR_LIST(session_write_kbytes),
|
| 298 | ATTR_LIST(lifetime_write_kbytes),
|
| 299 | ATTR_LIST(reserved_clusters),
|
| 300 | ATTR_LIST(sra_exceeded_retry_limit),
|
| 301 | ATTR_LIST(inode_readahead_blks),
|
| 302 | ATTR_LIST(inode_goal),
|
| 303 | ATTR_LIST(mb_stats),
|
| 304 | ATTR_LIST(mb_max_to_scan),
|
| 305 | ATTR_LIST(mb_min_to_scan),
|
| 306 | ATTR_LIST(mb_order2_req),
|
| 307 | ATTR_LIST(mb_stream_req),
|
| 308 | ATTR_LIST(mb_group_prealloc),
|
| 309 | ATTR_LIST(mb_max_linear_groups),
|
| 310 | ATTR_LIST(max_writeback_mb_bump),
|
| 311 | ATTR_LIST(extent_max_zeroout_kb),
|
| 312 | ATTR_LIST(trigger_fs_error),
|
| 313 | ATTR_LIST(err_ratelimit_interval_ms),
|
| 314 | ATTR_LIST(err_ratelimit_burst),
|
| 315 | ATTR_LIST(warning_ratelimit_interval_ms),
|
| 316 | ATTR_LIST(warning_ratelimit_burst),
|
| 317 | ATTR_LIST(msg_ratelimit_interval_ms),
|
| 318 | ATTR_LIST(msg_ratelimit_burst),
|
| 319 | ATTR_LIST(mb_best_avail_max_trim_order),
|
| 320 | ATTR_LIST(errors_count),
|
| 321 | ATTR_LIST(warning_count),
|
| 322 | ATTR_LIST(msg_count),
|
| 323 | ATTR_LIST(first_error_ino),
|
| 324 | ATTR_LIST(last_error_ino),
|
| 325 | ATTR_LIST(first_error_block),
|
| 326 | ATTR_LIST(last_error_block),
|
| 327 | ATTR_LIST(first_error_line),
|
| 328 | ATTR_LIST(last_error_line),
|
| 329 | ATTR_LIST(first_error_func),
|
| 330 | ATTR_LIST(last_error_func),
|
| 331 | ATTR_LIST(first_error_errcode),
|
| 332 | ATTR_LIST(last_error_errcode),
|
| 333 | ATTR_LIST(first_error_time),
|
| 334 | ATTR_LIST(last_error_time),
|
| 335 | ATTR_LIST(journal_task),
|
| 336 | #ifdef CONFIG_EXT4_DEBUG
|
| 337 | ATTR_LIST(simulate_fail),
|
| 338 | #endif
|
| 339 | ATTR_LIST(mb_prefetch),
|
| 340 | ATTR_LIST(mb_prefetch_limit),
|
| 341 | ATTR_LIST(last_trim_minblks),
|
| 342 | ATTR_LIST(sb_update_sec),
|
| 343 | ATTR_LIST(sb_update_kb),
|
| 344 | ATTR_LIST(err_report_sec),
|
| 345 | NULL,
|
| 346 | };
|
| 347 | ATTRIBUTE_GROUPS(ext4);
|
| 348 |
|
| 349 |
|
| 350 | EXT4_ATTR_FEATURE(lazy_itable_init);
|
| 351 | EXT4_ATTR_FEATURE(batched_discard);
|
| 352 | EXT4_ATTR_FEATURE(meta_bg_resize);
|
| 353 | #ifdef CONFIG_FS_ENCRYPTION
|
| 354 | EXT4_ATTR_FEATURE(encryption);
|
| 355 | EXT4_ATTR_FEATURE(test_dummy_encryption_v2);
|
| 356 | #endif
|
| 357 | #if IS_ENABLED(CONFIG_UNICODE)
|
| 358 | EXT4_ATTR_FEATURE(casefold);
|
| 359 | #endif
|
| 360 | #ifdef CONFIG_FS_VERITY
|
| 361 | EXT4_ATTR_FEATURE(verity);
|
| 362 | #endif
|
| 363 | EXT4_ATTR_FEATURE(metadata_csum_seed);
|
| 364 | EXT4_ATTR_FEATURE(fast_commit);
|
| 365 | #if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_FS_ENCRYPTION)
|
| 366 | EXT4_ATTR_FEATURE(encrypted_casefold);
|
| 367 | #endif
|
| 368 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
| 369 | EXT4_ATTR_FEATURE(blocksize_gt_pagesize);
|
| 370 | #endif
|
| 371 |
|
| 372 | static struct attribute *ext4_feat_attrs[] = {
|
| 373 | ATTR_LIST(lazy_itable_init),
|
| 374 | ATTR_LIST(batched_discard),
|
| 375 | ATTR_LIST(meta_bg_resize),
|
| 376 | #ifdef CONFIG_FS_ENCRYPTION
|
| 377 | ATTR_LIST(encryption),
|
| 378 | ATTR_LIST(test_dummy_encryption_v2),
|
| 379 | #endif
|
| 380 | #if IS_ENABLED(CONFIG_UNICODE)
|
| 381 | ATTR_LIST(casefold),
|
| 382 | #endif
|
| 383 | #ifdef CONFIG_FS_VERITY
|
| 384 | ATTR_LIST(verity),
|
| 385 | #endif
|
| 386 | ATTR_LIST(metadata_csum_seed),
|
| 387 | ATTR_LIST(fast_commit),
|
| 388 | #if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_FS_ENCRYPTION)
|
| 389 | ATTR_LIST(encrypted_casefold),
|
| 390 | #endif
|
| 391 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
| 392 | ATTR_LIST(blocksize_gt_pagesize),
|
| 393 | #endif
|
| 394 | NULL,
|
| 395 | };
|
| 396 | ATTRIBUTE_GROUPS(ext4_feat);
|
| 397 |
|
| 398 | static void *calc_ptr(struct ext4_attr *a, struct ext4_sb_info *sbi)
|
| 399 | {
|
| 400 | switch (a->attr_ptr) {
|
| 401 | case ptr_explicit:
|
| 402 | return a->u.explicit_ptr;
|
| 403 | case ptr_ext4_sb_info_offset:
|
| 404 | return (void *) (((char *) sbi) + a->u.offset);
|
| 405 | case ptr_ext4_super_block_offset:
|
| 406 | return (void *) (((char *) sbi->s_es) + a->u.offset);
|
| 407 | }
|
| 408 | return NULL;
|
| 409 | }
|
| 410 |
|
| 411 | static ssize_t __print_tstamp(char *buf, __le32 lo, __u8 hi)
|
| 412 | {
|
| 413 | return sysfs_emit(buf, "%lld\n",
|
| 414 | ((time64_t)hi << 32) + le32_to_cpu(lo));
|
| 415 | }
|
| 416 |
|
| 417 | #define print_tstamp(buf, es, tstamp) \
|
| 418 | __print_tstamp(buf, (es)->tstamp, (es)->tstamp ## _hi)
|
| 419 |
|
| 420 | static ssize_t ext4_generic_attr_show(struct ext4_attr *a,
|
| 421 | struct ext4_sb_info *sbi, char *buf)
|
| 422 | {
|
| 423 | void *ptr = calc_ptr(a, sbi);
|
| 424 |
|
| 425 | if (!ptr)
|
| 426 | return 0;
|
| 427 |
|
| 428 | switch (a->attr_id) {
|
| 429 | case attr_inode_readahead:
|
| 430 | case attr_clusters_in_group:
|
| 431 | case attr_mb_order:
|
| 432 | case attr_pointer_pi:
|
| 433 | case attr_pointer_ui:
|
| 434 | if (a->attr_ptr == ptr_ext4_super_block_offset)
|
| 435 | return sysfs_emit(buf, "%u\n", le32_to_cpup(ptr));
|
| 436 | return sysfs_emit(buf, "%u\n", *((unsigned int *) ptr));
|
| 437 | case attr_pointer_ul:
|
| 438 | case attr_err_report_sec:
|
| 439 | return sysfs_emit(buf, "%lu\n", *((unsigned long *) ptr));
|
| 440 | case attr_pointer_u8:
|
| 441 | return sysfs_emit(buf, "%u\n", *((unsigned char *) ptr));
|
| 442 | case attr_pointer_u64:
|
| 443 | if (a->attr_ptr == ptr_ext4_super_block_offset)
|
| 444 | return sysfs_emit(buf, "%llu\n", le64_to_cpup(ptr));
|
| 445 | return sysfs_emit(buf, "%llu\n", *((unsigned long long *) ptr));
|
| 446 | case attr_pointer_string:
|
| 447 | return sysfs_emit(buf, "%.*s\n", a->attr_size, (char *) ptr);
|
| 448 | case attr_pointer_atomic:
|
| 449 | return sysfs_emit(buf, "%d\n", atomic_read((atomic_t *) ptr));
|
| 450 | }
|
| 451 | return 0;
|
| 452 | }
|
| 453 |
|
| 454 | static ssize_t ext4_attr_show(struct kobject *kobj,
|
| 455 | struct attribute *attr, char *buf)
|
| 456 | {
|
| 457 | struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
|
| 458 | s_kobj);
|
| 459 | struct ext4_attr *a = container_of(attr, struct ext4_attr, attr);
|
| 460 |
|
| 461 | switch (a->attr_id) {
|
| 462 | case attr_delayed_allocation_blocks:
|
| 463 | return sysfs_emit(buf, "%llu\n",
|
| 464 | (s64) EXT4_C2B(sbi,
|
| 465 | percpu_counter_sum(&sbi->s_dirtyclusters_counter)));
|
| 466 | case attr_session_write_kbytes:
|
| 467 | return session_write_kbytes_show(sbi, buf);
|
| 468 | case attr_lifetime_write_kbytes:
|
| 469 | return lifetime_write_kbytes_show(sbi, buf);
|
| 470 | case attr_reserved_clusters:
|
| 471 | return sysfs_emit(buf, "%llu\n",
|
| 472 | (unsigned long long)
|
| 473 | atomic64_read(&sbi->s_resv_clusters));
|
| 474 | case attr_sra_exceeded_retry_limit:
|
| 475 | return sysfs_emit(buf, "%llu\n",
|
| 476 | (unsigned long long)
|
| 477 | percpu_counter_sum(&sbi->s_sra_exceeded_retry_limit));
|
| 478 | case attr_feature:
|
| 479 | return sysfs_emit(buf, "supported\n");
|
| 480 | case attr_first_error_time:
|
| 481 | return print_tstamp(buf, sbi->s_es, s_first_error_time);
|
| 482 | case attr_last_error_time:
|
| 483 | return print_tstamp(buf, sbi->s_es, s_last_error_time);
|
| 484 | case attr_journal_task:
|
| 485 | return journal_task_show(sbi, buf);
|
| 486 | default:
|
| 487 | return ext4_generic_attr_show(a, sbi, buf);
|
| 488 | }
|
| 489 | }
|
| 490 |
|
| 491 | static ssize_t ext4_generic_attr_store(struct ext4_attr *a,
|
| 492 | struct ext4_sb_info *sbi,
|
| 493 | const char *buf, size_t len)
|
| 494 | {
|
| 495 | int ret;
|
| 496 | unsigned int t;
|
| 497 | unsigned long lt;
|
| 498 | void *ptr = calc_ptr(a, sbi);
|
| 499 |
|
| 500 | if (!ptr)
|
| 501 | return 0;
|
| 502 |
|
| 503 | switch (a->attr_id) {
|
| 504 | case attr_pointer_pi:
|
| 505 | ret = kstrtouint(skip_spaces(buf), 0, &t);
|
| 506 | if (ret)
|
| 507 | return ret;
|
| 508 | if ((int)t < 0)
|
| 509 | return -EINVAL;
|
| 510 | *((unsigned int *) ptr) = t;
|
| 511 | return len;
|
| 512 | case attr_pointer_ui:
|
| 513 | ret = kstrtouint(skip_spaces(buf), 0, &t);
|
| 514 | if (ret)
|
| 515 | return ret;
|
| 516 | if (a->attr_ptr == ptr_ext4_super_block_offset)
|
| 517 | *((__le32 *) ptr) = cpu_to_le32(t);
|
| 518 | else
|
| 519 | *((unsigned int *) ptr) = t;
|
| 520 | return len;
|
| 521 | case attr_mb_order:
|
| 522 | ret = kstrtouint(skip_spaces(buf), 0, &t);
|
| 523 | if (ret)
|
| 524 | return ret;
|
| 525 | if (t > 64)
|
| 526 | return -EINVAL;
|
| 527 | *((unsigned int *) ptr) = t;
|
| 528 | return len;
|
| 529 | case attr_clusters_in_group:
|
| 530 | ret = kstrtouint(skip_spaces(buf), 0, &t);
|
| 531 | if (ret)
|
| 532 | return ret;
|
| 533 | if (t > sbi->s_clusters_per_group)
|
| 534 | return -EINVAL;
|
| 535 | *((unsigned int *) ptr) = t;
|
| 536 | return len;
|
| 537 | case attr_pointer_ul:
|
| 538 | ret = kstrtoul(skip_spaces(buf), 0, <);
|
| 539 | if (ret)
|
| 540 | return ret;
|
| 541 | *((unsigned long *) ptr) = lt;
|
| 542 | return len;
|
| 543 | }
|
| 544 | return 0;
|
| 545 | }
|
| 546 |
|
| 547 | static ssize_t ext4_attr_store(struct kobject *kobj,
|
| 548 | struct attribute *attr,
|
| 549 | const char *buf, size_t len)
|
| 550 | {
|
| 551 | struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
|
| 552 | s_kobj);
|
| 553 | struct ext4_attr *a = container_of(attr, struct ext4_attr, attr);
|
| 554 |
|
| 555 | switch (a->attr_id) {
|
| 556 | case attr_reserved_clusters:
|
| 557 | return reserved_clusters_store(sbi, buf, len);
|
| 558 | case attr_inode_readahead:
|
| 559 | return inode_readahead_blks_store(sbi, buf, len);
|
| 560 | case attr_trigger_test_error:
|
| 561 | return trigger_test_error(sbi, buf, len);
|
| 562 | case attr_err_report_sec:
|
| 563 | return err_report_sec_store(sbi, buf, len);
|
| 564 | default:
|
| 565 | return ext4_generic_attr_store(a, sbi, buf, len);
|
| 566 | }
|
| 567 | }
|
| 568 |
|
| 569 | static void ext4_sb_release(struct kobject *kobj)
|
| 570 | {
|
| 571 | struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info,
|
| 572 | s_kobj);
|
| 573 | complete(&sbi->s_kobj_unregister);
|
| 574 | }
|
| 575 |
|
| 576 | static void ext4_feat_release(struct kobject *kobj)
|
| 577 | {
|
| 578 | kfree(kobj);
|
| 579 | }
|
| 580 |
|
| 581 | static const struct sysfs_ops ext4_attr_ops = {
|
| 582 | .show = ext4_attr_show,
|
| 583 | .store = ext4_attr_store,
|
| 584 | };
|
| 585 |
|
| 586 | static const struct kobj_type ext4_sb_ktype = {
|
| 587 | .default_groups = ext4_groups,
|
| 588 | .sysfs_ops = &ext4_attr_ops,
|
| 589 | .release = ext4_sb_release,
|
| 590 | };
|
| 591 |
|
| 592 | static const struct kobj_type ext4_feat_ktype = {
|
| 593 | .default_groups = ext4_feat_groups,
|
| 594 | .sysfs_ops = &ext4_attr_ops,
|
| 595 | .release = ext4_feat_release,
|
| 596 | };
|
| 597 |
|
| 598 | void ext4_notify_error_sysfs(struct ext4_sb_info *sbi)
|
| 599 | {
|
| 600 | mutex_lock(&sbi->s_error_notify_mutex);
|
| 601 | if (sbi->s_kobj.state_in_sysfs)
|
| 602 | sysfs_notify(&sbi->s_kobj, NULL, "errors_count");
|
| 603 | mutex_unlock(&sbi->s_error_notify_mutex);
|
| 604 | }
|
| 605 |
|
| 606 | static struct kobject *ext4_root;
|
| 607 |
|
| 608 | static struct kobject *ext4_feat;
|
| 609 |
|
| 610 | int ext4_register_sysfs(struct super_block *sb)
|
| 611 | {
|
| 612 | struct ext4_sb_info *sbi = EXT4_SB(sb);
|
| 613 | int err;
|
| 614 |
|
| 615 | init_completion(&sbi->s_kobj_unregister);
|
| 616 | mutex_lock(&sbi->s_error_notify_mutex);
|
| 617 | err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, ext4_root,
|
| 618 | "%s", sb->s_id);
|
| 619 | mutex_unlock(&sbi->s_error_notify_mutex);
|
| 620 | if (err) {
|
| 621 | kobject_put(&sbi->s_kobj);
|
| 622 | wait_for_completion(&sbi->s_kobj_unregister);
|
| 623 | return err;
|
| 624 | }
|
| 625 |
|
| 626 | if (ext4_proc_root)
|
| 627 | sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root);
|
| 628 | if (sbi->s_proc) {
|
| 629 | proc_create_single_data("options", S_IRUGO, sbi->s_proc,
|
| 630 | ext4_seq_options_show, sb);
|
| 631 | proc_create_single_data("es_shrinker_info", S_IRUGO,
|
| 632 | sbi->s_proc, ext4_seq_es_shrinker_info_show,
|
| 633 | sb);
|
| 634 | proc_create_single_data("fc_info", 0444, sbi->s_proc,
|
| 635 | ext4_fc_info_show, sb);
|
| 636 | proc_create_seq_data("mb_groups", S_IRUGO, sbi->s_proc,
|
| 637 | &ext4_mb_seq_groups_ops, sb);
|
| 638 | proc_create_single_data("mb_stats", 0444, sbi->s_proc,
|
| 639 | ext4_seq_mb_stats_show, sb);
|
| 640 | proc_create_seq_data("mb_structs_summary", 0444, sbi->s_proc,
|
| 641 | &ext4_mb_seq_structs_summary_ops, sb);
|
| 642 | }
|
| 643 | return 0;
|
| 644 | }
|
| 645 |
|
| 646 | void ext4_unregister_sysfs(struct super_block *sb)
|
| 647 | {
|
| 648 | struct ext4_sb_info *sbi = EXT4_SB(sb);
|
| 649 |
|
| 650 | if (sbi->s_proc)
|
| 651 | remove_proc_subtree(sb->s_id, ext4_proc_root);
|
| 652 |
|
| 653 | mutex_lock(&sbi->s_error_notify_mutex);
|
| 654 | kobject_del(&sbi->s_kobj);
|
| 655 | mutex_unlock(&sbi->s_error_notify_mutex);
|
| 656 | }
|
| 657 |
|
| 658 | int __init ext4_init_sysfs(void)
|
| 659 | {
|
| 660 | int ret;
|
| 661 |
|
| 662 | ext4_root = kobject_create_and_add("ext4", fs_kobj);
|
| 663 | if (!ext4_root)
|
| 664 | return -ENOMEM;
|
| 665 |
|
| 666 | ext4_feat = kzalloc_obj(*ext4_feat);
|
| 667 | if (!ext4_feat) {
|
| 668 | ret = -ENOMEM;
|
| 669 | goto root_err;
|
| 670 | }
|
| 671 |
|
| 672 | ret = kobject_init_and_add(ext4_feat, &ext4_feat_ktype,
|
| 673 | ext4_root, "features");
|
| 674 | if (ret)
|
| 675 | goto feat_err;
|
| 676 |
|
| 677 | ext4_proc_root = proc_mkdir(proc_dirname, NULL);
|
| 678 | return ret;
|
| 679 |
|
| 680 | feat_err:
|
| 681 | kobject_put(ext4_feat);
|
| 682 | ext4_feat = NULL;
|
| 683 | root_err:
|
| 684 | kobject_put(ext4_root);
|
| 685 | ext4_root = NULL;
|
| 686 | return ret;
|
| 687 | }
|
| 688 |
|
| 689 | void ext4_exit_sysfs(void)
|
| 690 | {
|
| 691 | kobject_put(ext4_feat);
|
| 692 | ext4_feat = NULL;
|
| 693 | kobject_put(ext4_root);
|
| 694 | ext4_root = NULL;
|
| 695 | remove_proc_entry(proc_dirname, NULL);
|
| 696 | ext4_proc_root = NULL;
|
| 697 | }
|
| 698 |
|