From: Intel Date: Mon, 3 Jun 2013 00:00:00 +0000 (+0000) Subject: kni: fix multi-process support X-Git-Tag: spdx-start~11208 X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=f2e7592c474cf096266b02c71b292a696d19099e;p=dpdk.git kni: fix multi-process support Signed-off-by: Intel --- diff --git a/app/test/Makefile b/app/test/Makefile index 4fecdb6258..44a025549f 100755 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -88,6 +88,7 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_lib.c SRCS-$(CONFIG_RTE_APP_TEST) += test_red.c SRCS-$(CONFIG_RTE_APP_TEST) += test_sched.c SRCS-$(CONFIG_RTE_APP_TEST) += test_meter.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_kni.c SRCS-$(CONFIG_RTE_APP_TEST) += test_pmac_pm.c SRCS-$(CONFIG_RTE_APP_TEST) += test_pmac_acl.c SRCS-$(CONFIG_RTE_APP_TEST) += test_power.c diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py index f2f9965d31..a6847ae46a 100755 --- a/app/test/autotest_data.py +++ b/app/test/autotest_data.py @@ -336,6 +336,19 @@ parallel_test_group_list = [ # tests that should not be run when any other tests are running non_parallel_test_group_list = [ +{ + "Prefix" : "kni", + "Memory" : "512", + "Tests" : + [ + { + "Name" : "KNI autotest", + "Command" : "kni_autotest", + "Func" : default_autotest, + "Report" : None, + }, + ] +}, { "Prefix": "mempool_perf", "Memory" : all_sockets(256), diff --git a/app/test/commands.c b/app/test/commands.c index c7ac1e423a..a9cb545456 100755 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -173,6 +173,8 @@ static void cmd_autotest_parsed(void *parsed_result, ret |= test_sched(); if (all || !strcmp(res->autotest, "meter_autotest")) ret |= test_meter(); + if (all || !strcmp(res->autotest, "kni_autotest")) + ret |= test_kni(); if (all || !strcmp(res->autotest, "pm_autotest")) ret |= test_pmac_pm(); if (all || !strcmp(res->autotest, "acl_autotest")) @@ -208,8 +210,8 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "cmdline_autotest#func_reentrancy_autotest#" "mempool_perf_autotest#hash_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" - "memcpy_perf_autotest#pm_autotest#" - "acl_autotest#power_autotest#" + "memcpy_perf_autotest#kni_autotest#" + "pm_autotest#acl_autotest#power_autotest#" "all_autotests"); cmdline_parse_inst_t cmd_autotest = { diff --git a/app/test/test.h b/app/test/test.h index 6bac20943f..a2a45ad80e 100755 --- a/app/test/test.h +++ b/app/test/test.h @@ -89,6 +89,7 @@ int test_sched(void); int test_meter(void); int test_pmac_pm(void); int test_pmac_acl(void); +int test_kni(void); int test_power(void); int test_pci_run; diff --git a/app/test/test_kni.c b/app/test/test_kni.c new file mode 100644 index 0000000000..b02150f819 --- /dev/null +++ b/app/test/test_kni.c @@ -0,0 +1,537 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include + +#include "test.h" + +#ifdef RTE_LIBRTE_KNI +#include +#include +#include +#include + +#define NB_MBUF (8192 * 16) +#define MAX_PACKET_SZ 2048 +#define MBUF_SZ \ + (MAX_PACKET_SZ + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define PKT_BURST_SZ 32 +#define MEMPOOL_CACHE_SZ PKT_BURST_SZ +#define SOCKET 0 +#define NB_RXD 128 +#define NB_TXD 512 +#define KNI_TIMEOUT_MS 5000 /* ms */ + +#define IFCONFIG "/sbin/ifconfig" + +/* The threshold number of mbufs to be transmitted or received. */ +#define KNI_NUM_MBUF_THRESHOLD 100 +static int kni_pkt_mtu = 0; + +struct test_kni_stats { + volatile uint64_t ingress; + volatile uint64_t egress; +}; + +static const struct rte_eth_rxconf rx_conf = { + .rx_thresh = { + .pthresh = 8, + .hthresh = 8, + .wthresh = 4, + }, + .rx_free_thresh = 0, +}; + +static const struct rte_eth_txconf tx_conf = { + .tx_thresh = { + .pthresh = 36, + .hthresh = 0, + .wthresh = 0, + }, + .tx_free_thresh = 0, + .tx_rs_thresh = 0, +}; + +static const struct rte_eth_conf port_conf = { + .rxmode = { + .header_split = 0, + .hw_ip_checksum = 0, + .hw_vlan_filter = 0, + .jumbo_frame = 0, + .hw_strip_crc = 0, + }, + .txmode = { + .mq_mode = ETH_DCB_NONE, + }, +}; + +static struct rte_kni_ops kni_ops = { + .change_mtu = NULL, + .config_network_if = NULL, +}; + +static unsigned lcore_master, lcore_ingress, lcore_egress; +static struct rte_kni *test_kni_ctx; +static struct test_kni_stats stats; + +static volatile uint32_t test_kni_processing_flag; + +static struct rte_mempool * +test_kni_create_mempool(void) +{ + struct rte_mempool * mp; + + mp = rte_mempool_lookup("kni_mempool"); + if (!mp) + mp = rte_mempool_create("kni_mempool", + NB_MBUF, + MBUF_SZ, + MEMPOOL_CACHE_SZ, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, + NULL, + rte_pktmbuf_init, + NULL, + SOCKET, + 0); + + return mp; +} + +static struct rte_mempool * +test_kni_lookup_mempool(void) +{ + return rte_mempool_lookup("kni_mempool"); +} +/* Callback for request of changing MTU */ +static int +kni_change_mtu(uint8_t port_id, unsigned new_mtu) +{ + printf("Change MTU of port %d to %u\n", port_id, new_mtu); + kni_pkt_mtu = new_mtu; + printf("Change MTU of port %d to %i successfully.\n", + port_id, kni_pkt_mtu); + return 0; +} +/** + * This loop fully tests the basic functions of KNI. e.g. transmitting, + * receiving to, from kernel space, and kernel requests. + * + * This is the loop to transmit/receive mbufs to/from kernel interface with + * supported by KNI kernel module. The ingress lcore will allocate mbufs and + * transmit them to kernel space; while the egress lcore will receive the mbufs + * from kernel space and free them. + * On the master lcore, several commands will be run to check handling the + * kernel requests. And it will finally set the flag to exit the KNI + * transmitting/receiving to/from the kernel space. + * + * Note: To support this testing, the KNI kernel module needs to be insmodded + * in one of its loopback modes. + */ +static int +test_kni_loop(__rte_unused void *arg) +{ + int ret = 0; + unsigned nb_rx, nb_tx, num, i; + const unsigned lcore_id = rte_lcore_id(); + struct rte_mbuf *pkts_burst[PKT_BURST_SZ]; + + if (lcore_id == lcore_master) { + rte_delay_ms(KNI_TIMEOUT_MS); + /* tests of handling kernel request */ + if (system(IFCONFIG " vEth0 up") == -1) + ret = -1; + if (system(IFCONFIG " vEth0 mtu 1400") == -1) + ret = -1; + if (system(IFCONFIG " vEth0 down") == -1) + ret = -1; + rte_delay_ms(KNI_TIMEOUT_MS); + test_kni_processing_flag = 1; + } else if (lcore_id == lcore_ingress) { + struct rte_mempool *mp = test_kni_lookup_mempool(); + + if (mp == NULL) + return -1; + + while (1) { + if (test_kni_processing_flag) + break; + + for (nb_rx = 0; nb_rx < PKT_BURST_SZ; nb_rx++) { + pkts_burst[nb_rx] = rte_pktmbuf_alloc(mp); + if (!pkts_burst[nb_rx]) + break; + } + + num = rte_kni_tx_burst(test_kni_ctx, pkts_burst, + nb_rx); + stats.ingress += num; + rte_kni_handle_request(test_kni_ctx); + if (num < nb_rx) { + for (i = num; i < nb_rx; i++) { + rte_pktmbuf_free(pkts_burst[i]); + } + } + } + } else if (lcore_id == lcore_egress) { + while (1) { + if (test_kni_processing_flag) + break; + num = rte_kni_rx_burst(test_kni_ctx, pkts_burst, + PKT_BURST_SZ); + stats.egress += num; + for (nb_tx = 0; nb_tx < num; nb_tx++) + rte_pktmbuf_free(pkts_burst[nb_tx]); + } + } + + return ret; +} + +static int +test_kni_allocate_lcores(void) +{ + unsigned i, count = 0; + + lcore_master = rte_get_master_lcore(); + printf("master lcore: %u\n", lcore_master); + for (i = 0; i < RTE_MAX_LCORE; i++) { + if (count >=2 ) + break; + if (rte_lcore_is_enabled(i) && i != lcore_master) { + count ++; + if (count == 1) + lcore_ingress = i; + else if (count == 2) + lcore_egress = i; + } + } + printf("count: %u\n", count); + + return (count == 2 ? 0 : -1); +} + +static int +test_kni_processing(uint8_t pid, struct rte_mempool *mp) +{ + int ret = 0; + unsigned i; + struct rte_kni *kni; + int p_id,p_ret; + int status; + + if (!mp) + return -1; + + /* basic test of kni processing */ + kni = rte_kni_create(pid, MAX_PACKET_SZ, mp, &kni_ops); + if (!kni) { + printf("fail to create kni\n"); + return -1; + } + if (rte_kni_get_port_id(kni) != pid) { + printf("fail to get port id\n"); + ret = -1; + goto fail_kni; + } + + test_kni_ctx = kni; + test_kni_processing_flag = 0; + stats.ingress = 0; + stats.egress = 0; + + /* create a subprocess to test the APIs of supporting multi-process */ + p_id = fork(); + if (p_id == 0) { + struct rte_kni *kni_test; + #define TEST_MTU_SIZE 1450 + kni_test = rte_kni_info_get(RTE_MAX_ETHPORTS); + if (kni_test) { + printf("unexpectedly gets kni successfully with an invalid " + "port id\n"); + exit(-1); + } + kni_test = rte_kni_info_get(pid); + if (NULL == kni_test) { + printf("Failed to get KNI info of the port %d\n",pid); + exit(-1); + } + struct rte_kni_ops kni_ops_test = { + .change_mtu = kni_change_mtu, + .config_network_if = NULL, + }; + /* test of registering kni with NULL ops */ + if (rte_kni_register_handlers(kni_test,NULL) == 0) { + printf("unexpectedly register kni successfully" + "with NULL ops\n"); + exit(-1); + } + if (rte_kni_register_handlers(kni_test,&kni_ops_test) < 0) { + printf("Failed to register KNI request handler" + "of the port %d\n",pid); + exit(-1); + } + if (system(IFCONFIG " vEth0 mtu 1450") == -1) + exit(-1); + + rte_kni_handle_request(kni_test); + if (kni_pkt_mtu != TEST_MTU_SIZE) { + printf("Failed to change kni MTU\n"); + exit(-1) ; + } + + /* test of unregistering kni request */ + kni_pkt_mtu = 0; + if (rte_kni_unregister_handlers(kni_test) < 0) { + printf("Failed to unregister kni request handlers\n"); + exit(-1); + } + if (system(IFCONFIG " vEth0 mtu 1450") == -1) + exit(-1); + + rte_kni_handle_request(kni_test); + if (kni_pkt_mtu != 0) { + printf("Failed to test kni unregister handlers\n"); + exit(-1); + } + exit(0); + }else if (p_id < 0) { + printf("Failed to fork a process\n"); + return -1; + }else { + p_ret = wait(&status); + if (WIFEXITED(status)) + printf("test of multi-process api passed.\n"); + else { + printf("KNI test:The child process %d exit abnormally./n",p_ret); + return -1; + } + } + rte_eal_mp_remote_launch(test_kni_loop, NULL, CALL_MASTER); + RTE_LCORE_FOREACH_SLAVE(i) { + if (rte_eal_wait_lcore(i) < 0) { + ret = -1; + goto fail_kni; + } + } + /** + * Check if the number of mbufs received from kernel space is equal + * to that of transmitted to kernel space + */ + if (stats.ingress < KNI_NUM_MBUF_THRESHOLD || + stats.egress < KNI_NUM_MBUF_THRESHOLD) { + printf("The ingress/egress number should not be " + "less than %u\n", (unsigned)KNI_NUM_MBUF_THRESHOLD); + ret = -1; + goto fail_kni; + } + + /* test of creating kni on a port which has been used for a kni */ + if (rte_kni_create(pid, MAX_PACKET_SZ, mp, &kni_ops) != NULL) { + printf("should not create a kni successfully for a port which" + "has been used for a kni\n"); + ret = -1; + goto fail_kni; + } + + if (rte_kni_release(kni) < 0) { + printf("fail to release kni\n"); + return -1; + } + test_kni_ctx = NULL; + + /* test of releasing a released kni device */ + if (rte_kni_release(kni) == 0) { + printf("should not release a released kni device\n"); + return -1; + } + + /* test of reusing memzone */ + kni = rte_kni_create(pid, MAX_PACKET_SZ, mp, &kni_ops); + if (!kni) { + printf("fail to create kni\n"); + return -1; + } + + /* Release the kni for following testing */ + if (rte_kni_release(kni) < 0) { + printf("fail to release kni\n"); + return -1; + } + + return ret; +fail_kni: + if (rte_kni_release(kni) < 0) { + printf("fail to release kni\n"); + ret = -1; + } + + return ret; +} + +int +test_kni(void) +{ + int ret = -1; + uint8_t nb_ports, pid; + struct rte_kni *kni; + struct rte_mempool * mp; + + if (test_kni_allocate_lcores() < 0) { + printf("No enough lcores for kni processing\n"); + return -1; + } + + mp = test_kni_create_mempool(); + if (!mp) { + printf("fail to create mempool for kni\n"); + return -1; + } + ret = rte_pmd_init_all(); + if (ret < 0) { + printf("fail to initialize PMD\n"); + return -1; + } + ret = rte_eal_pci_probe(); + if (ret < 0) { + printf("fail to probe PCI devices\n"); + return -1; + } + + nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) { + printf("no supported nic port found\n"); + return -1; + } + + /* configuring port 0 for the test is enough */ + pid = 0; + ret = rte_eth_dev_configure(pid, 1, 1, &port_conf); + if (ret < 0) { + printf("fail to configure port %d\n", pid); + return -1; + } + + ret = rte_eth_rx_queue_setup(pid, 0, NB_RXD, SOCKET, &rx_conf, mp); + if (ret < 0) { + printf("fail to setup rx queue for port %d\n", pid); + return -1; + } + + ret = rte_eth_tx_queue_setup(pid, 0, NB_TXD, SOCKET, &tx_conf); + if (ret < 0) { + printf("fail to setup tx queue for port %d\n", pid); + return -1; + } + + ret = rte_eth_dev_start(pid); + if (ret < 0) { + printf("fail to start port %d\n", pid); + return -1; + } + + rte_eth_promiscuous_enable(pid); + + /* basic test of kni processing */ + ret = test_kni_processing(pid, mp); + if (ret < 0) + goto fail; + + /* test of creating kni with port id exceeds the maximum */ + kni = rte_kni_create(RTE_MAX_ETHPORTS, MAX_PACKET_SZ, mp, &kni_ops); + if (kni) { + printf("unexpectedly creates kni successfully with an invalid " + "port id\n"); + goto fail; + } + + /* test of creating kni with NULL mempool pointer */ + kni = rte_kni_create(pid, MAX_PACKET_SZ, NULL, &kni_ops); + if (kni) { + printf("unexpectedly creates kni successfully with NULL " + "mempool pointer\n"); + goto fail; + } + + /* test of creating kni with NULL ops */ + kni = rte_kni_create(pid, MAX_PACKET_SZ, mp, NULL); + if (!kni) { + printf("unexpectedly creates kni falied with NULL ops\n"); + goto fail; + } + + /* test of releasing kni with NULL ops */ + if (rte_kni_release(kni) < 0) { + printf("fail to release kni\n"); + goto fail; + } + + /* test of getting port id according to NULL kni context */ + if (rte_kni_get_port_id(NULL) < RTE_MAX_ETHPORTS) { + printf("unexpectedly get port id successfully by NULL kni " + "pointer\n"); + goto fail; + } + + /* test of releasing NULL kni context */ + ret = rte_kni_release(NULL); + if (ret == 0) { + printf("unexpectedly release kni successfully\n"); + goto fail; + } + + ret = 0; + +fail: + rte_eth_dev_stop(pid); + + return ret; +} + +#else /* RTE_LIBRTE_KNI */ + +int +test_kni(void) +{ + printf("The KNI library is not included in this build\n"); + return 0; +} + +#endif /* RTE_LIBRTE_KNI */ diff --git a/examples/kni/main.c b/examples/kni/main.c index cda3d73328..f7f99ed4a5 100644 --- a/examples/kni/main.c +++ b/examples/kni/main.c @@ -107,6 +107,9 @@ /* Total octets in the FCS */ #define KNI_ENET_FCS_SIZE 4 +#define KNI_US_PER_SECOND 1000000 +#define KNI_SECOND_PER_DAY 86400 + /* * RX and TX Prefetch, Host, and Write-back threshold values should be * carefully set for optimal performance. Consult the network @@ -209,6 +212,8 @@ static struct rte_kni_ops kni_ops = { .config_network_if = kni_config_network_interface, }; +static rte_atomic32_t kni_stop = RTE_ATOMIC32_INIT(0); + /* Print out statistics on packets handled */ static void print_stats(void) @@ -235,7 +240,7 @@ print_stats(void) printf("====== ============== ============ ============ ============ ============\n"); } -/* Custom handling of signals to handle stats */ +/* Custom handling of signals to handle stats and kni processing */ static void signal_handler(int signum) { @@ -250,6 +255,14 @@ signal_handler(int signum) printf("\n**Statistics have been reset**\n"); return; } + + /* When we receive a RTMIN signal, stop kni processing */ + if (signum == SIGRTMIN) { + printf("SIGRTMIN is received, and the KNI processing is " + "going to stop\n"); + rte_atomic32_inc(&kni_stop); + return; + } } static void @@ -290,6 +303,7 @@ kni_ingress(struct rte_kni *kni) num = rte_kni_tx_burst(kni, pkts_burst, nb_rx); kni_stats[port_id].rx_packets += num; + rte_kni_handle_request(kni); if (unlikely(num < nb_rx)) { /* Free mbufs not tx to kni interface */ kni_burst_free_mbufs(&pkts_burst[num], nb_rx - num); @@ -329,18 +343,14 @@ kni_egress(struct rte_kni *kni) } /* Main processing loop */ -static __attribute__((noreturn)) int +static int main_loop(__rte_unused void *arg) { uint8_t pid; const unsigned lcore_id = rte_lcore_id(); struct rte_kni *kni = kni_lcore_to_kni(lcore_id); - if (kni == NULL) { - RTE_LOG(INFO, APP, "Lcore %u has nothing to do\n", lcore_id); - for (;;) - ; /* loop doing nothing */ - } else { + if (kni != NULL) { pid = rte_kni_get_port_id(kni); if (pid >= RTE_MAX_ETHPORTS) rte_exit(EXIT_FAILURE, "Failure: port id >= %d\n", @@ -353,8 +363,13 @@ main_loop(__rte_unused void *arg) fflush(stdout); /* rx loop */ - while (1) + while (1) { + int32_t flag = rte_atomic32_read(&kni_stop); + + if (flag) + break; kni_ingress(kni); + } } else if (kni_port_info[pid].lcore_id_egress == lcore_id) { /* Running on lcores for output packets */ RTE_LOG(INFO, APP, "Lcore %u is writing to port %d\n", @@ -362,15 +377,20 @@ main_loop(__rte_unused void *arg) fflush(stdout); /* tx loop */ - while (1) + while (1) { + int32_t flag = rte_atomic32_read(&kni_stop); + + if (flag) + break; kni_egress(kni); - } else { - RTE_LOG(INFO, APP, "Lcore %u has nothing to do\n", - lcore_id); - for (;;) - ; /* loop doing nothing */ + } } } + + /* fallthrough to here if we don't have any work */ + RTE_LOG(INFO, APP, "Lcore %u has nothing to do\n", lcore_id); + + return 0; } /* Display usage instructions */ @@ -379,10 +399,11 @@ print_usage(const char *prgname) { RTE_LOG(INFO, APP, "\nUsage: %s [EAL options] -- -p PORTMASK " "-i IN_CORES -o OUT_CORES\n" - " -p PORTMASK: hex bitmask of ports to use\n" - " -i IN_CORES: hex bitmask of cores which read " + " -p PORTMASK: hex bitmask of ports to use\n" + " -i IN_CORES: hex bitmask of cores which read " "from NIC\n" - " -o OUT_CORES: hex bitmask of cores which write to NIC\n", + " -o OUT_CORES: hex bitmask of cores which write " + "to NIC\n", prgname); } @@ -436,7 +457,7 @@ kni_setup_port_affinities(uint8_t nb_port) } if (in_lcore != 0) { - /* It is be for packet receiving */ + /* It is for packet receiving */ while ((rx_port < nb_port) && ((ports_mask & (1 << rx_port)) == 0)) rx_port++; @@ -702,6 +723,7 @@ main(int argc, char** argv) /* Associate signal_hanlder function with USR signals */ signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); + signal(SIGRTMIN, signal_handler); /* Initialise EAL */ ret = rte_eal_init(argc, argv); @@ -781,6 +803,13 @@ main(int argc, char** argv) return -1; } + for (port = 0; port < nb_sys_ports; port++) { + struct rte_kni *kni = kni_port_info[port].kni; + + if (kni != NULL) + rte_kni_release(kni); + } + return 0; } diff --git a/lib/librte_eal/common/include/rte_log.h b/lib/librte_eal/common/include/rte_log.h index bf4a3e2c76..f5403a9d4d 100644 --- a/lib/librte_eal/common/include/rte_log.h +++ b/lib/librte_eal/common/include/rte_log.h @@ -70,7 +70,7 @@ extern struct rte_logs rte_logs; #define RTE_LOGTYPE_PMD 0x00000020 /**< Log related to poll mode driver. */ #define RTE_LOGTYPE_HASH 0x00000040 /**< Log related to hash table. */ #define RTE_LOGTYPE_LPM 0x00000080 /**< Log related to LPM. */ -#define RTE_LOGTYPE_KNI 0X00000100 /**< Log related to KNI. */ +#define RTE_LOGTYPE_KNI 0x00000100 /**< Log related to KNI. */ #define RTE_LOGTYPE_PMAC 0x00000200 /**< Log related to PMAC. */ #define RTE_LOGTYPE_POWER 0x00000400 /**< Log related to power. */ #define RTE_LOGTYPE_METER 0x00000800 /**< Log related to QoS meter. */ diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kni_common.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kni_common.h index 9bcdfb28b9..40db84a5b6 100755 --- a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kni_common.h +++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kni_common.h @@ -144,6 +144,7 @@ struct rte_kni_device_info uint8_t bus; /**< Device bus */ uint8_t devid; /**< Device ID */ uint8_t function; /**< Device function. */ + uint8_t port_id; /**< Port ID */ /* mbuf size */ unsigned mbuf_size; @@ -151,7 +152,8 @@ struct rte_kni_device_info #define KNI_DEVICE "kni" -#define RTE_KNI_IOCTL_TEST _IOWR(0, 1, int) -#define RTE_KNI_IOCTL_CREATE _IOWR(0, 2, struct rte_kni_device_info) +#define RTE_KNI_IOCTL_TEST _IOWR(0, 1, int) +#define RTE_KNI_IOCTL_CREATE _IOWR(0, 2, struct rte_kni_device_info) +#define RTE_KNI_IOCTL_RELEASE _IOWR(0, 3, uint8_t) #endif /* _RTE_KNI_COMMON_H_ */ diff --git a/lib/librte_eal/linuxapp/kni/kni_dev.h b/lib/librte_eal/linuxapp/kni/kni_dev.h index 449a36a228..ac3949dd10 100755 --- a/lib/librte_eal/linuxapp/kni/kni_dev.h +++ b/lib/librte_eal/linuxapp/kni/kni_dev.h @@ -31,6 +31,7 @@ #include #include #include +#include #define KNI_KTHREAD_RESCHEDULE_INTERVAL 10 /* us */ @@ -38,9 +39,12 @@ * A structure describing the private information for a kni device. */ struct kni_dev { + /* kni list */ + struct list_head list; + struct net_device_stats stats; int status; - int idx; + int port_id; /* wait queue for req/resp */ wait_queue_head_t wq; diff --git a/lib/librte_eal/linuxapp/kni/kni_misc.c b/lib/librte_eal/linuxapp/kni/kni_misc.c index 98f3066cca..dca1ba56a1 100755 --- a/lib/librte_eal/linuxapp/kni/kni_misc.c +++ b/lib/librte_eal/linuxapp/kni/kni_misc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "kni_dev.h" @@ -79,14 +80,17 @@ static struct miscdevice kni_misc = { /* loopback mode */ static char *lo_mode = NULL; -static struct kni_dev *kni_devs[KNI_MAX_DEVICES]; -static volatile int num_devs; /* number of kni devices */ - #define KNI_DEV_IN_USE_BIT_NUM 0 /* Bit number for device in use */ static volatile unsigned long device_in_use; /* device in use flag */ static struct task_struct *kni_kthread; +/* kni list lock */ +static DECLARE_RWSEM(kni_list_lock); + +/* kni list */ +static struct list_head kni_list_head = LIST_HEAD_INIT(kni_list_head); + static int __init kni_init(void) { @@ -122,9 +126,6 @@ kni_open(struct inode *inode, struct file *file) if (test_and_set_bit(KNI_DEV_IN_USE_BIT_NUM, &device_in_use)) return -EBUSY; - memset(kni_devs, 0, sizeof(kni_devs)); - num_devs = 0; - /* Create kernel thread for RX */ kni_kthread = kthread_run(kni_thread, NULL, "kni_thread"); if (IS_ERR(kni_kthread)) { @@ -140,7 +141,7 @@ kni_open(struct inode *inode, struct file *file) static int kni_release(struct inode *inode, struct file *file) { - int i; + struct kni_dev *dev, *n; KNI_PRINT("Stopping KNI thread..."); @@ -148,28 +149,26 @@ kni_release(struct inode *inode, struct file *file) kthread_stop(kni_kthread); kni_kthread = NULL; - for (i = 0; i < KNI_MAX_DEVICES; i++) { - if (kni_devs[i] != NULL) { - /* Call the remove part to restore pci dev */ - switch (kni_devs[i]->device_id) { - #define RTE_PCI_DEV_ID_DECL_IGB(vend, dev) case (dev): - #include - igb_kni_remove(kni_devs[i]->pci_dev); - break; - #define RTE_PCI_DEV_ID_DECL_IXGBE(vend, dev) case (dev): - #include - ixgbe_kni_remove(kni_devs[i]->pci_dev); - break; - default: - break; - } - - unregister_netdev(kni_devs[i]->net_dev); - free_netdev(kni_devs[i]->net_dev); - kni_devs[i] = NULL; + down_write(&kni_list_lock); + list_for_each_entry_safe(dev, n, &kni_list_head, list) { + /* Call the remove part to restore pci dev */ + switch (dev->device_id) { + #define RTE_PCI_DEV_ID_DECL_IGB(vend, dev) case (dev): + #include + igb_kni_remove(dev->pci_dev); + break; + #define RTE_PCI_DEV_ID_DECL_IXGBE(vend, dev) case (dev): + #include + ixgbe_kni_remove(dev->pci_dev); + break; + default: + break; } + unregister_netdev(dev->net_dev); + free_netdev(dev->net_dev); + list_del(&dev->list); } - num_devs = 0; + up_write(&kni_list_lock); /* Clear the bit of device in use */ clear_bit(KNI_DEV_IN_USE_BIT_NUM, &device_in_use); @@ -182,22 +181,20 @@ kni_release(struct inode *inode, struct file *file) static int kni_thread(void *unused) { - int i, j; + int j; + struct kni_dev *dev, *n; KNI_PRINT("Kernel thread for KNI started\n"); while (!kthread_should_stop()) { - int n_devs = num_devs; + down_read(&kni_list_lock); for (j = 0; j < KNI_RX_LOOP_NUM; j++) { - for (i = 0; i < n_devs; i++) { - /* This shouldn't be needed */ - if (kni_devs[i]) { - kni_net_rx(kni_devs[i]); - kni_net_poll_resp(kni_devs[i]); - } - else - KNI_ERR("kni_thread -no kni found!!!"); + list_for_each_entry_safe(dev, n, + &kni_list_head, list) { + kni_net_rx(dev); + kni_net_poll_resp(dev); } } + up_read(&kni_list_lock); /* reschedule out for a while */ schedule_timeout_interruptible(usecs_to_jiffies( \ KNI_KTHREAD_RESCHEDULE_INTERVAL)); @@ -216,23 +213,32 @@ kni_ioctl_create(unsigned int ioctl_num, unsigned long ioctl_param) struct pci_dev *found_pci = NULL; struct net_device *net_dev = NULL; struct net_device *lad_dev = NULL; - struct kni_dev *kni; - - if (num_devs == KNI_MAX_DEVICES) - return -EBUSY; + struct kni_dev *kni, *dev, *n; + printk(KERN_INFO "KNI: Creating kni...\n"); /* Check the buffer size, to avoid warning */ if (_IOC_SIZE(ioctl_num) > sizeof(dev_info)) return -EINVAL; /* Copy kni info from user space */ - ret = copy_from_user(&dev_info, (void *)ioctl_param, - _IOC_SIZE(ioctl_num)); + ret = copy_from_user(&dev_info, (void *)ioctl_param, sizeof(dev_info)); if (ret) { - KNI_ERR("copy_from_user"); + KNI_ERR("copy_from_user in kni_ioctl_create"); return -EIO; } + /* Check if it has been created */ + down_read(&kni_list_lock); + list_for_each_entry_safe(dev, n, &kni_list_head, list) { + if (dev->port_id == dev_info.port_id) { + up_read(&kni_list_lock); + KNI_ERR("Port %d has already been created\n", + dev_info.port_id); + return -EINVAL; + } + } + up_read(&kni_list_lock); + net_dev = alloc_netdev(sizeof(struct kni_dev), dev_info.name, kni_net_init); if (net_dev == NULL) { @@ -243,7 +249,7 @@ kni_ioctl_create(unsigned int ioctl_num, unsigned long ioctl_param) kni = netdev_priv(net_dev); kni->net_dev = net_dev; - kni->idx = num_devs; + kni->port_id = dev_info.port_id; /* Translate user space info into kernel space info */ kni->tx_q = phys_to_virt(dev_info.tx_phys); @@ -337,11 +343,61 @@ kni_ioctl_create(unsigned int ioctl_num, unsigned long ioctl_param) return -ENODEV; } - kni_devs[num_devs++] = kni; + down_write(&kni_list_lock); + list_add(&kni->list, &kni_list_head); + up_write(&kni_list_lock); + printk(KERN_INFO "KNI: Successfully create kni for port %d\n", + dev_info.port_id); return 0; } +static int +kni_ioctl_release(unsigned int ioctl_num, unsigned long ioctl_param) +{ + int ret = -EINVAL; + uint8_t port_id; + struct kni_dev *dev, *n; + + if (_IOC_SIZE(ioctl_num) > sizeof(port_id)) + return -EINVAL; + + ret = copy_from_user(&port_id, (void *)ioctl_param, sizeof(port_id)); + if (ret) { + KNI_ERR("copy_from_user in kni_ioctl_release"); + return -EIO; + } + + down_write(&kni_list_lock); + list_for_each_entry_safe(dev, n, &kni_list_head, list) { + if (dev->port_id != port_id) + continue; + + switch (dev->device_id) { + #define RTE_PCI_DEV_ID_DECL_IGB(vend, dev) case (dev): + #include + igb_kni_remove(dev->pci_dev); + break; + #define RTE_PCI_DEV_ID_DECL_IXGBE(vend, dev) case (dev): + #include + ixgbe_kni_remove(dev->pci_dev); + break; + default: + break; + } + unregister_netdev(dev->net_dev); + free_netdev(dev->net_dev); + list_del(&dev->list); + ret = 0; + break; + } + up_write(&kni_list_lock); + printk(KERN_INFO "KNI: %s release kni for port %d\n", + (ret == 0 ? "Successfully" : "Unsuccessfully"), port_id); + + return ret; +} + static int kni_ioctl(struct inode *inode, unsigned int ioctl_num, @@ -361,6 +417,9 @@ kni_ioctl(struct inode *inode, case _IOC_NR(RTE_KNI_IOCTL_CREATE): ret = kni_ioctl_create(ioctl_num, ioctl_param); break; + case _IOC_NR(RTE_KNI_IOCTL_RELEASE): + ret = kni_ioctl_release(ioctl_num, ioctl_param); + break; default: KNI_DBG("IOCTL default \n"); break; diff --git a/lib/librte_eal/linuxapp/kni/kni_net.c b/lib/librte_eal/linuxapp/kni/kni_net.c index b4d1b6c03f..d6f9af8858 100755 --- a/lib/librte_eal/linuxapp/kni/kni_net.c +++ b/lib/librte_eal/linuxapp/kni/kni_net.c @@ -132,7 +132,7 @@ kni_net_open(struct net_device *dev) struct rte_kni_request req; struct kni_dev *kni = netdev_priv(dev); - KNI_DBG("kni_net_open %d\n", kni->idx); + KNI_DBG("kni_net_open %d\n", kni->port_id); /* * Assign the hardware address of the board: use "\0KNIx", where @@ -144,7 +144,7 @@ kni_net_open(struct net_device *dev) memcpy(dev->dev_addr, kni->lad_dev->dev_addr, ETH_ALEN); else { memcpy(dev->dev_addr, "\0KNI0", ETH_ALEN); - dev->dev_addr[ETH_ALEN-1] += kni->idx; /* \0KNI1 */ + dev->dev_addr[ETH_ALEN-1] += kni->port_id; /* \0KNI1 */ } netif_start_queue(dev); @@ -247,7 +247,7 @@ kni_net_rx_normal(struct kni_dev *kni) skb->ip_summed = CHECKSUM_UNNECESSARY; /* Call netif interface */ - netif_rx(skb); + netif_receive_skb(skb); /* Update statistics */ kni->stats.rx_bytes += len; @@ -543,7 +543,7 @@ static int kni_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct kni_dev *kni = netdev_priv(dev); - KNI_DBG("kni_net_ioctl %d\n", kni->idx); + KNI_DBG("kni_net_ioctl %d\n", kni->port_id); return 0; } diff --git a/lib/librte_kni/rte_kni.c b/lib/librte_kni/rte_kni.c index 7b24677242..89e46d5e5a 100755 --- a/lib/librte_kni/rte_kni.c +++ b/lib/librte_kni/rte_kni.c @@ -61,6 +61,8 @@ #define KNI_REQUEST_MBUF_NUM_MAX 32 +#define KNI_MZ_CHECK(mz) do { if (mz) goto fail; } while (0) + /** * KNI context */ @@ -78,16 +80,33 @@ struct rte_kni { /* For request & response */ struct rte_kni_fifo *req_q; /**< Request queue */ struct rte_kni_fifo *resp_q; /**< Response queue */ - void * sync_addr; /**< Req/Resp Mem address */ + void * sync_addr; /**< Req/Resp Mem address */ struct rte_kni_ops ops; /**< operations for request */ + uint8_t port_in_use : 1; /**< kni creation flag */ +}; + +enum kni_ops_status { + KNI_REQ_NO_REGISTER = 0, + KNI_REQ_REGISTERED, }; static void kni_free_mbufs(struct rte_kni *kni); static void kni_allocate_mbufs(struct rte_kni *kni); -static int kni_fd = -1; +static volatile int kni_fd = -1; + +static const struct rte_memzone * +kni_memzone_reserve(const char *name, size_t len, int socket_id, + unsigned flags) +{ + const struct rte_memzone *mz = rte_memzone_lookup(name); + if (mz == NULL) + mz = rte_memzone_reserve(name, len, socket_id, flags); + + return mz; +} struct rte_kni * rte_kni_create(uint8_t port_id, @@ -95,15 +114,17 @@ rte_kni_create(uint8_t port_id, struct rte_mempool *pktmbuf_pool, struct rte_kni_ops *ops) { + int ret; struct rte_kni_device_info dev_info; struct rte_eth_dev_info eth_dev_info; struct rte_kni *ctx; char itf_name[IFNAMSIZ]; #define OBJNAMSIZ 32 char obj_name[OBJNAMSIZ]; + char mz_name[RTE_MEMZONE_NAMESIZE]; const struct rte_memzone *mz; - if (port_id >= RTE_MAX_ETHPORTS || pktmbuf_pool == NULL || !ops) + if (port_id >= RTE_MAX_ETHPORTS || pktmbuf_pool == NULL) return NULL; /* Check FD and open once */ @@ -128,11 +149,21 @@ rte_kni_create(uint8_t port_id, dev_info.function = eth_dev_info.pci_dev->addr.function; dev_info.vendor_id = eth_dev_info.pci_dev->id.vendor_id; dev_info.device_id = eth_dev_info.pci_dev->id.device_id; + dev_info.port_id = port_id; - ctx = rte_zmalloc("kni devs", sizeof(struct rte_kni), 0); - if (ctx == NULL) - rte_panic("Cannot allocate memory for kni dev\n"); - memcpy(&ctx->ops, ops, sizeof(struct rte_kni_ops)); + rte_snprintf(mz_name, RTE_MEMZONE_NAMESIZE, "KNI_INFO_%d", port_id); + mz = kni_memzone_reserve(mz_name, sizeof(struct rte_kni), + SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); + ctx = mz->addr; + + if (ctx->port_in_use != 0) { + RTE_LOG(ERR, KNI, "Port %d has been used\n", port_id); + goto fail; + } + memset(ctx, 0, sizeof(struct rte_kni)); + if (ops) + memcpy(&ctx->ops, ops, sizeof(struct rte_kni_ops)); rte_snprintf(itf_name, IFNAMSIZ, "vEth%u", port_id); rte_snprintf(ctx->name, IFNAMSIZ, itf_name); @@ -140,73 +171,64 @@ rte_kni_create(uint8_t port_id, /* TX RING */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_tx_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_tx_%d queue\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->tx_q = mz->addr; kni_fifo_init(ctx->tx_q, KNI_FIFO_COUNT_MAX); dev_info.tx_phys = mz->phys_addr; /* RX RING */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_rx_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_rx_%d queue\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->rx_q = mz->addr; kni_fifo_init(ctx->rx_q, KNI_FIFO_COUNT_MAX); dev_info.rx_phys = mz->phys_addr; /* ALLOC RING */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_alloc_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_alloc_%d queue\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->alloc_q = mz->addr; kni_fifo_init(ctx->alloc_q, KNI_FIFO_COUNT_MAX); dev_info.alloc_phys = mz->phys_addr; /* FREE RING */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_free_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_free_%d queue\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->free_q = mz->addr; kni_fifo_init(ctx->free_q, KNI_FIFO_COUNT_MAX); dev_info.free_phys = mz->phys_addr; /* Request RING */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_req_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_req_%d ring\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->req_q = mz->addr; kni_fifo_init(ctx->req_q, KNI_FIFO_COUNT_MAX); dev_info.req_phys = mz->phys_addr; /* Response RING */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_resp_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_resp_%d ring\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->resp_q = mz->addr; kni_fifo_init(ctx->resp_q, KNI_FIFO_COUNT_MAX); dev_info.resp_phys = mz->phys_addr; /* Req/Resp sync mem area */ rte_snprintf(obj_name, OBJNAMSIZ, "kni_sync_%d", port_id); - mz = rte_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); - if (mz == NULL || mz->addr == NULL) - rte_panic("Cannot create kni_sync_%d mem\n", port_id); + mz = kni_memzone_reserve(obj_name, KNI_FIFO_SIZE, SOCKET_ID_ANY, 0); + KNI_MZ_CHECK(mz == NULL); ctx->sync_addr = mz->addr; dev_info.sync_va = mz->addr; dev_info.sync_phys = mz->phys_addr; /* MBUF mempool */ - mz = rte_memzone_lookup("MP_mbuf_pool"); - if (mz == NULL) { - RTE_LOG(ERR, KNI, "Can not find MP_mbuf_pool\n"); - goto fail; - } + rte_snprintf(mz_name, sizeof(mz_name), "MP_%s", pktmbuf_pool->name); + mz = rte_memzone_lookup(mz_name); + KNI_MZ_CHECK(mz == NULL); dev_info.mbuf_va = mz->addr; dev_info.mbuf_phys = mz->phys_addr; ctx->pktmbuf_pool = pktmbuf_pool; @@ -216,28 +238,54 @@ rte_kni_create(uint8_t port_id, /* Configure the buffer size which will be checked in kernel module */ dev_info.mbuf_size = ctx->mbuf_size; - if (ioctl(kni_fd, RTE_KNI_IOCTL_CREATE, &dev_info) < 0) { - RTE_LOG(ERR, KNI, "Fail to create kni device\n"); - goto fail; - } + ret = ioctl(kni_fd, RTE_KNI_IOCTL_CREATE, &dev_info); + KNI_MZ_CHECK(ret < 0); + + ctx->port_in_use = 1; return ctx; fail: - if (ctx != NULL) - rte_free(ctx); return NULL; } -/** - * It is called in the same lcore of receiving packets, and polls the request - * mbufs sent from kernel space. Then analyzes it and calls the specific - * actions for the specific requests. Finally constructs the response mbuf and - * puts it back to the resp_q. - */ -static int -kni_request_handler(struct rte_kni *kni) +static void +kni_free_fifo(struct rte_kni_fifo *fifo) +{ + int ret; + struct rte_mbuf *pkt; + + do { + ret = kni_fifo_get(fifo, (void **)&pkt, 1); + if (ret) + rte_pktmbuf_free(pkt); + } while (ret); +} + +int +rte_kni_release(struct rte_kni *kni) +{ + if (!kni || kni->port_in_use == 0) + return -1; + + if (ioctl(kni_fd, RTE_KNI_IOCTL_RELEASE, &kni->port_id) < 0) { + RTE_LOG(ERR, KNI, "Fail to release kni device\n"); + return -1; + } + + /* mbufs in all fifo should be released, except request/response */ + kni_free_fifo(kni->tx_q); + kni_free_fifo(kni->rx_q); + kni_free_fifo(kni->alloc_q); + kni_free_fifo(kni->free_q); + memset(kni, 0, sizeof(struct rte_kni)); + + return 0; +} + +int +rte_kni_handle_request(struct rte_kni *kni) { unsigned ret; struct rte_kni_request *req; @@ -290,9 +338,6 @@ rte_kni_tx_burst(struct rte_kni *kni, struct rte_mbuf **mbufs, unsigned num) /* Get mbufs from free_q and then free them */ kni_free_mbufs(kni); - /* Handle the requests from kernel space */ - kni_request_handler(kni); - return ret; } @@ -365,3 +410,81 @@ rte_kni_get_port_id(struct rte_kni *kni) return kni->port_id; } +struct rte_kni * +rte_kni_info_get(uint8_t port_id) +{ + struct rte_kni *kni; + const struct rte_memzone *mz; + char mz_name[RTE_MEMZONE_NAMESIZE]; + + if(port_id >= RTE_MAX_ETHPORTS) + return NULL; + + rte_snprintf(mz_name, RTE_MEMZONE_NAMESIZE, "KNI_INFO_%d", port_id); + mz = rte_memzone_lookup(mz_name); + if (NULL == mz) + return NULL; + + kni = mz->addr; + if (0 == kni->port_in_use) + return NULL; + + return kni; +} + +static enum kni_ops_status +kni_check_request_register(struct rte_kni_ops *ops) +{ + /* check if KNI request ops has been registered*/ + if( NULL == ops ) + return KNI_REQ_NO_REGISTER; + + if((NULL == ops->change_mtu) && (NULL == ops->config_network_if)) + return KNI_REQ_NO_REGISTER; + + return KNI_REQ_REGISTERED; +} + +int +rte_kni_register_handlers(struct rte_kni *kni,struct rte_kni_ops *ops) +{ + enum kni_ops_status req_status; + + if (NULL == ops) { + RTE_LOG(ERR, KNI, "Invalid KNI request operation.\n"); + return -1; + } + + if (NULL == kni) { + RTE_LOG(ERR, KNI, "Invalid kni info.\n"); + return -1; + } + + req_status = kni_check_request_register(&kni->ops); + if ( KNI_REQ_REGISTERED == req_status) { + RTE_LOG(ERR, KNI, "The KNI request operation" + "has already registered.\n"); + return -1; + } + + memcpy(&kni->ops, ops, sizeof(struct rte_kni_ops)); + return 0; +} + +int +rte_kni_unregister_handlers(struct rte_kni *kni) +{ + if (NULL == kni) { + RTE_LOG(ERR, KNI, "Invalid kni info.\n"); + return -1; + } + + if (NULL == &kni->ops) { + RTE_LOG(ERR, KNI, "The invalid KNI unregister operation.\n"); + return -1; + } + + kni->ops.change_mtu = NULL; + kni->ops.config_network_if = NULL; + return 0; +} diff --git a/lib/librte_kni/rte_kni.h b/lib/librte_kni/rte_kni.h index 70923d8ab3..0f230bcdc0 100644 --- a/lib/librte_kni/rte_kni.h +++ b/lib/librte_kni/rte_kni.h @@ -86,6 +86,34 @@ struct rte_kni_ops { extern struct rte_kni *rte_kni_create(uint8_t port_id, unsigned mbuf_size, struct rte_mempool *pktmbuf_pool, struct rte_kni_ops *ops); +/** + * Release kni interface according to the context. It will also release the + * paired KNI interface in kernel space. All processing on the specific kni + * context need to be stopped before calling this interface. + * + * @param kni + * The pointer to the context of an existant kni interface. + * + * @return + * - 0 indicates success. + * - negative value indicates failure. + */ +extern int rte_kni_release(struct rte_kni *kni); + +/** + * It is used to handle the request mbufs sent from kernel space. + * Then analyzes it and calls the specific actions for the specific requests. + * Finally constructs the response mbuf and puts it back to the resp_q. + * + * @param kni + * The pointer to the context of an existant kni interface. + * + * @return + * - 0 + * - negative value indicates failure. + */ +extern int rte_kni_handle_request(struct rte_kni *kni); + /** * Retrieve a burst of packets from a kni interface. The retrieved packets are * stored in rte_mbuf structures whose pointers are supplied in the array of @@ -136,6 +164,46 @@ extern unsigned rte_kni_tx_burst(struct rte_kni *kni, */ extern uint8_t rte_kni_get_port_id(struct rte_kni *kni); +/** + * Get kni context information of the port. + * + * @port_id + * the port id. + * + * @return + * On success: Pointer to kni interface. + * On failure: NULL + */ +extern struct rte_kni * rte_kni_info_get(uint8_t port_id); + +/** + * Register kni request handling for a specified port,and it can + * be called by master process or slave process. + * + * @param kni + * pointer to struct rte_kni. + * @param ops + * ponter to struct rte_kni_ops. + * + * @return + * On success: 0 + * On failure: -1 + */ +extern int rte_kni_register_handlers(struct rte_kni *kni, + struct rte_kni_ops *ops); + +/** + * Unregister kni request handling for a specified port. + * + * @param kni + * pointer to struct rte_kni. + * + * @return + * On success: 0 + * On failure: -1 + */ +extern int rte_kni_unregister_handlers(struct rte_kni *kni); + #ifdef __cplusplus } #endif