diff --git a/lib/librte_eal/common/include/rte_pci.h b/lib/librte_eal/common/include/rte_pci.h
index ac30925..b9cdf8b 100644
--- a/lib/librte_eal/common/include/rte_pci.h
+++ b/lib/librte_eal/common/include/rte_pci.h
@@ -313,6 +313,15 @@ rte_eal_compare_pci_addr(struct rte_pci_addr *addr, struct rte_pci_addr *addr2)
 }
 
 /**
+ * Scan the content of the PCI bus, and the devices in the devices
+ * list
+ *
+ * @return
+ *  0 on success, negative on error
+ */
+int rte_eal_pci_scan(void);
+
+/**
  * Probe the PCI bus for registered drivers.
  *
  * Scan the content of the PCI bus, and call the probe() function for
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci.c b/lib/librte_eal/linuxapp/eal/eal_pci.c
index f880f90..6d4932d 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci.c
@@ -440,8 +440,8 @@ error:
  * Scan the content of the PCI bus, and the devices in the devices
  * list
  */
-static int
-pci_scan(void)
+int
+rte_eal_pci_scan(void)
 {
 	struct dirent *e;
 	DIR *dir;
@@ -773,7 +773,7 @@ rte_eal_pci_init(void)
 	if (internal_config.no_pci)
 		return 0;
 
-	if (pci_scan() < 0) {
+	if (rte_eal_pci_scan() < 0) {
 		RTE_LOG(ERR, EAL, "%s(): Cannot scan PCI bus\n", __func__);
 		return -1;
 	}
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 214643d..7c2aac3 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -45,6 +45,7 @@ DPDK_2.0 {
 	rte_eal_pci_probe_one;
 	rte_eal_pci_register;
 	rte_eal_pci_unregister;
+	rte_eal_pci_scan;
 	rte_eal_process_type;
 	rte_eal_remote_launch;
 	rte_eal_tailq_lookup;
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index b07f2df..868289c 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -201,7 +201,7 @@ rte_eth_dev_data_alloc(void)
 				RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data));
 }
 
-static struct rte_eth_dev *
+struct rte_eth_dev *
 rte_eth_dev_allocated(const char *name)
 {
 	unsigned i;
@@ -270,7 +270,6 @@ rte_eth_dev_create_unique_device_name(char *name, size_t size,
 			pci_dev->addr.function);
 	if (ret < 0)
 		return ret;
-
 	return 0;
 }
 
@@ -427,6 +426,306 @@ rte_eth_dev_count(void)
 	return (nb_ports);
 }
 
