2026-01-29 22:25:33 +08:00

325 lines
7.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2020-2022 Hisilicon Limited.
#include <net/sock.h>
#include <linux/sysfs.h>
#include <linux/stat.h>
#include "core.h"
#include "core_priv.h"
#define ROH_MIB_STATS_TYPE_NUM 2
static ssize_t node_eid_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct roh_device *dev = container_of(device, struct roh_device, dev);
struct roh_eid_attr eid;
roh_device_get_eid(dev, &eid);
return sprintf(buf, "base:%u num:%u\n", eid.base, eid.num);
}
static ssize_t node_link_status_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct roh_device *dev = container_of(device, struct roh_device, dev);
return sprintf(buf, "%s\n",
(roh_device_query_link_status(dev) == ROH_LINK_UP) ?
"UP" : "DOWN");
}
static DEVICE_ATTR_RO(node_eid);
static DEVICE_ATTR_RO(node_link_status);
static struct device_attribute *roh_class_attr[] = {
&dev_attr_node_eid,
&dev_attr_node_link_status,
};
struct roh_hw_stats_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj,
struct attribute *attr, char *buf);
ssize_t (*store)(struct kobject *kobj,
struct attribute *attr,
const char *buf,
size_t count);
};
static void remove_device_sysfs(struct roh_device *device)
{
int i;
for (i = 0; i < ARRAY_SIZE(roh_class_attr); i++)
device_remove_file(&device->dev, roh_class_attr[i]);
}
static const char * const g_roh_hw_stats_name[ROH_MIB_STATS_TYPE_NUM] = {
"mib_public",
"mib_private",
};
static ssize_t print_hw_stat(struct roh_device *dev,
struct roh_mib_stats *stats, char *buf)
{
int offset = 0;
int i;
for (i = 0; i < stats->num_counters; i++)
offset += sprintf(buf + offset, "%s: %llu\n",
stats->names[i], stats->value[i]);
return offset;
}
static ssize_t node_public_mib_stats_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct roh_mib_stats *public_stats;
struct roh_device *dev;
int ret;
dev = container_of(kobj, struct roh_device, dev.kobj);
public_stats = dev->hw_public_stats;
mutex_lock(&public_stats->lock);
ret = dev->ops.get_hw_stats(dev, public_stats, ROH_MIB_PUBLIC);
if (ret) {
dev_err(&dev->dev,
"failed to get roh public hw stats, ret = %d.\n", ret);
goto unlock;
}
ret = print_hw_stat(dev, public_stats, buf);
unlock:
mutex_unlock(&public_stats->lock);
return ret;
}
static ssize_t node_private_mib_stats_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct roh_mib_stats *private_stats;
struct roh_device *dev;
int ret;
dev = container_of(kobj, struct roh_device, dev.kobj);
private_stats = dev->hw_private_stats;
mutex_lock(&private_stats->lock);
ret = dev->ops.get_hw_stats(dev, private_stats, ROH_MIB_PRIVATE);
if (ret) {
dev_err(&dev->dev,
"failed to get roh private hw stats, ret = %d.\n", ret);
goto unlock;
}
ret = print_hw_stat(dev, private_stats, buf);
unlock:
mutex_unlock(&private_stats->lock);
return ret;
}
static ssize_t (*g_show_roh_mib_hw_stats[ROH_MIB_STATS_TYPE_NUM])
(struct kobject *, struct attribute *, char *) = {
node_public_mib_stats_show,
node_private_mib_stats_show
};
static void free_hsag(struct kobject *kobj, struct attribute_group *attr_group)
{
struct attribute **attr;
sysfs_remove_group(kobj, attr_group);
for (attr = attr_group->attrs; *attr; attr++)
kfree(*attr);
kfree(attr_group);
}
static struct attribute *alloc_hsa(const char *name,
ssize_t (*show_roh_mib_hw_stats)
(struct kobject *, struct attribute *, char *))
{
struct roh_hw_stats_attribute *hsa;
hsa = kmalloc(sizeof(*hsa), GFP_KERNEL);
if (!hsa)
return NULL;
hsa->attr.name = (char *)name;
hsa->attr.mode = 0444;
hsa->show = show_roh_mib_hw_stats;
hsa->store = NULL;
return &hsa->attr;
}
static void free_hw_stats(struct roh_device *device)
{
kfree(device->hw_private_stats);
kfree(device->hw_public_stats);
}
static int alloc_and_get_hw_stats(struct roh_device *device)
{
struct roh_mib_stats *privite_stats, *public_stats;
int ret;
public_stats = device->ops.alloc_hw_stats(device, ROH_MIB_PUBLIC);
if (!public_stats) {
dev_err(&device->dev, "failed to alloc roh public hw stats.\n");
return -ENOMEM;
}
privite_stats = device->ops.alloc_hw_stats(device, ROH_MIB_PRIVATE);
if (!privite_stats) {
dev_err(&device->dev, "failed to alloc roh privite hw stats.\n");
kfree(public_stats);
return -ENOMEM;
}
ret = device->ops.get_hw_stats(device, public_stats, ROH_MIB_PUBLIC);
if (ret) {
dev_err(&device->dev,
"failed to get roh public mib stats, ret = %d\n", ret);
goto err;
}
ret = device->ops.get_hw_stats(device, privite_stats, ROH_MIB_PRIVATE);
if (ret) {
dev_err(&device->dev,
"failed to get roh privite mib stats, ret = %d\n", ret);
goto err;
}
mutex_init(&privite_stats->lock);
mutex_init(&public_stats->lock);
device->hw_public_stats = public_stats;
device->hw_private_stats = privite_stats;
return 0;
err:
free_hw_stats(device);
return ret;
}
static int alloc_hsag(struct roh_device *device)
{
struct attribute_group *hsag;
struct kobject *kobj;
int i, j;
int ret;
/*
* one extra attribue elements here, terminate the
* list for the sysfs core code
*/
hsag = kzalloc(sizeof(*hsag) +
sizeof(void *) * (ARRAY_SIZE(g_roh_hw_stats_name) + 1),
GFP_KERNEL);
if (!hsag) {
dev_err(&device->dev, "failed to kzalloc hsag.\n");
return -ENOMEM;
}
hsag->name = "node_mib_stats";
hsag->attrs = (void *)hsag + sizeof(*hsag);
for (i = 0; i < ARRAY_SIZE(g_roh_hw_stats_name); i++) {
hsag->attrs[i] = alloc_hsa(g_roh_hw_stats_name[i],
g_show_roh_mib_hw_stats[i]);
if (!hsag->attrs[i]) {
ret = -ENOMEM;
dev_err(&device->dev,
"failed to alloc hsa for hsag attrs[%d].\n", i);
goto err;
}
sysfs_attr_init(hsag->attrs[i]);
}
kobj = &device->dev.kobj;
ret = sysfs_create_group(kobj, hsag);
if (ret) {
dev_err(&device->dev,
"failed to create roh sysfs group, ret = %d\n", ret);
goto err;
}
device->hw_stats_ag = hsag;
return 0;
err:
for (j = i - 1; j >= 0; j--)
kfree(hsag->attrs[j]);
kfree(hsag);
return ret;
}
static int setup_mib_stats(struct roh_device *device)
{
int ret;
ret = alloc_and_get_hw_stats(device);
if (ret) {
dev_err(&device->dev,
"failed to alloc and get roh hw stats, ret = %d.\n", ret);
return ret;
}
ret = alloc_hsag(device);
if (ret) {
dev_err(&device->dev,
"failed to alloc hsag, ret = %d.\n", ret);
free_hw_stats(device);
return ret;
}
return 0;
}
int roh_device_register_sysfs(struct roh_device *device)
{
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(roh_class_attr); i++) {
ret = device_create_file(&device->dev, roh_class_attr[i]);
if (ret) {
dev_err(&device->dev,
"failed to create node %s, ret = %d.\n",
roh_class_attr[i]->attr.name, ret);
goto err;
}
}
if (device->ops.alloc_hw_stats) {
ret = setup_mib_stats(device);
if (ret) {
dev_err(&device->dev,
"failed to setup roh mib stats, ret = %d.\n", ret);
goto err;
}
}
return 0;
err:
remove_device_sysfs(device);
return ret;
}
void roh_device_unregister_sysfs(struct roh_device *device)
{
if (device->hw_stats_ag)
free_hsag(&device->dev.kobj, device->hw_stats_ag);
free_hw_stats(device);
remove_device_sysfs(device);
}