This kernel module is based on KNI module, but this one is stripped version of it and only for control messages, no data transfer functionality provided.
This Linux kernel module helps userspace application create virtual interfaces and when a control command issued into that virtual interface, module pushes the command to the userspace and gets the response back for the caller application. The Linux tools like ethtool/ifconfig/ip can be used on virtual interfaces but not ones for related data, like tcpdump. In long term this patch intends to replace the KNI and KNI will be depreciated. Signed-off-by: Ferruh Yigit <ferruh.yigit at intel.com> --- v3: * Devices are not up by default * Add enable/disable promisc, allmulti support * Increase timeout to 500ms and print log when a command timedout v2: * Use rtnetlink to create interfaces * Fix ethtool get/set eeprom * Remove commented out code --- MAINTAINERS | 4 + config/common_linuxapp | 6 + lib/librte_eal/linuxapp/Makefile | 5 +- lib/librte_eal/linuxapp/eal/Makefile | 3 +- .../linuxapp/eal/include/exec-env/rte_kcp_common.h | 109 ++++++++ lib/librte_eal/linuxapp/kcp/Makefile | 57 ++++ lib/librte_eal/linuxapp/kcp/kcp_dev.h | 54 ++++ lib/librte_eal/linuxapp/kcp/kcp_ethtool.c | 300 +++++++++++++++++++++ lib/librte_eal/linuxapp/kcp/kcp_net.c | 225 ++++++++++++++++ lib/librte_eal/linuxapp/kcp/kcp_nl.c | 215 +++++++++++++++ 10 files changed, 976 insertions(+), 2 deletions(-) create mode 100644 lib/librte_eal/linuxapp/eal/include/exec-env/rte_kcp_common.h create mode 100644 lib/librte_eal/linuxapp/kcp/Makefile create mode 100644 lib/librte_eal/linuxapp/kcp/kcp_dev.h create mode 100644 lib/librte_eal/linuxapp/kcp/kcp_ethtool.c create mode 100644 lib/librte_eal/linuxapp/kcp/kcp_net.c create mode 100644 lib/librte_eal/linuxapp/kcp/kcp_nl.c diff --git a/MAINTAINERS b/MAINTAINERS index 628bc05..6a77728 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -257,6 +257,10 @@ F: app/test/test_kni.c F: examples/kni/ F: doc/guides/sample_app_ug/kernel_nic_interface.rst +Linux KCP +M: Ferruh Yigit <ferruh.yigit at intel.com> +F: lib/librte_eal/linuxapp/kcp/ + Linux AF_PACKET M: John W. Linville <linville at tuxdriver.com> F: drivers/net/af_packet/ diff --git a/config/common_linuxapp b/config/common_linuxapp index 960dde4..0284fae 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -502,6 +502,12 @@ CONFIG_RTE_KNI_VHOST_DEBUG_TX=n CONFIG_RTE_LIBRTE_ETHTOOL=y # +# Compile librte_ctrl_if +# +CONFIG_RTE_KCP_KMOD=y +CONFIG_RTE_KCP_KO_DEBUG=n + +# # Compile vhost library # fuse-devel is needed to run vhost-cuse. # fuse-devel enables user space char driver development diff --git a/lib/librte_eal/linuxapp/Makefile b/lib/librte_eal/linuxapp/Makefile index d9c5233..d1fa3a3 100644 --- a/lib/librte_eal/linuxapp/Makefile +++ b/lib/librte_eal/linuxapp/Makefile @@ -1,6 +1,6 @@ # BSD LICENSE # -# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# Copyright(c) 2010-2016 Intel Corporation. All rights reserved. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -38,6 +38,9 @@ DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal ifeq ($(CONFIG_RTE_KNI_KMOD),y) DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += kni endif +ifeq ($(CONFIG_RTE_KCP_KMOD),y) +DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += kcp +endif ifeq ($(CONFIG_RTE_LIBRTE_XEN_DOM0),y) DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += xen_dom0 endif diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile index 6e26250..f6a3a41 100644 --- a/lib/librte_eal/linuxapp/eal/Makefile +++ b/lib/librte_eal/linuxapp/eal/Makefile @@ -1,6 +1,6 @@ # BSD LICENSE # -# Copyright(c) 2010-2015 Intel Corporation. All rights reserved. +# Copyright(c) 2010-2016 Intel Corporation. All rights reserved. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -121,6 +121,7 @@ CFLAGS_eal_thread.o += -Wno-return-type endif INC := rte_interrupts.h rte_kni_common.h rte_dom0_common.h +INC += rte_kcp_common.h SYMLINK-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP)-include/exec-env := \ $(addprefix include/exec-env/,$(INC)) diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kcp_common.h b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kcp_common.h new file mode 100644 index 0000000..988713e --- /dev/null +++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_kcp_common.h @@ -0,0 +1,109 @@ +/*- + * This file is provided under a dual BSD/LGPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GNU LESSER GENERAL PUBLIC LICENSE + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; + * + * Contact Information: + * Intel Corporation + * + * + * BSD LICENSE + * + * Copyright(c) 2016 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. + * + */ + +#ifndef _RTE_KCP_COMMON_H_ +#define _RTE_KCP_COMMON_H_ + +#define KCP_DEVICE "kcp" + +#define KCP_NL_GRP 31 + +#define KCP_ETHTOOL_MSG_LEN 500 +struct kcp_ethtool_msg { + int cmd_id; + int port_id; + unsigned int flag; + char input_buffer[KCP_ETHTOOL_MSG_LEN]; + char output_buffer[KCP_ETHTOOL_MSG_LEN]; + int input_buffer_len; + int output_buffer_len; + int err; +}; + +enum kcp_ethtool_msg_flag { + KCP_MSG_FLAG_NONE, + KCP_MSG_FLAG_REQUEST, + KCP_MSG_FLAG_RESPONSE, +}; + +enum { + IFLA_KCP_UNSPEC, + IFLA_KCP_PORTID, + IFLA_KCP_PID, + __IFLA_KCP_MAX, +}; + +#define IFLA_KCP_MAX (__IFLA_KCP_MAX - 1) + +/* + * Request id. + */ +enum rte_kcp_req_id { + RTE_KCP_REQ_UNKNOWN = (1 << 16), + RTE_KCP_REQ_CHANGE_MTU, + RTE_KCP_REQ_CFG_NETWORK_IF, + RTE_KCP_REQ_GET_STATS, + RTE_KCP_REQ_GET_MAC, + RTE_KCP_REQ_SET_MAC, + RTE_KCP_REQ_START_PORT, + RTE_KCP_REQ_STOP_PORT, + RTE_KCP_REQ_SET_PROMISC, + RTE_KCP_REQ_SET_ALLMULTI, + RTE_KCP_REQ_MAX, +}; + +#endif /* _RTE_KCP_COMMON_H_ */ diff --git a/lib/librte_eal/linuxapp/kcp/Makefile b/lib/librte_eal/linuxapp/kcp/Makefile new file mode 100644 index 0000000..46d9dd8 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/Makefile @@ -0,0 +1,57 @@ +# BSD LICENSE +# +# Copyright(c) 2016 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 $(RTE_SDK)/mk/rte.vars.mk + +# +# module name and path +# +MODULE = rte_kcp + +# +# CFLAGS +# +MODULE_CFLAGS += -I$(SRCDIR) +MODULE_CFLAGS += -I$(RTE_OUTPUT)/include +MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h +MODULE_CFLAGS += -Wall -Werror + +# this lib needs main eal +DEPDIRS-y += lib/librte_eal/linuxapp/eal + +# +# all source are stored in SRCS-y +# +SRCS-y += kcp_net.c +SRCS-y += kcp_ethtool.c +SRCS-y += kcp_nl.c + +include $(RTE_SDK)/mk/rte.module.mk diff --git a/lib/librte_eal/linuxapp/kcp/kcp_dev.h b/lib/librte_eal/linuxapp/kcp/kcp_dev.h new file mode 100644 index 0000000..5eae51d --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_dev.h @@ -0,0 +1,54 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation + */ + +#ifndef _KCP_DEV_H_ +#define _KCP_DEV_H_ + +#include <linux/netdevice.h> +#include <exec-env/rte_kcp_common.h> + +struct kcp_dev { + int port_id; + unsigned int pid; + struct completion msg_received; + unsigned int nb_timedout_msg; +}; + +void kcp_nl_init(void); +void kcp_nl_release(void); +int kcp_nl_exec(int cmd, struct net_device *dev, void *in_data, int in_len, + void *out_data, int out_len); + +void kcp_set_ethtool_ops(struct net_device *netdev); + +#define KCP_ERR(args...) printk(KERN_ERR "KCP: " args) +#define KCP_INFO(args...) printk(KERN_INFO "KCP: " args) + +#ifdef RTE_KCP_KO_DEBUG +#define KCP_DBG(args...) printk(KERN_DEBUG "KCP: " args) +#else +#define KCP_DBG(args...) +#endif + +#endif /* _KCP_DEV_H_ */ diff --git a/lib/librte_eal/linuxapp/kcp/kcp_ethtool.c b/lib/librte_eal/linuxapp/kcp/kcp_ethtool.c new file mode 100644 index 0000000..830ad64 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_ethtool.c @@ -0,0 +1,300 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation + */ + +#include "kcp_dev.h" + +#define ETHTOOL_GEEPROM_LEN 99 +#define ETHTOOL_GREGS_LEN 98 +#define ETHTOOL_GSSET_COUNT 97 + +static int kcp_check_if_running(struct net_device *dev) +{ + return 0; +} + +static void kcp_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + int ret; + + ret = kcp_nl_exec(info->cmd, dev, NULL, 0, + info, sizeof(struct ethtool_drvinfo)); + if (ret < 0) + memset(info, 0, sizeof(struct ethtool_drvinfo)); +} + +static int kcp_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + return kcp_nl_exec(ecmd->cmd, dev, NULL, 0, + ecmd, sizeof(struct ethtool_cmd)); +} + +static int kcp_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + return kcp_nl_exec(ecmd->cmd, dev, ecmd, sizeof(struct ethtool_cmd), + NULL, 0); +} + +static void kcp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + int ret; + + ret = kcp_nl_exec(wol->cmd, dev, NULL, 0, + wol, sizeof(struct ethtool_wolinfo)); + if (ret < 0) + memset(wol, 0, sizeof(struct ethtool_wolinfo)); +} + +static int kcp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + return kcp_nl_exec(wol->cmd, dev, wol, sizeof(struct ethtool_wolinfo), + NULL, 0); +} + +static int kcp_nway_reset(struct net_device *dev) +{ + return kcp_nl_exec(ETHTOOL_NWAY_RST, dev, NULL, 0, NULL, 0); +} + +static unsigned int kcp_get_link(struct net_device *dev) +{ + unsigned int data; + int ret; + + ret = kcp_nl_exec(ETHTOOL_GLINK, dev, NULL, 0, &data, sizeof(int)); + if (ret < 0) + return 0; + + return data; +} + +static int kcp_get_eeprom_len(struct net_device *dev) +{ + int data; + int ret; + + ret = kcp_nl_exec(ETHTOOL_GEEPROM_LEN, dev, NULL, 0, + &data, sizeof(int)); + if (ret < 0) + return 0; + + return data; +} + +static int kcp_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct ethtool_eeprom eeprom_tmp; + int ret = 0; + int remaining; + int offset = 0; + + eeprom_tmp = *eeprom; + + remaining = eeprom_tmp.len; + while (remaining > 0 && ret == 0) { + eeprom_tmp.len = min(remaining, KCP_ETHTOOL_MSG_LEN); + + ret = kcp_nl_exec(eeprom_tmp.cmd, dev, + &eeprom_tmp, sizeof(struct ethtool_eeprom), + data + offset, eeprom_tmp.len); + eeprom_tmp.offset += eeprom_tmp.len; + offset += eeprom_tmp.len; + remaining -= eeprom_tmp.len; + } + + return ret; +} + +static int kcp_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct ethtool_eeprom *eeprom_tmp; + int ret = 0; + int remaining; + int offset = 0; + int payload; + + if (sizeof(struct ethtool_eeprom) > KCP_ETHTOOL_MSG_LEN) + return -1; + + eeprom_tmp = kmalloc(KCP_ETHTOOL_MSG_LEN, GFP_KERNEL); + payload = KCP_ETHTOOL_MSG_LEN - sizeof(struct ethtool_eeprom); + + *eeprom_tmp = *eeprom; + remaining = eeprom->len; + + while (remaining > 0 && ret == 0) { + eeprom_tmp->len = min(remaining, payload); + + memcpy(eeprom_tmp->data, data + offset, payload); + + ret = kcp_nl_exec(eeprom->cmd, dev, eeprom, + KCP_ETHTOOL_MSG_LEN, NULL, 0); + + eeprom_tmp->offset += eeprom_tmp->len; + offset += eeprom_tmp->len; + remaining -= eeprom_tmp->len; + } + + kfree(eeprom_tmp); + + return ret; +} + +static void kcp_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring) +{ + + kcp_nl_exec(ring->cmd, dev, NULL, 0, + ring, sizeof(struct ethtool_ringparam)); +} + +static int kcp_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring) +{ + return kcp_nl_exec(ring->cmd, dev, + ring, sizeof(struct ethtool_ringparam), + NULL, 0); +} + +static void kcp_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + + kcp_nl_exec(pause->cmd, dev, NULL, 0, + pause, sizeof(struct ethtool_pauseparam)); +} + +static int kcp_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + return kcp_nl_exec(pause->cmd, dev, + pause, sizeof(struct ethtool_pauseparam), + NULL, 0); +} + +static u32 kcp_get_msglevel(struct net_device *dev) +{ + int data; + int ret; + + ret = kcp_nl_exec(ETHTOOL_GMSGLVL, dev, NULL, 0, &data, sizeof(int)); + if (ret < 0) + return 0; + + return data; +} + +static void kcp_set_msglevel(struct net_device *dev, u32 data) +{ + + kcp_nl_exec(ETHTOOL_SMSGLVL, dev, &data, sizeof(int), NULL, 0); +} + +static int kcp_get_regs_len(struct net_device *dev) +{ + int data; + int ret; + + ret = kcp_nl_exec(ETHTOOL_GREGS_LEN, dev, NULL, 0, &data, sizeof(int)); + if (ret < 0) + return 0; + + return data; +} + +static void kcp_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct ethtool_regs regs_tmp; + int len = regs->len; + + regs_tmp = *regs; + + if (len > KCP_ETHTOOL_MSG_LEN) { + len = KCP_ETHTOOL_MSG_LEN; + regs_tmp.len = len; + } + + kcp_nl_exec(regs->cmd, dev, ®s_tmp, sizeof(struct ethtool_regs), + p, len); +} + +static void kcp_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + + kcp_nl_exec(ETHTOOL_GSTRINGS, dev, &stringset, sizeof(u32), data, 0); +} + +static int kcp_get_sset_count(struct net_device *dev, int sset) +{ + int data; + int ret; + + ret = kcp_nl_exec(ETHTOOL_GSSET_COUNT, dev, &sset, sizeof(int), + &data, sizeof(int)); + if (ret < 0) + return 0; + + return data; +} + +static void kcp_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + + kcp_nl_exec(stats->cmd, dev, stats, sizeof(struct ethtool_stats), + data, stats->n_stats); +} + +static const struct ethtool_ops kcp_ethtool_ops = { + .begin = kcp_check_if_running, + .get_drvinfo = kcp_get_drvinfo, + .get_settings = kcp_get_settings, + .set_settings = kcp_set_settings, + .get_regs_len = kcp_get_regs_len, + .get_regs = kcp_get_regs, + .get_wol = kcp_get_wol, + .set_wol = kcp_set_wol, + .nway_reset = kcp_nway_reset, + .get_link = kcp_get_link, + .get_eeprom_len = kcp_get_eeprom_len, + .get_eeprom = kcp_get_eeprom, + .set_eeprom = kcp_set_eeprom, + .get_ringparam = kcp_get_ringparam, + .set_ringparam = kcp_set_ringparam, + .get_pauseparam = kcp_get_pauseparam, + .set_pauseparam = kcp_set_pauseparam, + .get_msglevel = kcp_get_msglevel, + .set_msglevel = kcp_set_msglevel, + .get_strings = kcp_get_strings, + .get_sset_count = kcp_get_sset_count, + .get_ethtool_stats = kcp_get_ethtool_stats, +}; + +void kcp_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &kcp_ethtool_ops; +} diff --git a/lib/librte_eal/linuxapp/kcp/kcp_net.c b/lib/librte_eal/linuxapp/kcp/kcp_net.c new file mode 100644 index 0000000..16ea153 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_net.c @@ -0,0 +1,225 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/etherdevice.h> +#include <net/rtnetlink.h> + +#include "kcp_dev.h" + +static int kcp_net_init(struct net_device *dev) +{ + char mac[ETH_ALEN] = {0}; + + kcp_nl_exec(RTE_KCP_REQ_GET_MAC, dev, NULL, 0, mac, ETH_ALEN); + memcpy(dev->dev_addr, mac, dev->addr_len); + + return 0; +} + +static int kcp_net_open(struct net_device *dev) +{ + /* DPDK port already started, stop it first */ + kcp_nl_exec(RTE_KCP_REQ_STOP_PORT, dev, NULL, 0, NULL, 0); + kcp_nl_exec(RTE_KCP_REQ_START_PORT, dev, NULL, 0, NULL, 0); + netif_start_queue(dev); + return 0; +} + +static int kcp_net_close(struct net_device *dev) +{ + kcp_nl_exec(RTE_KCP_REQ_STOP_PORT, dev, NULL, 0, NULL, 0); + netif_stop_queue(dev); + return 0; +} + +static int kcp_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void kcp_net_change_rx_flags(struct net_device *dev, int flags) +{ + int on = 1; + int off = 0; + + if (flags & IFF_PROMISC) + kcp_nl_exec(RTE_KCP_REQ_SET_PROMISC, dev, + dev->flags & IFF_PROMISC ? &on : &off, + sizeof(int), NULL, 0); + + if (flags & IFF_ALLMULTI) + kcp_nl_exec(RTE_KCP_REQ_SET_ALLMULTI, dev, + dev->flags & IFF_ALLMULTI ? &on : &off, + sizeof(int), NULL, 0); +} + +static int kcp_net_set_mac(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + int err = 0; + + if (!is_valid_ether_addr((unsigned char *)(addr->sa_data))) + return -EADDRNOTAVAIL; + + err = kcp_nl_exec(RTE_KCP_REQ_SET_MAC, dev, addr->sa_data, + dev->addr_len, NULL, 0); + if (err < 0) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + return 0; +} + +static int kcp_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + return -EOPNOTSUPP; +} + +/* + * Configuration changes (passed on by ifconfig) + */ +static int kcp_net_config(struct net_device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) + return -EBUSY; + + return -EOPNOTSUPP; +} + +static int kcp_net_change_mtu(struct net_device *dev, int new_mtu) +{ + int err = 0; + + err = kcp_nl_exec(RTE_KCP_REQ_CHANGE_MTU, dev, &new_mtu, sizeof(int), + NULL, 0); + + if (err == 0) + dev->mtu = new_mtu; + + return err; +} + +static struct rtnl_link_stats64 *kcp_net_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + int err; + + err = kcp_nl_exec(RTE_KCP_REQ_GET_STATS, dev, NULL, 0, + stats, sizeof(struct rtnl_link_stats64)); + + return stats; +} + +#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE) +static int kcp_net_change_carrier(struct net_device *dev, bool new_carrier) +{ + if (new_carrier) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} +#endif + +static const struct net_device_ops kcp_net_netdev_ops = { + .ndo_init = kcp_net_init, + .ndo_open = kcp_net_open, + .ndo_stop = kcp_net_close, + .ndo_start_xmit = kcp_net_xmit, + .ndo_change_rx_flags = kcp_net_change_rx_flags, + .ndo_set_mac_address = kcp_net_set_mac, + .ndo_do_ioctl = kcp_net_ioctl, + .ndo_set_config = kcp_net_config, + .ndo_change_mtu = kcp_net_change_mtu, + .ndo_get_stats64 = kcp_net_stats64, +#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE) + .ndo_change_carrier = kcp_net_change_carrier, +#endif +}; + +static void kcp_net_setup(struct net_device *dev) +{ + struct kcp_dev *kcp; + + ether_setup(dev); + dev->netdev_ops = &kcp_net_netdev_ops; + + kcp = netdev_priv(dev); + init_completion(&kcp->msg_received); + + kcp_set_ethtool_ops(dev); +} + +static int kcp_net_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + int ret; + struct kcp_dev *kcp; + + kcp = netdev_priv(dev); + + if (data && data[IFLA_KCP_PORTID]) + kcp->port_id = nla_get_u8(data[IFLA_KCP_PORTID]); + else + kcp->port_id = 0; + + if (data && data[IFLA_KCP_PID]) + kcp->pid = nla_get_u32(data[IFLA_KCP_PID]); + else + kcp->pid = 0; + + ret = register_netdevice(dev); + + return ret; +} + +static struct rtnl_link_ops kcp_link_ops __read_mostly = { + .kind = KCP_DEVICE, + .priv_size = sizeof(struct kcp_dev), + .setup = kcp_net_setup, + .maxtype = IFLA_KCP_MAX, + .newlink = kcp_net_newlink, +}; + +static int __init kcp_init(void) +{ + kcp_nl_init(); + return rtnl_link_register(&kcp_link_ops); +} +module_init(kcp_init); + +static void __exit kcp_exit(void) +{ + rtnl_link_unregister(&kcp_link_ops); + kcp_nl_release(); +} +module_exit(kcp_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Kernel Module for managing kcp devices"); diff --git a/lib/librte_eal/linuxapp/kcp/kcp_nl.c b/lib/librte_eal/linuxapp/kcp/kcp_nl.c new file mode 100644 index 0000000..a4b56aa --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_nl.c @@ -0,0 +1,215 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation + */ + +#include <net/sock.h> + +#include "kcp_dev.h" + +#define KCP_CMD_TIMEOUT 500 /* ms */ + +static struct ethtool_input_buffer { + int magic; + void *buffer; + int length; + struct completion *msg_received; + int *err; + int in_use; +} ethtool_input_buffer; + +static struct sock *nl_sock; +static struct mutex sync_lock; + +static int kcp_input_buffer_register(int magic, void *buffer, int length, + struct completion *msg_received, int *err) +{ + if (ethtool_input_buffer.in_use == 0) { + ethtool_input_buffer.magic = magic; + ethtool_input_buffer.buffer = buffer; + ethtool_input_buffer.length = length; + ethtool_input_buffer.msg_received = msg_received; + ethtool_input_buffer.err = err; + ethtool_input_buffer.in_use = 1; + return 0; + } + + return 1; +} + +static void kcp_input_buffer_unregister(int magic) +{ + if (ethtool_input_buffer.in_use == 1) { + if (magic == ethtool_input_buffer.magic) { + ethtool_input_buffer.magic = -1; + ethtool_input_buffer.buffer = NULL; + ethtool_input_buffer.length = 0; + ethtool_input_buffer.msg_received = NULL; + ethtool_input_buffer.err = NULL; + ethtool_input_buffer.in_use = 0; + } else { + KCP_ERR("Unregister magic mismatch\n"); + } + } +} + +static void nl_recv_user_request(struct kcp_ethtool_msg *ethtool_msg) +{ + KCP_DBG("Request from userspace received\n"); +} + +static void nl_recv_user_response(struct kcp_ethtool_msg *ethtool_msg) +{ + struct completion *msg_received; + int recv_len; + int expected_len; + + if (ethtool_input_buffer.in_use == 1) { + if (ethtool_input_buffer.buffer != NULL) { + recv_len = ethtool_msg->output_buffer_len; + expected_len = ethtool_input_buffer.length; + + memcpy(ethtool_input_buffer.buffer, + ethtool_msg->output_buffer, + ethtool_input_buffer.length); + + if (ethtool_msg->err == 0 && recv_len != expected_len) + KCP_INFO("Expected and received len not match " + "%d - %d\n", recv_len, expected_len); + } + + *ethtool_input_buffer.err = ethtool_msg->err; + msg_received = ethtool_input_buffer.msg_received; + kcp_input_buffer_unregister(ethtool_input_buffer.magic); + complete(msg_received); + } +} + +static void nl_recv(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + struct kcp_ethtool_msg ethtool_msg; + + nlh = (struct nlmsghdr *)skb->data; + + memcpy(ðtool_msg, NLMSG_DATA(nlh), sizeof(struct kcp_ethtool_msg)); + KCP_DBG("CMD: %d\n", ethtool_msg.cmd_id); + + if (ethtool_msg.flag & KCP_MSG_FLAG_REQUEST) { + nl_recv_user_request(ðtool_msg); + return; + } + + nl_recv_user_response(ðtool_msg); +} + +static int kcp_nl_send(int cmd_id, int port_id, unsigned int pid, + void *in_data, int in_data_len) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct kcp_ethtool_msg ethtool_msg; + + if (pid == 0) + return -1; + + memset(ðtool_msg, 0, sizeof(struct kcp_ethtool_msg)); + ethtool_msg.cmd_id = cmd_id; + ethtool_msg.port_id = port_id; + + if (in_data) { + if (in_data_len == 0 || in_data_len > KCP_ETHTOOL_MSG_LEN) + return -EINVAL; + ethtool_msg.input_buffer_len = in_data_len; + memcpy(ethtool_msg.input_buffer, in_data, in_data_len); + } + + skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct kcp_ethtool_msg)), + GFP_ATOMIC); + nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, sizeof(struct kcp_ethtool_msg), + 0); + + NETLINK_CB(skb).dst_group = 0; + + memcpy(nlmsg_data(nlh), ðtool_msg, sizeof(struct kcp_ethtool_msg)); + + nlmsg_unicast(nl_sock, skb, pid); + KCP_DBG("Sent cmd:%d port:%d pid:%u\n", cmd_id, port_id, pid); + + return 0; +} + +int kcp_nl_exec(int cmd, struct net_device *dev, void *in_data, + int in_data_len, void *out_data, int out_data_len) +{ + struct kcp_dev *priv = netdev_priv(dev); + int err = -EINVAL; + int ret; + + if (out_data_len > KCP_ETHTOOL_MSG_LEN) { + KCP_ERR("Message is too big to receive:%u\n", out_data_len); + return err; + } + + mutex_lock(&sync_lock); + ret = kcp_input_buffer_register(cmd, out_data, out_data_len, + &priv->msg_received, &err); + if (ret) { + mutex_unlock(&sync_lock); + return -EINVAL; + } + + ret = kcp_nl_send(cmd, priv->port_id, priv->pid, in_data, in_data_len); + if (ret) { + kcp_input_buffer_unregister(ethtool_input_buffer.magic); + mutex_unlock(&sync_lock); + return ret; + } + + ret = wait_for_completion_interruptible_timeout(&priv->msg_received, + msecs_to_jiffies(KCP_CMD_TIMEOUT)); + if (ret == 0 || err < 0) { + kcp_input_buffer_unregister(ethtool_input_buffer.magic); + mutex_unlock(&sync_lock); + priv->nb_timedout_msg++; + KCP_INFO("Command timed-out for port:%d cmd:%d (%u)\n", + priv->port_id, cmd, priv->nb_timedout_msg); + return ret == 0 ? -EINVAL : err; + } + mutex_unlock(&sync_lock); + + return 0; +} + +static struct netlink_kernel_cfg cfg = { + .input = nl_recv, +}; + +void kcp_nl_init(void) +{ + nl_sock = netlink_kernel_create(&init_net, KCP_NL_GRP, &cfg); + mutex_init(&sync_lock); +} + +void kcp_nl_release(void) +{ + netlink_kernel_release(nl_sock); +} -- 2.5.0