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-10-04 08:58:29 +00:00
|
|
|
#include <net/netdev_queues.h>
|
2024-10-04 08:58:55 +00:00
|
|
|
#include <net/page_pool/helpers.h>
|
2017-12-01 23:08:58 +00:00
|
|
|
#include <net/netlink.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-10-04 08:59:17 +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-10-04 08:59:17 +00:00
|
|
|
struct net_device *peer_dev;
|
2024-05-28 11:28:32 +00:00
|
|
|
unsigned int len = skb->len;
|
|
|
|
struct netdevsim *peer_ns;
|
2024-10-04 08:59:17 +00:00
|
|
|
struct nsim_rq *rq;
|
|
|
|
int rxq;
|
2017-12-01 23:08:58 +00:00
|
|
|
|
2024-05-28 11:28:32 +00:00
|
|
|
rcu_read_lock();
|
2018-06-26 17:07:54 +00:00
|
|
|
if (!nsim_ipsec_tx(ns, skb))
|
2024-05-28 11:28:32 +00:00
|
|
|
goto out_drop_free;
|
2018-06-26 17:07:54 +00:00
|
|
|
|
2024-05-28 11:28:32 +00:00
|
|
|
peer_ns = rcu_dereference(ns->peer);
|
|
|
|
if (!peer_ns)
|
|
|
|
goto out_drop_free;
|
|
|
|
|
2024-10-04 08:59:17 +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-05-28 11:28:32 +00:00
|
|
|
skb_tx_timestamp(skb);
|
2024-10-04 08:59:17 +00:00
|
|
|
if (unlikely(nsim_forward_skb(peer_dev, skb, rq) == NET_RX_DROP))
|
2024-05-28 11:28:32 +00:00
|
|
|
goto out_drop_cnt;
|
|
|
|
|
2024-10-04 08:59:17 +00:00
|
|
|
napi_schedule(&rq->napi);
|
|
|
|
|
2024-05-28 11:28:32 +00:00
|
|
|
rcu_read_unlock();
|
2017-12-01 23:08:58 +00:00
|
|
|
u64_stats_update_begin(&ns->syncp);
|
|
|
|
ns->tx_packets++;
|
2024-05-28 11:28:32 +00:00
|
|
|
ns->tx_bytes += len;
|
2017-12-01 23:08:58 +00:00
|
|
|
u64_stats_update_end(&ns->syncp);
|
2024-05-28 11:28:32 +00:00
|
|
|
return NETDEV_TX_OK;
|
2017-12-01 23:08:58 +00:00
|
|
|
|
2024-05-28 11:28:32 +00:00
|
|
|
out_drop_free:
|
2017-12-01 23:08:58 +00:00
|
|
|
dev_kfree_skb(skb);
|
2024-05-28 11:28:32 +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-31 19:00:27 +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 {
|
2023-05-04 14:31:56 +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-05-28 11:28:32 +00:00
|
|
|
stats->tx_dropped = ns->tx_dropped;
|
2023-05-04 14:31:56 +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);
|
2022-07-29 11:22:58 +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. */
|
2022-07-29 11:22:58 +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;
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev) || vlan > 4095 || qos > 7)
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +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;
|
|
|
|
}
|
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
if (vf >= nsim_dev_get_vfs(nsim_dev))
|
2017-12-01 23:09:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ivi->vf = vf;
|
2022-07-29 11:22:58 +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);
|
2022-07-29 11:22:58 +00:00
|
|
|
struct nsim_dev *nsim_dev = ns->nsim_dev;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
2022-07-29 11:22:58 +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;
|
|
|
|
}
|
|
|
|
|
2022-07-29 11:22:58 +00:00
|
|
|
nsim_dev->vfconfigs[vf].link_state = state;
|
2017-12-01 23:09:01 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-05-14 09:14:25 +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) {
|
2024-05-14 09:14:25 +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-05-28 11:28:32 +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-10-04 08:59:19 +00:00
|
|
|
iflink = peer ? READ_ONCE(peer->netdev->ifindex) :
|
|
|
|
READ_ONCE(dev->ifindex);
|
2024-05-28 11:28:32 +00:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return iflink;
|
|
|
|
}
|
|
|
|
|
2024-10-04 08:59:17 +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-10-04 08:58:55 +00:00
|
|
|
static int nsim_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2024-10-04 08:59:17 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = nsim_init_napi(ns);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
nsim_enable_napi(ns);
|
2024-10-04 08:58:55 +00:00
|
|
|
|
2024-10-04 08:59:17 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2024-10-04 08:58:55 +00:00
|
|
|
|
2024-10-04 08:59:17 +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-10-04 08:58:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nsim_stop(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct netdevsim *ns = netdev_priv(dev);
|
2024-10-04 08:59:17 +00:00
|
|
|
struct netdevsim *peer;
|
|
|
|
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
peer = rtnl_dereference(ns->peer);
|
|
|
|
if (peer)
|
|
|
|
netif_carrier_off(peer->netdev);
|
2024-10-04 08:58:55 +00:00
|
|
|
|
2024-10-04 08:59:17 +00:00
|
|
|
nsim_del_napi(ns);
|
2024-10-04 08:58:55 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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-05-28 11:28:32 +00:00
|
|
|
.ndo_get_iflink = nsim_get_iflink,
|
2017-12-01 23:08:59 +00:00
|
|
|
.ndo_bpf = nsim_bpf,
|
2024-10-04 08:58:55 +00:00
|
|
|
.ndo_open = nsim_open,
|
|
|
|
.ndo_stop = nsim_stop,
|
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-10-04 08:58:29 +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-10-04 08:58:55 +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-10-04 08:59:17 +00:00
|
|
|
ns->page = page_pool_dev_alloc_pages(ns->rq[0].page_pool);
|
2024-10-04 08:58:55 +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;
|
2017-12-01 23:08:59 +00:00
|
|
|
dev->hw_features |= NETIF_F_HW_TC;
|
2017-12-01 23:08:58 +00:00
|
|
|
dev->max_mtu = ETH_MAX_MTU;
|
drivers: net: turn on XDP features
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2178930
Conflicts:
- drivers/net/ethernet/engleder/tsnep_main.c: We don't have this driver
- drivers/net/ethernet/fungible/funeth/funeth_main.c: We don't have this
driver
- drivers/net/ethernet/aquantia/atlantic/aq_nic.c: left out because it
does not have XDP support
- drivers/net/ethernet/mediatek/mtk_eth_soc.c: left out because mtk_eth_soc
does not have XDP support
- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c: left out because driver
is not enabled
- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c: left out because driver
is not enabled
- drivers/net/ethernet/freescale/enetc/enetc_pf.c: left out because driver
is not enabled
- drivers/net/ethernet/marvell/mvneta.c: left out because driver is not
enabled
- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c: left out because driver
is not enabled
- drivers/net/ethernet/socionext/netsec.c: left out because driver is not
enabled
- drivers/net/ethernet/ti/cpsw.c: left out because driver is not enabled
- drivers/net/ethernet/ti/cpsw_new.c: left out because driver is not
enabled
- drivers/net/ethernet/netronome/nfp/nfp_net_common.c: Context difference
due to missing 67d2656b48f1 ("nfp: support RX VLAN ctag/stag strip") and
7de8b691615f ("nfp: enable TSO by default for nfp netdev")
- drivers/net/ethernet/intel/ice/ice_main.c: Merge conflict upstream
between 5b246e533d01 ("ice: split probe into smaller functions") and this
commit. Resolved the same way as upstream in de4287336794 (pull-request:
bpf-next 2023-02-11): only add the XDP features to the existing
ice_cfg_netdev() function.
- drivers/net/ethernet/intel/i40e/i40e_main.c: Code difference because the
driver does not have support for frags/multi-buff. The last parameter of
xdp_features_set_redirect_target indicates if frags are supported for Tx,
we change the code to set it to false.
Omitted-fix: 1dc55923296d ("net: mvneta: do not set xdp_features for hw
buffer devices")
mvneta is not enabled and hunks in this commit are skipped
Omitted-fix: 481e96fc1307 ("mvpp2: take care of xdp_features when
reconfiguring queues")
mvpp2 is not enabled and hunks in this commit are skipped
Omitted-fix: e4ac7cc6e5a4 ("net: fec: turn on XDP features")
fec does not have XDP support
commit 66c0e13ad236c74ea88c7c1518f3cef7f372e3da
Author: Marek Majtyka <alardam@gmail.com>
Date: Wed Feb 1 11:24:18 2023 +0100
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>
Signed-off-by: Felix Maurer <fmaurer@redhat.com>
2023-05-10 09:50:53 +00:00
|
|
|
dev->xdp_features = NETDEV_XDP_ACT_HW_OFFLOAD;
|
2017-12-01 23:08:58 +00:00
|
|
|
}
|
|
|
|
|
2024-10-04 08:59:17 +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)
|
|
|
|
{
|
2024-05-14 09:14:24 +00:00
|
|
|
struct mock_phc *phc;
|
2021-06-02 12:17:17 +00:00
|
|
|
int err;
|
|
|
|
|
2024-05-14 09:14:24 +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-10-04 08:58:29 +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)
|
2024-05-14 09:14:24 +00:00
|
|
|
goto err_phc_destroy;
|
2021-06-02 12:17:17 +00:00
|
|
|
|
|
|
|
rtnl_lock();
|
2024-10-04 08:59:17 +00:00
|
|
|
err = nsim_queue_init(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
if (err)
|
|
|
|
goto err_utn_destroy;
|
|
|
|
|
2024-10-04 08:59:17 +00:00
|
|
|
err = nsim_bpf_init(ns);
|
|
|
|
if (err)
|
|
|
|
goto err_rq_destroy;
|
|
|
|
|
2024-02-07 09:06:55 +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);
|
2024-02-07 09:06:55 +00:00
|
|
|
nsim_macsec_teardown(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
nsim_bpf_uninit(ns);
|
2024-10-04 08:59:17 +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);
|
2024-05-14 09:14:24 +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-05-14 09:18:51 +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
|
|
|
|
2022-05-12 15:29: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);
|
2023-03-28 09:19:41 +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-10-04 08:58:55 +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-05-28 11:28:32 +00:00
|
|
|
struct netdevsim *peer;
|
2019-04-25 13:59:55 +00:00
|
|
|
|
2024-10-04 08:58:55 +00:00
|
|
|
debugfs_remove(ns->pp_dfs);
|
|
|
|
|
2019-04-25 13:59:55 +00:00
|
|
|
rtnl_lock();
|
2024-05-28 11:28:32 +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)) {
|
2024-02-07 09:06:55 +00:00
|
|
|
nsim_macsec_teardown(ns);
|
2021-06-02 12:17:17 +00:00
|
|
|
nsim_ipsec_teardown(ns);
|
|
|
|
nsim_bpf_uninit(ns);
|
2024-10-04 08:59:17 +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-05-14 09:18:51 +00:00
|
|
|
nsim_exit_netdevsim(ns);
|
2024-10-04 08:58:55 +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-05-28 11:28:32 +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)
|
|
|
|
{
|
2022-05-12 15:29: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");
|
2024-02-01 14:29:57 +00:00
|
|
|
MODULE_DESCRIPTION("Simulated networking device for testing");
|
2017-12-01 23:08:58 +00:00
|
|
|
MODULE_ALIAS_RTNL_LINK(DRV_NAME);
|