diff mbox series

[2/3] shmem: Introduce /sys/fs/tmpfs support

Message ID 20220322222738.182974-3-krisman@collabora.com (mailing list archive)
State New
Headers show
Series shmem: Allow userspace monitoring of tmpfs for lack of space. | expand

Commit Message

Gabriel Krisman Bertazi March 22, 2022, 10:27 p.m. UTC
In order to expose tmpfs statistics on sysfs, add the boilerplate code
to create the /sys/fs/tmpfs structure.  Other filesystems usually do
/sys/fs/<fs>/<disk>, but since this is a nodev filesystem, I'm proposing
to use fsid as <disk>.

This takes care of not exposing SB_NOUSER mounts.  I don't think we have
a usecase for showing them and, since they don't appear elsewhere, they
might be confusing to users.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
---
 include/linux/shmem_fs.h |  4 +++
 mm/shmem.c               | 73 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 76 insertions(+), 1 deletion(-)

Comments

Amir Goldstein March 22, 2022, 11:58 p.m. UTC | #1
[+fsdevel please CC on next versions]

On Wed, Mar 23, 2022 at 12:27 AM Gabriel Krisman Bertazi
<krisman@collabora.com> wrote:
>
> In order to expose tmpfs statistics on sysfs, add the boilerplate code
> to create the /sys/fs/tmpfs structure.  Other filesystems usually do
> /sys/fs/<fs>/<disk>, but since this is a nodev filesystem, I'm proposing
> to use fsid as <disk>.

I am proposing st_dev minor.

>
> This takes care of not exposing SB_NOUSER mounts.  I don't think we have
> a usecase for showing them and, since they don't appear elsewhere, they
> might be confusing to users.
>
> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
> ---
>  include/linux/shmem_fs.h |  4 +++
>  mm/shmem.c               | 73 +++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 76 insertions(+), 1 deletion(-)
>
[...]

