Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
9bf1dcb
Documentation
LICENSES
arch
block
certs
crypto
drivers
fs
9p
adfs
affs
afs
autofs
befs
bfs
btrfs
cachefiles
ceph
coda
configfs
cramfs
crypto
debugfs
devpts
dlm
ecryptfs
efivarfs
efs
erofs
exfat
exportfs
ext2
ext4
f2fs
Kconfig
Makefile
acl.c
acl.h
checkpoint.c
compress.c
data.c
debug.c
dir.c
extent_cache.c
f2fs.h
file.c
gc.c
gc.h
hash.c
inline.c
inode.c
iostat.c
iostat.h
namei.c
node.c
node.h
recovery.c
segment.c
segment.h
shrinker.c
super.c
sysfs.c
verity.c
xattr.c
xattr.h
fat
freevxfs
fscache
fuse
gfs2
hfs
hfsplus
hostfs
hpfs
hugetlbfs
iomap
isofs
jbd2
jffs2
jfs
kernfs
lockd
minix
netfs
nfs
nfs_common
nfsd
nilfs2
nls
notify
ntfs
ntfs3
ocfs2
omfs
openpromfs
orangefs
overlayfs
proc
pstore
qnx4
qnx6
quota
ramfs
reiserfs
romfs
smb
squashfs
sysfs
sysv
tracefs
ubifs
udf
ufs
unicode
vboxsf
verity
xfs
zonefs
Kconfig
Kconfig.binfmt
Makefile
aio.c
anon_inodes.c
attr.c
bad_inode.c
binfmt_elf.c
binfmt_elf_fdpic.c
binfmt_elf_test.c
binfmt_flat.c
binfmt_misc.c
binfmt_script.c
buffer.c
char_dev.c
compat_binfmt_elf.c
coredump.c
d_path.c
dax.c
dcache.c
direct-io.c
drop_caches.c
eventfd.c
eventpoll.c
exec.c
fcntl.c
fhandle.c
file.c
file_table.c
filesystems.c
fs-writeback.c
fs_context.c
fs_parser.c
fs_pin.c
fs_struct.c
fs_types.c
fsopen.c
init.c
inode.c
internal.h
ioctl.c
kernel_read_file.c
libfs.c
locks.c
mbcache.c
mnt_idmapping.c
mount.h
mpage.c
namei.c
namespace.c
nsfs.c
open.c
pipe.c
pnode.c
pnode.h
posix_acl.c
proc_namespace.c
read_write.c
readdir.c
remap_range.c
select.c
seq_file.c
signalfd.c
splice.c
stack.c
stat.c
statfs.c
super.c
sync.c
sysctls.c
timerfd.c
userfaultfd.c
utimes.c
xattr.c
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
fs
/
f2fs
/
sysfs.c
Copy path
Blame
Blame
Latest commit
History
History
1540 lines (1344 loc) · 41.6 KB
Breadcrumbs
linux
/
fs
/
f2fs
/
sysfs.c
Top
File metadata and controls
Code
Blame
1540 lines (1344 loc) · 41.6 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * f2fs sysfs interface * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * Copyright (c) 2017 Chao Yu <chao@kernel.org> */ #include <linux/compiler.h> #include <linux/proc_fs.h> #include <linux/f2fs_fs.h> #include <linux/seq_file.h> #include <linux/unicode.h> #include <linux/ioprio.h> #include <linux/sysfs.h> #include "f2fs.h" #include "segment.h" #include "gc.h" #include "iostat.h" #include <trace/events/f2fs.h> static struct proc_dir_entry *f2fs_proc_root; /* Sysfs support for f2fs */ enum { GC_THREAD, /* struct f2fs_gc_thread */ SM_INFO, /* struct f2fs_sm_info */ DCC_INFO, /* struct discard_cmd_control */ NM_INFO, /* struct f2fs_nm_info */ F2FS_SBI, /* struct f2fs_sb_info */ #ifdef CONFIG_F2FS_STAT_FS STAT_INFO, /* struct f2fs_stat_info */ #endif #ifdef CONFIG_F2FS_FAULT_INJECTION FAULT_INFO_RATE, /* struct f2fs_fault_info */ FAULT_INFO_TYPE, /* struct f2fs_fault_info */ #endif RESERVED_BLOCKS, /* struct f2fs_sb_info */ CPRC_INFO, /* struct ckpt_req_control */ ATGC_INFO, /* struct atgc_management */ }; static const char *gc_mode_names[MAX_GC_MODE] = { "GC_NORMAL", "GC_IDLE_CB", "GC_IDLE_GREEDY", "GC_IDLE_AT", "GC_URGENT_HIGH", "GC_URGENT_LOW", "GC_URGENT_MID" }; struct f2fs_attr { struct attribute attr; ssize_t (*show)(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf); ssize_t (*store)(struct f2fs_attr *a, struct f2fs_sb_info *sbi, const char *buf, size_t len); int struct_type; int offset; int id; }; static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf); static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) { if (struct_type == GC_THREAD) return (unsigned char *)sbi->gc_thread; else if (struct_type == SM_INFO) return (unsigned char *)SM_I(sbi); else if (struct_type == DCC_INFO) return (unsigned char *)SM_I(sbi)->dcc_info; else if (struct_type == NM_INFO) return (unsigned char *)NM_I(sbi); else if (struct_type == F2FS_SBI || struct_type == RESERVED_BLOCKS) return (unsigned char *)sbi; #ifdef CONFIG_F2FS_FAULT_INJECTION else if (struct_type == FAULT_INFO_RATE || struct_type == FAULT_INFO_TYPE) return (unsigned char *)&F2FS_OPTION(sbi).fault_info; #endif #ifdef CONFIG_F2FS_STAT_FS else if (struct_type == STAT_INFO) return (unsigned char *)F2FS_STAT(sbi); #endif else if (struct_type == CPRC_INFO) return (unsigned char *)&sbi->cprc_info; else if (struct_type == ATGC_INFO) return (unsigned char *)&sbi->am; return NULL; } static ssize_t dirty_segments_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%llu\n", (unsigned long long)(dirty_segments(sbi))); } static ssize_t free_segments_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%llu\n", (unsigned long long)(free_segments(sbi))); } static ssize_t ovp_segments_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%llu\n", (unsigned long long)(overprovision_segments(sbi))); } static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%llu\n", (unsigned long long)(sbi->kbytes_written + ((f2fs_get_sectors_written(sbi) - sbi->sectors_written_start) >> 1))); } static ssize_t sb_status_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%lx\n", sbi->s_flag); } static ssize_t cp_status_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%x\n", le32_to_cpu(F2FS_CKPT(sbi)->ckpt_flags)); } static ssize_t pending_discard_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { if (!SM_I(sbi)->dcc_info) return -EINVAL; return sysfs_emit(buf, "%llu\n", (unsigned long long)atomic_read( &SM_I(sbi)->dcc_info->discard_cmd_cnt)); } static ssize_t gc_mode_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%s\n", gc_mode_names[sbi->gc_mode]); } static ssize_t features_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { int len = 0; if (f2fs_sb_has_encrypt(sbi)) len += scnprintf(buf, PAGE_SIZE - len, "%s", "encryption"); if (f2fs_sb_has_blkzoned(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "blkzoned"); if (f2fs_sb_has_extra_attr(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "extra_attr"); if (f2fs_sb_has_project_quota(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "projquota"); if (f2fs_sb_has_inode_chksum(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "inode_checksum"); if (f2fs_sb_has_flexible_inline_xattr(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "flexible_inline_xattr"); if (f2fs_sb_has_quota_ino(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "quota_ino"); if (f2fs_sb_has_inode_crtime(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "inode_crtime"); if (f2fs_sb_has_lost_found(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "lost_found"); if (f2fs_sb_has_verity(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "verity"); if (f2fs_sb_has_sb_chksum(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "sb_checksum"); if (f2fs_sb_has_casefold(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "casefold"); if (f2fs_sb_has_readonly(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "readonly"); if (f2fs_sb_has_compression(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "compression"); len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "pin_file"); len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } static ssize_t current_reserved_blocks_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%u\n", sbi->current_reserved_blocks); } static ssize_t unusable_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { block_t unusable; if (test_opt(sbi, DISABLE_CHECKPOINT)) unusable = sbi->unusable_block_count; else unusable = f2fs_get_unusable_blocks(sbi); return sysfs_emit(buf, "%llu\n", (unsigned long long)unusable); } static ssize_t encoding_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { #if IS_ENABLED(CONFIG_UNICODE) struct super_block *sb = sbi->sb; if (f2fs_sb_has_casefold(sbi)) return sysfs_emit(buf, "UTF-8 (%d.%d.%d)\n", (sb->s_encoding->version >> 16) & 0xff, (sb->s_encoding->version >> 8) & 0xff, sb->s_encoding->version & 0xff); #endif return sysfs_emit(buf, "(none)\n"); } static ssize_t mounted_time_sec_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%llu\n", SIT_I(sbi)->mounted_time); } #ifdef CONFIG_F2FS_STAT_FS static ssize_t moved_blocks_foreground_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { struct f2fs_stat_info *si = F2FS_STAT(sbi); return sysfs_emit(buf, "%llu\n", (unsigned long long)(si->tot_blks - (si->bg_data_blks + si->bg_node_blks))); } static ssize_t moved_blocks_background_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { struct f2fs_stat_info *si = F2FS_STAT(sbi); return sysfs_emit(buf, "%llu\n", (unsigned long long)(si->bg_data_blks + si->bg_node_blks)); } static ssize_t avg_vblocks_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { struct f2fs_stat_info *si = F2FS_STAT(sbi); si->dirty_count = dirty_segments(sbi); f2fs_update_sit_info(sbi); return sysfs_emit(buf, "%llu\n", (unsigned long long)(si->avg_vblocks)); } #endif static ssize_t main_blkaddr_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "%llu\n", (unsigned long long)MAIN_BLKADDR(sbi)); } static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { unsigned char *ptr = NULL; unsigned int *ui; ptr = __struct_ptr(sbi, a->struct_type); if (!ptr) return -EINVAL; if (!strcmp(a->attr.name, "extension_list")) { __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list; int cold_count = le32_to_cpu(sbi->raw_super->extension_count); int hot_count = sbi->raw_super->hot_ext_count; int len = 0, i; len += scnprintf(buf + len, PAGE_SIZE - len, "cold file extension:\n"); for (i = 0; i < cold_count; i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", extlist[i]); len += scnprintf(buf + len, PAGE_SIZE - len, "hot file extension:\n"); for (i = cold_count; i < cold_count + hot_count; i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", extlist[i]); return len; } if (!strcmp(a->attr.name, "ckpt_thread_ioprio")) { struct ckpt_req_control *cprc = &sbi->cprc_info; int class = IOPRIO_PRIO_CLASS(cprc->ckpt_thread_ioprio); int data = IOPRIO_PRIO_DATA(cprc->ckpt_thread_ioprio); if (class != IOPRIO_CLASS_RT && class != IOPRIO_CLASS_BE) return -EINVAL; return sysfs_emit(buf, "%s,%d\n", class == IOPRIO_CLASS_RT ? "rt" : "be", data); } #ifdef CONFIG_F2FS_FS_COMPRESSION if (!strcmp(a->attr.name, "compr_written_block")) return sysfs_emit(buf, "%llu\n", sbi->compr_written_block); if (!strcmp(a->attr.name, "compr_saved_block")) return sysfs_emit(buf, "%llu\n", sbi->compr_saved_block); if (!strcmp(a->attr.name, "compr_new_inode")) return sysfs_emit(buf, "%u\n", sbi->compr_new_inode); #endif if (!strcmp(a->attr.name, "gc_segment_mode")) return sysfs_emit(buf, "%u\n", sbi->gc_segment_mode); if (!strcmp(a->attr.name, "gc_reclaimed_segments")) { return sysfs_emit(buf, "%u\n", sbi->gc_reclaimed_segs[sbi->gc_segment_mode]); } if (!strcmp(a->attr.name, "current_atomic_write")) { s64 current_write = atomic64_read(&sbi->current_atomic_write); return sysfs_emit(buf, "%lld\n", current_write); } if (!strcmp(a->attr.name, "peak_atomic_write")) return sysfs_emit(buf, "%lld\n", sbi->peak_atomic_write); if (!strcmp(a->attr.name, "committed_atomic_block")) return sysfs_emit(buf, "%llu\n", sbi->committed_atomic_block); if (!strcmp(a->attr.name, "revoked_atomic_block")) return sysfs_emit(buf, "%llu\n", sbi->revoked_atomic_block); ui = (unsigned int *)(ptr + a->offset); return sysfs_emit(buf, "%u\n", *ui); } static ssize_t __sbi_store(struct f2fs_attr *a, struct f2fs_sb_info *sbi, const char *buf, size_t count) { unsigned char *ptr; unsigned long t; unsigned int *ui; ssize_t ret; ptr = __struct_ptr(sbi, a->struct_type); if (!ptr) return -EINVAL; if (!strcmp(a->attr.name, "extension_list")) { const char *name = strim((char *)buf); bool set = true, hot; if (!strncmp(name, "[h]", 3)) hot = true; else if (!strncmp(name, "[c]", 3)) hot = false; else return -EINVAL; name += 3; if (*name == '!') { name++; set = false; } if (!strlen(name) || strlen(name) >= F2FS_EXTENSION_LEN) return -EINVAL; f2fs_down_write(&sbi->sb_lock); ret = f2fs_update_extension_list(sbi, name, hot, set); if (ret) goto out; ret = f2fs_commit_super(sbi, false); if (ret) f2fs_update_extension_list(sbi, name, hot, !set); out: f2fs_up_write(&sbi->sb_lock); return ret ? ret : count; } if (!strcmp(a->attr.name, "ckpt_thread_ioprio")) { const char *name = strim((char *)buf); struct ckpt_req_control *cprc = &sbi->cprc_info; int class; long data; int ret; if (!strncmp(name, "rt,", 3)) class = IOPRIO_CLASS_RT; else if (!strncmp(name, "be,", 3)) class = IOPRIO_CLASS_BE; else return -EINVAL; name += 3; ret = kstrtol(name, 10, &data); if (ret) return ret; if (data >= IOPRIO_NR_LEVELS || data < 0) return -EINVAL; cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, data); if (test_opt(sbi, MERGE_CHECKPOINT)) { ret = set_task_ioprio(cprc->f2fs_issue_ckpt, cprc->ckpt_thread_ioprio); if (ret) return ret; } return count; } ui = (unsigned int *)(ptr + a->offset); ret = kstrtoul(skip_spaces(buf), 0, &t); if (ret < 0) return ret; #ifdef CONFIG_F2FS_FAULT_INJECTION if (a->struct_type == FAULT_INFO_TYPE && t >= BIT(FAULT_MAX)) return -EINVAL; if (a->struct_type == FAULT_INFO_RATE && t >= UINT_MAX) return -EINVAL; #endif if (a->struct_type == RESERVED_BLOCKS) { spin_lock(&sbi->stat_lock); if (t > (unsigned long)(sbi->user_block_count - F2FS_OPTION(sbi).root_reserved_blocks - sbi->blocks_per_seg * SM_I(sbi)->additional_reserved_segments)) { spin_unlock(&sbi->stat_lock); return -EINVAL; } *ui = t; sbi->current_reserved_blocks = min(sbi->reserved_blocks, sbi->user_block_count - valid_user_blocks(sbi)); spin_unlock(&sbi->stat_lock); return count; } if (!strcmp(a->attr.name, "discard_io_aware_gran")) { if (t > MAX_PLIST_NUM) return -EINVAL; if (!f2fs_block_unit_discard(sbi)) return -EINVAL; if (t == *ui) return count; *ui = t; return count; } if (!strcmp(a->attr.name, "discard_granularity")) { if (t == 0 || t > MAX_PLIST_NUM) return -EINVAL; if (!f2fs_block_unit_discard(sbi)) return -EINVAL; if (t == *ui) return count; *ui = t; return count; } if (!strcmp(a->attr.name, "max_ordered_discard")) { if (t == 0 || t > MAX_PLIST_NUM) return -EINVAL; if (!f2fs_block_unit_discard(sbi)) return -EINVAL; *ui = t; return count; } if (!strcmp(a->attr.name, "discard_urgent_util")) { if (t > 100) return -EINVAL; *ui = t; return count; } if (!strcmp(a->attr.name, "migration_granularity")) { if (t == 0 || t > sbi->segs_per_sec) return -EINVAL; } if (!strcmp(a->attr.name, "gc_urgent")) { if (t == 0) { sbi->gc_mode = GC_NORMAL; } else if (t == 1) { sbi->gc_mode = GC_URGENT_HIGH; if (sbi->gc_thread) { sbi->gc_thread->gc_wake = true; wake_up_interruptible_all( &sbi->gc_thread->gc_wait_queue_head); wake_up_discard_thread(sbi, true); } } else if (t == 2) { sbi->gc_mode = GC_URGENT_LOW; } else if (t == 3) { sbi->gc_mode = GC_URGENT_MID; if (sbi->gc_thread) { sbi->gc_thread->gc_wake = true; wake_up_interruptible_all( &sbi->gc_thread->gc_wait_queue_head); } } else { return -EINVAL; } return count; } if (!strcmp(a->attr.name, "gc_idle")) { if (t == GC_IDLE_CB) { sbi->gc_mode = GC_IDLE_CB; } else if (t == GC_IDLE_GREEDY) { sbi->gc_mode = GC_IDLE_GREEDY; } else if (t == GC_IDLE_AT) { if (!sbi->am.atgc_enabled) return -EINVAL; sbi->gc_mode = GC_IDLE_AT; } else { sbi->gc_mode = GC_NORMAL; } return count; } if (!strcmp(a->attr.name, "gc_remaining_trials")) { spin_lock(&sbi->gc_remaining_trials_lock); sbi->gc_remaining_trials = t; spin_unlock(&sbi->gc_remaining_trials_lock); return count; } #ifdef CONFIG_F2FS_IOSTAT if (!strcmp(a->attr.name, "iostat_enable")) { sbi->iostat_enable = !!t; if (!sbi->iostat_enable) f2fs_reset_iostat(sbi); return count; } if (!strcmp(a->attr.name, "iostat_period_ms")) { if (t < MIN_IOSTAT_PERIOD_MS || t > MAX_IOSTAT_PERIOD_MS) return -EINVAL; spin_lock_irq(&sbi->iostat_lock); sbi->iostat_period_ms = (unsigned int)t; spin_unlock_irq(&sbi->iostat_lock); return count; } #endif #ifdef CONFIG_F2FS_FS_COMPRESSION if (!strcmp(a->attr.name, "compr_written_block") || !strcmp(a->attr.name, "compr_saved_block")) { if (t != 0) return -EINVAL; sbi->compr_written_block = 0; sbi->compr_saved_block = 0; return count; } if (!strcmp(a->attr.name, "compr_new_inode")) { if (t != 0) return -EINVAL; sbi->compr_new_inode = 0; return count; } if (!strcmp(a->attr.name, "compress_percent")) { if (t == 0 || t > 100) return -EINVAL; *ui = t; return count; } if (!strcmp(a->attr.name, "compress_watermark")) { if (t == 0 || t > 100) return -EINVAL; *ui = t; return count; } #endif if (!strcmp(a->attr.name, "atgc_candidate_ratio")) { if (t > 100) return -EINVAL; sbi->am.candidate_ratio = t; return count; } if (!strcmp(a->attr.name, "atgc_age_weight")) { if (t > 100) return -EINVAL; sbi->am.age_weight = t; return count; } if (!strcmp(a->attr.name, "gc_segment_mode")) { if (t < MAX_GC_MODE) sbi->gc_segment_mode = t; else return -EINVAL; return count; } if (!strcmp(a->attr.name, "gc_reclaimed_segments")) { if (t != 0) return -EINVAL; sbi->gc_reclaimed_segs[sbi->gc_segment_mode] = 0; return count; } if (!strcmp(a->attr.name, "seq_file_ra_mul")) { if (t >= MIN_RA_MUL && t <= MAX_RA_MUL) sbi->seq_file_ra_mul = t; else return -EINVAL; return count; } if (!strcmp(a->attr.name, "max_fragment_chunk")) { if (t >= MIN_FRAGMENT_SIZE && t <= MAX_FRAGMENT_SIZE) sbi->max_fragment_chunk = t; else return -EINVAL; return count; } if (!strcmp(a->attr.name, "max_fragment_hole")) { if (t >= MIN_FRAGMENT_SIZE && t <= MAX_FRAGMENT_SIZE) sbi->max_fragment_hole = t; else return -EINVAL; return count; } if (!strcmp(a->attr.name, "peak_atomic_write")) { if (t != 0) return -EINVAL; sbi->peak_atomic_write = 0; return count; } if (!strcmp(a->attr.name, "committed_atomic_block")) { if (t != 0) return -EINVAL; sbi->committed_atomic_block = 0; return count; } if (!strcmp(a->attr.name, "revoked_atomic_block")) { if (t != 0) return -EINVAL; sbi->revoked_atomic_block = 0; return count; } if (!strcmp(a->attr.name, "readdir_ra")) { sbi->readdir_ra = !!t; return count; } if (!strcmp(a->attr.name, "hot_data_age_threshold")) { if (t == 0 || t >= sbi->warm_data_age_threshold) return -EINVAL; if (t == *ui) return count; *ui = (unsigned int)t; return count; } if (!strcmp(a->attr.name, "warm_data_age_threshold")) { if (t <= sbi->hot_data_age_threshold) return -EINVAL; if (t == *ui) return count; *ui = (unsigned int)t; return count; } if (!strcmp(a->attr.name, "last_age_weight")) { if (t > 100) return -EINVAL; if (t == *ui) return count; *ui = (unsigned int)t; return count; } if (!strcmp(a->attr.name, "ipu_policy")) { if (t >= BIT(F2FS_IPU_MAX)) return -EINVAL; if (t && f2fs_lfs_mode(sbi)) return -EINVAL; SM_I(sbi)->ipu_policy = (unsigned int)t; return count; } *ui = (unsigned int)t; return count; } static ssize_t f2fs_sbi_store(struct f2fs_attr *a, struct f2fs_sb_info *sbi, const char *buf, size_t count) { ssize_t ret; bool gc_entry = (!strcmp(a->attr.name, "gc_urgent") || a->struct_type == GC_THREAD); if (gc_entry) { if (!down_read_trylock(&sbi->sb->s_umount)) return -EAGAIN; } ret = __sbi_store(a, sbi, buf, count); if (gc_entry) up_read(&sbi->sb->s_umount); return ret; } static ssize_t f2fs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_kobj); struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); return a->show ? a->show(a, sbi, buf) : 0; } static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_kobj); struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); return a->store ? a->store(a, sbi, buf, len) : 0; } static void f2fs_sb_release(struct kobject *kobj) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_kobj); complete(&sbi->s_kobj_unregister); } /* * Note that there are three feature list entries: * 1) /sys/fs/f2fs/features * : shows runtime features supported by in-kernel f2fs along with Kconfig. * - ref. F2FS_FEATURE_RO_ATTR() * * 2) /sys/fs/f2fs/$s_id/features <deprecated> * : shows on-disk features enabled by mkfs.f2fs, used for old kernels. This * won't add new feature anymore, and thus, users should check entries in 3) * instead of this 2). * * 3) /sys/fs/f2fs/$s_id/feature_list * : shows on-disk features enabled by mkfs.f2fs per instance, which follows * sysfs entry rule where each entry should expose single value. * This list covers old feature list provided by 2) and beyond. Therefore, * please add new on-disk feature in this list only. * - ref. F2FS_SB_FEATURE_RO_ATTR() */ static ssize_t f2fs_feature_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { return sysfs_emit(buf, "supported\n"); } #define F2FS_FEATURE_RO_ATTR(_name) \ static struct f2fs_attr f2fs_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = 0444 }, \ .show = f2fs_feature_show, \ } static ssize_t f2fs_sb_feature_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { if (F2FS_HAS_FEATURE(sbi, a->id)) return sysfs_emit(buf, "supported\n"); return sysfs_emit(buf, "unsupported\n"); } #define F2FS_SB_FEATURE_RO_ATTR(_name, _feat) \ static struct f2fs_attr f2fs_attr_sb_##_name = { \ .attr = {.name = __stringify(_name), .mode = 0444 }, \ .show = f2fs_sb_feature_show, \ .id = F2FS_FEATURE_##_feat, \ } #define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ static struct f2fs_attr f2fs_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ .struct_type = _struct_type, \ .offset = _offset \ } #define F2FS_RO_ATTR(struct_type, struct_name, name, elname) \ F2FS_ATTR_OFFSET(struct_type, name, 0444, \ f2fs_sbi_show, NULL, \ offsetof(struct struct_name, elname)) #define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \ F2FS_ATTR_OFFSET(struct_type, name, 0644, \ f2fs_sbi_show, f2fs_sbi_store, \ offsetof(struct struct_name, elname)) #define F2FS_GENERAL_RO_ATTR(name) \ static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) #ifdef CONFIG_F2FS_STAT_FS #define STAT_INFO_RO_ATTR(name, elname) \ F2FS_RO_ATTR(STAT_INFO, f2fs_stat_info, name, elname) #endif #define GC_THREAD_RW_ATTR(name, elname) \ F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, name, elname) #define SM_INFO_RW_ATTR(name, elname) \ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, name, elname) #define SM_INFO_GENERAL_RW_ATTR(elname) \ SM_INFO_RW_ATTR(elname, elname) #define DCC_INFO_RW_ATTR(name, elname) \ F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, name, elname) #define DCC_INFO_GENERAL_RW_ATTR(elname) \ DCC_INFO_RW_ATTR(elname, elname) #define NM_INFO_RW_ATTR(name, elname) \ F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, name, elname) #define NM_INFO_GENERAL_RW_ATTR(elname) \ NM_INFO_RW_ATTR(elname, elname) #define F2FS_SBI_RW_ATTR(name, elname) \ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, name, elname) #define F2FS_SBI_GENERAL_RW_ATTR(elname) \ F2FS_SBI_RW_ATTR(elname, elname) #define F2FS_SBI_GENERAL_RO_ATTR(elname) \ F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, elname, elname) #ifdef CONFIG_F2FS_FAULT_INJECTION #define FAULT_INFO_GENERAL_RW_ATTR(type, elname) \ F2FS_RW_ATTR(type, f2fs_fault_info, elname, elname) #endif #define RESERVED_BLOCKS_GENERAL_RW_ATTR(elname) \ F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, elname, elname) #define CPRC_INFO_GENERAL_RW_ATTR(elname) \ F2FS_RW_ATTR(CPRC_INFO, ckpt_req_control, elname, elname) #define ATGC_INFO_RW_ATTR(name, elname) \ F2FS_RW_ATTR(ATGC_INFO, atgc_management, name, elname) /* GC_THREAD ATTR */ GC_THREAD_RW_ATTR(gc_urgent_sleep_time, urgent_sleep_time); GC_THREAD_RW_ATTR(gc_min_sleep_time, min_sleep_time); GC_THREAD_RW_ATTR(gc_max_sleep_time, max_sleep_time); GC_THREAD_RW_ATTR(gc_no_gc_sleep_time, no_gc_sleep_time); /* SM_INFO ATTR */ SM_INFO_RW_ATTR(reclaim_segments, rec_prefree_segments); SM_INFO_GENERAL_RW_ATTR(ipu_policy); SM_INFO_GENERAL_RW_ATTR(min_ipu_util); SM_INFO_GENERAL_RW_ATTR(min_fsync_blocks); SM_INFO_GENERAL_RW_ATTR(min_seq_blocks); SM_INFO_GENERAL_RW_ATTR(min_hot_blocks); SM_INFO_GENERAL_RW_ATTR(min_ssr_sections); /* DCC_INFO ATTR */ DCC_INFO_RW_ATTR(max_small_discards, max_discards); DCC_INFO_GENERAL_RW_ATTR(max_discard_request); DCC_INFO_GENERAL_RW_ATTR(min_discard_issue_time); DCC_INFO_GENERAL_RW_ATTR(mid_discard_issue_time); DCC_INFO_GENERAL_RW_ATTR(max_discard_issue_time); DCC_INFO_GENERAL_RW_ATTR(discard_io_aware_gran); DCC_INFO_GENERAL_RW_ATTR(discard_urgent_util); DCC_INFO_GENERAL_RW_ATTR(discard_granularity); DCC_INFO_GENERAL_RW_ATTR(max_ordered_discard); /* NM_INFO ATTR */ NM_INFO_RW_ATTR(max_roll_forward_node_blocks, max_rf_node_blocks); NM_INFO_GENERAL_RW_ATTR(ram_thresh); NM_INFO_GENERAL_RW_ATTR(ra_nid_pages); NM_INFO_GENERAL_RW_ATTR(dirty_nats_ratio); /* F2FS_SBI ATTR */ F2FS_RW_ATTR(F2FS_SBI, f2fs_super_block, extension_list, extension_list); F2FS_SBI_RW_ATTR(gc_idle, gc_mode); F2FS_SBI_RW_ATTR(gc_urgent, gc_mode); F2FS_SBI_RW_ATTR(cp_interval, interval_time[CP_TIME]); F2FS_SBI_RW_ATTR(idle_interval, interval_time[REQ_TIME]); F2FS_SBI_RW_ATTR(discard_idle_interval, interval_time[DISCARD_TIME]); F2FS_SBI_RW_ATTR(gc_idle_interval, interval_time[GC_TIME]); F2FS_SBI_RW_ATTR(umount_discard_timeout, interval_time[UMOUNT_DISCARD_TIMEOUT]); F2FS_SBI_RW_ATTR(gc_pin_file_thresh, gc_pin_file_threshold); F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs); F2FS_SBI_GENERAL_RW_ATTR(max_victim_search); F2FS_SBI_GENERAL_RW_ATTR(migration_granularity); F2FS_SBI_GENERAL_RW_ATTR(dir_level); #ifdef CONFIG_F2FS_IOSTAT F2FS_SBI_GENERAL_RW_ATTR(iostat_enable); F2FS_SBI_GENERAL_RW_ATTR(iostat_period_ms); #endif F2FS_SBI_GENERAL_RW_ATTR(readdir_ra); F2FS_SBI_GENERAL_RW_ATTR(max_io_bytes); F2FS_SBI_GENERAL_RW_ATTR(data_io_flag); F2FS_SBI_GENERAL_RW_ATTR(node_io_flag); F2FS_SBI_GENERAL_RW_ATTR(gc_remaining_trials); F2FS_SBI_GENERAL_RW_ATTR(seq_file_ra_mul); F2FS_SBI_GENERAL_RW_ATTR(gc_segment_mode); F2FS_SBI_GENERAL_RW_ATTR(max_fragment_chunk); F2FS_SBI_GENERAL_RW_ATTR(max_fragment_hole); #ifdef CONFIG_F2FS_FS_COMPRESSION F2FS_SBI_GENERAL_RW_ATTR(compr_written_block); F2FS_SBI_GENERAL_RW_ATTR(compr_saved_block); F2FS_SBI_GENERAL_RW_ATTR(compr_new_inode); F2FS_SBI_GENERAL_RW_ATTR(compress_percent); F2FS_SBI_GENERAL_RW_ATTR(compress_watermark); #endif /* atomic write */ F2FS_SBI_GENERAL_RO_ATTR(current_atomic_write); F2FS_SBI_GENERAL_RW_ATTR(peak_atomic_write); F2FS_SBI_GENERAL_RW_ATTR(committed_atomic_block); F2FS_SBI_GENERAL_RW_ATTR(revoked_atomic_block); /* block age extent cache */ F2FS_SBI_GENERAL_RW_ATTR(hot_data_age_threshold); F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold); F2FS_SBI_GENERAL_RW_ATTR(last_age_weight); #ifdef CONFIG_BLK_DEV_ZONED F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec); #endif /* STAT_INFO ATTR */ #ifdef CONFIG_F2FS_STAT_FS STAT_INFO_RO_ATTR(cp_foreground_calls, cp_count); STAT_INFO_RO_ATTR(cp_background_calls, bg_cp_count); STAT_INFO_RO_ATTR(gc_foreground_calls, gc_call_count[FOREGROUND]); STAT_INFO_RO_ATTR(gc_background_calls, gc_call_count[BACKGROUND]); #endif /* FAULT_INFO ATTR */ #ifdef CONFIG_F2FS_FAULT_INJECTION FAULT_INFO_GENERAL_RW_ATTR(FAULT_INFO_RATE, inject_rate); FAULT_INFO_GENERAL_RW_ATTR(FAULT_INFO_TYPE, inject_type); #endif /* RESERVED_BLOCKS ATTR */ RESERVED_BLOCKS_GENERAL_RW_ATTR(reserved_blocks); /* CPRC_INFO ATTR */ CPRC_INFO_GENERAL_RW_ATTR(ckpt_thread_ioprio); /* ATGC_INFO ATTR */ ATGC_INFO_RW_ATTR(atgc_candidate_ratio, candidate_ratio); ATGC_INFO_RW_ATTR(atgc_candidate_count, max_candidate_count); ATGC_INFO_RW_ATTR(atgc_age_weight, age_weight); ATGC_INFO_RW_ATTR(atgc_age_threshold, age_threshold); F2FS_GENERAL_RO_ATTR(dirty_segments); F2FS_GENERAL_RO_ATTR(free_segments); F2FS_GENERAL_RO_ATTR(ovp_segments); F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(features); F2FS_GENERAL_RO_ATTR(current_reserved_blocks); F2FS_GENERAL_RO_ATTR(unusable); F2FS_GENERAL_RO_ATTR(encoding); F2FS_GENERAL_RO_ATTR(mounted_time_sec); F2FS_GENERAL_RO_ATTR(main_blkaddr); F2FS_GENERAL_RO_ATTR(pending_discard); F2FS_GENERAL_RO_ATTR(gc_mode); #ifdef CONFIG_F2FS_STAT_FS F2FS_GENERAL_RO_ATTR(moved_blocks_background); F2FS_GENERAL_RO_ATTR(moved_blocks_foreground); F2FS_GENERAL_RO_ATTR(avg_vblocks); #endif #ifdef CONFIG_FS_ENCRYPTION F2FS_FEATURE_RO_ATTR(encryption); F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2); #if IS_ENABLED(CONFIG_UNICODE) F2FS_FEATURE_RO_ATTR(encrypted_casefold); #endif #endif /* CONFIG_FS_ENCRYPTION */ #ifdef CONFIG_BLK_DEV_ZONED F2FS_FEATURE_RO_ATTR(block_zoned); #endif F2FS_FEATURE_RO_ATTR(atomic_write); F2FS_FEATURE_RO_ATTR(extra_attr); F2FS_FEATURE_RO_ATTR(project_quota); F2FS_FEATURE_RO_ATTR(inode_checksum); F2FS_FEATURE_RO_ATTR(flexible_inline_xattr); F2FS_FEATURE_RO_ATTR(quota_ino); F2FS_FEATURE_RO_ATTR(inode_crtime); F2FS_FEATURE_RO_ATTR(lost_found); #ifdef CONFIG_FS_VERITY F2FS_FEATURE_RO_ATTR(verity); #endif F2FS_FEATURE_RO_ATTR(sb_checksum); #if IS_ENABLED(CONFIG_UNICODE) F2FS_FEATURE_RO_ATTR(casefold); #endif F2FS_FEATURE_RO_ATTR(readonly); #ifdef CONFIG_F2FS_FS_COMPRESSION F2FS_FEATURE_RO_ATTR(compression); #endif F2FS_FEATURE_RO_ATTR(pin_file); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { ATTR_LIST(gc_urgent_sleep_time), ATTR_LIST(gc_min_sleep_time), ATTR_LIST(gc_max_sleep_time), ATTR_LIST(gc_no_gc_sleep_time), ATTR_LIST(gc_idle), ATTR_LIST(gc_urgent), ATTR_LIST(reclaim_segments), ATTR_LIST(main_blkaddr), ATTR_LIST(max_small_discards), ATTR_LIST(max_discard_request), ATTR_LIST(min_discard_issue_time), ATTR_LIST(mid_discard_issue_time), ATTR_LIST(max_discard_issue_time), ATTR_LIST(discard_io_aware_gran), ATTR_LIST(discard_urgent_util), ATTR_LIST(discard_granularity), ATTR_LIST(max_ordered_discard), ATTR_LIST(pending_discard), ATTR_LIST(gc_mode), ATTR_LIST(ipu_policy), ATTR_LIST(min_ipu_util), ATTR_LIST(min_fsync_blocks), ATTR_LIST(min_seq_blocks), ATTR_LIST(min_hot_blocks), ATTR_LIST(min_ssr_sections), ATTR_LIST(max_victim_search), ATTR_LIST(migration_granularity), ATTR_LIST(dir_level), ATTR_LIST(ram_thresh), ATTR_LIST(ra_nid_pages), ATTR_LIST(dirty_nats_ratio), ATTR_LIST(max_roll_forward_node_blocks), ATTR_LIST(cp_interval), ATTR_LIST(idle_interval), ATTR_LIST(discard_idle_interval), ATTR_LIST(gc_idle_interval), ATTR_LIST(umount_discard_timeout), #ifdef CONFIG_F2FS_IOSTAT ATTR_LIST(iostat_enable), ATTR_LIST(iostat_period_ms), #endif ATTR_LIST(readdir_ra), ATTR_LIST(max_io_bytes), ATTR_LIST(gc_pin_file_thresh), ATTR_LIST(extension_list), #ifdef CONFIG_F2FS_FAULT_INJECTION ATTR_LIST(inject_rate), ATTR_LIST(inject_type), #endif ATTR_LIST(data_io_flag), ATTR_LIST(node_io_flag), ATTR_LIST(gc_remaining_trials), ATTR_LIST(ckpt_thread_ioprio), ATTR_LIST(dirty_segments), ATTR_LIST(free_segments), ATTR_LIST(ovp_segments), ATTR_LIST(unusable), ATTR_LIST(lifetime_write_kbytes), ATTR_LIST(features), ATTR_LIST(reserved_blocks), ATTR_LIST(current_reserved_blocks), ATTR_LIST(encoding), ATTR_LIST(mounted_time_sec), #ifdef CONFIG_F2FS_STAT_FS ATTR_LIST(cp_foreground_calls), ATTR_LIST(cp_background_calls), ATTR_LIST(gc_foreground_calls), ATTR_LIST(gc_background_calls), ATTR_LIST(moved_blocks_foreground), ATTR_LIST(moved_blocks_background), ATTR_LIST(avg_vblocks), #endif #ifdef CONFIG_BLK_DEV_ZONED ATTR_LIST(unusable_blocks_per_sec), #endif #ifdef CONFIG_F2FS_FS_COMPRESSION ATTR_LIST(compr_written_block), ATTR_LIST(compr_saved_block), ATTR_LIST(compr_new_inode), ATTR_LIST(compress_percent), ATTR_LIST(compress_watermark), #endif /* For ATGC */ ATTR_LIST(atgc_candidate_ratio), ATTR_LIST(atgc_candidate_count), ATTR_LIST(atgc_age_weight), ATTR_LIST(atgc_age_threshold), ATTR_LIST(seq_file_ra_mul), ATTR_LIST(gc_segment_mode), ATTR_LIST(gc_reclaimed_segments), ATTR_LIST(max_fragment_chunk), ATTR_LIST(max_fragment_hole), ATTR_LIST(current_atomic_write), ATTR_LIST(peak_atomic_write), ATTR_LIST(committed_atomic_block), ATTR_LIST(revoked_atomic_block), ATTR_LIST(hot_data_age_threshold), ATTR_LIST(warm_data_age_threshold), ATTR_LIST(last_age_weight), NULL, }; ATTRIBUTE_GROUPS(f2fs); static struct attribute *f2fs_feat_attrs[] = { #ifdef CONFIG_FS_ENCRYPTION ATTR_LIST(encryption), ATTR_LIST(test_dummy_encryption_v2), #if IS_ENABLED(CONFIG_UNICODE) ATTR_LIST(encrypted_casefold), #endif #endif /* CONFIG_FS_ENCRYPTION */ #ifdef CONFIG_BLK_DEV_ZONED ATTR_LIST(block_zoned), #endif ATTR_LIST(atomic_write), ATTR_LIST(extra_attr), ATTR_LIST(project_quota), ATTR_LIST(inode_checksum), ATTR_LIST(flexible_inline_xattr), ATTR_LIST(quota_ino), ATTR_LIST(inode_crtime), ATTR_LIST(lost_found), #ifdef CONFIG_FS_VERITY ATTR_LIST(verity), #endif ATTR_LIST(sb_checksum), #if IS_ENABLED(CONFIG_UNICODE) ATTR_LIST(casefold), #endif ATTR_LIST(readonly), #ifdef CONFIG_F2FS_FS_COMPRESSION ATTR_LIST(compression), #endif ATTR_LIST(pin_file), NULL, }; ATTRIBUTE_GROUPS(f2fs_feat); F2FS_GENERAL_RO_ATTR(sb_status); F2FS_GENERAL_RO_ATTR(cp_status); static struct attribute *f2fs_stat_attrs[] = { ATTR_LIST(sb_status), ATTR_LIST(cp_status), NULL, }; ATTRIBUTE_GROUPS(f2fs_stat); F2FS_SB_FEATURE_RO_ATTR(encryption, ENCRYPT); F2FS_SB_FEATURE_RO_ATTR(block_zoned, BLKZONED); F2FS_SB_FEATURE_RO_ATTR(extra_attr, EXTRA_ATTR); F2FS_SB_FEATURE_RO_ATTR(project_quota, PRJQUOTA); F2FS_SB_FEATURE_RO_ATTR(inode_checksum, INODE_CHKSUM); F2FS_SB_FEATURE_RO_ATTR(flexible_inline_xattr, FLEXIBLE_INLINE_XATTR); F2FS_SB_FEATURE_RO_ATTR(quota_ino, QUOTA_INO); F2FS_SB_FEATURE_RO_ATTR(inode_crtime, INODE_CRTIME); F2FS_SB_FEATURE_RO_ATTR(lost_found, LOST_FOUND); F2FS_SB_FEATURE_RO_ATTR(verity, VERITY); F2FS_SB_FEATURE_RO_ATTR(sb_checksum, SB_CHKSUM); F2FS_SB_FEATURE_RO_ATTR(casefold, CASEFOLD); F2FS_SB_FEATURE_RO_ATTR(compression, COMPRESSION); F2FS_SB_FEATURE_RO_ATTR(readonly, RO); static struct attribute *f2fs_sb_feat_attrs[] = { ATTR_LIST(sb_encryption), ATTR_LIST(sb_block_zoned), ATTR_LIST(sb_extra_attr), ATTR_LIST(sb_project_quota), ATTR_LIST(sb_inode_checksum), ATTR_LIST(sb_flexible_inline_xattr), ATTR_LIST(sb_quota_ino), ATTR_LIST(sb_inode_crtime), ATTR_LIST(sb_lost_found), ATTR_LIST(sb_verity), ATTR_LIST(sb_sb_checksum), ATTR_LIST(sb_casefold), ATTR_LIST(sb_compression), ATTR_LIST(sb_readonly), NULL, }; ATTRIBUTE_GROUPS(f2fs_sb_feat); static const struct sysfs_ops f2fs_attr_ops = { .show = f2fs_attr_show, .store = f2fs_attr_store, }; static const struct kobj_type f2fs_sb_ktype = { .default_groups = f2fs_groups, .sysfs_ops = &f2fs_attr_ops, .release = f2fs_sb_release, }; static const struct kobj_type f2fs_ktype = { .sysfs_ops = &f2fs_attr_ops, }; static struct kset f2fs_kset = { .kobj = {.ktype = &f2fs_ktype}, }; static const struct kobj_type f2fs_feat_ktype = { .default_groups = f2fs_feat_groups, .sysfs_ops = &f2fs_attr_ops, }; static struct kobject f2fs_feat = { .kset = &f2fs_kset, }; static ssize_t f2fs_stat_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_stat_kobj); struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); return a->show ? a->show(a, sbi, buf) : 0; } static ssize_t f2fs_stat_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_stat_kobj); struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); return a->store ? a->store(a, sbi, buf, len) : 0; } static void f2fs_stat_kobj_release(struct kobject *kobj) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_stat_kobj); complete(&sbi->s_stat_kobj_unregister); } static const struct sysfs_ops f2fs_stat_attr_ops = { .show = f2fs_stat_attr_show, .store = f2fs_stat_attr_store, }; static const struct kobj_type f2fs_stat_ktype = { .default_groups = f2fs_stat_groups, .sysfs_ops = &f2fs_stat_attr_ops, .release = f2fs_stat_kobj_release, }; static ssize_t f2fs_sb_feat_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_feature_list_kobj); struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); return a->show ? a->show(a, sbi, buf) : 0; } static void f2fs_feature_list_kobj_release(struct kobject *kobj) { struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, s_feature_list_kobj); complete(&sbi->s_feature_list_kobj_unregister); } static const struct sysfs_ops f2fs_feature_list_attr_ops = { .show = f2fs_sb_feat_attr_show, }; static const struct kobj_type f2fs_feature_list_ktype = { .default_groups = f2fs_sb_feat_groups, .sysfs_ops = &f2fs_feature_list_attr_ops, .release = f2fs_feature_list_kobj_release, }; static int __maybe_unused segment_info_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; struct f2fs_sb_info *sbi = F2FS_SB(sb); unsigned int total_segs = le32_to_cpu(sbi->raw_super->segment_count_main); int i; seq_puts(seq, "format: segment_type|valid_blocks\n" "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); for (i = 0; i < total_segs; i++) { struct seg_entry *se = get_seg_entry(sbi, i); if ((i % 10) == 0) seq_printf(seq, "%-10d", i); seq_printf(seq, "%d|%-3u", se->type, se->valid_blocks); if ((i % 10) == 9 || i == (total_segs - 1)) seq_putc(seq, '\n'); else seq_putc(seq, ' '); } return 0; } static int __maybe_unused segment_bits_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; struct f2fs_sb_info *sbi = F2FS_SB(sb); unsigned int total_segs = le32_to_cpu(sbi->raw_super->segment_count_main); int i, j; seq_puts(seq, "format: segment_type|valid_blocks|bitmaps\n" "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); for (i = 0; i < total_segs; i++) { struct seg_entry *se = get_seg_entry(sbi, i); seq_printf(seq, "%-10d", i); seq_printf(seq, "%d|%-3u|", se->type, se->valid_blocks); for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) seq_printf(seq, " %.2x", se->cur_valid_map[j]); seq_putc(seq, '\n'); } return 0; } static int __maybe_unused victim_bits_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; struct f2fs_sb_info *sbi = F2FS_SB(sb); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); int i; seq_puts(seq, "format: victim_secmap bitmaps\n"); for (i = 0; i < MAIN_SECS(sbi); i++) { if ((i % 10) == 0) seq_printf(seq, "%-10d", i); seq_printf(seq, "%d", test_bit(i, dirty_i->victim_secmap) ? 1 : 0); if ((i % 10) == 9 || i == (MAIN_SECS(sbi) - 1)) seq_putc(seq, '\n'); else seq_putc(seq, ' '); } return 0; } static int __maybe_unused discard_plist_seq_show(struct seq_file *seq, void *offset) { struct super_block *sb = seq->private; struct f2fs_sb_info *sbi = F2FS_SB(sb); struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; int i, count; seq_puts(seq, "Discard pend list(Show diacrd_cmd count on each entry, .:not exist):\n"); if (!f2fs_realtime_discard_enable(sbi)) return 0; if (dcc) { mutex_lock(&dcc->cmd_lock); for (i = 0; i < MAX_PLIST_NUM; i++) { struct list_head *pend_list; struct discard_cmd *dc, *tmp; if (i % 8 == 0) seq_printf(seq, " %-3d", i); count = 0; pend_list = &dcc->pend_list[i]; list_for_each_entry_safe(dc, tmp, pend_list, list) count++; if (count) seq_printf(seq, " %7d", count); else seq_puts(seq, " ."); if (i % 8 == 7) seq_putc(seq, '\n'); } seq_putc(seq, '\n'); mutex_unlock(&dcc->cmd_lock); } return 0; } int __init f2fs_init_sysfs(void) { int ret; kobject_set_name(&f2fs_kset.kobj, "f2fs"); f2fs_kset.kobj.parent = fs_kobj; ret = kset_register(&f2fs_kset); if (ret) return ret; ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype, NULL, "features"); if (ret) goto put_kobject; f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); if (!f2fs_proc_root) { ret = -ENOMEM; goto put_kobject; } return 0; put_kobject: kobject_put(&f2fs_feat); kset_unregister(&f2fs_kset); return ret; } void f2fs_exit_sysfs(void) { kobject_put(&f2fs_feat); kset_unregister(&f2fs_kset); remove_proc_entry("fs/f2fs", NULL); f2fs_proc_root = NULL; } int f2fs_register_sysfs(struct f2fs_sb_info *sbi) { struct super_block *sb = sbi->sb; int err; sbi->s_kobj.kset = &f2fs_kset; init_completion(&sbi->s_kobj_unregister); err = kobject_init_and_add(&sbi->s_kobj, &f2fs_sb_ktype, NULL, "%s", sb->s_id); if (err) goto put_sb_kobj; sbi->s_stat_kobj.kset = &f2fs_kset; init_completion(&sbi->s_stat_kobj_unregister); err = kobject_init_and_add(&sbi->s_stat_kobj, &f2fs_stat_ktype, &sbi->s_kobj, "stat"); if (err) goto put_stat_kobj; sbi->s_feature_list_kobj.kset = &f2fs_kset; init_completion(&sbi->s_feature_list_kobj_unregister); err = kobject_init_and_add(&sbi->s_feature_list_kobj, &f2fs_feature_list_ktype, &sbi->s_kobj, "feature_list"); if (err) goto put_feature_list_kobj; sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); if (!sbi->s_proc) { err = -ENOMEM; goto put_feature_list_kobj; } proc_create_single_data("segment_info", 0444, sbi->s_proc, segment_info_seq_show, sb); proc_create_single_data("segment_bits", 0444, sbi->s_proc, segment_bits_seq_show, sb); #ifdef CONFIG_F2FS_IOSTAT proc_create_single_data("iostat_info", 0444, sbi->s_proc, iostat_info_seq_show, sb); #endif proc_create_single_data("victim_bits", 0444, sbi->s_proc, victim_bits_seq_show, sb); proc_create_single_data("discard_plist_info", 0444, sbi->s_proc, discard_plist_seq_show, sb); return 0; put_feature_list_kobj: kobject_put(&sbi->s_feature_list_kobj); wait_for_completion(&sbi->s_feature_list_kobj_unregister); put_stat_kobj: kobject_put(&sbi->s_stat_kobj); wait_for_completion(&sbi->s_stat_kobj_unregister); put_sb_kobj: kobject_put(&sbi->s_kobj); wait_for_completion(&sbi->s_kobj_unregister); return err; } void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) { remove_proc_subtree(sbi->sb->s_id, f2fs_proc_root); kobject_put(&sbi->s_stat_kobj); wait_for_completion(&sbi->s_stat_kobj_unregister); kobject_put(&sbi->s_feature_list_kobj); wait_for_completion(&sbi->s_feature_list_kobj_unregister); kobject_put(&sbi->s_kobj); wait_for_completion(&sbi->s_kobj_unregister); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
You can’t perform that action at this time.