Discussion:
[dpdk-dev] [RFC] Add hot plug event in rte eal interrupt and inplement it in i40e driver.
(too old to reply)
Jeff Guo
2017-05-28 15:44:40 UTC
Permalink
For HW hotplug feature, we had already got upstream that removal event adding from 6wind as bellow.

dependency of “add device removal event” patch set:
http://dpdk.org/dev/patchwork/patch/23693/
[dpdk-dev,v2,1/5] ethdev: introduce device removal event
http://dpdk.org/dev/patchwork/patch/23694/
[dpdk-dev,v2,2/5] net/mlx4: device removal event support
http://dpdk.org/dev/patchwork/patch/23695/
[dpdk-dev,v2,3/5] app/testpmd: generic event handler
http://dpdk.org/dev/patchwork/patch/23696/
[dpdk-dev,v2,4/5] app/testpmd: request link status interrupt
http://dpdk.org/dev/patchwork/patch/23697/
[dpdk-dev,v2,5/5] app/testpmd: request device removal interrupt

From the patches, we can see a new event type “RTE_ETH_DEV_INTR_RMV” has been added into the ethdev, and the event has been implemented in mlx4 driver, and Testpmd be use for testing purposes and as a practical usage example for how to use these event. The progress is use the mlx4 driver to register interrupt callback function to rte eal interrupt source, and when rte epolling detect the IBV_EVENT_DEVICE_FATAL , which is identify the device remove behavior, it will callback into the driver’s interrupt handle to handle it, and then callback to the user app, such as testpmd, to detach the pci device.

So far, except the mlx4 driver, other driver like i40, that not have the remove interrupt from hw, will not be able to monitoring the remove behavior, so in order to expand the hot plug feature for all driver cases, something must be done ot detect the remove event at the kernel level and offer a new line of interrupt to the userland. The idea is coming as below.

Use I40e as example, we know that the rmv interrupt is not added in hw, but we could monitor the uio file descriptor to catch the device remove event as bellow.

The info of uevent form FD monitoring:
remove@/devices/pci0000:80/0000:80:02.2/0000:82:00.0/0000:83:03.0/0000:84:00.2/uio/uio2
ACTION=remove
DEVPATH=/devices/pci0000:80/0000:80:02.2/0000:82:00.0/0000:83:03.0/0000:84:00.2/uio/uio2
SUBSYSTEM=uio
MAJOR=243
MINOR=2
DEVNAME=uio2
SEQNUM=11366

Firstly, in order to monitor the uio file descriptor, we need to create socket to monitor the uio fd, that is defined as “hotplug_fd”, and then add the uio fd into the epoll fd list, rte eal could epoll all of the interrupt event from hw interrupt and also include the uevent from hotplug_fd.

Secondly, in order to read out the uevent that monitoring, we need to add uevent API in rte layer. We plan add 2 , rte_uevent_connect and rte_get_uevent. All driver interrupt handler could use these API to enable the uevent monitoring, and read out the uevent type , then corresponding to handle these uevent, such as detach the device when get the remove type.

Signed-off-by: Jeff Guo <***@intel.com>
---
drivers/net/i40e/i40e_ethdev.c | 15 +++
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 146 ++++++++++++++++++++-
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 32 +++++
3 files changed, 191 insertions(+), 2 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4c49673..0336f82 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -66,6 +66,8 @@
#include "i40e_pf.h"
#include "i40e_regs.h"

+extern int hotplug_fd;
+
#define ETH_I40E_FLOATING_VEB_ARG "enable_floating_veb"
#define ETH_I40E_FLOATING_VEB_LIST_ARG "floating_veb_list"

@@ -5808,8 +5810,21 @@ i40e_dev_interrupt_handler(void *param)
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct rte_uevent event;
uint32_t icr0;

+ /* check device uevent */
+ while (rte_get_uevent(hotplug_fd, &event) > 0) {
+ if (event.subsystem == 1) {
+ if (event.action == RTE_UEVENT_ADD) {
+ //do nothing here
+ } else if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ }
+ }
+ }
+
/* Disable interrupt */
i40e_pf_disable_irq0(hw);

diff --git a/lib/librte_eal/linuxapp/eal/eal_interrupts.c b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
index 2e3bd12..873ab5f 100644
--- a/lib/librte_eal/linuxapp/eal/eal_interrupts.c
+++ b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
@@ -65,6 +65,10 @@
#include <rte_errno.h>
#include <rte_spinlock.h>

+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <sys/epoll.h>
+
#include "eal_private.h"
#include "eal_vfio.h"
#include "eal_thread.h"
@@ -74,6 +78,11 @@

static RTE_DEFINE_PER_LCORE(int, _epfd) = -1; /**< epoll fd per thread */

+#define RTE_UEVENT_MSG_LEN 4096
+#define RTE_UEVENT_SUBSYSTEM_UIO 1
+
+int hotplug_fd = -1;
+
/**
* union for pipe fds.
*/
@@ -669,10 +678,13 @@ eal_intr_process_interrupts(struct epoll_event *events, int nfds)
RTE_SET_USED(r);
return -1;
}
+
rte_spinlock_lock(&intr_lock);
TAILQ_FOREACH(src, &intr_sources, next)
- if (src->intr_handle.fd ==
- events[n].data.fd)
+ if ((src->intr_handle.fd ==
+ events[n].data.fd) ||
+ (hotplug_fd ==
+ events[n].data.fd))
break;
if (src == NULL){
rte_spinlock_unlock(&intr_lock);
@@ -858,7 +870,24 @@ eal_intr_thread_main(__rte_unused void *arg)
}
else
numfds++;
+
+ /**
+ * add device uevent file descriptor
+ * into wait list for hot plug.
+ */
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
+ ev.data.fd = hotplug_fd;
+ if (epoll_ctl(pfd, EPOLL_CTL_ADD,
+ hotplug_fd, &ev) < 0){
+ rte_panic("Error adding hotplug_fd %d epoll_ctl, %s\n",
+ hotplug_fd, strerror(errno));
+ }
+ else
+ numfds++;
+
}
+
+
rte_spinlock_unlock(&intr_lock);
/* serve the interrupt */
eal_intr_handle_interrupts(pfd, numfds);
@@ -877,6 +906,9 @@ rte_eal_intr_init(void)
int ret = 0, ret_1 = 0;
char thread_name[RTE_MAX_THREAD_NAME_LEN];

+ /* connect to monitor device uevent */
+ rte_uevent_connect();
+
/* init the global interrupt source head */
TAILQ_INIT(&intr_sources);

@@ -1255,3 +1287,113 @@ rte_intr_cap_multiple(struct rte_intr_handle *intr_handle)

