// SPDX-License-Identifier: GPL-2.0-only /* * This file is part of UBIFS. * * Copyright (C) 2021 Cisco Systems * * Author: Stefan Schaeckeler */ #include #include "ubifs.h" enum attr_id_t { attr_errors_magic, attr_errors_node, attr_errors_crc, }; struct ubifs_attr { struct attribute attr; enum attr_id_t attr_id; }; #define UBIFS_ATTR(_name, _mode, _id) \ static struct ubifs_attr ubifs_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .attr_id = attr_##_id, \ } #define UBIFS_ATTR_FUNC(_name, _mode) UBIFS_ATTR(_name, _mode, _name) UBIFS_ATTR_FUNC(errors_magic, 0444); UBIFS_ATTR_FUNC(errors_crc, 0444); UBIFS_ATTR_FUNC(errors_node, 0444); #define ATTR_LIST(name) (&ubifs_attr_##name.attr) static struct attribute *ubifs_attrs[] = { ATTR_LIST(errors_magic), ATTR_LIST(errors_node), ATTR_LIST(errors_crc), NULL, }; ATTRIBUTE_GROUPS(ubifs); static ssize_t ubifs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct ubifs_info *sbi = container_of(kobj, struct ubifs_info, kobj); struct ubifs_attr *a = container_of(attr, struct ubifs_attr, attr); switch (a->attr_id) { case attr_errors_magic: return sysfs_emit(buf, "%u\n", sbi->stats->magic_errors); case attr_errors_node: return sysfs_emit(buf, "%u\n", sbi->stats->node_errors); case attr_errors_crc: return sysfs_emit(buf, "%u\n", sbi->stats->crc_errors); } return 0; }; static void ubifs_sb_release(struct kobject *kobj) { struct ubifs_info *c = container_of(kobj, struct ubifs_info, kobj); complete(&c->kobj_unregister); } static const struct sysfs_ops ubifs_attr_ops = { .show = ubifs_attr_show, }; static const struct kobj_type ubifs_sb_ktype = { .default_groups = ubifs_groups, .sysfs_ops = &ubifs_attr_ops, .release = ubifs_sb_release, }; static const struct kobj_type ubifs_ktype = { .sysfs_ops = &ubifs_attr_ops, }; static struct kset ubifs_kset = { .kobj = {.ktype = &ubifs_ktype}, }; int ubifs_sysfs_register(struct ubifs_info *c) { int ret, n; char dfs_dir_name[UBIFS_DFS_DIR_LEN+1]; c->stats = kzalloc(sizeof(struct ubifs_stats_info), GFP_KERNEL); if (!c->stats) { ret = -ENOMEM; goto out_last; } n = snprintf(dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME, c->vi.ubi_num, c->vi.vol_id); if (n > UBIFS_DFS_DIR_LEN) { /* The array size is too small */ ret = -EINVAL; goto out_free; } c->kobj.kset = &ubifs_kset; init_completion(&c->kobj_unregister); ret = kobject_init_and_add(&c->kobj, &ubifs_sb_ktype, NULL, "%s", dfs_dir_name); if (ret) goto out_put; return 0; out_put: kobject_put(&c->kobj); wait_for_completion(&c->kobj_unregister); out_free: kfree(c->stats); out_last: ubifs_err(c, "cannot create sysfs entry for ubifs%d_%d, error %d\n", c->vi.ubi_num, c->vi.vol_id, ret); return ret; } void ubifs_sysfs_unregister(struct ubifs_info *c) { kobject_del(&c->kobj); kobject_put(&c->kobj); wait_for_completion(&c->kobj_unregister); kfree(c->stats); } int __init ubifs_sysfs_init(void) { int ret; kobject_set_name(&ubifs_kset.kobj, "ubifs"); ubifs_kset.kobj.parent = fs_kobj; ret = kset_register(&ubifs_kset); if (ret) kset_put(&ubifs_kset); return ret; } void ubifs_sysfs_exit(void) { kset_unregister(&ubifs_kset); }