The patch introduces following commands.
- port attach [ident]
- port detach [port_id]
 - attach: attaching a port
 - detach: detaching a port
 - ident: pci address of physical device.
          Or device name and parameters of virtual device.
         (ex. 0000:02:00.0, eth_pcap0,iface=eth0)
 - port_id: port identifier

v7:
- Fix doc.
  (Thanks to Iremonger, Bernard)
- Fix port checking implementation of star_port();
  (Thanks to Qiu, Michael)
v5:
- Add testpmd documentation.
  (Thanks to Iremonger, Bernard)
v4:
 - Fix strings of command help.

Signed-off-by: Tetsuya Mukawa <mukawa at igel.co.jp>
---
 app/test-pmd/cmdline.c                      | 137 +++++++++++++++----
 app/test-pmd/config.c                       | 116 +++++++++-------
 app/test-pmd/parameters.c                   |  22 ++-
 app/test-pmd/testpmd.c                      | 199 +++++++++++++++++++++-------
 app/test-pmd/testpmd.h                      |  18 ++-
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  57 ++++++++
 6 files changed, 416 insertions(+), 133 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 4753bb4..fa6e3a6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -579,6 +579,12 @@ static void cmd_help_long_parsed(void *parsed_result,
                        "port close (port_id|all)\n"
                        "    Close all ports or port_id.\n\n"

+                       "port attach (ident)\n"
+                       "    Attach physical or virtual dev by pci address or 
virtual device name\n\n"
+
+                       "port detach (port_id)\n"
+                       "    Detach physical or virtual dev by port_id\n\n"
+
                        "port config (port_id|all)"
                        " speed (10|100|1000|10000|40000|auto)"
                        " duplex (half|full|auto)\n"
@@ -870,6 +876,89 @@ cmdline_parse_inst_t cmd_operate_specific_port = {
        },
 };