return 0;
}
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = getpid();
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ hotplug_fd = netlink_fd;
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event)
+{
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (*buf) {
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ while (*buf++)
+ ;
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3)) {
+ event->action = RTE_UEVENT_ADD;
+ }
+ if (!strncmp(action, "remove", 6)) {
+ event->action = RTE_UEVENT_REMOVE;
+ }
+ return 1;
+ }
+
+ return -1;
+}
+
+int
+rte_get_uevent(int fd, struct rte_uevent *uevent)
+{
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0) {
+ return parse_event(buf, uevent);
+ }
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
+ } else {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
index 6daffeb..d32ba01 100644
--- a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
+++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
@@ -99,6 +99,16 @@ struct rte_intr_handle {
int *intr_vec; /**< intr vector number array */
};

+enum rte_uevent_action {
+ RTE_UEVENT_ADD = 0, /**< uevent type of device add */
+ RTE_UEVENT_REMOVE = 1, /**< uevent type of device remove*/
+};
+
+struct rte_uevent {
+ enum rte_uevent_action action; /**< uevent action type */
+ int subsystem; /**< subsystem id */
+};
+
#define RTE_EPOLL_PER_THREAD -1 /**< to hint using per thread epfd */

/**
@@ -236,4 +246,26 @@ rte_intr_allow_others(struct rte_intr_handle *intr_handle);
int
rte_intr_cap_multiple(struct rte_intr_handle *intr_handle);

+/**
+ * It read out the uevent from the specific file descriptor.
+ *
+ * @param fd
+ * The fd which the uevent associated to
+ * @param uevent
+ * Pointer to the uevent which read from the monitoring fd.
+ * @return
+ * - On success, one.
+ * - On failure, zeor or a negative value.
+ */
+int
+rte_get_uevent(int fd, struct rte_uevent *uevent);
+
+/**
+ * Connect to the device uevent file descriptor.
+ * @return
+ * return the connected uevent fd.
+ */
+int
+rte_uevent_connect(void);
+
#endif /* _RTE_LINUXAPP_INTERRUPTS_H_ */
--
2.7.4
Gaëtan Rivet
2017-05-30 07:14:00 UTC
Permalink
Hi Jeff,
Post by Jeff Guo
For HW hotplug feature, we had already got upstream that removal event adding from 6wind as bellow.
http://dpdk.org/dev/patchwork/patch/23693/
[dpdk-dev,v2,1/5] ethdev: introduce device removal event
http://dpdk.org/dev/patchwork/patch/23694/
[dpdk-dev,v2,2/5] net/mlx4: device removal event support
http://dpdk.org/dev/patchwork/patch/23695/
[dpdk-dev,v2,3/5] app/testpmd: generic event handler
http://dpdk.org/dev/patchwork/patch/23696/
[dpdk-dev,v2,4/5] app/testpmd: request link status interrupt
http://dpdk.org/dev/patchwork/patch/23697/
[dpdk-dev,v2,5/5] app/testpmd: request device removal interrupt
From the patches, we can see a new event type “RTE_ETH_DEV_INTR_RMV” has been added into the ethdev, and the event has been implemented in mlx4 driver, and Testpmd be use for testing purposes and as a practical usage example for how to use these event. The progress is use the mlx4 driver to register interrupt callback function to rte eal interrupt source, and when rte epolling detect the IBV_EVENT_DEVICE_FATAL , which is identify the device remove behavior, it will callback into the driver’s interrupt handle to handle it, and then callback to the user app, such as testpmd, to detach the pci device.
So far, except the mlx4 driver, other driver like i40, that not have the remove interrupt from hw, will not be able to monitoring the remove behavior, so in order to expand the hot plug feature for all driver cases, something must be done ot detect the remove event at the kernel level and offer a new line of interrupt to the userland. The idea is coming as below.
It's nice that this event is extended to other drivers.
Post by Jeff Guo
Use I40e as example, we know that the rmv interrupt is not added in hw, but we could monitor the uio file descriptor to catch the device remove event as bellow.
ACTION=remove
DEVPATH=/devices/pci0000:80/0000:80:02.2/0000:82:00.0/0000:83:03.0/0000:84:00.2/uio/uio2
SUBSYSTEM=uio
MAJOR=243
MINOR=2
DEVNAME=uio2
SEQNUM=11366
Firstly, in order to monitor the uio file descriptor, we need to create socket to monitor the uio fd, that is defined as “hotplug_fd”, and then add the uio fd into the epoll fd list, rte eal could epoll all of the interrupt event from hw interrupt and also include the uevent from hotplug_fd.
Secondly, in order to read out the uevent that monitoring, we need to add uevent API in rte layer. We plan add 2 , rte_uevent_connect and rte_get_uevent. All driver interrupt handler could use these API to enable the uevent monitoring, and read out the uevent type , then corresponding to handle these uevent, such as detach the device when get the remove type.
I find having a generic uevent API interesting.

However, all specifics pertaining to UIO use (hotplug_fd, subsystem
enum) should stay in UIO specific code (eal_pci_uio.c?).

I am currently moving the PCI bus out of the EAL. EAL subsystems should
not rely on PCI specifics, as they won't be available afterward.

It should also allow you to clean up your API. Exposing hotplug_fd and
requiring PMDs to link it can be avoided and should result in a simpler
API.
Post by Jeff Guo
---
drivers/net/i40e/i40e_ethdev.c | 15 +++
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 146 ++++++++++++++++++++-
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 32 +++++
3 files changed, 191 insertions(+), 2 deletions(-)
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4c49673..0336f82 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -66,6 +66,8 @@
#include "i40e_pf.h"
#include "i40e_regs.h"
+extern int hotplug_fd;
+
#define ETH_I40E_FLOATING_VEB_ARG "enable_floating_veb"
#define ETH_I40E_FLOATING_VEB_LIST_ARG "floating_veb_list"
@@ -5808,8 +5810,21 @@ i40e_dev_interrupt_handler(void *param)
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ /* check device uevent */
+ while (rte_get_uevent(hotplug_fd, &event) > 0) {
+ if (event.subsystem == 1) {
+ if (event.action == RTE_UEVENT_ADD) {
+ //do nothing here
+ } else if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ }
+ }
+ }
+
/* Disable interrupt */
i40e_pf_disable_irq0(hw);
diff --git a/lib/librte_eal/linuxapp/eal/eal_interrupts.c b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
index 2e3bd12..873ab5f 100644
--- a/lib/librte_eal/linuxapp/eal/eal_interrupts.c
+++ b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
@@ -65,6 +65,10 @@
#include <rte_errno.h>
#include <rte_spinlock.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <sys/epoll.h>
+
#include "eal_private.h"
#include "eal_vfio.h"
#include "eal_thread.h"
@@ -74,6 +78,11 @@
static RTE_DEFINE_PER_LCORE(int, _epfd) = -1; /**< epoll fd per thread */
+#define RTE_UEVENT_MSG_LEN 4096
+#define RTE_UEVENT_SUBSYSTEM_UIO 1
+
+int hotplug_fd = -1;
+
/**
* union for pipe fds.
*/
@@ -669,10 +678,13 @@ eal_intr_process_interrupts(struct epoll_event *events, int nfds)
RTE_SET_USED(r);
return -1;
}
+
rte_spinlock_lock(&intr_lock);
TAILQ_FOREACH(src, &intr_sources, next)
- if (src->intr_handle.fd ==
- events[n].data.fd)
+ if ((src->intr_handle.fd ==
+ events[n].data.fd) ||
+ (hotplug_fd ==
+ events[n].data.fd))
break;
if (src == NULL){
rte_spinlock_unlock(&intr_lock);
@@ -858,7 +870,24 @@ eal_intr_thread_main(__rte_unused void *arg)
}
else
numfds++;
+
+ /**
+ * add device uevent file descriptor
+ * into wait list for hot plug.
+ */
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
+ ev.data.fd = hotplug_fd;
+ if (epoll_ctl(pfd, EPOLL_CTL_ADD,
+ hotplug_fd, &ev) < 0){
+ rte_panic("Error adding hotplug_fd %d epoll_ctl, %s\n",
+ hotplug_fd, strerror(errno));
+ }
+ else
+ numfds++;
+
}
+
+
rte_spinlock_unlock(&intr_lock);
/* serve the interrupt */
eal_intr_handle_interrupts(pfd, numfds);
@@ -877,6 +906,9 @@ rte_eal_intr_init(void)
int ret = 0, ret_1 = 0;
char thread_name[RTE_MAX_THREAD_NAME_LEN];
+ /* connect to monitor device uevent */
+ rte_uevent_connect();
+
/* init the global interrupt source head */
TAILQ_INIT(&intr_sources);
@@ -1255,3 +1287,113 @@ rte_intr_cap_multiple(struct rte_intr_handle *intr_handle)
return 0;
}
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = getpid();
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ hotplug_fd = netlink_fd;
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event)
+{
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (*buf) {
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ while (*buf++)
+ ;
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3)) {
+ event->action = RTE_UEVENT_ADD;
+ }
+ if (!strncmp(action, "remove", 6)) {
+ event->action = RTE_UEVENT_REMOVE;
+ }
+ return 1;
+ }
+
+ return -1;
+}
+
+int
+rte_get_uevent(int fd, struct rte_uevent *uevent)
+{
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0) {
+ return parse_event(buf, uevent);
+ }
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
+ } else {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
index 6daffeb..d32ba01 100644
--- a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
+++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
@@ -99,6 +99,16 @@ struct rte_intr_handle {
int *intr_vec; /**< intr vector number array */
};
+enum rte_uevent_action {
+ RTE_UEVENT_ADD = 0, /**< uevent type of device add */
+ RTE_UEVENT_REMOVE = 1, /**< uevent type of device remove*/
+};
+
+struct rte_uevent {
+ enum rte_uevent_action action; /**< uevent action type */
+ int subsystem; /**< subsystem id */
+};
+
#define RTE_EPOLL_PER_THREAD -1 /**< to hint using per thread epfd */
/**
@@ -236,4 +246,26 @@ rte_intr_allow_others(struct rte_intr_handle *intr_handle);
int
rte_intr_cap_multiple(struct rte_intr_handle *intr_handle);
+/**
+ * It read out the uevent from the specific file descriptor.
+ *
+ * The fd which the uevent associated to
+ * Pointer to the uevent which read from the monitoring fd.
+ * - On success, one.
+ * - On failure, zeor or a negative value.
+ */
+int
+rte_get_uevent(int fd, struct rte_uevent *uevent);
+
+/**
+ * Connect to the device uevent file descriptor.
+ * return the connected uevent fd.
+ */
+int
+rte_uevent_connect(void);
+
#endif /* _RTE_LINUXAPP_INTERRUPTS_H_ */
--
2.7.4
--
Gaëtan Rivet
6WIND
Wu, Jingjing
2017-06-07 07:40:37 UTC
Permalink
Post by Jeff Guo
Post by Jeff Guo
Secondly, in order to read out the uevent that monitoring, we need to add uevent API in rte
layer. We plan add 2 , rte_uevent_connect and rte_get_uevent. All driver interrupt handler
could use these API to enable the uevent monitoring, and read out the uevent type , then
corresponding to handle these uevent, such as detach the device when get the remove type.
I find having a generic uevent API interesting.
However, all specifics pertaining to UIO use (hotplug_fd, subsystem
enum) should stay in UIO specific code (eal_pci_uio.c?).
Yes, but it can be also considered as interrupt mechanism, right?
Post by Jeff Guo
I am currently moving the PCI bus out of the EAL. EAL subsystems should
not rely on PCI specifics, as they won't be available afterward.
Will the interrupt handling be kept in EAL, right?
Post by Jeff Guo
It should also allow you to clean up your API. Exposing hotplug_fd and
requiring PMDs to link it can be avoided and should result in a simpler
API.
Didn't get the idea. Why it will result in a simpler API? Is there any patch he
Gaëtan Rivet
2017-06-15 21:22:40 UTC
Permalink
Hi Jingjing,
Post by Wu, Jingjing
Post by Jeff Guo
Post by Jeff Guo
Secondly, in order to read out the uevent that monitoring, we need to add uevent API in rte
layer. We plan add 2 , rte_uevent_connect and rte_get_uevent. All driver interrupt handler
could use these API to enable the uevent monitoring, and read out the uevent type , then
corresponding to handle these uevent, such as detach the device when get the remove type.
I find having a generic uevent API interesting.
However, all specifics pertaining to UIO use (hotplug_fd, subsystem
enum) should stay in UIO specific code (eal_pci_uio.c?).
Yes, but it can be also considered as interrupt mechanism, right?
Sure.
Post by Wu, Jingjing
Post by Jeff Guo
I am currently moving the PCI bus out of the EAL. EAL subsystems should
not rely on PCI specifics, as they won't be available afterward.
Will the interrupt handling be kept in EAL, right?
Ah yes, I was actually mistaken and thought more UIO parts would be
moving.
Post by Wu, Jingjing
Post by Jeff Guo
It should also allow you to clean up your API. Exposing hotplug_fd and
requiring PMDs to link it can be avoided and should result in a simpler
API.
Didn't get the idea. Why it will result in a simpler API? Is there any patch help
Me to understand?
How do you demux the hotplug_fd for several drivers / device?
--
Gaëtan Rivet
6WIND
Guo, Jia
2017-06-21 02:50:48 UTC
Permalink
hi,gaetan
Post by Gaëtan Rivet
Hi Jingjing,
Post by Wu, Jingjing
Post by Jeff Guo
Post by Jeff Guo
Secondly, in order to read out the uevent that monitoring, we need to add uevent API in rte
layer. We plan add 2 , rte_uevent_connect and rte_get_uevent. All driver interrupt handler
could use these API to enable the uevent monitoring, and read out the uevent type , then
corresponding to handle these uevent, such as detach the device when get the remove type.
I find having a generic uevent API interesting.
However, all specifics pertaining to UIO use (hotplug_fd, subsystem
enum) should stay in UIO specific code (eal_pci_uio.c?).
Yes, but it can be also considered as interrupt mechanism, right?
Sure.
Post by Wu, Jingjing
Post by Jeff Guo
I am currently moving the PCI bus out of the EAL. EAL subsystems should
not rely on PCI specifics, as they won't be available afterward.
Will the interrupt handling be kept in EAL, right?
Ah yes, I was actually mistaken and thought more UIO parts would be
moving.
so , i assumption that the interrupt handling still be kept in EAL, so
that would not affect my adding the uevent in the eal interrupt part,
right? so if it have any other dependency, please shout to let me know.
Post by Gaëtan Rivet
Post by Wu, Jingjing
Post by Jeff Guo
It should also allow you to clean up your API. Exposing hotplug_fd and
requiring PMDs to link it can be avoided and should result in a simpler
API.
Didn't get the idea. Why it will result in a simpler API? Is there any patch help
Me to understand?
How do you demux the hotplug_fd for several drivers / device?
it is related with the dual port/device/driver problem, i think what
should be some mapping of the dev_path there, i will refine the part to
handle it in v2. Thanks.
Wu, Jingjing
2017-06-07 07:27:14 UTC
Permalink
-----Original Message-----
From: Guo, Jia
Sent: Sunday, May 28, 2017 11:45 PM
Subject: [dpdk-dev] [RFC] Add hot plug event in rte eal interrupt and inplement it in i40e
driver.
For HW hotplug feature, we had already got upstream that removal event adding from 6wind
as bellow.
http://dpdk.org/dev/patchwork/patch/23693/
[dpdk-dev,v2,1/5] ethdev: introduce device removal event
http://dpdk.org/dev/patchwork/patch/23694/
[dpdk-dev,v2,2/5] net/mlx4: device removal event support
http://dpdk.org/dev/patchwork/patch/23695/
[dpdk-dev,v2,3/5] app/testpmd: generic event handler
http://dpdk.org/dev/patchwork/patch/23696/
[dpdk-dev,v2,4/5] app/testpmd: request link status interrupt
http://dpdk.org/dev/patchwork/patch/23697/
[dpdk-dev,v2,5/5] app/testpmd: request device removal interrupt
From the patches, we can see a new event type “RTE_ETH_DEV_INTR_RMV” has been added
into the ethdev, and the event has been implemented in mlx4 driver, and Testpmd be use for
testing purposes and as a practical usage example for how to use these event. The progress is
use the mlx4 driver to register interrupt callback function to rte eal interrupt source, and
when rte epolling detect the IBV_EVENT_DEVICE_FATAL , which is identify the device remove
behavior, it will callback into the driver’s interrupt handle to handle it, and then callback to
the user app, such as testpmd, to detach the pci device.
So far, except the mlx4 driver, other driver like i40, that not have the remove interrupt from
hw, will not be able to monitoring the remove behavior, so in order to expand the hot plug
feature for all driver cases, something must be done ot detect the remove event at the kernel
level and offer a new line of interrupt to the userland. The idea is coming as below.
Use I40e as example, we know that the rmv interrupt is not added in hw, but we could
monitor the uio file descriptor to catch the device remove event as bellow.
/uio2
ACTION=remove
DEVPATH=/devices/pci0000:80/0000:80:02.2/0000:82:00.0/0000:83:03.0/0000:84:00.2/ui
o/uio2
SUBSYSTEM=uio
MAJOR=243
MINOR=2
DEVNAME=uio2
SEQNUM=11366
Firstly, in order to monitor the uio file descriptor, we need to create socket to monitor the uio
fd, that is defined as “hotplug_fd”, and then add the uio fd into the epoll fd list, rte eal could
epoll all of the interrupt event from hw interrupt and also include the uevent from
hotplug_fd.
Secondly, in order to read out the uevent that monitoring, we need to add uevent API in rte
layer. We plan add 2 , rte_uevent_connect and rte_get_uevent. All driver interrupt handler
could use these API to enable the uevent monitoring, and read out the uevent type , then
corresponding to handle these uevent, such as detach the device when get the remove type.
---
drivers/net/i40e/i40e_ethdev.c | 15 +++
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 146 ++++++++++++++++++++-
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 32 +++++
3 files changed, 191 insertions(+), 2 deletions(-)
It will be better to split the patch to two sub patches, one is for eal change, the other is for driver enabling.
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4c49673..0336f82 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -66,6 +66,8 @@
#include "i40e_pf.h"
#include "i40e_regs.h"
+extern int hotplug_fd;
+
#define ETH_I40E_FLOATING_VEB_ARG "enable_floating_veb"
#define ETH_I40E_FLOATING_VEB_LIST_ARG "floating_veb_list"
@@ -5808,8 +5810,21 @@ i40e_dev_interrupt_handler(void *param)
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ /* check device uevent */
+ while (rte_get_uevent(hotplug_fd, &event) > 0) {
The hotplug_fd is used as uevent fd for all devices, right? but in i40e driver, how distinguish the event is to this dev?
I saw you check the src in eal_intr_process_interrupts, but you didn't assign the fd in i40e device's intr_handle.
Is that to say all driver's callback will be triggered?
+ if (event.subsystem == 1) {
What is the 1 meaning?
+ if (event.action == RTE_UEVENT_ADD) {
+ //do nothing here
Will you plan do anything later? Such as RTE_ETH_EVENT_INTR_NEW? If no, please remove it.
And {} can be omit.
+ } else if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0) {
+ return -1;
+ }
{} can be omit. Serval in this patch. Please check.
+/**
+ * It read out the uevent from the specific file descriptor.
+ *
+ * The fd which the uevent associated to
+ * Pointer to the uevent which read from the monitoring fd.
+ * - On success, one.
+ * - On failure, zeor or a negative value.
Zeor -> zero
Generally speaking, we are using negative value to indicate failure, and 0 indicate success. Expect the result has more than two options (success, failure).
+int
+rte_get_uevent(int fd, struct rte_uevent *uevent);
+
+/**
+ * Connect to the device uevent file descriptor.
+ * return the connected uevent fd.
+ */
Any return code for failure?

Thanks
Jingjin
Jeff Guo
2017-06-28 11:07:23 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch aim to add a variable "uevent_fd" in structure
"rte_intr_handle" for enable kernel object uevent monitoring,
and add some uevent API in rte eal interrupt, that is
“rte_uevent_connect” and “rte_uevent_get”, so that all driver
could use these API to monitor and read out the uevent, then
corresponding to handle these uevent, such as detach or attach
the device.

Signed-off-by: Guo, Jia <***@intel.com>
---
v2->v1: remove global variables of hotplug_fd, add uevent_fd
in rte_intr_handle to let each pci device self maintain
to fix dual device fd issue. refine some typo error.
---
lib/librte_eal/common/eal_common_pci_uio.c | 6 +-
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 143 ++++++++++++++++++++-
lib/librte_eal/linuxapp/eal/eal_pci_uio.c | 12 ++
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 34 +++++
4 files changed, 192 insertions(+), 3 deletions(-)

diff --git a/lib/librte_eal/common/eal_common_pci_uio.c b/lib/librte_eal/common/eal_common_pci_uio.c
index 367a681..5b62f70 100644
--- a/lib/librte_eal/common/eal_common_pci_uio.c
+++ b/lib/librte_eal/common/eal_common_pci_uio.c
@@ -117,6 +117,7 @@

dev->intr_handle.fd = -1;
dev->intr_handle.uio_cfg_fd = -1;
+ dev->intr_handle.uevent_fd = -1;
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;

/* secondary processes - use already recorded details */
@@ -227,7 +228,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
-
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
dev->intr_handle.fd = -1;
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
}
diff --git a/lib/librte_eal/linuxapp/eal/eal_interrupts.c b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
index 2e3bd12..d596522 100644
--- a/lib/librte_eal/linuxapp/eal/eal_interrupts.c
+++ b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
@@ -65,6 +65,10 @@
#include <rte_errno.h>
#include <rte_spinlock.h>

+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <sys/epoll.h>
+
#include "eal_private.h"
#include "eal_vfio.h"
#include "eal_thread.h"
@@ -74,6 +78,9 @@

static RTE_DEFINE_PER_LCORE(int, _epfd) = -1; /**< epoll fd per thread */

+#define RTE_UEVENT_MSG_LEN 4096
+#define RTE_UEVENT_SUBSYSTEM_UIO 1
+
/**
* union for pipe fds.
*/
@@ -669,10 +676,13 @@ struct rte_intr_source {
RTE_SET_USED(r);
return -1;
}
+
rte_spinlock_lock(&intr_lock);
TAILQ_FOREACH(src, &intr_sources, next)
- if (src->intr_handle.fd ==
- events[n].data.fd)
+ if ((src->intr_handle.fd ==
+ events[n].data.fd) ||
+ (src->intr_handle.uevent_fd ==
+ events[n].data.fd))
break;
if (src == NULL){
rte_spinlock_unlock(&intr_lock);
@@ -858,7 +868,24 @@ static __attribute__((noreturn)) void *
}
else
numfds++;
+
+ /**
+ * add device uevent file descriptor
+ * into wait list for uevent monitoring.
+ */
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
+ ev.data.fd = src->intr_handle.uevent_fd;
+ if (epoll_ctl(pfd, EPOLL_CTL_ADD,
+ src->intr_handle.uevent_fd, &ev) < 0){
+ rte_panic("Error adding uevent_fd %d epoll_ctl"
+ ", %s\n",
+ src->intr_handle.uevent_fd,
+ strerror(errno));
+ } else
+ numfds++;
}
+
+
rte_spinlock_unlock(&intr_lock);
/* serve the interrupt */
eal_intr_handle_interrupts(pfd, numfds);
@@ -1255,3 +1282,115 @@ static __attribute__((noreturn)) void *

