未验证 提交 f7990942 编写于 作者: O openeuler-ci-bot 提交者: Gitee

!191 roh/core: Add ROH device driver

Merge Pull Request from: @zhengzengkai 
 
```
[Description]​
The ROH module driver consists of the ROH Core and ROH DRV
modules, which work with hardware to implement communication
between nodes through HCCS packets.
ROH Core is a protocol stack of the ROH architecture. It provides
related services for upper layers by invoking operation interfaces
provided by the ROH DRV.
The ROH DRV implements the lower layer functions of the ROH
featureand provides a series of interfaces for operating hardware
for the ROH Core. 

This patch adds ROH device driver support, including:
1. ROH Core initialization
2. Provide roh device registration framework.
3. etc

[Testing]
kernel options:
CONFIG_ROH=m

Test passed with below step:
1. load the roh_core.ko
# insmod roh_core.ko
2. There is no error when roh_core.ko is loaded,
confirm that ko is loaded normally:
# lsmod | grep roh_core
roh_core 24576 0 - Live 0xffff800008f07000
3. View the sysfs file and confirm that the roh
directory has been generated:
# ls /sys/class/ | grep roh
roh
4.Uninstall roh_core.ko without any errors
```
 
 
Link:https://gitee.com/openeuler/kernel/pulls/191 
Reviewed-by: Ling Mingqiang <lingmingqiang@huawei.com> 
Signed-off-by: Xie XiuQi <xiexiuqi@huawei.com> 
......@@ -237,4 +237,7 @@ source "drivers/interconnect/Kconfig"
source "drivers/counter/Kconfig"
source "drivers/most/Kconfig"
source "drivers/roh/Kconfig"
endmenu
......@@ -191,3 +191,4 @@ obj-$(CONFIG_GNSS) += gnss/
obj-$(CONFIG_INTERCONNECT) += interconnect/
obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_ROH) += roh/
# SPDX-License-Identifier: GPL-2.0+
menuconfig ROH
tristate "ROH support"
depends on HAS_IOMEM && HAS_DMA
depends on NET
depends on INET
depends on m
select IRQ_POLL
select DIMLIB
help
Core support for ROH. Make sure to also select
any protocols you wish to use as well as drivers
for your ROH hardware.
To compile ROH core as module, choose M here.
# SPDX-License-Identifier: GPL-2.0+
#
# Makefile for the Linux kernel ROH device drivers.
#
obj-$(CONFIG_ROH) += core/
# SPDX-License-Identifier: GPL-2.0+
#
# Makefile for the Linux kernel ROH Core drivers.
#
roh_core-objs := main.o core.o
obj-$(CONFIG_ROH) += roh_core.o
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2022 Hisilicon Limited.
#include <linux/pci.h>
#include <net/rtnetlink.h>
#include "core.h"
#include "core_priv.h"
static DEFINE_XARRAY_FLAGS(devices, XA_FLAGS_ALLOC);
static DECLARE_RWSEM(devices_rwsem);
#define DEVICE_REGISTERED XA_MARK_1
static u32 highest_client_id;
#define CLIENT_REGISTERED XA_MARK_1
static DEFINE_XARRAY_FLAGS(clients, XA_FLAGS_ALLOC);
static DECLARE_RWSEM(clients_rwsem);
static void roh_client_put(struct roh_client *client)
{
if (refcount_dec_and_test(&client->uses))
complete(&client->uses_zero);
}
#define CLIENT_DATA_REGISTERED XA_MARK_1
static int add_client_context(struct roh_device *device,
struct roh_client *client);
static void remove_client_context(struct roh_device *device,
unsigned int client_id);
static void __roh_unregister_device(struct roh_device *device);
static void roh_device_release(struct device *device)
{
struct roh_device *dev = container_of(device, struct roh_device, dev);
WARN_ON(refcount_read(&dev->refcount));
mutex_destroy(&dev->unregistration_lock);
xa_destroy(&dev->client_data);
kfree(dev);
}
static int roh_device_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct roh_device *dev = container_of(device, struct roh_device, dev);
if (add_uevent_var(env, "NAME=%s", dev->name)) {
pr_err("failed to do add_uevent_var.\n");
return -ENOMEM;
}
return 0;
}
static struct class roh_class = {
.name = "roh",
.dev_release = roh_device_release,
.dev_uevent = roh_device_uevent,
};
struct roh_device *roh_alloc_device(size_t size)
{
struct roh_device *device;
if (WARN_ON(size < sizeof(struct roh_device)))
return NULL;
device = kzalloc(size, GFP_KERNEL);
if (!device)
return NULL;
device->dev.class = &roh_class;
device_initialize(&device->dev);
dev_set_drvdata(&device->dev, device);
mutex_init(&device->unregistration_lock);
xa_init_flags(&device->client_data, XA_FLAGS_ALLOC);
init_rwsem(&device->client_data_rwsem);
init_completion(&device->unreg_completion);
return device;
}
EXPORT_SYMBOL(roh_alloc_device);
void roh_dealloc_device(struct roh_device *device)
{
down_write(&devices_rwsem);
if (xa_load(&devices, device->index) == device)
xa_erase(&devices, device->index);
up_write(&devices_rwsem);
WARN_ON(!xa_empty(&device->client_data));
WARN_ON(refcount_read(&device->refcount));
put_device(&device->dev);
}
EXPORT_SYMBOL(roh_dealloc_device);
static int alloc_name(struct roh_device *device)
{
struct roh_device *dev;
unsigned long index;
struct ida inuse;
int rc;
int i;
lockdep_assert_held_write(&devices_rwsem);
ida_init(&inuse);
xa_for_each(&devices, index, dev) {
char buf[ROH_DEVICE_NAME_MAX];
if (sscanf(dev_name(&dev->dev), device->name, &i) != 1)
continue;
if (i < 0 || i >= INT_MAX)
continue;
rc = snprintf(buf, sizeof(buf), device->name, i);
if (rc >= sizeof(buf) || rc < 0) {
ida_destroy(&inuse);
pr_err("device name is too long.\n");
return -EINVAL;
}
if (strcmp(buf, dev_name(&dev->dev)) != 0)
continue;
rc = ida_alloc_range(&inuse, i, i, GFP_KERNEL);
if (rc < 0)
goto out;
}
rc = ida_alloc(&inuse, GFP_KERNEL);
if (rc < 0)
goto out;
rc = dev_set_name(&device->dev, device->name, rc);
out:
ida_destroy(&inuse);
return rc;
}
static void roh_device_put(struct roh_device *device)
{
if (refcount_dec_and_test(&device->refcount))
complete(&device->unreg_completion);
}
struct roh_device *__roh_device_get_by_name(const char *name)
{
struct roh_device *device;
unsigned long index;
xa_for_each(&devices, index, device)
if (!strcmp(name, dev_name(&device->dev)))
return device;
return NULL;
}
static int assign_name(struct roh_device *device)
{
static u32 last_id;
int ret;
down_write(&devices_rwsem);
if (strchr(device->name, '%'))
ret = alloc_name(device);
else
ret = dev_set_name(&device->dev, device->name);
if (ret)
goto out;
if (__roh_device_get_by_name(dev_name(&device->dev))) {
ret = -ENFILE;
goto out;
}
strlcpy(device->name, dev_name(&device->dev), ROH_DEVICE_NAME_MAX);
ret = xa_alloc_cyclic(&devices, &device->index, device, xa_limit_31b,
&last_id, GFP_KERNEL);
if (ret > 0)
ret = 0;
out:
up_write(&devices_rwsem);
return ret;
}
static void disable_device(struct roh_device *device)
{
u32 cid;
WARN_ON(!refcount_read(&device->refcount));
down_write(&devices_rwsem);
xa_clear_mark(&devices, device->index, DEVICE_REGISTERED);
up_write(&devices_rwsem);
down_read(&clients_rwsem);
cid = highest_client_id;
up_read(&clients_rwsem);
while (cid) {
cid--;
remove_client_context(device, cid);
}
roh_device_put(device);
wait_for_completion(&device->unreg_completion);
}
static int enable_device_and_get(struct roh_device *device)
{
struct roh_client *client;
unsigned long index;
int ret = 0;
refcount_set(&device->refcount, MAX_DEVICE_REFCOUNT);
down_write(&devices_rwsem);
xa_set_mark(&devices, device->index, DEVICE_REGISTERED);
downgrade_write(&devices_rwsem);
down_read(&clients_rwsem);
xa_for_each_marked(&clients, index, client, CLIENT_REGISTERED) {
ret = add_client_context(device, client);
if (ret)
break;
}
up_read(&clients_rwsem);
up_read(&devices_rwsem);
return ret;
}
int roh_register_device(struct roh_device *device)
{
int ret;
ret = assign_name(device);
if (ret) {
pr_err("roh_core: failed to assigne name, ret = %d\n", ret);
return ret;
}
dev_set_uevent_suppress(&device->dev, true);
ret = device_add(&device->dev);
if (ret) {
pr_err("roh_core: failed to add device, ret = %d\n", ret);
goto out;
}
ret = enable_device_and_get(device);
dev_set_uevent_suppress(&device->dev, false);
kobject_uevent(&device->dev.kobj, KOBJ_ADD);
if (ret) {
roh_device_put(device);
__roh_unregister_device(device);
return ret;
}
roh_device_put(device);
return 0;
out:
dev_set_uevent_suppress(&device->dev, false);
return ret;
}
EXPORT_SYMBOL(roh_register_device);
static void __roh_unregister_device(struct roh_device *device)
{
mutex_lock(&device->unregistration_lock);
if (!refcount_read(&device->refcount))
goto out;
disable_device(device);
device_del(&device->dev);
out:
mutex_unlock(&device->unregistration_lock);
}
void roh_unregister_device(struct roh_device *device)
{
get_device(&device->dev);
__roh_unregister_device(device);
put_device(&device->dev);
}
EXPORT_SYMBOL(roh_unregister_device);
void roh_set_client_data(struct roh_device *device, struct roh_client *client,
void *data)
{
void *rc;
if (WARN_ON(IS_ERR(data)))
data = NULL;
rc = xa_store(&device->client_data, client->client_id, data,
GFP_KERNEL);
WARN_ON(xa_is_err(rc));
}
static void remove_client_context(struct roh_device *device,
unsigned int client_id)
{
struct roh_client *client;
void *client_data;
down_write(&device->client_data_rwsem);
if (!xa_get_mark(&device->client_data, client_id,
CLIENT_DATA_REGISTERED)) {
up_write(&device->client_data_rwsem);
return;
}
client_data = xa_load(&device->client_data, client_id);
xa_clear_mark(&device->client_data, client_id, CLIENT_DATA_REGISTERED);
client = xa_load(&clients, client_id);
up_write(&device->client_data_rwsem);
if (client->remove)
client->remove(device, client_data);
xa_erase(&device->client_data, client_id);
roh_device_put(device);
roh_client_put(client);
}
static int add_client_context(struct roh_device *device,
struct roh_client *client)
{
int ret = 0;
down_write(&device->client_data_rwsem);
if (!refcount_inc_not_zero(&client->uses))
goto out_unlock;
refcount_inc(&device->refcount);
if (xa_get_mark(&device->client_data, client->client_id,
CLIENT_DATA_REGISTERED))
goto out;
ret = xa_err(xa_store(&device->client_data, client->client_id, NULL,
GFP_KERNEL));
if (ret)
goto out;
downgrade_write(&device->client_data_rwsem);
if (client->add) {
if (client->add(device)) {
xa_erase(&device->client_data, client->client_id);
up_read(&device->client_data_rwsem);
roh_device_put(device);
roh_client_put(client);
return 0;
}
}
xa_set_mark(&device->client_data, client->client_id,
CLIENT_DATA_REGISTERED);
up_read(&device->client_data_rwsem);
return 0;
out:
roh_device_put(device);
roh_client_put(client);
out_unlock:
up_write(&device->client_data_rwsem);
return ret;
}
static int assign_client_id(struct roh_client *client)
{
int ret;
down_write(&clients_rwsem);
client->client_id = highest_client_id;
ret = xa_insert(&clients, client->client_id, client, GFP_KERNEL);
if (ret)
goto out;
highest_client_id++;
xa_set_mark(&clients, client->client_id, CLIENT_REGISTERED);
out:
up_write(&clients_rwsem);
return ret;
}
static void remove_client_id(struct roh_client *client)
{
down_write(&clients_rwsem);
xa_erase(&clients, client->client_id);
for (; highest_client_id; highest_client_id--)
if (xa_load(&clients, highest_client_id - 1))
break;
up_write(&clients_rwsem);
}
int roh_register_client(struct roh_client *client)
{
struct roh_device *device;
unsigned long index;
int ret;
refcount_set(&client->uses, 1);
init_completion(&client->uses_zero);
ret = assign_client_id(client);
if (ret)
return ret;
down_read(&devices_rwsem);
xa_for_each_marked(&devices, index, device, DEVICE_REGISTERED) {
ret = add_client_context(device, client);
if (ret) {
up_read(&devices_rwsem);
roh_unregister_client(client);
return ret;
}
}
up_read(&devices_rwsem);
return 0;
}
void roh_unregister_client(struct roh_client *client)
{
struct roh_device *device;
unsigned long index;
down_write(&clients_rwsem);
roh_client_put(client);
xa_clear_mark(&clients, client->client_id, CLIENT_REGISTERED);
up_write(&clients_rwsem);
rcu_read_lock();
xa_for_each(&devices, index, device) {
if (!roh_device_try_get(device))
continue;
rcu_read_unlock();
remove_client_context(device, client->client_id);
roh_device_put(device);
rcu_read_lock();
}
rcu_read_unlock();
wait_for_completion(&client->uses_zero);
remove_client_id(client);
}
int roh_core_init(void)
{
int ret;
ret = class_register(&roh_class);
if (ret) {
pr_err("roh_core: couldn't create roh device class.\n");
return ret;
}
return 0;
}
void roh_core_cleanup(void)
{
class_unregister(&roh_class);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
MODULE_DESCRIPTION("ROH Core Driver");
/* SPDX-License-Identifier: GPL-2.0+ */
// Copyright (c) 2022 Hisilicon Limited.
#ifndef __CORE_H__
#define __CORE_H__
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/netdevice.h>
#include <net/bonding.h>
#define ROH_DEVICE_NAME_MAX 64
#define MAX_DEVICE_REFCOUNT 2
enum roh_dev_tx {
ROHDEV_TX_MIN = INT_MIN, /* make sure enum is signed */
ROHDEV_TX_OK = 0x00, /* driver took care of packet */
ROHDEV_TX_BUSY = 0x10, /* driver tx path was busy */
ROHDEV_TX_LOCKED = 0x20 /* driver tx lock was already taken */
};
enum roh_mib_type { ROH_MIB_PUBLIC = 0, ROH_MIB_PRIVATE };
static inline void convert_eid_to_mac(u8 mac[6], u32 eid)
{
mac[0] = 0;
mac[1] = 0;
mac[2] = 0;
mac[3] = (eid >> 16) & 0xff;
mac[4] = (eid >> 8) & 0xff;
mac[5] = eid & 0xff;
}
struct roh_eid_attr {
u32 base;
u32 num;
};
struct roh_guid_attr {
u8 data[16];
};
struct roh_mib_stats {
struct mutex lock; /* Protect values[] */
const char * const *names;
u32 num_counters;
u64 value[];
};
struct roh_device;
struct roh_device_ops {
int (*query_guid)(struct roh_device *device, struct roh_guid_attr *attr);
int (*set_eid)(struct roh_device *device, struct roh_eid_attr *attr);
struct sk_buff *(*pkt_create)(struct net_device *ndev,
u8 *dest_mac, u8 *src_mac, int ptype,
struct roh_guid_attr *guid, u16 eid_nums);
int (*pkt_parse)(struct sk_buff *skb, struct roh_eid_attr *eid_attr, int ptype);
enum roh_dev_tx (*xmit_pkt)(struct roh_device *device, struct sk_buff *skb);
struct roh_mib_stats *(*alloc_hw_stats)(struct roh_device *device, enum roh_mib_type);
int (*get_hw_stats)(struct roh_device *device,
struct roh_mib_stats *stats, enum roh_mib_type);
};
struct roh_device {
struct device dev;
char name[ROH_DEVICE_NAME_MAX];
struct roh_device_ops ops;
u32 abi_ver;
struct rcu_head rcu_head;
struct rw_semaphore client_data_rwsem;
struct xarray client_data;
struct module *owner;
struct net_device *netdev;
u32 index;
/*
* Positive refcount indicates that the device is currently
* registered and cannot be unregistered.
*/
refcount_t refcount;
struct completion unreg_completion;
struct mutex unregistration_lock; /* lock for unregiste */
};
static inline bool roh_device_try_get(struct roh_device *device)
{
return refcount_inc_not_zero(&device->refcount);
}
struct roh_device *__roh_device_get_by_name(const char *name);
struct roh_device *roh_alloc_device(size_t size);
void roh_dealloc_device(struct roh_device *device);
int roh_register_device(struct roh_device *device);
void roh_unregister_device(struct roh_device *device);
int roh_core_init(void);
void roh_core_cleanup(void);
#endif /* __CORE_H__ */
/* SPDX-License-Identifier: GPL-2.0+ */
// Copyright (c) 2022 Hisilicon Limited.
#ifndef __CORE_PRIV_H__
#define __CORE_PRIV_H__
struct roh_client {
char *name;
int (*add)(struct roh_device *device);
void (*remove)(struct roh_device *device, void *client_data);
refcount_t uses;
u32 client_id;
struct completion uses_zero;
};
int roh_register_client(struct roh_client *client);
void roh_unregister_client(struct roh_client *client);
void roh_set_client_data(struct roh_device *device,
struct roh_client *client, void *data);
#endif /* __CORE_PRIV_H__ */
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2022 Hisilicon Limited.
#include <linux/init.h>
#include <linux/module.h>
#include "core.h"
static int __init roh_init(void)
{
int ret;
ret = roh_core_init();
if (ret) {
pr_err("roh_core: roh core init failed.\n");
return ret;
}
return 0;
}
static void __exit roh_cleanup(void)
{
roh_core_cleanup();
}
module_init(roh_init);
module_exit(roh_cleanup);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册