> +static int shmem_register_sysfs(struct super_block *sb)
> +{
> +       int err;
> +       struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
> +       __kernel_fsid_t fsid = uuid_to_fsid(sb->s_uuid.b);
> +
> +       init_completion(&sbinfo->s_kobj_unregister);
> +       err = kobject_init_and_add(&sbinfo->s_kobj, &tmpfs_sb_ktype, shmem_root,
> +                                  "%x%x", fsid.val[0], fsid.val[1]);

uuid (and fsid) try to be unique across tmpfs instances from different times.
You don't need that.
I think you'd rather use s_dev (minor number) which is unique among all tmpfs
instances at a given time and also much easier from user scripts to read from
(e.g. stat or /proc/self/mountinfo).

That's btw the same number is used as an entry in /sys/fs/fuse/connections
(fusectl pseudo fs).

Thanks,
Amir.
Gabriel Krisman Bertazi March 23, 2022, 6:04 p.m. UTC | #2
Amir Goldstein <amir73il@gmail.com> writes:

>> +static int shmem_register_sysfs(struct super_block *sb)
>> +{
>> +       int err;
>> +       struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
>> +       __kernel_fsid_t fsid = uuid_to_fsid(sb->s_uuid.b);
>> +
>> +       init_completion(&sbinfo->s_kobj_unregister);
>> +       err = kobject_init_and_add(&sbinfo->s_kobj, &tmpfs_sb_ktype, shmem_root,
>> +                                  "%x%x", fsid.val[0], fsid.val[1]);
>
> uuid (and fsid) try to be unique across tmpfs instances from different times.
> You don't need that.
> I think you'd rather use s_dev (minor number) which is unique among all tmpfs
> instances at a given time and also much easier from user scripts to read from
> (e.g. stat or /proc/self/mountinfo).
>
> That's btw the same number is used as an entry in /sys/fs/fuse/connections
> (fusectl pseudo fs).

Hi Amir, thanks for the review.

Sounds good.  I will follow up with a new version that uses
MINOR(sb->s_dev).

Thank you,
diff mbox series

Patch

diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 1a7cd9ea9107..c27ecf0e1b3b 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -47,6 +47,10 @@  struct shmem_sb_info {
 
 	unsigned long acct_errors;
 	unsigned long space_errors;
+
+	/* sysfs */
+	struct kobject s_kobj;		/* /sys/fs/tmpfs/<uuid> */
+	struct completion s_kobj_unregister;
 };
 
 static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
diff --git a/mm/shmem.c b/mm/shmem.c
index c350fa0a0fff..5e8973664b7f 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -250,6 +250,7 @@  static const struct inode_operations shmem_dir_inode_operations;
 static const struct inode_operations shmem_special_inode_operations;
 static const struct vm_operations_struct shmem_vm_ops;
 static struct file_system_type shmem_fs_type;
+static struct kobject *shmem_root;
 
 bool vma_is_shmem(struct vm_area_struct *vma)
 {
@@ -3584,6 +3585,57 @@  static int shmem_show_options(struct seq_file *seq, struct dentry *root)
 
 #endif /* CONFIG_TMPFS */
 
+#if defined(CONFIG_TMPFS) && defined(CONFIG_SYSFS)
+#define TMPFS_SB_ATTR_RO(name)	\
+	static struct kobj_attribute tmpfs_sb_attr_##name = __ATTR_RO(name)
+
+static struct attribute *tmpfs_attrs[] = {
+	NULL
+};
+ATTRIBUTE_GROUPS(tmpfs);
+
+static void tmpfs_sb_release(struct kobject *kobj)
+{
+	struct shmem_sb_info *sbinfo =
+		container_of(kobj, struct shmem_sb_info, s_kobj);
+
+	complete(&sbinfo->s_kobj_unregister);
+}
+
+static struct kobj_type tmpfs_sb_ktype = {
+	.default_groups = tmpfs_groups,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.release	= tmpfs_sb_release,
+};
+
+static void shmem_unregister_sysfs(struct super_block *sb)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+
+	kobject_del(&sbinfo->s_kobj);
+	kobject_put(&sbinfo->s_kobj);
+	wait_for_completion(&sbinfo->s_kobj_unregister);
+}
+
+static int shmem_register_sysfs(struct super_block *sb)
+{
+	int err;
+	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+	__kernel_fsid_t fsid = uuid_to_fsid(sb->s_uuid.b);
+
+	init_completion(&sbinfo->s_kobj_unregister);
+	err = kobject_init_and_add(&sbinfo->s_kobj, &tmpfs_sb_ktype, shmem_root,
+				   "%x%x", fsid.val[0], fsid.val[1]);
+	if (err) {
+		kobject_put(&sbinfo->s_kobj);
+		wait_for_completion(&sbinfo->s_kobj_unregister);
+		return err;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_TMPFS && CONFIG_SYSFS */
+
 static void shmem_put_super(struct super_block *sb)
 {
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
@@ -3591,6 +3643,12 @@  static void shmem_put_super(struct super_block *sb)
 	free_percpu(sbinfo->ino_batch);
 	percpu_counter_destroy(&sbinfo->used_blocks);
 	mpol_put(sbinfo->mpol);
+
+#if IS_ENABLED(CONFIG_TMPFS) && IS_ENABLED(CONFIG_SYSFS)
+	if (!(sb->s_flags & SB_NOUSER))
+		shmem_unregister_sysfs(sb);
+#endif
+
 	kfree(sbinfo);
 	sb->s_fs_info = NULL;
 }
@@ -3673,6 +3731,13 @@  static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
 	sb->s_root = d_make_root(inode);
 	if (!sb->s_root)
 		goto failed;
+
+#if IS_ENABLED(CONFIG_TMPFS) && IS_ENABLED(CONFIG_SYSFS)
+	if (!(sb->s_flags & SB_NOUSER))
+		if (shmem_register_sysfs(sb))
+			goto failed;
+#endif
+
 	return 0;
 
 failed:
@@ -3889,11 +3954,15 @@  int __init shmem_init(void)
 		goto out2;
 	}
 
+	shmem_root = kobject_create_and_add("tmpfs", fs_kobj);
+	if (!shmem_root)
+		goto out1;
+
 	shm_mnt = kern_mount(&shmem_fs_type);
 	if (IS_ERR(shm_mnt)) {
 		error = PTR_ERR(shm_mnt);
 		pr_err("Could not kern_mount tmpfs\n");
-		goto out1;
+		goto put_kobj;
 	}
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -3904,6 +3973,8 @@  int __init shmem_init(void)
 #endif
 	return 0;
 
+put_kobj:
+	kobject_put(shmem_root);
 out1:
 	unregister_filesystem(&shmem_fs_type);
 out2: