2017-12-01 23:08:58 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2017 Netronome Systems, Inc.
|
|
|
|
|
*
|
|
|
|
|
* This software is licensed under the GNU General License Version 2,
|
|
|
|
|
* June 1991 as shown in the file COPYING in the top-level directory of this
|
|
|
|
|
* source tree.
|
|
|
|
|
*
|
|
|
|
|
* THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
|
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
|
|
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
|
* FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
|
|
|
|
* OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
|
|
|
|
* THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
|
|
|
*/
|
|
|
|
|
|
2017-12-01 23:08:59 +00:00
|
|
|
#include <linux/debugfs.h>
|
2017-12-01 23:08:58 +00:00
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
|
#include <linux/slab.h>
|
2024-04-05 02:45:24 +00:00
|
|
|
#include <net/netdev_queues.h>
|
2024-04-12 14:14:31 +00:00
|
|
|
#include <net/page_pool/helpers.h>
|
2017-12-01 23:08:58 +00:00
|
|
|
#include <net/netlink.h>
|
2024-10-09 08:09:57 +00:00
|
|
|
#include <net/net_shaper.h>
|
2017-12-01 23:08:59 +00:00
|
|
|
#include <net/pkt_cls.h>
|
2017-12-01 23:08:58 +00:00
|
|
|
#include <net/rtnetlink.h>
|
2020-07-10 00:42:48 +00:00
|
|
|
#include <net/udp_tunnel.h>
|
2017-12-01 23:08:58 +00:00
|
|
|
|
|
|
|
|
#include "netdevsim.h"
|
|
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
#define NSIM_RING_SIZE 256
|
|
|
|
|
|
|
|
|
|
static int nsim_napi_rx(struct nsim_rq *rq, struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
if (skb_queue_len(&rq->skb_queue) > NSIM_RING_SIZE) {
|
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
|
return NET_RX_DROP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skb_queue_tail(&rq->skb_queue, skb);
|
|
|
|
|
return NET_RX_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_forward_skb(struct net_device *dev, struct sk_buff *skb,
|
|
|
|
|
struct nsim_rq *rq)
|
|
|
|
|
{
|
|
|
|
|
return __dev_forward_skb(dev, skb) ?: nsim_napi_rx(rq, skb);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-01 23:08:58 +00:00
|
|
|
static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2024-05-07 16:32:27 +00:00
|
|
|
struct net_device *peer_dev;
|
2024-02-28 23:22:50 +00:00
|
|
|
unsigned int len = skb->len;
|
|
|
|
|
struct netdevsim *peer_ns;
|
2024-05-07 16:32:27 +00:00
|
|
|
struct nsim_rq *rq;
|
|
|
|
|
int rxq;
|
2017-12-01 23:08:58 +00:00
|
|
|
|
2024-02-28 23:22:50 +00:00
|
|
|
rcu_read_lock();
|
2018-06-26 17:07:54 +00:00
|
|
|
if (!nsim_ipsec_tx(ns, skb))
|
2024-02-28 23:22:50 +00:00
|
|
|
goto out_drop_free;
|
2018-06-26 17:07:54 +00:00
|
|
|
|
2024-02-28 23:22:50 +00:00
|
|
|
peer_ns = rcu_dereference(ns->peer);
|
|
|
|
|
if (!peer_ns)
|
|
|
|
|
goto out_drop_free;
|
|
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
peer_dev = peer_ns->netdev;
|
|
|
|
|
rxq = skb_get_queue_mapping(skb);
|
|
|
|
|
if (rxq >= peer_dev->num_rx_queues)
|
|
|
|
|
rxq = rxq % peer_dev->num_rx_queues;
|
|
|
|
|
rq = &peer_ns->rq[rxq];
|
|
|
|
|
|
2024-02-28 23:22:50 +00:00
|
|
|
skb_tx_timestamp(skb);
|
2024-05-07 16:32:27 +00:00
|
|
|
if (unlikely(nsim_forward_skb(peer_dev, skb, rq) == NET_RX_DROP))
|
2024-02-28 23:22:50 +00:00
|
|
|
goto out_drop_cnt;
|
|
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
napi_schedule(&rq->napi);
|
|
|
|
|
|
2024-02-28 23:22:50 +00:00
|
|
|
rcu_read_unlock();
|
2017-12-01 23:08:58 +00:00
|
|
|
u64_stats_update_begin(&ns->syncp);
|
|
|
|
|
ns->tx_packets++;
|
2024-02-28 23:22:50 +00:00
|
|
|
ns->tx_bytes += len;
|
2017-12-01 23:08:58 +00:00
|
|
|
u64_stats_update_end(&ns->syncp);
|
2024-02-28 23:22:50 +00:00
|
|
|
return NETDEV_TX_OK;
|
2017-12-01 23:08:58 +00:00
|
|
|
|
2024-02-28 23:22:50 +00:00
|
|
|
out_drop_free:
|
2017-12-01 23:08:58 +00:00
|
|
|
dev_kfree_skb(skb);
|
2024-02-28 23:22:50 +00:00
|
|
|
out_drop_cnt:
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
u64_stats_update_begin(&ns->syncp);
|
|
|
|
|
ns->tx_dropped++;
|
|
|
|
|
u64_stats_update_end(&ns->syncp);
|
2017-12-01 23:08:58 +00:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nsim_set_rx_mode(struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-01 23:08:59 +00:00
|
|
|
static int nsim_change_mtu(struct net_device *dev, int new_mtu)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
|
|
|
|
|
2018-07-12 03:36:42 +00:00
|
|
|
if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU)
|
2017-12-01 23:08:59 +00:00
|
|
|
return -EBUSY;
|
|
|
|
|
|
2024-05-06 10:28:12 +00:00
|
|
|
WRITE_ONCE(dev->mtu, new_mtu);
|
2017-12-01 23:08:59 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-01 23:08:58 +00:00
|
|
|
static void
|
|
|
|
|
nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
|
|
|
|
unsigned int start;
|
|
|
|
|
|
|
|
|
|
do {
|
2022-10-26 13:22:14 +00:00
|
|
|
start = u64_stats_fetch_begin(&ns->syncp);
|
2017-12-01 23:08:58 +00:00
|
|
|
stats->tx_bytes = ns->tx_bytes;
|
|
|
|
|
stats->tx_packets = ns->tx_packets;
|
2024-02-28 23:22:50 +00:00
|
|
|
stats->tx_dropped = ns->tx_dropped;
|
2022-10-26 13:22:14 +00:00
|
|
|
} while (u64_stats_fetch_retry(&ns->syncp, start));
|
2017-12-01 23:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-01 23:08:59 +00:00
|
|
|
static int
|
|
|
|
|
nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
|
|
|
|
|
{
|
|
|
|
|
return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-01 23:09:01 +00:00
|
|
|
static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
/* Only refuse multicast addresses, zero address can mean unset/any. */
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev) || is_multicast_ether_addr(mac))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2021-10-30 23:15:02 +00:00
|
|
|
memcpy(nsim_dev->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_set_vf_vlan(struct net_device *dev, int vf,
|
|
|
|
|
u16 vlan, u8 qos, __be16 vlan_proto)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev) || vlan > 4095 || qos > 7)
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
nsim_dev->vfconfigs[vf].vlan = vlan;
|
|
|
|
|
nsim_dev->vfconfigs[vf].qos = qos;
|
|
|
|
|
nsim_dev->vfconfigs[vf].vlan_proto = vlan_proto;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-06-02 12:17:18 +00:00
|
|
|
if (nsim_esw_mode_is_switchdev(ns->nsim_dev)) {
|
|
|
|
|
pr_err("Not supported in switchdev mode. Please use devlink API.\n");
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
nsim_dev->vfconfigs[vf].min_tx_rate = min;
|
|
|
|
|
nsim_dev->vfconfigs[vf].max_tx_rate = max;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2021-10-30 23:15:02 +00:00
|
|
|
nsim_dev->vfconfigs[vf].spoofchk_enabled = val;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2021-10-30 23:15:02 +00:00
|
|
|
nsim_dev->vfconfigs[vf].rss_query_enabled = val;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2021-10-30 23:15:02 +00:00
|
|
|
nsim_dev->vfconfigs[vf].trusted = val;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
ivi->vf = vf;
|
2021-10-30 23:15:02 +00:00
|
|
|
ivi->linkstate = nsim_dev->vfconfigs[vf].link_state;
|
|
|
|
|
ivi->min_tx_rate = nsim_dev->vfconfigs[vf].min_tx_rate;
|
|
|
|
|
ivi->max_tx_rate = nsim_dev->vfconfigs[vf].max_tx_rate;
|
|
|
|
|
ivi->vlan = nsim_dev->vfconfigs[vf].vlan;
|
|
|
|
|
ivi->vlan_proto = nsim_dev->vfconfigs[vf].vlan_proto;
|
|
|
|
|
ivi->qos = nsim_dev->vfconfigs[vf].qos;
|
|
|
|
|
memcpy(&ivi->mac, nsim_dev->vfconfigs[vf].vf_mac, ETH_ALEN);
|
|
|
|
|
ivi->spoofchk = nsim_dev->vfconfigs[vf].spoofchk_enabled;
|
|
|
|
|
ivi->trusted = nsim_dev->vfconfigs[vf].trusted;
|
|
|
|
|
ivi->rss_query_en = nsim_dev->vfconfigs[vf].rss_query_enabled;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2021-10-30 23:15:02 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
|
case IFLA_VF_LINK_STATE_AUTO:
|
|
|
|
|
case IFLA_VF_LINK_STATE_ENABLE:
|
|
|
|
|
case IFLA_VF_LINK_STATE_DISABLE:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 23:15:02 +00:00
|
|
|
nsim_dev->vfconfigs[vf].link_state = state;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-07 19:33:21 +00:00
|
|
|
static void nsim_taprio_stats(struct tc_taprio_qopt_stats *stats)
|
|
|
|
|
{
|
|
|
|
|
stats->window_drops = 0;
|
|
|
|
|
stats->tx_overruns = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_setup_tc_taprio(struct net_device *dev,
|
|
|
|
|
struct tc_taprio_qopt_offload *offload)
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
switch (offload->cmd) {
|
|
|
|
|
case TAPRIO_CMD_REPLACE:
|
|
|
|
|
case TAPRIO_CMD_DESTROY:
|
|
|
|
|
break;
|
|
|
|
|
case TAPRIO_CMD_STATS:
|
|
|
|
|
nsim_taprio_stats(&offload->stats);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-09 20:55:46 +00:00
|
|
|
static LIST_HEAD(nsim_block_cb_list);
|
|
|
|
|
|
2017-12-01 23:08:59 +00:00
|
|
|
static int
|
|
|
|
|
nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
|
|
|
|
|
{
|
2019-07-09 20:55:39 +00:00
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
|
|
|
|
|
2017-12-01 23:08:59 +00:00
|
|
|
switch (type) {
|
2023-08-07 19:33:21 +00:00
|
|
|
case TC_SETUP_QDISC_TAPRIO:
|
|
|
|
|
return nsim_setup_tc_taprio(dev, type_data);
|
2017-12-01 23:08:59 +00:00
|
|
|
case TC_SETUP_BLOCK:
|
2019-07-09 20:55:46 +00:00
|
|
|
return flow_block_cb_setup_simple(type_data,
|
|
|
|
|
&nsim_block_cb_list,
|
2019-07-09 20:55:39 +00:00
|
|
|
nsim_setup_tc_block_cb,
|
|
|
|
|
ns, ns, true);
|
2017-12-01 23:08:59 +00:00
|
|
|
default:
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
nsim_set_features(struct net_device *dev, netdev_features_t features)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
|
|
|
|
|
|
|
|
|
if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC))
|
|
|
|
|
return nsim_bpf_disable_tc(ns);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-28 23:22:51 +00:00
|
|
|
static int nsim_get_iflink(const struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *nsim, *peer;
|
|
|
|
|
int iflink;
|
|
|
|
|
|
|
|
|
|
nsim = netdev_priv(dev);
|
|
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
peer = rcu_dereference(nsim->peer);
|
2024-06-06 14:59:08 +00:00
|
|
|
iflink = peer ? READ_ONCE(peer->netdev->ifindex) :
|
|
|
|
|
READ_ONCE(dev->ifindex);
|
2024-02-28 23:22:51 +00:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
|
|
return iflink;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
static int nsim_rcv(struct nsim_rq *rq, int budget)
|
|
|
|
|
{
|
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < budget; i++) {
|
|
|
|
|
if (skb_queue_empty(&rq->skb_queue))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
skb = skb_dequeue(&rq->skb_queue);
|
|
|
|
|
netif_receive_skb(skb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_poll(struct napi_struct *napi, int budget)
|
|
|
|
|
{
|
|
|
|
|
struct nsim_rq *rq = container_of(napi, struct nsim_rq, napi);
|
|
|
|
|
int done;
|
|
|
|
|
|
|
|
|
|
done = nsim_rcv(rq, budget);
|
|
|
|
|
napi_complete(napi);
|
|
|
|
|
|
|
|
|
|
return done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_create_page_pool(struct nsim_rq *rq)
|
|
|
|
|
{
|
|
|
|
|
struct page_pool_params p = {
|
|
|
|
|
.order = 0,
|
|
|
|
|
.pool_size = NSIM_RING_SIZE,
|
|
|
|
|
.nid = NUMA_NO_NODE,
|
|
|
|
|
.dev = &rq->napi.dev->dev,
|
|
|
|
|
.napi = &rq->napi,
|
|
|
|
|
.dma_dir = DMA_BIDIRECTIONAL,
|
|
|
|
|
.netdev = rq->napi.dev,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
rq->page_pool = page_pool_create(&p);
|
|
|
|
|
if (IS_ERR(rq->page_pool)) {
|
|
|
|
|
int err = PTR_ERR(rq->page_pool);
|
|
|
|
|
|
|
|
|
|
rq->page_pool = NULL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_init_napi(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
struct net_device *dev = ns->netdev;
|
|
|
|
|
struct nsim_rq *rq;
|
|
|
|
|
int err, i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++) {
|
|
|
|
|
rq = &ns->rq[i];
|
|
|
|
|
|
|
|
|
|
netif_napi_add(dev, &rq->napi, nsim_poll);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++) {
|
|
|
|
|
rq = &ns->rq[i];
|
|
|
|
|
|
|
|
|
|
err = nsim_create_page_pool(rq);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_pp_destroy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_pp_destroy:
|
|
|
|
|
while (i--) {
|
|
|
|
|
page_pool_destroy(ns->rq[i].page_pool);
|
|
|
|
|
ns->rq[i].page_pool = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++)
|
|
|
|
|
__netif_napi_del(&ns->rq[i].napi);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nsim_enable_napi(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
struct net_device *dev = ns->netdev;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++) {
|
|
|
|
|
struct nsim_rq *rq = &ns->rq[i];
|
|
|
|
|
|
|
|
|
|
netif_queue_set_napi(dev, i, NETDEV_QUEUE_TYPE_RX, &rq->napi);
|
|
|
|
|
napi_enable(&rq->napi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-12 14:14:31 +00:00
|
|
|
static int nsim_open(struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2024-05-07 16:32:27 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = nsim_init_napi(ns);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
nsim_enable_napi(ns);
|
2024-04-12 14:14:31 +00:00
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
2024-04-12 14:14:31 +00:00
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
static void nsim_del_napi(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
struct net_device *dev = ns->netdev;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++) {
|
|
|
|
|
struct nsim_rq *rq = &ns->rq[i];
|
|
|
|
|
|
|
|
|
|
napi_disable(&rq->napi);
|
|
|
|
|
__netif_napi_del(&rq->napi);
|
|
|
|
|
}
|
|
|
|
|
synchronize_net();
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++) {
|
|
|
|
|
page_pool_destroy(ns->rq[i].page_pool);
|
|
|
|
|
ns->rq[i].page_pool = NULL;
|
|
|
|
|
}
|
2024-04-12 14:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_stop(struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2024-05-07 16:32:27 +00:00
|
|
|
struct netdevsim *peer;
|
|
|
|
|
|
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
|
peer = rtnl_dereference(ns->peer);
|
|
|
|
|
if (peer)
|
|
|
|
|
netif_carrier_off(peer->netdev);
|
2024-04-12 14:14:31 +00:00
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
nsim_del_napi(ns);
|
2024-04-12 14:14:31 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 08:09:57 +00:00
|
|
|
static int nsim_shaper_set(struct net_shaper_binding *binding,
|
|
|
|
|
const struct net_shaper *shaper,
|
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_shaper_del(struct net_shaper_binding *binding,
|
|
|
|
|
const struct net_shaper_handle *handle,
|
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_shaper_group(struct net_shaper_binding *binding,
|
|
|
|
|
int leaves_count,
|
|
|
|
|
const struct net_shaper *leaves,
|
|
|
|
|
const struct net_shaper *root,
|
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nsim_shaper_cap(struct net_shaper_binding *binding,
|
|
|
|
|
enum net_shaper_scope scope,
|
|
|
|
|
unsigned long *flags)
|
|
|
|
|
{
|
|
|
|
|
*flags = ULONG_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct net_shaper_ops nsim_shaper_ops = {
|
|
|
|
|
.set = nsim_shaper_set,
|
|
|
|
|
.delete = nsim_shaper_del,
|
|
|
|
|
.group = nsim_shaper_group,
|
|
|
|
|
.capabilities = nsim_shaper_cap,
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-01 23:08:58 +00:00
|
|
|
static const struct net_device_ops nsim_netdev_ops = {
|
|
|
|
|
.ndo_start_xmit = nsim_start_xmit,
|
|
|
|
|
.ndo_set_rx_mode = nsim_set_rx_mode,
|
|
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2017-12-01 23:08:59 +00:00
|
|
|
.ndo_change_mtu = nsim_change_mtu,
|
2017-12-01 23:08:58 +00:00
|
|
|
.ndo_get_stats64 = nsim_get_stats64,
|
2017-12-01 23:09:01 +00:00
|
|
|
.ndo_set_vf_mac = nsim_set_vf_mac,
|
|
|
|
|
.ndo_set_vf_vlan = nsim_set_vf_vlan,
|
|
|
|
|
.ndo_set_vf_rate = nsim_set_vf_rate,
|
|
|
|
|
.ndo_set_vf_spoofchk = nsim_set_vf_spoofchk,
|
|
|
|
|
.ndo_set_vf_trust = nsim_set_vf_trust,
|
|
|
|
|
.ndo_get_vf_config = nsim_get_vf_config,
|
|
|
|
|
.ndo_set_vf_link_state = nsim_set_vf_link_state,
|
|
|
|
|
.ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en,
|
2017-12-01 23:08:59 +00:00
|
|
|
.ndo_setup_tc = nsim_setup_tc,
|
|
|
|
|
.ndo_set_features = nsim_set_features,
|
2024-02-28 23:22:51 +00:00
|
|
|
.ndo_get_iflink = nsim_get_iflink,
|
2017-12-01 23:08:59 +00:00
|
|
|
.ndo_bpf = nsim_bpf,
|
2024-04-12 14:14:31 +00:00
|
|
|
.ndo_open = nsim_open,
|
|
|
|
|
.ndo_stop = nsim_stop,
|
2024-10-09 08:09:57 +00:00
|
|
|
.net_shaper_ops = &nsim_shaper_ops,
|
2017-12-01 23:08:58 +00:00
|
|
|
};
|
|
|
|
|
|
2021-06-02 12:17:17 +00:00
|
|
|
static const struct net_device_ops nsim_vf_netdev_ops = {
|
|
|
|
|
.ndo_start_xmit = nsim_start_xmit,
|
|
|
|
|
.ndo_set_rx_mode = nsim_set_rx_mode,
|
|
|
|
|
.ndo_set_mac_address = eth_mac_addr,
|
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
|
.ndo_change_mtu = nsim_change_mtu,
|
|
|
|
|
.ndo_get_stats64 = nsim_get_stats64,
|
|
|
|
|
.ndo_setup_tc = nsim_setup_tc,
|
|
|
|
|
.ndo_set_features = nsim_set_features,
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-05 02:45:24 +00:00
|
|
|
/* We don't have true per-queue stats, yet, so do some random fakery here.
|
|
|
|
|
* Only report stuff for queue 0.
|
|
|
|
|
*/
|
|
|
|
|
static void nsim_get_queue_stats_rx(struct net_device *dev, int idx,
|
|
|
|
|
struct netdev_queue_stats_rx *stats)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_link_stats64 rtstats = {};
|
|
|
|
|
|
|
|
|
|
if (!idx)
|
|
|
|
|
nsim_get_stats64(dev, &rtstats);
|
|
|
|
|
|
|
|
|
|
stats->packets = rtstats.rx_packets - !!rtstats.rx_packets;
|
|
|
|
|
stats->bytes = rtstats.rx_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nsim_get_queue_stats_tx(struct net_device *dev, int idx,
|
|
|
|
|
struct netdev_queue_stats_tx *stats)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_link_stats64 rtstats = {};
|
|
|
|
|
|
|
|
|
|
if (!idx)
|
|
|
|
|
nsim_get_stats64(dev, &rtstats);
|
|
|
|
|
|
|
|
|
|
stats->packets = rtstats.tx_packets - !!rtstats.tx_packets;
|
|
|
|
|
stats->bytes = rtstats.tx_bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nsim_get_base_stats(struct net_device *dev,
|
|
|
|
|
struct netdev_queue_stats_rx *rx,
|
|
|
|
|
struct netdev_queue_stats_tx *tx)
|
|
|
|
|
{
|
|
|
|
|
struct rtnl_link_stats64 rtstats = {};
|
|
|
|
|
|
|
|
|
|
nsim_get_stats64(dev, &rtstats);
|
|
|
|
|
|
|
|
|
|
rx->packets = !!rtstats.rx_packets;
|
|
|
|
|
rx->bytes = 0;
|
|
|
|
|
tx->packets = !!rtstats.tx_packets;
|
|
|
|
|
tx->bytes = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct netdev_stat_ops nsim_stat_ops = {
|
|
|
|
|
.get_queue_stats_tx = nsim_get_queue_stats_tx,
|
|
|
|
|
.get_queue_stats_rx = nsim_get_queue_stats_rx,
|
|
|
|
|
.get_base_stats = nsim_get_base_stats,
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-12 14:14:31 +00:00
|
|
|
static ssize_t
|
|
|
|
|
nsim_pp_hold_read(struct file *file, char __user *data,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = file->private_data;
|
|
|
|
|
char buf[3] = "n\n";
|
|
|
|
|
|
|
|
|
|
if (ns->page)
|
|
|
|
|
buf[0] = 'y';
|
|
|
|
|
|
|
|
|
|
return simple_read_from_buffer(data, count, ppos, buf, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
|
nsim_pp_hold_write(struct file *file, const char __user *data,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
struct netdevsim *ns = file->private_data;
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
bool val;
|
|
|
|
|
|
|
|
|
|
ret = kstrtobool_from_user(data, count, &val);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
ret = count;
|
|
|
|
|
if (val == !!ns->page)
|
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
|
|
if (!netif_running(ns->netdev) && val) {
|
|
|
|
|
ret = -ENETDOWN;
|
|
|
|
|
} else if (val) {
|
2024-05-07 16:32:27 +00:00
|
|
|
ns->page = page_pool_dev_alloc_pages(ns->rq[0].page_pool);
|
2024-04-12 14:14:31 +00:00
|
|
|
if (!ns->page)
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
} else {
|
|
|
|
|
page_pool_put_full_page(ns->page->pp, ns->page, false);
|
|
|
|
|
ns->page = NULL;
|
|
|
|
|
}
|
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct file_operations nsim_pp_hold_fops = {
|
|
|
|
|
.open = simple_open,
|
|
|
|
|
.read = nsim_pp_hold_read,
|
|
|
|
|
.write = nsim_pp_hold_write,
|
|
|
|
|
.llseek = generic_file_llseek,
|
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-01 23:08:58 +00:00
|
|
|
static void nsim_setup(struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
ether_setup(dev);
|
|
|
|
|
eth_hw_addr_random(dev);
|
|
|
|
|
|
|
|
|
|
dev->tx_queue_len = 0;
|
|
|
|
|
dev->flags &= ~IFF_MULTICAST;
|
|
|
|
|
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
|
|
|
|
|
IFF_NO_QUEUE;
|
|
|
|
|
dev->features |= NETIF_F_HIGHDMA |
|
|
|
|
|
NETIF_F_SG |
|
|
|
|
|
NETIF_F_FRAGLIST |
|
|
|
|
|
NETIF_F_HW_CSUM |
|
|
|
|
|
NETIF_F_TSO;
|
2024-11-06 23:13:27 +00:00
|
|
|
dev->hw_features |= NETIF_F_HW_TC |
|
|
|
|
|
NETIF_F_SG |
|
|
|
|
|
NETIF_F_FRAGLIST |
|
|
|
|
|
NETIF_F_HW_CSUM |
|
|
|
|
|
NETIF_F_TSO;
|
2017-12-01 23:08:58 +00:00
|
|
|
dev->max_mtu = ETH_MAX_MTU;
|
drivers: net: turn on XDP features
A summary of the flags being set for various drivers is given below.
Note that XDP_F_REDIRECT_TARGET and XDP_F_FRAG_TARGET are features
that can be turned off and on at runtime. This means that these flags
may be set and unset under RTNL lock protection by the driver. Hence,
READ_ONCE must be used by code loading the flag value.
Also, these flags are not used for synchronization against the availability
of XDP resources on a device. It is merely a hint, and hence the read
may race with the actual teardown of XDP resources on the device. This
may change in the future, e.g. operations taking a reference on the XDP
resources of the driver, and in turn inhibiting turning off this flag.
However, for now, it can only be used as a hint to check whether device
supports becoming a redirection target.
Turn 'hw-offload' feature flag on for:
- netronome (nfp)
- netdevsim.
Turn 'native' and 'zerocopy' features flags on for:
- intel (i40e, ice, ixgbe, igc)
- mellanox (mlx5).
- stmmac
- netronome (nfp)
Turn 'native' features flags on for:
- amazon (ena)
- broadcom (bnxt)
- freescale (dpaa, dpaa2, enetc)
- funeth
- intel (igb)
- marvell (mvneta, mvpp2, octeontx2)
- mellanox (mlx4)
- mtk_eth_soc
- qlogic (qede)
- sfc
- socionext (netsec)
- ti (cpsw)
- tap
- tsnep
- veth
- xen
- virtio_net.
Turn 'basic' (tx, pass, aborted and drop) features flags on for:
- netronome (nfp)
- cavium (thunder)
- hyperv.
Turn 'redirect_target' feature flag on for:
- amanzon (ena)
- broadcom (bnxt)
- freescale (dpaa, dpaa2)
- intel (i40e, ice, igb, ixgbe)
- ti (cpsw)
- marvell (mvneta, mvpp2)
- sfc
- socionext (netsec)
- qlogic (qede)
- mellanox (mlx5)
- tap
- veth
- virtio_net
- xen
Reviewed-by: Gerhard Engleder <gerhard@engleder-embedded.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Acked-by: Stanislav Fomichev <sdf@google.com>
Acked-by: Jakub Kicinski <kuba@kernel.org>
Co-developed-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Marek Majtyka <alardam@gmail.com>
Link: https://lore.kernel.org/r/3eca9fafb308462f7edb1f58e451d59209aa07eb.1675245258.git.lorenzo@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-02-01 10:24:18 +00:00
|
|
|
dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD;
|
2017-12-01 23:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
static int nsim_queue_init(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
struct net_device *dev = ns->netdev;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
ns->rq = kvcalloc(dev->num_rx_queues, sizeof(*ns->rq),
|
|
|
|
|
GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL);
|
|
|
|
|
if (!ns->rq)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++)
|
|
|
|
|
skb_queue_head_init(&ns->rq[i].skb_queue);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nsim_queue_free(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
struct net_device *dev = ns->netdev;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dev->num_rx_queues; i++)
|
|
|
|
|
skb_queue_purge_reason(&ns->rq[i].skb_queue,
|
|
|
|
|
SKB_DROP_REASON_QUEUE_PURGE);
|
|
|
|
|
|
|
|
|
|
kvfree(ns->rq);
|
|
|
|
|
ns->rq = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 12:17:17 +00:00
|
|
|
static int nsim_init_netdevsim(struct netdevsim *ns)
|
|
|
|
|
{
|
2023-08-07 19:33:20 +00:00
|
|
|
struct mock_phc *phc;
|
2021-06-02 12:17:17 +00:00
|
|
|
int err;
|
|
|
|
|
|
2023-08-07 19:33:20 +00:00
|
|
|
phc = mock_phc_create(&ns->nsim_bus_dev->dev);
|
|
|
|
|
if (IS_ERR(phc))
|
|
|
|
|
return PTR_ERR(phc);
|
|
|
|
|
|
|
|
|
|
ns->phc = phc;
|
2021-06-02 12:17:17 +00:00
|
|
|
ns->netdev->netdev_ops = &nsim_netdev_ops;
|
2024-04-05 02:45:24 +00:00
|
|
|
ns->netdev->stat_ops = &nsim_stat_ops;
|
2021-06-02 12:17:17 +00:00
|
|
|
|
|
|
|
|
err = nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev);
|
|
|
|
|
if (err)
|
2023-08-07 19:33:20 +00:00
|
|
|
goto err_phc_destroy;
|
2021-06-02 12:17:17 +00:00
|
|
|
|
|
|
|
|
rtnl_lock();
|
2024-05-07 16:32:27 +00:00
|
|
|
err = nsim_queue_init(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
if (err)
|
|
|
|
|
goto err_utn_destroy;
|
|
|
|
|
|
2024-05-07 16:32:27 +00:00
|
|
|
err = nsim_bpf_init(ns);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_rq_destroy;
|
|
|
|
|
|
2023-07-13 13:20:23 +00:00
|
|
|
nsim_macsec_init(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
nsim_ipsec_init(ns);
|
|
|
|
|
|
|
|
|
|
err = register_netdevice(ns->netdev);
|
|
|
|
|
if (err)
|
|
|
|
|
goto err_ipsec_teardown;
|
|
|
|
|
rtnl_unlock();
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_ipsec_teardown:
|
|
|
|
|
nsim_ipsec_teardown(ns);
|
2023-07-13 13:20:23 +00:00
|
|
|
nsim_macsec_teardown(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
nsim_bpf_uninit(ns);
|
2024-05-07 16:32:27 +00:00
|
|
|
err_rq_destroy:
|
|
|
|
|
nsim_queue_free(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
err_utn_destroy:
|
|
|
|
|
rtnl_unlock();
|
|
|
|
|
nsim_udp_tunnels_info_destroy(ns->netdev);
|
2023-08-07 19:33:20 +00:00
|
|
|
err_phc_destroy:
|
|
|
|
|
mock_phc_destroy(ns->phc);
|
2021-06-02 12:17:17 +00:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int nsim_init_netdevsim_vf(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ns->netdev->netdev_ops = &nsim_vf_netdev_ops;
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
err = register_netdevice(ns->netdev);
|
|
|
|
|
rtnl_unlock();
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-16 19:14:00 +00:00
|
|
|
static void nsim_exit_netdevsim(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
nsim_udp_tunnels_info_destroy(ns->netdev);
|
|
|
|
|
mock_phc_destroy(ns->phc);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-25 13:59:55 +00:00
|
|
|
struct netdevsim *
|
|
|
|
|
nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port)
|
2018-07-17 17:53:20 +00:00
|
|
|
{
|
2019-04-25 13:59:55 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
|
struct netdevsim *ns;
|
2019-04-12 12:49:26 +00:00
|
|
|
int err;
|
2018-07-17 17:53:20 +00:00
|
|
|
|
2021-07-16 01:52:45 +00:00
|
|
|
dev = alloc_netdev_mq(sizeof(*ns), "eth%d", NET_NAME_UNKNOWN, nsim_setup,
|
|
|
|
|
nsim_dev->nsim_bus_dev->num_queues);
|
2019-04-25 13:59:55 +00:00
|
|
|
if (!dev)
|
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2019-04-25 13:59:41 +00:00
|
|
|
|
2019-10-03 09:49:37 +00:00
|
|
|
dev_net_set(dev, nsim_dev_net(nsim_dev));
|
2019-04-25 13:59:55 +00:00
|
|
|
ns = netdev_priv(dev);
|
|
|
|
|
ns->netdev = dev;
|
2021-03-04 18:30:09 +00:00
|
|
|
u64_stats_init(&ns->syncp);
|
2019-04-25 13:59:55 +00:00
|
|
|
ns->nsim_dev = nsim_dev;
|
|
|
|
|
ns->nsim_dev_port = nsim_dev_port;
|
|
|
|
|
ns->nsim_bus_dev = nsim_dev->nsim_bus_dev;
|
2019-04-25 13:59:45 +00:00
|
|
|
SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev);
|
2022-11-02 16:02:04 +00:00
|
|
|
SET_NETDEV_DEVLINK_PORT(dev, &nsim_dev_port->devlink_port);
|
2020-09-15 00:11:54 +00:00
|
|
|
nsim_ethtool_init(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
if (nsim_dev_port_is_pf(nsim_dev_port))
|
|
|
|
|
err = nsim_init_netdevsim(ns);
|
|
|
|
|
else
|
|
|
|
|
err = nsim_init_netdevsim_vf(ns);
|
2020-07-10 00:42:48 +00:00
|
|
|
if (err)
|
|
|
|
|
goto err_free_netdev;
|
2024-04-12 14:14:31 +00:00
|
|
|
|
|
|
|
|
ns->pp_dfs = debugfs_create_file("pp_hold", 0600, nsim_dev_port->ddir,
|
|
|
|
|
ns, &nsim_pp_hold_fops);
|
|
|
|
|
|
2019-04-25 13:59:55 +00:00
|
|
|
return ns;
|
|
|
|
|
|
|
|
|
|
err_free_netdev:
|
|
|
|
|
free_netdev(dev);
|
|
|
|
|
return ERR_PTR(err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nsim_destroy(struct netdevsim *ns)
|
|
|
|
|
{
|
|
|
|
|
struct net_device *dev = ns->netdev;
|
2024-02-28 23:22:49 +00:00
|
|
|
struct netdevsim *peer;
|
2019-04-25 13:59:55 +00:00
|
|
|
|
2024-04-12 14:14:31 +00:00
|
|
|
debugfs_remove(ns->pp_dfs);
|
|
|
|
|
|
2019-04-25 13:59:55 +00:00
|
|
|
rtnl_lock();
|
2024-02-28 23:22:49 +00:00
|
|
|
peer = rtnl_dereference(ns->peer);
|
|
|
|
|
if (peer)
|
|
|
|
|
RCU_INIT_POINTER(peer->peer, NULL);
|
|
|
|
|
RCU_INIT_POINTER(ns->peer, NULL);
|
2019-04-25 13:59:55 +00:00
|
|
|
unregister_netdevice(dev);
|
2021-06-02 12:17:17 +00:00
|
|
|
if (nsim_dev_port_is_pf(ns->nsim_dev_port)) {
|
2023-07-13 13:20:23 +00:00
|
|
|
nsim_macsec_teardown(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
nsim_ipsec_teardown(ns);
|
|
|
|
|
nsim_bpf_uninit(ns);
|
2024-05-07 16:32:27 +00:00
|
|
|
nsim_queue_free(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
}
|
2019-04-25 13:59:55 +00:00
|
|
|
rtnl_unlock();
|
2021-06-02 12:17:17 +00:00
|
|
|
if (nsim_dev_port_is_pf(ns->nsim_dev_port))
|
2024-01-16 19:14:00 +00:00
|
|
|
nsim_exit_netdevsim(ns);
|
2024-04-12 14:14:31 +00:00
|
|
|
|
|
|
|
|
/* Put this intentionally late to exercise the orphaning path */
|
|
|
|
|
if (ns->page) {
|
|
|
|
|
page_pool_put_full_page(ns->page->pp, ns->page, false);
|
|
|
|
|
ns->page = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-25 13:59:55 +00:00
|
|
|
free_netdev(dev);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-28 23:22:49 +00:00
|
|
|
bool netdev_is_nsim(struct net_device *dev)
|
|
|
|
|
{
|
|
|
|
|
return dev->netdev_ops == &nsim_netdev_ops;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-25 13:59:55 +00:00
|
|
|
static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
|
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
|
{
|
2021-07-16 01:52:45 +00:00
|
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
|
|
|
"Please use: echo \"[ID] [PORT_COUNT] [NUM_QUEUES]\" > /sys/bus/netdevsim/new_device");
|
2019-04-25 13:59:55 +00:00
|
|
|
return -EOPNOTSUPP;
|
2018-07-17 17:53:20 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-01 23:08:58 +00:00
|
|
|
static struct rtnl_link_ops nsim_link_ops __read_mostly = {
|
|
|
|
|
.kind = DRV_NAME,
|
|
|
|
|
.validate = nsim_validate,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int __init nsim_module_init(void)
|
|
|
|
|
{
|
2017-12-01 23:08:59 +00:00
|
|
|
int err;
|
|
|
|
|
|
2019-04-25 13:59:50 +00:00
|
|
|
err = nsim_dev_init();
|
2019-04-12 12:49:26 +00:00
|
|
|
if (err)
|
2019-04-25 13:59:52 +00:00
|
|
|
return err;
|
2018-07-17 17:53:20 +00:00
|
|
|
|
2019-04-25 13:59:44 +00:00
|
|
|
err = nsim_bus_init();
|
2017-12-01 23:08:59 +00:00
|
|
|
if (err)
|
2019-04-25 13:59:50 +00:00
|
|
|
goto err_dev_exit;
|
2017-12-01 23:08:59 +00:00
|
|
|
|
2019-08-06 19:15:17 +00:00
|
|
|
err = rtnl_link_register(&nsim_link_ops);
|
|
|
|
|
if (err)
|
2019-10-03 09:49:26 +00:00
|
|
|
goto err_bus_exit;
|
2019-08-06 19:15:17 +00:00
|
|
|
|
2017-12-01 23:08:59 +00:00
|
|
|
return 0;
|
|
|
|
|
|
2019-04-25 13:59:44 +00:00
|
|
|
err_bus_exit:
|
|
|
|
|
nsim_bus_exit();
|
2019-04-25 13:59:50 +00:00
|
|
|
err_dev_exit:
|
|
|
|
|
nsim_dev_exit();
|
2017-12-01 23:08:59 +00:00
|
|
|
return err;
|
2017-12-01 23:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __exit nsim_module_exit(void)
|
|
|
|
|
{
|
|
|
|
|
rtnl_link_unregister(&nsim_link_ops);
|
2019-04-25 13:59:44 +00:00
|
|
|
nsim_bus_exit();
|
2019-04-25 13:59:50 +00:00
|
|
|
nsim_dev_exit();
|
2017-12-01 23:08:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_init(nsim_module_init);
|
|
|
|
|
module_exit(nsim_module_exit);
|
|
|
|
|
MODULE_LICENSE("GPL");
|
2023-10-27 21:13:08 +00:00
|
|
|
MODULE_DESCRIPTION("Simulated networking device for testing");
|
2017-12-01 23:08:58 +00:00
|
|
|
MODULE_ALIAS_RTNL_LINK(DRV_NAME);
|