return 0;
}
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd = -1;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ RTE_LOG(ERR, EAL,
+ "netlink_fd is %d\n", netlink_fd);
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event)
+{
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+ int i = 0;
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (*buf && i < RTE_UEVENT_MSG_LEN) {
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ while (*buf++)
+ i++;
+ while (*buf == '\0') {
+ buf++;
+ i++;
+ }
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3))
+ event->action = RTE_UEVENT_ADD;
+ if (!strncmp(action, "remove", 6))
+ event->action = RTE_UEVENT_REMOVE;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent)
+{
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0)
+ return parse_event(buf, uevent);
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
+ } else {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0)
+ return -1;
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index fa10329..2fead82 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -231,6 +231,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
if (dev->intr_handle.fd >= 0) {
close(dev->intr_handle.fd);
dev->intr_handle.fd = -1;
@@ -245,6 +249,7 @@
char dirname[PATH_MAX];
char cfgname[PATH_MAX];
char devname[PATH_MAX]; /* contains the /dev/uioX */
+ char uevtname[PATH_MAX];
int uio_num;
struct rte_pci_addr *loc;

@@ -276,6 +281,13 @@
goto error;
}

+ dev->intr_handle.uevent_fd = rte_uevent_connect();
+ if (dev->intr_handle.uevent_fd < 0) {
+ RTE_LOG(ERR, EAL, "Cannot open %s: %s\n",
+ uevtname, strerror(errno));
+ goto error;
+ }
+
if (dev->kdrv == RTE_KDRV_IGB_UIO)
dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
else {
diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
index 6daffeb..bd1780d 100644
--- a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
+++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
@@ -90,6 +90,7 @@ struct rte_intr_handle {
for uio_pci_generic */
};
int fd; /**< interrupt event file descriptor */
+ int uevent_fd; /**< uevent file descriptor */
enum rte_intr_handle_type type; /**< handle type */
uint32_t max_intr; /**< max interrupt requested */
uint32_t nb_efd; /**< number of available efd(event fd) */
@@ -99,6 +100,16 @@ struct rte_intr_handle {
int *intr_vec; /**< intr vector number array */
};

+enum rte_uevent_action {
+ RTE_UEVENT_ADD = 0, /**< uevent type of device add */
+ RTE_UEVENT_REMOVE = 1, /**< uevent type of device remove*/
+};
+
+struct rte_uevent {
+ enum rte_uevent_action action; /**< uevent action type */
+ int subsystem; /**< subsystem id */
+};
+
#define RTE_EPOLL_PER_THREAD -1 /**< to hint using per thread epfd */

/**
@@ -236,4 +247,27 @@ struct rte_intr_handle {
int
rte_intr_cap_multiple(struct rte_intr_handle *intr_handle);

+/**
+ * It read out the uevent from the specific file descriptor.
+ *
+ * @param fd
+ * The fd which the uevent associated to
+ * @param uevent
+ * Pointer to the uevent which read from the monitoring fd.
+ * @return
+ * - On success, zero.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent);
+
+/**
+ * Connect to the device uevent file descriptor.
+ * @return
+ * - On success, the connected uevent fd.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_connect(void);
+
#endif /* _RTE_LINUXAPP_INTERRUPTS_H_ */
--
1.8.3.1
Jeff Guo
2017-06-28 11:07:24 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch enable the hot plug feature in i40e, by monitoring the
hot plug uevent of the device. When remove event got, call the app
callback function to handle the detach process.

Signed-off-by: Guo, Jia <***@intel.com>
---
v2->v1: remove unused part for current stage.
---
drivers/net/i40e/i40e_ethdev.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4ee1113..122187e 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1283,6 +1283,7 @@ static inline void i40e_GLQF_reg_init(struct i40e_hw *hw)

/* enable uio intr after callback register */
rte_intr_enable(intr_handle);
+
/*
* Add an ethertype filter to drop all flow control frames transmitted
* from VSIs. By doing so, we stop VF from sending out PAUSE or PFC
@@ -5832,11 +5833,28 @@ struct i40e_vsi *
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ struct rte_pci_device *pci_dev;
+ struct rte_intr_handle *intr_handle;
+
+ pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ intr_handle = &pci_dev->intr_handle;

/* Disable interrupt */
i40e_pf_disable_irq0(hw);

+ /* check device uevent */
+ if (rte_uevent_get(intr_handle->uevent_fd, &event) > 0) {
+ if (event.subsystem == RTE_UEVENT_SUBSYSTEM_UIO) {
+ if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ }
+ }
+ goto done;
+ }
+
/* read out interrupt causes */
icr0 = I40E_READ_REG(hw, I40E_PFINT_ICR0);
--
1.8.3.1
Wu, Jingjing
2017-06-29 01:41:36 UTC
Permalink
-----Original Message-----
From: Guo, Jia
Sent: Wednesday, June 28, 2017 7:07 PM
Subject: [PATCH v2 2/2] net/i40e: add hot plug monitor in i40e
This patch enable the hot plug feature in i40e, by monitoring the hot plug
uevent of the device. When remove event got, call the app callback function to
handle the detach process.
---
v2->v1: remove unused part for current stage.
---
drivers/net/i40e/i40e_ethdev.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4ee1113..122187e 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1283,6 +1283,7 @@ static inline void i40e_GLQF_reg_init(struct i40e_hw *hw)
/* enable uio intr after callback register */
rte_intr_enable(intr_handle);
+
/*
* Add an ethertype filter to drop all flow control frames transmitted
* from VSIs. By doing so, we stop VF from sending out PAUSE or PFC
@@ -5832,11 +5833,28 @@ struct i40e_vsi * {
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data-
Post by Jeff Guo
dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ struct rte_pci_device *pci_dev;
+ struct rte_intr_handle *intr_handle;
+
+ pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ intr_handle = &pci_dev->intr_handle;
/* Disable interrupt */
i40e_pf_disable_irq0(hw);
+ /* check device uevent */
+ if (rte_uevent_get(intr_handle->uevent_fd, &event) > 0) {
You declare the rte_uevnet_get like

+ * @return
+ * - On success, zero.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent);


