提交 6720f108 编写于 作者: D Daniel Borkmann

Merge branch 'bpf-xdp-stack-uninit-and-offload-tests'

Jakub Kicinski says:

====================
The purpose of this series is to add a software model of BPF offloads
to make it easier for everyone to test them and make some of the more
arcane rules and assumptions more clear.

The series starts with 3 patches aiming to make XDP handling in the
drivers less error prone.  Currently driver authors have to remember
to free XDP programs if XDP is active during unregister.  With this
series the core will disable XDP on its own.  It will take place
after close, drivers are not expected to perform reconfiguration
when disabling XDP on a downed device.

Next two patches add the software netdev driver, followed by a python
test which exercises all the corner cases which came to my mind.

Test needs to be run as root.  It will print basic information to
stdout, but can also create a more detailed log of all commands
when --log option is passed.  Log is in Emacs Org-mode format.

  ./tools/testing/selftests/bpf/test_offload.py --log /tmp/log

Last two patches replace the SR-IOV API implementation of dummy.

v3:
 - move the freeing of vfs to release (Phil).
v2:
 - free device from the release function;
 - use bus-based name generatin instead of netdev name.
v1:
 - replace the SR-IOV API implementation of dummy;
 - make the dev_xdp_uninstall() also handle the XDP generic (Daniel).
