root/fs/ubifs/sysfs.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file is part of UBIFS.
 *
 * Copyright (C) 2021 Cisco Systems
 *
 * Author: Stefan Schaeckeler
 */


#include <linux/fs.h>
#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];

        c->stats = kzalloc_obj(struct ubifs_stats_info);
        if (!c->stats) {
                ret = -ENOMEM;
                goto out_last;
        }
        n = snprintf(dfs_dir_name, UBIFS_DFS_DIR_LEN, 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);
}