But here you check if it > 0?
+ if (event.subsystem == RTE_UEVENT_SUBSYSTEM_UIO) {
+ if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ }
+ }
+ goto done;
I think when the remove happen, no need to goto done, you can just return.
+ }
+
/* read out interrupt causes */
icr0 = I40E_READ_REG(hw, I40E_PFINT_ICR0);
--
1.8.3.1
Guo, Jia
2017-06-29 04:31:55 UTC
Permalink
Yes, if got remove uevent might be directly return to avoid invalid i/o. but if got other uevent such as add and change, must be go done to keep the interrupt process in device. I will refine this part, thanks.

Best regards,
Jeff Guo


-----Original Message-----
From: Wu, Jingjing
Sent: Thursday, June 29, 2017 9:42 AM
To: Guo, Jia <***@intel.com>; Zhang, Helin <***@intel.com>
Cc: ***@dpdk.org
Subject: RE: [PATCH v2 2/2] net/i40e: add hot plug monitor in i40e
-----Original Message-----
From: Guo, Jia
Sent: Wednesday, June 28, 2017 7:07 PM
Subject: [PATCH v2 2/2] net/i40e: add hot plug monitor in i40e
This patch enable the hot plug feature in i40e, by monitoring the hot
plug uevent of the device. When remove event got, call the app
callback function to handle the detach process.
---
v2->v1: remove unused part for current stage.
---
drivers/net/i40e/i40e_ethdev.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/net/i40e/i40e_ethdev.c
b/drivers/net/i40e/i40e_ethdev.c index 4ee1113..122187e 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1283,6 +1283,7 @@ static inline void i40e_GLQF_reg_init(struct
i40e_hw
*hw)
/* enable uio intr after callback register */
rte_intr_enable(intr_handle);
+
/*
* Add an ethertype filter to drop all flow control frames transmitted
* from VSIs. By doing so, we stop VF from sending out PAUSE or PFC
@@ -5832,11 +5833,28 @@ struct i40e_vsi * {
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data-
Post by Jeff Guo
dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ struct rte_pci_device *pci_dev;
+ struct rte_intr_handle *intr_handle;
+
+ pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ intr_handle = &pci_dev->intr_handle;
/* Disable interrupt */
i40e_pf_disable_irq0(hw);
+ /* check device uevent */
+ if (rte_uevent_get(intr_handle->uevent_fd, &event) > 0) {
You declare the rte_uevnet_get like

+ * @return
+ * - On success, zero.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent);


