Commit aa4a5ce0 authored by Ke Chen's avatar Ke Chen Committed by Wang Wensheng
Browse files

roh/core: Add roh device sysfs node

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I5WKYW



-----------------------------------------------------------------------

Provide roh device sysfs file operation node, include querying
the EID, GUID, link status and roh mib statistics.
The hns dirver provides some verbs for the roh core to set or
query the hardware information.

Signed-off-by: default avatarKe Chen <chenke54@huawei.com>
Reviewed-by: default avatarGang Zhang <gang.zhang@huawei.com>
Reviewed-by: default avatarYefeng Yan <yanyefeng@huawei.com>
Reviewed-by: default avatarJingchao Dai <daijingchao1@huawei.com>
parent 1cb9fff3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,5 +3,5 @@
# Makefile for the Linux kernel ROH Core drivers.
#

roh_core-objs := main.o core.o
roh_core-objs := main.o core.o sysfs.o
obj-$(CONFIG_ROH) += roh_core.o
+116 −0
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@ struct roh_device *roh_alloc_device(size_t size)

	mutex_init(&device->unregistration_lock);

	mutex_init(&device->eid_mutex);

	xa_init_flags(&device->client_data, XA_FLAGS_ALLOC);
	init_rwsem(&device->client_data_rwsem);
	init_completion(&device->unreg_completion);
@@ -191,6 +193,20 @@ static int assign_name(struct roh_device *device)
	return ret;
}

static int setup_device(struct roh_device *device)
{
	int ret;

	/* Query GUID */
	ret = device->ops.query_guid(device, &device->node_guid);
	if (ret) {
		pr_err("failed to query guid, ret = %d\n", ret);
		return ret;
	}

	return 0;
}

static void disable_device(struct roh_device *device)
{
	u32 cid;
@@ -247,6 +263,12 @@ int roh_register_device(struct roh_device *device)
		return ret;
	}

	ret = setup_device(device);
	if (ret) {
		pr_err("roh_core: failed to setup device, ret = %d\n", ret);
		return ret;
	}

	dev_set_uevent_suppress(&device->dev, true);
	ret = device_add(&device->dev);
	if (ret) {
@@ -254,6 +276,10 @@ int roh_register_device(struct roh_device *device)
		goto out;
	}

	ret = roh_device_register_sysfs(device);
	if (ret)
		goto err_dev_cleanup;

	ret = enable_device_and_get(device);
	dev_set_uevent_suppress(&device->dev, false);
	kobject_uevent(&device->dev.kobj, KOBJ_ADD);
@@ -266,6 +292,9 @@ int roh_register_device(struct roh_device *device)
	roh_device_put(device);

	return 0;

err_dev_cleanup:
	device_del(&device->dev);
out:
	dev_set_uevent_suppress(&device->dev, false);
	return ret;
@@ -279,6 +308,7 @@ static void __roh_unregister_device(struct roh_device *device)
		goto out;

	disable_device(device);
	roh_device_unregister_sysfs(device);
	device_del(&device->dev);

out:
@@ -458,6 +488,92 @@ void roh_unregister_client(struct roh_client *client)
	remove_client_id(client);
}

static int roh_set_pf_mac_by_eid(struct roh_device *device,
				 struct roh_eid_attr *eid_attr)
{
	const struct net_device_ops *ndev_ops;
	u32 eid = eid_attr->base;
	struct net_device *ndev;
	struct sockaddr s_addr;
	u8 mac[ETH_ALEN];
	int ret;

	ndev = device->netdev;
	if (!ndev)
		return -EINVAL;

	ndev_ops = ndev->netdev_ops;
	if (!ndev_ops->ndo_set_mac_address)
		return -EOPNOTSUPP;

	convert_eid_to_mac(mac, eid);

	s_addr.sa_family = ndev->type;
	memcpy(s_addr.sa_data, mac, ndev->addr_len);

	ret = dev_set_mac_address(ndev, &s_addr, NULL);
	if (ret) {
		netdev_err(ndev, "failed to set dev %s mac, ret = %d\n",
			   ndev->name, ret);
		return ret;
	}

	return 0;
}

static int roh_set_mac_by_eid(struct roh_device *device,
			      struct roh_eid_attr *eid_attr)
{
	int ret;

	ret = roh_set_pf_mac_by_eid(device, eid_attr);
	if (ret) {
		pr_err("failed to set pf mac, ret = %d\n", ret);
		return ret;
	}

	return 0;
}

int roh_device_set_eid(struct roh_device *device, struct roh_eid_attr *attr)
{
	int ret;

	if (!device->ops.set_eid)
		return -EPROTONOSUPPORT;

	mutex_lock(&device->eid_mutex);
	/* Update current EID */
	ret = device->ops.set_eid(device, attr);
	if (ret) {
		mutex_unlock(&device->eid_mutex);
		return ret;
	}

	device->eid = *attr;
	ret = roh_set_mac_by_eid(device, attr);
	mutex_unlock(&device->eid_mutex);

	return ret;
}

void roh_device_get_eid(struct roh_device *device, struct roh_eid_attr *attr)
{
	mutex_lock(&device->eid_mutex);
	memcpy(attr, &device->eid, sizeof(*attr));
	mutex_unlock(&device->eid_mutex);
}

void roh_device_query_guid(struct roh_device *device, struct roh_guid_attr *attr)
{
	memcpy(attr, &device->node_guid, sizeof(*attr));
}

enum roh_link_status roh_device_query_link_status(struct roh_device *device)
{
	return device->link_status;
}

