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