But here you check if it > 0?
+ if (event.subsystem == RTE_UEVENT_SUBSYSTEM_UIO) {
+ if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ }
+ }
+ goto done;
I think when the remove happen, no need to goto done, you can just return.
+ }
+
/* read out interrupt causes */
icr0 = I40E_READ_REG(hw, I40E_PFINT_ICR0);
--
1.8.3.1
Stephen Hemminger
2017-06-29 03:34:41 UTC
Permalink
On Wed, 28 Jun 2017 19:07:24 +0800
Post by Jeff Guo
This patch enable the hot plug feature in i40e, by monitoring the
hot plug uevent of the device. When remove event got, call the app
callback function to handle the detach process.
---
Hot plug is good and needed.

But it needs to be done in a generic fashion in the bus layer.
There is nothing about uevents that are unique to i40e or even Intel
devices. Plus the way hotplug is handled is OS specific, so this isn't going
to work well on BSD.

Sorry if I sound like a broken record but there has been a repeated pattern
of Intel developers putting their head down (or in the sand) and creating
functionality inside device driver.
Wu, Jingjing
2017-06-29 04:48:01 UTC
Permalink
-----Original Message-----
Sent: Thursday, June 29, 2017 11:35 AM
Subject: Re: [dpdk-dev] [PATCH v2 2/2] net/i40e: add hot plug monitor in i40e
On Wed, 28 Jun 2017 19:07:24 +0800
This patch enable the hot plug feature in i40e, by monitoring the hot
plug uevent of the device. When remove event got, call the app
callback function to handle the detach process.
---
Hot plug is good and needed.
But it needs to be done in a generic fashion in the bus layer.
There is nothing about uevents that are unique to i40e or even Intel devices.
Plus the way hotplug is handled is OS specific, so this isn't going to work well on
BSD.
This patch is not a way to full support hut plug. And we know it is handled in OS specific.
This patch just provides a way to tell DPDK user the remove happened on this device (DPDK dev).

And Mlx driver already supports that with patch
http://dpdk.org/dev/patchwork/patch/23695/

What GuoJia did is just making the EVENT can be process by application through interrupt callback
Mechanisms.
Sorry if I sound like a broken record but there has been a repeated pattern of
Intel developers putting their head down (or in the sand) and creating
functionality inside device driver.
Sorry, I cannot agree.

Thanks
Jingjing
Jeff Guo
2017-06-29 04:37:51 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch set aim to add a variable "uevent_fd" in structure
"rte_intr_handle" for enable kernel object uevent monitoring,
and add some uevent API in rte eal interrupt, that is
“rte_uevent_connect” and “rte_uevent_get”. The patch use i40e
for example, the driver could use these API to monitor and read
out the uevent, then corresponding to handle these uevent,
such as detach or attach the device.

