+void
+dev_set_link_down(portid_t pid)
+{
+ if (rte_eth_dev_set_link_down(pid) < 0)
+ printf("\nSet link down fail.\n");
+}
+
+static int
+all_ports_started(void)
+{
+ portid_t pi;
+ struct rte_port *port;
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ port = &ports[pi];
+ /* Check if there is a port which is not started */
+ if ((port->port_status != RTE_PORT_STARTED) &&
+ (port->slave_flag == 0))
+ return 0;
+ }
+
+ /* No port is not started */
+ return 1;
+}
+
+int
+port_is_stopped(portid_t port_id)
+{
+ struct rte_port *port = &ports[port_id];
+
+ if ((port->port_status != RTE_PORT_STOPPED) &&
+ (port->slave_flag == 0))
+ return 0;
+ return 1;
+}
+
+int
+all_ports_stopped(void)
+{
+ portid_t pi;
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (!port_is_stopped(pi))
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+port_is_started(portid_t port_id)
+{
+ if (port_id_is_invalid(port_id, ENABLED_WARN))
+ return 0;
+
+ if (ports[port_id].port_status != RTE_PORT_STARTED)
+ return 0;
+
+ return 1;
+}
+
+int
+start_port(portid_t pid)
+{
+ int diag, need_check_link_status = -1;
+ portid_t pi;
+ queueid_t qi;
+ struct rte_port *port;
+ struct ether_addr mac_addr;
+
+ if (port_id_is_invalid(pid, ENABLED_WARN))
+ return 0;
+
+ if(dcb_config)
+ dcb_test = 1;
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (pid != pi && pid != (portid_t)RTE_PORT_ALL)
+ continue;
+
+ need_check_link_status = 0;
+ port = &ports[pi];
+ if (rte_atomic16_cmpset(&(port->port_status), RTE_PORT_STOPPED,
+ RTE_PORT_HANDLING) == 0) {
+ printf("Port %d is now not stopped\n", pi);
+ continue;
+ }
+
+ if (port->need_reconfig > 0) {
+ port->need_reconfig = 0;
+
+ if (flow_isolate_all) {
+ int ret = port_flow_isolate(pi, 1);
+ if (ret) {
+ printf("Failed to apply isolated"
+ " mode on port %d\n", pi);
+ return -1;
+ }
+ }
+ configure_rxtx_dump_callbacks(0);
+ printf("Configuring Port %d (socket %u)\n", pi,
+ port->socket_id);
+ /* configure port */
+ diag = rte_eth_dev_configure(pi, nb_rxq, nb_txq,
+ &(port->dev_conf));
+ if (diag != 0) {
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING, RTE_PORT_STOPPED) == 0)
+ printf("Port %d can not be set back "
+ "to stopped\n", pi);
+ printf("Fail to configure port %d\n", pi);
+ /* try to reconfigure port next time */
+ port->need_reconfig = 1;
+ return -1;
+ }
+ }
+ if (port->need_reconfig_queues > 0) {
+ port->need_reconfig_queues = 0;
+ /* setup tx queues */
+ for (qi = 0; qi < nb_txq; qi++) {
+ if ((numa_support) &&
+ (txring_numa[pi] != NUMA_NO_CONFIG))
+ diag = rte_eth_tx_queue_setup(pi, qi,
+ port->nb_tx_desc[qi],
+ txring_numa[pi],
+ &(port->tx_conf[qi]));
+ else
+ diag = rte_eth_tx_queue_setup(pi, qi,
+ port->nb_tx_desc[qi],
+ port->socket_id,
+ &(port->tx_conf[qi]));
+
+ if (diag == 0)
+ continue;
+
+ /* Fail to setup tx queue, return */
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING,
+ RTE_PORT_STOPPED) == 0)
+ printf("Port %d can not be set back "
+ "to stopped\n", pi);
+ printf("Fail to configure port %d tx queues\n",
+ pi);
+ /* try to reconfigure queues next time */
+ port->need_reconfig_queues = 1;
+ return -1;
+ }
+ for (qi = 0; qi < nb_rxq; qi++) {
+ /* setup rx queues */
+ if ((numa_support) &&
+ (rxring_numa[pi] != NUMA_NO_CONFIG)) {
+ struct rte_mempool * mp =
+ mbuf_pool_find(rxring_numa[pi]);
+ if (mp == NULL) {
+ printf("Failed to setup RX queue:"
+ "No mempool allocation"
+ " on the socket %d\n",
+ rxring_numa[pi]);
+ return -1;
+ }
+
+ diag = rte_eth_rx_queue_setup(pi, qi,
+ port->nb_rx_desc[qi],
+ rxring_numa[pi],
+ &(port->rx_conf[qi]),
+ mp);
+ } else {
+ struct rte_mempool *mp =
+ mbuf_pool_find(port->socket_id);
+ if (mp == NULL) {
+ printf("Failed to setup RX queue:"
+ "No mempool allocation"
+ " on the socket %d\n",
+ port->socket_id);
+ return -1;
+ }
+ diag = rte_eth_rx_queue_setup(pi, qi,
+ port->nb_rx_desc[qi],
+ port->socket_id,
+ &(port->rx_conf[qi]),
+ mp);
+ }
+ if (diag == 0)
+ continue;
+
+ /* Fail to setup rx queue, return */
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING,
+ RTE_PORT_STOPPED) == 0)
+ printf("Port %d can not be set back "
+ "to stopped\n", pi);
+ printf("Fail to configure port %d rx queues\n",
+ pi);
+ /* try to reconfigure queues next time */
+ port->need_reconfig_queues = 1;
+ return -1;
+ }
+ }
+ configure_rxtx_dump_callbacks(verbose_level);
+ /* start port */
+ if (rte_eth_dev_start(pi) < 0) {
+ printf("Fail to start port %d\n", pi);
+
+ /* Fail to setup rx queue, return */
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING, RTE_PORT_STOPPED) == 0)
+ printf("Port %d can not be set back to "
+ "stopped\n", pi);
+ continue;
+ }
+
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0)
+ printf("Port %d can not be set into started\n", pi);
+
+ rte_eth_macaddr_get(pi, &mac_addr);
+ printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi,
+ mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
+ mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
+ mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]);
+
+ /* at least one port started, need checking link status */
+ need_check_link_status = 1;
+ }
+
+ if (need_check_link_status == 1 && !no_link_check)
+ check_all_ports_link_status(RTE_PORT_ALL);
+ else if (need_check_link_status == 0)
+ printf("Please stop the ports first\n");
+
+ printf("Done\n");
+ return 0;
+}
+
+void
+stop_port(portid_t pid)
+{
+ portid_t pi;
+ struct rte_port *port;
+ int need_check_link_status = 0;
+
+ if (dcb_test) {
+ dcb_test = 0;
+ dcb_config = 0;
+ }
+
+ if (port_id_is_invalid(pid, ENABLED_WARN))
+ return;
+
+ printf("Stopping ports...\n");
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (pid != pi && pid != (portid_t)RTE_PORT_ALL)
+ continue;
+
+ if (port_is_forwarding(pi) != 0 && test_done == 0) {
+ printf("Please remove port %d from forwarding configuration.\n", pi);
+ continue;
+ }
+
+ if (port_is_bonding_slave(pi)) {
+ printf("Please remove port %d from bonded device.\n", pi);
+ continue;
+ }
+
+ port = &ports[pi];
+ if (rte_atomic16_cmpset(&(port->port_status), RTE_PORT_STARTED,
+ RTE_PORT_HANDLING) == 0)
+ continue;
+
+ rte_eth_dev_stop(pi);
+
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING, RTE_PORT_STOPPED) == 0)
+ printf("Port %d can not be set into stopped\n", pi);
+ need_check_link_status = 1;
+ }
+ if (need_check_link_status && !no_link_check)
+ check_all_ports_link_status(RTE_PORT_ALL);
+
+ printf("Done\n");
+}
+
+static void
+remove_invalid_ports_in(portid_t *array, portid_t *total)
+{
+ portid_t i;
+ portid_t new_total = 0;
+
+ for (i = 0; i < *total; i++)
+ if (!port_id_is_invalid(array[i], DISABLED_WARN)) {
+ array[new_total] = array[i];
+ new_total++;
+ }
+ *total = new_total;
+}
+
+static void
+remove_invalid_ports(void)
+{
+ remove_invalid_ports_in(ports_ids, &nb_ports);
+ remove_invalid_ports_in(fwd_ports_ids, &nb_fwd_ports);
+ nb_cfg_ports = nb_fwd_ports;
+}
+
+void
+close_port(portid_t pid)
+{
+ portid_t pi;
+ struct rte_port *port;
+
+ if (port_id_is_invalid(pid, ENABLED_WARN))
+ return;
+
+ printf("Closing ports...\n");
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (pid != pi && pid != (portid_t)RTE_PORT_ALL)
+ continue;
+
+ if (port_is_forwarding(pi) != 0 && test_done == 0) {
+ printf("Please remove port %d from forwarding configuration.\n", pi);
+ continue;
+ }
+
+ if (port_is_bonding_slave(pi)) {
+ printf("Please remove port %d from bonded device.\n", pi);
+ continue;
+ }
+
+ port = &ports[pi];
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_CLOSED, RTE_PORT_CLOSED) == 1) {
+ printf("Port %d is already closed\n", pi);
+ continue;
+ }
+
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_STOPPED, RTE_PORT_HANDLING) == 0) {
+ printf("Port %d is now not stopped\n", pi);
+ continue;
+ }
+
+ if (port->flow_list)
+ port_flow_flush(pi);
+ rte_eth_dev_close(pi);
+
+ remove_invalid_ports();
+
+ if (rte_atomic16_cmpset(&(port->port_status),
+ RTE_PORT_HANDLING, RTE_PORT_CLOSED) == 0)
+ printf("Port %d cannot be set to closed\n", pi);
+ }
+
+ printf("Done\n");
+}
+
+void
+reset_port(portid_t pid)
+{
+ int diag;
+ portid_t pi;
+ struct rte_port *port;
+
+ if (port_id_is_invalid(pid, ENABLED_WARN))
+ return;
+
+ printf("Resetting ports...\n");
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (pid != pi && pid != (portid_t)RTE_PORT_ALL)
+ continue;
+
+ if (port_is_forwarding(pi) != 0 && test_done == 0) {
+ printf("Please remove port %d from forwarding "
+ "configuration.\n", pi);
+ continue;
+ }
+
+ if (port_is_bonding_slave(pi)) {
+ printf("Please remove port %d from bonded device.\n",
+ pi);
+ continue;
+ }
+
+ diag = rte_eth_dev_reset(pi);
+ if (diag == 0) {
+ port = &ports[pi];
+ port->need_reconfig = 1;
+ port->need_reconfig_queues = 1;
+ } else {
+ printf("Failed to reset port %d. diag=%d\n", pi, diag);
+ }
+ }
+
+ printf("Done\n");
+}
+
+void
+attach_port(char *identifier)
+{
+ portid_t pi;
+ struct rte_dev_iterator iterator;
+
+ printf("Attaching a new port...\n");
+
+ if (identifier == NULL) {
+ printf("Invalid parameters are specified\n");
+ return;
+ }
+
+ if (rte_dev_probe(identifier) != 0) {
+ TESTPMD_LOG(ERR, "Failed to attach port %s\n", identifier);
+ return;
+ }
+
+ /* first attach mode: event */
+ if (setup_on_probe_event) {
+ /* new ports are detected on RTE_ETH_EVENT_NEW event */
+ for (pi = 0; pi < RTE_MAX_ETHPORTS; pi++)
+ if (ports[pi].port_status == RTE_PORT_HANDLING &&
+ ports[pi].need_setup != 0)
+ setup_attached_port(pi);
+ return;
+ }
+
+ /* second attach mode: iterator */
+ RTE_ETH_FOREACH_MATCHING_DEV(pi, identifier, &iterator) {
+ /* setup ports matching the devargs used for probing */
+ if (port_is_forwarding(pi))
+ continue; /* port was already attached before */
+ setup_attached_port(pi);
+ }
+}
+
+static void
+setup_attached_port(portid_t pi)
+{
+ unsigned int socket_id;
+
+ socket_id = (unsigned)rte_eth_dev_socket_id(pi);
+ /* if socket_id is invalid, set to the first available socket. */
+ if (check_socket_id(socket_id) < 0)
+ socket_id = socket_ids[0];
+ reconfig(pi, socket_id);
+ rte_eth_promiscuous_enable(pi);
+
+ ports_ids[nb_ports++] = pi;
+ fwd_ports_ids[nb_fwd_ports++] = pi;
+ nb_cfg_ports = nb_fwd_ports;
+ ports[pi].need_setup = 0;
+ ports[pi].port_status = RTE_PORT_STOPPED;
+
+ printf("Port %d is attached. Now total ports is %d\n", pi, nb_ports);
+ printf("Done\n");
+}
+
+void
+detach_port_device(portid_t port_id)
+{
+ struct rte_device *dev;
+ portid_t sibling;
+
+ printf("Removing a device...\n");
+
+ dev = rte_eth_devices[port_id].device;
+ if (dev == NULL) {
+ printf("Device already removed\n");
+ return;
+ }
+
+ if (ports[port_id].port_status != RTE_PORT_CLOSED) {
+ if (ports[port_id].port_status != RTE_PORT_STOPPED) {
+ printf("Port not stopped\n");
+ return;
+ }
+ printf("Port was not closed\n");
+ if (ports[port_id].flow_list)
+ port_flow_flush(port_id);
+ }
+
+ if (rte_dev_remove(dev) != 0) {
+ TESTPMD_LOG(ERR, "Failed to detach device %s\n", dev->name);
+ return;
+ }
+
+ for (sibling = 0; sibling < RTE_MAX_ETHPORTS; sibling++) {
+ if (rte_eth_devices[sibling].device != dev)
+ continue;
+ /* reset mapping between old ports and removed device */
+ rte_eth_devices[sibling].device = NULL;
+ if (ports[sibling].port_status != RTE_PORT_CLOSED) {
+ /* sibling ports are forced to be closed */
+ ports[sibling].port_status = RTE_PORT_CLOSED;
+ printf("Port %u is closed\n", sibling);
+ }
+ }
+
+ remove_invalid_ports();
+
+ printf("Device of port %u is detached\n", port_id);
+ printf("Now total ports is %d\n", nb_ports);
+ printf("Done\n");
+ return;
+}
+
+void
+pmd_test_exit(void)
+{
+ struct rte_device *device;
+ portid_t pt_id;
+ int ret;
+
+ if (test_done == 0)
+ stop_packet_forwarding();
+
+ if (ports != NULL) {
+ no_link_check = 1;
+ RTE_ETH_FOREACH_DEV(pt_id) {
+ printf("\nStopping port %d...\n", pt_id);
+ fflush(stdout);
+ stop_port(pt_id);
+ }
+ RTE_ETH_FOREACH_DEV(pt_id) {
+ printf("\nShutting down port %d...\n", pt_id);
+ fflush(stdout);
+ close_port(pt_id);
+
+ /*
+ * This is a workaround to fix a virtio-user issue that
+ * requires to call clean-up routine to remove existing
+ * socket.
+ * This workaround valid only for testpmd, needs a fix
+ * valid for all applications.
+ * TODO: Implement proper resource cleanup
+ */
+ device = rte_eth_devices[pt_id].device;
+ if (device && !strcmp(device->driver->name, "net_virtio_user"))
+ detach_port_device(pt_id);
+ }
+ }
+
+ if (hot_plug) {
+ ret = rte_dev_event_monitor_stop();
+ if (ret) {
+ RTE_LOG(ERR, EAL,
+ "fail to stop device event monitor.");
+ return;
+ }
+
+ ret = rte_dev_event_callback_unregister(NULL,
+ dev_event_callback, NULL);
+ if (ret < 0) {
+ RTE_LOG(ERR, EAL,
+ "fail to unregister device event callback.\n");
+ return;
+ }
+
+ ret = rte_dev_hotplug_handle_disable();
+ if (ret) {
+ RTE_LOG(ERR, EAL,
+ "fail to disable hotplug handling.\n");
+ return;
+ }
+ }
+
+ printf("\nBye...\n");
+}
+
+typedef void (*cmd_func_t)(void);