[dpdk-dev,V18,5/5] app/testpmd: use auto handle for hotplug

Message ID 1522779443-1932-6-git-send-email-jia.guo@intel.com (mailing list archive)
State Superseded, archived
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail apply issues

Commit Message

Guo, Jia April 3, 2018, 6:17 p.m. UTC
  Use testpmd for example, to show how an application smoothly handle
failure when device be hot removal, and show how to auto bind kernal
driver to preparing attach device when device being hot insertion.

Signed-off-by: Jeff Guo <jia.guo@intel.com>
---
v16->v15:
refine some typo
---
 app/test-pmd/testpmd.c | 178 ++++++++++++++++++++++++++++++++++++++++++-------
 app/test-pmd/testpmd.h |   9 +++
 2 files changed, 164 insertions(+), 23 deletions(-)
  

Comments

Guo, Jia April 6, 2018, 10:56 a.m. UTC | #1
At the prior, device event monitor machenism have been introduced.
But for device hot unplug, if we want data path would not be break when
device hot plug in or out, we still need some preparatory measures to do
some preparation work for the device detach and attach, so that we will
not encounter memory fault after device have been plug out of the system,
and also let user directly attach device which have been auto bind onto
the specific kernel driver.

This patch set will introduces two APIs to do that failure and auto bind
handle for hot plug feature, and also use testpmd to show example how to
use these 2 APIs for process hot plug event, let the process could be
smoothly like below case:

1)hot plug removal:
plugout->failure handle->stop forward->stop port->close port->detach port

2)hot plug insertion:
plugin->kernel driver auto bind->attach port->start port

with this machenism, every user such as fail-safe driver or testpmd, if
enable device event monitor they will be able to develop their own
hotplug application.

patchset history:
v19->18:
note for limitation of multiple hotplug,fix some typo, sqeeze patch.

v18->v15:
add document, add signal bus handler, refine the code to be more clear.

the prior patch history please check the patch set
"add device event monitor framework"

Jeff Guo (4):
  bus/pci: introduce device hot unplug handle
  eal: add failure handler mechanism for hot plug
  eal: add driver auto bind for hot insertion
  app/testpmd: use auto handle for hotplug

 app/test-pmd/testpmd.c                  | 199 ++++++++++++++++++++++++++++----
 app/test-pmd/testpmd.h                  |   9 ++
 doc/guides/rel_notes/release_18_05.rst  |   8 ++
 drivers/bus/pci/pci_common.c            |  42 +++++++
 drivers/bus/pci/pci_common_uio.c        |  32 +++++
 drivers/bus/pci/private.h               |  12 ++
 kernel/linux/igb_uio/igb_uio.c          |   4 +
 lib/librte_eal/bsdapp/eal/eal_dev.c     |   7 ++
 lib/librte_eal/common/include/rte_bus.h |  15 +++
 lib/librte_eal/common/include/rte_dev.h |  35 ++++++
 lib/librte_eal/linuxapp/eal/eal_dev.c   | 194 ++++++++++++++++++++++++++++++-
 lib/librte_eal/rte_eal_version.map      |   2 +
 12 files changed, 534 insertions(+), 25 deletions(-)
  

Patch

diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 2faeb90..791378d 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -285,6 +285,8 @@  uint8_t lsc_interrupt = 1; /* enabled by default */
  */
 uint8_t rmv_interrupt = 1; /* enabled by default */
 
