提交 62aede9f 编写于 作者: T Tian Jiang 提交者: Jiantao Xiao

net: hns3: add support customized exception handling interfaces.

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I6QRKC
CVE: NA

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

Establish a framework to support customization requirement. Provides
interfaces to register special processing functions. When the system
is reset due to an abnormal interrupt, the registered handler is
called first.
Signed-off-by: NTian Jiang <jiangtian6@h-partners.com>
Signed-off-by: Nshaojijie <shaojijie@huawei.com>
Signed-off-by: NJiantao Xiao <xiaojiantao1@h-partners.com>
上级 ae344b95
......@@ -12,6 +12,7 @@ obj-$(CONFIG_HNS3) += hnae3.o
obj-$(CONFIG_HNS3_ENET) += hns3.o
hns3-objs = hns3_enet.o hns3_ethtool.o hns3_debugfs.o
hns3-objs += hns3_ext.o
hns3-$(CONFIG_HNS3_DCB) += hns3_dcbnl.o
......@@ -24,6 +25,6 @@ obj-$(CONFIG_HNS3_HCLGE) += hclge.o
hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hclge_sysfs.o \
hns3pf/hclge_mbx.o hns3pf/hclge_err.o hns3pf/hclge_debugfs.o hns3pf/hclge_ptp.o hns3pf/hclge_devlink.o \
hns3_common/hclge_comm_cmd.o hns3_common/hclge_comm_rss.o hns3_common/hclge_comm_tqp_stats.o
hclge-objs += hns3pf/hclge_ext.o
hclge-$(CONFIG_HNS3_DCB) += hns3pf/hclge_dcb.o
......@@ -785,6 +785,8 @@ struct hnae3_ae_ops {
struct ethtool_wolinfo *wol);
int (*set_wol)(struct hnae3_handle *handle,
struct ethtool_wolinfo *wol);
int (*priv_ops)(struct hnae3_handle *handle, int opcode,
void *data, size_t length);
};
struct hnae3_dcb_ops {
......
/* SPDX-License-Identifier: GPL-2.0+ */
// Copyright (c) 2023 Hisilicon Limited.
#ifndef __HNAE3_EXT_H
#define __HNAE3_EXT_H
enum hnae3_event_type_custom {
HNAE3_VF_RESET_CUSTOM,
HNAE3_VF_FUNC_RESET_CUSTOM,
HNAE3_VF_PF_FUNC_RESET_CUSTOM,
HNAE3_VF_FULL_RESET_CUSTOM,
HNAE3_FLR_RESET_CUSTOM,
HNAE3_FUNC_RESET_CUSTOM,
HNAE3_GLOBAL_RESET_CUSTOM,
HNAE3_IMP_RESET_CUSTOM,
HNAE3_UNKNOWN_RESET_CUSTOM,
HNAE3_NONE_RESET_CUSTOM,
HNAE3_PORT_FAULT,
HNAE3_RESET_DONE_CUSTOM,
HNAE3_FUNC_RESET_FAIL_CUSTOM,
HNAE3_GLOBAL_RESET_FAIL_CUSTOM,
HNAE3_IMP_RESET_FAIL_CUSTOM,
HNAE3_PPU_POISON_CUSTOM,
HNAE3_IMP_RD_POISON_CUSTOM,
HNAE3_INVALID_EVENT_CUSTOM,
};
enum hnae3_ext_opcode {
HNAE3_EXT_OPC_RESET,
HNAE3_EXT_OPC_EVENT_CALLBACK,
};
#endif
......@@ -24,6 +24,7 @@
#include <net/geneve.h>
#include "hnae3.h"
#include "hnae3_ext.h"
#include "hns3_enet.h"
/* All hns3 tracepoints are defined by the include below, which
* must be included exactly once across the whole kernel with
......@@ -6002,12 +6003,16 @@ static void hns3_process_hw_error(struct hnae3_handle *handle,
if (hns3_hw_err[i].type == type) {
dev_err(&handle->pdev->dev, "Detected %s!\n",
hns3_hw_err[i].msg);
if (handle->ae_algo->ops->priv_ops)
handle->ae_algo->ops->priv_ops(handle,
HNAE3_EXT_OPC_EVENT_CALLBACK, &type,
sizeof(type));
break;
}
}
}
static const struct hnae3_client_ops client_ops = {
const struct hnae3_client_ops client_ops = {
.init_instance = hns3_client_init,
.uninit_instance = hns3_client_uninit,
.link_status_change = hns3_link_status_change,
......
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2023 Hisilicon Limited.
#include "hns3_ext.h"
int nic_netdev_match_check(struct net_device *ndev)
{
#define HNS3_DRIVER_NAME_LEN 5
struct ethtool_drvinfo drv_info;
struct hnae3_handle *h;
if (!ndev || !ndev->ethtool_ops ||
!ndev->ethtool_ops->get_drvinfo)
return -EINVAL;
ndev->ethtool_ops->get_drvinfo(ndev, &drv_info);
if (strncmp(drv_info.driver, "hns3", HNS3_DRIVER_NAME_LEN))
return -EINVAL;
h = hns3_get_handle(ndev);
if (h->flags & HNAE3_SUPPORT_VF)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(nic_netdev_match_check);
static int nic_invoke_pri_ops(struct net_device *ndev, int opcode,
void *data, size_t length)
{
struct hnae3_handle *h;
int ret;
if ((!data && length) || (data && !length)) {
netdev_err(ndev, "failed to check data and length");
return -EINVAL;
}
if (nic_netdev_match_check(ndev))
return -ENODEV;
h = hns3_get_handle(ndev);
if (!h->ae_algo->ops->priv_ops)
return -EOPNOTSUPP;
ret = h->ae_algo->ops->priv_ops(h, opcode, data, length);
if (ret)
netdev_err(ndev,
"failed to invoke pri ops, opcode = %#x, ret = %d\n",
opcode, ret);
return ret;
}
void nic_chip_recover_handler(struct net_device *ndev,
enum hnae3_event_type_custom event_t)
{
dev_info(&ndev->dev, "reset type is %d!!\n", event_t);
if (event_t == HNAE3_PPU_POISON_CUSTOM)
event_t = HNAE3_FUNC_RESET_CUSTOM;
if (event_t != HNAE3_FUNC_RESET_CUSTOM &&
event_t != HNAE3_GLOBAL_RESET_CUSTOM &&
event_t != HNAE3_IMP_RESET_CUSTOM) {
dev_err(&ndev->dev, "reset type err!!\n");
return;
}
nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_RESET, &event_t, sizeof(event_t));
}
EXPORT_SYMBOL(nic_chip_recover_handler);
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (c) 2023 Hisilicon Limited. */
#ifndef __HNS3_EXT_H
#define __HNS3_EXT_H
#include <linux/types.h>
#include "hns3_enet.h"
#include "hnae3_ext.h"
int nic_netdev_match_check(struct net_device *netdev);
void nic_chip_recover_handler(struct net_device *ndev,
enum hnae3_event_type_custom event_t);
#endif
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2023 Hisilicon Limited.
#include "hclge_main.h"
#include "hnae3.h"
#include "hnae3_ext.h"
#include "hclge_cmd.h"
#include "hclge_ext.h"
static nic_event_fn_t nic_event_call;
/* We use a lock to ensure that the address of the nic_event_call function
* is valid when it is called. Avoid null pointer exceptions caused by
* external unregister during invoking.
*/
static DEFINE_MUTEX(hclge_nic_event_lock);
static int hclge_set_reset_task(struct hclge_dev *hdev, void *data,
size_t length)
{
u32 *reset_level = (u32 *)data;
if (length != sizeof(u32))
return -EINVAL;
dev_warn(&hdev->pdev->dev, "reset level is %u\n", *reset_level);
/* request reset & schedule reset task */
set_bit(*reset_level, &hdev->reset_request);
hclge_reset_task_schedule(hdev);
return 0;
}
int hclge_ext_call_event(struct hclge_dev *hdev,
enum hnae3_event_type_custom event_t)
{
if (event_t >= HNAE3_INVALID_EVENT_CUSTOM)
return -EINVAL;
mutex_lock(&hclge_nic_event_lock);
if (!nic_event_call) {
mutex_unlock(&hclge_nic_event_lock);
return -EOPNOTSUPP;
}
nic_event_call(hdev->vport[0].nic.netdev, event_t);
mutex_unlock(&hclge_nic_event_lock);
return 0;
}
int nic_register_event(nic_event_fn_t event_call)
{
if (!event_call) {
pr_err("hns3: register event handle is null\n");
return -EINVAL;
}
mutex_lock(&hclge_nic_event_lock);
if (nic_event_call) {
mutex_unlock(&hclge_nic_event_lock);
pr_err("hns3: event already register\n");
return -EBUSY;
}
nic_event_call = event_call;
mutex_unlock(&hclge_nic_event_lock);
pr_info("hns3: event register success\n");
return 0;
}
EXPORT_SYMBOL(nic_register_event);
int nic_unregister_event(void)
{
mutex_lock(&hclge_nic_event_lock);
nic_event_call = NULL;
mutex_unlock(&hclge_nic_event_lock);
pr_info("hns3: event unregister success\n");
return 0;
}
EXPORT_SYMBOL(nic_unregister_event);
static int hclge_nic_call_event(struct hclge_dev *hdev, void *data,
size_t length)
{
#define ERROR_EVENT_TYPE_NUM 3
u32 event_type[ERROR_EVENT_TYPE_NUM] = {
HNAE3_PPU_POISON_CUSTOM,
HNAE3_IMP_RESET_CUSTOM,
HNAE3_IMP_RD_POISON_CUSTOM
};
u32 *index = (u32 *)data;
if (length != sizeof(u32))
return -EINVAL;
if ((*index) >= ERROR_EVENT_TYPE_NUM)
return 0;
return hclge_ext_call_event(hdev, event_type[*index]);
}
static enum hnae3_event_type_custom
hclge_get_reset_fail_type(enum hnae3_reset_type reset_type)
{
const struct hclge_reset_fail_type_map fail_type_map[] = {
{HNAE3_FUNC_RESET, HNAE3_FUNC_RESET_FAIL_CUSTOM},
{HNAE3_GLOBAL_RESET, HNAE3_GLOBAL_RESET_FAIL_CUSTOM},
{HNAE3_IMP_RESET, HNAE3_IMP_RESET_FAIL_CUSTOM},
};
u32 i;
for (i = 0; i < ARRAY_SIZE(fail_type_map); i++)
if (fail_type_map[i].reset_type == reset_type)
return fail_type_map[i].custom_type;
return HNAE3_INVALID_EVENT_CUSTOM;
}
static void hclge_report_reset_fail_custom(struct hclge_dev *hdev)
{
#define HCLGE_RESET_MAX_FAIL_CNT_CUSTOM 1
u32 max_fail_custom_cnt = HCLGE_RESET_MAX_FAIL_CNT;
mutex_lock(&hclge_nic_event_lock);
if (nic_event_call)
max_fail_custom_cnt = HCLGE_RESET_MAX_FAIL_CNT_CUSTOM;
mutex_unlock(&hclge_nic_event_lock);
if (hdev->rst_stats.reset_fail_cnt < max_fail_custom_cnt)
return;
dev_err(&hdev->pdev->dev, "failed to report reset!\n");
hclge_ext_call_event(hdev, hclge_get_reset_fail_type(hdev->reset_type));
}
void hclge_ext_reset_end(struct hclge_dev *hdev, bool done)
{
if (!done) {
hclge_report_reset_fail_custom(hdev);
return;
}
hclge_ext_call_event(hdev, HNAE3_RESET_DONE_CUSTOM);
dev_info(&hdev->pdev->dev, "report reset done!\n");
}
static const hclge_priv_ops_fn hclge_ext_func_arr[] = {
[HNAE3_EXT_OPC_RESET] = hclge_set_reset_task,
[HNAE3_EXT_OPC_EVENT_CALLBACK] = hclge_nic_call_event,
};
int hclge_ext_ops_handle(struct hnae3_handle *handle, int opcode,
void *data, size_t length)
{
struct hclge_vport *vport = hclge_get_vport(handle);
int cmd_num = ARRAY_SIZE(hclge_ext_func_arr);
struct hclge_dev *hdev = vport->back;
hclge_priv_ops_fn ext_opcode_func;
if (opcode >= cmd_num) {
dev_err(&hdev->pdev->dev, "invalid opcode %d\n", opcode);
return -EINVAL;
}
ext_opcode_func = hclge_ext_func_arr[opcode];
if (!ext_opcode_func) {
dev_err(&hdev->pdev->dev, "unsupported opcode %d\n", opcode);
return -EOPNOTSUPP;
}
return ext_opcode_func(hdev, data, length);
}
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (c) 2016-2017 Hisilicon Limited. */
#ifndef __HCLGE_EXT_H
#define __HCLGE_EXT_H
#include <linux/types.h>
struct hclge_reset_fail_type_map {
enum hnae3_reset_type reset_type;
enum hnae3_event_type_custom custom_type;
};
typedef int (*hclge_priv_ops_fn)(struct hclge_dev *hdev, void *data,
size_t length);
/**
* nic_event_fn_t - nic event handler prototype
* @netdev: net device
* @hnae3_event_type_custom: nic device event type
*/
typedef void (*nic_event_fn_t) (struct net_device *netdev,
enum hnae3_event_type_custom);
/**
* nic_register_event - register for nic event handling
* @event_call: nic event handler
* return 0 - success , negative - fail
*/
int nic_register_event(nic_event_fn_t event_call);
/**
* nic_unregister_event - unregister for nic event handling
* return 0 - success , negative - fail
*/
int nic_unregister_event(void);
int hclge_ext_call_event(struct hclge_dev *hdev,
enum hnae3_event_type_custom event_t);
void hclge_ext_reset_end(struct hclge_dev *hdev, bool done);
int hclge_ext_ops_handle(struct hnae3_handle *handle, int opcode,
void *data, size_t length);
#endif
......@@ -18,6 +18,7 @@
#include <net/vxlan.h>
#include "hclge_cmd.h"
#include "hclge_dcb.h"
#include "hclge_ext.h"
#include "hclge_main.h"
#include "hclge_mbx.h"
#include "hclge_mdio.h"
......@@ -36,7 +37,6 @@
#define BUF_MAX_PERCENT 100
#define BUF_RESERVE_PERCENT 90
#define HCLGE_RESET_MAX_FAIL_CNT 5
#define HCLGE_RESET_SYNC_TIME 100
#define HCLGE_PF_RESET_SYNC_TIME 20
#define HCLGE_PF_RESET_SYNC_CNT 1500
......@@ -72,6 +72,7 @@ static void hclge_sync_mac_table(struct hclge_dev *hdev);
static void hclge_restore_hw_table(struct hclge_dev *hdev);
static void hclge_sync_promisc_mode(struct hclge_dev *hdev);
static void hclge_sync_fd_table(struct hclge_dev *hdev);
static void hclge_reset_end(struct hnae3_handle *handle, bool done);
static struct hnae3_ae_algo ae_algo;
......@@ -2922,7 +2923,7 @@ static void hclge_mbx_task_schedule(struct hclge_dev *hdev)
}
}
static void hclge_reset_task_schedule(struct hclge_dev *hdev)
void hclge_reset_task_schedule(struct hclge_dev *hdev)
{
if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) &&
test_bit(HCLGE_STATE_SERVICE_INITED, &hdev->state) &&
......@@ -4139,7 +4140,7 @@ static void hclge_show_rst_info(struct hclge_dev *hdev)
static bool hclge_reset_err_handle(struct hclge_dev *hdev)
{
#define MAX_RESET_FAIL_CNT 5
struct hnae3_handle *handle = &hdev->vport[0].nic;
if (hdev->reset_pending) {
dev_info(&hdev->pdev->dev, "Reset pending %lu\n",
......@@ -4151,7 +4152,7 @@ static bool hclge_reset_err_handle(struct hclge_dev *hdev)
"reset failed because new reset interrupt\n");
hclge_clear_reset_cause(hdev);
return false;
} else if (hdev->rst_stats.reset_fail_cnt < MAX_RESET_FAIL_CNT) {
} else if (hdev->rst_stats.reset_fail_cnt < HCLGE_RESET_MAX_FAIL_CNT) {
hdev->rst_stats.reset_fail_cnt++;
set_bit(hdev->reset_type, &hdev->reset_pending);
dev_info(&hdev->pdev->dev,
......@@ -4165,7 +4166,7 @@ static bool hclge_reset_err_handle(struct hclge_dev *hdev)
/* recover the handshake status when reset fail */
hclge_reset_handshake(hdev, true);
dev_err(&hdev->pdev->dev, "Reset fail!\n");
hclge_reset_end(handle, false);
hclge_show_rst_info(hdev);
......@@ -4286,6 +4287,7 @@ static int hclge_reset_prepare(struct hclge_dev *hdev)
static int hclge_reset_rebuild(struct hclge_dev *hdev)
{
struct hnae3_handle *handle = &hdev->vport[0].nic;
int ret;
hdev->rst_stats.hw_reset_done_cnt++;
......@@ -4347,6 +4349,8 @@ static int hclge_reset_rebuild(struct hclge_dev *hdev)
hclge_update_reset_level(hdev);
hclge_reset_end(handle, true);
return 0;
}
......@@ -4368,10 +4372,11 @@ static void hclge_reset(struct hclge_dev *hdev)
hclge_reset_task_schedule(hdev);
}
static void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle)
void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
struct hclge_dev *hdev = ae_dev->priv;
int ret;
/* We might end up getting called broadly because of 2 below cases:
* 1. Recoverable error was conveyed through APEI and only way to bring
......@@ -4405,9 +4410,12 @@ static void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle)
dev_info(&hdev->pdev->dev, "received reset event, reset type is %d\n",
hdev->reset_level);
/* request reset & schedule reset task */
set_bit(hdev->reset_level, &hdev->reset_request);
hclge_reset_task_schedule(hdev);
ret = hclge_ext_call_event(hdev, hdev->reset_level);
if (ret) {
/* request reset & schedule reset task */
set_bit(hdev->reset_level, &hdev->reset_request);
hclge_reset_task_schedule(hdev);
}
if (hdev->reset_level < HNAE3_GLOBAL_RESET)
hdev->reset_level++;
......@@ -4433,7 +4441,15 @@ static void hclge_reset_timer(struct timer_list *t)
dev_info(&hdev->pdev->dev,
"triggering reset in reset timer\n");
hclge_reset_event(hdev->pdev, NULL);
hclge_reset_event(hdev->pdev, &hdev->vport[0].nic);
}
static void hclge_reset_end(struct hnae3_handle *handle, bool done)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
hclge_ext_reset_end(hdev, done);
}
static void hclge_reset_subtask(struct hclge_dev *hdev)
......@@ -4473,8 +4489,8 @@ static void hclge_handle_err_reset_request(struct hclge_dev *hdev)
hclge_set_def_reset_request(ae_dev, reset_type);
}
if (hdev->default_reset_request && ae_dev->ops->reset_event)
ae_dev->ops->reset_event(hdev->pdev, NULL);
if (hdev->default_reset_request)
hclge_reset_event(hdev->pdev, &hdev->vport[0].nic);
/* enable interrupt after error handling complete */
hclge_enable_vector(&hdev->misc_vector, true);
......@@ -13753,7 +13769,7 @@ static int hclge_get_dscp_prio(struct hnae3_handle *h, u8 dscp, u8 *tc_mode,
return 0;
}
static const struct hnae3_ae_ops hclge_ops = {
struct hnae3_ae_ops hclge_ops = {
.init_ae_dev = hclge_init_ae_dev,
.uninit_ae_dev = hclge_uninit_ae_dev,
.reset_prepare = hclge_reset_prepare_general,
......@@ -13860,6 +13876,7 @@ static const struct hnae3_ae_ops hclge_ops = {
.get_dscp_prio = hclge_get_dscp_prio,
.get_wol = hclge_get_wol,
.set_wol = hclge_set_wol,
.priv_ops = hclge_ext_ops_handle,
};
static struct hnae3_ae_algo ae_algo = {
......
......@@ -12,7 +12,7 @@
#include "hclge_cmd.h"
#include "hclge_ptp.h"
#include "hnae3.h"
#include "hnae3_ext.h"
#include "hclge_comm_rss.h"
#include "hclge_comm_tqp_stats.h"
......@@ -26,6 +26,8 @@
#define HCLGE_RD_FIRST_STATS_NUM 2
#define HCLGE_RD_OTHER_STATS_NUM 4
#define HCLGE_RESET_MAX_FAIL_CNT 5
#define HCLGE_INVALID_VPORT 0xffff
#define HCLGE_PF_CFG_BLOCK_SIZE 32
......@@ -1159,4 +1161,6 @@ int hclge_cfg_mac_speed_dup_hw(struct hclge_dev *hdev, int speed, u8 duplex,
int hclge_get_wol_supported_mode(struct hclge_dev *hdev, u32 *wol_supported);
int hclge_get_wol_cfg(struct hclge_dev *hdev, u32 *mode);
struct hclge_vport *hclge_get_vf_vport(struct hclge_dev *hdev, int vf);
void hclge_reset_task_schedule(struct hclge_dev *hdev);
void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle);
#endif
......@@ -811,7 +811,7 @@ static void hclge_handle_ncsi_error(struct hclge_dev *hdev)
ae_dev->ops->set_default_reset_request(ae_dev, HNAE3_GLOBAL_RESET);
dev_warn(&hdev->pdev->dev, "requesting reset due to NCSI error\n");
ae_dev->ops->reset_event(hdev->pdev, NULL);
hclge_reset_event(hdev->pdev, &hdev->vport[0].nic);
}
static void hclge_handle_vf_tbl(struct hclge_vport *vport,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册