From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from bcmv-tmail01.ecl.ntt.co.jp (bcmv-tmail01.ecl.ntt.co.jp [124.146.185.148]) by dpdk.org (Postfix) with ESMTP id 5C4C91B120 for ; Tue, 15 Jan 2019 04:30:16 +0100 (CET) Received: from bcmv-ns01.ecl.ntt.co.jp (bcmv-ns01.ecl.ntt.co.jp [129.60.83.123]) by bcmv-tmail01.ecl.ntt.co.jp (8.14.4/8.14.4) with ESMTP id x0F3UEcP017625; Tue, 15 Jan 2019 12:30:14 +0900 Received: from bcmv-ns01.ecl.ntt.co.jp (localhost [127.0.0.1]) by bcmv-ns01.ecl.ntt.co.jp (Postfix) with ESMTP id A9E79186; Tue, 15 Jan 2019 12:30:14 +0900 (JST) Received: from localhost.localdomain (lobster.nslab.ecl.ntt.co.jp [129.60.13.95]) by bcmv-ns01.ecl.ntt.co.jp (Postfix) with ESMTP id 97ACD13F; Tue, 15 Jan 2019 12:30:14 +0900 (JST) From: ogawa.yasufumi@lab.ntt.co.jp To: ferruh.yigit@intel.com, spp@dpdk.org, ogawa.yasufumi@lab.ntt.co.jp Date: Tue, 15 Jan 2019 12:27:59 +0900 Message-Id: <1547522881-10105-5-git-send-email-ogawa.yasufumi@lab.ntt.co.jp> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1547522881-10105-1-git-send-email-ogawa.yasufumi@lab.ntt.co.jp> References: <1547522881-10105-1-git-send-email-ogawa.yasufumi@lab.ntt.co.jp> X-TM-AS-MML: disable Subject: [spp] [PATCH 4/6] shared: refactor common functions X-BeenThere: spp@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Soft Patch Panel List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 15 Jan 2019 03:30:18 -0000 From: Yasufumi Ogawa Move specific variables and functions of each of SPP processes from `shared`. Signed-off-by: Yasufumi Ogawa --- src/shared/common.c | 526 +------------------------------------------- src/shared/common.h | 141 +----------- 2 files changed, 9 insertions(+), 658 deletions(-) diff --git a/src/shared/common.c b/src/shared/common.c index 8837ff9..f7d522a 100644 --- a/src/shared/common.c +++ b/src/shared/common.c @@ -1,69 +1,12 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2015-2016 Intel Corporation + * Copyright(c) 2019 Nippon Telegraph and Telephone Corporation */ #include #include "common.h" -/* Check the link status of all ports in up to 9s, and print them finally */ -void -check_all_ports_link_status(struct port_info *ports, uint16_t port_num, - uint32_t port_mask) -{ -#define CHECK_INTERVAL 100 /* 100ms */ -#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ - uint8_t count, all_ports_up; - uint16_t portid; - struct rte_eth_link link; - - RTE_LOG(INFO, APP, "\nChecking link status"); - fflush(stdout); - for (count = 0; count <= MAX_CHECK_TIME; count++) { - all_ports_up = 1; - for (portid = 0; portid < port_num; portid++) { - if ((port_mask & (1 << ports->id[portid])) == 0) - continue; - - memset(&link, 0, sizeof(link)); - rte_eth_link_get_nowait(ports->id[portid], &link); - - /* clear all_ports_up flag if any link down */ - if (link.link_status == 0) { - all_ports_up = 0; - break; - } - } - - if (all_ports_up == 0) { - printf("."); - fflush(stdout); - rte_delay_ms(CHECK_INTERVAL); - } else { - printf("done\n"); - break; - } - } - - /* all ports up or timed out */ - for (portid = 0; portid < port_num; portid++) { - if ((port_mask & (1 << ports->id[portid])) == 0) - continue; - - memset(&link, 0, sizeof(link)); - rte_eth_link_get_nowait(ports->id[portid], &link); - - /* print link status */ - if (link.link_status) - RTE_LOG(INFO, APP, - "Port %d Link Up - speed %u Mbps - %s\n", - ports->id[portid], link.link_speed, - (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? - "full-duplex\n" : "half-duplex\n"); - else - RTE_LOG(INFO, APP, - "Port %d Link Down\n", ports->id[portid]); - } -} +#define RTE_LOGTYPE_SHARED RTE_LOGTYPE_USER1 /** * Set log level of type RTE_LOGTYPE_USER* to given level, for instance, @@ -75,7 +18,7 @@ check_all_ports_link_status(struct port_info *ports, uint16_t port_num, * ... * set_user_log_level(1, RTE_LOG_DEBUG); * ... - * RTE_LOG(APP, DEBUG, "Your debug log..."); + * RTE_LOG(DEBUG, APP, "Your debug log..."); */ int set_user_log_level(int num_user_log, uint32_t log_level) @@ -100,114 +43,6 @@ set_user_log_debug(int num_user_log) return 0; } -/** - * Initialise an individual port: - * - configure number of rx and tx rings - * - set up each rx ring, to pull from the main mbuf pool - * - set up each tx ring - * - start the port and report its status to stdout - */ -int -init_port(uint16_t port_num, struct rte_mempool *pktmbuf_pool) -{ - /* for port configuration all features are off by default */ - const struct rte_eth_conf port_conf = { - .rxmode = { - .mq_mode = ETH_MQ_RX_RSS, - }, - }; - const uint16_t rx_rings = 1, tx_rings = 1; - const uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT; - const uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT; - uint16_t q; - int retval; - struct rte_eth_dev_info dev_info; - struct rte_eth_conf local_port_conf = port_conf; - struct rte_eth_txconf txq_conf; - - RTE_LOG(INFO, APP, "Port %u init ... ", port_num); - fflush(stdout); - - rte_eth_dev_info_get(port_num, &dev_info); - if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) - local_port_conf.txmode.offloads |= - DEV_TX_OFFLOAD_MBUF_FAST_FREE; - txq_conf = dev_info.default_txconf; - txq_conf.offloads = local_port_conf.txmode.offloads; - - /* - * Standard DPDK port initialisation - config port, then set up - * rx and tx rings - */ - retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, - &port_conf); - if (retval != 0) - return retval; - - for (q = 0; q < rx_rings; q++) { - retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size, - rte_eth_dev_socket_id(port_num), NULL, pktmbuf_pool); - if (retval < 0) - return retval; - } - - for (q = 0; q < tx_rings; q++) { - retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size, - rte_eth_dev_socket_id(port_num), &txq_conf); - if (retval < 0) - return retval; - } - - rte_eth_promiscuous_enable(port_num); - - retval = rte_eth_dev_start(port_num); - if (retval < 0) - return retval; - - RTE_LOG(INFO, APP, "Port %d Init done\n", port_num); - - return 0; -} - -/** - * The ports to be used by the application are passed in - * the form of a bitmask. This function parses the bitmask - * and places the port numbers to be used into the port[] - * array variable - */ -int -parse_portmask(struct port_info *ports, uint16_t max_ports, - const char *portmask) -{ - char *end = NULL; - unsigned long pm; - uint16_t count = 0; - - if (portmask == NULL || *portmask == '\0') - return -1; - - /* convert parameter to a number and verify */ - pm = strtoul(portmask, &end, 16); - if (end == NULL || *end != '\0' || pm == 0) - return -1; - - /* loop through bits of the mask and mark ports */ - while (pm != 0) { - if (pm & 0x01) { /* bit is set in mask, use port */ - if (count >= max_ports) - RTE_LOG(WARNING, APP, - "port %u not present - ignoring\n", - count); - else - ports->id[ports->num_ports++] = count; - } - pm = (pm >> 1); - count++; - } - - return 0; -} - /** * Take the number of clients parameter passed to the app * and convert to a number to store in the num_clients variable @@ -239,363 +74,14 @@ parse_server(char **server_ip, int *server_port, char *server_addr) return -1; *server_ip = strtok(server_addr, delim); - RTE_LOG(DEBUG, APP, "server ip %s\n", *server_ip); + RTE_LOG(DEBUG, SHARED, "server ip %s\n", *server_ip); token = strtok(NULL, delim); - RTE_LOG(DEBUG, APP, "token %s\n", token); + RTE_LOG(DEBUG, SHARED, "token %s\n", token); if (token == NULL || *token == '\0') return -1; - RTE_LOG(DEBUG, APP, "token %s\n", token); + RTE_LOG(DEBUG, SHARED, "token %s\n", token); *server_port = atoi(token); return 0; } - -/** - * Retieve port type and ID from resource UID. For example, resource UID - * 'ring:0' is parsed to retrieve port tyep 'ring' and ID '0'. - */ -int -parse_resource_uid(char *str, char **port_type, int *port_id) -{ - char *token; - char delim[] = ":"; - char *endp; - - RTE_LOG(DEBUG, APP, "Parsing resource UID: '%s\n'", str); - if (strstr(str, delim) == NULL) { - RTE_LOG(ERR, APP, "Invalid resource UID: '%s'\n", str); - return -1; - } - RTE_LOG(DEBUG, APP, "Delimiter %s is included\n", delim); - - *port_type = strtok(str, delim); - - token = strtok(NULL, delim); - *port_id = strtol(token, &endp, 10); - - if (*endp) { - RTE_LOG(ERR, APP, "Bad integer value: %s\n", str); - return -1; - } - - return 0; -} - -int -spp_atoi(const char *str, int *val) -{ - char *end; - - *val = strtol(str, &end, 10); - - if (*end) { - RTE_LOG(ERR, APP, "Bad integer value: %s\n", str); - return -1; - } - - return 0; -} - -/* - * Get status of spp_nfv or spp_vm as JSON format. It consists of running - * status and patch info of ports. - * - * Here is an example of well-formatted JSON status to better understand. - * Actual status has no spaces and new lines inserted as - * '{"status":"running","ports":[{"src":"phy:0","dst":"ring:0"},...]}' - * - * { - * "status": "running", - * "ports": ["phy:0", "phy:1", "ring:0", "vhost:0"], - * "patches": [ - * {"src":"phy:0","dst": "ring:0"}, - * {"src":"ring:0","dst": "vhost:0"} - * ] - * } - */ -void -get_sec_stats_json(char *str, uint16_t client_id, - const char *running_stat, - struct port *ports_fwd_array, - struct port_map *port_map) -{ - sprintf(str, "{\"client-id\":%d,", client_id); - - sprintf(str + strlen(str), "\"status\":"); - sprintf(str + strlen(str), "\"%s\",", running_stat); - - append_port_info_json(str, ports_fwd_array, port_map); - sprintf(str + strlen(str), ","); - - append_patch_info_json(str, ports_fwd_array, port_map); - sprintf(str + strlen(str), "}"); - - // make sure to be terminated with null character - sprintf(str + strlen(str), "%c", '\0'); -} - -/* - * Append patch info to sec status. It is called from get_sec_stats_json() - * to add a JSON formatted patch info to given 'str'. Here is an example. - * - * "ports": ["phy:0", "phy:1", "ring:0", "vhost:0"] - */ -int -append_port_info_json(char *str, - struct port *ports_fwd_array, - struct port_map *port_map) -{ - unsigned int i; - unsigned int has_port = 0; // for checking having port at last - - sprintf(str + strlen(str), "\"ports\":["); - for (i = 0; i < RTE_MAX_ETHPORTS; i++) { - - if (ports_fwd_array[i].in_port_id == PORT_RESET) - continue; - - has_port = 1; - switch (port_map[i].port_type) { - case PHY: - sprintf(str + strlen(str), "\"phy:%u\",", - port_map[i].id); - break; - case RING: - sprintf(str + strlen(str), "\"ring:%u\",", - port_map[i].id); - break; - case VHOST: - sprintf(str + strlen(str), "\"vhost:%u\",", - port_map[i].id); - break; - case PCAP: - sprintf(str + strlen(str), "\"pcap:%u\",", - port_map[i].id); - break; - case NULLPMD: - sprintf(str + strlen(str), "\"nullpmd:%u\",", - port_map[i].id); - break; - case UNDEF: - /* TODO(yasufum) Need to remove print for undefined ? */ - sprintf(str + strlen(str), "\"udf\","); - break; - } - } - - // Check if it has at least one port to remove ",". - if (has_port == 0) { - sprintf(str + strlen(str), "]"); - } else { // Remove last ',' - sprintf(str + strlen(str) - 1, "]"); - } - - return 0; -} - -/* - * Append patch info to sec status. It is called from get_sec_stats_json() - * to add a JSON formatted patch info to given 'str'. Here is an example. - * - * "patches": [ - * {"src":"phy:0","dst": "ring:0"}, - * {"src":"ring:0","dst": "vhost:0"} - * ] - */ -int -append_patch_info_json(char *str, - struct port *ports_fwd_array, - struct port_map *port_map) -{ - unsigned int i; - unsigned int has_patch = 0; // for checking having patch at last - - char patch_str[128]; - sprintf(str + strlen(str), "\"patches\":["); - for (i = 0; i < RTE_MAX_ETHPORTS; i++) { - - if (ports_fwd_array[i].in_port_id == PORT_RESET) - continue; - - RTE_LOG(INFO, APP, "Port ID %d\n", i); - RTE_LOG(INFO, APP, "Status %d\n", - ports_fwd_array[i].in_port_id); - - memset(patch_str, '\0', sizeof(patch_str)); - - sprintf(patch_str, "{\"src\":"); - - switch (port_map[i].port_type) { - case PHY: - RTE_LOG(INFO, APP, "Type: PHY\n"); - sprintf(patch_str + strlen(patch_str), - "\"phy:%u\",", - port_map[i].id); - break; - case RING: - RTE_LOG(INFO, APP, "Type: RING\n"); - sprintf(patch_str + strlen(patch_str), - "\"ring:%u\",", - port_map[i].id); - break; - case VHOST: - RTE_LOG(INFO, APP, "Type: VHOST\n"); - sprintf(patch_str + strlen(patch_str), - "\"vhost:%u\",", - port_map[i].id); - break; - case PCAP: - RTE_LOG(INFO, APP, "Type: PCAP\n"); - sprintf(patch_str + strlen(patch_str), - "\"pcap:%u\",", - port_map[i].id); - break; - case NULLPMD: - RTE_LOG(INFO, APP, "Type: NULLPMD\n"); - sprintf(patch_str + strlen(patch_str), - "\"nullpmd:%u\",", - port_map[i].id); - break; - case UNDEF: - RTE_LOG(INFO, APP, "Type: UDF\n"); - /* TODO(yasufum) Need to remove print for undefined ? */ - sprintf(patch_str + strlen(patch_str), - "\"udf\","); - break; - } - - sprintf(patch_str + strlen(patch_str), "\"dst\":"); - - RTE_LOG(INFO, APP, "Out Port ID %d\n", - ports_fwd_array[i].out_port_id); - - if (ports_fwd_array[i].out_port_id == PORT_RESET) { - //sprintf(patch_str + strlen(patch_str), "%s", "\"\""); - continue; - } else { - has_patch = 1; - unsigned int j = ports_fwd_array[i].out_port_id; - switch (port_map[j].port_type) { - case PHY: - RTE_LOG(INFO, APP, "Type: PHY\n"); - sprintf(patch_str + strlen(patch_str), - "\"phy:%u\"", - port_map[j].id); - break; - case RING: - RTE_LOG(INFO, APP, "Type: RING\n"); - sprintf(patch_str + strlen(patch_str), - "\"ring:%u\"", - port_map[j].id); - break; - case VHOST: - RTE_LOG(INFO, APP, "Type: VHOST\n"); - sprintf(patch_str + strlen(patch_str), - "\"vhost:%u\"", - port_map[j].id); - break; - case PCAP: - RTE_LOG(INFO, APP, "Type: PCAP\n"); - sprintf(patch_str + strlen(patch_str), - "\"pcap:%u\"", - port_map[j].id); - break; - case NULLPMD: - RTE_LOG(INFO, APP, "Type: NULLPMD\n"); - sprintf(patch_str + strlen(patch_str), - "\"nullpmd:%u\"", - port_map[j].id); - break; - case UNDEF: - RTE_LOG(INFO, APP, "Type: UDF\n"); - /* - * TODO(yasufum) Need to remove print for - * undefined ? - */ - sprintf(patch_str + strlen(patch_str), - "\"udf\""); - break; - } - } - - sprintf(patch_str + strlen(patch_str), "},"); - - if (has_patch != 0) - sprintf(str + strlen(str), "%s", patch_str); - } - - - // Check if it has at least one patch to remove ",". - if (has_patch == 0) { - sprintf(str + strlen(str), "]"); - } else { // Remove last ',' - sprintf(str + strlen(str) - 1, "]"); - } - - return 0; -} - -/* attach the new device, then store port_id of the device */ -int -dev_attach_by_devargs(const char *devargs, uint16_t *port_id) -{ - int ret = -1; - struct rte_devargs da; - - memset(&da, 0, sizeof(da)); - - /* parse devargs */ - if (rte_devargs_parse(&da, devargs)) - return -1; - - ret = rte_eal_hotplug_add(da.bus->name, da.name, da.args); - if (ret < 0) { - free(da.args); - return ret; - } - - ret = rte_eth_dev_get_port_by_name(da.name, port_id); - - free(da.args); - - return ret; -} - -/* detach the device, then store the name of the device */ -int -dev_detach_by_port_id(uint16_t port_id) -{ - struct rte_device *dev; - struct rte_bus *bus; - uint32_t dev_flags; - int ret = -1; - - if (rte_eth_devices[port_id].data == NULL) { - RTE_LOG(INFO, APP, - "rte_eth_devices[%"PRIu16"].data is NULL\n", port_id); - return 0; - } - dev_flags = rte_eth_devices[port_id].data->dev_flags; - if (dev_flags & RTE_ETH_DEV_BONDED_SLAVE) { - RTE_LOG(ERR, APP, - "Port %"PRIu16" is bonded, cannot detach\n", port_id); - return -ENOTSUP; - } - - dev = rte_eth_devices[port_id].device; - if (dev == NULL) - return -EINVAL; - - bus = rte_bus_find_by_device(dev); - if (bus == NULL) - return -ENOENT; - - ret = rte_eal_hotplug_remove(bus->name, dev->name); - if (ret < 0) - return ret; - - rte_eth_dev_release_port(&rte_eth_devices[port_id]); - - return 0; -} diff --git a/src/shared/common.h b/src/shared/common.h index 3607f4b..cde013e 100644 --- a/src/shared/common.h +++ b/src/shared/common.h @@ -1,32 +1,14 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2015-2016 Intel Corporation + * Copyright(c) 2019 Nippon Telegraph and Telephone Corporation */ -#ifndef _COMMON_H_ -#define _COMMON_H_ +#ifndef _SHARED_COMMON_H_ +#define _SHARED_COMMON_H_ #include -#include -#include -#include -#include -#include #include -#include - -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #define MSG_SIZE 2048 /* socket buffer len */ @@ -53,15 +35,6 @@ */ #define MAX_PKT_BURST 32 -// TODO(yasufum) move it from common.h used only for primary and spp_vm. -#define MBUFS_PER_CLIENT 1536 -#define MBUFS_PER_PORT 1536 -#define MBUF_CACHE_SIZE 512 - -#define MBUF_OVERHEAD (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) -#define RX_MBUF_DATA_SIZE 2048 -#define MBUF_SIZE (RX_MBUF_DATA_SIZE + MBUF_OVERHEAD) - #define RTE_MP_RX_DESC_DEFAULT 512 #define RTE_MP_TX_DESC_DEFAULT 512 @@ -124,19 +97,8 @@ struct port { /* define common names for structures shared between server and client */ #define MP_CLIENT_RXQ_NAME "eth_ring%u" #define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool" -#define VM_PKTMBUF_POOL_NAME "VM_Proc_pktmbuf_pool" -#define VM_MZ_PORT_INFO "VM_Proc_port_info" #define MZ_PORT_INFO "MProc_port_info" -#define VHOST_BACKEND_NAME "eth_vhost%u" -#define VHOST_IFACE_NAME "/tmp/sock%u" - -#define PCAP_PMD_DEV_NAME "eth_pcap%u" -#define PCAP_IFACE_RX "/tmp/spp-rx%d.pcap" -#define PCAP_IFACE_TX "/tmp/spp-tx%d.pcap" - -#define NULL_PMD_DEV_NAME "eth_null%u" - /* * Given the rx queue name template above, get the queue name */ @@ -153,110 +115,13 @@ get_rx_queue_name(unsigned int id) return buffer; } -static inline const char * -get_vhost_backend_name(unsigned int id) -{ - /* - * buffer for return value. Size calculated by %u being replaced - * by maximum 3 digits (plus an extra byte for safety) - */ - static char buffer[sizeof(VHOST_BACKEND_NAME) + 2]; - - snprintf(buffer, sizeof(buffer) - 1, VHOST_BACKEND_NAME, id); - return buffer; -} - -static inline char * -get_vhost_iface_name(unsigned int id) -{ - /* - * buffer for return value. Size calculated by %u being replaced - * by maximum 3 digits (plus an extra byte for safety) - */ - static char buffer[sizeof(VHOST_IFACE_NAME) + 2]; - - snprintf(buffer, sizeof(buffer) - 1, VHOST_IFACE_NAME, id); - return buffer; -} - -static inline const char * -get_pcap_pmd_name(int id) -{ - static char buffer[sizeof(PCAP_PMD_DEV_NAME) + 2]; - snprintf(buffer, sizeof(buffer) - 1, PCAP_PMD_DEV_NAME, id); - return buffer; -} - -static inline const char * -get_null_pmd_name(int id) -{ - static char buffer[sizeof(NULL_PMD_DEV_NAME) + 2]; - snprintf(buffer, sizeof(buffer) - 1, NULL_PMD_DEV_NAME, id); - return buffer; -} - /* Set log level of type RTE_LOGTYPE_USER* to given level. */ int set_user_log_level(int num_user_log, uint32_t log_level); /* Set log level of type RTE_LOGTYPE_USER* to RTE_LOG_DEBUG. */ int set_user_log_debug(int num_user_log); -void check_all_ports_link_status(struct port_info *ports, uint16_t port_num, - uint32_t port_mask); - -int init_port(uint16_t port_num, struct rte_mempool *pktmbuf_pool); - -int parse_portmask(struct port_info *ports, uint16_t max_ports, - const char *portmask); int parse_num_clients(uint16_t *num_clients, const char *clients); int parse_server(char **server_ip, int *server_port, char *server_addr); -/* Get status of spp_nfv or spp_vm as JSON format. */ -void get_sec_stats_json(char *str, uint16_t client_id, - const char *running_stat, - struct port *ports_fwd_array, - struct port_map *port_map); - -/* Append port info to sec status, called from get_sec_stats_json(). */ -int append_port_info_json(char *str, - struct port *ports_fwd_array, - struct port_map *port_map); - -/* Append patch info to sec status, called from get_sec_stats_json(). */ -int append_patch_info_json(char *str, - struct port *ports_fwd_array, - struct port_map *port_map); - -int parse_resource_uid(char *str, char **port_type, int *port_id); -int spp_atoi(const char *str, int *val); - -#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1 - -/** - * Attach a new Ethernet device specified by arguments. - * - * @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 'net_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 -dev_attach_by_devargs(const char *devargs, uint16_t *port_id); - -/** - * Detach a Ethernet device specified by port identifier. - * This function must be called when the device is in the - * closed state. - * - * @param port_id - * The port identifier of the device to detach. - * @return - * 0 on success and devname is filled, negative on error - */ -int dev_detach_by_port_id(uint16_t port_id); - #endif -- 2.17.1