+static enum rte_eth_dev_type
+rte_eth_dev_get_device_type(uint8_t port_id)
+{
+	if (!rte_eth_dev_is_valid_port(port_id))
+		return -1;
+	return rte_eth_devices[port_id].dev_type;
+}
+
+static int
+rte_eth_dev_save(struct rte_eth_dev *devs, size_t size)
+{
+	if ((devs == NULL) ||
+	    (size != sizeof(struct rte_eth_dev) * RTE_MAX_ETHPORTS))
+		return -EINVAL;
+
+	/* save current rte_eth_devices */
+	memcpy(devs, rte_eth_devices, size);
+	return 0;
+}
+
+static int
+rte_eth_dev_get_changed_port(struct rte_eth_dev *devs, uint8_t *port_id)
+{
+	if ((devs == NULL) || (port_id == NULL))
+		return -EINVAL;
+
+	/* check which port was attached or detached */
+	for (*port_id = 0; *port_id < RTE_MAX_ETHPORTS; (*port_id)++, devs++) {
+		if (rte_eth_devices[*port_id].attached ^ devs->attached)
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int
+rte_eth_dev_get_addr_by_port(uint8_t port_id, struct rte_pci_addr *addr)
+{
+	if (!rte_eth_dev_is_valid_port(port_id)) {
+		PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id);
+		return -EINVAL;
+	}
+
+	if (addr == NULL) {
+		PMD_DEBUG_TRACE("Null pointer is specified\n");
+		return -EINVAL;
+	}
+
+	*addr = rte_eth_devices[port_id].pci_dev->addr;
+	return 0;
+}
+
+static int
+rte_eth_dev_get_name_by_port(uint8_t port_id, char *name)
+{
+	char *tmp;
+
+	if (!rte_eth_dev_is_valid_port(port_id)) {
+		PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id);
+		return -EINVAL;
+	}
+
+	if (name == NULL) {
+		PMD_DEBUG_TRACE("Null pointer is specified\n");
+		return -EINVAL;
+	}
+
+	/* shouldn't check 'rte_eth_devices[i].data',
+	 * because it might be overwritten by VDEV PMD */
+	tmp = rte_eth_dev_data[port_id].name;
+	strcpy(name, tmp);
+	return 0;
+}
+
+static int
+rte_eth_dev_is_detachable(uint8_t port_id)
+{
+	uint32_t drv_flags;
+
+	if (port_id >= RTE_MAX_ETHPORTS) {
+		PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id);
+		return -EINVAL;
+	}
+
+	if (rte_eth_devices[port_id].dev_type == RTE_ETH_DEV_PCI) {
+		switch (rte_eth_devices[port_id].pci_dev->pt_driver) {
+		case RTE_PT_IGB_UIO:
+		case RTE_PT_UIO_GENERIC:
+			break;
+		case RTE_PT_VFIO:
+		default:
+			return -ENOTSUP;
+		}
+	}
+
+	drv_flags = rte_eth_devices[port_id].driver->pci_drv.drv_flags;
+	return !(drv_flags & RTE_PCI_DRV_DETACHABLE);
+}
+
+/* So far, DPDK hotplug function only supports linux */
+#ifdef RTE_LIBRTE_EAL_HOTPLUG
+/* attach the new physical device, then store port_id of the device */
+static int
+rte_eth_dev_attach_pdev(struct rte_pci_addr *addr, uint8_t *port_id)
+{
+	uint8_t new_port_id;
+	struct rte_eth_dev devs[RTE_MAX_ETHPORTS];
+
+	if ((addr == NULL) || (port_id == NULL))
+		goto err;
+
+	/* save current port status */
+	if (rte_eth_dev_save(devs, sizeof(devs)))
+		goto err;
+	/* re-construct pci_device_list */
+	if (rte_eal_pci_scan())
+		goto err;
+	/* invoke probe func of the driver can handle the new device.
+	 * TODO:
+	 * rte_eal_pci_probe_one() should return port_id.
+	 * And rte_eth_dev_save() and rte_eth_dev_get_changed_port()
+	 * should be removed. */
+	if (rte_eal_pci_probe_one(addr))
+		goto err;
+	/* get port_id enabled by above procedures */
+	if (rte_eth_dev_get_changed_port(devs, &new_port_id))
+		goto err;
+
+	*port_id = new_port_id;
+	return 0;
+err:
+	RTE_LOG(ERR, EAL, "Driver, cannot attach the device\n");
+	return -1;
+}
+
+/* detach the new physical device, then store pci_addr of the device */
+static int
+rte_eth_dev_detach_pdev(uint8_t port_id, struct rte_pci_addr *addr)
+{
+	struct rte_pci_addr freed_addr;
+	struct rte_pci_addr vp;
+
+	if (addr == NULL)
+		goto err;
+
+	/* check whether the driver supports detach feature, or not */
+	if (rte_eth_dev_is_detachable(port_id))
+		goto err;
+
+	/* get pci address by port id */
+	if (rte_eth_dev_get_addr_by_port(port_id, &freed_addr))
+		goto err;
+
+	/* Zerod pci addr means the port comes from virtual device */
+	vp.domain = vp.bus = vp.devid = vp.function = 0;
+	if (rte_eal_compare_pci_addr(&vp, &freed_addr) == 0)
+		goto err;
+
+	/* invoke close func of the driver,
+	 * also remove the device from pci_device_list */
+	if (rte_eal_pci_close_one(&freed_addr))
+		goto err;
+
+	*addr = freed_addr;
+	return 0;
+err:
+	RTE_LOG(ERR, EAL, "Driver, cannot detach the device\n");
+	return -1;
+}
+
+/* attach the new virtual device, then store port_id of the device */
+static int
+rte_eth_dev_attach_vdev(const char *vdevargs, uint8_t *port_id)
+{
+	char *name = NULL, *args = NULL;
+	uint8_t new_port_id;
+	struct rte_eth_dev devs[RTE_MAX_ETHPORTS];
+	int ret = -1;
+
+	if ((vdevargs == NULL) || (port_id == NULL))
+		goto end;
+
+	/* parse vdevargs, then retrieve device name and args */
+	if (rte_eal_parse_devargs_str(vdevargs, &name, &args))
+		goto end;
+
+	/* save current port status */
+	if (rte_eth_dev_save(devs, sizeof(devs)))
+		goto end;
+	/* walk around dev_driver_list to find the driver of the device,
+	 * then invoke probe function o the driver.
+	 * TODO:
+	 * rte_eal_vdev_init() should return port_id,
+	 * And rte_eth_dev_save() and rte_eth_dev_get_changed_port()
+	 * should be removed. */
+	if (rte_eal_vdev_init(name, args))
+		goto end;
+	/* get port_id enabled by above procedures */
+	if (rte_eth_dev_get_changed_port(devs, &new_port_id))
+		goto end;
+	ret = 0;
+	*port_id = new_port_id;
+end:
+	if (name)
+		free(name);
+	if (args)
+		free(args);
+
+	if (ret < 0)
+		RTE_LOG(ERR, EAL, "Driver, cannot attach the device\n");
+	return ret;
+}
+
+/* detach the new virtual device, then store the name of the device */
+static int
+rte_eth_dev_detach_vdev(uint8_t port_id, char *vdevname)
+{
+	char name[RTE_ETH_NAME_MAX_LEN];
+
+	if (vdevname == NULL)
+		goto err;
+
+	/* check whether the driver supports detach feature, or not */
+	if (rte_eth_dev_is_detachable(port_id))
+		goto err;
+
+	/* get device name by port id */
+	if (rte_eth_dev_get_name_by_port(port_id, name))
+		goto err;
+	/* walk around dev_driver_list to find the driver of the device,
+	 * then invoke close function o the driver */
+	if (rte_eal_vdev_uninit(name))
+		goto err;
+
+	strncpy(vdevname, name, sizeof(name));
+	return 0;
+err:
+	RTE_LOG(ERR, EAL, "Driver, cannot detach the device\n");
+	return -1;
+}
+
+/* attach the new device, then store port_id of the device */
+int
+rte_eth_dev_attach(const char *devargs, uint8_t *port_id)
+{
+	struct rte_pci_addr addr;
+
+	if ((devargs == NULL) || (port_id == NULL))
+		return -EINVAL;
+
+	if (eal_parse_pci_DomBDF(devargs, &addr) == 0)
+		return rte_eth_dev_attach_pdev(&addr, port_id);
+	else
+		return rte_eth_dev_attach_vdev(devargs, port_id);
+}
+
+/* detach the device, then store the name of the device */
+int
+rte_eth_dev_detach(uint8_t port_id, char *name)
+{
+	struct rte_pci_addr addr;
+	int ret;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	if (rte_eth_dev_get_device_type(port_id) == RTE_ETH_DEV_PCI) {
+		ret = rte_eth_dev_get_addr_by_port(port_id, &addr);
+		if (ret < 0)
+			return ret;
+
+		ret = rte_eth_dev_detach_pdev(port_id, &addr);
+		if (ret == 0)
+			snprintf(name, RTE_ETH_NAME_MAX_LEN,
+				"%04x:%02x:%02x.%d",
+				addr.domain, addr.bus,
+				addr.devid, addr.function);
+
+		return ret;
+	} else
+		return rte_eth_dev_detach_vdev(port_id, name);
+}
+#else /* RTE_LIBRTE_EAL_HOTPLUG */
+int
+rte_eth_dev_attach(const char *devargs __rte_unused,
+			uint8_t *port_id __rte_unused)
+{
+	RTE_LOG(ERR, EAL, "Hotplug support isn't enabled\n");
+	return -1;
+}
+
+/* detach the device, then store the name of the device */
+int
+rte_eth_dev_detach(uint8_t port_id __rte_unused,
+			char *name __rte_unused)
+{
+	RTE_LOG(ERR, EAL, "Hotplug support isn't enabled\n");
+	return -1;
+}
+#endif /* RTE_LIBRTE_EAL_HOTPLUG */
+
 static int
 rte_eth_dev_rx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues)
 {
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 50c2dce..53d146c 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -175,6 +175,8 @@ extern "C" {
 #include <rte_log.h>
 #include <rte_interrupts.h>
 #include <rte_pci.h>
+#include <rte_dev.h>
+#include <rte_devargs.h>
 #include <rte_mbuf.h>
 #include "rte_ether.h"
 #include "rte_eth_ctrl.h"
@@ -1540,6 +1542,16 @@ extern struct rte_eth_dev rte_eth_devices[];
 extern uint8_t rte_eth_dev_count(void);
 
 /**
+ * Function for internal use by port hotplug functions.
+ * Returns a ethdev slot specified by the unique identifier name.
+ * @param	name
+ *  The pointer to the Unique identifier name for each Ethernet device
+ * @return
+ *   - The pointer to the ethdev slot, on success. NULL on error
+ */
+extern struct rte_eth_dev *rte_eth_dev_allocated(const char *name);
+
+/**
  * Function for internal use by dummy drivers primarily, e.g. ring-based
  * driver.
  * Allocates a new ethdev slot for an ethernet device and returns the pointer
@@ -1565,6 +1577,32 @@ struct rte_eth_dev *rte_eth_dev_allocate(const char *name,
  */
 int rte_eth_dev_release_port(struct rte_eth_dev *eth_dev);
 
+/**
+ * Attach a new Ethernet device specified by aruguments.
+ *
+ * @param devargs
+ *  A pointer to a strings array describing the new device
+ *  to be attached. The strings should be a pci address like
+ *  '0000:01:00.0' or virtual device name like 'eth_pcap0'.
+ * @param port_id
+ *  A pointer to a port identifier actually attached.
+ * @return
+ *  0 on success and port_id is filled, negative on error
+ */
+int rte_eth_dev_attach(const char *devargs, uint8_t *port_id);
+
+/**
+ * Detach a Ethernet device specified by port identifier.
+ *
+ * @param port_id
+ *   The port identifier of the device to detach.
+ * @param addr
+ *  A pointer to a device name actually detached.
+ * @return
+ *  0 on success and devname is filled, negative on error
+ */
+int rte_eth_dev_detach(uint8_t port_id, char *devname);
+
 struct eth_driver;
 /**
  * @internal
diff --git a/lib/librte_ether/rte_ether_version.map b/lib/librte_ether/rte_ether_version.map
index 94fd685..0d46578 100644
--- a/lib/librte_ether/rte_ether_version.map
+++ b/lib/librte_ether/rte_ether_version.map
@@ -8,6 +8,8 @@ DPDK_2.0 {
 	rte_eth_allmulticast_enable;
 	rte_eth_allmulticast_get;
 	rte_eth_dev_allocate;
+	rte_eth_dev_allocated;
+	rte_eth_dev_attach;
 	rte_eth_dev_bypass_event_show;
 	rte_eth_dev_bypass_event_store;
 	rte_eth_dev_bypass_init;
@@ -22,6 +24,7 @@ DPDK_2.0 {
 	rte_eth_dev_close;
 	rte_eth_dev_configure;
 	rte_eth_dev_count;
+	rte_eth_dev_detach;
 	rte_eth_dev_fdir_add_perfect_filter;
 	rte_eth_dev_fdir_add_signature_filter;
 	rte_eth_dev_fdir_get_infos;
