157 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			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 struct kobj_type ubifs_sb_ktype = {
 | 
						|
	.default_groups	= ubifs_groups,
 | 
						|
	.sysfs_ops	= &ubifs_attr_ops,
 | 
						|
	.release	= ubifs_sb_release,
 | 
						|
};
 | 
						|
 | 
						|
static 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);
 | 
						|
}
 |