2118 lines · 57.6 KB · C
RawDownload
// 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 */	FAULT_INFO_TIMEOUT,	/* 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 size;	int id;}; struct f2fs_base_attr {	struct attribute attr;	ssize_t (*show)(struct f2fs_base_attr *a, char *buf);	ssize_t (*store)(struct f2fs_base_attr *a, const char *buf, size_t len);}; 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 ||		struct_type == FAULT_INFO_TIMEOUT)		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 issued_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->issued_discard));} static ssize_t queued_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->queued_discard));} static ssize_t undiscard_blks_show(struct f2fs_attr *a,		struct f2fs_sb_info *sbi, char *buf){	if (!SM_I(sbi)->dcc_info)		return -EINVAL;	return sysfs_emit(buf, "%u\n",				SM_I(sbi)->dcc_info->undiscard_blks);} static ssize_t atgc_enabled_show(struct f2fs_attr *a,		struct f2fs_sb_info *sbi, char *buf){	return sysfs_emit(buf, "%d\n", sbi->am.atgc_enabled ? 1 : 0);} 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 += sysfs_emit_at(buf, len, "%s",						"encryption");	if (f2fs_sb_has_blkzoned(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "blkzoned");	if (f2fs_sb_has_extra_attr(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "extra_attr");	if (f2fs_sb_has_project_quota(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "projquota");	if (f2fs_sb_has_inode_chksum(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "inode_checksum");	if (f2fs_sb_has_flexible_inline_xattr(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "flexible_inline_xattr");	if (f2fs_sb_has_quota_ino(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "quota_ino");	if (f2fs_sb_has_inode_crtime(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "inode_crtime");	if (f2fs_sb_has_lost_found(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "lost_found");	if (f2fs_sb_has_verity(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "verity");	if (f2fs_sb_has_sb_chksum(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "sb_checksum");	if (f2fs_sb_has_casefold(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "casefold");	if (f2fs_sb_has_readonly(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "readonly");	if (f2fs_sb_has_compression(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "compression");	if (f2fs_sb_has_packed_ssa(sbi))		len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "packed_ssa");	len += sysfs_emit_at(buf, len, "%s%s",				len ? ", " : "", "pin_file");	len += sysfs_emit_at(buf, 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 encoding_flags_show(struct f2fs_attr *a,		struct f2fs_sb_info *sbi, char *buf){	return sysfs_emit(buf, "%x\n",		le16_to_cpu(F2FS_RAW_SUPER(sbi)->s_encoding_flags));} static ssize_t effective_lookup_mode_show(struct f2fs_attr *a,		struct f2fs_sb_info *sbi, char *buf){	switch (F2FS_OPTION(sbi).lookup_mode) {	case LOOKUP_PERF:		return sysfs_emit(buf, "perf\n");	case LOOKUP_COMPAT:		return sysfs_emit(buf, "compat\n");	case LOOKUP_AUTO:		if (sb_no_casefold_compat_fallback(sbi->sb))			return sysfs_emit(buf, "auto:perf\n");		return sysfs_emit(buf, "auto:compat\n");	}	return 0;} 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_FSstatic 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));} static ssize_t defrag_blocks_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->defrag_blks));}#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 __sbi_show_value(struct f2fs_attr *a,		struct f2fs_sb_info *sbi, char *buf,		unsigned char *value){	switch (a->size) {	case 1:		return sysfs_emit(buf, "%u\n", *(u8 *)value);	case 2:		return sysfs_emit(buf, "%u\n", *(u16 *)value);	case 4:		return sysfs_emit(buf, "%u\n", *(u32 *)value);	case 8:		return sysfs_emit(buf, "%llu\n", *(u64 *)value);	default:		f2fs_bug_on(sbi, 1);		return sysfs_emit(buf,				"show sysfs node value with wrong type\n");	}} static ssize_t f2fs_sbi_show(struct f2fs_attr *a,			struct f2fs_sb_info *sbi, char *buf){	unsigned char *ptr = NULL; 	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, hot_count;		int len = 0, i; 		f2fs_down_read(&sbi->sb_lock);		cold_count = le32_to_cpu(sbi->raw_super->extension_count);		hot_count = sbi->raw_super->hot_ext_count;		len += sysfs_emit_at(buf, len, "cold file extension:\n");		for (i = 0; i < cold_count; i++)			len += sysfs_emit_at(buf, len, "%s\n", extlist[i]); 		len += sysfs_emit_at(buf, len, "hot file extension:\n");		for (i = cold_count; i < cold_count + hot_count; i++)			len += sysfs_emit_at(buf, len, "%s\n", extlist[i]);		f2fs_up_read(&sbi->sb_lock); 		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 level = IOPRIO_PRIO_LEVEL(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", level);	} #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); #ifdef CONFIG_F2FS_STAT_FS	if (!strcmp(a->attr.name, "cp_foreground_calls"))		return sysfs_emit(buf, "%d\n",				atomic_read(&sbi->cp_call_count[TOTAL_CALL]) -				atomic_read(&sbi->cp_call_count[BACKGROUND]));	if (!strcmp(a->attr.name, "cp_background_calls"))		return sysfs_emit(buf, "%d\n",				atomic_read(&sbi->cp_call_count[BACKGROUND]));#endif 	return __sbi_show_value(a, sbi, buf, ptr + a->offset);} static void __sbi_store_value(struct f2fs_attr *a,			struct f2fs_sb_info *sbi,			unsigned char *ui, unsigned long value){	switch (a->size) {	case 1:		*(u8 *)ui = value;		break;	case 2:		*(u16 *)ui = value;		break;	case 4:		*(u32 *)ui = value;		break;	case 8:		*(u64 *)ui = value;		break;	default:		f2fs_bug_on(sbi, 1);		f2fs_err(sbi, "store sysfs node value with wrong type");	}} 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)

Lines 1–500 of 2118.