mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00

hbg_irqs is a global array which contains irq statistics. However, the irq statistics of different network ports point to the same global array. As a result, the statistics are incorrect. This patch allocates a statistics array for each network port to prevent the statistics of different network ports from affecting each other. irq statistics are removed from hbg_irq_info. Therefore, all data in hbg_irq_info remains unchanged. Therefore, the input parameter of some functions is changed to const. Fixes: 4d089035fa19 ("net: hibmcge: Add interrupt supported in this module") Signed-off-by: Jijie Shao <shaojijie@huawei.com> Reviewed-by: Simon Horman <horms@kernel.org> Link: https://patch.msgid.link/20250410021327.590362-4-shaojijie@huawei.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
145 lines
4.0 KiB
C
145 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
// Copyright (c) 2024 Hisilicon Limited.
|
|
|
|
#include <linux/interrupt.h>
|
|
#include "hbg_irq.h"
|
|
#include "hbg_hw.h"
|
|
|
|
static void hbg_irq_handle_err(struct hbg_priv *priv,
|
|
const struct hbg_irq_info *irq_info)
|
|
{
|
|
if (irq_info->need_print)
|
|
dev_err(&priv->pdev->dev,
|
|
"receive error interrupt: %s\n", irq_info->name);
|
|
|
|
if (irq_info->need_reset)
|
|
hbg_err_reset_task_schedule(priv);
|
|
}
|
|
|
|
static void hbg_irq_handle_tx(struct hbg_priv *priv,
|
|
const struct hbg_irq_info *irq_info)
|
|
{
|
|
napi_schedule(&priv->tx_ring.napi);
|
|
}
|
|
|
|
static void hbg_irq_handle_rx(struct hbg_priv *priv,
|
|
const struct hbg_irq_info *irq_info)
|
|
{
|
|
napi_schedule(&priv->rx_ring.napi);
|
|
}
|
|
|
|
static void hbg_irq_handle_rx_buf_val(struct hbg_priv *priv,
|
|
const struct hbg_irq_info *irq_info)
|
|
{
|
|
priv->stats.rx_fifo_less_empty_thrsld_cnt++;
|
|
}
|
|
|
|
#define HBG_IRQ_I(name, handle) \
|
|
{#name, HBG_INT_MSK_##name##_B, false, false, false, handle}
|
|
#define HBG_ERR_IRQ_I(name, need_print, ndde_reset) \
|
|
{#name, HBG_INT_MSK_##name##_B, true, need_print, \
|
|
ndde_reset, hbg_irq_handle_err}
|
|
|
|
static const struct hbg_irq_info hbg_irqs[] = {
|
|
HBG_IRQ_I(RX, hbg_irq_handle_rx),
|
|
HBG_IRQ_I(TX, hbg_irq_handle_tx),
|
|
HBG_ERR_IRQ_I(TX_PKT_CPL, true, true),
|
|
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true, true),
|
|
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true, true),
|
|
HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true, true),
|
|
HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true, true),
|
|
HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true, true),
|
|
HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true, false),
|
|
HBG_ERR_IRQ_I(TX_AHB_ERR, true, true),
|
|
HBG_IRQ_I(RX_BUF_AVL, hbg_irq_handle_rx_buf_val),
|
|
HBG_ERR_IRQ_I(REL_BUF_ERR, true, false),
|
|
HBG_ERR_IRQ_I(TXCFG_AVL, false, false),
|
|
HBG_ERR_IRQ_I(TX_DROP, false, false),
|
|
HBG_ERR_IRQ_I(RX_DROP, false, false),
|
|
HBG_ERR_IRQ_I(RX_AHB_ERR, true, false),
|
|
HBG_ERR_IRQ_I(MAC_FIFO_ERR, true, true),
|
|
HBG_ERR_IRQ_I(RBREQ_ERR, true, true),
|
|
HBG_ERR_IRQ_I(WE_ERR, true, true),
|
|
};
|
|
|
|
static irqreturn_t hbg_irq_handle(int irq_num, void *p)
|
|
{
|
|
const struct hbg_irq_info *info;
|
|
struct hbg_priv *priv = p;
|
|
u32 status;
|
|
u32 i;
|
|
|
|
status = hbg_hw_get_irq_status(priv);
|
|
for (i = 0; i < priv->vectors.info_array_len; i++) {
|
|
info = &priv->vectors.info_array[i];
|
|
if (status & info->mask) {
|
|
if (!hbg_hw_irq_is_enabled(priv, info->mask))
|
|
continue;
|
|
|
|
hbg_hw_irq_enable(priv, info->mask, false);
|
|
hbg_hw_irq_clear(priv, info->mask);
|
|
|
|
priv->vectors.stats_array[i]++;
|
|
if (info->irq_handle)
|
|
info->irq_handle(priv, info);
|
|
|
|
if (info->re_enable)
|
|
hbg_hw_irq_enable(priv, info->mask, true);
|
|
}
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static const char *irq_names_map[HBG_VECTOR_NUM] = { "tx", "rx",
|
|
"err", "mdio" };
|
|
|
|
int hbg_irq_init(struct hbg_priv *priv)
|
|
{
|
|
struct hbg_vector *vectors = &priv->vectors;
|
|
struct device *dev = &priv->pdev->dev;
|
|
int ret, id;
|
|
u32 i;
|
|
|
|
/* used pcim_enable_device(), so the vectors become device managed */
|
|
ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM,
|
|
PCI_IRQ_MSI | PCI_IRQ_MSIX);
|
|
if (ret < 0)
|
|
return dev_err_probe(dev, ret, "failed to allocate vectors\n");
|
|
|
|
if (ret != HBG_VECTOR_NUM)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"requested %u MSI, but allocated %d MSI\n",
|
|
HBG_VECTOR_NUM, ret);
|
|
|
|
/* mdio irq not requested, so the number of requested interrupts
|
|
* is HBG_VECTOR_NUM - 1.
|
|
*/
|
|
for (i = 0; i < HBG_VECTOR_NUM - 1; i++) {
|
|
id = pci_irq_vector(priv->pdev, i);
|
|
if (id < 0)
|
|
return dev_err_probe(dev, id, "failed to get irq id\n");
|
|
|
|
snprintf(vectors->name[i], sizeof(vectors->name[i]), "%s-%s-%s",
|
|
dev_driver_string(dev), pci_name(priv->pdev),
|
|
irq_names_map[i]);
|
|
|
|
ret = devm_request_irq(dev, id, hbg_irq_handle, 0,
|
|
vectors->name[i], priv);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"failed to request irq: %s\n",
|
|
irq_names_map[i]);
|
|
}
|
|
|
|
vectors->stats_array = devm_kcalloc(&priv->pdev->dev,
|
|
ARRAY_SIZE(hbg_irqs),
|
|
sizeof(u64), GFP_KERNEL);
|
|
if (!vectors->stats_array)
|
|
return -ENOMEM;
|
|
|
|
vectors->info_array = hbg_irqs;
|
|
vectors->info_array_len = ARRAY_SIZE(hbg_irqs);
|
|
return 0;
|
|
}
|