@@ -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
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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_ */
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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");
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}