+/* *** attach a specified port *** */
+struct cmd_operate_attach_port_result {
+       cmdline_fixed_string_t port;
+       cmdline_fixed_string_t keyword;
+       cmdline_fixed_string_t identifier;
+};
+
+static void cmd_operate_attach_port_parsed(void *parsed_result,
+                               __attribute__((unused)) struct cmdline *cl,
+                               __attribute__((unused)) void *data)
+{
+       struct cmd_operate_attach_port_result *res = parsed_result;
+
+       if (!strcmp(res->keyword, "attach"))
+               attach_port(res->identifier);
+       else
+               printf("Unknown parameter\n");
+}
+
+cmdline_parse_token_string_t cmd_operate_attach_port_port =
+       TOKEN_STRING_INITIALIZER(struct cmd_operate_attach_port_result,
+                       port, "port");
+cmdline_parse_token_string_t cmd_operate_attach_port_keyword =
+       TOKEN_STRING_INITIALIZER(struct cmd_operate_attach_port_result,
+                       keyword, "attach");
+cmdline_parse_token_string_t cmd_operate_attach_port_identifier =
+       TOKEN_STRING_INITIALIZER(struct cmd_operate_attach_port_result,
+                       identifier, NULL);
+
+cmdline_parse_inst_t cmd_operate_attach_port = {
+       .f = cmd_operate_attach_port_parsed,
+       .data = NULL,
+       .help_str = "port attach identifier, "
+               "identifier: pci address or virtual dev name",
+       .tokens = {
+               (void *)&cmd_operate_attach_port_port,
+               (void *)&cmd_operate_attach_port_keyword,
+               (void *)&cmd_operate_attach_port_identifier,
+               NULL,
+       },
+};
+
+/* *** detach a specified port *** */
+struct cmd_operate_detach_port_result {
+       cmdline_fixed_string_t port;
+       cmdline_fixed_string_t keyword;
+       uint8_t port_id;
+};
+
+static void cmd_operate_detach_port_parsed(void *parsed_result,
+                               __attribute__((unused)) struct cmdline *cl,
+                               __attribute__((unused)) void *data)
+{
+       struct cmd_operate_detach_port_result *res = parsed_result;
+
+       if (!strcmp(res->keyword, "detach"))
+               detach_port(res->port_id);
+       else
+               printf("Unknown parameter\n");
+}
+
+cmdline_parse_token_string_t cmd_operate_detach_port_port =
+       TOKEN_STRING_INITIALIZER(struct cmd_operate_detach_port_result,
+                       port, "port");
+cmdline_parse_token_string_t cmd_operate_detach_port_keyword =
+       TOKEN_STRING_INITIALIZER(struct cmd_operate_detach_port_result,
+                       keyword, "detach");
+cmdline_parse_token_num_t cmd_operate_detach_port_port_id =
+       TOKEN_NUM_INITIALIZER(struct cmd_operate_detach_port_result,
+                       port_id, UINT8);
+
+cmdline_parse_inst_t cmd_operate_detach_port = {
+       .f = cmd_operate_detach_port_parsed,
+       .data = NULL,
+       .help_str = "port detach port_id",
+       .tokens = {
+               (void *)&cmd_operate_detach_port_port,
+               (void *)&cmd_operate_detach_port_keyword,
+               (void *)&cmd_operate_detach_port_port_id,
+               NULL,
+       },
+};
+
 /* *** configure speed for all ports *** */
 struct cmd_config_speed_all {
        cmdline_fixed_string_t port;
@@ -924,7 +1013,7 @@ cmd_config_speed_all_parsed(void *parsed_result,
                return;
        }

-       for (pid = 0; pid < nb_ports; pid++) {
+       FOREACH_PORT(pid, ports) {
                ports[pid].dev_conf.link_speed = link_speed;
                ports[pid].dev_conf.link_duplex = link_duplex;
        }
@@ -992,10 +1081,8 @@ cmd_config_speed_specific_parsed(void *parsed_result,
                return;
        }

-       if (res->id >= nb_ports) {
-               printf("Port id %d must be less than %d\n", res->id, nb_ports);
+       if (port_id_is_invalid(res->id, ENABLED_WARN))
                return;
-       }

        if (!strcmp(res->value1, "10"))
                link_speed = ETH_LINK_SPEED_10;
@@ -1555,7 +1642,7 @@ cmd_config_rxtx_queue_parsed(void *parsed_result,
                return;
        }

-       if (port_id_is_invalid(res->portid))
+       if (port_id_is_invalid(res->portid, ENABLED_WARN))
                return;

        if (port_is_started(res->portid) != 1) {
@@ -2955,7 +3042,7 @@ cmd_csum_parsed(void *parsed_result,
        int hw = 0;
        uint16_t mask = 0;

-       if (port_id_is_invalid(res->port_id)) {
+       if (port_id_is_invalid(res->port_id, ENABLED_WARN)) {
                printf("invalid port %d\n", res->port_id);
                return;
        }
@@ -3047,10 +3134,8 @@ cmd_csum_tunnel_parsed(void *parsed_result,
 {
        struct cmd_csum_tunnel_result *res = parsed_result;

-       if (port_id_is_invalid(res->port_id)) {
-               printf("invalid port %d\n", res->port_id);
+       if (port_id_is_invalid(res->port_id, ENABLED_WARN))
                return;
-       }

        if (!strcmp(res->onoff, "on"))
                ports[res->port_id].tx_ol_flags |=
@@ -3105,7 +3190,7 @@ cmd_tso_set_parsed(void *parsed_result,
        struct cmd_tso_set_result *res = parsed_result;
        struct rte_eth_dev_info dev_info;

-       if (port_id_is_invalid(res->port_id))
+       if (port_id_is_invalid(res->port_id, ENABLED_WARN))
                return;

        if (!strcmp(res->mode, "set"))
@@ -4081,10 +4166,8 @@ static void cmd_set_bond_mac_addr_parsed(void 
*parsed_result,
        struct cmd_set_bond_mac_addr_result *res = parsed_result;
        int ret;

-       if (res->port_num >= nb_ports) {
-               printf("Port id %d must be less than %d\n", res->port_num, 
nb_ports);
+       if (port_id_is_invalid(res->port_num, ENABLED_WARN))
                return;
-       }

        ret = rte_eth_bond_mac_address_set(res->port_num, &res->address);

@@ -4321,7 +4404,7 @@ static void cmd_set_promisc_mode_parsed(void 
*parsed_result,

        /* all ports */
        if (allports) {
-               for (i = 0; i < nb_ports; i++) {
+               FOREACH_PORT(i, ports) {
                        if (enable)
                                rte_eth_promiscuous_enable(i);
                        else
@@ -4401,7 +4484,7 @@ static void cmd_set_allmulti_mode_parsed(void 
*parsed_result,

        /* all ports */
        if (allports) {
-               for (i = 0; i < nb_ports; i++) {
+               FOREACH_PORT(i, ports) {
                        if (enable)
                                rte_eth_allmulticast_enable(i);
                        else
@@ -5586,25 +5669,25 @@ static void cmd_showportall_parsed(void *parsed_result,
        struct cmd_showportall_result *res = parsed_result;
        if (!strcmp(res->show, "clear")) {
                if (!strcmp(res->what, "stats"))
-                       for (i = 0; i < nb_ports; i++)
+                       FOREACH_PORT(i, ports)
                                nic_stats_clear(i);
                else if (!strcmp(res->what, "xstats"))
-                       for (i = 0; i < nb_ports; i++)
+                       FOREACH_PORT(i, ports)
                                nic_xstats_clear(i);
        } else if (!strcmp(res->what, "info"))
-               for (i = 0; i < nb_ports; i++)
+               FOREACH_PORT(i, ports)
                        port_infos_display(i);
        else if (!strcmp(res->what, "stats"))
-               for (i = 0; i < nb_ports; i++)
+               FOREACH_PORT(i, ports)
                        nic_stats_display(i);
        else if (!strcmp(res->what, "xstats"))
-               for (i = 0; i < nb_ports; i++)
+               FOREACH_PORT(i, ports)
                        nic_xstats_display(i);
        else if (!strcmp(res->what, "fdir"))
-               for (i = 0; i < nb_ports; i++)
+               FOREACH_PORT(i, ports)
                        fdir_get_infos(i);
        else if (!strcmp(res->what, "stat_qmap"))
-               for (i = 0; i < nb_ports; i++)
+               FOREACH_PORT(i, ports)
                        nic_stats_mapping_display(i);
 }

@@ -9172,6 +9255,8 @@ cmdline_parse_ctx_t main_ctx[] = {
        (cmdline_parse_inst_t *)&cmd_set_qmap,
        (cmdline_parse_inst_t *)&cmd_operate_port,
        (cmdline_parse_inst_t *)&cmd_operate_specific_port,
+       (cmdline_parse_inst_t *)&cmd_operate_attach_port,
+       (cmdline_parse_inst_t *)&cmd_operate_detach_port,
        (cmdline_parse_inst_t *)&cmd_config_speed_all,
        (cmdline_parse_inst_t *)&cmd_config_speed_specific,
        (cmdline_parse_inst_t *)&cmd_config_rx_tx,
@@ -9250,7 +9335,7 @@ prompt(void)
 static void
 cmd_reconfig_device_queue(portid_t id, uint8_t dev, uint8_t queue)
 {
-       if (id < nb_ports) {
+       if (!port_id_is_invalid(id, DISABLED_WARN)) {
                /* check if need_reconfig has been set to 1 */
                if (ports[id].need_reconfig == 0)
                        ports[id].need_reconfig = dev;
@@ -9260,7 +9345,7 @@ cmd_reconfig_device_queue(portid_t id, uint8_t dev, 
uint8_t queue)
        } else {
                portid_t pid;

-               for (pid = 0; pid < nb_ports; pid++) {
+               FOREACH_PORT(pid, ports) {
                        /* check if need_reconfig has been set to 1 */
                        if (ports[pid].need_reconfig == 0)
                                ports[pid].need_reconfig = dev;
@@ -9278,10 +9363,8 @@ bypass_is_supported(portid_t port_id)
        struct rte_port   *port;
        struct rte_pci_id *pci_id;

-       if (port_id >= nb_ports) {
-               printf("\tPort id must be less than %d.\n", nb_ports);
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return 0;
-       }

        /* Get the device id. */
        port    = &ports[port_id];
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 6bcd23c..718167c 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -124,11 +124,15 @@ nic_stats_display(portid_t port_id)
        struct rte_eth_stats stats;
        struct rte_port *port = &ports[port_id];
        uint8_t i;
+       portid_t pid;

        static const char *nic_stats_border = "########################";

-       if (port_id >= nb_ports) {
-               printf("Invalid port, range is [0, %d]\n", nb_ports - 1);
+       if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+               printf("Valid port range is [0");
+               FOREACH_PORT(pid, ports)
+                       printf(", %d", pid);
+               printf("]\n");
                return;
        }
        rte_eth_stats_get(port_id, &stats);
@@ -201,8 +205,13 @@ nic_stats_display(portid_t port_id)
 void
 nic_stats_clear(portid_t port_id)
 {
-       if (port_id >= nb_ports) {
-               printf("Invalid port, range is [0, %d]\n", nb_ports - 1);
+       portid_t pid;
+
+       if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+               printf("Valid port range is [0");
+               FOREACH_PORT(pid, ports)
+                       printf(", %d", pid);
+               printf("]\n");
                return;
        }
        rte_eth_stats_reset(port_id);
@@ -249,11 +258,15 @@ nic_stats_mapping_display(portid_t port_id)
 {
        struct rte_port *port = &ports[port_id];
        uint16_t i;
+       portid_t pid;

        static const char *nic_stats_mapping_border = 
"########################";

-       if (port_id >= nb_ports) {
-               printf("Invalid port, range is [0, %d]\n", nb_ports - 1);
+       if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+               printf("Valid port range is [0");
+               FOREACH_PORT(pid, ports)
+                       printf(", %d", pid);
+               printf("]\n");
                return;
        }

@@ -302,9 +315,13 @@ port_infos_display(portid_t port_id)
        int vlan_offload;
        struct rte_mempool * mp;
        static const char *info_border = "*********************";
+       portid_t pid;

-       if (port_id >= nb_ports) {
-               printf("Invalid port, range is [0, %d]\n", nb_ports - 1);
+       if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+               printf("Valid port range is [0");
+               FOREACH_PORT(pid, ports)
+                       printf(", %d", pid);
+               printf("]\n");
                return;
        }
        port = &ports[port_id];
@@ -362,11 +379,14 @@ port_infos_display(portid_t port_id)
 }

 int
-port_id_is_invalid(portid_t port_id)
+port_id_is_invalid(portid_t port_id, enum print_warning warning)
 {
-       if (port_id < nb_ports)
+       if (ports[port_id].enabled)
                return 0;
-       printf("Invalid port %d (must be < nb_ports=%d)\n", port_id, nb_ports);
+
+       if (warning == ENABLED_WARN)
+               printf("Invalid port %d\n", port_id);
+
        return 1;
 }

@@ -425,7 +445,7 @@ port_reg_bit_display(portid_t port_id, uint32_t reg_off, 
uint8_t bit_x)
        uint32_t reg_v;


-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (port_reg_off_is_invalid(port_id, reg_off))
                return;
@@ -444,7 +464,7 @@ port_reg_bit_field_display(portid_t port_id, uint32_t 
reg_off,
        uint8_t  l_bit;
        uint8_t  h_bit;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (port_reg_off_is_invalid(port_id, reg_off))
                return;
@@ -471,7 +491,7 @@ port_reg_display(portid_t port_id, uint32_t reg_off)
 {
        uint32_t reg_v;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (port_reg_off_is_invalid(port_id, reg_off))
                return;
@@ -485,7 +505,7 @@ port_reg_bit_set(portid_t port_id, uint32_t reg_off, 
uint8_t bit_pos,
 {
        uint32_t reg_v;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (port_reg_off_is_invalid(port_id, reg_off))
                return;
@@ -513,7 +533,7 @@ port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
        uint8_t  l_bit;
        uint8_t  h_bit;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (port_reg_off_is_invalid(port_id, reg_off))
                return;
@@ -547,7 +567,7 @@ port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,
 void
 port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t reg_v)
 {
-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (port_reg_off_is_invalid(port_id, reg_off))
                return;
@@ -560,7 +580,7 @@ port_mtu_set(portid_t port_id, uint16_t mtu)
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        diag = rte_eth_dev_set_mtu(port_id, mtu);
        if (diag == 0)
@@ -723,7 +743,7 @@ rx_ring_desc_display(portid_t port_id, queueid_t rxq_id, 
uint16_t rxd_id)
 {
        const struct rte_memzone *rx_mz;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (rx_queue_id_is_invalid(rxq_id))
                return;
@@ -740,7 +760,7 @@ tx_ring_desc_display(portid_t port_id, queueid_t txq_id, 
uint16_t txd_id)
 {
        const struct rte_memzone *tx_mz;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (tx_queue_id_is_invalid(txq_id))
                return;
@@ -801,7 +821,7 @@ port_rss_reta_info(portid_t port_id,
        uint16_t i, idx, shift;
        int ret;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        ret = rte_eth_dev_rss_reta_query(port_id, reta_conf, nb_entries);
@@ -833,7 +853,7 @@ port_rss_hash_conf_show(portid_t port_id, int show_rss_key)
        uint8_t i;
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        /* Get RSS hash key if asked to display it */
        rss_conf.rss_key = (show_rss_key) ? rss_key : NULL;
@@ -1411,12 +1431,8 @@ set_fwd_ports_list(unsigned int *portlist, unsigned int 
nb_pt)
  again:
        for (i = 0; i < nb_pt; i++) {
                port_id = (portid_t) portlist[i];
-               if (port_id >= nb_ports) {
-                       printf("Invalid port id %u >= %u\n",
-                              (unsigned int) port_id,
-                              (unsigned int) nb_ports);
+               if (port_id_is_invalid(port_id, ENABLED_WARN))
                        return;
-               }
                if (record_now)
                        fwd_ports_ids[i] = port_id;
        }
@@ -1574,7 +1590,7 @@ vlan_extend_set(portid_t port_id, int on)
        int diag;
        int vlan_offload;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        vlan_offload = rte_eth_dev_get_vlan_offload(port_id);
@@ -1596,7 +1612,7 @@ rx_vlan_strip_set(portid_t port_id, int on)
        int diag;
        int vlan_offload;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        vlan_offload = rte_eth_dev_get_vlan_offload(port_id);
@@ -1617,7 +1633,7 @@ rx_vlan_strip_set_on_queue(portid_t port_id, uint16_t 
queue_id, int on)
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_set_vlan_strip_on_queue(port_id, queue_id, on);
@@ -1632,7 +1648,7 @@ rx_vlan_filter_set(portid_t port_id, int on)
        int diag;
        int vlan_offload;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        vlan_offload = rte_eth_dev_get_vlan_offload(port_id);
@@ -1653,7 +1669,7 @@ rx_vft_set(portid_t port_id, uint16_t vlan_id, int on)
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (vlan_id_is_invalid(vlan_id))
                return;
@@ -1670,7 +1686,7 @@ rx_vlan_all_filter_set(portid_t port_id, int on)
 {
        uint16_t vlan_id;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        for (vlan_id = 0; vlan_id < 4096; vlan_id++)
                rx_vft_set(port_id, vlan_id, on);
@@ -1680,7 +1696,7 @@ void
 vlan_tpid_set(portid_t port_id, uint16_t tp_id)
 {
        int diag;
-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_set_vlan_ether_type(port_id, tp_id);
@@ -1695,7 +1711,7 @@ vlan_tpid_set(portid_t port_id, uint16_t tp_id)
 void
 tx_vlan_set(portid_t port_id, uint16_t vlan_id)
 {
-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (vlan_id_is_invalid(vlan_id))
                return;
@@ -1706,7 +1722,7 @@ tx_vlan_set(portid_t port_id, uint16_t vlan_id)
 void
 tx_vlan_reset(portid_t port_id)
 {
-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        ports[port_id].tx_ol_flags &= ~TESTPMD_TX_OFFLOAD_INSERT_VLAN;
 }
@@ -1714,7 +1730,7 @@ tx_vlan_reset(portid_t port_id)
 void
 tx_vlan_pvid_set(portid_t port_id, uint16_t vlan_id, int on)
 {
-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        rte_eth_dev_set_vlan_pvid(port_id, vlan_id, on);
@@ -1726,7 +1742,7 @@ set_qmap(portid_t port_id, uint8_t is_rx, uint16_t 
queue_id, uint8_t map_value)
        uint16_t i;
        uint8_t existing_mapping_found = 0;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        if (is_rx ? (rx_queue_id_is_invalid(queue_id)) : 
(tx_queue_id_is_invalid(queue_id)))
@@ -1778,7 +1794,7 @@ fdir_add_signature_filter(portid_t port_id, uint8_t 
queue_id,
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_add_signature_filter(port_id, fdir_filter,
@@ -1796,7 +1812,7 @@ fdir_update_signature_filter(portid_t port_id, uint8_t 
queue_id,
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_update_signature_filter(port_id, fdir_filter,
@@ -1814,7 +1830,7 @@ fdir_remove_signature_filter(portid_t port_id,
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_remove_signature_filter(port_id, fdir_filter);
@@ -1886,7 +1902,7 @@ fdir_get_infos(portid_t port_id)

        static const char *fdir_stats_border = "########################";

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        ret = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR);
        if (ret < 0) {
@@ -1960,7 +1976,7 @@ fdir_add_perfect_filter(portid_t port_id, uint16_t 
soft_id, uint8_t queue_id,
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_add_perfect_filter(port_id, fdir_filter,
@@ -1978,7 +1994,7 @@ fdir_update_perfect_filter(portid_t port_id, uint16_t 
soft_id, uint8_t queue_id,
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_update_perfect_filter(port_id, fdir_filter,
@@ -1996,7 +2012,7 @@ fdir_remove_perfect_filter(portid_t port_id, uint16_t 
soft_id,
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_remove_perfect_filter(port_id, fdir_filter,
@@ -2013,7 +2029,7 @@ fdir_set_masks(portid_t port_id, struct rte_fdir_masks 
*fdir_masks)
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;

        diag = rte_eth_dev_fdir_set_masks(port_id, fdir_masks);
@@ -2090,7 +2106,7 @@ set_vf_traffic(portid_t port_id, uint8_t is_rx, uint16_t 
vf, uint8_t on)
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (is_rx)
                diag = rte_eth_dev_set_vf_rx(port_id,vf,on);
@@ -2112,7 +2128,7 @@ set_vf_rx_vlan(portid_t port_id, uint16_t vlan_id, 
uint64_t vf_mask, uint8_t on)
 {
        int diag;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return;
        if (vlan_id_is_invalid(vlan_id))
                return;
@@ -2129,7 +2145,7 @@ set_queue_rate_limit(portid_t port_id, uint16_t 
queue_idx, uint16_t rate)
        int diag;
        struct rte_eth_link link;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return 1;
        rte_eth_link_get_nowait(port_id, &link);
        if (rate > link.link_speed) {
@@ -2154,7 +2170,7 @@ set_vf_rate_limit(portid_t port_id, uint16_t vf, uint16_t 
rate, uint64_t q_msk)
        if (q_msk == 0)
                return 0;

-       if (port_id_is_invalid(port_id))
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
                return 1;
        rte_eth_link_get_nowait(port_id, &link);
        if (rate > link.link_speed) {
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 19fbf46..07740c4 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -370,6 +370,7 @@ parse_portnuma_config(const char *q_arg)
        };
        unsigned long int_fld[_NUM_FLD];
        char *str_fld[_NUM_FLD];
+       portid_t pid;

        /* reset from value set at definition */
        while ((p = strchr(p0,'(')) != NULL) {
@@ -391,8 +392,11 @@ parse_portnuma_config(const char *q_arg)
                                return -1;
                }
                port_id = (uint8_t)int_fld[FLD_PORT];
-               if (port_id >= nb_ports) {
-                       printf("Invalid port, range is [0, %d]\n", nb_ports - 
1);
+               if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+                       printf("Valid port range is [0");
+                       FOREACH_PORT(pid, ports)
+                               printf(", %d", pid);
+                       printf("]\n");
                        return -1;
                }
                socket_id = (uint8_t)int_fld[FLD_SOCKET];
@@ -423,6 +427,7 @@ parse_ringnuma_config(const char *q_arg)
        };
        unsigned long int_fld[_NUM_FLD];
        char *str_fld[_NUM_FLD];
+       portid_t pid;
        #define RX_RING_ONLY 0x1
        #define TX_RING_ONLY 0x2
        #define RXTX_RING    0x3
@@ -447,8 +452,11 @@ parse_ringnuma_config(const char *q_arg)
                                return -1;
                }
                port_id = (uint8_t)int_fld[FLD_PORT];
-               if (port_id >= nb_ports) {
-                       printf("Invalid port, range is [0, %d]\n", nb_ports - 
1);
+               if (port_id_is_invalid(port_id, ENABLED_WARN)) {
+                       printf("Valid port range is [0");
+                       FOREACH_PORT(pid, ports)
+                               printf(", %d", pid);
+                       printf("]\n");
                        return -1;
                }
                socket_id = (uint8_t)int_fld[FLD_SOCKET];
@@ -620,12 +628,12 @@ launch_args_parse(int argc, char** argv)
 #endif
                        if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
                                n = atoi(optarg);
-                               if (n > 0 && n <= nb_ports)
+                               if (n > 0 &&
+                                   !port_id_is_invalid(n, DISABLED_WARN))
                                        nb_fwd_ports = (uint8_t) n;
                                else
                                        rte_exit(EXIT_FAILURE,
-                                                "nb-ports should be > 0 and <= 
%d\n",
-                                                nb_ports);
+                                                "Invalid port %d\n", n);
                        }
                        if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
                                n = atoi(optarg);
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 3aebea6..48e726e 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -71,6 +71,7 @@
 #include <rte_pci.h>
 #include <rte_ether.h>
 #include <rte_ethdev.h>
+#include <rte_dev.h>
 #include <rte_string_fns.h>
 #ifdef RTE_LIBRTE_PMD_XENVIRT
 #include <rte_eth_xenvirt.h>
@@ -303,7 +304,7 @@ uint16_t nb_rx_queue_stats_mappings = 0;

 /* Forward function declarations */
 static void map_port_queue_stats_mapping_registers(uint8_t pi, struct rte_port 
*port);
-static void check_all_ports_link_status(uint8_t port_num, uint32_t port_mask);
+static void check_all_ports_link_status(uint32_t port_mask);

 /*
  * Check if all the ports are started.
@@ -312,6 +313,20 @@ static void check_all_ports_link_status(uint8_t port_num, 
uint32_t port_mask);
 static int all_ports_started(void);

 /*
+ * Find next enabled port
+ */
+portid_t
+find_next_port(portid_t p, struct rte_port *ports, int size)
+{
+       if (ports == NULL)
+               rte_exit(-EINVAL, "failed to find a next port id\n");
+
+       while ((ports[p].enabled == 0) && (p < size))
+               p++;
+       return p;
+}
+
+/*
  * Setup default configuration.
  */
 static void
@@ -540,7 +555,8 @@ init_config(void)
                                + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST;

                if (!numa_support)
-                       nb_mbuf_per_pool = (nb_mbuf_per_pool * nb_ports);
+                       nb_mbuf_per_pool =
+                               (nb_mbuf_per_pool * RTE_MAX_ETHPORTS);
        }

        if (!numa_support) {
@@ -553,14 +569,19 @@ init_config(void)

        /* Configuration of Ethernet ports. */
        ports = rte_zmalloc("testpmd: ports",
-                           sizeof(struct rte_port) * nb_ports,
+                           sizeof(struct rte_port) * RTE_MAX_ETHPORTS,
                            RTE_CACHE_LINE_SIZE);
        if (ports == NULL) {
-               rte_exit(EXIT_FAILURE, "rte_zmalloc(%d struct rte_port) "
-                                                       "failed\n", nb_ports);
+               rte_exit(EXIT_FAILURE,
+                               "rte_zmalloc(%d struct rte_port) failed\n",
+                               RTE_MAX_ETHPORTS);
        }

-       for (pid = 0; pid < nb_ports; pid++) {
+       /* enabled allocated ports */
+       for (pid = 0; pid < nb_ports; pid++)
+               ports[pid].enabled = 1;
+
+       FOREACH_PORT(pid, ports) {
                port = &ports[pid];
                rte_eth_dev_info_get(pid, &port->dev_info);

@@ -590,8 +611,7 @@ init_config(void)
                        nb_mbuf_per_pool = nb_mbuf_per_pool/nb_ports;

                for (i = 0; i < MAX_SOCKET; i++) {
-                       nb_mbuf = (nb_mbuf_per_pool *
-                                               port_per_socket[i]);
+                       nb_mbuf = (nb_mbuf_per_pool * RTE_MAX_ETHPORTS);
                        if (nb_mbuf)
                                mbuf_pool_create(mbuf_data_size,
                                                nb_mbuf,i);
@@ -623,14 +643,6 @@ reconfig(portid_t new_port_id, unsigned socket_id)
        struct rte_port *port;

        /* Reconfiguration of Ethernet ports. */
-       ports = rte_realloc(ports,
-                           sizeof(struct rte_port) * nb_ports,
-                           RTE_CACHE_LINE_SIZE);
-       if (ports == NULL) {
-               rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) 
failed\n",
-                               nb_ports);
-       }
-
        port = &ports[new_port_id];
        rte_eth_dev_info_get(new_port_id, &port->dev_info);

@@ -651,7 +663,7 @@ init_fwd_streams(void)
        streamid_t sm_id, nb_fwd_streams_new;

        /* set socket id according to numa or not */
-       for (pid = 0; pid < nb_ports; pid++) {
+       FOREACH_PORT(pid, ports) {
                port = &ports[pid];
                if (nb_rxq > port->dev_info.max_rx_queues) {
                        printf("Fail: nb_rxq(%d) is greater than "
@@ -1252,7 +1264,7 @@ all_ports_started(void)
        portid_t pi;
        struct rte_port *port;

-       for (pi = 0; pi < nb_ports; pi++) {
+       FOREACH_PORT(pi, ports) {
                port = &ports[pi];
                /* Check if there is a port which is not started */
                if (port->port_status != RTE_PORT_STARTED)
@@ -1264,6 +1276,45 @@ all_ports_started(void)
 }

 int
+all_ports_stopped(void)
+{
+       portid_t pi;
+       struct rte_port *port;
+
+       FOREACH_PORT(pi, ports) {
+               port = &ports[pi];
+               if (port->port_status != RTE_PORT_STOPPED)
+                       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;
+}
+
+static int
+port_is_closed(portid_t port_id)
+{
+       if (port_id_is_invalid(port_id, ENABLED_WARN))
+               return 0;
+
+       if (ports[port_id].port_status != RTE_PORT_CLOSED)
+               return 0;
+
+       return 1;
+}
+
+int
 start_port(portid_t pid)
 {
        int diag, need_check_link_status = 0;
@@ -1284,8 +1335,8 @@ start_port(portid_t pid)

        if(dcb_config)
                dcb_test = 1;
-       for (pi = 0; pi < nb_ports; pi++) {
-               if (pid < nb_ports && pid != pi)
+       FOREACH_PORT(pi, ports) {
+               if (pid != pi && pid != (portid_t)RTE_PORT_ALL)
                        continue;

                port = &ports[pi];
@@ -1409,7 +1460,7 @@ start_port(portid_t pid)
        }

        if (need_check_link_status && !no_link_check)
-               check_all_ports_link_status(nb_ports, RTE_PORT_ALL);
+               check_all_ports_link_status(RTE_PORT_ALL);
        else
                printf("Please stop the ports first\n");

@@ -1434,8 +1485,8 @@ stop_port(portid_t pid)
        }
        printf("Stopping ports...\n");

-       for (pi = 0; pi < nb_ports; pi++) {
-               if (pid < nb_ports && pid != pi)
+       FOREACH_PORT(pi, ports) {
+               if (!port_id_is_invalid(pid, DISABLED_WARN) && pid != pi)
                        continue;

                port = &ports[pi];
@@ -1451,7 +1502,7 @@ stop_port(portid_t pid)
                need_check_link_status = 1;
        }
        if (need_check_link_status && !no_link_check)
-               check_all_ports_link_status(nb_ports, RTE_PORT_ALL);
+               check_all_ports_link_status(RTE_PORT_ALL);

        printf("Done\n");
 }
@@ -1469,8 +1520,8 @@ close_port(portid_t pid)

        printf("Closing ports...\n");

-       for (pi = 0; pi < nb_ports; pi++) {
-               if (pid < nb_ports && pid != pi)
+       FOREACH_PORT(pi, ports) {
+               if (!port_id_is_invalid(pid, DISABLED_WARN) && pid != pi)
                        continue;

                port = &ports[pi];
@@ -1490,31 +1541,83 @@ close_port(portid_t pid)
        printf("Done\n");
 }

-int
-all_ports_stopped(void)
+void
+attach_port(char *identifier)
 {
-       portid_t pi;
-       struct rte_port *port;
+       portid_t i, j, pi = 0;

-       for (pi = 0; pi < nb_ports; pi++) {
-               port = &ports[pi];
-               if (port->port_status != RTE_PORT_STOPPED)
-                       return 0;
+       printf("Attaching a new port...\n");
+
+       if (identifier == NULL) {
+               printf("Invalid parameters are specified\n");
+               return;
        }

-       return 1;
+       if (test_done == 0) {
+               printf("Please stop forwarding first\n");
+               return;
+       }
+
+       if (rte_eal_dev_attach(identifier, &pi))
+               return;
+
+       ports[pi].enabled = 1;
+       reconfig(pi, rte_eth_dev_socket_id(pi));
+       rte_eth_promiscuous_enable(pi);
+
+       nb_ports = rte_eth_dev_count();
+
+       /* set_default_fwd_ports_config(); */
+       bzero(fwd_ports_ids, sizeof(fwd_ports_ids));
+       i = 0;
+       FOREACH_PORT(j, ports) {
+               fwd_ports_ids[i] = j;
+               i++;
+       }
+       nb_cfg_ports = nb_ports;
+       nb_fwd_ports++;
+
+       ports[pi].port_status = RTE_PORT_STOPPED;
+
+       printf("Port %d is attached. Now total ports is %d\n", pi, nb_ports);
+       printf("Done\n");
 }

-int
-port_is_started(portid_t port_id)
+void
+detach_port(uint8_t port_id)
 {
-       if (port_id_is_invalid(port_id))
-               return -1;
+       portid_t i, pi = 0;
+       char name[RTE_ETH_NAME_MAX_LEN];

-       if (ports[port_id].port_status != RTE_PORT_STARTED)
-               return 0;
+       printf("Detaching a port...\n");

-       return 1;
+       if (!port_is_closed(port_id)) {
+               printf("Please close port first\n");
+               return;
+       }
+
+       rte_eth_promiscuous_disable(port_id);
+
+       if (rte_eal_dev_detach(port_id, name))
+               return;
+
+       ports[port_id].enabled = 0;
+       nb_ports = rte_eth_dev_count();
+
+       /* set_default_fwd_ports_config(); */
+       bzero(fwd_ports_ids, sizeof(fwd_ports_ids));
+       i = 0;
+       FOREACH_PORT(pi, ports) {
+               fwd_ports_ids[i] = pi;
+               i++;
+       }
+       nb_cfg_ports = nb_ports;
+       nb_fwd_ports--;
+
+       printf("Port '%s' is detached. Now total ports is %d\n",
+                       name, nb_ports);
+       printf("Done\n");
+       return;
 }

 void
@@ -1522,7 +1625,7 @@ pmd_test_exit(void)
 {
        portid_t pt_id;

-       for (pt_id = 0; pt_id < nb_ports; pt_id++) {
+       FOREACH_PORT(pt_id, ports) {
                printf("Stopping port %d...", pt_id);
                fflush(stdout);
                rte_eth_dev_close(pt_id);
@@ -1541,7 +1644,7 @@ struct pmd_test_command {

 /* Check the link status of all ports in up to 9s, and print them finally */
 static void
-check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
+check_all_ports_link_status(uint32_t port_mask)
 {
 #define CHECK_INTERVAL 100 /* 100ms */
 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
@@ -1552,7 +1655,7 @@ check_all_ports_link_status(uint8_t port_num, uint32_t 
port_mask)
        fflush(stdout);
        for (count = 0; count <= MAX_CHECK_TIME; count++) {
                all_ports_up = 1;
-               for (portid = 0; portid < port_num; portid++) {
+               FOREACH_PORT(portid, ports) {
                        if ((port_mask & (1 << portid)) == 0)
                                continue;
                        memset(&link, 0, sizeof(link));
@@ -1717,7 +1820,7 @@ init_port_config(void)
        portid_t pid;
        struct rte_port *port;

-       for (pid = 0; pid < nb_ports; pid++) {
+       FOREACH_PORT(pid, ports) {
                port = &ports[pid];
                port->dev_conf.rxmode = rx_mode;
                port->dev_conf.fdir_conf = fdir_conf;
@@ -1896,7 +1999,7 @@ main(int argc, char** argv)

        nb_ports = (portid_t) rte_eth_dev_count();
        if (nb_ports == 0)
-               rte_exit(EXIT_FAILURE, "No probed ethernet device\n");
+               RTE_LOG(WARNING, EAL, "No probed ethernet devices\n");

        set_def_fwd_config();
        if (nb_lcores == 0)
@@ -1918,7 +2021,7 @@ main(int argc, char** argv)
                rte_exit(EXIT_FAILURE, "Start ports failed\n");

        /* set all ports to promiscuous mode by default */
-       for (port_id = 0; port_id < nb_ports; port_id++)
+       FOREACH_PORT(port_id, ports)
                rte_eth_promiscuous_enable(port_id);

 #ifdef RTE_LIBRTE_CMDLINE
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 581130b..29b6755 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -137,6 +137,7 @@ struct fwd_stream {
  * The data structure associated with each port.
  */
 struct rte_port {
+       uint8_t                 enabled;    /**< Port enabled or not */
        struct rte_eth_dev_info dev_info;   /**< PCI info + driver name */
        struct rte_eth_conf     dev_conf;   /**< Port configuration. */
        struct ether_addr       eth_addr;   /**< Port ethernet address */
@@ -162,6 +163,14 @@ struct rte_port {
        struct rte_eth_txconf   tx_conf;    /**< tx configuration */
 };

+extern portid_t __rte_unused
+find_next_port(portid_t p, struct rte_port *ports, int size);
+
+#define FOREACH_PORT(p, ports) \
+       for (p = find_next_port(0, ports, RTE_MAX_ETHPORTS); \
+           p < RTE_MAX_ETHPORTS; \
+           p = find_next_port(p + 1, ports, RTE_MAX_ETHPORTS))
+
 /**
  * The data structure associated with each forwarding logical core.
  * The logical cores are internally numbered by a core index from 0 to
@@ -522,6 +531,8 @@ int init_port_dcb_config(portid_t pid,struct dcb_config 
*dcb_conf);
 int start_port(portid_t pid);
 void stop_port(portid_t pid);
 void close_port(portid_t pid);
+void attach_port(char *identifier);
+void detach_port(uint8_t port_id);
 int all_ports_stopped(void);
 int port_is_started(portid_t port_id);
 void pmd_test_exit(void);
@@ -565,10 +576,15 @@ void get_ethertype_filter(uint8_t port_id, uint16_t 
index);
 void get_2tuple_filter(uint8_t port_id, uint16_t index);
 void get_5tuple_filter(uint8_t port_id, uint16_t index);
 void get_flex_filter(uint8_t port_id, uint16_t index);
-int port_id_is_invalid(portid_t port_id);
 int rx_queue_id_is_invalid(queueid_t rxq_id);
 int tx_queue_id_is_invalid(queueid_t txq_id);

+enum print_warning {
+       ENABLED_WARN = 0,
+       DISABLED_WARN
+};
+int port_id_is_invalid(portid_t port_id, enum print_warning warning);
+
 /*
  * Work-around of a compilation error with ICC on invocations of the
  * rte_be_to_cpu_16() function.
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst 
b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 218835a..936f9e0 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -808,6 +808,63 @@ The following sections show functions for configuring 
ports.

     Port configuration changes only become active when forwarding is 
started/restarted.

+port attach
+~~~~~~~~~~~
+
+Attach a port specified by pci address or virtual device args.
+
+To attach a new pci device, the device should be recognized by kernel first.
+Then it should be moved under DPDK management.
+Finally the port can be attached to testpmd.
+On the other hand, to attach a port created by virtual device, above steps are 
not needed.
+
+port attach (identifier)
+
+For example, to attach a port whose pci address is 0000:02:00.0.
+
+.. code-block:: console
+
+    testpmd> port attach 0000:02:00.0
+    Attaching a new port...
+    ... snip ...
+    Port 0 is attached. Now total ports is 1
+    Done
+
+For example, to attach a port created by pcap PMD.
+
+.. code-block:: console
+
+    testpmd> port attach eth_pcap0,iface=eth0
+    Attaching a new port...
+    ... snip ...
+    Port 0 is attached. Now total ports is 1
+    Done
+
+In this case, identifier is "eth_pcap0,iface=eth0".
+This identifier format is the same as "--vdev" format of DPDK applications.
+
+port detach
+~~~~~~~~~~~
+
+Detach a specific port.
+
+Before detaching a port, the port should be closed.
+Also to remove a pci device completely from the system, first detach the port 
from testpmd.
+Then the device should be moved under kernel management.
+Finally the device can be removed using kernel pci hotplug functionality.
+On the other hand, to remove a port created by a virtual device, above steps 
are not needed.
+
+port detach (port_id)
+
+For example, to detach a port 0.
+
+.. code-block:: console
+
+    testpmd> port detach 0
+    Detaching a port...
+    ... snip ...
+    Done
+
 port start
 ~~~~~~~~~~

-- 
1.9.1

Reply via email to