> -----Original Message-----
> From: Guo, Jia
> Sent: Tuesday, January 30, 2018 8:21 PM
> To: stephen@networkplumber.org; Richardson, Bruce <bruce.richardson@intel.com>;
> gaetan.rivet@6wind.com; Wu, Jingjing <jingjing.wu@intel.com>; thomas@monjalon.net;
> motih@mellanox.com
> Cc: Yigit, Ferruh <ferruh.yigit@intel.com>; Ananyev, Konstantin
> <konstantin.ananyev@intel.com>; jblunck@infradead.org; shreyansh.jain@nxp.com;
> dev@dpdk.org; Guo, Jia <jia.guo@intel.com>; Zhang, Helin <helin.zhang@intel.com>;
> Van Haaren, Harry <harry.van.haaren@intel.com>; Tan, Jianfeng
> <jianfeng.tan@intel.com>
> Subject: [PATCH V14 3/3] app/testpmd: use uevent to monitor hotplug
>
> use testpmd for example, to show app how to request and use
> uevent monitoring to handle the hot removal event and the
> hot insertion event.
>
> Signed-off-by: Jeff Guo <jia.guo@intel.com>
Acked-by: Jingjing Wu <jingjing.wu@intel.com>
@@ -12,6 +12,7 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>
+#include <stdbool.h>
#include <sys/queue.h>
#include <sys/stat.h>
@@ -368,6 +369,8 @@ uint8_t bitrate_enabled;
struct gro_status gro_ports[RTE_MAX_ETHPORTS];
uint8_t gro_flush_cycles = GRO_DEFAULT_FLUSH_CYCLES;
+static struct hotplug_request_list hp_list;
+
/* Forward function declarations */
static void map_port_queue_stats_mapping_registers(portid_t pi,
struct rte_port *port);
@@ -375,6 +378,13 @@ static void check_all_ports_link_status(uint32_t port_mask);
static int eth_event_callback(portid_t port_id,
enum rte_eth_event_type type,
void *param, void *ret_param);
+static int eth_uevent_callback(char *device_name, enum rte_dev_event_type type,
+ void *param);
+static int eth_uevent_callback_register(portid_t port_id);
+static bool in_hotplug_list(const char *dev_name);
+
+static int hotplug_list_add(const char *dev_name,
+ enum rte_dev_event_type event);
/*
* Check if all the ports are started.
@@ -1838,6 +1848,27 @@ reset_port(portid_t pid)
printf("Done\n");
}
+static int
+eth_uevent_callback_register(portid_t port_id)
+{
+ int diag;
+ char device_name[128];
+
+ snprintf(device_name, sizeof(device_name),
+ "%s", rte_eth_devices[port_id].device->name);
+
+ /* register the uevent callback */
+
+ diag = rte_dev_callback_register(device_name,
+ eth_uevent_callback, (void *)(intptr_t)port_id);
+ if (diag) {
+ printf("Failed to setup uevent callback\n");
+ return -1;
+ }
+
+ return 0;
+}
+
void
attach_port(char *identifier)
{
@@ -1854,6 +1885,8 @@ attach_port(char *identifier)
if (rte_eth_dev_attach(identifier, &pi))
return;
+ eth_uevent_callback_register(pi);
+
socket_id = (unsigned)rte_eth_dev_socket_id(pi);
/* if socket_id is invalid, set to 0 */
if (check_socket_id(socket_id) < 0)
@@ -1865,6 +1898,8 @@ attach_port(char *identifier)
ports[pi].port_status = RTE_PORT_STOPPED;
+ hotplug_list_add(identifier, RTE_DEV_EVENT_REMOVE);
+
printf("Port %d is attached. Now total ports is %d\n", pi, nb_ports);
printf("Done\n");
}
@@ -1891,6 +1926,9 @@ detach_port(portid_t port_id)
nb_ports = rte_eth_dev_count();
+ hotplug_list_add(rte_eth_devices[port_id].device->name,
+ RTE_DEV_EVENT_ADD);
+
printf("Port '%s' is detached. Now total ports is %d\n",
name, nb_ports);
printf("Done\n");
@@ -1914,6 +1952,9 @@ pmd_test_exit(void)
close_port(pt_id);
}
}
+
+ rte_dev_event_monitor_stop();
+
printf("\nBye...\n");
}
@@ -1998,6 +2039,49 @@ rmv_event_callback(void *arg)
dev->device->name);
}
+static void
+rmv_uevent_callback(void *arg)
+{
+ char name[RTE_ETH_NAME_MAX_LEN];
+ uint8_t port_id = (intptr_t)arg;
+
+ rte_eal_alarm_cancel(rmv_uevent_callback, arg);
+
+ RTE_ETH_VALID_PORTID_OR_RET(port_id);
+ printf("removing port id:%u\n", port_id);
+
+ if (!in_hotplug_list(rte_eth_devices[port_id].device->name))
+ return;
+
+ stop_packet_forwarding();
+
+ stop_port(port_id);
+ close_port(port_id);
+ if (rte_eth_dev_detach(port_id, name)) {
+ TESTPMD_LOG(ERR, "Failed to detach port '%s'\n", name);
+ return;
+ }
+
+ nb_ports = rte_eth_dev_count();
+
+ printf("Port '%s' is detached. Now total ports is %d\n",
+ name, nb_ports);
+}
+
+static void
+add_uevent_callback(void *arg)
+{
+ char *dev_name = (char *)arg;
+
+ rte_eal_alarm_cancel(add_uevent_callback, arg);
+
+ if (!in_hotplug_list(dev_name))
+ return;
+
+ printf("add device: %s\n", dev_name);
+ attach_port(dev_name);
+}
+
/* This function is used by the interrupt thread */
static int
eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
@@ -2041,6 +2125,82 @@ eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
return 0;
}
+static bool
+in_hotplug_list(const char *dev_name)
+{
+ struct hotplug_request *hp_request = NULL;
+
+ TAILQ_FOREACH(hp_request, &hp_list, next) {
+ if (!strcmp(hp_request->dev_name, dev_name))
+ break;
+ }
+
+ if (hp_request)
+ return true;
+
+ return false;
+}
+
+static int
+hotplug_list_add(const char *dev_name, enum rte_dev_event_type event)
+{
+ struct hotplug_request *hp_request;
+
+ hp_request = rte_zmalloc("hoplug request",
+ sizeof(*hp_request), 0);
+ if (hp_request == NULL) {
+ fprintf(stderr, "%s can not alloc memory\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ hp_request->dev_name = dev_name;
+ hp_request->event = event;
+
+ TAILQ_INSERT_TAIL(&hp_list, hp_request, next);
+
+ return 0;
+}
+
+/* This function is used by the interrupt thread */
+static int
+eth_uevent_callback(char *device_name, enum rte_dev_event_type type, void *arg)
+{
+ static const char * const event_desc[] = {
+ [RTE_DEV_EVENT_UNKNOWN] = "Unknown",
+ [RTE_DEV_EVENT_ADD] = "add",
+ [RTE_DEV_EVENT_REMOVE] = "remove",
+ };
+
+ if (type >= RTE_DEV_EVENT_MAX) {
+ fprintf(stderr, "%s called upon invalid event %d\n",
+ __func__, type);
+ fflush(stderr);
+ } else if (event_print_mask & (UINT32_C(1) << type)) {
+ printf("%s event\n",
+ event_desc[type]);
+ fflush(stdout);
+ }
+
+ switch (type) {
+ case RTE_DEV_EVENT_REMOVE:
+ if (rte_eal_alarm_set(100000,
+ rmv_uevent_callback, arg))
+ fprintf(stderr, "Could not set up deferred "
+ "device removal\n");
+ break;
+ case RTE_DEV_EVENT_ADD:
+ if (rte_eal_alarm_set(500000,
+ add_uevent_callback, (void *)device_name))
+ fprintf(stderr, "Could not set up deferred "
+ "device add\n");
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
static int
set_tx_queue_stats_mapping_registers(portid_t port_id, struct rte_port *port)
{
@@ -2522,6 +2682,15 @@ main(int argc, char** argv)
nb_rxq, nb_txq);
init_config();
+
+ /* enable hot plug monitoring */
+ TAILQ_INIT(&hp_list);
+ rte_dev_event_monitor_start();
+ RTE_ETH_FOREACH_DEV(port_id) {
+ hotplug_list_add(rte_eth_devices[port_id].device->name,
+ RTE_DEV_EVENT_REMOVE);
+ eth_uevent_callback_register(port_id);
+ }
if (start_port(RTE_PORT_ALL) != 0)
rte_exit(EXIT_FAILURE, "Start ports failed\n");
@@ -63,6 +63,15 @@ typedef uint16_t streamid_t;
#define TM_MODE 0
#endif
+struct hotplug_request {
+ TAILQ_ENTRY(hotplug_request) next; /**< Callbacks list */
+ const char *dev_name; /* request device name */
+ enum rte_dev_event_type event; /**< device event type */
+};
+
+/** @internal Structure to keep track of registered callbacks */
+TAILQ_HEAD(hotplug_request_list, hotplug_request);
+
enum {
PORT_TOPOLOGY_PAIRED,
PORT_TOPOLOGY_CHAINED,