@@ -277,6 +277,10 @@ F: test/test/test_kni.c
F: examples/kni/
F: doc/guides/sample_app_ug/kernel_nic_interface.rst
+Linux Userspace Network Control Interface (UNCI)
+M: Ferruh Yigit <ferruh.yigit@intel.com>
+F: lib/librte_eal/linuxapp/unci/
+
Linux AF_PACKET
M: John W. Linville <linville@tuxdriver.com>
F: drivers/net/af_packet/
@@ -704,6 +704,12 @@ CONFIG_RTE_LIBRTE_PDUMP=y
CONFIG_RTE_LIBRTE_ETHTOOL=n
#
+# Compile Userspace Network Control Interface (UNCI) kernel module
+#
+CONFIG_RTE_UNCI_KMOD=n
+CONFIG_RTE_UNCI_KO_DEBUG=n
+
+#
# Compile vhost user library
#
CONFIG_RTE_LIBRTE_VHOST=n
@@ -41,6 +41,7 @@ CONFIG_RTE_KNI_KMOD=y
CONFIG_RTE_LIBRTE_KNI=y
CONFIG_RTE_LIBRTE_PMD_KNI=y
CONFIG_RTE_LIBRTE_ETHTOOL=y
+CONFIG_RTE_UNCI_KMOD=y
CONFIG_RTE_LIBRTE_VHOST=y
CONFIG_RTE_LIBRTE_PMD_VHOST=y
CONFIG_RTE_LIBRTE_PMD_AF_PACKET=y
@@ -1,6 +1,6 @@
# BSD LICENSE
#
-# Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+# Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,8 @@ DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += eal
DIRS-$(CONFIG_RTE_EAL_IGB_UIO) += igb_uio
DIRS-$(CONFIG_RTE_KNI_KMOD) += kni
DEPDIRS-kni := eal
+DIRS-$(CONFIG_RTE_UNCI_KMOD) += unci
+DEPDIRS-unci := eal
DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += xen_dom0
DEPDIRS-xen_dom0 := eal
@@ -127,6 +127,7 @@ CFLAGS_eal_thread.o += -Wno-return-type
endif
INC := rte_interrupts.h rte_kni_common.h rte_dom0_common.h
+INC += rte_unci_common.h
SYMLINK-$(CONFIG_RTE_EXEC_ENV_LINUXAPP)-include/exec-env := \
$(addprefix include/exec-env/,$(INC))
new file mode 100644
@@ -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) 2017 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) 2017 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_UNCI_COMMON_H_
+#define _RTE_UNCI_COMMON_H_
+
+#define UNCI_DEVICE "unci"
+
+#define UNCI_NL_GRP 31
+
+#define UNCI_ETHTOOL_MSG_LEN 500
+struct unci_ethtool_msg {
+ uint32_t cmd_id;
+ uint8_t port_id;
+ uint32_t flag;
+ uint8_t input_buffer[UNCI_ETHTOOL_MSG_LEN];
+ uint8_t output_buffer[UNCI_ETHTOOL_MSG_LEN];
+ size_t input_buffer_len;
+ size_t output_buffer_len;
+ int err;
+};
+
+enum unci_ethtool_msg_flag {
+ UNCI_MSG_FLAG_NONE,
+ UNCI_MSG_FLAG_REQUEST,
+ UNCI_MSG_FLAG_RESPONSE,
+};
+
+enum {
+ IFLA_UNCI_UNSPEC,
+ IFLA_UNCI_PORTID,
+ IFLA_UNCI_PID,
+ __IFLA_UNCI_MAX,
+};
+
+#define IFLA_UNCI_MAX (__IFLA_UNCI_MAX - 1)
+
+/*
+ * Request id.
+ */
+enum rte_unci_req_id {
+ RTE_UNCI_REQ_UNKNOWN = (1 << 16),
+ RTE_UNCI_REQ_CHANGE_MTU,
+ RTE_UNCI_REQ_CFG_NETWORK_IF,
+ RTE_UNCI_REQ_GET_STATS,
+ RTE_UNCI_REQ_GET_MAC,
+ RTE_UNCI_REQ_SET_MAC,
+ RTE_UNCI_REQ_START_PORT,
+ RTE_UNCI_REQ_STOP_PORT,
+ RTE_UNCI_REQ_SET_PROMISC,
+ RTE_UNCI_REQ_SET_ALLMULTI,
+ RTE_UNCI_REQ_MAX,
+};
+
+#endif /* _RTE_UNCI_COMMON_H_ */
new file mode 100644
@@ -0,0 +1,54 @@
+# 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_unci
+
+#
+# CFLAGS
+#
+MODULE_CFLAGS += -I$(SRCDIR)
+MODULE_CFLAGS += -I$(RTE_OUTPUT)/include
+MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h
+MODULE_CFLAGS += -Wall -Werror
+
+#
+# all source are stored in SRCS-y
+#
+SRCS-y += unci_net.c
+SRCS-y += unci_ethtool.c
+SRCS-y += unci_nl.c
+
+include $(RTE_SDK)/mk/rte.module.mk
new file mode 100644
@@ -0,0 +1,56 @@
+/*-
+ * 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 _UNCI_DEV_H_
+#define _UNCI_DEV_H_
+
+#include <linux/netdevice.h>
+#include <exec-env/rte_unci_common.h>
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+struct unci_dev {
+ u8 port_id;
+ u32 pid;
+ struct completion msg_received;
+ u32 nb_timedout_msg;
+};
+
+void unci_nl_init(void);
+void unci_nl_release(void);
+int unci_nl_exec(u32 cmd, struct net_device *dev, void *in_data,
+ size_t in_len, void *out_data, size_t out_len);
+
+void unci_set_ethtool_ops(struct net_device *netdev);
+
+#ifdef RTE_UNCI_KO_DEBUG
+#define UNCI_DBG(args...) pr_debug(args)
+#else
+#define UNCI_DBG(args...)
+#endif
+
+#endif /* _UNCI_DEV_H_ */
new file mode 100644
@@ -0,0 +1,293 @@
+/*-
+ * 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 "unci_dev.h"
+
+#define ETHTOOL_GEEPROM_LEN 99
+#define ETHTOOL_GREGS_LEN 98
+#define ETHTOOL_GSSET_COUNT 97
+
+static int unci_check_if_running(struct net_device *dev)
+{
+ return 0;
+}
+
+static void unci_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ int ret;
+
+ ret = unci_nl_exec(info->cmd, dev, NULL, 0,
+ info, sizeof(struct ethtool_drvinfo));
+ if (ret < 0)
+ memset(info, 0, sizeof(struct ethtool_drvinfo));
+}
+
+static int unci_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ return unci_nl_exec(ecmd->cmd, dev, NULL, 0,
+ ecmd, sizeof(struct ethtool_cmd));
+}
+
+static int unci_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ return unci_nl_exec(ecmd->cmd, dev, ecmd, sizeof(struct ethtool_cmd),
+ NULL, 0);
+}
+
+static void unci_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ int ret;
+
+ ret = unci_nl_exec(wol->cmd, dev, NULL, 0,
+ wol, sizeof(struct ethtool_wolinfo));
+ if (ret < 0)
+ memset(wol, 0, sizeof(struct ethtool_wolinfo));
+}
+
+static int unci_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ return unci_nl_exec(wol->cmd, dev, wol, sizeof(struct ethtool_wolinfo),
+ NULL, 0);
+}
+
+static int unci_nway_reset(struct net_device *dev)
+{
+ return unci_nl_exec(ETHTOOL_NWAY_RST, dev, NULL, 0, NULL, 0);
+}
+
+static u32 unci_get_link(struct net_device *dev)
+{
+ u32 data;
+ int ret;
+
+ ret = unci_nl_exec(ETHTOOL_GLINK, dev, NULL, 0, &data, sizeof(u32));
+ if (ret < 0)
+ return 0;
+
+ return data;
+}
+
+static int unci_get_eeprom_len(struct net_device *dev)
+{
+ int data;
+ int ret;
+
+ ret = unci_nl_exec(ETHTOOL_GEEPROM_LEN, dev, NULL, 0,
+ &data, sizeof(int));
+ if (ret < 0)
+ return 0;
+
+ return data;
+}
+
+static int unci_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct ethtool_eeprom eeprom_tmp;
+ int ret = 0;
+ int remaining;
+ u32 offset = 0;
+
+ eeprom_tmp = *eeprom;
+
+ remaining = eeprom_tmp.len;
+ while (remaining > 0 && ret == 0) {
+ eeprom_tmp.len = min(remaining, UNCI_ETHTOOL_MSG_LEN);
+
+ ret = unci_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 unci_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct ethtool_eeprom *eeprom_tmp;
+ int ret = 0;
+ u32 remaining;
+ u32 offset = 0;
+ u32 payload;
+
+ if (sizeof(struct ethtool_eeprom) > UNCI_ETHTOOL_MSG_LEN)
+ return -1;
+
+ eeprom_tmp = kmalloc(UNCI_ETHTOOL_MSG_LEN, GFP_KERNEL);
+ payload = UNCI_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 = unci_nl_exec(eeprom->cmd, dev, eeprom,
+ UNCI_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 unci_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ unci_nl_exec(ring->cmd, dev, NULL, 0,
+ ring, sizeof(struct ethtool_ringparam));
+}
+
+static int unci_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ return unci_nl_exec(ring->cmd, dev, ring,
+ sizeof(struct ethtool_ringparam), NULL, 0);
+}
+
+static void unci_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ unci_nl_exec(pause->cmd, dev, NULL, 0,
+ pause, sizeof(struct ethtool_pauseparam));
+}
+
+static int unci_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ return unci_nl_exec(pause->cmd, dev, pause,
+ sizeof(struct ethtool_pauseparam), NULL, 0);
+}
+
+static u32 unci_get_msglevel(struct net_device *dev)
+{
+ u32 data;
+ int ret;
+
+ ret = unci_nl_exec(ETHTOOL_GMSGLVL, dev, NULL, 0, &data, sizeof(u32));
+ if (ret < 0)
+ return 0;
+
+ return data;
+}
+
+static void unci_set_msglevel(struct net_device *dev, u32 data)
+{
+ unci_nl_exec(ETHTOOL_SMSGLVL, dev, &data, sizeof(u32), NULL, 0);
+}
+
+static int unci_get_regs_len(struct net_device *dev)
+{
+ int data;
+ int ret;
+
+ ret = unci_nl_exec(ETHTOOL_GREGS_LEN, dev, NULL, 0, &data, sizeof(int));
+ if (ret < 0)
+ return 0;
+
+ return data;
+}
+
+static void unci_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *p)
+{
+ struct ethtool_regs regs_tmp;
+ u32 len = regs->len;
+
+ regs_tmp = *regs;
+
+ if (len > UNCI_ETHTOOL_MSG_LEN) {
+ len = UNCI_ETHTOOL_MSG_LEN;
+ regs_tmp.len = len;
+ }
+
+ unci_nl_exec(regs->cmd, dev, ®s_tmp, sizeof(struct ethtool_regs),
+ p, len);
+}
+
+static void unci_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ unci_nl_exec(ETHTOOL_GSTRINGS, dev, &stringset, sizeof(u32), data, 0);
+}
+
+static int unci_get_sset_count(struct net_device *dev, int sset)
+{
+ int data;
+ int ret;
+
+ ret = unci_nl_exec(ETHTOOL_GSSET_COUNT, dev, &sset, sizeof(int),
+ &data, sizeof(int));
+ if (ret < 0)
+ return 0;
+
+ return data;
+}
+
+static void unci_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ unci_nl_exec(stats->cmd, dev, stats, sizeof(struct ethtool_stats),
+ data, stats->n_stats);
+}
+
+static const struct ethtool_ops unci_ethtool_ops = {
+ .begin = unci_check_if_running,
+ .get_drvinfo = unci_get_drvinfo,
+ .get_settings = unci_get_settings,
+ .set_settings = unci_set_settings,
+ .get_regs_len = unci_get_regs_len,
+ .get_regs = unci_get_regs,
+ .get_wol = unci_get_wol,
+ .set_wol = unci_set_wol,
+ .nway_reset = unci_nway_reset,
+ .get_link = unci_get_link,
+ .get_eeprom_len = unci_get_eeprom_len,
+ .get_eeprom = unci_get_eeprom,
+ .set_eeprom = unci_set_eeprom,
+ .get_ringparam = unci_get_ringparam,
+ .set_ringparam = unci_set_ringparam,
+ .get_pauseparam = unci_get_pauseparam,
+ .set_pauseparam = unci_set_pauseparam,
+ .get_msglevel = unci_get_msglevel,
+ .set_msglevel = unci_set_msglevel,
+ .get_strings = unci_get_strings,
+ .get_sset_count = unci_get_sset_count,
+ .get_ethtool_stats = unci_get_ethtool_stats,
+};
+
+void unci_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &unci_ethtool_ops;
+}
new file mode 100644
@@ -0,0 +1,217 @@
+/*-
+ * 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 "unci_dev.h"
+
+static int unci_net_init(struct net_device *dev)
+{
+ u8 mac[ETH_ALEN] = {0};
+
+ unci_nl_exec(RTE_UNCI_REQ_GET_MAC, dev, NULL, 0, mac, ETH_ALEN);
+ memcpy(dev->dev_addr, mac, dev->addr_len);
+ return 0;
+}
+
+static int unci_net_open(struct net_device *dev)
+{
+ /* DPDK port already started, stop it first */
+ unci_nl_exec(RTE_UNCI_REQ_STOP_PORT, dev, NULL, 0, NULL, 0);
+ unci_nl_exec(RTE_UNCI_REQ_START_PORT, dev, NULL, 0, NULL, 0);
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int unci_net_close(struct net_device *dev)
+{
+ unci_nl_exec(RTE_UNCI_REQ_STOP_PORT, dev, NULL, 0, NULL, 0);
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int unci_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static void unci_net_change_rx_flags(struct net_device *dev, int flags)
+{
+ u32 on = 1;
+ u32 off = 0;
+
+ if (flags & IFF_PROMISC)
+ unci_nl_exec(RTE_UNCI_REQ_SET_PROMISC, dev,
+ dev->flags & IFF_PROMISC ? &on : &off,
+ sizeof(u32), NULL, 0);
+
+ if (flags & IFF_ALLMULTI)
+ unci_nl_exec(RTE_UNCI_REQ_SET_ALLMULTI, dev,
+ dev->flags & IFF_ALLMULTI ? &on : &off,
+ sizeof(u32), NULL, 0);
+}
+
+static int unci_net_set_mac(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ int err;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ err = unci_nl_exec(RTE_UNCI_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 unci_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Configuration changes (passed on by ifconfig)
+ */
+static int unci_net_config(struct net_device *dev, struct ifmap *map)
+{
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ return -EOPNOTSUPP;
+}
+
+static int unci_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+ int err = 0;
+
+ err = unci_nl_exec(RTE_UNCI_REQ_CHANGE_MTU, dev, &new_mtu, sizeof(int),
+ NULL, 0);
+
+ if (err == 0)
+ dev->mtu = new_mtu;
+
+ return err;
+}
+
+static void unci_net_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ int err;
+
+ err = unci_nl_exec(RTE_UNCI_REQ_GET_STATS, dev, NULL, 0,
+ stats, sizeof(struct rtnl_link_stats64));
+}
+
+#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE)
+static int unci_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 unci_net_netdev_ops = {
+ .ndo_init = unci_net_init,
+ .ndo_open = unci_net_open,
+ .ndo_stop = unci_net_close,
+ .ndo_start_xmit = unci_net_xmit,
+ .ndo_change_rx_flags = unci_net_change_rx_flags,
+ .ndo_set_mac_address = unci_net_set_mac,
+ .ndo_do_ioctl = unci_net_ioctl,
+ .ndo_set_config = unci_net_config,
+ .ndo_change_mtu = unci_net_change_mtu,
+ .ndo_get_stats64 = unci_net_stats64,
+#if (KERNEL_VERSION(3, 9, 0) <= LINUX_VERSION_CODE)
+ .ndo_change_carrier = unci_net_change_carrier,
+#endif
+};
+
+static void unci_net_setup(struct net_device *dev)
+{
+ struct unci_dev *unci;
+
+ ether_setup(dev);
+ dev->netdev_ops = &unci_net_netdev_ops;
+
+ unci = netdev_priv(dev);
+ init_completion(&unci->msg_received);
+
+ unci_set_ethtool_ops(dev);
+}
+
+static int unci_net_newlink(struct net *net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct unci_dev *unci = netdev_priv(dev);
+
+ if (data && data[IFLA_UNCI_PORTID])
+ unci->port_id = nla_get_u8(data[IFLA_UNCI_PORTID]);
+ else
+ unci->port_id = 0;
+
+ if (data && data[IFLA_UNCI_PID])
+ unci->pid = nla_get_u32(data[IFLA_UNCI_PID]);
+ else
+ unci->pid = 0;
+
+ return register_netdevice(dev);
+}
+
+static struct rtnl_link_ops unci_link_ops __read_mostly = {
+ .kind = UNCI_DEVICE,
+ .priv_size = sizeof(struct unci_dev),
+ .setup = unci_net_setup,
+ .maxtype = IFLA_UNCI_MAX,
+ .newlink = unci_net_newlink,
+};
+
+static int __init unci_init(void)
+{
+ unci_nl_init();
+ return rtnl_link_register(&unci_link_ops);
+}
+module_init(unci_init);
+
+static void __exit unci_exit(void)
+{
+ rtnl_link_unregister(&unci_link_ops);
+ unci_nl_release();
+}
+module_exit(unci_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Kernel Module for managing unci devices");
new file mode 100644
@@ -0,0 +1,220 @@
+/*-
+ * 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 "unci_dev.h"
+
+#define UNCI_CMD_TIMEOUT 500 /* ms */
+
+static struct ethtool_input_buffer {
+ int magic;
+ void *buffer;
+ size_t length;
+ struct completion *msg_received;
+ int *err;
+ u32 in_use;
+} ethtool_input_buffer;
+
+static struct sock *nl_sock;
+static struct mutex sync_lock;
+
+static int unci_input_buffer_register(int magic, void *buffer, size_t length,
+ struct completion *msg_received, int *err)
+{
+ if (!ethtool_input_buffer.in_use) {
+ 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 unci_input_buffer_unregister(int magic)
+{
+ if (ethtool_input_buffer.in_use) {
+ 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 {
+ pr_err("Unregister magic mismatch\n");
+ }
+ }
+}
+
+static void nl_recv_user_request(struct unci_ethtool_msg *ethtool_msg)
+{
+ UNCI_DBG("Request from userspace received\n");
+}
+
+static void nl_recv_user_response(struct unci_ethtool_msg *ethtool_msg)
+{
+ struct completion *msg_received;
+ size_t recv_len;
+ size_t expected_len;
+
+ if (ethtool_input_buffer.in_use) {
+ 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)
+ pr_info("Expected and received len not match "
+ "%zu - %zu\n", recv_len, expected_len);
+ }
+
+ *ethtool_input_buffer.err = ethtool_msg->err;
+ msg_received = ethtool_input_buffer.msg_received;
+ unci_input_buffer_unregister(ethtool_input_buffer.magic);
+ complete(msg_received);
+ }
+}
+
+static void nl_recv(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh;
+ struct unci_ethtool_msg ethtool_msg;
+
+ nlh = (struct nlmsghdr *)skb->data;
+
+ memcpy(ðtool_msg, NLMSG_DATA(nlh), sizeof(struct unci_ethtool_msg));
+ UNCI_DBG("CMD: %u\n", ethtool_msg.cmd_id);
+
+ if (ethtool_msg.flag & UNCI_MSG_FLAG_REQUEST) {
+ nl_recv_user_request(ðtool_msg);
+ return;
+ }
+
+ nl_recv_user_response(ðtool_msg);
+}
+
+static int unci_nl_send(u32 cmd_id, u8 port_id, u32 pid, void *in_data,
+ size_t in_data_len)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ struct unci_ethtool_msg ethtool_msg;
+
+ if (pid == 0)
+ return -1;
+
+ memset(ðtool_msg, 0, sizeof(struct unci_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 > UNCI_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 unci_ethtool_msg)),
+ GFP_ATOMIC);
+ nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, sizeof(struct unci_ethtool_msg),
+ 0);
+
+ NETLINK_CB(skb).dst_group = 0;
+
+ memcpy(nlmsg_data(nlh), ðtool_msg, sizeof(struct unci_ethtool_msg));
+
+ nlmsg_unicast(nl_sock, skb, pid);
+ UNCI_DBG("Sent cmd:%u port:%u pid:%u\n", cmd_id, port_id, pid);
+
+ return 0;
+}
+
+int unci_nl_exec(u32 cmd, struct net_device *dev, void *in_data,
+ size_t in_data_len, void *out_data, size_t out_data_len)
+{
+ struct unci_dev *unci = netdev_priv(dev);
+ int err = -EINVAL;
+ int ret;
+
+ if (out_data_len > UNCI_ETHTOOL_MSG_LEN) {
+ pr_err("Message is too big to receive:%zu\n", out_data_len);
+ return err;
+ }
+
+ mutex_lock(&sync_lock);
+ ret = unci_input_buffer_register(cmd, out_data, out_data_len,
+ &unci->msg_received, &err);
+ if (ret) {
+ mutex_unlock(&sync_lock);
+ return -EINVAL;
+ }
+
+ ret = unci_nl_send(cmd, unci->port_id, unci->pid, in_data, in_data_len);
+ if (ret) {
+ unci_input_buffer_unregister(ethtool_input_buffer.magic);
+ mutex_unlock(&sync_lock);
+ return ret;
+ }
+
+ ret = wait_for_completion_interruptible_timeout(&unci->msg_received,
+ msecs_to_jiffies(UNCI_CMD_TIMEOUT));
+ if (ret == 0 || err < 0) {
+ unci_input_buffer_unregister(ethtool_input_buffer.magic);
+ mutex_unlock(&sync_lock);
+ if (ret == 0) { /* timeout */
+ unci->nb_timedout_msg++;
+ pr_info("Command timed-out for port:%u cmd:%u (%u)\n",
+ unci->port_id, cmd, unci->nb_timedout_msg);
+ return -EINVAL;
+ }
+ UNCI_DBG("Command return error for port:%d cmd:%d err:%d\n",
+ unci->port_id, cmd, err);
+ return err;
+ }
+ mutex_unlock(&sync_lock);
+
+ return 0;
+}
+
+static struct netlink_kernel_cfg cfg = {
+ .input = nl_recv,
+};
+
+void unci_nl_init(void)
+{
+ nl_sock = netlink_kernel_create(&init_net, UNCI_NL_GRP, &cfg);
+ mutex_init(&sync_lock);
+}
+
+void unci_nl_release(void)
+{
+ netlink_kernel_release(nl_sock);
+}