int roh_core_init(void)
{
	int ret;
+11 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ enum roh_dev_tx {
	ROHDEV_TX_LOCKED = 0x20 /* driver tx lock was already taken */
};

enum roh_link_status { ROH_LINK_DOWN = 0, ROH_LINK_UP };

enum roh_mib_type { ROH_MIB_PUBLIC = 0, ROH_MIB_PRIVATE };

static inline void convert_eid_to_mac(u8 mac[6], u32 eid)
@@ -82,6 +84,15 @@ struct roh_device {
	refcount_t refcount;
	struct completion unreg_completion;
	struct mutex unregistration_lock; /* lock for unregiste */

	struct roh_guid_attr node_guid;
	struct roh_eid_attr eid;
	struct mutex eid_mutex; /* operate eid needs to be mutexed */
	u32 link_status;

	struct attribute_group *hw_stats_ag;
	struct roh_mib_stats *hw_public_stats;
	struct roh_mib_stats *hw_private_stats;
};

static inline bool roh_device_try_get(struct roh_device *device)
+9 −0
Original line number Diff line number Diff line
@@ -18,4 +18,13 @@ void roh_unregister_client(struct roh_client *client);
void roh_set_client_data(struct roh_device *device,
			 struct roh_client *client, void *data);

int roh_device_register_sysfs(struct roh_device *device);
void roh_device_unregister_sysfs(struct roh_device *device);

int roh_device_set_eid(struct roh_device *device, struct roh_eid_attr *attr);
void roh_device_get_eid(struct roh_device *device, struct roh_eid_attr *attr);

void roh_device_query_guid(struct roh_device *device, struct roh_guid_attr *attr);
enum roh_link_status roh_device_query_link_status(struct roh_device *device);

#endif /* __CORE_PRIV_H__ */
+270 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2020-2022 Hisilicon Limited.

#include <linux/sysfs.h>
#include <linux/stat.h>
#include <net/sock.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_guid_show(struct device *device,
			      struct device_attribute *attr, char *buf)
{
	struct roh_device *dev = container_of(device, struct roh_device, dev);
	struct roh_guid_attr guid;
	u32 *val = (u32 *)guid.data;

	roh_device_query_guid(dev, &guid);

	return sprintf(buf, "%08x:%08x:%08x:%08x\n", val[0], val[1], val[2], val[3]);
}

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_guid);
static DEVICE_ATTR_RO(node_link_status);

static struct device_attribute *roh_class_attr[] = {
	&dev_attr_node_eid,
	&dev_attr_node_guid,
	&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 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_hw_stats_attribute *hsa;
	struct roh_mib_stats *public_stats;
	struct roh_device *dev;
	int ret;

	hsa = container_of(attr, struct roh_hw_stats_attribute, attr);
	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)
		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_hw_stats_attribute *hsa;
	struct roh_device *dev;
	int ret;

	hsa = container_of(attr, struct roh_hw_stats_attribute, attr);
	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)
		goto unlock;

	ret = print_hw_stat(dev, private_stats, buf);

unlock:
	mutex_unlock(&private_stats->lock);
	return ret;
}

static ssize_t (*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 setup_mib_stats(struct roh_device *device)
{
	struct roh_mib_stats *privite_stats, *public_stats;
	struct attribute_group *hsag;
	struct kobject *kobj;
	int i, j;
	int ret;

	public_stats = device->ops.alloc_hw_stats(device, ROH_MIB_PUBLIC);
	if (!public_stats)
		return;

	privite_stats = device->ops.alloc_hw_stats(device, ROH_MIB_PRIVATE);
	if (!privite_stats) {
		kfree(public_stats);
		return;
	}

	hsag = kzalloc(sizeof(*hsag) +
		       sizeof(void *) * (ARRAY_SIZE(roh_hw_stats_name)),
		       GFP_KERNEL);
	if (!hsag)
		goto err_free_stats;

	ret = device->ops.get_hw_stats(device, public_stats, ROH_MIB_PUBLIC);
	if (ret)
		goto err_free_hsag;

	ret = device->ops.get_hw_stats(device, privite_stats, ROH_MIB_PRIVATE);
	if (ret)
		goto err_free_hsag;

	hsag->name = "node_mib_stats";
	hsag->attrs = (void *)hsag + sizeof(*hsag);

	for (i = 0; i < ARRAY_SIZE(roh_hw_stats_name); i++) {
		hsag->attrs[i] = alloc_hsa(roh_hw_stats_name[i],
					   show_roh_mib_hw_stats[i]);
		if (!hsag->attrs[i])
			goto err;
		sysfs_attr_init(hsag->attrs[i]);
	}

	mutex_init(&privite_stats->lock);
	mutex_init(&public_stats->lock);

	kobj = &device->dev.kobj;
	ret = sysfs_create_group(kobj, hsag);
	if (ret)
		goto err;
	device->hw_stats_ag = hsag;
	device->hw_public_stats = public_stats;
	device->hw_private_stats = privite_stats;

	return;

err:
	for (j = i - 1; j >= 0; j--)
		kfree(hsag->attrs[j]);
err_free_hsag:
	kfree(hsag);
err_free_stats:
	kfree(public_stats);
	kfree(privite_stats);
}

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) {
			pr_err("failed to create node %s, ret = %d.\n",
			       roh_class_attr[i]->attr.name, ret);
			goto err;
		}
	}

	if (device->ops.alloc_hw_stats)
		setup_mib_stats(device);

	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);

	kfree(device->hw_private_stats);
	kfree(device->hw_public_stats);

	remove_device_sysfs(device);
}
Loading