Guo, Jia (2):
eal: add uevent api for hot plug
net/i40e: add hot plug monitor in i40e

drivers/net/i40e/i40e_ethdev.c | 19 +++
lib/librte_eal/common/eal_common_pci_uio.c | 6 +-
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 136 ++++++++++++++++++++-
lib/librte_eal/linuxapp/eal/eal_pci_uio.c | 6 +
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 37 ++++++
5 files changed, 201 insertions(+), 3 deletions(-)
--
1.8.3.1
Jeff Guo
2017-06-29 04:37:52 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch aim to add a variable "uevent_fd" in structure
"rte_intr_handle" for enable kernel object uevent monitoring,
and add some uevent API in rte eal interrupt, that is
“rte_uevent_connect” and “rte_uevent_get”, so that all driver
could use these API to monitor and read out the uevent, then
corresponding to handle these uevent, such as detach or attach
the device.

Signed-off-by: Guo, Jia <***@intel.com>
---
v3->v2: refine some return error
refine the string searching logic to aviod memory issue
---
lib/librte_eal/common/eal_common_pci_uio.c | 6 +-
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 136 ++++++++++++++++++++-
lib/librte_eal/linuxapp/eal/eal_pci_uio.c | 6 +
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 37 ++++++
4 files changed, 182 insertions(+), 3 deletions(-)

diff --git a/lib/librte_eal/common/eal_common_pci_uio.c b/lib/librte_eal/common/eal_common_pci_uio.c
index 367a681..5b62f70 100644
--- a/lib/librte_eal/common/eal_common_pci_uio.c
+++ b/lib/librte_eal/common/eal_common_pci_uio.c
@@ -117,6 +117,7 @@

dev->intr_handle.fd = -1;
dev->intr_handle.uio_cfg_fd = -1;
+ dev->intr_handle.uevent_fd = -1;
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;

/* secondary processes - use already recorded details */
@@ -227,7 +228,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
-
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
dev->intr_handle.fd = -1;
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
}
diff --git a/lib/librte_eal/linuxapp/eal/eal_interrupts.c b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
index 2e3bd12..2c4a3fb 100644
--- a/lib/librte_eal/linuxapp/eal/eal_interrupts.c
+++ b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
@@ -65,6 +65,10 @@
#include <rte_errno.h>
#include <rte_spinlock.h>

+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <sys/epoll.h>
+
#include "eal_private.h"
#include "eal_vfio.h"
#include "eal_thread.h"
@@ -669,10 +673,13 @@ struct rte_intr_source {
RTE_SET_USED(r);
return -1;
}
+
rte_spinlock_lock(&intr_lock);
TAILQ_FOREACH(src, &intr_sources, next)
- if (src->intr_handle.fd ==
- events[n].data.fd)
+ if ((src->intr_handle.fd ==
+ events[n].data.fd) ||
+ (src->intr_handle.uevent_fd ==
+ events[n].data.fd))
break;
if (src == NULL){
rte_spinlock_unlock(&intr_lock);
@@ -858,7 +865,24 @@ static __attribute__((noreturn)) void *
}
else
numfds++;
+
+ /**
+ * add device uevent file descriptor
+ * into wait list for uevent monitoring.
+ */
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
+ ev.data.fd = src->intr_handle.uevent_fd;
+ if (epoll_ctl(pfd, EPOLL_CTL_ADD,
+ src->intr_handle.uevent_fd, &ev) < 0){
+ rte_panic("Error adding uevent_fd %d epoll_ctl"
+ ", %s\n",
+ src->intr_handle.uevent_fd,
+ strerror(errno));
+ } else
+ numfds++;
}
+
+
rte_spinlock_unlock(&intr_lock);
/* serve the interrupt */
eal_intr_handle_interrupts(pfd, numfds);
@@ -1255,3 +1279,111 @@ static __attribute__((noreturn)) void *

return 0;
}
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd = -1;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event)
+{
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+ int i = 0;
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (i < RTE_UEVENT_MSG_LEN) {
+ for (; i < RTE_UEVENT_MSG_LEN; i++) {
+ if (*buf)
+ break;
+ buf++;
+ }
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ i += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ i += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ i += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ for (; i < RTE_UEVENT_MSG_LEN; i++) {
+ if (*buf == '\0')
+ break;
+ buf++;
+ }
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3))
+ event->action = RTE_UEVENT_ADD;
+ if (!strncmp(action, "remove", 6))
+ event->action = RTE_UEVENT_REMOVE;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent)
+{
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0)
+ return parse_event(buf, uevent);
+ else if (ret < 0) {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ } else
+ /* connection closed */
+ return -1;
+}
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index fa10329..eae9cd5 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -231,6 +231,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
if (dev->intr_handle.fd >= 0) {
close(dev->intr_handle.fd);
dev->intr_handle.fd = -1;
@@ -276,6 +280,8 @@
goto error;
}

+ dev->intr_handle.uevent_fd = rte_uevent_connect();
+
if (dev->kdrv == RTE_KDRV_IGB_UIO)
dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
else {
diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
index 6daffeb..0b31a22 100644
--- a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
+++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
@@ -90,6 +90,7 @@ struct rte_intr_handle {
for uio_pci_generic */
};
int fd; /**< interrupt event file descriptor */
+ int uevent_fd; /**< uevent file descriptor */
enum rte_intr_handle_type type; /**< handle type */
uint32_t max_intr; /**< max interrupt requested */
uint32_t nb_efd; /**< number of available efd(event fd) */
@@ -99,6 +100,19 @@ struct rte_intr_handle {
int *intr_vec; /**< intr vector number array */
};

+#define RTE_UEVENT_MSG_LEN 4096
+#define RTE_UEVENT_SUBSYSTEM_UIO 1
+
+enum rte_uevent_action {
+ RTE_UEVENT_ADD = 0, /**< uevent type of device add */
+ RTE_UEVENT_REMOVE = 1, /**< uevent type of device remove*/
+};
+
+struct rte_uevent {
+ enum rte_uevent_action action; /**< uevent action type */
+ int subsystem; /**< subsystem id */
+};
+
#define RTE_EPOLL_PER_THREAD -1 /**< to hint using per thread epfd */

/**
@@ -236,4 +250,27 @@ struct rte_intr_handle {
int
rte_intr_cap_multiple(struct rte_intr_handle *intr_handle);

+/**
+ * It read out the uevent from the specific file descriptor.
+ *
+ * @param fd
+ * The fd which the uevent associated to
+ * @param uevent
+ * Pointer to the uevent which read from the monitoring fd.
+ * @return
+ * - On success, zero.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent);
+
+/**
+ * Connect to the device uevent file descriptor.
+ * @return
+ * - On success, the connected uevent fd.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_connect(void);
+
#endif /* _RTE_LINUXAPP_INTERRUPTS_H_ */
--
1.8.3.1
Jeff Guo
2017-06-29 04:37:53 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch enable the hot plug feature in i40e, by monitoring the
hot plug uevent of the device. When remove event got, call the app
callback function to handle the detach process.

Signed-off-by: Guo, Jia <***@intel.com>
---
v3->v2: refine the return issue if device remove
---
drivers/net/i40e/i40e_ethdev.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4ee1113..67ffc14 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1283,6 +1283,7 @@ static inline void i40e_GLQF_reg_init(struct i40e_hw *hw)

/* enable uio intr after callback register */
rte_intr_enable(intr_handle);
+
/*
* Add an ethertype filter to drop all flow control frames transmitted
* from VSIs. By doing so, we stop VF from sending out PAUSE or PFC
@@ -5832,11 +5833,29 @@ struct i40e_vsi *
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ struct rte_pci_device *pci_dev;
+ struct rte_intr_handle *intr_handle;
+
+ pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ intr_handle = &pci_dev->intr_handle;

/* Disable interrupt */
i40e_pf_disable_irq0(hw);

+ /* check device uevent */
+ if (rte_uevent_get(intr_handle->uevent_fd, &event) == 0) {
+ if (event.subsystem == RTE_UEVENT_SUBSYSTEM_UIO) {
+ if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ return;
+ }
+ }
+ goto done;
+ }
+
/* read out interrupt causes */
icr0 = I40E_READ_REG(hw, I40E_PFINT_ICR0);
--
1.8.3.1
Jeff Guo
2017-06-29 05:01:48 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch set aim to add a variable "uevent_fd" in structure
"rte_intr_handle" for enable kernel object uevent monitoring,
and add some uevent API in rte eal interrupt, that is
“rte_uevent_connect” and “rte_uevent_get”. The patch use i40e
for example, the driver could use these API to monitor and read
out the uevent, then corresponding to handle these uevent,
such as detach or attach the device.