+#define HOT_PLUG_FOR_ALL_DEVICE -1
+#define ALL_CALLBACK -1
 uint8_t hot_plug = 0; /**< hotplug disabled by default. */
 
 /*
@@ -387,6 +389,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);
@@ -397,9 +401,12 @@  static int eth_event_callback(portid_t port_id,
 static int eth_dev_event_callback(char *device_name,
 				enum rte_dev_event_type type,
 				void *param);
-static int eth_dev_event_callback_register(void);
-static int eth_dev_event_callback_unregister(void);
+static int eth_dev_event_callback_register(portid_t port_id);
+static int eth_dev_event_callback_unregister(portid_t port_id);
 
+static bool in_hotplug_list(const char *dev_name);
+static int hotplug_list_add(struct rte_device *device,
+				enum rte_kernel_driver device_kdrv);
 
 /*
  * Check if all the ports are started.
@@ -1120,11 +1127,19 @@  run_pkt_fwd_on_lcore(struct fwd_lcore *fc, packet_fwd_t pkt_fwd)
 	uint64_t tics_datum;
 	uint64_t tics_current;
 	uint8_t idx_port, cnt_ports;
+	int ret;
 
 	cnt_ports = rte_eth_dev_count();
 	tics_datum = rte_rdtsc();
 	tics_per_1sec = rte_get_timer_hz();
 #endif
+	if (hot_plug) {
+		ret = rte_dev_handle_hot_unplug();
+		if (ret) {
+			printf("Can not setup handler for hot unplug!\n");
+			return;
+		}
+	}
 	fsm = &fwd_streams[fc->stream_idx];
 	nb_fs = fc->stream_nb;
 	do {
@@ -1863,15 +1878,24 @@  reset_port(portid_t pid)
 }
 
 static int
-eth_dev_event_callback_register(void)
+eth_dev_event_callback_register(portid_t port_id)
 {
-	int diag;
+	int ret;
+	char *device_name;
 
+	/* if port id equal -1, unregister event callbacks for all device. */
+	if (port_id == (portid_t)HOT_PLUG_FOR_ALL_DEVICE) {
+		device_name = NULL;
+	} else {
+		device_name = strdup(rte_eth_devices[port_id].device->name);
+		if (!device_name)
+			return -1;
+	}
 	/* register the device event callback */
-	diag = rte_dev_event_callback_register(NULL,
-		eth_dev_event_callback, NULL);
-	if (diag) {
-		printf("Failed to setup dev_event callback\n");
+	ret = rte_dev_event_callback_register(device_name,
+		eth_dev_event_callback, (void *)(intptr_t)port_id);
+	if (ret) {
+		printf("Failed to register device event callback.\n");
 		return -1;
 	}
 
@@ -1880,15 +1904,25 @@  eth_dev_event_callback_register(void)
 
 
 static int
-eth_dev_event_callback_unregister(void)
+eth_dev_event_callback_unregister(portid_t port_id)
 {
-	int diag;
+	int ret;
+	char *device_name;
+
+	/* if port id equal -1, unregister all device event callbacks */
+	if (port_id == (portid_t)HOT_PLUG_FOR_ALL_DEVICE) {
+		device_name = NULL;
+	} else {
+		device_name = strdup(rte_eth_devices[port_id].device->name);
+		if (!device_name)
+			return -1;
+	}
 
 	/* unregister the device event callback */
-	diag = rte_dev_event_callback_unregister(NULL,
-		eth_dev_event_callback, NULL);
-	if (diag) {
-		printf("Failed to setup dev_event callback\n");
+	ret = rte_dev_event_callback_unregister(device_name,
+		eth_dev_event_callback, (void *)(intptr_t)port_id);
+	if (ret) {
+		printf("Failed to unregister device event callback.\n");
 		return -1;
 	}
 
@@ -1911,6 +1945,8 @@  attach_port(char *identifier)
 	if (rte_eth_dev_attach(identifier, &pi))
 		return;
 
+	eth_dev_event_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)
@@ -1922,6 +1958,12 @@  attach_port(char *identifier)
 
 	ports[pi].port_status = RTE_PORT_STOPPED;
 
+	if (hot_plug) {
+		hotplug_list_add(rte_eth_devices[pi].device,
+				 rte_eth_devices[pi].data->kdrv);
+		eth_dev_event_callback_register(pi);
+	}
+
 	printf("Port %d is attached. Now total ports is %d\n", pi, nb_ports);
 	printf("Done\n");
 }
@@ -1948,6 +1990,12 @@  detach_port(portid_t port_id)
 
 	nb_ports = rte_eth_dev_count();
 
+	if (hot_plug) {
+		hotplug_list_add(rte_eth_devices[port_id].device,
+				 rte_eth_devices[port_id].data->kdrv);
+		eth_dev_event_callback_register(port_id);
+	}
+
 	printf("Port '%s' is detached. Now total ports is %d\n",
 			name, nb_ports);
 	printf("Done\n");
@@ -1979,7 +2027,7 @@  pmd_test_exit(void)
 			RTE_LOG(ERR, EAL,
 				"fail to stop device event monitor.");
 
-		ret = eth_dev_event_callback_unregister();
+		ret = eth_dev_event_callback_unregister(ALL_CALLBACK);
 		if (ret)
 			RTE_LOG(ERR, EAL,
 				"fail to unregister all event callbacks.");
@@ -2068,6 +2116,31 @@  rmv_event_callback(void *arg)
 			dev->device->name);
 }
 
