Damien Le Moal | 9277a6d | 2022-04-12 16:25:34 +0900 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Simple file system for zoned block devices exposing zones as files. |
| 4 | * |
| 5 | * Copyright (C) 2022 Western Digital Corporation or its affiliates. |
| 6 | */ |
| 7 | #include <linux/fs.h> |
| 8 | #include <linux/seq_file.h> |
| 9 | #include <linux/blkdev.h> |
| 10 | |
| 11 | #include "zonefs.h" |
| 12 | |
| 13 | struct zonefs_sysfs_attr { |
| 14 | struct attribute attr; |
| 15 | ssize_t (*show)(struct zonefs_sb_info *sbi, char *buf); |
| 16 | }; |
| 17 | |
| 18 | static inline struct zonefs_sysfs_attr *to_attr(struct attribute *attr) |
| 19 | { |
| 20 | return container_of(attr, struct zonefs_sysfs_attr, attr); |
| 21 | } |
| 22 | |
| 23 | #define ZONEFS_SYSFS_ATTR_RO(name) \ |
| 24 | static struct zonefs_sysfs_attr zonefs_sysfs_attr_##name = __ATTR_RO(name) |
| 25 | |
| 26 | #define ATTR_LIST(name) &zonefs_sysfs_attr_##name.attr |
| 27 | |
| 28 | static ssize_t zonefs_sysfs_attr_show(struct kobject *kobj, |
| 29 | struct attribute *attr, char *buf) |
| 30 | { |
| 31 | struct zonefs_sb_info *sbi = |
| 32 | container_of(kobj, struct zonefs_sb_info, s_kobj); |
| 33 | struct zonefs_sysfs_attr *zonefs_attr = |
| 34 | container_of(attr, struct zonefs_sysfs_attr, attr); |
| 35 | |
| 36 | if (!zonefs_attr->show) |
| 37 | return 0; |
| 38 | |
| 39 | return zonefs_attr->show(sbi, buf); |
| 40 | } |
| 41 | |
| 42 | static ssize_t max_wro_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
| 43 | { |
| 44 | return sysfs_emit(buf, "%u\n", sbi->s_max_wro_seq_files); |
| 45 | } |
| 46 | ZONEFS_SYSFS_ATTR_RO(max_wro_seq_files); |
| 47 | |
| 48 | static ssize_t nr_wro_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
| 49 | { |
| 50 | return sysfs_emit(buf, "%d\n", atomic_read(&sbi->s_wro_seq_files)); |
| 51 | } |
| 52 | ZONEFS_SYSFS_ATTR_RO(nr_wro_seq_files); |
| 53 | |
Damien Le Moal | 87c9ce3f | 2022-04-12 18:54:39 +0900 | [diff] [blame] | 54 | static ssize_t max_active_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
| 55 | { |
| 56 | return sysfs_emit(buf, "%u\n", sbi->s_max_active_seq_files); |
| 57 | } |
| 58 | ZONEFS_SYSFS_ATTR_RO(max_active_seq_files); |
| 59 | |
| 60 | static ssize_t nr_active_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
| 61 | { |
| 62 | return sysfs_emit(buf, "%d\n", atomic_read(&sbi->s_active_seq_files)); |
| 63 | } |
| 64 | ZONEFS_SYSFS_ATTR_RO(nr_active_seq_files); |
| 65 | |
Damien Le Moal | 9277a6d | 2022-04-12 16:25:34 +0900 | [diff] [blame] | 66 | static struct attribute *zonefs_sysfs_attrs[] = { |
| 67 | ATTR_LIST(max_wro_seq_files), |
| 68 | ATTR_LIST(nr_wro_seq_files), |
Damien Le Moal | 87c9ce3f | 2022-04-12 18:54:39 +0900 | [diff] [blame] | 69 | ATTR_LIST(max_active_seq_files), |
| 70 | ATTR_LIST(nr_active_seq_files), |
Damien Le Moal | 9277a6d | 2022-04-12 16:25:34 +0900 | [diff] [blame] | 71 | NULL, |
| 72 | }; |
| 73 | ATTRIBUTE_GROUPS(zonefs_sysfs); |
| 74 | |
| 75 | static void zonefs_sysfs_sb_release(struct kobject *kobj) |
| 76 | { |
| 77 | struct zonefs_sb_info *sbi = |
| 78 | container_of(kobj, struct zonefs_sb_info, s_kobj); |
| 79 | |
| 80 | complete(&sbi->s_kobj_unregister); |
| 81 | } |
| 82 | |
| 83 | static const struct sysfs_ops zonefs_sysfs_attr_ops = { |
| 84 | .show = zonefs_sysfs_attr_show, |
| 85 | }; |
| 86 | |
| 87 | static struct kobj_type zonefs_sb_ktype = { |
| 88 | .default_groups = zonefs_sysfs_groups, |
| 89 | .sysfs_ops = &zonefs_sysfs_attr_ops, |
| 90 | .release = zonefs_sysfs_sb_release, |
| 91 | }; |
| 92 | |
| 93 | static struct kobject *zonefs_sysfs_root; |
| 94 | |
| 95 | int zonefs_sysfs_register(struct super_block *sb) |
| 96 | { |
| 97 | struct zonefs_sb_info *sbi = ZONEFS_SB(sb); |
| 98 | int ret; |
| 99 | |
| 100 | init_completion(&sbi->s_kobj_unregister); |
| 101 | ret = kobject_init_and_add(&sbi->s_kobj, &zonefs_sb_ktype, |
| 102 | zonefs_sysfs_root, "%s", sb->s_id); |
| 103 | if (ret) { |
| 104 | kobject_put(&sbi->s_kobj); |
| 105 | wait_for_completion(&sbi->s_kobj_unregister); |
| 106 | return ret; |
| 107 | } |
| 108 | |
| 109 | sbi->s_sysfs_registered = true; |
| 110 | |
| 111 | return 0; |
| 112 | } |
| 113 | |
| 114 | void zonefs_sysfs_unregister(struct super_block *sb) |
| 115 | { |
| 116 | struct zonefs_sb_info *sbi = ZONEFS_SB(sb); |
| 117 | |
| 118 | if (!sbi || !sbi->s_sysfs_registered) |
| 119 | return; |
| 120 | |
| 121 | kobject_del(&sbi->s_kobj); |
| 122 | kobject_put(&sbi->s_kobj); |
| 123 | wait_for_completion(&sbi->s_kobj_unregister); |
| 124 | } |
| 125 | |
| 126 | int __init zonefs_sysfs_init(void) |
| 127 | { |
| 128 | zonefs_sysfs_root = kobject_create_and_add("zonefs", fs_kobj); |
| 129 | if (!zonefs_sysfs_root) |
| 130 | return -ENOMEM; |
| 131 | |
| 132 | return 0; |
| 133 | } |
| 134 | |
| 135 | void zonefs_sysfs_exit(void) |
| 136 | { |
| 137 | kobject_put(zonefs_sysfs_root); |
| 138 | zonefs_sysfs_root = NULL; |
| 139 | } |