KNI接口 "KNI_GetFieldID" 调用失败问题的解决

本文深入探讨了KNI接口KNI_GetFieldID调用失败的原因,并解释了当类的字段被romizer改名时如何导致获取失败。通过在cldc/src/vm/cldc_rom.cfg文件中使用DontRenameNonPublicFields选项,可以避免字段被改名,从而确保KNI_GetFieldID的正常工作。文章还建议对于JAVA上层实体类的成员应尽量定义为public,以提高代码的兼容性和稳定性。

KNI接口 "KNI_GetFieldID" 调用失败问题的解决


每次调用KNI_GetFieldID总是返回0,也就是在函数_KNI_field_lookup_helper()中执行到如下代码片段:

  if (!field.is_valid() || (field.is_static() != is_static) ||
      // We do not support field IDs in super classes:
      (field.is_static() && !holder().equals(&ic))) {
    return (jfieldID)0;


然而在 kni.h 中有这么一段注释:

/**
 * Instance field access.
 *
 * Note specific to this VM: KNI_GetFieldID() must not be applied
 * on a class whose
 * fields have been renamed by the romizer. You need to use
 * the DontRenameNonPublicFields flag in your romizer configuration
 * file. See src/vm/cldc_rom.cfg in this VM source distribution
 * for an example.
 */
KNIEXPORT jfieldID KNI_GetFieldID(jclass classHandle, const char* name,
                       const char* signature);

也就是说 class 的 fields 会被 romizer 改名,而KNI_GetFieldID对改名后的 fields 会获取失败;
为避免 fields 被改名,需要在cldc/src/vm/cldc_rom.cfg 中使用DontRenameNonPublicFields,如下:

# Do not rename the non-public fields/methods of the following classes,
# because these fields are accessed by name in KNI code.
DontRenameNonPublicFields = com.eastcom.eui.entity.Contact
DontRenameNonPublicFields = com.sun.cldc.io.ResourceInputStream
If CLDC_11
DontRenameNonPublicFields = java.lang.ref.WeakReference
EndIf
#Some symbols are referenced during bootstrap to verify the class
If ISOLATES
DontRenameNonPublicFields = com.sun.cldc.isolate.Isolate
DontRenameNonPublicMethods = com.sun.cldc.isolate.Isolate
EndIf

修改后验证,KNI_GetFieldID 获取成功。

结论就是KNI对应的JAVA上层实体类的成员一般最好定义成public的,如果是private,

需要修改cldc/src/vm/cldc_rom.cfg文件。



PS: 从字面上理解DontRenameNonPublicFields,private域会被修改,而public域是否不会被改名呢?
KNI_GetFieldID 对 public域是否能总是获取成功?此猜想待验证(不会改名)


/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2010-2014 Intel Corporation */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <inttypes.h> #include <string.h> #include <sys/queue.h> #include <stdarg.h> #include <errno.h> #include <getopt.h> #include <netinet/in.h> #include <linux/if.h> #include <linux/if_tun.h> #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <signal.h> #include <rte_common.h> #include <rte_log.h> #include <rte_memory.h> #include <rte_memcpy.h> #include <rte_eal.h> #include <rte_per_lcore.h> #include <rte_launch.h> #include <rte_atomic.h> #include <rte_lcore.h> #include <rte_branch_prediction.h> #include <rte_interrupts.h> #include <rte_bus_pci.h> #include <rte_debug.h> #include <rte_ether.h> #include <rte_ethdev.h> #include <rte_mempool.h> #include <rte_mbuf.h> #include <rte_string_fns.h> #include <rte_cycles.h> #include <rte_malloc.h> #include <rte_kni.h> /* Macros for printing using RTE_LOG */ #define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1 /* Max size of a single packet */ #define MAX_PACKET_SZ 2048 /* Size of the data buffer in each mbuf */ #define MBUF_DATA_SZ (MAX_PACKET_SZ + RTE_PKTMBUF_HEADROOM) /* Number of mbufs in mempool that is created */ #define NB_MBUF (8192 * 16) /* How many packets to attempt to read from NIC in one go */ #define PKT_BURST_SZ 32 /* How many objects (mbufs) to keep in per-lcore mempool cache */ #define MEMPOOL_CACHE_SZ PKT_BURST_SZ /* Number of RX ring descriptors */ #define NB_RXD 1024 /* Number of TX ring descriptors */ #define NB_TXD 1024 /* Total octets in ethernet header */ #define KNI_ENET_HEADER_SIZE 14 /* Total octets in the FCS */ #define KNI_ENET_FCS_SIZE 4 #define KNI_US_PER_SECOND 1000000 #define KNI_SECOND_PER_DAY 86400 #define KNI_MAX_KTHREAD 32 /* * Structure of port parameters */ struct kni_port_params { uint16_t port_id;/* Port ID */ unsigned lcore_rx; /* lcore ID for RX */ unsigned lcore_tx; /* lcore ID for TX */ uint32_t nb_lcore_k; /* Number of lcores for KNI multi kernel threads */ uint32_t nb_kni; /* Number of KNI devices to be created */ unsigned lcore_k[KNI_MAX_KTHREAD]; /* lcore ID list for kthreads */ struct rte_kni *kni[KNI_MAX_KTHREAD]; /* KNI context pointers */ } __rte_cache_aligned; static struct kni_port_params *kni_port_params_array[RTE_MAX_ETHPORTS]; /* Options for configuring ethernet port */ static struct rte_eth_conf port_conf = { .txmode = { .mq_mode = ETH_MQ_TX_NONE, }, }; /* Mempool for mbufs */ static struct rte_mempool * pktmbuf_pool = NULL; /* Mask of enabled ports */ static uint32_t ports_mask = 0; /* Ports set in promiscuous mode off by default. */ static int promiscuous_on = 0; /* Monitor link status continually. off by default. */ static int monitor_links; /* Structure type for recording kni interface specific stats */ struct kni_interface_stats { /* number of pkts received from NIC, and sent to KNI */ uint64_t rx_packets; /* number of pkts received from NIC, but failed to send to KNI */ uint64_t rx_dropped; /* number of pkts received from KNI, and sent to NIC */ uint64_t tx_packets; /* number of pkts received from KNI, but failed to send to NIC */ uint64_t tx_dropped; }; /* kni device statistics array */ static struct kni_interface_stats kni_stats[RTE_MAX_ETHPORTS]; static int kni_change_mtu(uint16_t port_id, unsigned int new_mtu); static int kni_config_network_interface(uint16_t port_id, uint8_t if_up); static int kni_config_mac_address(uint16_t port_id, uint8_t mac_addr[]); static rte_atomic32_t kni_stop = RTE_ATOMIC32_INIT(0); static rte_atomic32_t kni_pause = RTE_ATOMIC32_INIT(0); /* Print out statistics on packets handled */ static void print_stats(void) { uint16_t i; printf("\n**KNI example application statistics**\n" "====== ============== ============ ============ ============ ============\n" " Port Lcore(RX/TX) rx_packets rx_dropped tx_packets tx_dropped\n" "------ -------------- ------------ ------------ ------------ ------------\n"); for (i = 0; i < RTE_MAX_ETHPORTS; i++) { if (!kni_port_params_array[i]) continue; printf("%7d %10u/%2u %13"PRIu64" %13"PRIu64" %13"PRIu64" " "%13"PRIu64"\n", i, kni_port_params_array[i]->lcore_rx, kni_port_params_array[i]->lcore_tx, kni_stats[i].rx_packets, kni_stats[i].rx_dropped, kni_stats[i].tx_packets, kni_stats[i].tx_dropped); } printf("====== ============== ============ ============ ============ ============\n"); fflush(stdout); } /* Custom handling of signals to handle stats and kni processing */ static void signal_handler(int signum) { /* When we receive a USR1 signal, print stats */ if (signum == SIGUSR1) { print_stats(); } /* When we receive a USR2 signal, reset stats */ if (signum == SIGUSR2) { memset(&kni_stats, 0, sizeof(kni_stats)); printf("\n** Statistics have been reset **\n"); return; } /* When we receive a RTMIN or SIGINT signal, stop kni processing */ if (signum == SIGRTMIN || signum == SIGINT){ printf("\nSIGRTMIN/SIGINT received. KNI processing stopping.\n"); rte_atomic32_inc(&kni_stop); return; } } #include <stdio.h> #include <sys/time.h> static void kni_burst_free_mbufs(struct rte_mbuf **pkts, unsigned num) { unsigned i; if (pkts == NULL) return; for (i = 0; i < num; i++) { rte_pktmbuf_free(pkts[i]); pkts[i] = NULL; } } /** * Interface to burst rx and enqueue mbufs into rx_q */ static void kni_ingress(struct kni_port_params *p) { uint8_t i; uint16_t port_id; unsigned nb_rx, num; uint32_t nb_kni; struct rte_mbuf *pkts_burst[PKT_BURST_SZ]; if (p == NULL) return; nb_kni = p->nb_kni; port_id = p->port_id; for (i = 0; i < nb_kni; i++) { // struct timeval start, end; // double elapsed_time; // // // 记录开始时间 // gettimeofday(&start, NULL); /* Burst rx from eth */ nb_rx = rte_eth_rx_burst(port_id, 0, pkts_burst, PKT_BURST_SZ); // if (nb_rx > 0) { // printf ("Burst rx from eth: %d ,\n", nb_rx); // } if (unlikely(nb_rx > PKT_BURST_SZ)) { RTE_LOG(ERR, APP, "Error receiving from eth\n"); return; } /* Burst tx to kni */ num = rte_kni_tx_burst(p->kni[i], pkts_burst, nb_rx); // if (num > 0) { // printf ("Burst tx to kni: %d ,\n", num); // } if (num) kni_stats[port_id].rx_packets += num; rte_kni_handle_request(p->kni[i]); if (unlikely(num < nb_rx)) { /* Free mbufs not tx to kni interface */ kni_burst_free_mbufs(&pkts_burst[num], nb_rx - num); kni_stats[port_id].rx_dropped += nb_rx - num; } // if(num > 0) { // // 记录结束时间 // gettimeofday(&end, NULL); // // // 计算耗时(单位:秒) // elapsed_time = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) ; // printf("kni_ingress: %.6f us\n", elapsed_time); // } } } /** * Interface to dequeue mbufs from tx_q and burst tx */ static void kni_egress(struct kni_port_params *p) { uint8_t i; uint16_t port_id; unsigned nb_tx, num; uint32_t nb_kni; struct rte_mbuf *pkts_burst[PKT_BURST_SZ]; if (p == NULL) return; nb_kni = p->nb_kni; port_id = p->port_id; for (i = 0; i < nb_kni; i++) { // struct timeval start, end; // double elapsed_time; // // 记录开始时间 // gettimeofday(&start, NULL); /* Burst rx from kni */ num = rte_kni_rx_burst(p->kni[i], pkts_burst, PKT_BURST_SZ); // if (num > 0) { // printf ("Burst rx from kni: %d ,\n", num); // } if (unlikely(num > PKT_BURST_SZ)) { RTE_LOG(ERR, APP, "Error receiving from KNI\n"); return; } /* Burst tx to eth */ nb_tx = rte_eth_tx_burst(port_id, 0, pkts_burst, (uint16_t)num); // if (nb_tx > 0) { // printf ("Burst tx to eth: %d ,\n", nb_tx); // } if (nb_tx) kni_stats[port_id].tx_packets += nb_tx; if (unlikely(nb_tx < num)) { /* Free mbufs not tx to NIC */ kni_burst_free_mbufs(&pkts_burst[nb_tx], num - nb_tx); kni_stats[port_id].tx_dropped += num - nb_tx; } // if(num > 0) { // // 记录结束时间 // gettimeofday(&end, NULL); // // // 计算耗时(单位:秒) // elapsed_time = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) ; // printf("kni_egress: %.6f us\n", elapsed_time); // } } } static int main_loop(__rte_unused void *arg) { uint16_t i; int32_t f_stop; int32_t f_pause; const unsigned lcore_id = rte_lcore_id(); printf("lcore_id %d, \n", lcore_id); fflush(stdout); enum lcore_rxtx { LCORE_NONE, LCORE_RX, LCORE_TX, LCORE_MAX }; enum lcore_rxtx flag = LCORE_NONE; RTE_ETH_FOREACH_DEV(i) { if (!kni_port_params_array[i]) continue; if (kni_port_params_array[i]->lcore_rx == (uint8_t)lcore_id) { flag = LCORE_RX; break; } else if (kni_port_params_array[i]->lcore_tx == (uint8_t)lcore_id) { flag = LCORE_TX; break; } } if (flag == LCORE_RX) { RTE_LOG(INFO, APP, "Lcore %u is reading from port %d\n", kni_port_params_array[i]->lcore_rx, kni_port_params_array[i]->port_id); while (1) { f_stop = rte_atomic32_read(&kni_stop); f_pause = rte_atomic32_read(&kni_pause); if (f_stop) break; if (f_pause) continue; kni_ingress(kni_port_params_array[i]); } } else if (flag == LCORE_TX) { RTE_LOG(INFO, APP, "Lcore %u is writing to port %d\n", kni_port_params_array[i]->lcore_tx, kni_port_params_array[i]->port_id); while (1) { f_stop = rte_atomic32_read(&kni_stop); f_pause = rte_atomic32_read(&kni_pause); if (f_stop) break; if (f_pause) continue; kni_egress(kni_port_params_array[i]); } } else RTE_LOG(INFO, APP, "Lcore %u has nothing to do\n", lcore_id); return 0; } /* Display usage instructions */ static void print_usage(const char *prgname) { RTE_LOG(INFO, APP, "\nUsage: %s [EAL options] -- -p PORTMASK -P -m " "[--config (port,lcore_rx,lcore_tx,lcore_kthread...)" "[,(port,lcore_rx,lcore_tx,lcore_kthread...)]]\n" " -p PORTMASK: hex bitmask of ports to use\n" " -P : enable promiscuous mode\n" " -m : enable monitoring of port carrier state\n" " --config (port,lcore_rx,lcore_tx,lcore_kthread...): " "port and lcore configurations\n", prgname); } /* Convert string to unsigned number. 0 is returned if error occurs */ static uint32_t parse_unsigned(const char *portmask) { char *end = NULL; unsigned long num; num = strtoul(portmask, &end, 16); if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) return 0; return (uint32_t)num; } static void print_config(void) { uint32_t i, j; struct kni_port_params **p = kni_port_params_array; for (i = 0; i < RTE_MAX_ETHPORTS; i++) { if (!p[i]) continue; RTE_LOG(DEBUG, APP, "Port ID: %d\n", p[i]->port_id); RTE_LOG(DEBUG, APP, "Rx lcore ID: %u, Tx lcore ID: %u\n", p[i]->lcore_rx, p[i]->lcore_tx); for (j = 0; j < p[i]->nb_lcore_k; j++) RTE_LOG(DEBUG, APP, "Kernel thread lcore ID: %u\n", p[i]->lcore_k[j]); } } static int parse_config(const char *arg) { const char *p, *p0 = arg; char s[256], *end; unsigned size; enum fieldnames { FLD_PORT = 0, FLD_LCORE_RX, FLD_LCORE_TX, _NUM_FLD = KNI_MAX_KTHREAD + 3, }; int i, j, nb_token; char *str_fld[_NUM_FLD]; unsigned long int_fld[_NUM_FLD]; uint16_t port_id, nb_kni_port_params = 0; memset(&kni_port_params_array, 0, sizeof(kni_port_params_array)); while (((p = strchr(p0, '(')) != NULL) && nb_kni_port_params < RTE_MAX_ETHPORTS) { p++; if ((p0 = strchr(p, ')')) == NULL) goto fail; size = p0 - p; if (size >= sizeof(s)) { printf("Invalid config parameters\n"); goto fail; } snprintf(s, sizeof(s), "%.*s", size, p); nb_token = rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ','); if (nb_token <= FLD_LCORE_TX) { printf("Invalid config parameters\n"); goto fail; } for (i = 0; i < nb_token; i++) { errno = 0; int_fld[i] = strtoul(str_fld[i], &end, 0); if (errno != 0 || end == str_fld[i]) { printf("Invalid config parameters\n"); goto fail; } } i = 0; port_id = int_fld[i++]; if (port_id >= RTE_MAX_ETHPORTS) { printf("Port ID %d could not exceed the maximum %d\n", port_id, RTE_MAX_ETHPORTS); goto fail; } if (kni_port_params_array[port_id]) { printf("Port %d has been configured\n", port_id); goto fail; } kni_port_params_array[port_id] = rte_zmalloc("KNI_port_params", sizeof(struct kni_port_params), RTE_CACHE_LINE_SIZE); kni_port_params_array[port_id]->port_id = port_id; kni_port_params_array[port_id]->lcore_rx = (uint8_t)int_fld[i++]; kni_port_params_array[port_id]->lcore_tx = (uint8_t)int_fld[i++]; if (kni_port_params_array[port_id]->lcore_rx >= RTE_MAX_LCORE || kni_port_params_array[port_id]->lcore_tx >= RTE_MAX_LCORE) { printf("lcore_rx %u or lcore_tx %u ID could not " "exceed the maximum %u\n", kni_port_params_array[port_id]->lcore_rx, kni_port_params_array[port_id]->lcore_tx, (unsigned)RTE_MAX_LCORE); goto fail; } for (j = 0; i < nb_token && j < KNI_MAX_KTHREAD; i++, j++) kni_port_params_array[port_id]->lcore_k[j] = (uint8_t)int_fld[i]; kni_port_params_array[port_id]->nb_lcore_k = j; } print_config(); return 0; fail: for (i = 0; i < RTE_MAX_ETHPORTS; i++) { if (kni_port_params_array[i]) { rte_free(kni_port_params_array[i]); kni_port_params_array[i] = NULL; } } return -1; } static int validate_parameters(uint32_t portmask) { uint32_t i; if (!portmask) { printf("No port configured in port mask\n"); return -1; } for (i = 0; i < RTE_MAX_ETHPORTS; i++) { if (((portmask & (1 << i)) && !kni_port_params_array[i]) || (!(portmask & (1 << i)) && kni_port_params_array[i])) rte_exit(EXIT_FAILURE, "portmask is not consistent " "to port ids specified in --config\n"); if (kni_port_params_array[i] && !rte_lcore_is_enabled(\ (unsigned)(kni_port_params_array[i]->lcore_rx))) rte_exit(EXIT_FAILURE, "lcore id %u for " "port %d receiving not enabled\n", kni_port_params_array[i]->lcore_rx, kni_port_params_array[i]->port_id); if (kni_port_params_array[i] && !rte_lcore_is_enabled(\ (unsigned)(kni_port_params_array[i]->lcore_tx))) rte_exit(EXIT_FAILURE, "lcore id %u for " "port %d transmitting not enabled\n", kni_port_params_array[i]->lcore_tx, kni_port_params_array[i]->port_id); } return 0; } #define CMDLINE_OPT_CONFIG "config" /* Parse the arguments given in the command line of the application */ static int parse_args(int argc, char **argv) { int opt, longindex, ret = 0; const char *prgname = argv[0]; static struct option longopts[] = { {CMDLINE_OPT_CONFIG, required_argument, NULL, 0}, {NULL, 0, NULL, 0} }; /* Disable printing messages within getopt() */ opterr = 0; /* Parse command line */ while ((opt = getopt_long(argc, argv, "p:Pm", longopts, &longindex)) != EOF) { switch (opt) { case 'p': ports_mask = parse_unsigned(optarg); break; case 'P': promiscuous_on = 1; break; case 'm': monitor_links = 1; break; case 0: if (!strncmp(longopts[longindex].name, CMDLINE_OPT_CONFIG, sizeof(CMDLINE_OPT_CONFIG))) { ret = parse_config(optarg); if (ret) { printf("Invalid config\n"); print_usage(prgname); return -1; } } break; default: print_usage(prgname); rte_exit(EXIT_FAILURE, "Invalid option specified\n"); } } /* Check that options were parsed ok */ if (validate_parameters(ports_mask) < 0) { print_usage(prgname); rte_exit(EXIT_FAILURE, "Invalid parameters\n"); } return ret; } /* Initialize KNI subsystem */ static void init_kni(void) { unsigned int num_of_kni_ports = 0, i; struct kni_port_params **params = kni_port_params_array; /* Calculate the maximum number of KNI interfaces that will be used */ for (i = 0; i < RTE_MAX_ETHPORTS; i++) { if (kni_port_params_array[i]) { num_of_kni_ports += (params[i]->nb_lcore_k ? params[i]->nb_lcore_k : 1); } } /* Invoke rte KNI init to preallocate the ports */ rte_kni_init(num_of_kni_ports); } /* Initialise a single port on an Ethernet device */ static void init_port(uint16_t port) { int ret; uint16_t nb_rxd = NB_RXD; uint16_t nb_txd = NB_TXD; struct rte_eth_dev_info dev_info; struct rte_eth_rxconf rxq_conf; struct rte_eth_txconf txq_conf; struct rte_eth_conf local_port_conf = port_conf; /* Initialise device and RX/TX queues */ RTE_LOG(INFO, APP, "Initialising port %u ...\n", (unsigned)port); fflush(stdout); ret = rte_eth_dev_info_get(port, &dev_info); if (ret != 0) rte_exit(EXIT_FAILURE, "Error during getting device (port %u) info: %s\n", port, strerror(-ret)); if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) local_port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MBUF_FAST_FREE; ret = rte_eth_dev_configure(port, 1, 1, &local_port_conf); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not configure port%u (%d)\n", (unsigned)port, ret); ret = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not adjust number of descriptors " "for port%u (%d)\n", (unsigned)port, ret); rxq_conf = dev_info.default_rxconf; rxq_conf.offloads = local_port_conf.rxmode.offloads; ret = rte_eth_rx_queue_setup(port, 0, nb_rxd, rte_eth_dev_socket_id(port), &rxq_conf, pktmbuf_pool); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not setup up RX queue for " "port%u (%d)\n", (unsigned)port, ret); txq_conf = dev_info.default_txconf; txq_conf.offloads = local_port_conf.txmode.offloads; ret = rte_eth_tx_queue_setup(port, 0, nb_txd, rte_eth_dev_socket_id(port), &txq_conf); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not setup up TX queue for " "port%u (%d)\n", (unsigned)port, ret); ret = rte_eth_dev_start(port); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not start port%u (%d)\n", (unsigned)port, ret); if (promiscuous_on) { ret = rte_eth_promiscuous_enable(port); if (ret != 0) rte_exit(EXIT_FAILURE, "Could not enable promiscuous mode for port%u: %s\n", port, rte_strerror(-ret)); } } /* Check the link status of all ports in up to 9s, and print them finally */ static void check_all_ports_link_status(uint32_t port_mask) { #define CHECK_INTERVAL 100 /* 100ms */ #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ uint16_t portid; uint8_t count, all_ports_up, print_flag = 0; struct rte_eth_link link; int ret; printf("\nChecking link status\n"); fflush(stdout); for (count = 0; count <= MAX_CHECK_TIME; count++) { all_ports_up = 1; RTE_ETH_FOREACH_DEV(portid) { if ((port_mask & (1 << portid)) == 0) continue; memset(&link, 0, sizeof(link)); ret = rte_eth_link_get_nowait(portid, &link); if (ret < 0) { all_ports_up = 0; if (print_flag == 1) printf("Port %u link get failed: %s\n", portid, rte_strerror(-ret)); continue; } /* print link status if flag set */ if (print_flag == 1) { if (link.link_status) printf( "Port%d Link Up - speed %uMbps - %s\n", portid, link.link_speed, (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? ("full-duplex") : ("half-duplex")); else printf("Port %d Link Down\n", portid); continue; } /* clear all_ports_up flag if any link down */ if (link.link_status == ETH_LINK_DOWN) { all_ports_up = 0; break; } } /* after finally printing all link status, get out */ if (print_flag == 1) break; if (all_ports_up == 0) { printf("."); fflush(stdout); rte_delay_ms(CHECK_INTERVAL); } /* set the print_flag if all ports up or timeout */ if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { print_flag = 1; printf("done\n"); } } } static void log_link_state(struct rte_kni *kni, int prev, struct rte_eth_link *link) { if (kni == NULL || link == NULL) return; if (prev == ETH_LINK_DOWN && link->link_status == ETH_LINK_UP) { RTE_LOG(INFO, APP, "%s NIC Link is Up %d Mbps %s %s.\n", rte_kni_get_name(kni), link->link_speed, link->link_autoneg ? "(AutoNeg)" : "(Fixed)", link->link_duplex ? "Full Duplex" : "Half Duplex"); } else if (prev == ETH_LINK_UP && link->link_status == ETH_LINK_DOWN) { RTE_LOG(INFO, APP, "%s NIC Link is Down.\n", rte_kni_get_name(kni)); } } /* * Monitor the link status of all ports and update the * corresponding KNI interface(s) */ static void * monitor_all_ports_link_status(void *arg) { uint16_t portid; struct rte_eth_link link; unsigned int i; struct kni_port_params **p = kni_port_params_array; int prev; (void) arg; int ret; while (monitor_links) { rte_delay_ms(500); RTE_ETH_FOREACH_DEV(portid) { if ((ports_mask & (1 << portid)) == 0) continue; memset(&link, 0, sizeof(link)); ret = rte_eth_link_get_nowait(portid, &link); if (ret < 0) { RTE_LOG(ERR, APP, "Get link failed (port %u): %s\n", portid, rte_strerror(-ret)); continue; } for (i = 0; i < p[portid]->nb_kni; i++) { prev = rte_kni_update_link(p[portid]->kni[i], link.link_status); log_link_state(p[portid]->kni[i], prev, &link); } } } return NULL; } static int kni_change_mtu_(uint16_t port_id, unsigned int new_mtu) { int ret; uint16_t nb_rxd = NB_RXD; uint16_t nb_txd = NB_TXD; struct rte_eth_conf conf; struct rte_eth_dev_info dev_info; struct rte_eth_rxconf rxq_conf; struct rte_eth_txconf txq_conf; if (!rte_eth_dev_is_valid_port(port_id)) { RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id); return -EINVAL; } RTE_LOG(INFO, APP, "Change MTU of port %d to %u\n", port_id, new_mtu); /* Stop specific port */ rte_eth_dev_stop(port_id); memcpy(&conf, &port_conf, sizeof(conf)); /* Set new MTU */ if (new_mtu > RTE_ETHER_MAX_LEN) conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME; else conf.rxmode.offloads &= ~DEV_RX_OFFLOAD_JUMBO_FRAME; /* mtu + length of header + length of FCS = max pkt length */ conf.rxmode.max_rx_pkt_len = new_mtu + KNI_ENET_HEADER_SIZE + KNI_ENET_FCS_SIZE; ret = rte_eth_dev_configure(port_id, 1, 1, &conf); if (ret < 0) { RTE_LOG(ERR, APP, "Fail to reconfigure port %d\n", port_id); return ret; } ret = rte_eth_dev_adjust_nb_rx_tx_desc(port_id, &nb_rxd, &nb_txd); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not adjust number of descriptors " "for port%u (%d)\n", (unsigned int)port_id, ret); ret = rte_eth_dev_info_get(port_id, &dev_info); if (ret != 0) { RTE_LOG(ERR, APP, "Error during getting device (port %u) info: %s\n", port_id, strerror(-ret)); return ret; } rxq_conf = dev_info.default_rxconf; rxq_conf.offloads = conf.rxmode.offloads; ret = rte_eth_rx_queue_setup(port_id, 0, nb_rxd, rte_eth_dev_socket_id(port_id), &rxq_conf, pktmbuf_pool); if (ret < 0) { RTE_LOG(ERR, APP, "Fail to setup Rx queue of port %d\n", port_id); return ret; } txq_conf = dev_info.default_txconf; txq_conf.offloads = conf.txmode.offloads; ret = rte_eth_tx_queue_setup(port_id, 0, nb_txd, rte_eth_dev_socket_id(port_id), &txq_conf); if (ret < 0) { RTE_LOG(ERR, APP, "Fail to setup Tx queue of port %d\n", port_id); return ret; } /* Restart specific port */ ret = rte_eth_dev_start(port_id); if (ret < 0) { RTE_LOG(ERR, APP, "Fail to restart port %d\n", port_id); return ret; } return 0; } /* Callback for request of changing MTU */ static int kni_change_mtu(uint16_t port_id, unsigned int new_mtu) { int ret; rte_atomic32_inc(&kni_pause); ret = kni_change_mtu_(port_id, new_mtu); rte_atomic32_dec(&kni_pause); return ret; } /* Callback for request of configuring network interface up/down */ static int kni_config_network_interface(uint16_t port_id, uint8_t if_up) { int ret = 0; if (!rte_eth_dev_is_valid_port(port_id)) { RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id); return -EINVAL; } RTE_LOG(INFO, APP, "Configure network interface of %d %s\n", port_id, if_up ? "up" : "down"); rte_atomic32_inc(&kni_pause); if (if_up != 0) { /* Configure network interface up */ rte_eth_dev_stop(port_id); ret = rte_eth_dev_start(port_id); } else /* Configure network interface down */ rte_eth_dev_stop(port_id); rte_atomic32_dec(&kni_pause); if (ret < 0) RTE_LOG(ERR, APP, "Failed to start port %d\n", port_id); return ret; } static void print_ethaddr(const char *name, struct rte_ether_addr *mac_addr) { char buf[RTE_ETHER_ADDR_FMT_SIZE]; rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, mac_addr); RTE_LOG(INFO, APP, "\t%s%s\n", name, buf); } /* Callback for request of configuring mac address */ static int kni_config_mac_address(uint16_t port_id, uint8_t mac_addr[]) { int ret = 0; if (!rte_eth_dev_is_valid_port(port_id)) { RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id); return -EINVAL; } RTE_LOG(INFO, APP, "Configure mac address of %d\n", port_id); print_ethaddr("Address:", (struct rte_ether_addr *)mac_addr); ret = rte_eth_dev_default_mac_addr_set(port_id, (struct rte_ether_addr *)mac_addr); if (ret < 0) RTE_LOG(ERR, APP, "Failed to config mac_addr for port %d\n", port_id); return ret; } static int kni_alloc(uint16_t port_id) { uint8_t i; struct rte_kni *kni; struct rte_kni_conf conf; struct kni_port_params **params = kni_port_params_array; int ret; if (port_id >= RTE_MAX_ETHPORTS || !params[port_id]) return -1; params[port_id]->nb_kni = params[port_id]->nb_lcore_k ? params[port_id]->nb_lcore_k : 1; for (i = 0; i < params[port_id]->nb_kni; i++) { /* Clear conf at first */ memset(&conf, 0, sizeof(conf)); if (params[port_id]->nb_lcore_k) { snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u_%u", port_id, i); conf.core_id = params[port_id]->lcore_k[i]; conf.force_bind = 1; } else snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", port_id); conf.group_id = port_id; conf.mbuf_size = MAX_PACKET_SZ; /* * The first KNI device associated to a port * is the master, for multiple kernel thread * environment. */ if (i == 0) { struct rte_kni_ops ops; struct rte_eth_dev_info dev_info; ret = rte_eth_dev_info_get(port_id, &dev_info); if (ret != 0) rte_exit(EXIT_FAILURE, "Error during getting device (port %u) info: %s\n", port_id, strerror(-ret)); /* Get the interface default mac address */ ret = rte_eth_macaddr_get(port_id, (struct rte_ether_addr *)&conf.mac_addr); if (ret != 0) rte_exit(EXIT_FAILURE, "Failed to get MAC address (port %u): %s\n", port_id, rte_strerror(-ret)); rte_eth_dev_get_mtu(port_id, &conf.mtu); conf.min_mtu = dev_info.min_mtu; conf.max_mtu = dev_info.max_mtu; memset(&ops, 0, sizeof(ops)); ops.port_id = port_id; ops.change_mtu = kni_change_mtu; ops.config_network_if = kni_config_network_interface; ops.config_mac_address = kni_config_mac_address; kni = rte_kni_alloc(pktmbuf_pool, &conf, &ops); } else kni = rte_kni_alloc(pktmbuf_pool, &conf, NULL); if (!kni) rte_exit(EXIT_FAILURE, "Fail to create kni for " "port: %d\n", port_id); params[port_id]->kni[i] = kni; } return 0; } static int kni_free_kni(uint16_t port_id) { uint8_t i; struct kni_port_params **p = kni_port_params_array; if (port_id >= RTE_MAX_ETHPORTS || !p[port_id]) return -1; for (i = 0; i < p[port_id]->nb_kni; i++) { if (rte_kni_release(p[port_id]->kni[i])) printf("Fail to release kni\n"); p[port_id]->kni[i] = NULL; } rte_eth_dev_stop(port_id); return 0; } /* Initialise ports/queues etc. and start main loop on each core */ int main(int argc, char** argv) { int ret; uint16_t nb_sys_ports, port; unsigned i; void *retval; pthread_t kni_link_tid; int pid; /* Associate signal_hanlder function with USR signals */ signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGRTMIN, signal_handler); signal(SIGINT, signal_handler); /* Initialise EAL */ ret = rte_eal_init(argc, argv); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not initialise EAL (%d)\n", ret); argc -= ret; argv += ret; /* Parse application arguments (after the EAL ones) */ ret = parse_args(argc, argv); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not parse input parameters\n"); /* Create the mbuf pool */ pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF, MEMPOOL_CACHE_SZ, 0, MBUF_DATA_SZ, rte_socket_id()); if (pktmbuf_pool == NULL) { rte_exit(EXIT_FAILURE, "Could not initialise mbuf pool\n"); return -1; } /* Get number of ports found in scan */ nb_sys_ports = rte_eth_dev_count_avail(); if (nb_sys_ports == 0) rte_exit(EXIT_FAILURE, "No supported Ethernet device found\n"); /* Check if the configured port ID is valid */ for (i = 0; i < RTE_MAX_ETHPORTS; i++) if (kni_port_params_array[i] && !rte_eth_dev_is_valid_port(i)) rte_exit(EXIT_FAILURE, "Configured invalid " "port ID %u\n", i); /* Initialize KNI subsystem */ init_kni(); /* Initialise each port */ RTE_ETH_FOREACH_DEV(port) { /* Skip ports that are not enabled */ if (!(ports_mask & (1 << port))) continue; init_port(port); if (port >= RTE_MAX_ETHPORTS) rte_exit(EXIT_FAILURE, "Can not use more than " "%d ports for kni\n", RTE_MAX_ETHPORTS); kni_alloc(port); } check_all_ports_link_status(ports_mask); pid = getpid(); RTE_LOG(INFO, APP, "========================\n"); RTE_LOG(INFO, APP, "KNI Running\n"); RTE_LOG(INFO, APP, "kill -SIGUSR1 %d\n", pid); RTE_LOG(INFO, APP, " Show KNI Statistics.\n"); RTE_LOG(INFO, APP, "kill -SIGUSR2 %d\n", pid); RTE_LOG(INFO, APP, " Zero KNI Statistics.\n"); RTE_LOG(INFO, APP, "========================\n"); fflush(stdout); ret = rte_ctrl_thread_create(&kni_link_tid, "KNI link status check", NULL, monitor_all_ports_link_status, NULL); if (ret < 0) rte_exit(EXIT_FAILURE, "Could not create link status thread!\n"); /* Launch per-lcore function on every lcore */ rte_eal_mp_remote_launch(main_loop, NULL, CALL_MASTER); printf("core %d, \n", i); fflush(stdout); RTE_LCORE_FOREACH_SLAVE(i) { printf("core %d, \n", i); fflush(stdout); if (rte_eal_wait_lcore(i) < 0) return -1; } monitor_links = 0; pthread_join(kni_link_tid, &retval); /* Release resources */ RTE_ETH_FOREACH_DEV(port) { if (!(ports_mask & (1 << port))) continue; kni_free_kni(port); } for (i = 0; i < RTE_MAX_ETHPORTS; i++) if (kni_port_params_array[i]) { rte_free(kni_port_params_array[i]); kni_port_params_array[i] = NULL; } /* clean up the EAL */ rte_eal_cleanup(); return 0; } 请解释我这样执行的参数是什么意思./build/kni -l 0-1 -n 1 -- -P -p 0x1 -m --config="(0,0,1)"
最新发布
11-13
你执行的命令是: ```bash ./build/kni -l 0-1 -n 1 -- -P -p 0x1 -m --config="(0,0,1)" ``` 这个命令用于运行一个基于 **DPDK(Data Plane Development Kit)** 的 KNI(Kernel NIC Interface)示例程序。下面我将逐部分详细解释每个参数的含义,并结合你的代码说明其作用。 --- ### ✅ 整体结构 该命令分为两大部分: 1. **EAL 参数(Environment Abstraction Layer)**:在 `--` 之前的部分,由 DPDK EAL 解析。 2. **应用程序自定义参数**:在 `--` 之后的部分,由你的应用逻辑解析。 ```bash ./build/kni [EAL args] -- [app args] ``` --- ## 🔹一、EAL 参数(`-l 0-1 -n 1`) 这些是 DPDK 启动时必须指定的基础环境参数。 | 参数 | 含义 | |------|------| | `-l 0-1` | 指定使用逻辑核心(lcore)0 和 1 来运行程序。相当于绑定了两个 CPU 核心。 | | `-n 1` | 指定每个 socket 使用 1 个内存通道(memory channel)。通常设置为实际物理内存通道数以优化性能。 | > 💡 这意味着 DPDK 初始化时会使用 CPU core 0 和 core 1,并分配相应的内存池等资源。 --- ## 🔹二、应用层参数(`-- -P -p 0x1 -m --config="(0,0,1)"`) 这部分是你写的程序通过 `parse_args()` 函数解析的参数。 ### 1. `-P`:启用混杂模式(Promiscuous Mode) ```c case 'P': promiscuous_on = 1; break; ``` - 开启网卡的 **混杂模式**,即接收所有经过该端口的数据包(不管 MAC 地址是否匹配)。 - 适用于抓包、桥接或监控场景。 ✅ 效果:你的程序可以接收到目标不是本设备的以太网帧。 --- ### 2. `-p 0x1`:端口掩码(Ports Mask) ```c case 'p': ports_mask = parse_unsigned(optarg); ``` - `0x1` 表示十六进制值 `1`,即二进制 `000...0001` - 它表示只启用 **Port ID 0**(第0个网口) - 如果有多个网口,比如想用 port 0 和 port 1,则写成 `-p 0x3`(即 `1 | 2`) ✅ 效果:程序只会初始化并处理编号为 0 的以太网端口。 --- ### 3. `-m`:开启链路状态监控线程(monitor links) ```c case 'm': monitor_links = 1; break; ``` - 设置标志位 `monitor_links = 1` - 在 `main()` 中会启动一个独立的 pthread 线程 `monitor_all_ports_link_status()`,每 500ms 检查一次物理端口的链路状态(up/down),并通过 KNI 接口同步到内核空间虚拟接口。 ✅ 效果:当物理网卡断开/连接时,对应的 `vEthX` 虚拟网卡也会自动更新链路状态(例如 `ifconfig vEth0 up/down` 自动发生)。 --- ### 4. `--config="(0,0,1)"`:配置端口与 lcore 映射关系 这是最关键的部分! #### 解析过程(见 `parse_config()` 函数): ```c --config=(port,lcore_rx,lcore_tx,lcore_kthread...) ``` 括号内的三个数字 `(0,0,1)` 分别代表: | 字段 | 值 | 含义 | |------|----|------| | `port` | `0` | 使用的是 DPDK 中的 Port ID 0 | | `lcore_rx` | `0` | 绑定到 lcore 0 处理从 NIC 到 KNI 的数据流(Ingress) | | `lcore_tx` | `1` | 绑定到 lcore 1 处理从 KNI 到 NIC 的数据流(Egress) | 此外,由于没有更多参数,`nb_lcore_k = 0` → 所以不会创建多核 KNI 内核线程,仅创建一个默认 KNI 设备。 #### 对应结构体赋值结果(`kni_port_params_array[0]`): ```c kni_port_params_array[0]->port_id = 0; kni_port_params_array[0]->lcore_rx = 0; kni_port_params_array[0]->lcore_tx = 1; kni_port_params_array[0]->nb_lcore_k = 0; // 没有额外 kernel thread 配置 kni_port_params_array[0]->nb_kni = 1; // 默认创建 1 个 KNI 接口 ``` #### KNI 设备命名规则(来自 `kni_alloc()`): 因为 `nb_lcore_k == 0`,所以使用单线程模式: ```c snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", port_id); ``` ➡️ 创建的虚拟网卡名为:`vEth0` --- ## 🧩 总结:整个命令的行为 | 功能 | 具体行为 | |------|---------| | **CPU 分配** | 使用 lcore 0 和 lcore 1 | | **内存通道** | 使用 1 个内存通道 | | **处理端口** | 只使用 Port 0 | | **工作模式** | 混杂模式开启(收所有包) | | **链路监控** | 开启链路状态轮询线程 | | **任务分工** |<ul><li>Lcore 0: 负责从 NIC 收包 ➝ 发给 KNI (`kni_ingress`)</li><li>Lcore 1: 负责从 KNI 收包 ➝ 发给 NIC (`kni_egress`)</li></ul>| | **KNI 设备** | 创建一个名为 `vEth0` 的虚拟网卡,绑定到 Port 0 | | **交互方式** | 用户态 ↔ 内核态可通过 `vEth0` 接口通信 | --- ## 📦 数据流向示意 ``` Physical NIC (Port 0) ⇅ rte_eth_rx_burst / rte_eth_tx_burst DPDK App (lcore 0 & 1) ⇅ rte_kni_rx_burst / rte_kni_tx_burst Virtual Interface: vEth0 (in Linux kernel) ``` - 包从 NIC 进来 → lcore 0 调用 `kni_ingress()` → 把 mbuf 发送到 `vEth0` → 内核协议栈可处理 - 包从内核发往 `vEth0` → lcore 1 调用 `kni_egress()` → 把包转发出去到真实网卡 --- ## ⚠️ 注意事项 1. **需要 root 权限运行**:操作网卡和创建 `/dev/kni` 需要权限。 2. **加载 igb_uio 或 vfio 驱动**:确保网卡已经被 DPDK 驱动接管(不能被 Linux 内核原生驱动占用)。 3. **插入 kni 模块**: ```bash modprobe uio insmod build/kmod/rte_kni.ko ``` 4. **查看虚拟网卡**: ```bash ip link show vEth0 ``` --- ## ✅ 如何测试? 你可以试试以下操作验证功能: ```bash # 查看生成的虚拟网卡 ip link show vEth0 # 启动它(假设你想让它参与路由或 ping) ip link set vEth0 up ip addr add 192.168.100.1/24 dev vEth0 # 然后尝试发送数据包(会经过 DPDK 应用转发到物理网卡) ping 192.168.100.2 ``` 如果 DPDK 正确转发,那么远端应该能收到 ICMP 请求。 --- ##
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值