xref: /linux-6.15/drivers/net/netdevsim/netdev.c (revision f394d07b)
183c9e13aSJakub Kicinski /*
283c9e13aSJakub Kicinski  * Copyright (C) 2017 Netronome Systems, Inc.
383c9e13aSJakub Kicinski  *
483c9e13aSJakub Kicinski  * This software is licensed under the GNU General License Version 2,
583c9e13aSJakub Kicinski  * June 1991 as shown in the file COPYING in the top-level directory of this
683c9e13aSJakub Kicinski  * source tree.
783c9e13aSJakub Kicinski  *
883c9e13aSJakub Kicinski  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
983c9e13aSJakub Kicinski  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
1083c9e13aSJakub Kicinski  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1183c9e13aSJakub Kicinski  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
1283c9e13aSJakub Kicinski  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
1383c9e13aSJakub Kicinski  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
1483c9e13aSJakub Kicinski  */
1583c9e13aSJakub Kicinski 
1631d3ad83SJakub Kicinski #include <linux/debugfs.h>
1783c9e13aSJakub Kicinski #include <linux/etherdevice.h>
18*f394d07bSTaehee Yoo #include <linux/ethtool_netlink.h>
1983c9e13aSJakub Kicinski #include <linux/kernel.h>
2083c9e13aSJakub Kicinski #include <linux/module.h>
2183c9e13aSJakub Kicinski #include <linux/netdevice.h>
2283c9e13aSJakub Kicinski #include <linux/slab.h>
23f216306bSJakub Kicinski #include <net/netdev_queues.h>
246917d207SJakub Kicinski #include <net/netdev_rx_queue.h>
251580cbcbSJakub Kicinski #include <net/page_pool/helpers.h>
2683c9e13aSJakub Kicinski #include <net/netlink.h>
27b3ea4164SPaolo Abeni #include <net/net_shaper.h>
2831d3ad83SJakub Kicinski #include <net/pkt_cls.h>
2983c9e13aSJakub Kicinski #include <net/rtnetlink.h>
30424be63aSJakub Kicinski #include <net/udp_tunnel.h>
3183c9e13aSJakub Kicinski 
3283c9e13aSJakub Kicinski #include "netdevsim.h"
3383c9e13aSJakub Kicinski 
346917d207SJakub Kicinski MODULE_IMPORT_NS("NETDEV_INTERNAL");
356917d207SJakub Kicinski 
363762ec05SDavid Wei #define NSIM_RING_SIZE		256
373762ec05SDavid Wei 
383762ec05SDavid Wei static int nsim_napi_rx(struct nsim_rq *rq, struct sk_buff *skb)
393762ec05SDavid Wei {
403762ec05SDavid Wei 	if (skb_queue_len(&rq->skb_queue) > NSIM_RING_SIZE) {
413762ec05SDavid Wei 		dev_kfree_skb_any(skb);
423762ec05SDavid Wei 		return NET_RX_DROP;
433762ec05SDavid Wei 	}
443762ec05SDavid Wei 
453762ec05SDavid Wei 	skb_queue_tail(&rq->skb_queue, skb);
463762ec05SDavid Wei 	return NET_RX_SUCCESS;
473762ec05SDavid Wei }
483762ec05SDavid Wei 
493762ec05SDavid Wei static int nsim_forward_skb(struct net_device *dev, struct sk_buff *skb,
503762ec05SDavid Wei 			    struct nsim_rq *rq)
513762ec05SDavid Wei {
523762ec05SDavid Wei 	return __dev_forward_skb(dev, skb) ?: nsim_napi_rx(rq, skb);
533762ec05SDavid Wei }
543762ec05SDavid Wei 
5583c9e13aSJakub Kicinski static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
5683c9e13aSJakub Kicinski {
5783c9e13aSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
58*f394d07bSTaehee Yoo 	struct ethtool_netdev_state *ethtool;
593762ec05SDavid Wei 	struct net_device *peer_dev;
609eb95228SDavid Wei 	unsigned int len = skb->len;
619eb95228SDavid Wei 	struct netdevsim *peer_ns;
623762ec05SDavid Wei 	struct nsim_rq *rq;
633762ec05SDavid Wei 	int rxq;
6483c9e13aSJakub Kicinski 
659eb95228SDavid Wei 	rcu_read_lock();
667699353dSShannon Nelson 	if (!nsim_ipsec_tx(ns, skb))
679eb95228SDavid Wei 		goto out_drop_free;
687699353dSShannon Nelson 
699eb95228SDavid Wei 	peer_ns = rcu_dereference(ns->peer);
709eb95228SDavid Wei 	if (!peer_ns)
719eb95228SDavid Wei 		goto out_drop_free;
729eb95228SDavid Wei 
733762ec05SDavid Wei 	peer_dev = peer_ns->netdev;
743762ec05SDavid Wei 	rxq = skb_get_queue_mapping(skb);
753762ec05SDavid Wei 	if (rxq >= peer_dev->num_rx_queues)
763762ec05SDavid Wei 		rxq = rxq % peer_dev->num_rx_queues;
77915c82f8SJakub Kicinski 	rq = peer_ns->rq[rxq];
783762ec05SDavid Wei 
79*f394d07bSTaehee Yoo 	ethtool = peer_dev->ethtool;
80*f394d07bSTaehee Yoo 	if (skb_is_nonlinear(skb) &&
81*f394d07bSTaehee Yoo 	    (ethtool->hds_config != ETHTOOL_TCP_DATA_SPLIT_ENABLED ||
82*f394d07bSTaehee Yoo 	     (ethtool->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED &&
83*f394d07bSTaehee Yoo 	      ethtool->hds_thresh > len)))
84*f394d07bSTaehee Yoo 		skb_linearize(skb);
85*f394d07bSTaehee Yoo 
869eb95228SDavid Wei 	skb_tx_timestamp(skb);
873762ec05SDavid Wei 	if (unlikely(nsim_forward_skb(peer_dev, skb, rq) == NET_RX_DROP))
889eb95228SDavid Wei 		goto out_drop_cnt;
899eb95228SDavid Wei 
903762ec05SDavid Wei 	napi_schedule(&rq->napi);
913762ec05SDavid Wei 
929eb95228SDavid Wei 	rcu_read_unlock();
9383c9e13aSJakub Kicinski 	u64_stats_update_begin(&ns->syncp);
9483c9e13aSJakub Kicinski 	ns->tx_packets++;
959eb95228SDavid Wei 	ns->tx_bytes += len;
9683c9e13aSJakub Kicinski 	u64_stats_update_end(&ns->syncp);
979eb95228SDavid Wei 	return NETDEV_TX_OK;
9883c9e13aSJakub Kicinski 
999eb95228SDavid Wei out_drop_free:
10083c9e13aSJakub Kicinski 	dev_kfree_skb(skb);
1019eb95228SDavid Wei out_drop_cnt:
1029eb95228SDavid Wei 	rcu_read_unlock();
1039eb95228SDavid Wei 	u64_stats_update_begin(&ns->syncp);
1049eb95228SDavid Wei 	ns->tx_dropped++;
1059eb95228SDavid Wei 	u64_stats_update_end(&ns->syncp);
10683c9e13aSJakub Kicinski 	return NETDEV_TX_OK;
10783c9e13aSJakub Kicinski }
10883c9e13aSJakub Kicinski 
10983c9e13aSJakub Kicinski static void nsim_set_rx_mode(struct net_device *dev)
11083c9e13aSJakub Kicinski {
11183c9e13aSJakub Kicinski }
11283c9e13aSJakub Kicinski 
11331d3ad83SJakub Kicinski static int nsim_change_mtu(struct net_device *dev, int new_mtu)
11431d3ad83SJakub Kicinski {
11531d3ad83SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
11631d3ad83SJakub Kicinski 
117799e173dSJakub Kicinski 	if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU)
11831d3ad83SJakub Kicinski 		return -EBUSY;
11931d3ad83SJakub Kicinski 
1201eb2cdedSEric Dumazet 	WRITE_ONCE(dev->mtu, new_mtu);
12131d3ad83SJakub Kicinski 
12231d3ad83SJakub Kicinski 	return 0;
12331d3ad83SJakub Kicinski }
12431d3ad83SJakub Kicinski 
12583c9e13aSJakub Kicinski static void
12683c9e13aSJakub Kicinski nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
12783c9e13aSJakub Kicinski {
12883c9e13aSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
12983c9e13aSJakub Kicinski 	unsigned int start;
13083c9e13aSJakub Kicinski 
13183c9e13aSJakub Kicinski 	do {
132068c38adSThomas Gleixner 		start = u64_stats_fetch_begin(&ns->syncp);
13383c9e13aSJakub Kicinski 		stats->tx_bytes = ns->tx_bytes;
13483c9e13aSJakub Kicinski 		stats->tx_packets = ns->tx_packets;
1359eb95228SDavid Wei 		stats->tx_dropped = ns->tx_dropped;
136068c38adSThomas Gleixner 	} while (u64_stats_fetch_retry(&ns->syncp, start));
13783c9e13aSJakub Kicinski }
13883c9e13aSJakub Kicinski 
13931d3ad83SJakub Kicinski static int
14031d3ad83SJakub Kicinski nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
14131d3ad83SJakub Kicinski {
14231d3ad83SJakub Kicinski 	return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv);
14331d3ad83SJakub Kicinski }
14431d3ad83SJakub Kicinski 
14579579220SJakub Kicinski static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
14679579220SJakub Kicinski {
14779579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
1485e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
14979579220SJakub Kicinski 
15079579220SJakub Kicinski 	/* Only refuse multicast addresses, zero address can mean unset/any. */
1515e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev) || is_multicast_ether_addr(mac))
15279579220SJakub Kicinski 		return -EINVAL;
1535e388f3dSJakub Kicinski 	memcpy(nsim_dev->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
15479579220SJakub Kicinski 
15579579220SJakub Kicinski 	return 0;
15679579220SJakub Kicinski }
15779579220SJakub Kicinski 
15879579220SJakub Kicinski static int nsim_set_vf_vlan(struct net_device *dev, int vf,
15979579220SJakub Kicinski 			    u16 vlan, u8 qos, __be16 vlan_proto)
16079579220SJakub Kicinski {
16179579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
1625e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
16379579220SJakub Kicinski 
1645e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev) || vlan > 4095 || qos > 7)
16579579220SJakub Kicinski 		return -EINVAL;
16679579220SJakub Kicinski 
1675e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].vlan = vlan;
1685e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].qos = qos;
1695e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].vlan_proto = vlan_proto;
17079579220SJakub Kicinski 
17179579220SJakub Kicinski 	return 0;
17279579220SJakub Kicinski }
17379579220SJakub Kicinski 
17479579220SJakub Kicinski static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
17579579220SJakub Kicinski {
17679579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
1775e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
17879579220SJakub Kicinski 
179160dc373SDmytro Linkin 	if (nsim_esw_mode_is_switchdev(ns->nsim_dev)) {
180160dc373SDmytro Linkin 		pr_err("Not supported in switchdev mode. Please use devlink API.\n");
181160dc373SDmytro Linkin 		return -EOPNOTSUPP;
182160dc373SDmytro Linkin 	}
183160dc373SDmytro Linkin 
1845e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev))
18579579220SJakub Kicinski 		return -EINVAL;
18679579220SJakub Kicinski 
1875e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].min_tx_rate = min;
1885e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].max_tx_rate = max;
18979579220SJakub Kicinski 
19079579220SJakub Kicinski 	return 0;
19179579220SJakub Kicinski }
19279579220SJakub Kicinski 
19379579220SJakub Kicinski static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
19479579220SJakub Kicinski {
19579579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
1965e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
19779579220SJakub Kicinski 
1985e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev))
19979579220SJakub Kicinski 		return -EINVAL;
2005e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].spoofchk_enabled = val;
20179579220SJakub Kicinski 
20279579220SJakub Kicinski 	return 0;
20379579220SJakub Kicinski }
20479579220SJakub Kicinski 
20579579220SJakub Kicinski static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
20679579220SJakub Kicinski {
20779579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
2085e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
20979579220SJakub Kicinski 
2105e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev))
21179579220SJakub Kicinski 		return -EINVAL;
2125e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].rss_query_enabled = val;
21379579220SJakub Kicinski 
21479579220SJakub Kicinski 	return 0;
21579579220SJakub Kicinski }
21679579220SJakub Kicinski 
21779579220SJakub Kicinski static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val)
21879579220SJakub Kicinski {
21979579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
2205e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
22179579220SJakub Kicinski 
2225e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev))
22379579220SJakub Kicinski 		return -EINVAL;
2245e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].trusted = val;
22579579220SJakub Kicinski 
22679579220SJakub Kicinski 	return 0;
22779579220SJakub Kicinski }
22879579220SJakub Kicinski 
22979579220SJakub Kicinski static int
23079579220SJakub Kicinski nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi)
23179579220SJakub Kicinski {
23279579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
2335e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
23479579220SJakub Kicinski 
2355e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev))
23679579220SJakub Kicinski 		return -EINVAL;
23779579220SJakub Kicinski 
23879579220SJakub Kicinski 	ivi->vf = vf;
2395e388f3dSJakub Kicinski 	ivi->linkstate = nsim_dev->vfconfigs[vf].link_state;
2405e388f3dSJakub Kicinski 	ivi->min_tx_rate = nsim_dev->vfconfigs[vf].min_tx_rate;
2415e388f3dSJakub Kicinski 	ivi->max_tx_rate = nsim_dev->vfconfigs[vf].max_tx_rate;
2425e388f3dSJakub Kicinski 	ivi->vlan = nsim_dev->vfconfigs[vf].vlan;
2435e388f3dSJakub Kicinski 	ivi->vlan_proto = nsim_dev->vfconfigs[vf].vlan_proto;
2445e388f3dSJakub Kicinski 	ivi->qos = nsim_dev->vfconfigs[vf].qos;
2455e388f3dSJakub Kicinski 	memcpy(&ivi->mac, nsim_dev->vfconfigs[vf].vf_mac, ETH_ALEN);
2465e388f3dSJakub Kicinski 	ivi->spoofchk = nsim_dev->vfconfigs[vf].spoofchk_enabled;
2475e388f3dSJakub Kicinski 	ivi->trusted = nsim_dev->vfconfigs[vf].trusted;
2485e388f3dSJakub Kicinski 	ivi->rss_query_en = nsim_dev->vfconfigs[vf].rss_query_enabled;
24979579220SJakub Kicinski 
25079579220SJakub Kicinski 	return 0;
25179579220SJakub Kicinski }
25279579220SJakub Kicinski 
25379579220SJakub Kicinski static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
25479579220SJakub Kicinski {
25579579220SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
2565e388f3dSJakub Kicinski 	struct nsim_dev *nsim_dev = ns->nsim_dev;
25779579220SJakub Kicinski 
2585e388f3dSJakub Kicinski 	if (vf >= nsim_dev_get_vfs(nsim_dev))
25979579220SJakub Kicinski 		return -EINVAL;
26079579220SJakub Kicinski 
26179579220SJakub Kicinski 	switch (state) {
26279579220SJakub Kicinski 	case IFLA_VF_LINK_STATE_AUTO:
26379579220SJakub Kicinski 	case IFLA_VF_LINK_STATE_ENABLE:
26479579220SJakub Kicinski 	case IFLA_VF_LINK_STATE_DISABLE:
26579579220SJakub Kicinski 		break;
26679579220SJakub Kicinski 	default:
26779579220SJakub Kicinski 		return -EINVAL;
26879579220SJakub Kicinski 	}
26979579220SJakub Kicinski 
2705e388f3dSJakub Kicinski 	nsim_dev->vfconfigs[vf].link_state = state;
27179579220SJakub Kicinski 
27279579220SJakub Kicinski 	return 0;
27379579220SJakub Kicinski }
27479579220SJakub Kicinski 
27535da47feSVladimir Oltean static void nsim_taprio_stats(struct tc_taprio_qopt_stats *stats)
27635da47feSVladimir Oltean {
27735da47feSVladimir Oltean 	stats->window_drops = 0;
27835da47feSVladimir Oltean 	stats->tx_overruns = 0;
27935da47feSVladimir Oltean }
28035da47feSVladimir Oltean 
28135da47feSVladimir Oltean static int nsim_setup_tc_taprio(struct net_device *dev,
28235da47feSVladimir Oltean 				struct tc_taprio_qopt_offload *offload)
28335da47feSVladimir Oltean {
28435da47feSVladimir Oltean 	int err = 0;
28535da47feSVladimir Oltean 
28635da47feSVladimir Oltean 	switch (offload->cmd) {
28735da47feSVladimir Oltean 	case TAPRIO_CMD_REPLACE:
28835da47feSVladimir Oltean 	case TAPRIO_CMD_DESTROY:
28935da47feSVladimir Oltean 		break;
29035da47feSVladimir Oltean 	case TAPRIO_CMD_STATS:
29135da47feSVladimir Oltean 		nsim_taprio_stats(&offload->stats);
29235da47feSVladimir Oltean 		break;
29335da47feSVladimir Oltean 	default:
29435da47feSVladimir Oltean 		err = -EOPNOTSUPP;
29535da47feSVladimir Oltean 	}
29635da47feSVladimir Oltean 
29735da47feSVladimir Oltean 	return err;
29835da47feSVladimir Oltean }
29935da47feSVladimir Oltean 
300955bcb6eSPablo Neira Ayuso static LIST_HEAD(nsim_block_cb_list);
301955bcb6eSPablo Neira Ayuso 
30231d3ad83SJakub Kicinski static int
30331d3ad83SJakub Kicinski nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
30431d3ad83SJakub Kicinski {
3054e95bc26SPablo Neira Ayuso 	struct netdevsim *ns = netdev_priv(dev);
3064e95bc26SPablo Neira Ayuso 
30731d3ad83SJakub Kicinski 	switch (type) {
30835da47feSVladimir Oltean 	case TC_SETUP_QDISC_TAPRIO:
30935da47feSVladimir Oltean 		return nsim_setup_tc_taprio(dev, type_data);
31031d3ad83SJakub Kicinski 	case TC_SETUP_BLOCK:
311955bcb6eSPablo Neira Ayuso 		return flow_block_cb_setup_simple(type_data,
312955bcb6eSPablo Neira Ayuso 						  &nsim_block_cb_list,
3134e95bc26SPablo Neira Ayuso 						  nsim_setup_tc_block_cb,
3144e95bc26SPablo Neira Ayuso 						  ns, ns, true);
31531d3ad83SJakub Kicinski 	default:
31631d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
31731d3ad83SJakub Kicinski 	}
31831d3ad83SJakub Kicinski }
31931d3ad83SJakub Kicinski 
32031d3ad83SJakub Kicinski static int
32131d3ad83SJakub Kicinski nsim_set_features(struct net_device *dev, netdev_features_t features)
32231d3ad83SJakub Kicinski {
32331d3ad83SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
32431d3ad83SJakub Kicinski 
32531d3ad83SJakub Kicinski 	if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC))
32631d3ad83SJakub Kicinski 		return nsim_bpf_disable_tc(ns);
32731d3ad83SJakub Kicinski 
32831d3ad83SJakub Kicinski 	return 0;
32931d3ad83SJakub Kicinski }
33031d3ad83SJakub Kicinski 
3318debcf58SDavid Wei static int nsim_get_iflink(const struct net_device *dev)
3328debcf58SDavid Wei {
3338debcf58SDavid Wei 	struct netdevsim *nsim, *peer;
3348debcf58SDavid Wei 	int iflink;
3358debcf58SDavid Wei 
3368debcf58SDavid Wei 	nsim = netdev_priv(dev);
3378debcf58SDavid Wei 
3388debcf58SDavid Wei 	rcu_read_lock();
3398debcf58SDavid Wei 	peer = rcu_dereference(nsim->peer);
3405add2f72SDavid Wei 	iflink = peer ? READ_ONCE(peer->netdev->ifindex) :
3415add2f72SDavid Wei 			READ_ONCE(dev->ifindex);
3428debcf58SDavid Wei 	rcu_read_unlock();
3438debcf58SDavid Wei 
3448debcf58SDavid Wei 	return iflink;
3458debcf58SDavid Wei }
3468debcf58SDavid Wei 
3473762ec05SDavid Wei static int nsim_rcv(struct nsim_rq *rq, int budget)
3483762ec05SDavid Wei {
3493762ec05SDavid Wei 	struct sk_buff *skb;
3503762ec05SDavid Wei 	int i;
3513762ec05SDavid Wei 
3523762ec05SDavid Wei 	for (i = 0; i < budget; i++) {
3533762ec05SDavid Wei 		if (skb_queue_empty(&rq->skb_queue))
3543762ec05SDavid Wei 			break;
3553762ec05SDavid Wei 
3563762ec05SDavid Wei 		skb = skb_dequeue(&rq->skb_queue);
3573762ec05SDavid Wei 		netif_receive_skb(skb);
3583762ec05SDavid Wei 	}
3593762ec05SDavid Wei 
3603762ec05SDavid Wei 	return i;
3613762ec05SDavid Wei }
3623762ec05SDavid Wei 
3633762ec05SDavid Wei static int nsim_poll(struct napi_struct *napi, int budget)
3643762ec05SDavid Wei {
3653762ec05SDavid Wei 	struct nsim_rq *rq = container_of(napi, struct nsim_rq, napi);
3663762ec05SDavid Wei 	int done;
3673762ec05SDavid Wei 
3683762ec05SDavid Wei 	done = nsim_rcv(rq, budget);
3693762ec05SDavid Wei 	napi_complete(napi);
3703762ec05SDavid Wei 
3713762ec05SDavid Wei 	return done;
3723762ec05SDavid Wei }
3733762ec05SDavid Wei 
3745bc8e8dbSJakub Kicinski static int nsim_create_page_pool(struct page_pool **p, struct napi_struct *napi)
3753762ec05SDavid Wei {
3765bc8e8dbSJakub Kicinski 	struct page_pool_params params = {
3773762ec05SDavid Wei 		.order = 0,
3783762ec05SDavid Wei 		.pool_size = NSIM_RING_SIZE,
3793762ec05SDavid Wei 		.nid = NUMA_NO_NODE,
3805bc8e8dbSJakub Kicinski 		.dev = &napi->dev->dev,
3815bc8e8dbSJakub Kicinski 		.napi = napi,
3823762ec05SDavid Wei 		.dma_dir = DMA_BIDIRECTIONAL,
3835bc8e8dbSJakub Kicinski 		.netdev = napi->dev,
3843762ec05SDavid Wei 	};
3855bc8e8dbSJakub Kicinski 	struct page_pool *pool;
3863762ec05SDavid Wei 
3875bc8e8dbSJakub Kicinski 	pool = page_pool_create(&params);
3885bc8e8dbSJakub Kicinski 	if (IS_ERR(pool))
3895bc8e8dbSJakub Kicinski 		return PTR_ERR(pool);
3903762ec05SDavid Wei 
3915bc8e8dbSJakub Kicinski 	*p = pool;
3923762ec05SDavid Wei 	return 0;
3933762ec05SDavid Wei }
3943762ec05SDavid Wei 
3953762ec05SDavid Wei static int nsim_init_napi(struct netdevsim *ns)
3963762ec05SDavid Wei {
3973762ec05SDavid Wei 	struct net_device *dev = ns->netdev;
3983762ec05SDavid Wei 	struct nsim_rq *rq;
3993762ec05SDavid Wei 	int err, i;
4003762ec05SDavid Wei 
4013762ec05SDavid Wei 	for (i = 0; i < dev->num_rx_queues; i++) {
402915c82f8SJakub Kicinski 		rq = ns->rq[i];
4033762ec05SDavid Wei 
40400adf88bSJakub Kicinski 		netif_napi_add_config(dev, &rq->napi, nsim_poll, i);
4053762ec05SDavid Wei 	}
4063762ec05SDavid Wei 
4073762ec05SDavid Wei 	for (i = 0; i < dev->num_rx_queues; i++) {
408915c82f8SJakub Kicinski 		rq = ns->rq[i];
4093762ec05SDavid Wei 
4105bc8e8dbSJakub Kicinski 		err = nsim_create_page_pool(&rq->page_pool, &rq->napi);
4113762ec05SDavid Wei 		if (err)
4123762ec05SDavid Wei 			goto err_pp_destroy;
4133762ec05SDavid Wei 	}
4143762ec05SDavid Wei 
4153762ec05SDavid Wei 	return 0;
4163762ec05SDavid Wei 
4173762ec05SDavid Wei err_pp_destroy:
4183762ec05SDavid Wei 	while (i--) {
419915c82f8SJakub Kicinski 		page_pool_destroy(ns->rq[i]->page_pool);
420915c82f8SJakub Kicinski 		ns->rq[i]->page_pool = NULL;
4213762ec05SDavid Wei 	}
4223762ec05SDavid Wei 
4233762ec05SDavid Wei 	for (i = 0; i < dev->num_rx_queues; i++)
424915c82f8SJakub Kicinski 		__netif_napi_del(&ns->rq[i]->napi);
4253762ec05SDavid Wei 
4263762ec05SDavid Wei 	return err;
4273762ec05SDavid Wei }
4283762ec05SDavid Wei 
4293762ec05SDavid Wei static void nsim_enable_napi(struct netdevsim *ns)
4303762ec05SDavid Wei {
4313762ec05SDavid Wei 	struct net_device *dev = ns->netdev;
4323762ec05SDavid Wei 	int i;
4333762ec05SDavid Wei 
4343762ec05SDavid Wei 	for (i = 0; i < dev->num_rx_queues; i++) {
435915c82f8SJakub Kicinski 		struct nsim_rq *rq = ns->rq[i];
4363762ec05SDavid Wei 
4373762ec05SDavid Wei 		netif_queue_set_napi(dev, i, NETDEV_QUEUE_TYPE_RX, &rq->napi);
4383762ec05SDavid Wei 		napi_enable(&rq->napi);
4393762ec05SDavid Wei 	}
4403762ec05SDavid Wei }
4413762ec05SDavid Wei 
4421580cbcbSJakub Kicinski static int nsim_open(struct net_device *dev)
4431580cbcbSJakub Kicinski {
4441580cbcbSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
4453762ec05SDavid Wei 	int err;
4461580cbcbSJakub Kicinski 
4473762ec05SDavid Wei 	err = nsim_init_napi(ns);
4483762ec05SDavid Wei 	if (err)
4493762ec05SDavid Wei 		return err;
4501580cbcbSJakub Kicinski 
4513762ec05SDavid Wei 	nsim_enable_napi(ns);
4523762ec05SDavid Wei 
4533762ec05SDavid Wei 	return 0;
4543762ec05SDavid Wei }
4553762ec05SDavid Wei 
4563762ec05SDavid Wei static void nsim_del_napi(struct netdevsim *ns)
4573762ec05SDavid Wei {
4583762ec05SDavid Wei 	struct net_device *dev = ns->netdev;
4593762ec05SDavid Wei 	int i;
4603762ec05SDavid Wei 
4613762ec05SDavid Wei 	for (i = 0; i < dev->num_rx_queues; i++) {
462915c82f8SJakub Kicinski 		struct nsim_rq *rq = ns->rq[i];
4633762ec05SDavid Wei 
4643762ec05SDavid Wei 		napi_disable(&rq->napi);
4653762ec05SDavid Wei 		__netif_napi_del(&rq->napi);
4663762ec05SDavid Wei 	}
4673762ec05SDavid Wei 	synchronize_net();
4683762ec05SDavid Wei 
4693762ec05SDavid Wei 	for (i = 0; i < dev->num_rx_queues; i++) {
470915c82f8SJakub Kicinski 		page_pool_destroy(ns->rq[i]->page_pool);
471915c82f8SJakub Kicinski 		ns->rq[i]->page_pool = NULL;
4723762ec05SDavid Wei 	}
4731580cbcbSJakub Kicinski }
4741580cbcbSJakub Kicinski 
4751580cbcbSJakub Kicinski static int nsim_stop(struct net_device *dev)
4761580cbcbSJakub Kicinski {
4771580cbcbSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
4783762ec05SDavid Wei 	struct netdevsim *peer;
4791580cbcbSJakub Kicinski 
4803762ec05SDavid Wei 	netif_carrier_off(dev);
4813762ec05SDavid Wei 	peer = rtnl_dereference(ns->peer);
4823762ec05SDavid Wei 	if (peer)
4833762ec05SDavid Wei 		netif_carrier_off(peer->netdev);
4843762ec05SDavid Wei 
4853762ec05SDavid Wei 	nsim_del_napi(ns);
4861580cbcbSJakub Kicinski 
4871580cbcbSJakub Kicinski 	return 0;
4881580cbcbSJakub Kicinski }
4891580cbcbSJakub Kicinski 
490b3ea4164SPaolo Abeni static int nsim_shaper_set(struct net_shaper_binding *binding,
491b3ea4164SPaolo Abeni 			   const struct net_shaper *shaper,
492b3ea4164SPaolo Abeni 			   struct netlink_ext_ack *extack)
493b3ea4164SPaolo Abeni {
494b3ea4164SPaolo Abeni 	return 0;
495b3ea4164SPaolo Abeni }
496b3ea4164SPaolo Abeni 
497b3ea4164SPaolo Abeni static int nsim_shaper_del(struct net_shaper_binding *binding,
498b3ea4164SPaolo Abeni 			   const struct net_shaper_handle *handle,
499b3ea4164SPaolo Abeni 			   struct netlink_ext_ack *extack)
500b3ea4164SPaolo Abeni {
501b3ea4164SPaolo Abeni 	return 0;
502b3ea4164SPaolo Abeni }
503b3ea4164SPaolo Abeni 
504b3ea4164SPaolo Abeni static int nsim_shaper_group(struct net_shaper_binding *binding,
505b3ea4164SPaolo Abeni 			     int leaves_count,
506b3ea4164SPaolo Abeni 			     const struct net_shaper *leaves,
507b3ea4164SPaolo Abeni 			     const struct net_shaper *root,
508b3ea4164SPaolo Abeni 			     struct netlink_ext_ack *extack)
509b3ea4164SPaolo Abeni {
510b3ea4164SPaolo Abeni 	return 0;
511b3ea4164SPaolo Abeni }
512b3ea4164SPaolo Abeni 
513b3ea4164SPaolo Abeni static void nsim_shaper_cap(struct net_shaper_binding *binding,
514b3ea4164SPaolo Abeni 			    enum net_shaper_scope scope,
515b3ea4164SPaolo Abeni 			    unsigned long *flags)
516b3ea4164SPaolo Abeni {
517b3ea4164SPaolo Abeni 	*flags = ULONG_MAX;
518b3ea4164SPaolo Abeni }
519b3ea4164SPaolo Abeni 
520b3ea4164SPaolo Abeni static const struct net_shaper_ops nsim_shaper_ops = {
521b3ea4164SPaolo Abeni 	.set			= nsim_shaper_set,
522b3ea4164SPaolo Abeni 	.delete			= nsim_shaper_del,
523b3ea4164SPaolo Abeni 	.group			= nsim_shaper_group,
524b3ea4164SPaolo Abeni 	.capabilities		= nsim_shaper_cap,
525b3ea4164SPaolo Abeni };
526b3ea4164SPaolo Abeni 
52783c9e13aSJakub Kicinski static const struct net_device_ops nsim_netdev_ops = {
52883c9e13aSJakub Kicinski 	.ndo_start_xmit		= nsim_start_xmit,
52983c9e13aSJakub Kicinski 	.ndo_set_rx_mode	= nsim_set_rx_mode,
53083c9e13aSJakub Kicinski 	.ndo_set_mac_address	= eth_mac_addr,
53183c9e13aSJakub Kicinski 	.ndo_validate_addr	= eth_validate_addr,
53231d3ad83SJakub Kicinski 	.ndo_change_mtu		= nsim_change_mtu,
53383c9e13aSJakub Kicinski 	.ndo_get_stats64	= nsim_get_stats64,
53479579220SJakub Kicinski 	.ndo_set_vf_mac		= nsim_set_vf_mac,
53579579220SJakub Kicinski 	.ndo_set_vf_vlan	= nsim_set_vf_vlan,
53679579220SJakub Kicinski 	.ndo_set_vf_rate	= nsim_set_vf_rate,
53779579220SJakub Kicinski 	.ndo_set_vf_spoofchk	= nsim_set_vf_spoofchk,
53879579220SJakub Kicinski 	.ndo_set_vf_trust	= nsim_set_vf_trust,
53979579220SJakub Kicinski 	.ndo_get_vf_config	= nsim_get_vf_config,
54079579220SJakub Kicinski 	.ndo_set_vf_link_state	= nsim_set_vf_link_state,
54179579220SJakub Kicinski 	.ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en,
54231d3ad83SJakub Kicinski 	.ndo_setup_tc		= nsim_setup_tc,
54331d3ad83SJakub Kicinski 	.ndo_set_features	= nsim_set_features,
5448debcf58SDavid Wei 	.ndo_get_iflink		= nsim_get_iflink,
54531d3ad83SJakub Kicinski 	.ndo_bpf		= nsim_bpf,
5461580cbcbSJakub Kicinski 	.ndo_open		= nsim_open,
5471580cbcbSJakub Kicinski 	.ndo_stop		= nsim_stop,
548b3ea4164SPaolo Abeni 	.net_shaper_ops		= &nsim_shaper_ops,
54983c9e13aSJakub Kicinski };
55083c9e13aSJakub Kicinski 
55192ba1f29SDmytro Linkin static const struct net_device_ops nsim_vf_netdev_ops = {
55292ba1f29SDmytro Linkin 	.ndo_start_xmit		= nsim_start_xmit,
55392ba1f29SDmytro Linkin 	.ndo_set_rx_mode	= nsim_set_rx_mode,
55492ba1f29SDmytro Linkin 	.ndo_set_mac_address	= eth_mac_addr,
55592ba1f29SDmytro Linkin 	.ndo_validate_addr	= eth_validate_addr,
55692ba1f29SDmytro Linkin 	.ndo_change_mtu		= nsim_change_mtu,
55792ba1f29SDmytro Linkin 	.ndo_get_stats64	= nsim_get_stats64,
55892ba1f29SDmytro Linkin 	.ndo_setup_tc		= nsim_setup_tc,
55992ba1f29SDmytro Linkin 	.ndo_set_features	= nsim_set_features,
56092ba1f29SDmytro Linkin };
56192ba1f29SDmytro Linkin 
562f216306bSJakub Kicinski /* We don't have true per-queue stats, yet, so do some random fakery here.
563f216306bSJakub Kicinski  * Only report stuff for queue 0.
564f216306bSJakub Kicinski  */
565f216306bSJakub Kicinski static void nsim_get_queue_stats_rx(struct net_device *dev, int idx,
566f216306bSJakub Kicinski 				    struct netdev_queue_stats_rx *stats)
567f216306bSJakub Kicinski {
568f216306bSJakub Kicinski 	struct rtnl_link_stats64 rtstats = {};
569f216306bSJakub Kicinski 
570f216306bSJakub Kicinski 	if (!idx)
571f216306bSJakub Kicinski 		nsim_get_stats64(dev, &rtstats);
572f216306bSJakub Kicinski 
573f216306bSJakub Kicinski 	stats->packets = rtstats.rx_packets - !!rtstats.rx_packets;
574f216306bSJakub Kicinski 	stats->bytes = rtstats.rx_bytes;
575f216306bSJakub Kicinski }
576f216306bSJakub Kicinski 
577f216306bSJakub Kicinski static void nsim_get_queue_stats_tx(struct net_device *dev, int idx,
578f216306bSJakub Kicinski 				    struct netdev_queue_stats_tx *stats)
579f216306bSJakub Kicinski {
580f216306bSJakub Kicinski 	struct rtnl_link_stats64 rtstats = {};
581f216306bSJakub Kicinski 
582f216306bSJakub Kicinski 	if (!idx)
583f216306bSJakub Kicinski 		nsim_get_stats64(dev, &rtstats);
584f216306bSJakub Kicinski 
585f216306bSJakub Kicinski 	stats->packets = rtstats.tx_packets - !!rtstats.tx_packets;
586f216306bSJakub Kicinski 	stats->bytes = rtstats.tx_bytes;
587f216306bSJakub Kicinski }
588f216306bSJakub Kicinski 
589f216306bSJakub Kicinski static void nsim_get_base_stats(struct net_device *dev,
590f216306bSJakub Kicinski 				struct netdev_queue_stats_rx *rx,
591f216306bSJakub Kicinski 				struct netdev_queue_stats_tx *tx)
592f216306bSJakub Kicinski {
593f216306bSJakub Kicinski 	struct rtnl_link_stats64 rtstats = {};
594f216306bSJakub Kicinski 
595f216306bSJakub Kicinski 	nsim_get_stats64(dev, &rtstats);
596f216306bSJakub Kicinski 
597f216306bSJakub Kicinski 	rx->packets = !!rtstats.rx_packets;
598f216306bSJakub Kicinski 	rx->bytes = 0;
599f216306bSJakub Kicinski 	tx->packets = !!rtstats.tx_packets;
600f216306bSJakub Kicinski 	tx->bytes = 0;
601f216306bSJakub Kicinski }
602f216306bSJakub Kicinski 
603f216306bSJakub Kicinski static const struct netdev_stat_ops nsim_stat_ops = {
604f216306bSJakub Kicinski 	.get_queue_stats_tx	= nsim_get_queue_stats_tx,
605f216306bSJakub Kicinski 	.get_queue_stats_rx	= nsim_get_queue_stats_rx,
606f216306bSJakub Kicinski 	.get_base_stats		= nsim_get_base_stats,
607f216306bSJakub Kicinski };
608f216306bSJakub Kicinski 
609a565dd04SJakub Kicinski static struct nsim_rq *nsim_queue_alloc(void)
610a565dd04SJakub Kicinski {
611a565dd04SJakub Kicinski 	struct nsim_rq *rq;
612a565dd04SJakub Kicinski 
613a565dd04SJakub Kicinski 	rq = kzalloc(sizeof(*rq), GFP_KERNEL_ACCOUNT);
614a565dd04SJakub Kicinski 	if (!rq)
615a565dd04SJakub Kicinski 		return NULL;
616a565dd04SJakub Kicinski 
617a565dd04SJakub Kicinski 	skb_queue_head_init(&rq->skb_queue);
618a565dd04SJakub Kicinski 	return rq;
619a565dd04SJakub Kicinski }
620a565dd04SJakub Kicinski 
621a565dd04SJakub Kicinski static void nsim_queue_free(struct nsim_rq *rq)
622a565dd04SJakub Kicinski {
623a565dd04SJakub Kicinski 	skb_queue_purge_reason(&rq->skb_queue, SKB_DROP_REASON_QUEUE_PURGE);
624a565dd04SJakub Kicinski 	kfree(rq);
625a565dd04SJakub Kicinski }
626a565dd04SJakub Kicinski 
6275bc8e8dbSJakub Kicinski /* Queue reset mode is controlled by ns->rq_reset_mode.
6285bc8e8dbSJakub Kicinski  * - normal - new NAPI new pool (old NAPI enabled when new added)
6295bc8e8dbSJakub Kicinski  * - mode 1 - allocate new pool (NAPI is only disabled / enabled)
6305bc8e8dbSJakub Kicinski  * - mode 2 - new NAPI new pool (old NAPI removed before new added)
6315bc8e8dbSJakub Kicinski  * - mode 3 - new NAPI new pool (old NAPI disabled when new added)
6325bc8e8dbSJakub Kicinski  */
6335bc8e8dbSJakub Kicinski struct nsim_queue_mem {
6345bc8e8dbSJakub Kicinski 	struct nsim_rq *rq;
6355bc8e8dbSJakub Kicinski 	struct page_pool *pp;
6365bc8e8dbSJakub Kicinski };
6375bc8e8dbSJakub Kicinski 
6385bc8e8dbSJakub Kicinski static int
6395bc8e8dbSJakub Kicinski nsim_queue_mem_alloc(struct net_device *dev, void *per_queue_mem, int idx)
6405bc8e8dbSJakub Kicinski {
6415bc8e8dbSJakub Kicinski 	struct nsim_queue_mem *qmem = per_queue_mem;
6425bc8e8dbSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
6435bc8e8dbSJakub Kicinski 	int err;
6445bc8e8dbSJakub Kicinski 
6455bc8e8dbSJakub Kicinski 	if (ns->rq_reset_mode > 3)
6465bc8e8dbSJakub Kicinski 		return -EINVAL;
6475bc8e8dbSJakub Kicinski 
6485bc8e8dbSJakub Kicinski 	if (ns->rq_reset_mode == 1)
6495bc8e8dbSJakub Kicinski 		return nsim_create_page_pool(&qmem->pp, &ns->rq[idx]->napi);
6505bc8e8dbSJakub Kicinski 
6515bc8e8dbSJakub Kicinski 	qmem->rq = nsim_queue_alloc();
6525bc8e8dbSJakub Kicinski 	if (!qmem->rq)
6535bc8e8dbSJakub Kicinski 		return -ENOMEM;
6545bc8e8dbSJakub Kicinski 
6555bc8e8dbSJakub Kicinski 	err = nsim_create_page_pool(&qmem->rq->page_pool, &qmem->rq->napi);
6565bc8e8dbSJakub Kicinski 	if (err)
6575bc8e8dbSJakub Kicinski 		goto err_free;
6585bc8e8dbSJakub Kicinski 
6595bc8e8dbSJakub Kicinski 	if (!ns->rq_reset_mode)
6605bc8e8dbSJakub Kicinski 		netif_napi_add_config(dev, &qmem->rq->napi, nsim_poll, idx);
6615bc8e8dbSJakub Kicinski 
6625bc8e8dbSJakub Kicinski 	return 0;
6635bc8e8dbSJakub Kicinski 
6645bc8e8dbSJakub Kicinski err_free:
6655bc8e8dbSJakub Kicinski 	nsim_queue_free(qmem->rq);
6665bc8e8dbSJakub Kicinski 	return err;
6675bc8e8dbSJakub Kicinski }
6685bc8e8dbSJakub Kicinski 
6695bc8e8dbSJakub Kicinski static void nsim_queue_mem_free(struct net_device *dev, void *per_queue_mem)
6705bc8e8dbSJakub Kicinski {
6715bc8e8dbSJakub Kicinski 	struct nsim_queue_mem *qmem = per_queue_mem;
6725bc8e8dbSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
6735bc8e8dbSJakub Kicinski 
6745bc8e8dbSJakub Kicinski 	page_pool_destroy(qmem->pp);
6755bc8e8dbSJakub Kicinski 	if (qmem->rq) {
6765bc8e8dbSJakub Kicinski 		if (!ns->rq_reset_mode)
6775bc8e8dbSJakub Kicinski 			netif_napi_del(&qmem->rq->napi);
6785bc8e8dbSJakub Kicinski 		page_pool_destroy(qmem->rq->page_pool);
6795bc8e8dbSJakub Kicinski 		nsim_queue_free(qmem->rq);
6805bc8e8dbSJakub Kicinski 	}
6815bc8e8dbSJakub Kicinski }
6825bc8e8dbSJakub Kicinski 
6835bc8e8dbSJakub Kicinski static int
6845bc8e8dbSJakub Kicinski nsim_queue_start(struct net_device *dev, void *per_queue_mem, int idx)
6855bc8e8dbSJakub Kicinski {
6865bc8e8dbSJakub Kicinski 	struct nsim_queue_mem *qmem = per_queue_mem;
6875bc8e8dbSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
6885bc8e8dbSJakub Kicinski 
6895bc8e8dbSJakub Kicinski 	if (ns->rq_reset_mode == 1) {
6905bc8e8dbSJakub Kicinski 		ns->rq[idx]->page_pool = qmem->pp;
6915bc8e8dbSJakub Kicinski 		napi_enable(&ns->rq[idx]->napi);
6925bc8e8dbSJakub Kicinski 		return 0;
6935bc8e8dbSJakub Kicinski 	}
6945bc8e8dbSJakub Kicinski 
6955bc8e8dbSJakub Kicinski 	/* netif_napi_add()/_del() should normally be called from alloc/free,
6965bc8e8dbSJakub Kicinski 	 * here we want to test various call orders.
6975bc8e8dbSJakub Kicinski 	 */
6985bc8e8dbSJakub Kicinski 	if (ns->rq_reset_mode == 2) {
6995bc8e8dbSJakub Kicinski 		netif_napi_del(&ns->rq[idx]->napi);
7005bc8e8dbSJakub Kicinski 		netif_napi_add_config(dev, &qmem->rq->napi, nsim_poll, idx);
7015bc8e8dbSJakub Kicinski 	} else if (ns->rq_reset_mode == 3) {
7025bc8e8dbSJakub Kicinski 		netif_napi_add_config(dev, &qmem->rq->napi, nsim_poll, idx);
7035bc8e8dbSJakub Kicinski 		netif_napi_del(&ns->rq[idx]->napi);
7045bc8e8dbSJakub Kicinski 	}
7055bc8e8dbSJakub Kicinski 
7065bc8e8dbSJakub Kicinski 	ns->rq[idx] = qmem->rq;
7075bc8e8dbSJakub Kicinski 	napi_enable(&ns->rq[idx]->napi);
7085bc8e8dbSJakub Kicinski 
7095bc8e8dbSJakub Kicinski 	return 0;
7105bc8e8dbSJakub Kicinski }
7115bc8e8dbSJakub Kicinski 
7125bc8e8dbSJakub Kicinski static int nsim_queue_stop(struct net_device *dev, void *per_queue_mem, int idx)
7135bc8e8dbSJakub Kicinski {
7145bc8e8dbSJakub Kicinski 	struct nsim_queue_mem *qmem = per_queue_mem;
7155bc8e8dbSJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
7165bc8e8dbSJakub Kicinski 
7175bc8e8dbSJakub Kicinski 	napi_disable(&ns->rq[idx]->napi);
7185bc8e8dbSJakub Kicinski 
7195bc8e8dbSJakub Kicinski 	if (ns->rq_reset_mode == 1) {
7205bc8e8dbSJakub Kicinski 		qmem->pp = ns->rq[idx]->page_pool;
7215bc8e8dbSJakub Kicinski 		page_pool_disable_direct_recycling(qmem->pp);
7225bc8e8dbSJakub Kicinski 	} else {
7235bc8e8dbSJakub Kicinski 		qmem->rq = ns->rq[idx];
7245bc8e8dbSJakub Kicinski 	}
7255bc8e8dbSJakub Kicinski 
7265bc8e8dbSJakub Kicinski 	return 0;
7275bc8e8dbSJakub Kicinski }
7285bc8e8dbSJakub Kicinski 
7295bc8e8dbSJakub Kicinski static const struct netdev_queue_mgmt_ops nsim_queue_mgmt_ops = {
7305bc8e8dbSJakub Kicinski 	.ndo_queue_mem_size	= sizeof(struct nsim_queue_mem),
7315bc8e8dbSJakub Kicinski 	.ndo_queue_mem_alloc	= nsim_queue_mem_alloc,
7325bc8e8dbSJakub Kicinski 	.ndo_queue_mem_free	= nsim_queue_mem_free,
7335bc8e8dbSJakub Kicinski 	.ndo_queue_start	= nsim_queue_start,
7345bc8e8dbSJakub Kicinski 	.ndo_queue_stop		= nsim_queue_stop,
7355bc8e8dbSJakub Kicinski };
7365bc8e8dbSJakub Kicinski 
7371580cbcbSJakub Kicinski static ssize_t
7386917d207SJakub Kicinski nsim_qreset_write(struct file *file, const char __user *data,
7396917d207SJakub Kicinski 		  size_t count, loff_t *ppos)
7406917d207SJakub Kicinski {
7416917d207SJakub Kicinski 	struct netdevsim *ns = file->private_data;
7426917d207SJakub Kicinski 	unsigned int queue, mode;
7436917d207SJakub Kicinski 	char buf[32];
7446917d207SJakub Kicinski 	ssize_t ret;
7456917d207SJakub Kicinski 
7466917d207SJakub Kicinski 	if (count >= sizeof(buf))
7476917d207SJakub Kicinski 		return -EINVAL;
7486917d207SJakub Kicinski 	if (copy_from_user(buf, data, count))
7496917d207SJakub Kicinski 		return -EFAULT;
7506917d207SJakub Kicinski 	buf[count] = '\0';
7516917d207SJakub Kicinski 
7526917d207SJakub Kicinski 	ret = sscanf(buf, "%u %u", &queue, &mode);
7536917d207SJakub Kicinski 	if (ret != 2)
7546917d207SJakub Kicinski 		return -EINVAL;
7556917d207SJakub Kicinski 
7566917d207SJakub Kicinski 	rtnl_lock();
7576917d207SJakub Kicinski 	if (!netif_running(ns->netdev)) {
7586917d207SJakub Kicinski 		ret = -ENETDOWN;
7596917d207SJakub Kicinski 		goto exit_unlock;
7606917d207SJakub Kicinski 	}
7616917d207SJakub Kicinski 
7626917d207SJakub Kicinski 	if (queue >= ns->netdev->real_num_rx_queues) {
7636917d207SJakub Kicinski 		ret = -EINVAL;
7646917d207SJakub Kicinski 		goto exit_unlock;
7656917d207SJakub Kicinski 	}
7666917d207SJakub Kicinski 
7676917d207SJakub Kicinski 	ns->rq_reset_mode = mode;
7686917d207SJakub Kicinski 	ret = netdev_rx_queue_restart(ns->netdev, queue);
7696917d207SJakub Kicinski 	ns->rq_reset_mode = 0;
7706917d207SJakub Kicinski 	if (ret)
7716917d207SJakub Kicinski 		goto exit_unlock;
7726917d207SJakub Kicinski 
7736917d207SJakub Kicinski 	ret = count;
7746917d207SJakub Kicinski exit_unlock:
7756917d207SJakub Kicinski 	rtnl_unlock();
7766917d207SJakub Kicinski 	return ret;
7776917d207SJakub Kicinski }
7786917d207SJakub Kicinski 
7796917d207SJakub Kicinski static const struct file_operations nsim_qreset_fops = {
7806917d207SJakub Kicinski 	.open = simple_open,
7816917d207SJakub Kicinski 	.write = nsim_qreset_write,
7826917d207SJakub Kicinski 	.owner = THIS_MODULE,
7836917d207SJakub Kicinski };
7846917d207SJakub Kicinski 
7856917d207SJakub Kicinski static ssize_t
7861580cbcbSJakub Kicinski nsim_pp_hold_read(struct file *file, char __user *data,
7871580cbcbSJakub Kicinski 		  size_t count, loff_t *ppos)
7881580cbcbSJakub Kicinski {
7891580cbcbSJakub Kicinski 	struct netdevsim *ns = file->private_data;
7901580cbcbSJakub Kicinski 	char buf[3] = "n\n";
7911580cbcbSJakub Kicinski 
7921580cbcbSJakub Kicinski 	if (ns->page)
7931580cbcbSJakub Kicinski 		buf[0] = 'y';
7941580cbcbSJakub Kicinski 
7951580cbcbSJakub Kicinski 	return simple_read_from_buffer(data, count, ppos, buf, 2);
7961580cbcbSJakub Kicinski }
7971580cbcbSJakub Kicinski 
7981580cbcbSJakub Kicinski static ssize_t
7991580cbcbSJakub Kicinski nsim_pp_hold_write(struct file *file, const char __user *data,
8001580cbcbSJakub Kicinski 		   size_t count, loff_t *ppos)
8011580cbcbSJakub Kicinski {
8021580cbcbSJakub Kicinski 	struct netdevsim *ns = file->private_data;
8031580cbcbSJakub Kicinski 	ssize_t ret;
8041580cbcbSJakub Kicinski 	bool val;
8051580cbcbSJakub Kicinski 
8061580cbcbSJakub Kicinski 	ret = kstrtobool_from_user(data, count, &val);
8071580cbcbSJakub Kicinski 	if (ret)
8081580cbcbSJakub Kicinski 		return ret;
8091580cbcbSJakub Kicinski 
8101580cbcbSJakub Kicinski 	rtnl_lock();
8111580cbcbSJakub Kicinski 	ret = count;
8121580cbcbSJakub Kicinski 	if (val == !!ns->page)
8131580cbcbSJakub Kicinski 		goto exit;
8141580cbcbSJakub Kicinski 
8151580cbcbSJakub Kicinski 	if (!netif_running(ns->netdev) && val) {
8161580cbcbSJakub Kicinski 		ret = -ENETDOWN;
8171580cbcbSJakub Kicinski 	} else if (val) {
818915c82f8SJakub Kicinski 		ns->page = page_pool_dev_alloc_pages(ns->rq[0]->page_pool);
8191580cbcbSJakub Kicinski 		if (!ns->page)
8201580cbcbSJakub Kicinski 			ret = -ENOMEM;
8211580cbcbSJakub Kicinski 	} else {
8221580cbcbSJakub Kicinski 		page_pool_put_full_page(ns->page->pp, ns->page, false);
8231580cbcbSJakub Kicinski 		ns->page = NULL;
8241580cbcbSJakub Kicinski 	}
8251580cbcbSJakub Kicinski 
8261580cbcbSJakub Kicinski exit:
827b9b8301dSEric Dumazet 	rtnl_unlock();
828b9b8301dSEric Dumazet 	return ret;
8291580cbcbSJakub Kicinski }
8301580cbcbSJakub Kicinski 
8311580cbcbSJakub Kicinski static const struct file_operations nsim_pp_hold_fops = {
8321580cbcbSJakub Kicinski 	.open = simple_open,
8331580cbcbSJakub Kicinski 	.read = nsim_pp_hold_read,
8341580cbcbSJakub Kicinski 	.write = nsim_pp_hold_write,
8351580cbcbSJakub Kicinski 	.llseek = generic_file_llseek,
8361580cbcbSJakub Kicinski 	.owner = THIS_MODULE,
8371580cbcbSJakub Kicinski };
8381580cbcbSJakub Kicinski 
83983c9e13aSJakub Kicinski static void nsim_setup(struct net_device *dev)
84083c9e13aSJakub Kicinski {
84183c9e13aSJakub Kicinski 	ether_setup(dev);
84283c9e13aSJakub Kicinski 	eth_hw_addr_random(dev);
84383c9e13aSJakub Kicinski 
84483c9e13aSJakub Kicinski 	dev->tx_queue_len = 0;
84583c9e13aSJakub Kicinski 	dev->flags &= ~IFF_MULTICAST;
84683c9e13aSJakub Kicinski 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
84783c9e13aSJakub Kicinski 			   IFF_NO_QUEUE;
84883c9e13aSJakub Kicinski 	dev->features |= NETIF_F_HIGHDMA |
84983c9e13aSJakub Kicinski 			 NETIF_F_SG |
85083c9e13aSJakub Kicinski 			 NETIF_F_FRAGLIST |
85183c9e13aSJakub Kicinski 			 NETIF_F_HW_CSUM |
85283c9e13aSJakub Kicinski 			 NETIF_F_TSO;
853494bd83bSSabrina Dubroca 	dev->hw_features |= NETIF_F_HW_TC |
854494bd83bSSabrina Dubroca 			    NETIF_F_SG |
855494bd83bSSabrina Dubroca 			    NETIF_F_FRAGLIST |
856494bd83bSSabrina Dubroca 			    NETIF_F_HW_CSUM |
857494bd83bSSabrina Dubroca 			    NETIF_F_TSO;
85883c9e13aSJakub Kicinski 	dev->max_mtu = ETH_MAX_MTU;
85966c0e13aSMarek Majtyka 	dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD;
86083c9e13aSJakub Kicinski }
86183c9e13aSJakub Kicinski 
8623762ec05SDavid Wei static int nsim_queue_init(struct netdevsim *ns)
8633762ec05SDavid Wei {
8643762ec05SDavid Wei 	struct net_device *dev = ns->netdev;
8653762ec05SDavid Wei 	int i;
8663762ec05SDavid Wei 
867915c82f8SJakub Kicinski 	ns->rq = kcalloc(dev->num_rx_queues, sizeof(*ns->rq),
868915c82f8SJakub Kicinski 			 GFP_KERNEL_ACCOUNT);
8693762ec05SDavid Wei 	if (!ns->rq)
8703762ec05SDavid Wei 		return -ENOMEM;
8713762ec05SDavid Wei 
872915c82f8SJakub Kicinski 	for (i = 0; i < dev->num_rx_queues; i++) {
873a565dd04SJakub Kicinski 		ns->rq[i] = nsim_queue_alloc();
874915c82f8SJakub Kicinski 		if (!ns->rq[i])
875915c82f8SJakub Kicinski 			goto err_free_prev;
876915c82f8SJakub Kicinski 	}
8773762ec05SDavid Wei 
8783762ec05SDavid Wei 	return 0;
879915c82f8SJakub Kicinski 
880915c82f8SJakub Kicinski err_free_prev:
881915c82f8SJakub Kicinski 	while (i--)
882915c82f8SJakub Kicinski 		kfree(ns->rq[i]);
883915c82f8SJakub Kicinski 	kfree(ns->rq);
884915c82f8SJakub Kicinski 	return -ENOMEM;
8853762ec05SDavid Wei }
8863762ec05SDavid Wei 
887a565dd04SJakub Kicinski static void nsim_queue_uninit(struct netdevsim *ns)
8883762ec05SDavid Wei {
8893762ec05SDavid Wei 	struct net_device *dev = ns->netdev;
8903762ec05SDavid Wei 	int i;
8913762ec05SDavid Wei 
892a565dd04SJakub Kicinski 	for (i = 0; i < dev->num_rx_queues; i++)
893a565dd04SJakub Kicinski 		nsim_queue_free(ns->rq[i]);
8943762ec05SDavid Wei 
895915c82f8SJakub Kicinski 	kfree(ns->rq);
8963762ec05SDavid Wei 	ns->rq = NULL;
8973762ec05SDavid Wei }
8983762ec05SDavid Wei 
89992ba1f29SDmytro Linkin static int nsim_init_netdevsim(struct netdevsim *ns)
90092ba1f29SDmytro Linkin {
901b63e78fcSVladimir Oltean 	struct mock_phc *phc;
90292ba1f29SDmytro Linkin 	int err;
90392ba1f29SDmytro Linkin 
904b63e78fcSVladimir Oltean 	phc = mock_phc_create(&ns->nsim_bus_dev->dev);
905b63e78fcSVladimir Oltean 	if (IS_ERR(phc))
906b63e78fcSVladimir Oltean 		return PTR_ERR(phc);
907b63e78fcSVladimir Oltean 
908b63e78fcSVladimir Oltean 	ns->phc = phc;
90992ba1f29SDmytro Linkin 	ns->netdev->netdev_ops = &nsim_netdev_ops;
910f216306bSJakub Kicinski 	ns->netdev->stat_ops = &nsim_stat_ops;
9115bc8e8dbSJakub Kicinski 	ns->netdev->queue_mgmt_ops = &nsim_queue_mgmt_ops;
91292ba1f29SDmytro Linkin 
91392ba1f29SDmytro Linkin 	err = nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev);
91492ba1f29SDmytro Linkin 	if (err)
915b63e78fcSVladimir Oltean 		goto err_phc_destroy;
91692ba1f29SDmytro Linkin 
91792ba1f29SDmytro Linkin 	rtnl_lock();
9183762ec05SDavid Wei 	err = nsim_queue_init(ns);
91992ba1f29SDmytro Linkin 	if (err)
92092ba1f29SDmytro Linkin 		goto err_utn_destroy;
92192ba1f29SDmytro Linkin 
9223762ec05SDavid Wei 	err = nsim_bpf_init(ns);
9233762ec05SDavid Wei 	if (err)
9243762ec05SDavid Wei 		goto err_rq_destroy;
9253762ec05SDavid Wei 
92602b34d03SSabrina Dubroca 	nsim_macsec_init(ns);
92792ba1f29SDmytro Linkin 	nsim_ipsec_init(ns);
92892ba1f29SDmytro Linkin 
92992ba1f29SDmytro Linkin 	err = register_netdevice(ns->netdev);
93092ba1f29SDmytro Linkin 	if (err)
93192ba1f29SDmytro Linkin 		goto err_ipsec_teardown;
93292ba1f29SDmytro Linkin 	rtnl_unlock();
93392ba1f29SDmytro Linkin 	return 0;
93492ba1f29SDmytro Linkin 
93592ba1f29SDmytro Linkin err_ipsec_teardown:
93692ba1f29SDmytro Linkin 	nsim_ipsec_teardown(ns);
93702b34d03SSabrina Dubroca 	nsim_macsec_teardown(ns);
93892ba1f29SDmytro Linkin 	nsim_bpf_uninit(ns);
9393762ec05SDavid Wei err_rq_destroy:
940a565dd04SJakub Kicinski 	nsim_queue_uninit(ns);
94192ba1f29SDmytro Linkin err_utn_destroy:
94292ba1f29SDmytro Linkin 	rtnl_unlock();
94392ba1f29SDmytro Linkin 	nsim_udp_tunnels_info_destroy(ns->netdev);
944b63e78fcSVladimir Oltean err_phc_destroy:
945b63e78fcSVladimir Oltean 	mock_phc_destroy(ns->phc);
94692ba1f29SDmytro Linkin 	return err;
94792ba1f29SDmytro Linkin }
94892ba1f29SDmytro Linkin 
94992ba1f29SDmytro Linkin static int nsim_init_netdevsim_vf(struct netdevsim *ns)
95092ba1f29SDmytro Linkin {
95192ba1f29SDmytro Linkin 	int err;
95292ba1f29SDmytro Linkin 
95392ba1f29SDmytro Linkin 	ns->netdev->netdev_ops = &nsim_vf_netdev_ops;
95492ba1f29SDmytro Linkin 	rtnl_lock();
95592ba1f29SDmytro Linkin 	err = register_netdevice(ns->netdev);
95692ba1f29SDmytro Linkin 	rtnl_unlock();
95792ba1f29SDmytro Linkin 	return err;
95892ba1f29SDmytro Linkin }
95992ba1f29SDmytro Linkin 
960ea937f77SJakub Kicinski static void nsim_exit_netdevsim(struct netdevsim *ns)
961ea937f77SJakub Kicinski {
962ea937f77SJakub Kicinski 	nsim_udp_tunnels_info_destroy(ns->netdev);
963ea937f77SJakub Kicinski 	mock_phc_destroy(ns->phc);
964ea937f77SJakub Kicinski }
965ea937f77SJakub Kicinski 
966e05b2d14SJiri Pirko struct netdevsim *
967e05b2d14SJiri Pirko nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port)
96883c9e13aSJakub Kicinski {
969e05b2d14SJiri Pirko 	struct net_device *dev;
970e05b2d14SJiri Pirko 	struct netdevsim *ns;
971af9095f0SJiri Pirko 	int err;
972eeeaaf18SJakub Kicinski 
973d4861fc6SPeilin Ye 	dev = alloc_netdev_mq(sizeof(*ns), "eth%d", NET_NAME_UNKNOWN, nsim_setup,
974d4861fc6SPeilin Ye 			      nsim_dev->nsim_bus_dev->num_queues);
975e05b2d14SJiri Pirko 	if (!dev)
976e05b2d14SJiri Pirko 		return ERR_PTR(-ENOMEM);
977e05b2d14SJiri Pirko 
97890d29913SJiri Pirko 	dev_net_set(dev, nsim_dev_net(nsim_dev));
979e05b2d14SJiri Pirko 	ns = netdev_priv(dev);
9808320d145SJiri Pirko 	ns->netdev = dev;
981863a42b2SHillf Danton 	u64_stats_init(&ns->syncp);
982e05b2d14SJiri Pirko 	ns->nsim_dev = nsim_dev;
983e05b2d14SJiri Pirko 	ns->nsim_dev_port = nsim_dev_port;
984e05b2d14SJiri Pirko 	ns->nsim_bus_dev = nsim_dev->nsim_bus_dev;
98540e4fe4cSJiri Pirko 	SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev);
986ac73d4bfSJiri Pirko 	SET_NETDEV_DEVLINK_PORT(dev, &nsim_dev_port->devlink_port);
987ff1f7c17SJakub Kicinski 	nsim_ethtool_init(ns);
98892ba1f29SDmytro Linkin 	if (nsim_dev_port_is_pf(nsim_dev_port))
98992ba1f29SDmytro Linkin 		err = nsim_init_netdevsim(ns);
99092ba1f29SDmytro Linkin 	else
99192ba1f29SDmytro Linkin 		err = nsim_init_netdevsim_vf(ns);
992424be63aSJakub Kicinski 	if (err)
993424be63aSJakub Kicinski 		goto err_free_netdev;
9941580cbcbSJakub Kicinski 
9951580cbcbSJakub Kicinski 	ns->pp_dfs = debugfs_create_file("pp_hold", 0600, nsim_dev_port->ddir,
9961580cbcbSJakub Kicinski 					 ns, &nsim_pp_hold_fops);
9976917d207SJakub Kicinski 	ns->qr_dfs = debugfs_create_file("queue_reset", 0200,
9986917d207SJakub Kicinski 					 nsim_dev_port->ddir, ns,
9996917d207SJakub Kicinski 					 &nsim_qreset_fops);
10001580cbcbSJakub Kicinski 
1001e05b2d14SJiri Pirko 	return ns;
1002e05b2d14SJiri Pirko 
1003e05b2d14SJiri Pirko err_free_netdev:
1004e05b2d14SJiri Pirko 	free_netdev(dev);
1005e05b2d14SJiri Pirko 	return ERR_PTR(err);
1006e05b2d14SJiri Pirko }
1007e05b2d14SJiri Pirko 
1008e05b2d14SJiri Pirko void nsim_destroy(struct netdevsim *ns)
1009e05b2d14SJiri Pirko {
1010e05b2d14SJiri Pirko 	struct net_device *dev = ns->netdev;
1011f532957dSDavid Wei 	struct netdevsim *peer;
1012e05b2d14SJiri Pirko 
10136917d207SJakub Kicinski 	debugfs_remove(ns->qr_dfs);
10141580cbcbSJakub Kicinski 	debugfs_remove(ns->pp_dfs);
10151580cbcbSJakub Kicinski 
1016e05b2d14SJiri Pirko 	rtnl_lock();
1017f532957dSDavid Wei 	peer = rtnl_dereference(ns->peer);
1018f532957dSDavid Wei 	if (peer)
1019f532957dSDavid Wei 		RCU_INIT_POINTER(peer->peer, NULL);
1020f532957dSDavid Wei 	RCU_INIT_POINTER(ns->peer, NULL);
1021e05b2d14SJiri Pirko 	unregister_netdevice(dev);
102292ba1f29SDmytro Linkin 	if (nsim_dev_port_is_pf(ns->nsim_dev_port)) {
102302b34d03SSabrina Dubroca 		nsim_macsec_teardown(ns);
1024e05b2d14SJiri Pirko 		nsim_ipsec_teardown(ns);
1025e05b2d14SJiri Pirko 		nsim_bpf_uninit(ns);
1026a565dd04SJakub Kicinski 		nsim_queue_uninit(ns);
102792ba1f29SDmytro Linkin 	}
1028e05b2d14SJiri Pirko 	rtnl_unlock();
102992ba1f29SDmytro Linkin 	if (nsim_dev_port_is_pf(ns->nsim_dev_port))
1030ea937f77SJakub Kicinski 		nsim_exit_netdevsim(ns);
10311580cbcbSJakub Kicinski 
10321580cbcbSJakub Kicinski 	/* Put this intentionally late to exercise the orphaning path */
10331580cbcbSJakub Kicinski 	if (ns->page) {
10341580cbcbSJakub Kicinski 		page_pool_put_full_page(ns->page->pp, ns->page, false);
10351580cbcbSJakub Kicinski 		ns->page = NULL;
10361580cbcbSJakub Kicinski 	}
10371580cbcbSJakub Kicinski 
1038e05b2d14SJiri Pirko 	free_netdev(dev);
1039e05b2d14SJiri Pirko }
1040e05b2d14SJiri Pirko 
1041f532957dSDavid Wei bool netdev_is_nsim(struct net_device *dev)
1042f532957dSDavid Wei {
1043f532957dSDavid Wei 	return dev->netdev_ops == &nsim_netdev_ops;
1044f532957dSDavid Wei }
1045f532957dSDavid Wei 
1046e05b2d14SJiri Pirko static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
1047e05b2d14SJiri Pirko 			 struct netlink_ext_ack *extack)
1048e05b2d14SJiri Pirko {
1049d4861fc6SPeilin Ye 	NL_SET_ERR_MSG_MOD(extack,
1050d4861fc6SPeilin Ye 			   "Please use: echo \"[ID] [PORT_COUNT] [NUM_QUEUES]\" > /sys/bus/netdevsim/new_device");
1051e05b2d14SJiri Pirko 	return -EOPNOTSUPP;
1052eeeaaf18SJakub Kicinski }
1053eeeaaf18SJakub Kicinski 
105483c9e13aSJakub Kicinski static struct rtnl_link_ops nsim_link_ops __read_mostly = {
105583c9e13aSJakub Kicinski 	.kind		= DRV_NAME,
105683c9e13aSJakub Kicinski 	.validate	= nsim_validate,
105783c9e13aSJakub Kicinski };
105883c9e13aSJakub Kicinski 
105983c9e13aSJakub Kicinski static int __init nsim_module_init(void)
106083c9e13aSJakub Kicinski {
106131d3ad83SJakub Kicinski 	int err;
106231d3ad83SJakub Kicinski 
1063d514f41eSJiri Pirko 	err = nsim_dev_init();
1064af9095f0SJiri Pirko 	if (err)
1065ab1d0cc0SJiri Pirko 		return err;
1066eeeaaf18SJakub Kicinski 
1067925f5afeSJiri Pirko 	err = nsim_bus_init();
106831d3ad83SJakub Kicinski 	if (err)
1069d514f41eSJiri Pirko 		goto err_dev_exit;
107031d3ad83SJakub Kicinski 
1071a5facc4cSJiri Pirko 	err = rtnl_link_register(&nsim_link_ops);
107279579220SJakub Kicinski 	if (err)
1073925f5afeSJiri Pirko 		goto err_bus_exit;
107479579220SJakub Kicinski 
107531d3ad83SJakub Kicinski 	return 0;
107631d3ad83SJakub Kicinski 
1077925f5afeSJiri Pirko err_bus_exit:
1078925f5afeSJiri Pirko 	nsim_bus_exit();
1079d514f41eSJiri Pirko err_dev_exit:
1080d514f41eSJiri Pirko 	nsim_dev_exit();
108131d3ad83SJakub Kicinski 	return err;
108283c9e13aSJakub Kicinski }
108383c9e13aSJakub Kicinski 
108483c9e13aSJakub Kicinski static void __exit nsim_module_exit(void)
108583c9e13aSJakub Kicinski {
108683c9e13aSJakub Kicinski 	rtnl_link_unregister(&nsim_link_ops);
1087925f5afeSJiri Pirko 	nsim_bus_exit();
1088d514f41eSJiri Pirko 	nsim_dev_exit();
108983c9e13aSJakub Kicinski }
109083c9e13aSJakub Kicinski 
109183c9e13aSJakub Kicinski module_init(nsim_module_init);
109283c9e13aSJakub Kicinski module_exit(nsim_module_exit);
109383c9e13aSJakub Kicinski MODULE_LICENSE("GPL");
10941fff1f79SJakub Kicinski MODULE_DESCRIPTION("Simulated networking device for testing");
109583c9e13aSJakub Kicinski MODULE_ALIAS_RTNL_LINK(DRV_NAME);
1096