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