exfat: add sysfs interface

Message ID 20230405084635.74680-1-frank.li@vivo.com
State New
Headers
Series exfat: add sysfs interface |

Commit Message

李扬韬 April 5, 2023, 8:46 a.m. UTC
  Add sysfs interface to configure exfat related parameters.

Signed-off-by: Yangtao Li <frank.li@vivo.com>
---
 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
  

Comments

李扬韬 April 25, 2023, 7:32 p.m. UTC | #1
ping.....

Thx,
Yangtao
  
Namjae Jeon April 25, 2023, 11:26 p.m. UTC | #2
2023-04-26 4:32 GMT+09:00, Yangtao Li <frank.li@vivo.com>:
> ping.....
There is no explanation as to why this parameter is needed.
>
> Thx,
> Yangtao
>
  

Patch

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@vivo.com>
+ */
+
+#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;
+}