Guo, Jia (2):
eal: add uevent api for hot plug
net/i40e: add hot plug monitor in i40e

drivers/net/i40e/i40e_ethdev.c | 19 +++
lib/librte_eal/common/eal_common_pci_uio.c | 6 +-
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 136 ++++++++++++++++++++-
lib/librte_eal/linuxapp/eal/eal_pci_uio.c | 6 +
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 37 ++++++
5 files changed, 201 insertions(+), 3 deletions(-)
--
1.8.3.1
Jeff Guo
2017-06-29 05:01:49 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch aim to add a variable "uevent_fd" in structure
"rte_intr_handle" for enable kernel object uevent monitoring,
and add some uevent API in rte eal interrupt, that is
“rte_uevent_connect” and “rte_uevent_get”, so that all driver
could use these API to monitor and read out the uevent, then
corresponding to handle these uevent, such as detach or attach
the device.

Signed-off-by: Guo, Jia <***@intel.com>
---
v3->v2: refine some return error
refine the string searching logic to aviod memory issue
---
lib/librte_eal/common/eal_common_pci_uio.c | 6 +-
lib/librte_eal/linuxapp/eal/eal_interrupts.c | 136 ++++++++++++++++++++-
lib/librte_eal/linuxapp/eal/eal_pci_uio.c | 6 +
.../linuxapp/eal/include/exec-env/rte_interrupts.h | 37 ++++++
4 files changed, 182 insertions(+), 3 deletions(-)

diff --git a/lib/librte_eal/common/eal_common_pci_uio.c b/lib/librte_eal/common/eal_common_pci_uio.c
index 367a681..5b62f70 100644
--- a/lib/librte_eal/common/eal_common_pci_uio.c
+++ b/lib/librte_eal/common/eal_common_pci_uio.c
@@ -117,6 +117,7 @@

dev->intr_handle.fd = -1;
dev->intr_handle.uio_cfg_fd = -1;
+ dev->intr_handle.uevent_fd = -1;
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;

/* secondary processes - use already recorded details */
@@ -227,7 +228,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
-
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
dev->intr_handle.fd = -1;
dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
}
diff --git a/lib/librte_eal/linuxapp/eal/eal_interrupts.c b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
index 2e3bd12..2c4a3fb 100644
--- a/lib/librte_eal/linuxapp/eal/eal_interrupts.c
+++ b/lib/librte_eal/linuxapp/eal/eal_interrupts.c
@@ -65,6 +65,10 @@
#include <rte_errno.h>
#include <rte_spinlock.h>

+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <sys/epoll.h>
+
#include "eal_private.h"
#include "eal_vfio.h"
#include "eal_thread.h"
@@ -669,10 +673,13 @@ struct rte_intr_source {
RTE_SET_USED(r);
return -1;
}
+
rte_spinlock_lock(&intr_lock);
TAILQ_FOREACH(src, &intr_sources, next)
- if (src->intr_handle.fd ==
- events[n].data.fd)
+ if ((src->intr_handle.fd ==
+ events[n].data.fd) ||
+ (src->intr_handle.uevent_fd ==
+ events[n].data.fd))
break;
if (src == NULL){
rte_spinlock_unlock(&intr_lock);
@@ -858,7 +865,24 @@ static __attribute__((noreturn)) void *
}
else
numfds++;
+
+ /**
+ * add device uevent file descriptor
+ * into wait list for uevent monitoring.
+ */
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
+ ev.data.fd = src->intr_handle.uevent_fd;
+ if (epoll_ctl(pfd, EPOLL_CTL_ADD,
+ src->intr_handle.uevent_fd, &ev) < 0){
+ rte_panic("Error adding uevent_fd %d epoll_ctl"
+ ", %s\n",
+ src->intr_handle.uevent_fd,
+ strerror(errno));
+ } else
+ numfds++;
}
+
+
rte_spinlock_unlock(&intr_lock);
/* serve the interrupt */
eal_intr_handle_interrupts(pfd, numfds);
@@ -1255,3 +1279,111 @@ static __attribute__((noreturn)) void *

return 0;
}
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd = -1;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event)
+{
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+ int i = 0;
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (i < RTE_UEVENT_MSG_LEN) {
+ for (; i < RTE_UEVENT_MSG_LEN; i++) {
+ if (*buf)
+ break;
+ buf++;
+ }
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ i += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ i += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ i += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ for (; i < RTE_UEVENT_MSG_LEN; i++) {
+ if (*buf == '\0')
+ break;
+ buf++;
+ }
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3))
+ event->action = RTE_UEVENT_ADD;
+ if (!strncmp(action, "remove", 6))
+ event->action = RTE_UEVENT_REMOVE;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent)
+{
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0)
+ return parse_event(buf, uevent);
+ else if (ret < 0) {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ } else
+ /* connection closed */
+ return -1;
+}
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index fa10329..eae9cd5 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -231,6 +231,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
if (dev->intr_handle.fd >= 0) {
close(dev->intr_handle.fd);
dev->intr_handle.fd = -1;
@@ -276,6 +280,8 @@
goto error;
}

