fa7bb47ace
In multiprocess context, the sub-device structure is shared between processes. The reference to the failsafe device was a per process pointer. It's changed to port id which is the same for all processes. Signed-off-by: Raslan Darawsheh <rasland@mellanox.com> Signed-off-by: Thomas Monjalon <thomas@monjalon.net> Acked-by: Gaetan Rivet <gaetan.rivet@6wind.com>
537 lines
13 KiB
C
537 lines
13 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright 2018 Mellanox Technologies, Ltd
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Interrupts handling for failsafe driver.
|
|
*/
|
|
|
|
#if defined(LINUX)
|
|
#include <sys/epoll.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
|
|
#include <rte_alarm.h>
|
|
#include <rte_config.h>
|
|
#include <rte_errno.h>
|
|
#include <rte_ethdev.h>
|
|
#include <rte_interrupts.h>
|
|
#include <rte_io.h>
|
|
#include <rte_service_component.h>
|
|
|
|
#include "failsafe_private.h"
|
|
|
|
#define NUM_RX_PROXIES (FAILSAFE_MAX_ETHPORTS * RTE_MAX_RXTX_INTR_VEC_ID)
|
|
|
|
|
|
/**
|
|
* Open an epoll file descriptor.
|
|
*
|
|
* @param flags
|
|
* Flags for defining epoll behavior.
|
|
* @return
|
|
* 0 on success, negative errno value otherwise.
|
|
*/
|
|
static int
|
|
fs_epoll_create1(int flags)
|
|
{
|
|
#if defined(LINUX)
|
|
return epoll_create1(flags);
|
|
#elif defined(BSD)
|
|
RTE_SET_USED(flags);
|
|
return -ENOTSUP;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Install failsafe Rx event proxy service.
|
|
* The Rx event proxy is the service that listens to Rx events from the
|
|
* subdevices and triggers failsafe Rx events accordingly.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
* @return
|
|
* 0 on success, negative errno value otherwise.
|
|
*/
|
|
static int
|
|
fs_rx_event_proxy_routine(void *data)
|
|
{
|
|
struct fs_priv *priv;
|
|
struct rxq *rxq;
|
|
struct rte_epoll_event *events;
|
|
uint64_t u64;
|
|
int i, n;
|
|
int rc = 0;
|
|
|
|
u64 = 1;
|
|
priv = data;
|
|
events = priv->rxp.evec;
|
|
n = rte_epoll_wait(priv->rxp.efd, events, NUM_RX_PROXIES, -1);
|
|
for (i = 0; i < n; i++) {
|
|
rxq = events[i].epdata.data;
|
|
if (rxq->enable_events && rxq->event_fd != -1) {
|
|
if (write(rxq->event_fd, &u64, sizeof(u64)) !=
|
|
sizeof(u64)) {
|
|
ERROR("Failed to proxy Rx event to socket %d",
|
|
rxq->event_fd);
|
|
rc = -EIO;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Uninstall failsafe Rx event proxy service.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
*/
|
|
static void
|
|
fs_rx_event_proxy_service_uninstall(struct fs_priv *priv)
|
|
{
|
|
/* Unregister the event service. */
|
|
switch (priv->rxp.sstate) {
|
|
case SS_RUNNING:
|
|
rte_service_map_lcore_set(priv->rxp.sid, priv->rxp.scid, 0);
|
|
/* fall through */
|
|
case SS_READY:
|
|
rte_service_runstate_set(priv->rxp.sid, 0);
|
|
rte_service_set_stats_enable(priv->rxp.sid, 0);
|
|
rte_service_component_runstate_set(priv->rxp.sid, 0);
|
|
/* fall through */
|
|
case SS_REGISTERED:
|
|
rte_service_component_unregister(priv->rxp.sid);
|
|
/* fall through */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Install the failsafe Rx event proxy service.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
* @return
|
|
* 0 on success, negative errno value otherwise.
|
|
*/
|
|
static int
|
|
fs_rx_event_proxy_service_install(struct fs_priv *priv)
|
|
{
|
|
struct rte_service_spec service;
|
|
int32_t num_service_cores;
|
|
int ret = 0;
|
|
|
|
num_service_cores = rte_service_lcore_count();
|
|
if (num_service_cores <= 0) {
|
|
ERROR("Failed to install Rx interrupts, "
|
|
"no service core found");
|
|
return -ENOTSUP;
|
|
}
|
|
/* prepare service info */
|
|
memset(&service, 0, sizeof(struct rte_service_spec));
|
|
snprintf(service.name, sizeof(service.name), "%s_Rx_service",
|
|
priv->data->name);
|
|
service.socket_id = priv->data->numa_node;
|
|
service.callback = fs_rx_event_proxy_routine;
|
|
service.callback_userdata = priv;
|
|
|
|
if (priv->rxp.sstate == SS_NO_SERVICE) {
|
|
uint32_t service_core_list[num_service_cores];
|
|
|
|
/* get a service core to work with */
|
|
ret = rte_service_lcore_list(service_core_list,
|
|
num_service_cores);
|
|
if (ret <= 0) {
|
|
ERROR("Failed to install Rx interrupts, "
|
|
"service core list empty or corrupted");
|
|
return -ENOTSUP;
|
|
}
|
|
priv->rxp.scid = service_core_list[0];
|
|
ret = rte_service_lcore_add(priv->rxp.scid);
|
|
if (ret && ret != -EALREADY) {
|
|
ERROR("Failed adding service core");
|
|
return ret;
|
|
}
|
|
/* service core may be in "stopped" state, start it */
|
|
ret = rte_service_lcore_start(priv->rxp.scid);
|
|
if (ret && (ret != -EALREADY)) {
|
|
ERROR("Failed to install Rx interrupts, "
|
|
"service core not started");
|
|
return ret;
|
|
}
|
|
/* register our service */
|
|
int32_t ret = rte_service_component_register(&service,
|
|
&priv->rxp.sid);
|
|
if (ret) {
|
|
ERROR("service register() failed");
|
|
return -ENOEXEC;
|
|
}
|
|
priv->rxp.sstate = SS_REGISTERED;
|
|
/* run the service */
|
|
ret = rte_service_component_runstate_set(priv->rxp.sid, 1);
|
|
if (ret < 0) {
|
|
ERROR("Failed Setting component runstate\n");
|
|
return ret;
|
|
}
|
|
ret = rte_service_set_stats_enable(priv->rxp.sid, 1);
|
|
if (ret < 0) {
|
|
ERROR("Failed enabling stats\n");
|
|
return ret;
|
|
}
|
|
ret = rte_service_runstate_set(priv->rxp.sid, 1);
|
|
if (ret < 0) {
|
|
ERROR("Failed to run service\n");
|
|
return ret;
|
|
}
|
|
priv->rxp.sstate = SS_READY;
|
|
/* map the service with the service core */
|
|
ret = rte_service_map_lcore_set(priv->rxp.sid,
|
|
priv->rxp.scid, 1);
|
|
if (ret) {
|
|
ERROR("Failed to install Rx interrupts, "
|
|
"could not map service core");
|
|
return ret;
|
|
}
|
|
priv->rxp.sstate = SS_RUNNING;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Install failsafe Rx event proxy subsystem.
|
|
* This is the way the failsafe PMD generates Rx events on behalf of its
|
|
* subdevices.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
* @return
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
static int
|
|
fs_rx_event_proxy_install(struct fs_priv *priv)
|
|
{
|
|
int rc = 0;
|
|
|
|
/*
|
|
* Create the epoll fd and event vector for the proxy service to
|
|
* wait on for Rx events generated by the subdevices.
|
|
*/
|
|
priv->rxp.efd = fs_epoll_create1(0);
|
|
if (priv->rxp.efd < 0) {
|
|
rte_errno = errno;
|
|
ERROR("Failed to create epoll,"
|
|
" Rx interrupts will not be supported");
|
|
return -rte_errno;
|
|
}
|
|
priv->rxp.evec = calloc(NUM_RX_PROXIES, sizeof(*priv->rxp.evec));
|
|
if (priv->rxp.evec == NULL) {
|
|
ERROR("Failed to allocate memory for event vectors,"
|
|
" Rx interrupts will not be supported");
|
|
rc = -ENOMEM;
|
|
goto error;
|
|
}
|
|
rc = fs_rx_event_proxy_service_install(priv);
|
|
if (rc < 0)
|
|
goto error;
|
|
return 0;
|
|
error:
|
|
if (priv->rxp.efd >= 0) {
|
|
close(priv->rxp.efd);
|
|
priv->rxp.efd = -1;
|
|
}
|
|
if (priv->rxp.evec != NULL) {
|
|
free(priv->rxp.evec);
|
|
priv->rxp.evec = NULL;
|
|
}
|
|
rte_errno = -rc;
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* RX Interrupt control per subdevice.
|
|
*
|
|
* @param sdev
|
|
* Pointer to sub-device structure.
|
|
* @param op
|
|
* The operation be performed for the vector.
|
|
* Operation type of {RTE_INTR_EVENT_ADD, RTE_INTR_EVENT_DEL}.
|
|
* @return
|
|
* - On success, zero.
|
|
* - On failure, a negative value.
|
|
*/
|
|
static int
|
|
failsafe_eth_rx_intr_ctl_subdevice(struct sub_device *sdev, int op)
|
|
{
|
|
struct rte_eth_dev *dev;
|
|
struct rte_eth_dev *fsdev;
|
|
int epfd;
|
|
uint16_t pid;
|
|
uint16_t qid;
|
|
struct rxq *fsrxq;
|
|
int rc;
|
|
int ret = 0;
|
|
|
|
fsdev = fs_dev(sdev);
|
|
if (sdev == NULL || (ETH(sdev) == NULL) ||
|
|
fsdev == NULL || (PRIV(fsdev) == NULL)) {
|
|
ERROR("Called with invalid arguments");
|
|
return -EINVAL;
|
|
}
|
|
dev = ETH(sdev);
|
|
epfd = PRIV(fsdev)->rxp.efd;
|
|
pid = PORT_ID(sdev);
|
|
|
|
if (epfd <= 0) {
|
|
if (op == RTE_INTR_EVENT_ADD) {
|
|
ERROR("Proxy events are not initialized");
|
|
return -EBADF;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
if (dev->data->nb_rx_queues > fsdev->data->nb_rx_queues) {
|
|
ERROR("subdevice has too many queues,"
|
|
" Interrupts will not be enabled");
|
|
return -E2BIG;
|
|
}
|
|
for (qid = 0; qid < dev->data->nb_rx_queues; qid++) {
|
|
fsrxq = fsdev->data->rx_queues[qid];
|
|
rc = rte_eth_dev_rx_intr_ctl_q(pid, qid, epfd,
|
|
op, (void *)fsrxq);
|
|
if (rc) {
|
|
ERROR("rte_eth_dev_rx_intr_ctl_q failed for "
|
|
"port %d queue %d, epfd %d, error %d",
|
|
pid, qid, epfd, rc);
|
|
ret = rc;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Install Rx interrupts subsystem for a subdevice.
|
|
* This is a support for dynamically adding subdevices.
|
|
*
|
|
* @param sdev
|
|
* Pointer to subdevice structure.
|
|
*
|
|
* @return
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
int failsafe_rx_intr_install_subdevice(struct sub_device *sdev)
|
|
{
|
|
int rc;
|
|
int qid;
|
|
struct rte_eth_dev *fsdev;
|
|
struct rxq **rxq;
|
|
const struct rte_intr_conf *const intr_conf =
|
|
Ð(sdev)->data->dev_conf.intr_conf;
|
|
|
|
fsdev = fs_dev(sdev);
|
|
rxq = (struct rxq **)fsdev->data->rx_queues;
|
|
if (intr_conf->rxq == 0)
|
|
return 0;
|
|
rc = failsafe_eth_rx_intr_ctl_subdevice(sdev, RTE_INTR_EVENT_ADD);
|
|
if (rc)
|
|
return rc;
|
|
/* enable interrupts on already-enabled queues */
|
|
for (qid = 0; qid < ETH(sdev)->data->nb_rx_queues; qid++) {
|
|
if (rxq[qid]->enable_events) {
|
|
int ret = rte_eth_dev_rx_intr_enable(PORT_ID(sdev),
|
|
qid);
|
|
if (ret && (ret != -ENOTSUP)) {
|
|
ERROR("Failed to enable interrupts on "
|
|
"port %d queue %d", PORT_ID(sdev), qid);
|
|
rc = ret;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Uninstall Rx interrupts subsystem for a subdevice.
|
|
* This is a support for dynamically removing subdevices.
|
|
*
|
|
* @param sdev
|
|
* Pointer to subdevice structure.
|
|
*
|
|
* @return
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
void failsafe_rx_intr_uninstall_subdevice(struct sub_device *sdev)
|
|
{
|
|
int qid;
|
|
struct rte_eth_dev *fsdev;
|
|
struct rxq *fsrxq;
|
|
|
|
fsdev = fs_dev(sdev);
|
|
for (qid = 0; qid < ETH(sdev)->data->nb_rx_queues; qid++) {
|
|
if (qid < fsdev->data->nb_rx_queues) {
|
|
fsrxq = fsdev->data->rx_queues[qid];
|
|
if (fsrxq != NULL && fsrxq->enable_events)
|
|
rte_eth_dev_rx_intr_disable(PORT_ID(sdev),
|
|
qid);
|
|
}
|
|
}
|
|
failsafe_eth_rx_intr_ctl_subdevice(sdev, RTE_INTR_EVENT_DEL);
|
|
}
|
|
|
|
/**
|
|
* Uninstall failsafe Rx event proxy.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
*/
|
|
static void
|
|
fs_rx_event_proxy_uninstall(struct fs_priv *priv)
|
|
{
|
|
fs_rx_event_proxy_service_uninstall(priv);
|
|
if (priv->rxp.evec != NULL) {
|
|
free(priv->rxp.evec);
|
|
priv->rxp.evec = NULL;
|
|
}
|
|
if (priv->rxp.efd > 0) {
|
|
close(priv->rxp.efd);
|
|
priv->rxp.efd = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uninstall failsafe interrupt vector.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
*/
|
|
static void
|
|
fs_rx_intr_vec_uninstall(struct fs_priv *priv)
|
|
{
|
|
struct rte_intr_handle *intr_handle;
|
|
|
|
intr_handle = &priv->intr_handle;
|
|
if (intr_handle->intr_vec != NULL) {
|
|
free(intr_handle->intr_vec);
|
|
intr_handle->intr_vec = NULL;
|
|
}
|
|
intr_handle->nb_efd = 0;
|
|
}
|
|
|
|
/**
|
|
* Installs failsafe interrupt vector to be registered with EAL later on.
|
|
*
|
|
* @param priv
|
|
* Pointer to failsafe private structure.
|
|
*
|
|
* @return
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
static int
|
|
fs_rx_intr_vec_install(struct fs_priv *priv)
|
|
{
|
|
unsigned int i;
|
|
unsigned int rxqs_n;
|
|
unsigned int n;
|
|
unsigned int count;
|
|
struct rte_intr_handle *intr_handle;
|
|
|
|
rxqs_n = priv->data->nb_rx_queues;
|
|
n = RTE_MIN(rxqs_n, (uint32_t)RTE_MAX_RXTX_INTR_VEC_ID);
|
|
count = 0;
|
|
intr_handle = &priv->intr_handle;
|
|
RTE_ASSERT(intr_handle->intr_vec == NULL);
|
|
/* Allocate the interrupt vector of the failsafe Rx proxy interrupts */
|
|
intr_handle->intr_vec = malloc(n * sizeof(intr_handle->intr_vec[0]));
|
|
if (intr_handle->intr_vec == NULL) {
|
|
fs_rx_intr_vec_uninstall(priv);
|
|
rte_errno = ENOMEM;
|
|
ERROR("Failed to allocate memory for interrupt vector,"
|
|
" Rx interrupts will not be supported");
|
|
return -rte_errno;
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
struct rxq *rxq = priv->data->rx_queues[i];
|
|
|
|
/* Skip queues that cannot request interrupts. */
|
|
if (rxq == NULL || rxq->event_fd < 0) {
|
|
/* Use invalid intr_vec[] index to disable entry. */
|
|
intr_handle->intr_vec[i] =
|
|
RTE_INTR_VEC_RXTX_OFFSET +
|
|
RTE_MAX_RXTX_INTR_VEC_ID;
|
|
continue;
|
|
}
|
|
if (count >= RTE_MAX_RXTX_INTR_VEC_ID) {
|
|
rte_errno = E2BIG;
|
|
ERROR("Too many Rx queues for interrupt vector size"
|
|
" (%d), Rx interrupts cannot be enabled",
|
|
RTE_MAX_RXTX_INTR_VEC_ID);
|
|
fs_rx_intr_vec_uninstall(priv);
|
|
return -rte_errno;
|
|
}
|
|
intr_handle->intr_vec[i] = RTE_INTR_VEC_RXTX_OFFSET + count;
|
|
intr_handle->efds[count] = rxq->event_fd;
|
|
count++;
|
|
}
|
|
if (count == 0) {
|
|
fs_rx_intr_vec_uninstall(priv);
|
|
} else {
|
|
intr_handle->nb_efd = count;
|
|
intr_handle->efd_counter_size = sizeof(uint64_t);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Uninstall failsafe Rx interrupts subsystem.
|
|
*
|
|
* @param priv
|
|
* Pointer to private structure.
|
|
*
|
|
* @return
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
void
|
|
failsafe_rx_intr_uninstall(struct rte_eth_dev *dev)
|
|
{
|
|
struct fs_priv *priv;
|
|
struct rte_intr_handle *intr_handle;
|
|
|
|
priv = PRIV(dev);
|
|
intr_handle = &priv->intr_handle;
|
|
rte_intr_free_epoll_fd(intr_handle);
|
|
fs_rx_event_proxy_uninstall(priv);
|
|
fs_rx_intr_vec_uninstall(priv);
|
|
dev->intr_handle = NULL;
|
|
}
|
|
|
|
/**
|
|
* Install failsafe Rx interrupts subsystem.
|
|
*
|
|
* @param priv
|
|
* Pointer to private structure.
|
|
*
|
|
* @return
|
|
* 0 on success, negative errno value otherwise and rte_errno is set.
|
|
*/
|
|
int
|
|
failsafe_rx_intr_install(struct rte_eth_dev *dev)
|
|
{
|
|
struct fs_priv *priv = PRIV(dev);
|
|
const struct rte_intr_conf *const intr_conf =
|
|
&priv->data->dev_conf.intr_conf;
|
|
|
|
if (intr_conf->rxq == 0 || dev->intr_handle != NULL)
|
|
return 0;
|
|
if (fs_rx_intr_vec_install(priv) < 0)
|
|
return -rte_errno;
|
|
if (fs_rx_event_proxy_install(priv) < 0) {
|
|
fs_rx_intr_vec_uninstall(priv);
|
|
return -rte_errno;
|
|
}
|
|
dev->intr_handle = &priv->intr_handle;
|
|
return 0;
|
|
}
|