From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <jia.guo@intel.com>
Received: from mga09.intel.com (mga09.intel.com [134.134.136.24])
 by dpdk.org (Postfix) with ESMTP id 170D91B415
 for <dev@dpdk.org>; Tue, 30 Jan 2018 13:21:37 +0100 (CET)
X-Amp-Result: SKIPPED(no attachment in message)
X-Amp-File-Uploaded: False
Received: from orsmga005.jf.intel.com ([10.7.209.41])
 by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;
 30 Jan 2018 04:21:37 -0800
X-ExtLoop1: 1
X-IronPort-AV: E=Sophos;i="5.46,435,1511856000"; d="scan'208";a="197320456"
Received: from jeffguo-z170x-ud5.sh.intel.com (HELO localhost.localdomain)
 ([10.67.104.10])
 by orsmga005.jf.intel.com with ESMTP; 30 Jan 2018 04:21:35 -0800
From: Jeff Guo <jia.guo@intel.com>
To: stephen@networkplumber.org, bruce.richardson@intel.com,
 gaetan.rivet@6wind.com, jingjing.wu@intel.com, thomas@monjalon.net,
 motih@mellanox.com
Cc: ferruh.yigit@intel.com, konstantin.ananyev@intel.com,
 jblunck@infradead.org, shreyansh.jain@nxp.com, dev@dpdk.org,
 jia.guo@intel.com, helin.zhang@intel.com, harry.van.haaren@intel.com,
 jianfeng.tan@intel.com
Date: Tue, 30 Jan 2018 20:21:00 +0800
Message-Id: <1517314860-8097-3-git-send-email-jia.guo@intel.com>
X-Mailer: git-send-email 2.7.4
In-Reply-To: <1517314860-8097-1-git-send-email-jia.guo@intel.com>
References: <1516938577-27662-3-git-send-email-jia.guo@intel.com>
 <1517314860-8097-1-git-send-email-jia.guo@intel.com>
Subject: [dpdk-dev] [PATCH V14 3/3] app/testpmd: use uevent to monitor
	hotplug
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://dpdk.org/ml/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://dpdk.org/ml/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://dpdk.org/ml/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
X-List-Received-Date: Tue, 30 Jan 2018 12:21:38 -0000

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>
---
v14->v13:
no change
---
 app/test-pmd/testpmd.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++
 app/test-pmd/testpmd.h |   9 +++
 2 files changed, 178 insertions(+)

diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index d8ac432..36b7325 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -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");
 
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 47f8fa8..c797667 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -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,
-- 
2.7.4