at for-next 276 lines 6.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C), 2008-2021, OPPO Mobile Comm Corp., Ltd. 4 * https://www.oppo.com/ 5 */ 6#include <linux/sysfs.h> 7#include <linux/kobject.h> 8 9#include "internal.h" 10 11enum { 12 attr_feature, 13 attr_drop_caches, 14 attr_pointer_ui, 15 attr_pointer_bool, 16}; 17 18enum { 19 struct_erofs_sb_info, 20 struct_erofs_mount_opts, 21}; 22 23struct erofs_attr { 24 struct attribute attr; 25 short attr_id; 26 int struct_type, offset; 27}; 28 29#define EROFS_ATTR(_name, _mode, _id) \ 30static struct erofs_attr erofs_attr_##_name = { \ 31 .attr = {.name = __stringify(_name), .mode = _mode }, \ 32 .attr_id = attr_##_id, \ 33} 34#define EROFS_ATTR_FUNC(_name, _mode) EROFS_ATTR(_name, _mode, _name) 35#define EROFS_ATTR_FEATURE(_name) EROFS_ATTR(_name, 0444, feature) 36 37#define EROFS_ATTR_OFFSET(_name, _mode, _id, _struct) \ 38static struct erofs_attr erofs_attr_##_name = { \ 39 .attr = {.name = __stringify(_name), .mode = _mode }, \ 40 .attr_id = attr_##_id, \ 41 .struct_type = struct_##_struct, \ 42 .offset = offsetof(struct _struct, _name),\ 43} 44 45#define EROFS_ATTR_RW(_name, _id, _struct) \ 46 EROFS_ATTR_OFFSET(_name, 0644, _id, _struct) 47 48#define EROFS_RO_ATTR(_name, _id, _struct) \ 49 EROFS_ATTR_OFFSET(_name, 0444, _id, _struct) 50 51#define EROFS_ATTR_RW_UI(_name, _struct) \ 52 EROFS_ATTR_RW(_name, pointer_ui, _struct) 53 54#define EROFS_ATTR_RW_BOOL(_name, _struct) \ 55 EROFS_ATTR_RW(_name, pointer_bool, _struct) 56 57#define ATTR_LIST(name) (&erofs_attr_##name.attr) 58 59#ifdef CONFIG_EROFS_FS_ZIP 60EROFS_ATTR_RW_UI(sync_decompress, erofs_mount_opts); 61EROFS_ATTR_FUNC(drop_caches, 0200); 62#endif 63 64static struct attribute *erofs_attrs[] = { 65#ifdef CONFIG_EROFS_FS_ZIP 66 ATTR_LIST(sync_decompress), 67 ATTR_LIST(drop_caches), 68#endif 69 NULL, 70}; 71ATTRIBUTE_GROUPS(erofs); 72 73/* Features this copy of erofs supports */ 74EROFS_ATTR_FEATURE(zero_padding); 75EROFS_ATTR_FEATURE(compr_cfgs); 76EROFS_ATTR_FEATURE(big_pcluster); 77EROFS_ATTR_FEATURE(chunked_file); 78EROFS_ATTR_FEATURE(device_table); 79EROFS_ATTR_FEATURE(compr_head2); 80EROFS_ATTR_FEATURE(sb_chksum); 81EROFS_ATTR_FEATURE(ztailpacking); 82EROFS_ATTR_FEATURE(fragments); 83EROFS_ATTR_FEATURE(dedupe); 84 85static struct attribute *erofs_feat_attrs[] = { 86 ATTR_LIST(zero_padding), 87 ATTR_LIST(compr_cfgs), 88 ATTR_LIST(big_pcluster), 89 ATTR_LIST(chunked_file), 90 ATTR_LIST(device_table), 91 ATTR_LIST(compr_head2), 92 ATTR_LIST(sb_chksum), 93 ATTR_LIST(ztailpacking), 94 ATTR_LIST(fragments), 95 ATTR_LIST(dedupe), 96 NULL, 97}; 98ATTRIBUTE_GROUPS(erofs_feat); 99 100static unsigned char *__struct_ptr(struct erofs_sb_info *sbi, 101 int struct_type, int offset) 102{ 103 if (struct_type == struct_erofs_sb_info) 104 return (unsigned char *)sbi + offset; 105 if (struct_type == struct_erofs_mount_opts) 106 return (unsigned char *)&sbi->opt + offset; 107 return NULL; 108} 109 110static ssize_t erofs_attr_show(struct kobject *kobj, 111 struct attribute *attr, char *buf) 112{ 113 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 114 s_kobj); 115 struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 116 unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 117 118 switch (a->attr_id) { 119 case attr_feature: 120 return sysfs_emit(buf, "supported\n"); 121 case attr_pointer_ui: 122 if (!ptr) 123 return 0; 124 return sysfs_emit(buf, "%u\n", *(unsigned int *)ptr); 125 case attr_pointer_bool: 126 if (!ptr) 127 return 0; 128 return sysfs_emit(buf, "%d\n", *(bool *)ptr); 129 } 130 return 0; 131} 132 133static ssize_t erofs_attr_store(struct kobject *kobj, struct attribute *attr, 134 const char *buf, size_t len) 135{ 136 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 137 s_kobj); 138 struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 139 unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 140 unsigned long t; 141 int ret; 142 143 switch (a->attr_id) { 144 case attr_pointer_ui: 145 if (!ptr) 146 return 0; 147 ret = kstrtoul(skip_spaces(buf), 0, &t); 148 if (ret) 149 return ret; 150 if (t != (unsigned int)t) 151 return -ERANGE; 152#ifdef CONFIG_EROFS_FS_ZIP 153 if (!strcmp(a->attr.name, "sync_decompress") && 154 (t > EROFS_SYNC_DECOMPRESS_FORCE_OFF)) 155 return -EINVAL; 156#endif 157 *(unsigned int *)ptr = t; 158 return len; 159 case attr_pointer_bool: 160 if (!ptr) 161 return 0; 162 ret = kstrtoul(skip_spaces(buf), 0, &t); 163 if (ret) 164 return ret; 165 if (t != 0 && t != 1) 166 return -EINVAL; 167 *(bool *)ptr = !!t; 168 return len; 169#ifdef CONFIG_EROFS_FS_ZIP 170 case attr_drop_caches: 171 ret = kstrtoul(skip_spaces(buf), 0, &t); 172 if (ret) 173 return ret; 174 if (t < 1 || t > 3) 175 return -EINVAL; 176 177 if (t & 2) 178 z_erofs_shrink_scan(sbi, ~0UL); 179 if (t & 1) 180 invalidate_mapping_pages(MNGD_MAPPING(sbi), 0, -1); 181 return len; 182#endif 183 } 184 return 0; 185} 186 187static void erofs_sb_release(struct kobject *kobj) 188{ 189 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 190 s_kobj); 191 complete(&sbi->s_kobj_unregister); 192} 193 194static const struct sysfs_ops erofs_attr_ops = { 195 .show = erofs_attr_show, 196 .store = erofs_attr_store, 197}; 198 199static const struct kobj_type erofs_sb_ktype = { 200 .default_groups = erofs_groups, 201 .sysfs_ops = &erofs_attr_ops, 202 .release = erofs_sb_release, 203}; 204 205static const struct kobj_type erofs_ktype = { 206 .sysfs_ops = &erofs_attr_ops, 207}; 208 209static struct kset erofs_root = { 210 .kobj = {.ktype = &erofs_ktype}, 211}; 212 213static const struct kobj_type erofs_feat_ktype = { 214 .default_groups = erofs_feat_groups, 215 .sysfs_ops = &erofs_attr_ops, 216}; 217 218static struct kobject erofs_feat = { 219 .kset = &erofs_root, 220}; 221 222int erofs_register_sysfs(struct super_block *sb) 223{ 224 struct erofs_sb_info *sbi = EROFS_SB(sb); 225 int err; 226 227 sbi->s_kobj.kset = &erofs_root; 228 init_completion(&sbi->s_kobj_unregister); 229 err = kobject_init_and_add(&sbi->s_kobj, &erofs_sb_ktype, NULL, "%s", 230 sb->s_sysfs_name); 231 if (err) { 232 kobject_put(&sbi->s_kobj); 233 wait_for_completion(&sbi->s_kobj_unregister); 234 } 235 return err; 236} 237 238void erofs_unregister_sysfs(struct super_block *sb) 239{ 240 struct erofs_sb_info *sbi = EROFS_SB(sb); 241 242 if (sbi->s_kobj.state_in_sysfs) { 243 kobject_del(&sbi->s_kobj); 244 kobject_put(&sbi->s_kobj); 245 wait_for_completion(&sbi->s_kobj_unregister); 246 } 247} 248 249int __init erofs_init_sysfs(void) 250{ 251 int ret; 252 253 kobject_set_name(&erofs_root.kobj, "erofs"); 254 erofs_root.kobj.parent = fs_kobj; 255 ret = kset_register(&erofs_root); 256 if (ret) 257 goto root_err; 258 259 ret = kobject_init_and_add(&erofs_feat, &erofs_feat_ktype, 260 NULL, "features"); 261 if (ret) 262 goto feat_err; 263 return ret; 264 265feat_err: 266 kobject_put(&erofs_feat); 267 kset_unregister(&erofs_root); 268root_err: 269 return ret; 270} 271 272void erofs_exit_sysfs(void) 273{ 274 kobject_put(&erofs_feat); 275 kset_unregister(&erofs_root); 276}