net/txgbe: add interrupt operation

Add device interrupt handler and setup misx interrupt.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
This commit is contained in:
Jiawen Wu 2020-10-19 16:53:30 +08:00 committed by Ferruh Yigit
parent 86d8adc770
commit 2fc745e6b6
5 changed files with 512 additions and 0 deletions

View File

@ -5,6 +5,8 @@
;
[Features]
Speed capabilities = Y
Link status = Y
Link status event = Y
Linux UIO = Y
Linux VFIO = Y
ARMv8 = Y

View File

@ -7,6 +7,11 @@ TXGBE Poll Mode Driver
The TXGBE PMD (librte_pmd_txgbe) provides poll mode driver support
for Wangxun 10 Gigabit Ethernet NICs.
Features
--------
- Link state information
Prerequisites
-------------

View File

@ -378,6 +378,14 @@ struct txgbe_mbx_info {
s32 (*check_for_rst)(struct txgbe_hw *hw, u16 mbx_id);
};
enum txgbe_isb_idx {
TXGBE_ISB_HEADER,
TXGBE_ISB_MISC,
TXGBE_ISB_VEC0,
TXGBE_ISB_VEC1,
TXGBE_ISB_MAX
};
struct txgbe_hw {
void IOMEM *hw_addr;
void *back;

View File

@ -8,8 +8,12 @@
#include <string.h>
#include <rte_common.h>
#include <rte_ethdev_pci.h>
#include <rte_interrupts.h>
#include <rte_pci.h>
#include <rte_memory.h>
#include <rte_eal.h>
#include <rte_alarm.h>
#include "txgbe_logs.h"
#include "base/txgbe.h"
@ -18,6 +22,17 @@
static int txgbe_dev_close(struct rte_eth_dev *dev);
static void txgbe_dev_link_status_print(struct rte_eth_dev *dev);
static int txgbe_dev_lsc_interrupt_setup(struct rte_eth_dev *dev, uint8_t on);
static int txgbe_dev_macsec_interrupt_setup(struct rte_eth_dev *dev);
static int txgbe_dev_rxq_interrupt_setup(struct rte_eth_dev *dev);
static int txgbe_dev_interrupt_get_status(struct rte_eth_dev *dev);
static int txgbe_dev_interrupt_action(struct rte_eth_dev *dev,
struct rte_intr_handle *handle);
static void txgbe_dev_interrupt_handler(void *param);
static void txgbe_dev_interrupt_delayed_handler(void *param);
static void txgbe_configure_msix(struct rte_eth_dev *dev);
/*
* The set of PCI devices this driver supports
*/
@ -59,6 +74,29 @@ txgbe_is_sfp(struct txgbe_hw *hw)
}
}
static inline void
txgbe_enable_intr(struct rte_eth_dev *dev)
{
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
wr32(hw, TXGBE_IENMISC, intr->mask_misc);
wr32(hw, TXGBE_IMC(0), TXGBE_IMC_MASK);
wr32(hw, TXGBE_IMC(1), TXGBE_IMC_MASK);
txgbe_flush(hw);
}
static void
txgbe_disable_intr(struct txgbe_hw *hw)
{
PMD_INIT_FUNC_TRACE();
wr32(hw, TXGBE_IENMISC, ~BIT_MASK32);
wr32(hw, TXGBE_IMS(0), TXGBE_IMC_MASK);
wr32(hw, TXGBE_IMS(1), TXGBE_IMC_MASK);
txgbe_flush(hw);
}
static int
eth_txgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
{
@ -145,6 +183,9 @@ eth_txgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
return -EIO;
}
/* disable interrupt */
txgbe_disable_intr(hw);
/* Allocate memory for storing MAC addresses */
eth_dev->data->mac_addrs = rte_zmalloc("txgbe", RTE_ETHER_ADDR_LEN *
hw->mac.num_rar_entries, 0);
@ -182,9 +223,15 @@ eth_txgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused)
eth_dev->data->port_id, pci_dev->id.vendor_id,
pci_dev->id.device_id);
rte_intr_callback_register(intr_handle,
txgbe_dev_interrupt_handler, eth_dev);
/* enable uio/vfio intr/eventfd mapping */
rte_intr_enable(intr_handle);
/* enable support intr */
txgbe_enable_intr(eth_dev);
return 0;
}
@ -252,6 +299,20 @@ static struct rte_pci_driver rte_txgbe_pmd = {
.remove = eth_txgbe_pci_remove,
};
static void
txgbe_dev_phy_intr_setup(struct rte_eth_dev *dev)
{
struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
uint32_t gpie;
gpie = rd32(hw, TXGBE_GPIOINTEN);
gpie |= TXGBE_GPIOBIT_6;
wr32(hw, TXGBE_GPIOINTEN, gpie);
intr->mask_misc |= TXGBE_ICRMISC_GPIO;
}
/*
* Reset and stop device.
*/
@ -260,12 +321,30 @@ txgbe_dev_close(struct rte_eth_dev *dev)
{
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
int retries = 0;
int ret;
PMD_INIT_FUNC_TRACE();
/* disable uio intr before callback unregister */
rte_intr_disable(intr_handle);
do {
ret = rte_intr_callback_unregister(intr_handle,
txgbe_dev_interrupt_handler, dev);
if (ret >= 0 || ret == -ENOENT) {
break;
} else if (ret != -EAGAIN) {
PMD_INIT_LOG(ERR,
"intr callback unregister failed: %d",
ret);
}
rte_delay_ms(100);
} while (retries++ < (10 + TXGBE_LINK_UP_TIME));
/* cancel the delay handler before remove dev */
rte_eal_alarm_cancel(txgbe_dev_interrupt_delayed_handler, dev);
rte_free(dev->data->mac_addrs);
dev->data->mac_addrs = NULL;
@ -338,6 +417,394 @@ txgbe_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
return 0;
}
static int
txgbe_dev_link_update(struct rte_eth_dev *dev, int wait_to_complete)
{
RTE_SET_USED(dev);
RTE_SET_USED(wait_to_complete);
return 0;
}
/**
* It clears the interrupt causes and enables the interrupt.
* It will be called once only during nic initialized.
*
* @param dev
* Pointer to struct rte_eth_dev.
* @param on
* Enable or Disable.
*
* @return
* - On success, zero.
* - On failure, a negative value.
*/
static int
txgbe_dev_lsc_interrupt_setup(struct rte_eth_dev *dev, uint8_t on)
{
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
txgbe_dev_link_status_print(dev);
if (on)
intr->mask_misc |= TXGBE_ICRMISC_LSC;
else
intr->mask_misc &= ~TXGBE_ICRMISC_LSC;
return 0;
}
/**
* It clears the interrupt causes and enables the interrupt.
* It will be called once only during nic initialized.
*
* @param dev
* Pointer to struct rte_eth_dev.
*
* @return
* - On success, zero.
* - On failure, a negative value.
*/
static int
txgbe_dev_rxq_interrupt_setup(struct rte_eth_dev *dev)
{
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
intr->mask[0] |= TXGBE_ICR_MASK;
intr->mask[1] |= TXGBE_ICR_MASK;
return 0;
}
/**
* It clears the interrupt causes and enables the interrupt.
* It will be called once only during nic initialized.
*
* @param dev
* Pointer to struct rte_eth_dev.
*
* @return
* - On success, zero.
* - On failure, a negative value.
*/
static int
txgbe_dev_macsec_interrupt_setup(struct rte_eth_dev *dev)
{
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
intr->mask_misc |= TXGBE_ICRMISC_LNKSEC;
return 0;
}
/*
* It reads ICR and sets flag (TXGBE_ICRMISC_LSC) for the link_update.
*
* @param dev
* Pointer to struct rte_eth_dev.
*
* @return
* - On success, zero.
* - On failure, a negative value.
*/
static int
txgbe_dev_interrupt_get_status(struct rte_eth_dev *dev)
{
uint32_t eicr;
struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
/* clear all cause mask */
txgbe_disable_intr(hw);
/* read-on-clear nic registers here */
eicr = ((u32 *)hw->isb_mem)[TXGBE_ISB_MISC];
PMD_DRV_LOG(DEBUG, "eicr %x", eicr);
intr->flags = 0;
/* set flag for async link update */
if (eicr & TXGBE_ICRMISC_LSC)
intr->flags |= TXGBE_FLAG_NEED_LINK_UPDATE;
if (eicr & TXGBE_ICRMISC_VFMBX)
intr->flags |= TXGBE_FLAG_MAILBOX;
if (eicr & TXGBE_ICRMISC_LNKSEC)
intr->flags |= TXGBE_FLAG_MACSEC;
if (eicr & TXGBE_ICRMISC_GPIO)
intr->flags |= TXGBE_FLAG_PHY_INTERRUPT;
return 0;
}
/**
* It gets and then prints the link status.
*
* @param dev
* Pointer to struct rte_eth_dev.
*
* @return
* - On success, zero.
* - On failure, a negative value.
*/
static void
txgbe_dev_link_status_print(struct rte_eth_dev *dev)
{
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_eth_link link;
rte_eth_linkstatus_get(dev, &link);
if (link.link_status) {
PMD_INIT_LOG(INFO, "Port %d: Link Up - speed %u Mbps - %s",
(int)(dev->data->port_id),
(unsigned int)link.link_speed,
link.link_duplex == ETH_LINK_FULL_DUPLEX ?
"full-duplex" : "half-duplex");
} else {
PMD_INIT_LOG(INFO, " Port %d: Link Down",
(int)(dev->data->port_id));
}
PMD_INIT_LOG(DEBUG, "PCI Address: " PCI_PRI_FMT,
pci_dev->addr.domain,
pci_dev->addr.bus,
pci_dev->addr.devid,
pci_dev->addr.function);
}
/*
* It executes link_update after knowing an interrupt occurred.
*
* @param dev
* Pointer to struct rte_eth_dev.
*
* @return
* - On success, zero.
* - On failure, a negative value.
*/
static int
txgbe_dev_interrupt_action(struct rte_eth_dev *dev,
struct rte_intr_handle *intr_handle)
{
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
int64_t timeout;
struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
PMD_DRV_LOG(DEBUG, "intr action type %d", intr->flags);
if (intr->flags & TXGBE_FLAG_MAILBOX)
intr->flags &= ~TXGBE_FLAG_MAILBOX;
if (intr->flags & TXGBE_FLAG_PHY_INTERRUPT) {
hw->phy.handle_lasi(hw);
intr->flags &= ~TXGBE_FLAG_PHY_INTERRUPT;
}
if (intr->flags & TXGBE_FLAG_NEED_LINK_UPDATE) {
struct rte_eth_link link;
/*get the link status before link update, for predicting later*/
rte_eth_linkstatus_get(dev, &link);
txgbe_dev_link_update(dev, 0);
/* likely to up */
if (!link.link_status)
/* handle it 1 sec later, wait it being stable */
timeout = TXGBE_LINK_UP_CHECK_TIMEOUT;
/* likely to down */
else
/* handle it 4 sec later, wait it being stable */
timeout = TXGBE_LINK_DOWN_CHECK_TIMEOUT;
txgbe_dev_link_status_print(dev);
if (rte_eal_alarm_set(timeout * 1000,
txgbe_dev_interrupt_delayed_handler,
(void *)dev) < 0) {
PMD_DRV_LOG(ERR, "Error setting alarm");
} else {
/* remember original mask */
intr->mask_misc_orig = intr->mask_misc;
/* only disable lsc interrupt */
intr->mask_misc &= ~TXGBE_ICRMISC_LSC;
}
}
PMD_DRV_LOG(DEBUG, "enable intr immediately");
txgbe_enable_intr(dev);
rte_intr_enable(intr_handle);
return 0;
}
/**
* Interrupt handler which shall be registered for alarm callback for delayed
* handling specific interrupt to wait for the stable nic state. As the
* NIC interrupt state is not stable for txgbe after link is just down,
* it needs to wait 4 seconds to get the stable status.
*
* @param handle
* Pointer to interrupt handle.
* @param param
* The address of parameter (struct rte_eth_dev *) registered before.
*
* @return
* void
*/
static void
txgbe_dev_interrupt_delayed_handler(void *param)
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
struct txgbe_interrupt *intr = TXGBE_DEV_INTR(dev);
struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
uint32_t eicr;
txgbe_disable_intr(hw);
eicr = ((u32 *)hw->isb_mem)[TXGBE_ISB_MISC];
if (intr->flags & TXGBE_FLAG_PHY_INTERRUPT) {
hw->phy.handle_lasi(hw);
intr->flags &= ~TXGBE_FLAG_PHY_INTERRUPT;
}
if (intr->flags & TXGBE_FLAG_NEED_LINK_UPDATE) {
txgbe_dev_link_update(dev, 0);
intr->flags &= ~TXGBE_FLAG_NEED_LINK_UPDATE;
txgbe_dev_link_status_print(dev);
rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC,
NULL);
}
if (intr->flags & TXGBE_FLAG_MACSEC) {
rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_MACSEC,
NULL);
intr->flags &= ~TXGBE_FLAG_MACSEC;
}
/* restore original mask */
intr->mask_misc = intr->mask_misc_orig;
intr->mask_misc_orig = 0;
PMD_DRV_LOG(DEBUG, "enable intr in delayed handler S[%08x]", eicr);
txgbe_enable_intr(dev);
rte_intr_enable(intr_handle);
}
/**
* Interrupt handler triggered by NIC for handling
* specific interrupt.
*
* @param handle
* Pointer to interrupt handle.
* @param param
* The address of parameter (struct rte_eth_dev *) registered before.
*
* @return
* void
*/
static void
txgbe_dev_interrupt_handler(void *param)
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
txgbe_dev_interrupt_get_status(dev);
txgbe_dev_interrupt_action(dev, dev->intr_handle);
}
/**
* set the IVAR registers, mapping interrupt causes to vectors
* @param hw
* pointer to txgbe_hw struct
* @direction
* 0 for Rx, 1 for Tx, -1 for other causes
* @queue
* queue to map the corresponding interrupt to
* @msix_vector
* the vector to map to the corresponding queue
*/
void
txgbe_set_ivar_map(struct txgbe_hw *hw, int8_t direction,
uint8_t queue, uint8_t msix_vector)
{
uint32_t tmp, idx;
if (direction == -1) {
/* other causes */
msix_vector |= TXGBE_IVARMISC_VLD;
idx = 0;
tmp = rd32(hw, TXGBE_IVARMISC);
tmp &= ~(0xFF << idx);
tmp |= (msix_vector << idx);
wr32(hw, TXGBE_IVARMISC, tmp);
} else {
/* rx or tx causes */
/* Workround for ICR lost */
idx = ((16 * (queue & 1)) + (8 * direction));
tmp = rd32(hw, TXGBE_IVAR(queue >> 1));
tmp &= ~(0xFF << idx);
tmp |= (msix_vector << idx);
wr32(hw, TXGBE_IVAR(queue >> 1), tmp);
}
}
/**
* Sets up the hardware to properly generate MSI-X interrupts
* @hw
* board private structure
*/
static void
txgbe_configure_msix(struct rte_eth_dev *dev)
{
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
struct txgbe_hw *hw = TXGBE_DEV_HW(dev);
uint32_t queue_id, base = TXGBE_MISC_VEC_ID;
uint32_t vec = TXGBE_MISC_VEC_ID;
uint32_t gpie;
/* won't configure msix register if no mapping is done
* between intr vector and event fd
* but if misx has been enabled already, need to configure
* auto clean, auto mask and throttling.
*/
gpie = rd32(hw, TXGBE_GPIE);
if (!rte_intr_dp_is_en(intr_handle) &&
!(gpie & TXGBE_GPIE_MSIX))
return;
if (rte_intr_allow_others(intr_handle)) {
base = TXGBE_RX_VEC_START;
vec = base;
}
/* setup GPIE for MSI-x mode */
gpie = rd32(hw, TXGBE_GPIE);
gpie |= TXGBE_GPIE_MSIX;
wr32(hw, TXGBE_GPIE, gpie);
/* Populate the IVAR table and set the ITR values to the
* corresponding register.
*/
if (rte_intr_dp_is_en(intr_handle)) {
for (queue_id = 0; queue_id < dev->data->nb_rx_queues;
queue_id++) {
/* by default, 1:1 mapping */
txgbe_set_ivar_map(hw, 0, queue_id, vec);
intr_handle->intr_vec[queue_id] = vec;
if (vec < base + intr_handle->nb_efd - 1)
vec++;
}
txgbe_set_ivar_map(hw, -1, 1, TXGBE_MISC_VEC_ID);
}
wr32(hw, TXGBE_ITR(TXGBE_MISC_VEC_ID),
TXGBE_ITR_IVAL_10G(TXGBE_QUEUE_ITR_INTERVAL_DEFAULT)
| TXGBE_ITR_WRDSA);
}
static const struct eth_dev_ops txgbe_eth_dev_ops = {
.dev_infos_get = txgbe_dev_info_get,
};

