18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Netronome Systems, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software is licensed under the GNU General License Version 2,
58c2ecf20Sopenharmony_ci * June 1991 as shown in the file COPYING in the top-level directory of this
68c2ecf20Sopenharmony_ci * source tree.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
98c2ecf20Sopenharmony_ci * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
108c2ecf20Sopenharmony_ci * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
118c2ecf20Sopenharmony_ci * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
128c2ecf20Sopenharmony_ci * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
138c2ecf20Sopenharmony_ci * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <net/netlink.h>
238c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
248c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
258c2ecf20Sopenharmony_ci#include <net/udp_tunnel.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "netdevsim.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (!nsim_ipsec_tx(ns, skb))
348c2ecf20Sopenharmony_ci		goto out;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	u64_stats_update_begin(&ns->syncp);
378c2ecf20Sopenharmony_ci	ns->tx_packets++;
388c2ecf20Sopenharmony_ci	ns->tx_bytes += skb->len;
398c2ecf20Sopenharmony_ci	u64_stats_update_end(&ns->syncp);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ciout:
428c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void nsim_set_rx_mode(struct net_device *dev)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int nsim_change_mtu(struct net_device *dev, int new_mtu)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU)
568c2ecf20Sopenharmony_ci		return -EBUSY;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	dev->mtu = new_mtu;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void
648c2ecf20Sopenharmony_cinsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
678c2ecf20Sopenharmony_ci	unsigned int start;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	do {
708c2ecf20Sopenharmony_ci		start = u64_stats_fetch_begin_irq(&ns->syncp);
718c2ecf20Sopenharmony_ci		stats->tx_bytes = ns->tx_bytes;
728c2ecf20Sopenharmony_ci		stats->tx_packets = ns->tx_packets;
738c2ecf20Sopenharmony_ci	} while (u64_stats_fetch_retry_irq(&ns->syncp, start));
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int
778c2ecf20Sopenharmony_cinsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
858c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* Only refuse multicast addresses, zero address can mean unset/any. */
888c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs || is_multicast_ether_addr(mac))
898c2ecf20Sopenharmony_ci		return -EINVAL;
908c2ecf20Sopenharmony_ci	memcpy(nsim_bus_dev->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int nsim_set_vf_vlan(struct net_device *dev, int vf,
968c2ecf20Sopenharmony_ci			    u16 vlan, u8 qos, __be16 vlan_proto)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
998c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs || vlan > 4095 || qos > 7)
1028c2ecf20Sopenharmony_ci		return -EINVAL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].vlan = vlan;
1058c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].qos = qos;
1068c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].vlan_proto = vlan_proto;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
1148c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs)
1178c2ecf20Sopenharmony_ci		return -EINVAL;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].min_tx_rate = min;
1208c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].max_tx_rate = max;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
1288c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs)
1318c2ecf20Sopenharmony_ci		return -EINVAL;
1328c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].spoofchk_enabled = val;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
1408c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs)
1438c2ecf20Sopenharmony_ci		return -EINVAL;
1448c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].rss_query_enabled = val;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int nsim_set_vf_trust(struct net_device *dev, int vf, bool val)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
1528c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs)
1558c2ecf20Sopenharmony_ci		return -EINVAL;
1568c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].trusted = val;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return 0;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int
1628c2ecf20Sopenharmony_cinsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
1658c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs)
1688c2ecf20Sopenharmony_ci		return -EINVAL;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ivi->vf = vf;
1718c2ecf20Sopenharmony_ci	ivi->linkstate = nsim_bus_dev->vfconfigs[vf].link_state;
1728c2ecf20Sopenharmony_ci	ivi->min_tx_rate = nsim_bus_dev->vfconfigs[vf].min_tx_rate;
1738c2ecf20Sopenharmony_ci	ivi->max_tx_rate = nsim_bus_dev->vfconfigs[vf].max_tx_rate;
1748c2ecf20Sopenharmony_ci	ivi->vlan = nsim_bus_dev->vfconfigs[vf].vlan;
1758c2ecf20Sopenharmony_ci	ivi->vlan_proto = nsim_bus_dev->vfconfigs[vf].vlan_proto;
1768c2ecf20Sopenharmony_ci	ivi->qos = nsim_bus_dev->vfconfigs[vf].qos;
1778c2ecf20Sopenharmony_ci	memcpy(&ivi->mac, nsim_bus_dev->vfconfigs[vf].vf_mac, ETH_ALEN);
1788c2ecf20Sopenharmony_ci	ivi->spoofchk = nsim_bus_dev->vfconfigs[vf].spoofchk_enabled;
1798c2ecf20Sopenharmony_ci	ivi->trusted = nsim_bus_dev->vfconfigs[vf].trusted;
1808c2ecf20Sopenharmony_ci	ivi->rss_query_en = nsim_bus_dev->vfconfigs[vf].rss_query_enabled;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
1888c2ecf20Sopenharmony_ci	struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (vf >= nsim_bus_dev->num_vfs)
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	switch (state) {
1948c2ecf20Sopenharmony_ci	case IFLA_VF_LINK_STATE_AUTO:
1958c2ecf20Sopenharmony_ci	case IFLA_VF_LINK_STATE_ENABLE:
1968c2ecf20Sopenharmony_ci	case IFLA_VF_LINK_STATE_DISABLE:
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci	default:
1998c2ecf20Sopenharmony_ci		return -EINVAL;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	nsim_bus_dev->vfconfigs[vf].link_state = state;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return 0;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic LIST_HEAD(nsim_block_cb_list);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int
2108c2ecf20Sopenharmony_cinsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	switch (type) {
2158c2ecf20Sopenharmony_ci	case TC_SETUP_BLOCK:
2168c2ecf20Sopenharmony_ci		return flow_block_cb_setup_simple(type_data,
2178c2ecf20Sopenharmony_ci						  &nsim_block_cb_list,
2188c2ecf20Sopenharmony_ci						  nsim_setup_tc_block_cb,
2198c2ecf20Sopenharmony_ci						  ns, ns, true);
2208c2ecf20Sopenharmony_ci	default:
2218c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int
2268c2ecf20Sopenharmony_cinsim_set_features(struct net_device *dev, netdev_features_t features)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC))
2318c2ecf20Sopenharmony_ci		return nsim_bpf_disable_tc(ns);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic struct devlink_port *nsim_get_devlink_port(struct net_device *dev)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct netdevsim *ns = netdev_priv(dev);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return &ns->nsim_dev_port->devlink_port;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic const struct net_device_ops nsim_netdev_ops = {
2448c2ecf20Sopenharmony_ci	.ndo_start_xmit		= nsim_start_xmit,
2458c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= nsim_set_rx_mode,
2468c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
2478c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
2488c2ecf20Sopenharmony_ci	.ndo_change_mtu		= nsim_change_mtu,
2498c2ecf20Sopenharmony_ci	.ndo_get_stats64	= nsim_get_stats64,
2508c2ecf20Sopenharmony_ci	.ndo_set_vf_mac		= nsim_set_vf_mac,
2518c2ecf20Sopenharmony_ci	.ndo_set_vf_vlan	= nsim_set_vf_vlan,
2528c2ecf20Sopenharmony_ci	.ndo_set_vf_rate	= nsim_set_vf_rate,
2538c2ecf20Sopenharmony_ci	.ndo_set_vf_spoofchk	= nsim_set_vf_spoofchk,
2548c2ecf20Sopenharmony_ci	.ndo_set_vf_trust	= nsim_set_vf_trust,
2558c2ecf20Sopenharmony_ci	.ndo_get_vf_config	= nsim_get_vf_config,
2568c2ecf20Sopenharmony_ci	.ndo_set_vf_link_state	= nsim_set_vf_link_state,
2578c2ecf20Sopenharmony_ci	.ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en,
2588c2ecf20Sopenharmony_ci	.ndo_setup_tc		= nsim_setup_tc,
2598c2ecf20Sopenharmony_ci	.ndo_set_features	= nsim_set_features,
2608c2ecf20Sopenharmony_ci	.ndo_bpf		= nsim_bpf,
2618c2ecf20Sopenharmony_ci	.ndo_udp_tunnel_add	= udp_tunnel_nic_add_port,
2628c2ecf20Sopenharmony_ci	.ndo_udp_tunnel_del	= udp_tunnel_nic_del_port,
2638c2ecf20Sopenharmony_ci	.ndo_get_devlink_port	= nsim_get_devlink_port,
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void nsim_setup(struct net_device *dev)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	ether_setup(dev);
2698c2ecf20Sopenharmony_ci	eth_hw_addr_random(dev);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	dev->tx_queue_len = 0;
2728c2ecf20Sopenharmony_ci	dev->flags |= IFF_NOARP;
2738c2ecf20Sopenharmony_ci	dev->flags &= ~IFF_MULTICAST;
2748c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
2758c2ecf20Sopenharmony_ci			   IFF_NO_QUEUE;
2768c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_HIGHDMA |
2778c2ecf20Sopenharmony_ci			 NETIF_F_SG |
2788c2ecf20Sopenharmony_ci			 NETIF_F_FRAGLIST |
2798c2ecf20Sopenharmony_ci			 NETIF_F_HW_CSUM |
2808c2ecf20Sopenharmony_ci			 NETIF_F_TSO;
2818c2ecf20Sopenharmony_ci	dev->hw_features |= NETIF_F_HW_TC;
2828c2ecf20Sopenharmony_ci	dev->max_mtu = ETH_MAX_MTU;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistruct netdevsim *
2868c2ecf20Sopenharmony_cinsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct net_device *dev;
2898c2ecf20Sopenharmony_ci	struct netdevsim *ns;
2908c2ecf20Sopenharmony_ci	int err;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	dev = alloc_netdev(sizeof(*ns), "eth%d", NET_NAME_UNKNOWN, nsim_setup);
2938c2ecf20Sopenharmony_ci	if (!dev)
2948c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	dev_net_set(dev, nsim_dev_net(nsim_dev));
2978c2ecf20Sopenharmony_ci	ns = netdev_priv(dev);
2988c2ecf20Sopenharmony_ci	ns->netdev = dev;
2998c2ecf20Sopenharmony_ci	u64_stats_init(&ns->syncp);
3008c2ecf20Sopenharmony_ci	ns->nsim_dev = nsim_dev;
3018c2ecf20Sopenharmony_ci	ns->nsim_dev_port = nsim_dev_port;
3028c2ecf20Sopenharmony_ci	ns->nsim_bus_dev = nsim_dev->nsim_bus_dev;
3038c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev);
3048c2ecf20Sopenharmony_ci	dev->netdev_ops = &nsim_netdev_ops;
3058c2ecf20Sopenharmony_ci	nsim_ethtool_init(ns);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	err = nsim_udp_tunnels_info_create(nsim_dev, dev);
3088c2ecf20Sopenharmony_ci	if (err)
3098c2ecf20Sopenharmony_ci		goto err_free_netdev;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	rtnl_lock();
3128c2ecf20Sopenharmony_ci	err = nsim_bpf_init(ns);
3138c2ecf20Sopenharmony_ci	if (err)
3148c2ecf20Sopenharmony_ci		goto err_utn_destroy;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	nsim_ipsec_init(ns);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
3198c2ecf20Sopenharmony_ci	if (err)
3208c2ecf20Sopenharmony_ci		goto err_ipsec_teardown;
3218c2ecf20Sopenharmony_ci	rtnl_unlock();
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return ns;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cierr_ipsec_teardown:
3268c2ecf20Sopenharmony_ci	nsim_ipsec_teardown(ns);
3278c2ecf20Sopenharmony_ci	nsim_bpf_uninit(ns);
3288c2ecf20Sopenharmony_cierr_utn_destroy:
3298c2ecf20Sopenharmony_ci	rtnl_unlock();
3308c2ecf20Sopenharmony_ci	nsim_udp_tunnels_info_destroy(dev);
3318c2ecf20Sopenharmony_cierr_free_netdev:
3328c2ecf20Sopenharmony_ci	free_netdev(dev);
3338c2ecf20Sopenharmony_ci	return ERR_PTR(err);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_civoid nsim_destroy(struct netdevsim *ns)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct net_device *dev = ns->netdev;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	rtnl_lock();
3418c2ecf20Sopenharmony_ci	unregister_netdevice(dev);
3428c2ecf20Sopenharmony_ci	nsim_ipsec_teardown(ns);
3438c2ecf20Sopenharmony_ci	nsim_bpf_uninit(ns);
3448c2ecf20Sopenharmony_ci	rtnl_unlock();
3458c2ecf20Sopenharmony_ci	nsim_udp_tunnels_info_destroy(dev);
3468c2ecf20Sopenharmony_ci	free_netdev(dev);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
3508c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "Please use: echo \"[ID] [PORT_COUNT]\" > /sys/bus/netdevsim/new_device");
3538c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic struct rtnl_link_ops nsim_link_ops __read_mostly = {
3578c2ecf20Sopenharmony_ci	.kind		= DRV_NAME,
3588c2ecf20Sopenharmony_ci	.validate	= nsim_validate,
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int __init nsim_module_init(void)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	int err;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	err = nsim_dev_init();
3668c2ecf20Sopenharmony_ci	if (err)
3678c2ecf20Sopenharmony_ci		return err;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	err = nsim_bus_init();
3708c2ecf20Sopenharmony_ci	if (err)
3718c2ecf20Sopenharmony_ci		goto err_dev_exit;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	err = rtnl_link_register(&nsim_link_ops);
3748c2ecf20Sopenharmony_ci	if (err)
3758c2ecf20Sopenharmony_ci		goto err_bus_exit;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cierr_bus_exit:
3808c2ecf20Sopenharmony_ci	nsim_bus_exit();
3818c2ecf20Sopenharmony_cierr_dev_exit:
3828c2ecf20Sopenharmony_ci	nsim_dev_exit();
3838c2ecf20Sopenharmony_ci	return err;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void __exit nsim_module_exit(void)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	rtnl_link_unregister(&nsim_link_ops);
3898c2ecf20Sopenharmony_ci	nsim_bus_exit();
3908c2ecf20Sopenharmony_ci	nsim_dev_exit();
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cimodule_init(nsim_module_init);
3948c2ecf20Sopenharmony_cimodule_exit(nsim_module_exit);
3958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3968c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME);
397