+static void
+rmv_dev_event_callback(uint16_t port_id)
+{
+	RTE_ETH_VALID_PORTID_OR_RET(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);
+	detach_port(port_id);
+}
+
+static void
+add_dev_event_callback(char *dev_name)
+{
+	if (!in_hotplug_list(dev_name))
+		return;
+
+	RTE_LOG(ERR, EAL, "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,
@@ -2114,16 +2187,68 @@  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 enum rte_kernel_driver
+get_hotplug_driver(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))
+			return hp_request->dev_kdrv;
+	}
+	return -1;
+}
+
+static int
+hotplug_list_add(struct rte_device *device, enum rte_kernel_driver device_kdrv)
+{
+	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 = device->name;
+	hp_request->dev_kdrv = device_kdrv;
+
+	TAILQ_INSERT_TAIL(&hp_list, hp_request, next);
+
+	return 0;
+}
+
 /* This function is used by the interrupt thread */
 static int
 eth_dev_event_callback(char *device_name, enum rte_dev_event_type type,
-			     __rte_unused void *arg)
+			     void *arg)
 {
 	int ret = 0;
 	static const char * const event_desc[] = {
 		[RTE_DEV_EVENT_ADD] = "add",
 		[RTE_DEV_EVENT_REMOVE] = "remove",
 	};
+	char *dev_name = malloc(strlen(device_name) + 1);
+
+	strcpy(dev_name, device_name);
 
 	if (type >= RTE_DEV_EVENT_MAX) {
 		fprintf(stderr, "%s called upon invalid event %d\n",
@@ -2139,16 +2264,18 @@  eth_dev_event_callback(char *device_name, enum rte_dev_event_type type,
 	case RTE_DEV_EVENT_REMOVE:
 		RTE_LOG(ERR, EAL, "The device: %s has been removed!\n",
 			device_name);
-		/* TODO: After finish failure handle, begin to stop
-		 * packet forward, stop port, close port, detach port.
-		 */
+		rmv_dev_event_callback((uint16_t)(uintptr_t)arg);
 		break;
 	case RTE_DEV_EVENT_ADD:
 		RTE_LOG(ERR, EAL, "The device: %s has been added!\n",
 			device_name);
-		/* TODO: After finish kernel driver binding,
-		 * begin to attach port.
+		/**
+		 * bind the driver to the device
+		 * before process of hot plug adding device
 		 */
+		rte_dev_bind_kernel_driver(dev_name,
+					   get_hotplug_driver(dev_name));
+		add_dev_event_callback(dev_name);
 		break;
 	default:
 		break;
@@ -2649,8 +2776,13 @@  main(int argc, char** argv)
 			rte_errno = EINVAL;
 			return -1;
 		}
-		eth_dev_event_callback_register();
-
+		if (TAILQ_EMPTY(&hp_list))
+			TAILQ_INIT(&hp_list);
+		RTE_ETH_FOREACH_DEV(port_id) {
+			hotplug_list_add(rte_eth_devices[port_id].device,
+					 rte_eth_devices[port_id].data->kdrv);
+			eth_dev_event_callback_register(port_id);
+		}
 	}
 
 	if (start_port(RTE_PORT_ALL) != 0)
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 8fde68d..c619e11 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_kernel_driver dev_kdrv;            /* kernel driver binded */
+};
+
+/** @internal Structure to keep track of registered callbacks */
+TAILQ_HEAD(hotplug_request_list, hotplug_request);
+
 enum {
 	PORT_TOPOLOGY_PAIRED,
 	PORT_TOPOLOGY_CHAINED,