+ dev->intr_handle.uevent_fd = rte_uevent_connect();
+
if (dev->kdrv == RTE_KDRV_IGB_UIO)
dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
else {
diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
index 6daffeb..0b31a22 100644
--- a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
+++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_interrupts.h
@@ -90,6 +90,7 @@ struct rte_intr_handle {
for uio_pci_generic */
};
int fd; /**< interrupt event file descriptor */
+ int uevent_fd; /**< uevent file descriptor */
enum rte_intr_handle_type type; /**< handle type */
uint32_t max_intr; /**< max interrupt requested */
uint32_t nb_efd; /**< number of available efd(event fd) */
@@ -99,6 +100,19 @@ struct rte_intr_handle {
int *intr_vec; /**< intr vector number array */
};

+#define RTE_UEVENT_MSG_LEN 4096
+#define RTE_UEVENT_SUBSYSTEM_UIO 1
+
+enum rte_uevent_action {
+ RTE_UEVENT_ADD = 0, /**< uevent type of device add */
+ RTE_UEVENT_REMOVE = 1, /**< uevent type of device remove*/
+};
+
+struct rte_uevent {
+ enum rte_uevent_action action; /**< uevent action type */
+ int subsystem; /**< subsystem id */
+};
+
#define RTE_EPOLL_PER_THREAD -1 /**< to hint using per thread epfd */

/**
@@ -236,4 +250,27 @@ struct rte_intr_handle {
int
rte_intr_cap_multiple(struct rte_intr_handle *intr_handle);

+/**
+ * It read out the uevent from the specific file descriptor.
+ *
+ * @param fd
+ * The fd which the uevent associated to
+ * @param uevent
+ * Pointer to the uevent which read from the monitoring fd.
+ * @return
+ * - On success, zero.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent);
+
+/**
+ * Connect to the device uevent file descriptor.
+ * @return
+ * - On success, the connected uevent fd.
+ * - On failure, a negative value.
+ */
+int
+rte_uevent_connect(void);
+
#endif /* _RTE_LINUXAPP_INTERRUPTS_H_ */
--
1.8.3.1
Jeff Guo
2017-06-29 05:01:50 UTC
Permalink
From: "Guo, Jia" <***@intel.com>

This patch enable the hot plug feature in i40e, by monitoring the
hot plug uevent of the device. When remove event got, call the app
callback function to handle the detach process.

Signed-off-by: Guo, Jia <***@intel.com>
---
v3->v2: refine the return issue if device remove
---
drivers/net/i40e/i40e_ethdev.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4ee1113..67ffc14 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -1283,6 +1283,7 @@ static inline void i40e_GLQF_reg_init(struct i40e_hw *hw)

/* enable uio intr after callback register */
rte_intr_enable(intr_handle);
+
/*
* Add an ethertype filter to drop all flow control frames transmitted
* from VSIs. By doing so, we stop VF from sending out PAUSE or PFC
@@ -5832,11 +5833,29 @@ struct i40e_vsi *
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct rte_uevent event;
uint32_t icr0;
+ struct rte_pci_device *pci_dev;
+ struct rte_intr_handle *intr_handle;
+
+ pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ intr_handle = &pci_dev->intr_handle;

/* Disable interrupt */
i40e_pf_disable_irq0(hw);

+ /* check device uevent */
+ if (rte_uevent_get(intr_handle->uevent_fd, &event) == 0) {
+ if (event.subsystem == RTE_UEVENT_SUBSYSTEM_UIO) {
+ if (event.action == RTE_UEVENT_REMOVE) {
+ _rte_eth_dev_callback_process(dev,
+ RTE_ETH_EVENT_INTR_RMV, NULL);
+ return;
+ }
+ }
+ goto done;
+ }
+
/* read out interrupt causes */
icr0 = I40E_READ_REG(hw, I40E_PFINT_ICR0);
--
1.8.3.1
Wu, Jingjing
2017-06-29 02:25:29 UTC
Permalink
Post by Jeff Guo
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd = -1;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM,
NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ RTE_LOG(ERR, EAL,
+ "netlink_fd is %d\n", netlink_fd);
Is this a ERR log?
Post by Jeff Guo
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size,
+sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event) {
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+ int i = 0;
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (*buf && i < RTE_UEVENT_MSG_LEN) {
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ while (*buf++)
+ i++;
+ while (*buf == '\0') {
+ buf++;
+ i++;
+ }
++ until to the end? The logic looks wrong. Please check carefully.
Post by Jeff Guo
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3))
+ event->action = RTE_UEVENT_ADD;
+ if (!strncmp(action, "remove", 6))
+ event->action = RTE_UEVENT_REMOVE;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent) {
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0)
+ return parse_event(buf, uevent);
+
+ if (ret < 0) {
Meaningless check.
Post by Jeff Guo
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
Return -1? The function is declared as 0 means success.
Post by Jeff Guo
+ } else {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
Why not return?
Post by Jeff Guo
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0)
+ return -1;
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index fa10329..2fead82 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -231,6 +231,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
if (dev->intr_handle.fd >= 0) {
close(dev->intr_handle.fd);
dev->intr_handle.fd = -1;
@@ -245,6 +249,7 @@
char dirname[PATH_MAX];
char cfgname[PATH_MAX];
char devname[PATH_MAX]; /* contains the /dev/uioX */
+ char uevtname[PATH_MAX];
int uio_num;
struct rte_pci_addr *loc;
@@ -276,6 +281,13 @@
goto error;
}
+ dev->intr_handle.uevent_fd = rte_uevent_connect();
+ if (dev->intr_handle.uevent_fd < 0) {
+ RTE_LOG(ERR, EAL, "Cannot open %s: %s\n",
+ uevtname, strerror(errno));
+ goto error;
It seems uevtname is not assigned at all. Do we need it? And the log may means
"cannot connect the event fd", right?. And even the event fd is failed to create,
should it block the process?
Guo, Jia
2017-06-29 04:29:01 UTC
Permalink
The buf have contain lot of consistent '/0', so it is why I need to check that. anyway I will refine that and other return issue in v3.

Thanks jingjing .

Best regards,
Jeff Guo


-----Original Message-----
From: Wu, Jingjing
Sent: Thursday, June 29, 2017 10:25 AM
To: Guo, Jia <***@intel.com>; Zhang, Helin <***@intel.com>
Cc: ***@dpdk.org
Subject: RE: [PATCH v2 1/2] eal: add uevent api for hot plug
Post by Jeff Guo
+
+int
+rte_uevent_connect(void)
+{
+ struct sockaddr_nl addr;
+ int ret;
+ int netlink_fd = -1;
+ int size = 64 * 1024;
+ int nonblock = 1;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0xffffffff;
+
+ netlink_fd = socket(PF_NETLINK, SOCK_DGRAM,
NETLINK_KOBJECT_UEVENT);
+ if (netlink_fd < 0)
+ return -1;
+
+ RTE_LOG(ERR, EAL,
+ "netlink_fd is %d\n", netlink_fd);
Is this a ERR log?
Post by Jeff Guo
+
+ setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size,
+sizeof(size));
+
+ ret = ioctl(netlink_fd, FIONBIO, &nonblock);
+ if (ret != 0) {
+ RTE_LOG(ERR, EAL,
+ "ioctl(FIONBIO) failed\n");
+ close(netlink_fd);
+ return -1;
+ }
+
+ if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(netlink_fd);
+ return -1;
+ }
+
+ return netlink_fd;
+}
+
+static int
+parse_event(const char *buf, struct rte_uevent *event) {
+ char action[RTE_UEVENT_MSG_LEN];
+ char subsystem[RTE_UEVENT_MSG_LEN];
+ char dev_path[RTE_UEVENT_MSG_LEN];
+ int i = 0;
+
+ memset(action, 0, RTE_UEVENT_MSG_LEN);
+ memset(subsystem, 0, RTE_UEVENT_MSG_LEN);
+ memset(dev_path, 0, RTE_UEVENT_MSG_LEN);
+
+ while (*buf && i < RTE_UEVENT_MSG_LEN) {
+ if (!strncmp(buf, "ACTION=", 7)) {
+ buf += 7;
+ snprintf(action, sizeof(action), "%s", buf);
+ } else if (!strncmp(buf, "DEVPATH=", 8)) {
+ buf += 8;
+ snprintf(dev_path, sizeof(dev_path), "%s", buf);
+ } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
+ buf += 10;
+ snprintf(subsystem, sizeof(subsystem), "%s", buf);
+ }
+ while (*buf++)
+ i++;
+ while (*buf == '\0') {
+ buf++;
+ i++;
+ }
++ until to the end? The logic looks wrong. Please check carefully.
Post by Jeff Guo
+ }
+
+ if (!strncmp(subsystem, "uio", 3)) {
+
+ event->subsystem = RTE_UEVENT_SUBSYSTEM_UIO;
+ if (!strncmp(action, "add", 3))
+ event->action = RTE_UEVENT_ADD;
+ if (!strncmp(action, "remove", 6))
+ event->action = RTE_UEVENT_REMOVE;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+rte_uevent_get(int fd, struct rte_uevent *uevent) {
+ int ret;
+ char buf[RTE_UEVENT_MSG_LEN];
+
+ memset(uevent, 0, sizeof(struct rte_uevent));
+ memset(buf, 0, RTE_UEVENT_MSG_LEN);
+
+ ret = recv(fd, buf, RTE_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
+ if (ret > 0)
+ return parse_event(buf, uevent);
+
+ if (ret < 0) {
Meaningless check.
Post by Jeff Guo
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
Return -1? The function is declared as 0 means success.
Post by Jeff Guo
+ } else {
+ RTE_LOG(ERR, EAL,
+ "Socket read error(%d): %s\n",
+ errno, strerror(errno));
Why not return?
Post by Jeff Guo
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0)
+ return -1;
+
+ return 0;
+}
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index fa10329..2fead82 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -231,6 +231,10 @@
close(dev->intr_handle.uio_cfg_fd);
dev->intr_handle.uio_cfg_fd = -1;
}
+ if (dev->intr_handle.uevent_fd >= 0) {
+ close(dev->intr_handle.uevent_fd);
+ dev->intr_handle.uevent_fd = -1;
+ }
if (dev->intr_handle.fd >= 0) {
close(dev->intr_handle.fd);
dev->intr_handle.fd = -1;
@@ -245,6 +249,7 @@
char dirname[PATH_MAX];
char cfgname[PATH_MAX];
char devname[PATH_MAX]; /* contains the /dev/uioX */
+ char uevtname[PATH_MAX];
int uio_num;
struct rte_pci_addr *loc;
@@ -276,6 +281,13 @@
goto error;
}
+ dev->intr_handle.uevent_fd = rte_uevent_connect();
+ if (dev->intr_handle.uevent_fd < 0) {
+ RTE_LOG(ERR, EAL, "Cannot open %s: %s\n",
+ uevtname, strerror(errno));
+ goto error;
It seems uevtname is not assigned at all. Do we need it? And the log may means "cannot connect the event fd", right?. And even the event fd is failed to create,
Continue reading on narkive:
Loading...