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. Signed-off-by: Ferruh Yigit <ferruh.yigit at intel.com> --- config/common_linuxapp | 2 + lib/librte_eal/linuxapp/Makefile | 5 +- lib/librte_eal/linuxapp/kcp/Makefile | 58 ++++++ lib/librte_eal/linuxapp/kcp/kcp_dev.h | 81 +++++++++ lib/librte_eal/linuxapp/kcp/kcp_ethtool.c | 261 +++++++++++++++++++++++++++ lib/librte_eal/linuxapp/kcp/kcp_misc.c | 282 ++++++++++++++++++++++++++++++ lib/librte_eal/linuxapp/kcp/kcp_net.c | 209 ++++++++++++++++++++++ lib/librte_eal/linuxapp/kcp/kcp_nl.c | 194 ++++++++++++++++++++ 8 files changed, 1091 insertions(+), 1 deletion(-) 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_misc.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/config/common_linuxapp b/config/common_linuxapp index de705d0..ed32ca8 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -506,6 +506,8 @@ CONFIG_RTE_KNI_VHOST_DEBUG_TX=n # Compile librte_ctrl_if # CONFIG_RTE_LIBRTE_CTRL_IF=y +CONFIG_RTE_KCP_KMOD=y +CONFIG_RTE_KCP_KO_DEBUG=n # # Compile vhost library 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/kcp/Makefile b/lib/librte_eal/linuxapp/kcp/Makefile new file mode 100644 index 0000000..e7472f3 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/Makefile @@ -0,0 +1,58 @@ +# BSD LICENSE +# +# Copyright(c) 2010-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_misc.c +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..1097cb4 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_dev.h @@ -0,0 +1,81 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010-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; if not, write to the Free Software + * Foundation. + * 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> + +#define RTE_KCP_NAMESIZE 32 +#define KCP_DEVICE "kcp" + +#define RTE_KCP_IOCTL_TEST _IOWR(0, 1, int) +#define RTE_KCP_IOCTL_CREATE _IOWR(0, 2, int) +#define RTE_KCP_IOCTL_RELEASE _IOWR(0, 3, int) + +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_MAX, +}; + +struct kcp_dev { + /* kcp list */ + struct list_head list; + + char name[RTE_KCP_NAMESIZE]; /* Network device name */ + + /* kcp device */ + struct net_device *net_dev; + + int port_id; + struct completion msg_received; +}; + +void kcp_net_init(struct net_device *dev); + +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) +#define KCP_PRINT(args...) printk(KERN_DEBUG "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..0f5b583 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_ethtool.c @@ -0,0 +1,261 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010-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; if not, write to the Free Software + * Foundation. + * 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 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 ret; + + return data; +} + +static int +kcp_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, + u8 *bytes) +{ + int ret; + + ret = kcp_nl_exec(eeprom->cmd, dev, + eeprom, sizeof(struct ethtool_eeprom), + bytes, eeprom->len); + *bytes = 0; + return ret; +} + +static int +kcp_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, + u8 *bytes) +{ + int ret; + + ret = kcp_nl_exec(eeprom->cmd, dev, + eeprom, sizeof(struct ethtool_eeprom), + bytes, eeprom->len); + *bytes = 0; + 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) +{ + int ret; + + ret = kcp_nl_exec(ring->cmd, dev, + ring, sizeof(struct ethtool_ringparam), + NULL, 0); + return ret; +} + +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 ret; + + 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 ret; + + return data; +} + +static void +kcp_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) +{ + + kcp_nl_exec(regs->cmd, dev, regs, sizeof(struct ethtool_regs), + p, regs->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 ret; + + 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 = ethtool_op_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_misc.c b/lib/librte_eal/linuxapp/kcp/kcp_misc.c new file mode 100644 index 0000000..eadd1d7 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_misc.c @@ -0,0 +1,282 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010-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; if not, write to the Free Software + * Foundation. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation + */ + +#include <linux/module.h> +#include <linux/miscdevice.h> + +#include "kcp_dev.h" + +#define KCP_DEV_IN_USE_BIT_NUM 0 /* Bit number for device in use */ + +static volatile unsigned long device_in_use; /* device in use flag */ + +/* kcp list lock */ +static DECLARE_RWSEM(kcp_list_lock); + +/* kcp list */ +static struct list_head kcp_list_head = LIST_HEAD_INIT(kcp_list_head); + +static int +kcp_open(struct inode *inode, struct file *file) +{ + /* kcp device can be opened by one user only, test and set bit */ + if (test_and_set_bit(KCP_DEV_IN_USE_BIT_NUM, &device_in_use)) + return -EBUSY; + + KCP_PRINT("/dev/kcp opened\n"); + + kcp_nl_init(); + + return 0; +} + +static int +kcp_dev_remove(struct kcp_dev *dev) +{ + if (!dev) + return -ENODEV; + + if (dev->net_dev) { + unregister_netdev(dev->net_dev); + free_netdev(dev->net_dev); + } + + return 0; +} + +static int +kcp_release(struct inode *inode, struct file *file) +{ + struct kcp_dev *dev, *n; + + down_write(&kcp_list_lock); + list_for_each_entry_safe(dev, n, &kcp_list_head, list) { + kcp_dev_remove(dev); + list_del(&dev->list); + } + up_write(&kcp_list_lock); + + kcp_nl_release(); + + /* Clear the bit of device in use */ + clear_bit(KCP_DEV_IN_USE_BIT_NUM, &device_in_use); + + KCP_PRINT("/dev/kcp closed\n"); + + return 0; +} + +static int +kcp_check_param(struct kcp_dev *kcp, char *name) +{ + if (!kcp) + return -1; + + /* Check if network name has been used */ + if (!strncmp(kcp->name, name, RTE_KCP_NAMESIZE)) { + KCP_ERR("KCP interface name %s duplicated\n", name); + return -1; + } + + return 0; +} + +static int +kcp_ioctl_create(unsigned int ioctl_num, unsigned long ioctl_param) +{ + int ret; + struct net_device *net_dev = NULL; + struct kcp_dev *kcp, *dev, *n; + struct net *net; + char name[RTE_KCP_NAMESIZE]; + unsigned int instance = ioctl_param; + char mac[ETH_ALEN]; + + KCP_PRINT("Creating kcp...\n"); + + snprintf(name, RTE_KCP_NAMESIZE, "dpdk%u", instance); + + /* Check if it has been created */ + down_read(&kcp_list_lock); + list_for_each_entry_safe(dev, n, &kcp_list_head, list) { + if (kcp_check_param(dev, name) < 0) { + up_read(&kcp_list_lock); + return -EINVAL; + } + } + up_read(&kcp_list_lock); + + net_dev = alloc_netdev(sizeof(struct kcp_dev), name, +#ifdef NET_NAME_UNKNOWN + NET_NAME_UNKNOWN, +#endif + kcp_net_init); + if (net_dev == NULL) { + KCP_ERR("error allocating device \"%s\"\n", name); + return -EBUSY; + } + + net = get_net_ns_by_pid(task_pid_vnr(current)); + if (IS_ERR(net)) { + free_netdev(net_dev); + return PTR_ERR(net); + } + dev_net_set(net_dev, net); + put_net(net); + + kcp = netdev_priv(net_dev); + + kcp->net_dev = net_dev; + kcp->port_id = instance; + init_completion(&kcp->msg_received); + strncpy(kcp->name, name, RTE_KCP_NAMESIZE); + + kcp_nl_exec(RTE_KCP_REQ_GET_MAC, net_dev, NULL, 0, mac, ETH_ALEN); + memcpy(net_dev->dev_addr, mac, net_dev->addr_len); + + kcp_set_ethtool_ops(net_dev); + ret = register_netdev(net_dev); + if (ret) { + KCP_ERR("error %i registering device \"%s\"\n", ret, name); + kcp_dev_remove(kcp); + return -ENODEV; + } + + down_write(&kcp_list_lock); + list_add(&kcp->list, &kcp_list_head); + up_write(&kcp_list_lock); + + return 0; +} + +static int +kcp_ioctl_release(unsigned int ioctl_num, unsigned long ioctl_param) +{ + int ret = -EINVAL; + struct kcp_dev *dev; + struct kcp_dev *n; + char name[RTE_KCP_NAMESIZE]; + unsigned int instance = ioctl_param; + + snprintf(name, RTE_KCP_NAMESIZE, "dpdk%u", instance); + + down_write(&kcp_list_lock); + list_for_each_entry_safe(dev, n, &kcp_list_head, list) { + if (strncmp(dev->name, name, RTE_KCP_NAMESIZE) != 0) + continue; + kcp_dev_remove(dev); + list_del(&dev->list); + ret = 0; + break; + } + up_write(&kcp_list_lock); + KCP_INFO("%s release kcp named %s\n", + (ret == 0 ? "Successfully" : "Unsuccessfully"), name); + + return ret; +} + +static int +kcp_ioctl(struct inode *inode, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + int ret = -EINVAL; + + KCP_DBG("IOCTL num=0x%0x param=0x%0lx\n", ioctl_num, ioctl_param); + + /* + * Switch according to the ioctl called + */ + switch (_IOC_NR(ioctl_num)) { + case _IOC_NR(RTE_KCP_IOCTL_TEST): + /* For test only, not used */ + break; + case _IOC_NR(RTE_KCP_IOCTL_CREATE): + ret = kcp_ioctl_create(ioctl_num, ioctl_param); + break; + case _IOC_NR(RTE_KCP_IOCTL_RELEASE): + ret = kcp_ioctl_release(ioctl_num, ioctl_param); + break; + default: + KCP_DBG("IOCTL default\n"); + break; + } + + return ret; +} + +static int +kcp_compat_ioctl(struct inode *inode, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + /* 32 bits app on 64 bits OS to be supported later */ + KCP_PRINT("Not implemented.\n"); + + return -EINVAL; +} + +static const struct file_operations kcp_fops = { + .owner = THIS_MODULE, + .open = kcp_open, + .release = kcp_release, + .unlocked_ioctl = (void *)kcp_ioctl, + .compat_ioctl = (void *)kcp_compat_ioctl, +}; + +static struct miscdevice kcp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = KCP_DEVICE, + .fops = &kcp_fops, +}; + +static int __init +kcp_init(void) +{ + KCP_PRINT("DPDK kcp module loading\n"); + + if (misc_register(&kcp_misc) != 0) { + KCP_ERR("Misc registration failed\n"); + return -EPERM; + } + + /* Clear the bit of device in use */ + clear_bit(KCP_DEV_IN_USE_BIT_NUM, &device_in_use); + + KCP_PRINT("DPDK kcp module loaded\n"); + + return 0; +} +module_init(kcp_init); + +static void __exit +kcp_exit(void) +{ + misc_deregister(&kcp_misc); + KCP_PRINT("DPDK kcp module unloaded\n"); +} +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_net.c b/lib/librte_eal/linuxapp/kcp/kcp_net.c new file mode 100644 index 0000000..8aba386 --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_net.c @@ -0,0 +1,209 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010-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; if not, write to the Free Software + * Foundation. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation + */ + +/* + * This code is inspired from the book "Linux Device Drivers" by + * Alessandro Rubini and Jonathan Corbet, published by O'Reilly & Associates + */ + +#include <linux/version.h> +#include <linux/etherdevice.h> /* eth_type_trans */ + +#include "kcp_dev.h" + +/* + * Open and close + */ +static int +kcp_net_open(struct net_device *dev) +{ + kcp_nl_exec(RTE_KCP_REQ_START_PORT, dev, NULL, 0, NULL, 0); + netif_start_queue(dev); + return 0; +} + +static int +kcp_net_release(struct net_device *dev) +{ + kcp_nl_exec(RTE_KCP_REQ_STOP_PORT, dev, NULL, 0, NULL, 0); + netif_stop_queue(dev); /* can't transmit any more */ + return 0; +} + +/* + * Configuration changes (passed on by ifconfig) + */ +static int +kcp_net_config(struct net_device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) /* can't act on a running interface */ + return -EBUSY; + + /* ignore other fields */ + return 0; +} + +static int +kcp_net_change_mtu(struct net_device *dev, int new_mtu) +{ + int err; + + KCP_DBG("kcp_net_change_mtu new mtu %d to be set\n", new_mtu); + 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; +} + +/* + * Ioctl commands + */ +static int +kcp_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + KCP_DBG("kcp_net_ioctl\n"); + + return 0; +} + +/* + * Return statistics to the caller + */ +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; +} + +/** + * kcp_net_set_mac - Change the Ethernet Address of the KCP NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int +kcp_net_set_mac(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + int err; + + 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; +} + +#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_open = kcp_net_open, + .ndo_stop = kcp_net_release, + .ndo_set_config = kcp_net_config, + .ndo_change_mtu = kcp_net_change_mtu, + .ndo_do_ioctl = kcp_net_ioctl, + .ndo_get_stats64 = kcp_net_stats64, + .ndo_set_mac_address = kcp_net_set_mac, +#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE) + .ndo_change_carrier = kcp_net_change_carrier, +#endif +}; + +/* + * Fill the eth header + */ +static int +kcp_net_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) +{ + struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); + + memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len); + eth->h_proto = htons(type); + + return dev->hard_header_len; +} + +/* + * Re-fill the eth header + */ +#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE) +static int +kcp_net_rebuild_header(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct ethhdr *eth = (struct ethhdr *) skb->data; + + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, dev->dev_addr, dev->addr_len); + + return 0; +} +#endif + +static const struct header_ops kcp_net_header_ops = { + .create = kcp_net_header, +#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE) + .rebuild = kcp_net_rebuild_header, +#endif + .cache = NULL, /* disable caching */ +}; + +void +kcp_net_init(struct net_device *dev) +{ + KCP_DBG("kcp_net_init\n"); + + ether_setup(dev); /* assign some of the fields */ + dev->netdev_ops = &kcp_net_netdev_ops; + dev->header_ops = &kcp_net_header_ops; + + dev->flags |= IFF_UP; +} 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..3c2ed5b --- /dev/null +++ b/lib/librte_eal/linuxapp/kcp/kcp_nl.c @@ -0,0 +1,194 @@ +/*- + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010-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_NL_GRP 31 + +#define KCP_ETHTOOL_MSG_LEN 500 +struct kcp_ethtool_msg { + int cmd_id; + int port_id; + char input_buffer[KCP_ETHTOOL_MSG_LEN]; + char output_buffer[KCP_ETHTOOL_MSG_LEN]; + int input_buf_len; + int output_buf_len; + int err; +}; + +static struct ethtool_input_buffer { + int magic; + void *buffer; + int length; + struct completion *msg_received; + int *err; +} ethtool_input_buffer; + +static struct sock *nl_sock; +static int pid __read_mostly = -1; +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.buffer == NULL) { + 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; + return 0; + } + + return 1; +} + +static void +kcp_input_buffer_unregister(int magic) +{ + if (ethtool_input_buffer.buffer != NULL) { + 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; + } + } +} + +static void +nl_recv(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + struct kcp_ethtool_msg ethtool_msg; + + nlh = (struct nlmsghdr *)skb->data; + if (pid < 0) { + pid = nlh->nlmsg_pid; + KCP_INFO("PID: %d\n", pid); + return; + } else if (pid != nlh->nlmsg_pid) { + KCP_INFO("Message from unexpected peer: %d", nlh->nlmsg_pid); + return; + } + + memcpy(ðtool_msg, NLMSG_DATA(nlh), sizeof(struct kcp_ethtool_msg)); + KCP_DBG("CMD: %d\n", ethtool_msg.cmd_id); + + if (ethtool_input_buffer.magic > 0) { + if (ethtool_input_buffer.buffer != NULL) { + memcpy(ethtool_input_buffer.buffer, + ðtool_msg.output_buffer, + ethtool_input_buffer.length); + } + *ethtool_input_buffer.err = ethtool_msg.err; + complete(ethtool_input_buffer.msg_received); + kcp_input_buffer_unregister(ethtool_input_buffer.magic); + } +} + +static int +kcp_nl_send(int cmd_id, int port_id, void *input_buffer, int input_buf_len) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct kcp_ethtool_msg ethtool_msg; + + memset(ðtool_msg, 0, sizeof(struct kcp_ethtool_msg)); + ethtool_msg.cmd_id = cmd_id; + ethtool_msg.port_id = port_id; + + if (input_buffer) { + if (input_buf_len == 0 || input_buf_len > KCP_ETHTOOL_MSG_LEN) + return -EINVAL; + ethtool_msg.input_buf_len = input_buf_len; + memcpy(ethtool_msg.input_buffer, input_buffer, input_buf_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\n", cmd_id, port_id); + + /*nlmsg_free(skb);*/ + + return 0; +} + +int +kcp_nl_exec(int cmd, struct net_device *dev, void *in_data, int in_len, + void *out_data, int out_len) +{ + struct kcp_dev *priv = netdev_priv(dev); + int err = -EINVAL; + int ret; + + mutex_lock(&sync_lock); + ret = kcp_input_buffer_register(cmd, out_data, out_len, + &priv->msg_received, &err); + if (ret) { + mutex_unlock(&sync_lock); + return -EINVAL; + } + + kcp_nl_send(cmd, priv->port_id, in_data, in_len); + ret = wait_for_completion_interruptible_timeout(&priv->msg_received, + msecs_to_jiffies(10)); + if (ret == 0 || err < 0) { + kcp_input_buffer_unregister(ethtool_input_buffer.magic); + mutex_unlock(&sync_lock); + 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); + pid = -1; +} -- 2.5.0