View File

@ -7,12 +7,21 @@
#include "base/txgbe.h"
/* need update link, bit flag */
#define TXGBE_FLAG_NEED_LINK_UPDATE (uint32_t)(1 << 0)
#define TXGBE_FLAG_MAILBOX (uint32_t)(1 << 1)
#define TXGBE_FLAG_PHY_INTERRUPT (uint32_t)(1 << 2)
#define TXGBE_FLAG_MACSEC (uint32_t)(1 << 3)
#define TXGBE_FLAG_NEED_LINK_CONFIG (uint32_t)(1 << 4)
/*
* Defines that were not part of txgbe_type.h as they are not used by the
* FreeBSD driver.
*/
#define TXGBE_HKEY_MAX_INDEX 10
#define TXGBE_QUEUE_ITR_INTERVAL_DEFAULT 500 /* 500us */
#define TXGBE_RSS_OFFLOAD_ALL ( \
ETH_RSS_IPV4 | \
ETH_RSS_NONFRAG_IPV4_TCP | \
@ -24,16 +33,37 @@
ETH_RSS_IPV6_TCP_EX | \
ETH_RSS_IPV6_UDP_EX)
#define TXGBE_MISC_VEC_ID RTE_INTR_VEC_ZERO_OFFSET
#define TXGBE_RX_VEC_START RTE_INTR_VEC_RXTX_OFFSET
/* structure for interrupt relative data */
struct txgbe_interrupt {
uint32_t flags;
uint32_t mask_misc;
/* to save original mask during delayed handler */
uint32_t mask_misc_orig;
uint32_t mask[2];
};
/*
* Structure to store private data for each driver instance (for each port).
*/
struct txgbe_adapter {
struct txgbe_hw hw;
struct txgbe_interrupt intr;
};
#define TXGBE_DEV_HW(dev) \
(&((struct txgbe_adapter *)(dev)->data->dev_private)->hw)
#define TXGBE_DEV_INTR(dev) \
(&((struct txgbe_adapter *)(dev)->data->dev_private)->intr)
void txgbe_set_ivar_map(struct txgbe_hw *hw, int8_t direction,
uint8_t queue, uint8_t msix_vector);
#define TXGBE_LINK_DOWN_CHECK_TIMEOUT 4000 /* ms */
#define TXGBE_LINK_UP_CHECK_TIMEOUT 1000 /* ms */
#define TXGBE_VMDQ_NUM_UC_MAC 4096 /* Maximum nb. of UC MAC addr. */
/*