Add sysfs interface to configure exfat related parameters. Signed-off-by: Yangtao Li <frank.li@xxxxxxxx> --- fs/exfat/Makefile | 2 +- fs/exfat/dir.c | 4 +- fs/exfat/exfat_fs.h | 13 +++++ fs/exfat/super.c | 23 +++++++- fs/exfat/sysfs.c | 138 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 fs/exfat/sysfs.c diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile index ed51926a4971..96bda5e5cca2 100644 --- a/fs/exfat/Makefile +++ b/fs/exfat/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_EXFAT_FS) += exfat.o exfat-y := inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \ - file.o balloc.o + file.o balloc.o sysfs.o diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 957574180a5e..efd2bd6e0567 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -711,15 +711,13 @@ static int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir return 0; } -#define EXFAT_MAX_RA_SIZE (128*1024) static int exfat_dir_readahead(struct super_block *sb, sector_t sec) { struct exfat_sb_info *sbi = EXFAT_SB(sb); struct buffer_head *bh; - unsigned int max_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits; unsigned int page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits; unsigned int adj_ra_count = max(sbi->sect_per_clus, page_ra_count); - unsigned int ra_count = min(adj_ra_count, max_ra_count); + unsigned int ra_count = min(adj_ra_count, sbi->max_dir_ra_count); /* Read-ahead is not required */ if (sbi->sect_per_clus == 1) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 729ada9e26e8..20638334e1ec 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -236,10 +236,13 @@ struct exfat_mount_options { int time_offset; /* Offset of timestamps from UTC (in minutes) */ }; +#define EXFAT_MAX_RA_SIZE (128*1024) + /* * EXFAT file system superblock in-memory data */ struct exfat_sb_info { + struct super_block *sb; /* pointer to VFS super block */ unsigned long long num_sectors; /* num of sectors in volume */ unsigned int num_clusters; /* num of clusters in volume */ unsigned int cluster_size; /* cluster size in bytes */ @@ -275,6 +278,10 @@ struct exfat_sb_info { struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; struct rcu_head rcu; + + unsigned int max_dir_ra_count; + struct kobject s_kobj; + struct completion s_kobj_unregister; }; #define EXFAT_CACHE_VALID 0 @@ -516,6 +523,12 @@ int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); void exfat_evict_inode(struct inode *inode); int exfat_block_truncate_page(struct inode *inode, loff_t from); +/* In sysfs.c */ +int exfat_sysfs_register(struct super_block *sb); +void exfat_sysfs_unregister(struct super_block *sb); +int __init exfat_sysfs_init(void); +void exfat_sysfs_exit(void); + /* exfat/nls.c */ unsigned short exfat_toupper(struct super_block *sb, unsigned short a); int exfat_uniname_ncmp(struct super_block *sb, unsigned short *a, diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 8c32460e031e..a29ce9561cc1 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -501,6 +501,7 @@ static int exfat_read_boot_sector(struct super_block *sb) sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE); sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER; sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED; + sbi->max_dir_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits; /* check consistencies */ if ((u64)sbi->num_FAT_sectors << p_boot->sect_size_bits < @@ -638,6 +639,7 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) opts->discard = 0; } + sbi->sb = sb; sb->s_flags |= SB_NODIRATIME; sb->s_magic = EXFAT_SUPER_MAGIC; sb->s_op = &exfat_sops; @@ -697,6 +699,10 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) goto free_table; } + err = exfat_sysfs_register(sb); + if (err) + goto put_inode; + return 0; put_inode: @@ -773,12 +779,18 @@ static int exfat_init_fs_context(struct fs_context *fc) return 0; } +static void exfat_kill_super(struct super_block *sb) +{ + kill_block_super(sb); + exfat_sysfs_unregister(sb); +} + static struct file_system_type exfat_fs_type = { .owner = THIS_MODULE, .name = "exfat", .init_fs_context = exfat_init_fs_context, .parameters = exfat_parameters, - .kill_sb = kill_block_super, + .kill_sb = exfat_kill_super, .fs_flags = FS_REQUIRES_DEV, }; @@ -811,12 +823,18 @@ static int __init init_exfat_fs(void) goto shutdown_cache; } - err = register_filesystem(&exfat_fs_type); + err = exfat_sysfs_init(); if (err) goto destroy_cache; + err = register_filesystem(&exfat_fs_type); + if (err) + goto sysfs_exit; + return 0; +sysfs_exit: + exfat_sysfs_exit(); destroy_cache: kmem_cache_destroy(exfat_inode_cachep); shutdown_cache: @@ -833,6 +851,7 @@ static void __exit exit_exfat_fs(void) rcu_barrier(); kmem_cache_destroy(exfat_inode_cachep); unregister_filesystem(&exfat_fs_type); + exfat_sysfs_exit(); exfat_cache_shutdown(); } diff --git a/fs/exfat/sysfs.c b/fs/exfat/sysfs.c new file mode 100644 index 000000000000..d0a4dac3bc71 --- /dev/null +++ b/fs/exfat/sysfs.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Vivo Communication Technology Co.,Ltd. + * Author: Yangtao Li <frank.li@xxxxxxxx> + */ + +#include <linux/fs.h> +#include <linux/seq_file.h> +#include <linux/blkdev.h> + +#include "exfat_raw.h" +#include "exfat_fs.h" + +struct exfat_sysfs_attr { + struct attribute attr; + ssize_t (*show)(struct exfat_sb_info *sbi, char *buf); + ssize_t (*store)(struct exfat_sb_info *sbi, const char *buf, + size_t count); +}; + +#define EXFAT_SYSFS_ATTR_RW(name) \ +static struct exfat_sysfs_attr exfat_sysfs_attr_##name = __ATTR_RW(name) + +#define ATTR_LIST(name) (&exfat_sysfs_attr_##name.attr) + +static ssize_t exfat_sysfs_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct exfat_sb_info *sbi = + container_of(kobj, struct exfat_sb_info, s_kobj); + struct exfat_sysfs_attr *exfat_attr = + container_of(attr, struct exfat_sysfs_attr, attr); + + return exfat_attr->show(sbi, buf); +} + +static ssize_t exfat_sysfs_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct exfat_sb_info *sbi = + container_of(kobj, struct exfat_sb_info, s_kobj); + struct exfat_sysfs_attr *exfat_attr = + container_of(attr, struct exfat_sysfs_attr, attr); + + return exfat_attr->store(sbi, buf, count); +} + +static ssize_t max_dir_ra_count_show(struct exfat_sb_info *sbi, char *buf) +{ + return sysfs_emit(buf, "%u\n", sbi->max_dir_ra_count); +} + +static ssize_t max_dir_ra_count_store(struct exfat_sb_info *sbi, const char *buf, + size_t count) +{ + struct super_block *sb = sbi->sb; + unsigned long t; + int ret; + + ret = kstrtoul(skip_spaces(buf), 0, &t); + if (ret < 0) + return ret; + + if (t > EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits) + return -EINVAL; + + sbi->max_dir_ra_count = t; + + return count; +} +EXFAT_SYSFS_ATTR_RW(max_dir_ra_count); + +static struct attribute *exfat_sysfs_attrs[] = { + ATTR_LIST(max_dir_ra_count), + NULL, +}; +ATTRIBUTE_GROUPS(exfat_sysfs); + +static void exfat_sysfs_sb_release(struct kobject *kobj) +{ + struct exfat_sb_info *sbi = + container_of(kobj, struct exfat_sb_info, s_kobj); + + complete(&sbi->s_kobj_unregister); +} + +static const struct sysfs_ops exfat_sysfs_attr_ops = { + .show = exfat_sysfs_attr_show, + .store = exfat_sysfs_attr_store, +}; + +static const struct kobj_type exfat_sb_ktype = { + .default_groups = exfat_sysfs_groups, + .sysfs_ops = &exfat_sysfs_attr_ops, + .release = exfat_sysfs_sb_release, +}; + +static struct kobject *exfat_sysfs_root; + +int exfat_sysfs_register(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int ret; + + init_completion(&sbi->s_kobj_unregister); + ret = kobject_init_and_add(&sbi->s_kobj, &exfat_sb_ktype, + exfat_sysfs_root, "%s", sb->s_id); + if (ret) { + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); + return ret; + } + + return 0; +} + +void exfat_sysfs_unregister(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); +} + +int __init exfat_sysfs_init(void) +{ + exfat_sysfs_root = kobject_create_and_add("exfat", fs_kobj); + if (!exfat_sysfs_root) + return -ENOMEM; + + return 0; +} + +void exfat_sysfs_exit(void) +{ + kobject_put(exfat_sysfs_root); + exfat_sysfs_root = NULL; +} -- 2.35.1