====================
Signed-off-by: NDaniel Borkmann <daniel@iogearbox.net>
...@@ -9599,6 +9599,11 @@ NETWORKING [WIRELESS] ...@@ -9599,6 +9599,11 @@ NETWORKING [WIRELESS]
L: linux-wireless@vger.kernel.org L: linux-wireless@vger.kernel.org
Q: http://patchwork.kernel.org/project/linux-wireless/list/ Q: http://patchwork.kernel.org/project/linux-wireless/list/
NETDEVSIM
M: Jakub Kicinski <jakub.kicinski@netronome.com>
S: Maintained
F: drivers/net/netdevsim/*
NETXEN (1/10) GbE SUPPORT NETXEN (1/10) GbE SUPPORT
M: Manish Chopra <manish.chopra@cavium.com> M: Manish Chopra <manish.chopra@cavium.com>
M: Rahul Verma <rahul.verma@cavium.com> M: Rahul Verma <rahul.verma@cavium.com>
......
...@@ -497,4 +497,15 @@ config THUNDERBOLT_NET ...@@ -497,4 +497,15 @@ config THUNDERBOLT_NET
source "drivers/net/hyperv/Kconfig" source "drivers/net/hyperv/Kconfig"
config NETDEVSIM
tristate "Simulated networking device"
depends on DEBUG_FS
help
This driver is a developer testing tool and software model that can
be used to test various control path networking APIs, especially
HW-offload related.
To compile this driver as a module, choose M here: the module
will be called netdevsim.
endif # NETDEVICES endif # NETDEVICES
...@@ -78,3 +78,4 @@ obj-$(CONFIG_FUJITSU_ES) += fjes/ ...@@ -78,3 +78,4 @@ obj-$(CONFIG_FUJITSU_ES) += fjes/
thunderbolt-net-y += thunderbolt.o thunderbolt-net-y += thunderbolt.o
obj-$(CONFIG_THUNDERBOLT_NET) += thunderbolt-net.o obj-$(CONFIG_THUNDERBOLT_NET) += thunderbolt-net.o
obj-$(CONFIG_NETDEVSIM) += netdevsim/
...@@ -42,48 +42,7 @@ ...@@ -42,48 +42,7 @@
#define DRV_NAME "dummy" #define DRV_NAME "dummy"
#define DRV_VERSION "1.0" #define DRV_VERSION "1.0"
#undef pr_fmt
#define pr_fmt(fmt) DRV_NAME ": " fmt
static int numdummies = 1; static int numdummies = 1;
static int num_vfs;
struct vf_data_storage {
u8 vf_mac[ETH_ALEN];
u16 pf_vlan; /* When set, guest VLAN config not allowed. */
u16 pf_qos;
__be16 vlan_proto;
u16 min_tx_rate;
u16 max_tx_rate;
u8 spoofchk_enabled;
bool rss_query_enabled;
u8 trusted;
int link_state;
};
struct dummy_priv {
struct vf_data_storage *vfinfo;
};
static int dummy_num_vf(struct device *dev)
{
return num_vfs;
}
static struct bus_type dummy_bus = {
.name = "dummy",
.num_vf = dummy_num_vf,
};
static void release_dummy_parent(struct device *dev)
{
}
static struct device dummy_parent = {
.init_name = "dummy",
.bus = &dummy_bus,
.release = release_dummy_parent,
};
/* fake multicast ability */ /* fake multicast ability */
static void set_multicast_list(struct net_device *dev) static void set_multicast_list(struct net_device *dev)
...@@ -133,25 +92,10 @@ static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -133,25 +92,10 @@ static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
static int dummy_dev_init(struct net_device *dev) static int dummy_dev_init(struct net_device *dev)
{ {
struct dummy_priv *priv = netdev_priv(dev);
dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
if (!dev->dstats) if (!dev->dstats)
return -ENOMEM; return -ENOMEM;
priv->vfinfo = NULL;
if (!num_vfs)
return 0;
dev->dev.parent = &dummy_parent;
priv->vfinfo = kcalloc(num_vfs, sizeof(struct vf_data_storage),
GFP_KERNEL);
if (!priv->vfinfo) {
free_percpu(dev->dstats);
return -ENOMEM;
}
return 0; return 0;
} }
...@@ -169,117 +113,6 @@ static int dummy_change_carrier(struct net_device *dev, bool new_carrier) ...@@ -169,117 +113,6 @@ static int dummy_change_carrier(struct net_device *dev, bool new_carrier)
return 0; return 0;
} }
static int dummy_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
{
struct dummy_priv *priv = netdev_priv(dev);
if (!is_valid_ether_addr(mac) || (vf >= num_vfs))
return -EINVAL;
memcpy(priv->vfinfo[vf].vf_mac, mac, ETH_ALEN);
return 0;
}
static int dummy_set_vf_vlan(struct net_device *dev, int vf,
u16 vlan, u8 qos, __be16 vlan_proto)
{
struct dummy_priv *priv = netdev_priv(dev);
if ((vf >= num_vfs) || (vlan > 4095) || (qos > 7))
return -EINVAL;
priv->vfinfo[vf].pf_vlan = vlan;
priv->vfinfo[vf].pf_qos = qos;
priv->vfinfo[vf].vlan_proto = vlan_proto;
return 0;
}
static int dummy_set_vf_rate(struct net_device *dev, int vf, int min, int max)
{
struct dummy_priv *priv = netdev_priv(dev);
if (vf >= num_vfs)
return -EINVAL;
priv->vfinfo[vf].min_tx_rate = min;
priv->vfinfo[vf].max_tx_rate = max;
return 0;
}
static int dummy_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
{
struct dummy_priv *priv = netdev_priv(dev);
if (vf >= num_vfs)
return -EINVAL;
priv->vfinfo[vf].spoofchk_enabled = val;
return 0;
}
static int dummy_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
{
struct dummy_priv *priv = netdev_priv(dev);
if (vf >= num_vfs)
return -EINVAL;
priv->vfinfo[vf].rss_query_enabled = val;
return 0;
}
static int dummy_set_vf_trust(struct net_device *dev, int vf, bool val)
{
struct dummy_priv *priv = netdev_priv(dev);
if (vf >= num_vfs)
return -EINVAL;
priv->vfinfo[vf].trusted = val;
return 0;
}
static int dummy_get_vf_config(struct net_device *dev,
int vf, struct ifla_vf_info *ivi)
{
struct dummy_priv *priv = netdev_priv(dev);
if (vf >= num_vfs)
return -EINVAL;
ivi->vf = vf;
memcpy(&ivi->mac, priv->vfinfo[vf].vf_mac, ETH_ALEN);
ivi->vlan = priv->vfinfo[vf].pf_vlan;
ivi->qos = priv->vfinfo[vf].pf_qos;
ivi->spoofchk = priv->vfinfo[vf].spoofchk_enabled;
ivi->linkstate = priv->vfinfo[vf].link_state;
ivi->min_tx_rate = priv->vfinfo[vf].min_tx_rate;
ivi->max_tx_rate = priv->vfinfo[vf].max_tx_rate;
ivi->rss_query_en = priv->vfinfo[vf].rss_query_enabled;
ivi->trusted = priv->vfinfo[vf].trusted;
ivi->vlan_proto = priv->vfinfo[vf].vlan_proto;
return 0;
}
static int dummy_set_vf_link_state(struct net_device *dev, int vf, int state)
{
struct dummy_priv *priv = netdev_priv(dev);
if (vf >= num_vfs)
return -EINVAL;
priv->vfinfo[vf].link_state = state;
return 0;
}
static const struct net_device_ops dummy_netdev_ops = { static const struct net_device_ops dummy_netdev_ops = {
.ndo_init = dummy_dev_init, .ndo_init = dummy_dev_init,
.ndo_uninit = dummy_dev_uninit, .ndo_uninit = dummy_dev_uninit,
...@@ -289,14 +122,6 @@ static const struct net_device_ops dummy_netdev_ops = { ...@@ -289,14 +122,6 @@ static const struct net_device_ops dummy_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr, .ndo_set_mac_address = eth_mac_addr,
.ndo_get_stats64 = dummy_get_stats64, .ndo_get_stats64 = dummy_get_stats64,
.ndo_change_carrier = dummy_change_carrier, .ndo_change_carrier = dummy_change_carrier,
.ndo_set_vf_mac = dummy_set_vf_mac,
.ndo_set_vf_vlan = dummy_set_vf_vlan,
.ndo_set_vf_rate = dummy_set_vf_rate,
.ndo_set_vf_spoofchk = dummy_set_vf_spoofchk,
.ndo_set_vf_trust = dummy_set_vf_trust,
.ndo_get_vf_config = dummy_get_vf_config,
.ndo_set_vf_link_state = dummy_set_vf_link_state,
.ndo_set_vf_rss_query_en = dummy_set_vf_rss_query_en,
}; };
static void dummy_get_drvinfo(struct net_device *dev, static void dummy_get_drvinfo(struct net_device *dev,
...@@ -323,13 +148,6 @@ static const struct ethtool_ops dummy_ethtool_ops = { ...@@ -323,13 +148,6 @@ static const struct ethtool_ops dummy_ethtool_ops = {
.get_ts_info = dummy_get_ts_info, .get_ts_info = dummy_get_ts_info,
}; };
static void dummy_free_netdev(struct net_device *dev)
{
struct dummy_priv *priv = netdev_priv(dev);
kfree(priv->vfinfo);
}
static void dummy_setup(struct net_device *dev) static void dummy_setup(struct net_device *dev)
{ {
ether_setup(dev); ether_setup(dev);
...@@ -338,7 +156,6 @@ static void dummy_setup(struct net_device *dev) ...@@ -338,7 +156,6 @@ static void dummy_setup(struct net_device *dev)
dev->netdev_ops = &dummy_netdev_ops; dev->netdev_ops = &dummy_netdev_ops;
dev->ethtool_ops = &dummy_ethtool_ops; dev->ethtool_ops = &dummy_ethtool_ops;
dev->needs_free_netdev = true; dev->needs_free_netdev = true;
dev->priv_destructor = dummy_free_netdev;
/* Fill in device structure with ethernet-generic values. */ /* Fill in device structure with ethernet-generic values. */
dev->flags |= IFF_NOARP; dev->flags |= IFF_NOARP;
...@@ -370,7 +187,6 @@ static int dummy_validate(struct nlattr *tb[], struct nlattr *data[], ...@@ -370,7 +187,6 @@ static int dummy_validate(struct nlattr *tb[], struct nlattr *data[],
static struct rtnl_link_ops dummy_link_ops __read_mostly = { static struct rtnl_link_ops dummy_link_ops __read_mostly = {
.kind = DRV_NAME, .kind = DRV_NAME,
.priv_size = sizeof(struct dummy_priv),
.setup = dummy_setup, .setup = dummy_setup,
.validate = dummy_validate, .validate = dummy_validate,
}; };
...@@ -379,16 +195,12 @@ static struct rtnl_link_ops dummy_link_ops __read_mostly = { ...@@ -379,16 +195,12 @@ static struct rtnl_link_ops dummy_link_ops __read_mostly = {
module_param(numdummies, int, 0); module_param(numdummies, int, 0);
MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices"); MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices");
module_param(num_vfs, int, 0);
MODULE_PARM_DESC(num_vfs, "Number of dummy VFs per dummy device");
static int __init dummy_init_one(void) static int __init dummy_init_one(void)
{ {
struct net_device *dev_dummy; struct net_device *dev_dummy;
int err; int err;
dev_dummy = alloc_netdev(sizeof(struct dummy_priv), dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_ENUM, dummy_setup);
"dummy%d", NET_NAME_ENUM, dummy_setup);
if (!dev_dummy) if (!dev_dummy)
return -ENOMEM; return -ENOMEM;
...@@ -407,21 +219,6 @@ static int __init dummy_init_module(void) ...@@ -407,21 +219,6 @@ static int __init dummy_init_module(void)
{ {
int i, err = 0; int i, err = 0;
if (num_vfs) {
err = bus_register(&dummy_bus);
if (err < 0) {
pr_err("registering dummy bus failed\n");
return err;
}
err = device_register(&dummy_parent);
if (err < 0) {
pr_err("registering dummy parent device failed\n");
bus_unregister(&dummy_bus);
return err;
}
}
rtnl_lock(); rtnl_lock();
err = __rtnl_link_register(&dummy_link_ops); err = __rtnl_link_register(&dummy_link_ops);
if (err < 0) if (err < 0)
...@@ -437,22 +234,12 @@ static int __init dummy_init_module(void) ...@@ -437,22 +234,12 @@ static int __init dummy_init_module(void)
out: out:
rtnl_unlock(); rtnl_unlock();
if (err && num_vfs) {
device_unregister(&dummy_parent);
bus_unregister(&dummy_bus);
}
return err; return err;
} }
static void __exit dummy_cleanup_module(void) static void __exit dummy_cleanup_module(void)
{ {
rtnl_link_unregister(&dummy_link_ops); rtnl_link_unregister(&dummy_link_ops);
if (num_vfs) {
device_unregister(&dummy_parent);
bus_unregister(&dummy_bus);
}
} }
module_init(dummy_init_module); module_init(dummy_init_module);
......
...@@ -7800,8 +7800,6 @@ static void bnxt_remove_one(struct pci_dev *pdev) ...@@ -7800,8 +7800,6 @@ static void bnxt_remove_one(struct pci_dev *pdev)
bnxt_dcb_free(bp); bnxt_dcb_free(bp);
kfree(bp->edev); kfree(bp->edev);
bp->edev = NULL; bp->edev = NULL;
if (bp->xdp_prog)
bpf_prog_put(bp->xdp_prog);
bnxt_cleanup_pci(bp); bnxt_cleanup_pci(bp);
free_netdev(dev); free_netdev(dev);
} }
......
...@@ -4308,9 +4308,6 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) ...@@ -4308,9 +4308,6 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
{ {
mlx5e_ipsec_cleanup(priv); mlx5e_ipsec_cleanup(priv);
mlx5e_vxlan_cleanup(priv); mlx5e_vxlan_cleanup(priv);
if (priv->channels.params.xdp_prog)
bpf_prog_put(priv->channels.params.xdp_prog);
} }
static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) static int mlx5e_init_nic_rx(struct mlx5e_priv *priv)
......
...@@ -82,12 +82,6 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn) ...@@ -82,12 +82,6 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn)
return nfp_net_ebpf_capable(nn) ? "BPF" : ""; return nfp_net_ebpf_capable(nn) ? "BPF" : "";
} }
static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn)
{
if (nn->dp.bpf_offload_xdp)
nfp_bpf_xdp_offload(app, nn, NULL);
}
static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type, static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv) void *type_data, void *cb_priv)
{ {
...@@ -168,7 +162,6 @@ const struct nfp_app_type app_bpf = { ...@@ -168,7 +162,6 @@ const struct nfp_app_type app_bpf = {
.extra_cap = nfp_bpf_extra_cap, .extra_cap = nfp_bpf_extra_cap,
.vnic_alloc = nfp_app_nic_vnic_alloc, .vnic_alloc = nfp_app_nic_vnic_alloc,
.vnic_free = nfp_bpf_vnic_free,
.setup_tc = nfp_bpf_setup_tc, .setup_tc = nfp_bpf_setup_tc,
.tc_busy = nfp_bpf_tc_busy, .tc_busy = nfp_bpf_tc_busy,
......
...@@ -3392,6 +3392,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp) ...@@ -3392,6 +3392,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
if (nn->dp.bpf_offload_xdp) if (nn->dp.bpf_offload_xdp)
xdp->prog_attached = XDP_ATTACHED_HW; xdp->prog_attached = XDP_ATTACHED_HW;
xdp->prog_id = nn->xdp_prog ? nn->xdp_prog->aux->id : 0; xdp->prog_id = nn->xdp_prog ? nn->xdp_prog->aux->id : 0;
xdp->flags = nn->xdp_prog ? nn->xdp_flags : 0;
return 0; return 0;
case BPF_OFFLOAD_VERIFIER_PREP: case BPF_OFFLOAD_VERIFIER_PREP:
return nfp_app_bpf_verifier_prep(nn->app, nn, xdp); return nfp_app_bpf_verifier_prep(nn->app, nn, xdp);
...@@ -3561,9 +3562,6 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev, ...@@ -3561,9 +3562,6 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
*/ */
void nfp_net_free(struct nfp_net *nn) void nfp_net_free(struct nfp_net *nn)
{ {
if (nn->xdp_prog)
bpf_prog_put(nn->xdp_prog);
if (nn->dp.netdev) if (nn->dp.netdev)
free_netdev(nn->dp.netdev); free_netdev(nn->dp.netdev);
else else
......
...@@ -1068,10 +1068,6 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) ...@@ -1068,10 +1068,6 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
/* Release edev's reference to XDP's bpf if such exist */
if (edev->xdp_prog)
bpf_prog_put(edev->xdp_prog);
/* Use global ops since we've freed edev */ /* Use global ops since we've freed edev */
qed_ops->common->slowpath_stop(cdev); qed_ops->common->slowpath_stop(cdev);
if (system_state == SYSTEM_POWER_OFF) if (system_state == SYSTEM_POWER_OFF)
......
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NETDEVSIM) += netdevsim.o
netdevsim-objs := \
netdev.o \
bpf.o \
/*
* 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.
*/
#include <linux/bpf.h>
#include <linux/bpf_verifier.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/rtnetlink.h>
#include <net/pkt_cls.h>
#include "netdevsim.h"
struct nsim_bpf_bound_prog {
struct netdevsim *ns;
struct bpf_prog *prog;
struct dentry *ddir;
const char *state;
bool is_loaded;
struct list_head l;
};
static int nsim_debugfs_bpf_string_read(struct seq_file *file, void *data)
{
const char **str = file->private;
if (*str)
seq_printf(file, "%s\n", *str);
return 0;
}
static int nsim_debugfs_bpf_string_open(struct inode *inode, struct file *f)
{
return single_open(f, nsim_debugfs_bpf_string_read, inode->i_private);
}
static const struct file_operations nsim_bpf_string_fops = {
.owner = THIS_MODULE,
.open = nsim_debugfs_bpf_string_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek
};
static int
nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)
{
struct nsim_bpf_bound_prog *state;
state = env->prog->aux->offload->dev_priv;
if (state->ns->bpf_bind_verifier_delay && !insn_idx)
msleep(state->ns->bpf_bind_verifier_delay);
return 0;
}
static const struct bpf_ext_analyzer_ops nsim_bpf_analyzer_ops = {
.insn_hook = nsim_bpf_verify_insn,
};
static bool nsim_xdp_offload_active(struct netdevsim *ns)
{
return ns->xdp_prog_mode == XDP_ATTACHED_HW;
}
static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded)
{
struct nsim_bpf_bound_prog *state;
if (!prog || !prog->aux->offload)
return;
state = prog->aux->offload->dev_priv;
state->is_loaded = loaded;
}
static int
nsim_bpf_offload(struct netdevsim *ns, struct bpf_prog *prog, bool oldprog)
{
nsim_prog_set_loaded(ns->bpf_offloaded, false);
WARN(!!ns->bpf_offloaded != oldprog,
"bad offload state, expected offload %sto be active",
oldprog ? "" : "not ");
ns->bpf_offloaded = prog;
ns->bpf_offloaded_id = prog ? prog->aux->id : 0;
nsim_prog_set_loaded(prog, true);
return 0;
}
int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
struct tc_cls_bpf_offload *cls_bpf = type_data;
struct bpf_prog *prog = cls_bpf->prog;
struct netdevsim *ns = cb_priv;
bool skip_sw;
if (type != TC_SETUP_CLSBPF ||
!tc_can_offload(ns->netdev) ||
cls_bpf->common.protocol != htons(ETH_P_ALL) ||
cls_bpf->common.chain_index)
return -EOPNOTSUPP;
skip_sw = cls_bpf->gen_flags & TCA_CLS_FLAGS_SKIP_SW;
if (nsim_xdp_offload_active(ns))
return -EBUSY;
if (!ns->bpf_tc_accept)
return -EOPNOTSUPP;
/* Note: progs without skip_sw will probably not be dev bound */
if (prog && !prog->aux->offload && !ns->bpf_tc_non_bound_accept)
return -EOPNOTSUPP;
switch (cls_bpf->command) {
case TC_CLSBPF_REPLACE:
return nsim_bpf_offload(ns, prog, true);
case TC_CLSBPF_ADD:
return nsim_bpf_offload(ns, prog, false);
case TC_CLSBPF_DESTROY:
return nsim_bpf_offload(ns, NULL, true);
default:
return -EOPNOTSUPP;
}
}
int nsim_bpf_disable_tc(struct netdevsim *ns)
{
if (ns->bpf_offloaded && !nsim_xdp_offload_active(ns))
return -EBUSY;
return 0;
}
static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
{
if (!nsim_xdp_offload_active(ns) && !bpf->prog)
return 0;
if (!nsim_xdp_offload_active(ns) && bpf->prog && ns->bpf_offloaded) {
NSIM_EA(bpf->extack, "TC program is already loaded");
return -EBUSY;
}
return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns));
}
static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
{
int err;
if (ns->xdp_prog && (bpf->flags ^ ns->xdp_flags) & XDP_FLAGS_MODES) {
NSIM_EA(bpf->extack, "program loaded with different flags");
return -EBUSY;
}
if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) {
NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS");
return -EOPNOTSUPP;
}
if (bpf->command == XDP_SETUP_PROG_HW && !ns->bpf_xdpoffload_accept) {
NSIM_EA(bpf->extack, "XDP offload disabled in DebugFS");
return -EOPNOTSUPP;
}
if (bpf->command == XDP_SETUP_PROG_HW) {
err = nsim_xdp_offload_prog(ns, bpf);
if (err)
return err;
}
if (ns->xdp_prog)
bpf_prog_put(ns->xdp_prog);
ns->xdp_prog = bpf->prog;
ns->xdp_flags = bpf->flags;
if (!bpf->prog)
ns->xdp_prog_mode = XDP_ATTACHED_NONE;
else if (bpf->command == XDP_SETUP_PROG)
ns->xdp_prog_mode = XDP_ATTACHED_DRV;
else
ns->xdp_prog_mode = XDP_ATTACHED_HW;
return 0;
}
int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog)
{
struct nsim_bpf_bound_prog *state;
char name[16];
int err;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
state->ns = ns;
state->prog = prog;
state->state = "verify";
/* Program id is not populated yet when we create the state. */
sprintf(name, "%u", ns->prog_id_gen++);
state->ddir = debugfs_create_dir(name, ns->ddir_bpf_bound_progs);
if (IS_ERR(state->ddir)) {
err = PTR_ERR(state->ddir);
kfree(state);
return err;
}
debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id);
debugfs_create_file("state", 0400, state->ddir,
&state->state, &nsim_bpf_string_fops);
debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded);
list_add_tail(&state->l, &ns->bpf_bound_progs);
prog->aux->offload->dev_priv = state;
return 0;
}
void nsim_bpf_destroy_prog(struct bpf_prog *prog)
{
struct nsim_bpf_bound_prog *state;
state = prog->aux->offload->dev_priv;
WARN(state->is_loaded,
"offload state destroyed while program still bound");
debugfs_remove_recursive(state->ddir);
list_del(&state->l);
kfree(state);
}
static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
{
if (bpf->prog && bpf->prog->aux->offload) {
NSIM_EA(bpf->extack, "attempt to load offloaded prog to drv");
return -EINVAL;
}
if (ns->netdev->mtu > NSIM_XDP_MAX_MTU) {
NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled");
return -EINVAL;
}
if (nsim_xdp_offload_active(ns)) {
NSIM_EA(bpf->extack, "xdp offload active, can't load drv prog");
return -EBUSY;
}
return 0;
}
static int
nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
{
struct nsim_bpf_bound_prog *state;
if (!bpf->prog)
return 0;
if (!bpf->prog->aux->offload) {
NSIM_EA(bpf->extack, "xdpoffload of non-bound program");
return -EINVAL;
}
if (bpf->prog->aux->offload->netdev != ns->netdev) {
NSIM_EA(bpf->extack, "program bound to different dev");
return -EINVAL;
}
state = bpf->prog->aux->offload->dev_priv;
if (WARN_ON(strcmp(state->state, "xlated"))) {
NSIM_EA(bpf->extack, "offloading program in bad state");
return -EINVAL;
}
return 0;
}
int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bpf_bound_prog *state;
int err;
ASSERT_RTNL();
switch (bpf->command) {
case BPF_OFFLOAD_VERIFIER_PREP:
if (!ns->bpf_bind_accept)
return -EOPNOTSUPP;
err = nsim_bpf_create_prog(ns, bpf->verifier.prog);
if (err)
return err;
bpf->verifier.ops = &nsim_bpf_analyzer_ops;
return 0;
case BPF_OFFLOAD_TRANSLATE:
state = bpf->offload.prog->aux->offload->dev_priv;
state->state = "xlated";
return 0;
case BPF_OFFLOAD_DESTROY:
nsim_bpf_destroy_prog(bpf->offload.prog);
return 0;
case XDP_QUERY_PROG:
bpf->prog_attached = ns->xdp_prog_mode;
bpf->prog_id = ns->xdp_prog ? ns->xdp_prog->aux->id : 0;
bpf->prog_flags = ns->xdp_prog ? ns->xdp_flags : 0;
return 0;
case XDP_SETUP_PROG:
err = nsim_setup_prog_checks(ns, bpf);
if (err)
return err;
return nsim_xdp_set_prog(ns, bpf);
case XDP_SETUP_PROG_HW:
err = nsim_setup_prog_hw_checks(ns, bpf);
if (err)
return err;
return nsim_xdp_set_prog(ns, bpf);
default:
return -EINVAL;
}
}
int nsim_bpf_init(struct netdevsim *ns)
{
INIT_LIST_HEAD(&ns->bpf_bound_progs);
debugfs_create_u32("bpf_offloaded_id", 0400, ns->ddir,
&ns->bpf_offloaded_id);
ns->bpf_bind_accept = true;
debugfs_create_bool("bpf_bind_accept", 0600, ns->ddir,
&ns->bpf_bind_accept);
debugfs_create_u32("bpf_bind_verifier_delay", 0600, ns->ddir,
&ns->bpf_bind_verifier_delay);
ns->ddir_bpf_bound_progs =
debugfs_create_dir("bpf_bound_progs", ns->ddir);
ns->bpf_tc_accept = true;
debugfs_create_bool("bpf_tc_accept", 0600, ns->ddir,
&ns->bpf_tc_accept);
debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ns->ddir,
&ns->bpf_tc_non_bound_accept);
ns->bpf_xdpdrv_accept = true;
debugfs_create_bool("bpf_xdpdrv_accept", 0600, ns->ddir,
&ns->bpf_xdpdrv_accept);
ns->bpf_xdpoffload_accept = true;
debugfs_create_bool("bpf_xdpoffload_accept", 0600, ns->ddir,
&ns->bpf_xdpoffload_accept);
return 0;
}
void nsim_bpf_uninit(struct netdevsim *ns)
{
WARN_ON(!list_empty(&ns->bpf_bound_progs));
WARN_ON(ns->xdp_prog);
WARN_ON(ns->bpf_offloaded);
}
/*
* 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.
*/
#include <linux/debugfs.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <net/netlink.h>
#include <net/pkt_cls.h>
#include <net/rtnetlink.h>
#include "netdevsim.h"
struct nsim_vf_config {
int link_state;
u16 min_tx_rate;
u16 max_tx_rate;
u16 vlan;
__be16 vlan_proto;
u16 qos;
u8 vf_mac[ETH_ALEN];
bool spoofchk_enabled;
bool trusted;
bool rss_query_enabled;
};
static u32 nsim_dev_id;
static int nsim_num_vf(struct device *dev)
{
struct netdevsim *ns = to_nsim(dev);
return ns->num_vfs;
}
static struct bus_type nsim_bus = {
.name = DRV_NAME,
.dev_name = DRV_NAME,
.num_vf = nsim_num_vf,
};
static int nsim_vfs_enable(struct netdevsim *ns, unsigned int num_vfs)
{
ns->vfconfigs = kcalloc(num_vfs, sizeof(struct nsim_vf_config),
GFP_KERNEL);
if (!ns->vfconfigs)
return -ENOMEM;
ns->num_vfs = num_vfs;
return 0;
}
static void nsim_vfs_disable(struct netdevsim *ns)
{
kfree(ns->vfconfigs);
ns->vfconfigs = NULL;
ns->num_vfs = 0;
}
static ssize_t
nsim_numvfs_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct netdevsim *ns = to_nsim(dev);
unsigned int num_vfs;
int ret;
ret = kstrtouint(buf, 0, &num_vfs);
if (ret)
return ret;
rtnl_lock();
if (ns->num_vfs == num_vfs)
goto exit_good;
if (ns->num_vfs && num_vfs) {
ret = -EBUSY;
goto exit_unlock;
}
if (num_vfs) {
ret = nsim_vfs_enable(ns, num_vfs);
if (ret)
goto exit_unlock;
} else {
nsim_vfs_disable(ns);
}
exit_good:
ret = count;
exit_unlock:
rtnl_unlock();
return ret;
}
static ssize_t
nsim_numvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct netdevsim *ns = to_nsim(dev);
return sprintf(buf, "%u\n", ns->num_vfs);
}
static struct device_attribute nsim_numvfs_attr =
__ATTR(sriov_numvfs, 0664, nsim_numvfs_show, nsim_numvfs_store);
static struct attribute *nsim_dev_attrs[] = {
&nsim_numvfs_attr.attr,
NULL,
};
static const struct attribute_group nsim_dev_attr_group = {
.attrs = nsim_dev_attrs,
};
static const struct attribute_group *nsim_dev_attr_groups[] = {
&nsim_dev_attr_group,
NULL,
};
static void nsim_dev_release(struct device *dev)
{
struct netdevsim *ns = to_nsim(dev);
nsim_vfs_disable(ns);
free_netdev(ns->netdev);
}
struct device_type nsim_dev_type = {
.groups = nsim_dev_attr_groups,
.release = nsim_dev_release,
};
static int nsim_init(struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
int err;
ns->netdev = dev;
ns->ddir = debugfs_create_dir(netdev_name(dev), nsim_ddir);
err = nsim_bpf_init(ns);
if (err)
goto err_debugfs_destroy;
ns->dev.id = nsim_dev_id++;
ns->dev.bus = &nsim_bus;
ns->dev.type = &nsim_dev_type;
err = device_register(&ns->dev);
if (err)
goto err_bpf_uninit;
SET_NETDEV_DEV(dev, &ns->dev);
return 0;
err_bpf_uninit:
nsim_bpf_uninit(ns);
err_debugfs_destroy:
debugfs_remove_recursive(ns->ddir);
return err;
}
static void nsim_uninit(struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
debugfs_remove_recursive(ns->ddir);
nsim_bpf_uninit(ns);
}
static void nsim_free(struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
device_unregister(&ns->dev);
/* netdev and vf state will be freed out of device_release() */
}
static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
u64_stats_update_begin(&ns->syncp);
ns->tx_packets++;
ns->tx_bytes += skb->len;
u64_stats_update_end(&ns->syncp);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static void nsim_set_rx_mode(struct net_device *dev)
{
}
static int nsim_change_mtu(struct net_device *dev, int new_mtu)
{
struct netdevsim *ns = netdev_priv(dev);
if (ns->xdp_prog_mode == XDP_ATTACHED_DRV &&
new_mtu > NSIM_XDP_MAX_MTU)
return -EBUSY;
dev->mtu = new_mtu;
return 0;
}
static void
nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
struct netdevsim *ns = netdev_priv(dev);
unsigned int start;
do {
start = u64_stats_fetch_begin(&ns->syncp);
stats->tx_bytes = ns->tx_bytes;
stats->tx_packets = ns->tx_packets;
} while (u64_stats_fetch_retry(&ns->syncp, start));
}
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);
}
static int
nsim_setup_tc_block(struct net_device *dev, struct tc_block_offload *f)
{
struct netdevsim *ns = netdev_priv(dev);
if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
return -EOPNOTSUPP;
switch (f->command) {
case TC_BLOCK_BIND:
return tcf_block_cb_register(f->block, nsim_setup_tc_block_cb,
ns, ns);
case TC_BLOCK_UNBIND:
tcf_block_cb_unregister(f->block, nsim_setup_tc_block_cb, ns);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
{
struct netdevsim *ns = netdev_priv(dev);
/* Only refuse multicast addresses, zero address can mean unset/any. */
if (vf >= ns->num_vfs || is_multicast_ether_addr(mac))
return -EINVAL;
memcpy(ns->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
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);
if (vf >= ns->num_vfs || vlan > 4095 || qos > 7)
return -EINVAL;
ns->vfconfigs[vf].vlan = vlan;
ns->vfconfigs[vf].qos = qos;
ns->vfconfigs[vf].vlan_proto = vlan_proto;
return 0;
}
static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
{
struct netdevsim *ns = netdev_priv(dev);
if (vf >= ns->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].min_tx_rate = min;
ns->vfconfigs[vf].max_tx_rate = max;
return 0;
}
static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
{
struct netdevsim *ns = netdev_priv(dev);
if (vf >= ns->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].spoofchk_enabled = val;
return 0;
}
static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
{
struct netdevsim *ns = netdev_priv(dev);
if (vf >= ns->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].rss_query_enabled = val;
return 0;
}
static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val)
{
struct netdevsim *ns = netdev_priv(dev);
if (vf >= ns->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].trusted = val;
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);
if (vf >= ns->num_vfs)
return -EINVAL;
ivi->vf = vf;
ivi->linkstate = ns->vfconfigs[vf].link_state;
ivi->min_tx_rate = ns->vfconfigs[vf].min_tx_rate;
ivi->max_tx_rate = ns->vfconfigs[vf].max_tx_rate;
ivi->vlan = ns->vfconfigs[vf].vlan;
ivi->vlan_proto = ns->vfconfigs[vf].vlan_proto;
ivi->qos = ns->vfconfigs[vf].qos;
memcpy(&ivi->mac, ns->vfconfigs[vf].vf_mac, ETH_ALEN);
ivi->spoofchk = ns->vfconfigs[vf].spoofchk_enabled;
ivi->trusted = ns->vfconfigs[vf].trusted;
ivi->rss_query_en = ns->vfconfigs[vf].rss_query_enabled;
return 0;
}
static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
{
struct netdevsim *ns = netdev_priv(dev);
if (vf >= ns->num_vfs)
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;
}
ns->vfconfigs[vf].link_state = state;
return 0;
}
static int
nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
{
switch (type) {
case TC_SETUP_BLOCK:
return nsim_setup_tc_block(dev, type_data);
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;
}
static const struct net_device_ops nsim_netdev_ops = {
.ndo_init = nsim_init,
.ndo_uninit = nsim_uninit,
.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_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,
.ndo_setup_tc = nsim_setup_tc,
.ndo_set_features = nsim_set_features,
.ndo_bpf = nsim_bpf,
};
static void nsim_setup(struct net_device *dev)
{
ether_setup(dev);
eth_hw_addr_random(dev);
dev->netdev_ops = &nsim_netdev_ops;
dev->priv_destructor = nsim_free;
dev->tx_queue_len = 0;
dev->flags |= IFF_NOARP;
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;
dev->hw_features |= NETIF_F_HW_TC;
dev->max_mtu = ETH_MAX_MTU;
}
static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
if (tb[IFLA_ADDRESS]) {
if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
return -EINVAL;
if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
return -EADDRNOTAVAIL;
}
return 0;
}
static struct rtnl_link_ops nsim_link_ops __read_mostly = {
.kind = DRV_NAME,
.priv_size = sizeof(struct netdevsim),
.setup = nsim_setup,
.validate = nsim_validate,
};
struct dentry *nsim_ddir;
static int __init nsim_module_init(void)
{
int err;
nsim_ddir = debugfs_create_dir(DRV_NAME, NULL);
if (IS_ERR(nsim_ddir))
return PTR_ERR(nsim_ddir);
err = bus_register(&nsim_bus);
if (err)
goto err_debugfs_destroy;
err = rtnl_link_register(&nsim_link_ops);
if (err)
goto err_unreg_bus;
return 0;
err_unreg_bus:
bus_unregister(&nsim_bus);
err_debugfs_destroy:
debugfs_remove_recursive(nsim_ddir);
return err;
}
static void __exit nsim_module_exit(void)
{
rtnl_link_unregister(&nsim_link_ops);
bus_unregister(&nsim_bus);
debugfs_remove_recursive(nsim_ddir);
}
module_init(nsim_module_init);
module_exit(nsim_module_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_RTNL_LINK(DRV_NAME);
/*
* 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.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/u64_stats_sync.h>
#define DRV_NAME "netdevsim"
#define NSIM_XDP_MAX_MTU 4000
#define NSIM_EA(extack, msg) NL_SET_ERR_MSG_MOD((extack), msg)
struct bpf_prog;
struct dentry;
struct nsim_vf_config;
struct netdevsim {
struct net_device *netdev;
u64 tx_packets;
u64 tx_bytes;
struct u64_stats_sync syncp;
struct device dev;
struct dentry *ddir;
unsigned int num_vfs;
struct nsim_vf_config *vfconfigs;
struct bpf_prog *bpf_offloaded;
u32 bpf_offloaded_id;
u32 xdp_flags;
int xdp_prog_mode;
struct bpf_prog *xdp_prog;
u32 prog_id_gen;
bool bpf_bind_accept;
u32 bpf_bind_verifier_delay;
struct dentry *ddir_bpf_bound_progs;
struct list_head bpf_bound_progs;
bool bpf_tc_accept;
bool bpf_tc_non_bound_accept;
bool bpf_xdpdrv_accept;
bool bpf_xdpoffload_accept;
};
extern struct dentry *nsim_ddir;
int nsim_bpf_init(struct netdevsim *ns);
void nsim_bpf_uninit(struct netdevsim *ns);
int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf);
int nsim_bpf_disable_tc(struct netdevsim *ns);
int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv);
static inline struct netdevsim *to_nsim(struct device *ptr)
{
return container_of(ptr, struct netdevsim, dev);
}
...@@ -673,7 +673,6 @@ static void tun_detach(struct tun_file *tfile, bool clean) ...@@ -673,7 +673,6 @@ static void tun_detach(struct tun_file *tfile, bool clean)
static void tun_detach_all(struct net_device *dev) static void tun_detach_all(struct net_device *dev)
{ {
struct tun_struct *tun = netdev_priv(dev); struct tun_struct *tun = netdev_priv(dev);
struct bpf_prog *xdp_prog = rtnl_dereference(tun->xdp_prog);
struct tun_file *tfile, *tmp; struct tun_file *tfile, *tmp;
int i, n = tun->numqueues; int i, n = tun->numqueues;
...@@ -708,9 +707,6 @@ static void tun_detach_all(struct net_device *dev) ...@@ -708,9 +707,6 @@ static void tun_detach_all(struct net_device *dev)
} }
BUG_ON(tun->numdisabled != 0); BUG_ON(tun->numdisabled != 0);
if (xdp_prog)
bpf_prog_put(xdp_prog);
if (tun->flags & IFF_PERSIST) if (tun->flags & IFF_PERSIST)
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
......
...@@ -820,6 +820,8 @@ struct netdev_bpf { ...@@ -820,6 +820,8 @@ struct netdev_bpf {
struct { struct {
u8 prog_attached; u8 prog_attached;
u32 prog_id; u32 prog_id;
/* flags with which program was installed */
u32 prog_flags;
}; };
/* BPF_OFFLOAD_VERIFIER_PREP */ /* BPF_OFFLOAD_VERIFIER_PREP */
struct { struct {
...@@ -3330,7 +3332,8 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, ...@@ -3330,7 +3332,8 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf); typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf);
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
int fd, u32 flags); int fd, u32 flags);
u8 __dev_xdp_attached(struct net_device *dev, bpf_op_t xdp_op, u32 *prog_id); void __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op,
struct netdev_bpf *xdp);
int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
......
...@@ -7073,17 +7073,21 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down) ...@@ -7073,17 +7073,21 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)
} }
EXPORT_SYMBOL(dev_change_proto_down); EXPORT_SYMBOL(dev_change_proto_down);
u8 __dev_xdp_attached(struct net_device *dev, bpf_op_t bpf_op, u32 *prog_id) void __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op,
struct netdev_bpf *xdp)
{ {
struct netdev_bpf xdp; memset(xdp, 0, sizeof(*xdp));
xdp->command = XDP_QUERY_PROG;
memset(&xdp, 0, sizeof(xdp));
xdp.command = XDP_QUERY_PROG;
/* Query must always succeed. */ /* Query must always succeed. */
WARN_ON(bpf_op(dev, &xdp) < 0); WARN_ON(bpf_op(dev, xdp) < 0);
if (prog_id) }
*prog_id = xdp.prog_id;
static u8 __dev_xdp_attached(struct net_device *dev, bpf_op_t bpf_op)
{
struct netdev_bpf xdp;
__dev_xdp_query(dev, bpf_op, &xdp);
return xdp.prog_attached; return xdp.prog_attached;
} }
...@@ -7106,6 +7110,27 @@ static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, ...@@ -7106,6 +7110,27 @@ static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op,
return bpf_op(dev, &xdp); return bpf_op(dev, &xdp);
} }
static void dev_xdp_uninstall(struct net_device *dev)
{
struct netdev_bpf xdp;
bpf_op_t ndo_bpf;
/* Remove generic XDP */
WARN_ON(dev_xdp_install(dev, generic_xdp_install, NULL, 0, NULL));
/* Remove from the driver */
ndo_bpf = dev->netdev_ops->ndo_bpf;
if (!ndo_bpf)
return;
__dev_xdp_query(dev, ndo_bpf, &xdp);
if (xdp.prog_attached == XDP_ATTACHED_NONE)
return;
/* Program removal should always succeed */
WARN_ON(dev_xdp_install(dev, ndo_bpf, NULL, xdp.prog_flags, NULL));
}
/** /**
* dev_change_xdp_fd - set or clear a bpf program for a device rx path * dev_change_xdp_fd - set or clear a bpf program for a device rx path
* @dev: device * @dev: device
...@@ -7134,10 +7159,10 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, ...@@ -7134,10 +7159,10 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
bpf_chk = generic_xdp_install; bpf_chk = generic_xdp_install;
if (fd >= 0) { if (fd >= 0) {
if (bpf_chk && __dev_xdp_attached(dev, bpf_chk, NULL)) if (bpf_chk && __dev_xdp_attached(dev, bpf_chk))
return -EEXIST; return -EEXIST;
if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) &&
__dev_xdp_attached(dev, bpf_op, NULL)) __dev_xdp_attached(dev, bpf_op))
return -EBUSY; return -EBUSY;
prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP, prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP,
...@@ -7236,6 +7261,7 @@ static void rollback_registered_many(struct list_head *head) ...@@ -7236,6 +7261,7 @@ static void rollback_registered_many(struct list_head *head)
/* Shutdown queueing discipline. */ /* Shutdown queueing discipline. */
dev_shutdown(dev); dev_shutdown(dev);
dev_xdp_uninstall(dev);
/* Notify protocols, that we are about to destroy /* Notify protocols, that we are about to destroy
* this device. They should clean all the things. * this device. They should clean all the things.
...@@ -8195,7 +8221,6 @@ EXPORT_SYMBOL(alloc_netdev_mqs); ...@@ -8195,7 +8221,6 @@ EXPORT_SYMBOL(alloc_netdev_mqs);
void free_netdev(struct net_device *dev) void free_netdev(struct net_device *dev)
{ {
struct napi_struct *p, *n; struct napi_struct *p, *n;
struct bpf_prog *prog;
might_sleep(); might_sleep();
netif_free_tx_queues(dev); netif_free_tx_queues(dev);
...@@ -8214,12 +8239,6 @@ void free_netdev(struct net_device *dev) ...@@ -8214,12 +8239,6 @@ void free_netdev(struct net_device *dev)
free_percpu(dev->pcpu_refcnt); free_percpu(dev->pcpu_refcnt);
dev->pcpu_refcnt = NULL; dev->pcpu_refcnt = NULL;
prog = rcu_dereference_protected(dev->xdp_prog, 1);
if (prog) {
bpf_prog_put(prog);
static_key_slow_dec(&generic_xdp_needed);
}
/* Compatibility with error handling in drivers */ /* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) { if (dev->reg_state == NETREG_UNINITIALIZED) {
netdev_freemem(dev); netdev_freemem(dev);
......
...@@ -1261,6 +1261,7 @@ static u8 rtnl_xdp_attached_mode(struct net_device *dev, u32 *prog_id) ...@@ -1261,6 +1261,7 @@ static u8 rtnl_xdp_attached_mode(struct net_device *dev, u32 *prog_id)
{ {
const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_ops *ops = dev->netdev_ops;
const struct bpf_prog *generic_xdp_prog; const struct bpf_prog *generic_xdp_prog;
struct netdev_bpf xdp;
ASSERT_RTNL(); ASSERT_RTNL();
...@@ -1273,7 +1274,10 @@ static u8 rtnl_xdp_attached_mode(struct net_device *dev, u32 *prog_id) ...@@ -1273,7 +1274,10 @@ static u8 rtnl_xdp_attached_mode(struct net_device *dev, u32 *prog_id)
if (!ops->ndo_bpf) if (!ops->ndo_bpf)
return XDP_ATTACHED_NONE; return XDP_ATTACHED_NONE;
return __dev_xdp_attached(dev, ops->ndo_bpf, prog_id); __dev_xdp_query(dev, ops->ndo_bpf, &xdp);
*prog_id = xdp.prog_id;
return xdp.prog_attached;
} }
static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev)
......
...@@ -17,9 +17,10 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test ...@@ -17,9 +17,10 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
sockmap_verdict_prog.o dev_cgroup.o sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o
TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \
test_offload.py
include ../lib.mk include ../lib.mk
......
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
/* Sample program which should always load for testing control paths. */
int func()
{
return 0;
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册