Merge branch 'netdevsim-implement-proper-device-model'

Jiri Pirko says:

====================
netdevsim: implement proper device model

Currently the model of netdevsim is a bit odd in multiple ways.
1) devlink instance is not in any way related with actual netdevsim
   netdevices. Instead, it is created per-namespace.
2) multi-port netdevsim device is done using "link" attribute.
3) netdevsim bus is there only to have something to bind the netdev to,
   it really does not act as a bus.
4) netdevsim instances are created by "ip link add" which is great for
   soft devices with no hw backend. The rtnl core allocates netdev and
   calls into driver holding rtnl mutex. For hw-backed devices, this
   flow is wrong as it breaks order in which things are done.

This patchset adjust netdevsim to fix all above.

In order to support proper devlink and devlink port instances and to be
able to emulate real devices, there is need to implement bus probe and
instantiate everything from there. User can specify device id and port
count to be instantianted. For example:

$ echo "10 4" > /sys/bus/netdevsim/new_device

Then devlink shows this:

$ devlink dev
netdevsim/netdevsim10

$ devlink port
netdevsim/netdevsim10/0: type eth netdev eni0np1 flavour physical
netdevsim/netdevsim10/1: type eth netdev eni0np2 flavour physical
netdevsim/netdevsim10/2: type eth netdev eni0np3 flavour physical
netdevsim/netdevsim10/3: type eth netdev eni0np4 flavour physical

There is possible to add and delete ports using their indexes
during netdevsim device lifetime like this:

Then devlink shows this:

$ devlink port
netdevsim/netdevsim10/1: type eth netdev eni10np2 flavour physical
netdevsim/netdevsim10/2: type eth netdev eni10np3 flavour physical
netdevsim/netdevsim10/3: type eth netdev eni10np4 flavour physical
netdevsim/netdevsim10/43: type eth netdev eni10np44 flavour physical

Debugfs topology is also adjusted a bit. The rest stays the same as
before.

Udev bits are merged un systemd upstream git:
eaa9d507d8

See individual patches for changelog.
====================

Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-04-26 01:52:03 -04:00
commit 9157bc2a5e
11 changed files with 1213 additions and 833 deletions

View File

@ -3,7 +3,7 @@
obj-$(CONFIG_NETDEVSIM) += netdevsim.o
netdevsim-objs := \
netdev.o devlink.o fib.o sdev.o \
netdev.o dev.o fib.o bus.o
ifeq ($(CONFIG_BPF_SYSCALL),y)
netdevsim-objs += \

View File

@ -27,7 +27,7 @@
bpf_verifier_log_write(env, "[netdevsim] " fmt, ##__VA_ARGS__)
struct nsim_bpf_bound_prog {
struct netdevsim_shared_dev *sdev;
struct nsim_dev *nsim_dev;
struct bpf_prog *prog;
struct dentry *ddir;
const char *state;
@ -65,8 +65,8 @@ 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->sdev->bpf_bind_verifier_delay && !insn_idx)
msleep(state->sdev->bpf_bind_verifier_delay);
if (state->nsim_dev->bpf_bind_verifier_delay && !insn_idx)
msleep(state->nsim_dev->bpf_bind_verifier_delay);
if (insn_idx == env->prog->len - 1)
pr_vlog(env, "Hello from netdevsim!\n");
@ -213,7 +213,7 @@ nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf,
return 0;
}
static int nsim_bpf_create_prog(struct netdevsim_shared_dev *sdev,
static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev,
struct bpf_prog *prog)
{
struct nsim_bpf_bound_prog *state;
@ -223,13 +223,13 @@ static int nsim_bpf_create_prog(struct netdevsim_shared_dev *sdev,
if (!state)
return -ENOMEM;
state->sdev = sdev;
state->nsim_dev = nsim_dev;
state->prog = prog;
state->state = "verify";
/* Program id is not populated yet when we create the state. */
sprintf(name, "%u", sdev->prog_id_gen++);
state->ddir = debugfs_create_dir(name, sdev->ddir_bpf_bound_progs);
sprintf(name, "%u", nsim_dev->prog_id_gen++);
state->ddir = debugfs_create_dir(name, nsim_dev->ddir_bpf_bound_progs);
if (IS_ERR_OR_NULL(state->ddir)) {
kfree(state);
return -ENOMEM;
@ -240,7 +240,7 @@ static int nsim_bpf_create_prog(struct netdevsim_shared_dev *sdev,
&state->state, &nsim_bpf_string_fops);
debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded);
list_add_tail(&state->l, &sdev->bpf_bound_progs);
list_add_tail(&state->l, &nsim_dev->bpf_bound_progs);
prog->aux->offload->dev_priv = state;
@ -249,13 +249,13 @@ static int nsim_bpf_create_prog(struct netdevsim_shared_dev *sdev,
static int nsim_bpf_verifier_prep(struct bpf_prog *prog)
{
struct netdevsim_shared_dev *sdev =
struct nsim_dev *nsim_dev =
bpf_offload_dev_priv(prog->aux->offload->offdev);
if (!sdev->bpf_bind_accept)
if (!nsim_dev->bpf_bind_accept)
return -EOPNOTSUPP;
return nsim_bpf_create_prog(sdev, prog);
return nsim_bpf_create_prog(nsim_dev, prog);
}
static int nsim_bpf_translate(struct bpf_prog *prog)
@ -514,7 +514,7 @@ nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap)
}
offmap->dev_ops = &nsim_bpf_map_ops;
list_add_tail(&nmap->l, &ns->sdev->bpf_bound_maps);
list_add_tail(&nmap->l, &ns->nsim_dev->bpf_bound_maps);
return 0;
@ -578,77 +578,68 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
}
}
static int nsim_bpf_sdev_init(struct netdevsim_shared_dev *sdev)
int nsim_bpf_dev_init(struct nsim_dev *nsim_dev)
{
int err;
INIT_LIST_HEAD(&sdev->bpf_bound_progs);
INIT_LIST_HEAD(&sdev->bpf_bound_maps);
INIT_LIST_HEAD(&nsim_dev->bpf_bound_progs);
INIT_LIST_HEAD(&nsim_dev->bpf_bound_maps);
sdev->ddir_bpf_bound_progs =
debugfs_create_dir("bpf_bound_progs", sdev->ddir);
if (IS_ERR_OR_NULL(sdev->ddir_bpf_bound_progs))
nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir("bpf_bound_progs",
nsim_dev->ddir);
if (IS_ERR_OR_NULL(nsim_dev->ddir_bpf_bound_progs))
return -ENOMEM;
sdev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, sdev);
err = PTR_ERR_OR_ZERO(sdev->bpf_dev);
nsim_dev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, nsim_dev);
err = PTR_ERR_OR_ZERO(nsim_dev->bpf_dev);
if (err)
return err;
sdev->bpf_bind_accept = true;
debugfs_create_bool("bpf_bind_accept", 0600, sdev->ddir,
&sdev->bpf_bind_accept);
debugfs_create_u32("bpf_bind_verifier_delay", 0600, sdev->ddir,
&sdev->bpf_bind_verifier_delay);
nsim_dev->bpf_bind_accept = true;
debugfs_create_bool("bpf_bind_accept", 0600, nsim_dev->ddir,
&nsim_dev->bpf_bind_accept);
debugfs_create_u32("bpf_bind_verifier_delay", 0600, nsim_dev->ddir,
&nsim_dev->bpf_bind_verifier_delay);
return 0;
}
static void nsim_bpf_sdev_uninit(struct netdevsim_shared_dev *sdev)
void nsim_bpf_dev_exit(struct nsim_dev *nsim_dev)
{
WARN_ON(!list_empty(&sdev->bpf_bound_progs));
WARN_ON(!list_empty(&sdev->bpf_bound_maps));
bpf_offload_dev_destroy(sdev->bpf_dev);
WARN_ON(!list_empty(&nsim_dev->bpf_bound_progs));
WARN_ON(!list_empty(&nsim_dev->bpf_bound_maps));
bpf_offload_dev_destroy(nsim_dev->bpf_dev);
}
int nsim_bpf_init(struct netdevsim *ns)
{
struct dentry *ddir = ns->nsim_dev_port->ddir;
int err;
if (ns->sdev->refcnt == 1) {
err = nsim_bpf_sdev_init(ns->sdev);
if (err)
return err;
}
err = bpf_offload_dev_netdev_register(ns->sdev->bpf_dev, ns->netdev);
err = bpf_offload_dev_netdev_register(ns->nsim_dev->bpf_dev,
ns->netdev);
if (err)
goto err_bpf_sdev_uninit;
return err;
debugfs_create_u32("bpf_offloaded_id", 0400, ns->ddir,
debugfs_create_u32("bpf_offloaded_id", 0400, ddir,
&ns->bpf_offloaded_id);
ns->bpf_tc_accept = true;
debugfs_create_bool("bpf_tc_accept", 0600, ns->ddir,
debugfs_create_bool("bpf_tc_accept", 0600, ddir,
&ns->bpf_tc_accept);
debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ns->ddir,
debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ddir,
&ns->bpf_tc_non_bound_accept);
ns->bpf_xdpdrv_accept = true;
debugfs_create_bool("bpf_xdpdrv_accept", 0600, ns->ddir,
debugfs_create_bool("bpf_xdpdrv_accept", 0600, ddir,
&ns->bpf_xdpdrv_accept);
ns->bpf_xdpoffload_accept = true;
debugfs_create_bool("bpf_xdpoffload_accept", 0600, ns->ddir,
debugfs_create_bool("bpf_xdpoffload_accept", 0600, ddir,
&ns->bpf_xdpoffload_accept);
ns->bpf_map_accept = true;
debugfs_create_bool("bpf_map_accept", 0600, ns->ddir,
debugfs_create_bool("bpf_map_accept", 0600, ddir,
&ns->bpf_map_accept);
return 0;
err_bpf_sdev_uninit:
if (ns->sdev->refcnt == 1)
nsim_bpf_sdev_uninit(ns->sdev);
return err;
}
void nsim_bpf_uninit(struct netdevsim *ns)
@ -656,8 +647,5 @@ void nsim_bpf_uninit(struct netdevsim *ns)
WARN_ON(ns->xdp.prog);
WARN_ON(ns->xdp_hw.prog);
WARN_ON(ns->bpf_offloaded);
bpf_offload_dev_netdev_unregister(ns->sdev->bpf_dev, ns->netdev);
if (ns->sdev->refcnt == 1)
nsim_bpf_sdev_uninit(ns->sdev);
bpf_offload_dev_netdev_unregister(ns->nsim_dev->bpf_dev, ns->netdev);
}

341
drivers/net/netdevsim/bus.c Normal file
View File

@ -0,0 +1,341 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2017 Netronome Systems, Inc.
* Copyright (C) 2019 Mellanox Technologies. All rights reserved
*/
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "netdevsim.h"
static DEFINE_IDA(nsim_bus_dev_ids);
static LIST_HEAD(nsim_bus_dev_list);
static DEFINE_MUTEX(nsim_bus_dev_list_lock);
static struct nsim_bus_dev *to_nsim_bus_dev(struct device *dev)
{
return container_of(dev, struct nsim_bus_dev, dev);
}
static int nsim_bus_dev_vfs_enable(struct nsim_bus_dev *nsim_bus_dev,
unsigned int num_vfs)
{
nsim_bus_dev->vfconfigs = kcalloc(num_vfs,
sizeof(struct nsim_vf_config),
GFP_KERNEL);
if (!nsim_bus_dev->vfconfigs)
return -ENOMEM;
nsim_bus_dev->num_vfs = num_vfs;
return 0;
}
static void nsim_bus_dev_vfs_disable(struct nsim_bus_dev *nsim_bus_dev)
{
kfree(nsim_bus_dev->vfconfigs);
nsim_bus_dev->vfconfigs = NULL;
nsim_bus_dev->num_vfs = 0;
}
static ssize_t
nsim_bus_dev_numvfs_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
unsigned int num_vfs;
int ret;
ret = kstrtouint(buf, 0, &num_vfs);
if (ret)
return ret;
rtnl_lock();
if (nsim_bus_dev->num_vfs == num_vfs)
goto exit_good;
if (nsim_bus_dev->num_vfs && num_vfs) {
ret = -EBUSY;
goto exit_unlock;
}
if (num_vfs) {
ret = nsim_bus_dev_vfs_enable(nsim_bus_dev, num_vfs);
if (ret)
goto exit_unlock;
} else {
nsim_bus_dev_vfs_disable(nsim_bus_dev);
}
exit_good:
ret = count;
exit_unlock:
rtnl_unlock();
return ret;
}
static ssize_t
nsim_bus_dev_numvfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
return sprintf(buf, "%u\n", nsim_bus_dev->num_vfs);
}
static struct device_attribute nsim_bus_dev_numvfs_attr =
__ATTR(sriov_numvfs, 0664, nsim_bus_dev_numvfs_show,
nsim_bus_dev_numvfs_store);
static ssize_t
new_port_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
unsigned int port_index;
int ret;
ret = kstrtouint(buf, 0, &port_index);
if (ret)
return ret;
ret = nsim_dev_port_add(nsim_bus_dev, port_index);
return ret ? ret : count;
}
static struct device_attribute nsim_bus_dev_new_port_attr = __ATTR_WO(new_port);
static ssize_t
del_port_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
unsigned int port_index;
int ret;
ret = kstrtouint(buf, 0, &port_index);
if (ret)
return ret;
ret = nsim_dev_port_del(nsim_bus_dev, port_index);
return ret ? ret : count;
}
static struct device_attribute nsim_bus_dev_del_port_attr = __ATTR_WO(del_port);
static struct attribute *nsim_bus_dev_attrs[] = {
&nsim_bus_dev_numvfs_attr.attr,
&nsim_bus_dev_new_port_attr.attr,
&nsim_bus_dev_del_port_attr.attr,
NULL,
};
static const struct attribute_group nsim_bus_dev_attr_group = {
.attrs = nsim_bus_dev_attrs,
};
static const struct attribute_group *nsim_bus_dev_attr_groups[] = {
&nsim_bus_dev_attr_group,
NULL,
};
static void nsim_bus_dev_release(struct device *dev)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
nsim_bus_dev_vfs_disable(nsim_bus_dev);
}
static struct device_type nsim_bus_dev_type = {
.groups = nsim_bus_dev_attr_groups,
.release = nsim_bus_dev_release,
};
static struct nsim_bus_dev *
nsim_bus_dev_new(unsigned int id, unsigned int port_count);
static ssize_t
new_device_store(struct bus_type *bus, const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev;
unsigned int port_count;
unsigned int id;
int err;
err = sscanf(buf, "%u %u", &id, &port_count);
switch (err) {
case 1:
port_count = 1;
/* pass through */
case 2:
if (id > INT_MAX) {
pr_err("Value of \"id\" is too big.\n");
return -EINVAL;
}
break;
default:
pr_err("Format for adding new device is \"id port_count\" (uint uint).\n");
return -EINVAL;
}
nsim_bus_dev = nsim_bus_dev_new(id, port_count);
if (IS_ERR(nsim_bus_dev))
return PTR_ERR(nsim_bus_dev);
mutex_lock(&nsim_bus_dev_list_lock);
list_add_tail(&nsim_bus_dev->list, &nsim_bus_dev_list);
mutex_unlock(&nsim_bus_dev_list_lock);
return count;
}
static BUS_ATTR_WO(new_device);
static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev);
static ssize_t
del_device_store(struct bus_type *bus, const char *buf, size_t count)
{
struct nsim_bus_dev *nsim_bus_dev, *tmp;
unsigned int id;
int err;
err = sscanf(buf, "%u", &id);
switch (err) {
case 1:
if (id > INT_MAX) {
pr_err("Value of \"id\" is too big.\n");
return -EINVAL;
}
break;
default:
pr_err("Format for deleting device is \"id\" (uint).\n");
return -EINVAL;
}
err = -ENOENT;
mutex_lock(&nsim_bus_dev_list_lock);
list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) {
if (nsim_bus_dev->dev.id != id)
continue;
list_del(&nsim_bus_dev->list);
nsim_bus_dev_del(nsim_bus_dev);
err = 0;
break;
}
mutex_unlock(&nsim_bus_dev_list_lock);
return !err ? count : err;
}
static BUS_ATTR_WO(del_device);
static struct attribute *nsim_bus_attrs[] = {
&bus_attr_new_device.attr,
&bus_attr_del_device.attr,
NULL
};
ATTRIBUTE_GROUPS(nsim_bus);
static int nsim_bus_probe(struct device *dev)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
return nsim_dev_probe(nsim_bus_dev);
}
static int nsim_bus_remove(struct device *dev)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
nsim_dev_remove(nsim_bus_dev);
return 0;
}
int nsim_num_vf(struct device *dev)
{
struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev);
return nsim_bus_dev->num_vfs;
}
static struct bus_type nsim_bus = {
.name = DRV_NAME,
.dev_name = DRV_NAME,
.bus_groups = nsim_bus_groups,
.probe = nsim_bus_probe,
.remove = nsim_bus_remove,
.num_vf = nsim_num_vf,
};
static struct nsim_bus_dev *
nsim_bus_dev_new(unsigned int id, unsigned int port_count)
{
struct nsim_bus_dev *nsim_bus_dev;
int err;
nsim_bus_dev = kzalloc(sizeof(*nsim_bus_dev), GFP_KERNEL);
if (!nsim_bus_dev)
return ERR_PTR(-ENOMEM);
err = ida_alloc_range(&nsim_bus_dev_ids, id, id, GFP_KERNEL);
if (err < 0)
goto err_nsim_bus_dev_free;
nsim_bus_dev->dev.id = err;
nsim_bus_dev->dev.bus = &nsim_bus;
nsim_bus_dev->dev.type = &nsim_bus_dev_type;
nsim_bus_dev->port_count = port_count;
err = device_register(&nsim_bus_dev->dev);
if (err)
goto err_nsim_bus_dev_id_free;
return nsim_bus_dev;
err_nsim_bus_dev_id_free:
ida_free(&nsim_bus_dev_ids, nsim_bus_dev->dev.id);
err_nsim_bus_dev_free:
kfree(nsim_bus_dev);
return ERR_PTR(err);
}
static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev)
{
device_unregister(&nsim_bus_dev->dev);
ida_free(&nsim_bus_dev_ids, nsim_bus_dev->dev.id);
kfree(nsim_bus_dev);
}
static struct device_driver nsim_driver = {
.name = DRV_NAME,
.bus = &nsim_bus,
.owner = THIS_MODULE,
};
int nsim_bus_init(void)
{
int err;
err = bus_register(&nsim_bus);
if (err)
return err;
err = driver_register(&nsim_driver);
if (err)
goto err_bus_unregister;
return 0;
err_bus_unregister:
bus_unregister(&nsim_bus);
return err;
}
void nsim_bus_exit(void)
{
struct nsim_bus_dev *nsim_bus_dev, *tmp;
mutex_lock(&nsim_bus_dev_list_lock);
list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) {
list_del(&nsim_bus_dev->list);
nsim_bus_dev_del(nsim_bus_dev);
}
mutex_unlock(&nsim_bus_dev_list_lock);
driver_unregister(&nsim_driver);
bus_unregister(&nsim_bus);
}

447
drivers/net/netdevsim/dev.c Normal file
View File

@ -0,0 +1,447 @@
/*
* Copyright (c) 2018 Cumulus Networks. All rights reserved.
* Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
* Copyright (c) 2019 Mellanox Technologies. All rights reserved.
*
* 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/device.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/rtnetlink.h>
#include <net/devlink.h>
#include "netdevsim.h"
static struct dentry *nsim_dev_ddir;
static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
{
char dev_ddir_name[16];
sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
if (IS_ERR_OR_NULL(nsim_dev->ddir))
return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL;
nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
if (IS_ERR_OR_NULL(nsim_dev->ports_ddir))
return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL;
return 0;
}
static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
{
debugfs_remove_recursive(nsim_dev->ports_ddir);
debugfs_remove_recursive(nsim_dev->ddir);
}
static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
struct nsim_dev_port *nsim_dev_port)
{
char port_ddir_name[16];
char dev_link_name[32];
sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
nsim_dev->ports_ddir);
if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
return -ENOMEM;
sprintf(dev_link_name, "../../../" DRV_NAME "%u",
nsim_dev->nsim_bus_dev->dev.id);
debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);
return 0;
}
static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
{
debugfs_remove_recursive(nsim_dev_port->ddir);
}
static u64 nsim_dev_ipv4_fib_resource_occ_get(void *priv)
{
struct nsim_dev *nsim_dev = priv;
return nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV4_FIB, false);
}
static u64 nsim_dev_ipv4_fib_rules_res_occ_get(void *priv)
{
struct nsim_dev *nsim_dev = priv;
return nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV4_FIB_RULES, false);
}
static u64 nsim_dev_ipv6_fib_resource_occ_get(void *priv)
{
struct nsim_dev *nsim_dev = priv;
return nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV6_FIB, false);
}
static u64 nsim_dev_ipv6_fib_rules_res_occ_get(void *priv)
{
struct nsim_dev *nsim_dev = priv;
return nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV6_FIB_RULES, false);
}
static int nsim_dev_resources_register(struct devlink *devlink)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
struct devlink_resource_size_params params = {
.size_max = (u64)-1,
.size_granularity = 1,
.unit = DEVLINK_RESOURCE_UNIT_ENTRY
};
int err;
u64 n;
/* Resources for IPv4 */
err = devlink_resource_register(devlink, "IPv4", (u64)-1,
NSIM_RESOURCE_IPV4,
DEVLINK_RESOURCE_ID_PARENT_TOP,
&params);
if (err) {
pr_err("Failed to register IPv4 top resource\n");
goto out;
}
n = nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV4_FIB, true);
err = devlink_resource_register(devlink, "fib", n,
NSIM_RESOURCE_IPV4_FIB,
NSIM_RESOURCE_IPV4, &params);
if (err) {
pr_err("Failed to register IPv4 FIB resource\n");
return err;
}
n = nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV4_FIB_RULES, true);
err = devlink_resource_register(devlink, "fib-rules", n,
NSIM_RESOURCE_IPV4_FIB_RULES,
NSIM_RESOURCE_IPV4, &params);
if (err) {
pr_err("Failed to register IPv4 FIB rules resource\n");
return err;
}
/* Resources for IPv6 */
err = devlink_resource_register(devlink, "IPv6", (u64)-1,
NSIM_RESOURCE_IPV6,
DEVLINK_RESOURCE_ID_PARENT_TOP,
&params);
if (err) {
pr_err("Failed to register IPv6 top resource\n");
goto out;
}
n = nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV6_FIB, true);
err = devlink_resource_register(devlink, "fib", n,
NSIM_RESOURCE_IPV6_FIB,
NSIM_RESOURCE_IPV6, &params);
if (err) {
pr_err("Failed to register IPv6 FIB resource\n");
return err;
}
n = nsim_fib_get_val(nsim_dev->fib_data,
NSIM_RESOURCE_IPV6_FIB_RULES, true);
err = devlink_resource_register(devlink, "fib-rules", n,
NSIM_RESOURCE_IPV6_FIB_RULES,
NSIM_RESOURCE_IPV6, &params);
if (err) {
pr_err("Failed to register IPv6 FIB rules resource\n");
return err;
}
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV4_FIB,
nsim_dev_ipv4_fib_resource_occ_get,
nsim_dev);
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV4_FIB_RULES,
nsim_dev_ipv4_fib_rules_res_occ_get,
nsim_dev);
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV6_FIB,
nsim_dev_ipv6_fib_resource_occ_get,
nsim_dev);
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV6_FIB_RULES,
nsim_dev_ipv6_fib_rules_res_occ_get,
nsim_dev);
out:
return err;
}
static int nsim_dev_reload(struct devlink *devlink,
struct netlink_ext_ack *extack)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
enum nsim_resource_id res_ids[] = {
NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
};
int i;
for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
int err;
u64 val;
err = devlink_resource_size_get(devlink, res_ids[i], &val);
if (!err) {
err = nsim_fib_set_max(nsim_dev->fib_data,
res_ids[i], val, extack);
if (err)
return err;
}
}
return 0;
}
static const struct devlink_ops nsim_dev_devlink_ops = {
.reload = nsim_dev_reload,
};
static struct nsim_dev *
nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
{
struct nsim_dev *nsim_dev;
struct devlink *devlink;
int err;
devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
if (!devlink)
return ERR_PTR(-ENOMEM);
nsim_dev = devlink_priv(devlink);
nsim_dev->nsim_bus_dev = nsim_bus_dev;
nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
INIT_LIST_HEAD(&nsim_dev->port_list);
mutex_init(&nsim_dev->port_list_lock);
nsim_dev->fib_data = nsim_fib_create();
if (IS_ERR(nsim_dev->fib_data)) {
err = PTR_ERR(nsim_dev->fib_data);
goto err_devlink_free;
}
err = nsim_dev_resources_register(devlink);
if (err)
goto err_fib_destroy;
err = devlink_register(devlink, &nsim_bus_dev->dev);
if (err)
goto err_resources_unregister;
err = nsim_dev_debugfs_init(nsim_dev);
if (err)
goto err_dl_unregister;
err = nsim_bpf_dev_init(nsim_dev);
if (err)
goto err_debugfs_exit;
return nsim_dev;
err_debugfs_exit:
nsim_dev_debugfs_exit(nsim_dev);
err_dl_unregister:
devlink_unregister(devlink);
err_resources_unregister:
devlink_resources_unregister(devlink, NULL);
err_fib_destroy:
nsim_fib_destroy(nsim_dev->fib_data);
err_devlink_free:
devlink_free(devlink);
return ERR_PTR(err);
}
static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
{
struct devlink *devlink = priv_to_devlink(nsim_dev);
nsim_bpf_dev_exit(nsim_dev);
nsim_dev_debugfs_exit(nsim_dev);
devlink_unregister(devlink);
devlink_resources_unregister(devlink, NULL);
nsim_fib_destroy(nsim_dev->fib_data);
mutex_destroy(&nsim_dev->port_list_lock);
devlink_free(devlink);
}
static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
unsigned int port_index)
{
struct nsim_dev_port *nsim_dev_port;
struct devlink_port *devlink_port;
int err;
nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
if (!nsim_dev_port)
return -ENOMEM;
nsim_dev_port->port_index = port_index;
devlink_port = &nsim_dev_port->devlink_port;
devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
port_index + 1, 0, 0,
nsim_dev->switch_id.id,
nsim_dev->switch_id.id_len);
err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
port_index);
if (err)
goto err_port_free;
err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
if (err)
goto err_dl_port_unregister;
nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
if (IS_ERR(nsim_dev_port->ns)) {
err = PTR_ERR(nsim_dev_port->ns);
goto err_port_debugfs_exit;
}
devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
list_add(&nsim_dev_port->list, &nsim_dev->port_list);
return 0;
err_port_debugfs_exit:
nsim_dev_port_debugfs_exit(nsim_dev_port);
err_dl_port_unregister:
devlink_port_unregister(devlink_port);
err_port_free:
kfree(nsim_dev_port);
return err;
}
static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
{
struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
list_del(&nsim_dev_port->list);
devlink_port_type_clear(devlink_port);
nsim_destroy(nsim_dev_port->ns);
nsim_dev_port_debugfs_exit(nsim_dev_port);
devlink_port_unregister(devlink_port);
kfree(nsim_dev_port);
}
static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
{
struct nsim_dev_port *nsim_dev_port, *tmp;
list_for_each_entry_safe(nsim_dev_port, tmp,
&nsim_dev->port_list, list)
__nsim_dev_port_del(nsim_dev_port);
}
int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
{
struct nsim_dev *nsim_dev;
int i;
int err;
nsim_dev = nsim_dev_create(nsim_bus_dev, nsim_bus_dev->port_count);
if (IS_ERR(nsim_dev))
return PTR_ERR(nsim_dev);
dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
for (i = 0; i < nsim_bus_dev->port_count; i++) {
err = __nsim_dev_port_add(nsim_dev, i);
if (err)
goto err_port_del_all;
}
return 0;
err_port_del_all:
nsim_dev_port_del_all(nsim_dev);
nsim_dev_destroy(nsim_dev);
return err;
}
void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
{
struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
nsim_dev_port_del_all(nsim_dev);
nsim_dev_destroy(nsim_dev);
}
static struct nsim_dev_port *
__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
{
struct nsim_dev_port *nsim_dev_port;
list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
if (nsim_dev_port->port_index == port_index)
return nsim_dev_port;
return NULL;
}
int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
unsigned int port_index)
{
struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
int err;
mutex_lock(&nsim_dev->port_list_lock);
if (__nsim_dev_port_lookup(nsim_dev, port_index))
err = -EEXIST;
else
err = __nsim_dev_port_add(nsim_dev, port_index);
mutex_unlock(&nsim_dev->port_list_lock);
return err;
}
int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
unsigned int port_index)
{
struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
struct nsim_dev_port *nsim_dev_port;
int err = 0;
mutex_lock(&nsim_dev->port_list_lock);
nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
if (!nsim_dev_port)
err = -ENOENT;
else
__nsim_dev_port_del(nsim_dev_port);
mutex_unlock(&nsim_dev->port_list_lock);
return err;
}
int nsim_dev_init(void)
{
nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
if (IS_ERR_OR_NULL(nsim_dev_ddir))
return -ENOMEM;
return 0;
}
void nsim_dev_exit(void)
{
debugfs_remove_recursive(nsim_dev_ddir);
}

View File

@ -1,295 +0,0 @@
/*
* Copyright (c) 2018 Cumulus Networks. All rights reserved.
* Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
*
* 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 <net/devlink.h>
#include <net/netns/generic.h>
#include "netdevsim.h"
static unsigned int nsim_devlink_id;
/* place holder until devlink and namespaces is sorted out */
static struct net *nsim_devlink_net(struct devlink *devlink)
{
return &init_net;
}
/* IPv4
*/
static u64 nsim_ipv4_fib_resource_occ_get(void *priv)
{
struct net *net = priv;
return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
}
static u64 nsim_ipv4_fib_rules_res_occ_get(void *priv)
{
struct net *net = priv;
return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
}
/* IPv6
*/
static u64 nsim_ipv6_fib_resource_occ_get(void *priv)
{
struct net *net = priv;
return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
}
static u64 nsim_ipv6_fib_rules_res_occ_get(void *priv)
{
struct net *net = priv;
return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
}
static int devlink_resources_register(struct devlink *devlink)
{
struct devlink_resource_size_params params = {
.size_max = (u64)-1,
.size_granularity = 1,
.unit = DEVLINK_RESOURCE_UNIT_ENTRY
};
struct net *net = nsim_devlink_net(devlink);
int err;
u64 n;
/* Resources for IPv4 */
err = devlink_resource_register(devlink, "IPv4", (u64)-1,
NSIM_RESOURCE_IPV4,
DEVLINK_RESOURCE_ID_PARENT_TOP,
&params);
if (err) {
pr_err("Failed to register IPv4 top resource\n");
goto out;
}
n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
err = devlink_resource_register(devlink, "fib", n,
NSIM_RESOURCE_IPV4_FIB,
NSIM_RESOURCE_IPV4, &params);
if (err) {
pr_err("Failed to register IPv4 FIB resource\n");
return err;
}
n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
err = devlink_resource_register(devlink, "fib-rules", n,
NSIM_RESOURCE_IPV4_FIB_RULES,
NSIM_RESOURCE_IPV4, &params);
if (err) {
pr_err("Failed to register IPv4 FIB rules resource\n");
return err;
}
/* Resources for IPv6 */
err = devlink_resource_register(devlink, "IPv6", (u64)-1,
NSIM_RESOURCE_IPV6,
DEVLINK_RESOURCE_ID_PARENT_TOP,
&params);
if (err) {
pr_err("Failed to register IPv6 top resource\n");
goto out;
}
n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
err = devlink_resource_register(devlink, "fib", n,
NSIM_RESOURCE_IPV6_FIB,
NSIM_RESOURCE_IPV6, &params);
if (err) {
pr_err("Failed to register IPv6 FIB resource\n");
return err;
}
n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
err = devlink_resource_register(devlink, "fib-rules", n,
NSIM_RESOURCE_IPV6_FIB_RULES,
NSIM_RESOURCE_IPV6, &params);
if (err) {
pr_err("Failed to register IPv6 FIB rules resource\n");
return err;
}
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV4_FIB,
nsim_ipv4_fib_resource_occ_get,
net);
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV4_FIB_RULES,
nsim_ipv4_fib_rules_res_occ_get,
net);
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV6_FIB,
nsim_ipv6_fib_resource_occ_get,
net);
devlink_resource_occ_get_register(devlink,
NSIM_RESOURCE_IPV6_FIB_RULES,
nsim_ipv6_fib_rules_res_occ_get,
net);
out:
return err;
}
static int nsim_devlink_reload(struct devlink *devlink,
struct netlink_ext_ack *extack)
{
enum nsim_resource_id res_ids[] = {
NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
};
struct net *net = nsim_devlink_net(devlink);
int i;
for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
int err;
u64 val;
err = devlink_resource_size_get(devlink, res_ids[i], &val);
if (!err) {
err = nsim_fib_set_max(net, res_ids[i], val, extack);
if (err)
return err;
}
}
return 0;
}
static void nsim_devlink_net_reset(struct net *net)
{
enum nsim_resource_id res_ids[] = {
NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
};
int i;
for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
if (nsim_fib_set_max(net, res_ids[i], (u64)-1, NULL)) {
pr_err("Failed to reset limit for resource %u\n",
res_ids[i]);
}
}
}
static const struct devlink_ops nsim_devlink_ops = {
.reload = nsim_devlink_reload,
};
/* once devlink / namespace issues are sorted out
* this needs to be net in which a devlink instance
* is to be created. e.g., dev_net(ns->netdev)
*/
static struct net *nsim_to_net(struct netdevsim *ns)
{
return &init_net;
}
void nsim_devlink_teardown(struct netdevsim *ns)
{
if (ns->devlink) {
struct net *net = nsim_to_net(ns);
bool *reg_devlink = net_generic(net, nsim_devlink_id);
devlink_resources_unregister(ns->devlink, NULL);
devlink_unregister(ns->devlink);
devlink_free(ns->devlink);
ns->devlink = NULL;
nsim_devlink_net_reset(net);
*reg_devlink = true;
}
}
int nsim_devlink_setup(struct netdevsim *ns)
{
struct net *net = nsim_to_net(ns);
bool *reg_devlink = net_generic(net, nsim_devlink_id);
struct devlink *devlink;
int err;
/* only one device per namespace controls devlink */
if (!*reg_devlink) {
ns->devlink = NULL;
return 0;
}
devlink = devlink_alloc(&nsim_devlink_ops, 0);
if (!devlink)
return -ENOMEM;
err = devlink_register(devlink, &ns->dev);
if (err)
goto err_devlink_free;
err = devlink_resources_register(devlink);
if (err)
goto err_dl_unregister;
ns->devlink = devlink;
*reg_devlink = false;
return 0;
err_dl_unregister:
devlink_unregister(devlink);
err_devlink_free:
devlink_free(devlink);
return err;
}
/* Initialize per network namespace state */
static int __net_init nsim_devlink_netns_init(struct net *net)
{
bool *reg_devlink = net_generic(net, nsim_devlink_id);
*reg_devlink = true;
return 0;
}
static struct pernet_operations nsim_devlink_net_ops = {
.init = nsim_devlink_netns_init,
.id = &nsim_devlink_id,
.size = sizeof(bool),
};
void nsim_devlink_exit(void)
{
unregister_pernet_subsys(&nsim_devlink_net_ops);
nsim_fib_exit();
}
int nsim_devlink_init(void)
{
int err;
err = nsim_fib_init();
if (err)
goto err_out;
err = register_pernet_subsys(&nsim_devlink_net_ops);
if (err)
nsim_fib_exit();
err_out:
return err;
}

View File

@ -18,7 +18,6 @@
#include <net/ip_fib.h>
#include <net/ip6_fib.h>
#include <net/fib_rules.h>
#include <net/netns/generic.h>
#include "netdevsim.h"
@ -33,15 +32,14 @@ struct nsim_per_fib_data {
};
struct nsim_fib_data {
struct notifier_block fib_nb;
struct nsim_per_fib_data ipv4;
struct nsim_per_fib_data ipv6;
};
static unsigned int nsim_fib_net_id;
u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
enum nsim_resource_id res_id, bool max)
{
struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
struct nsim_fib_entry *entry;
switch (res_id) {
@ -64,10 +62,10 @@ u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
return max ? entry->max : entry->num;
}
int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
int nsim_fib_set_max(struct nsim_fib_data *fib_data,
enum nsim_resource_id res_id, u64 val,
struct netlink_ext_ack *extack)
{
struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
struct nsim_fib_entry *entry;
int err = 0;
@ -120,9 +118,9 @@ static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
return err;
}
static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
static int nsim_fib_rule_event(struct nsim_fib_data *data,
struct fib_notifier_info *info, bool add)
{
struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
struct netlink_ext_ack *extack = info->extack;
int err = 0;
@ -157,9 +155,9 @@ static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
return err;
}
static int nsim_fib_event(struct fib_notifier_info *info, bool add)
static int nsim_fib_event(struct nsim_fib_data *data,
struct fib_notifier_info *info, bool add)
{
struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
struct netlink_ext_ack *extack = info->extack;
int err = 0;
@ -178,18 +176,22 @@ static int nsim_fib_event(struct fib_notifier_info *info, bool add)
static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
fib_nb);
struct fib_notifier_info *info = ptr;
int err = 0;
switch (event) {
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
err = nsim_fib_rule_event(data, info,
event == FIB_EVENT_RULE_ADD);
break;
case FIB_EVENT_ENTRY_ADD: /* fall through */
case FIB_EVENT_ENTRY_DEL:
err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
err = nsim_fib_event(data, info,
event == FIB_EVENT_ENTRY_ADD);
break;
}
@ -199,30 +201,23 @@ static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
/* inconsistent dump, trying again */
static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
{
struct nsim_fib_data *data;
struct net *net;
struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
fib_nb);
rcu_read_lock();
for_each_net_rcu(net) {
data = net_generic(net, nsim_fib_net_id);
data->ipv4.fib.num = 0ULL;
data->ipv4.rules.num = 0ULL;
data->ipv6.fib.num = 0ULL;
data->ipv6.rules.num = 0ULL;
}
rcu_read_unlock();
data->ipv4.fib.num = 0ULL;
data->ipv4.rules.num = 0ULL;
data->ipv6.fib.num = 0ULL;
data->ipv6.rules.num = 0ULL;
}
static struct notifier_block nsim_fib_nb = {
.notifier_call = nsim_fib_event_nb,
};
/* Initialize per network namespace state */
static int __net_init nsim_fib_netns_init(struct net *net)
struct nsim_fib_data *nsim_fib_create(void)
{
struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
struct nsim_fib_data *data;
int err;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return ERR_PTR(-ENOMEM);
data->ipv4.fib.max = (u64)-1;
data->ipv4.rules.max = (u64)-1;
@ -230,37 +225,22 @@ static int __net_init nsim_fib_netns_init(struct net *net)
data->ipv6.fib.max = (u64)-1;
data->ipv6.rules.max = (u64)-1;
return 0;
}
static struct pernet_operations nsim_fib_net_ops = {
.init = nsim_fib_netns_init,
.id = &nsim_fib_net_id,
.size = sizeof(struct nsim_fib_data),
};
void nsim_fib_exit(void)
{
unregister_pernet_subsys(&nsim_fib_net_ops);
unregister_fib_notifier(&nsim_fib_nb);
}
int nsim_fib_init(void)
{
int err;
err = register_pernet_subsys(&nsim_fib_net_ops);
if (err < 0) {
pr_err("Failed to register pernet subsystem\n");
goto err_out;
}
err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
if (err < 0) {
data->fib_nb.notifier_call = nsim_fib_event_nb;
err = register_fib_notifier(&data->fib_nb, nsim_fib_dump_inconsistent);
if (err) {
pr_err("Failed to register fib notifier\n");
goto err_out;
}
return data;
err_out:
return err;
kfree(data);
return ERR_PTR(err);
}
void nsim_fib_destroy(struct nsim_fib_data *data)
{
unregister_fib_notifier(&data->fib_nb);
kfree(data);
}

View File

@ -283,7 +283,8 @@ void nsim_ipsec_init(struct netdevsim *ns)
ns->netdev->features |= NSIM_ESP_FEATURES;
ns->netdev->hw_enc_features |= NSIM_ESP_FEATURES;
ns->ipsec.pfile = debugfs_create_file("ipsec", 0400, ns->ddir, ns,
ns->ipsec.pfile = debugfs_create_file("ipsec", 0400,
ns->nsim_dev_port->ddir, ns,
&ipsec_dbg_fops);
}

View File

@ -25,200 +25,6 @@
#include "netdevsim.h"
static u32 nsim_dev_id;
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 struct dentry *nsim_ddir;
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);
}
static struct device_type nsim_dev_type = {
.groups = nsim_dev_attr_groups,
.release = nsim_dev_release,
};
static int nsim_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct netdevsim *ns = netdev_priv(dev);
ppid->id_len = sizeof(ns->sdev->switch_id);
memcpy(&ppid->id, &ns->sdev->switch_id, ppid->id_len);
return 0;
}
static int nsim_init(struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
char sdev_link_name[32];
int err;
ns->netdev = dev;
ns->ddir = debugfs_create_dir(netdev_name(dev), nsim_ddir);
if (IS_ERR_OR_NULL(ns->ddir))
return -ENOMEM;
sprintf(sdev_link_name, "../../" DRV_NAME "_sdev/%u",
ns->sdev->switch_id);
debugfs_create_symlink("sdev", ns->ddir, sdev_link_name);
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);
err = nsim_devlink_setup(ns);
if (err)
goto err_unreg_dev;
nsim_ipsec_init(ns);
return 0;
err_unreg_dev:
device_unregister(&ns->dev);
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);
nsim_ipsec_teardown(ns);
nsim_devlink_teardown(ns);
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() */
nsim_sdev_put(ns->sdev);
}
static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
@ -295,11 +101,12 @@ nsim_setup_tc_block(struct net_device *dev, struct tc_block_offload *f)
static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
/* Only refuse multicast addresses, zero address can mean unset/any. */
if (vf >= ns->num_vfs || is_multicast_ether_addr(mac))
if (vf >= nsim_bus_dev->num_vfs || is_multicast_ether_addr(mac))
return -EINVAL;
memcpy(ns->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
memcpy(nsim_bus_dev->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
return 0;
}
@ -308,13 +115,14 @@ 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);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs || vlan > 4095 || qos > 7)
if (vf >= nsim_bus_dev->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;
nsim_bus_dev->vfconfigs[vf].vlan = vlan;
nsim_bus_dev->vfconfigs[vf].qos = qos;
nsim_bus_dev->vfconfigs[vf].vlan_proto = vlan_proto;
return 0;
}
@ -322,12 +130,13 @@ static int nsim_set_vf_vlan(struct net_device *dev, int vf,
static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs)
if (vf >= nsim_bus_dev->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].min_tx_rate = min;
ns->vfconfigs[vf].max_tx_rate = max;
nsim_bus_dev->vfconfigs[vf].min_tx_rate = min;
nsim_bus_dev->vfconfigs[vf].max_tx_rate = max;
return 0;
}
@ -335,10 +144,11 @@ static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs)
if (vf >= nsim_bus_dev->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].spoofchk_enabled = val;
nsim_bus_dev->vfconfigs[vf].spoofchk_enabled = val;
return 0;
}
@ -346,10 +156,11 @@ static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs)
if (vf >= nsim_bus_dev->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].rss_query_enabled = val;
nsim_bus_dev->vfconfigs[vf].rss_query_enabled = val;
return 0;
}
@ -357,10 +168,11 @@ static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs)
if (vf >= nsim_bus_dev->num_vfs)
return -EINVAL;
ns->vfconfigs[vf].trusted = val;
nsim_bus_dev->vfconfigs[vf].trusted = val;
return 0;
}
@ -369,21 +181,22 @@ static int
nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs)
if (vf >= nsim_bus_dev->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;
ivi->linkstate = nsim_bus_dev->vfconfigs[vf].link_state;
ivi->min_tx_rate = nsim_bus_dev->vfconfigs[vf].min_tx_rate;
ivi->max_tx_rate = nsim_bus_dev->vfconfigs[vf].max_tx_rate;
ivi->vlan = nsim_bus_dev->vfconfigs[vf].vlan;
ivi->vlan_proto = nsim_bus_dev->vfconfigs[vf].vlan_proto;
ivi->qos = nsim_bus_dev->vfconfigs[vf].qos;
memcpy(&ivi->mac, nsim_bus_dev->vfconfigs[vf].vf_mac, ETH_ALEN);
ivi->spoofchk = nsim_bus_dev->vfconfigs[vf].spoofchk_enabled;
ivi->trusted = nsim_bus_dev->vfconfigs[vf].trusted;
ivi->rss_query_en = nsim_bus_dev->vfconfigs[vf].rss_query_enabled;
return 0;
}
@ -391,8 +204,9 @@ nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi)
static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
{
struct netdevsim *ns = netdev_priv(dev);
struct nsim_bus_dev *nsim_bus_dev = ns->nsim_bus_dev;
if (vf >= ns->num_vfs)
if (vf >= nsim_bus_dev->num_vfs)
return -EINVAL;
switch (state) {
@ -404,7 +218,7 @@ static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
return -EINVAL;
}
ns->vfconfigs[vf].link_state = state;
nsim_bus_dev->vfconfigs[vf].link_state = state;
return 0;
}
@ -431,9 +245,14 @@ nsim_set_features(struct net_device *dev, netdev_features_t features)
return 0;
}
static struct devlink_port *nsim_get_devlink_port(struct net_device *dev)
{
struct netdevsim *ns = netdev_priv(dev);
return &ns->nsim_dev_port->devlink_port;
}
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,
@ -451,7 +270,7 @@ static const struct net_device_ops nsim_netdev_ops = {
.ndo_setup_tc = nsim_setup_tc,
.ndo_set_features = nsim_set_features,
.ndo_bpf = nsim_bpf,
.ndo_get_port_parent_id = nsim_get_port_parent_id,
.ndo_get_devlink_port = nsim_get_devlink_port,
};
static void nsim_setup(struct net_device *dev)
@ -459,10 +278,6 @@ static void nsim_setup(struct net_device *dev)
ether_setup(dev);
eth_hw_addr_random(dev);
dev->netdev_ops = &nsim_netdev_ops;
dev->needs_free_netdev = true;
dev->priv_destructor = nsim_free;
dev->tx_queue_len = 0;
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
@ -477,105 +292,102 @@ static void nsim_setup(struct net_device *dev)
dev->max_mtu = ETH_MAX_MTU;
}
static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
struct netdevsim *
nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port)
{
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 int nsim_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct netdevsim *ns = netdev_priv(dev);
struct netdevsim *joinns = NULL;
struct net_device *dev;
struct netdevsim *ns;
int err;
if (tb[IFLA_LINK]) {
struct net_device *joindev;
dev = alloc_netdev(sizeof(*ns), "eth%d", NET_NAME_UNKNOWN, nsim_setup);
if (!dev)
return ERR_PTR(-ENOMEM);
joindev = __dev_get_by_index(src_net,
nla_get_u32(tb[IFLA_LINK]));
if (!joindev)
return -ENODEV;
if (joindev->netdev_ops != &nsim_netdev_ops)
return -EINVAL;
ns = netdev_priv(dev);
ns->netdev = dev;
ns->nsim_dev = nsim_dev;
ns->nsim_dev_port = nsim_dev_port;
ns->nsim_bus_dev = nsim_dev->nsim_bus_dev;
SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev);
dev->netdev_ops = &nsim_netdev_ops;
joinns = netdev_priv(joindev);
}
rtnl_lock();
err = nsim_bpf_init(ns);
if (err)
goto err_free_netdev;
ns->sdev = nsim_sdev_get(joinns);
if (IS_ERR(ns->sdev))
return PTR_ERR(ns->sdev);
nsim_ipsec_init(ns);
err = register_netdevice(dev);
if (err)
goto err_sdev_put;
return 0;
goto err_ipsec_teardown;
rtnl_unlock();
err_sdev_put:
nsim_sdev_put(ns->sdev);
return err;
return ns;
err_ipsec_teardown:
nsim_ipsec_teardown(ns);
nsim_bpf_uninit(ns);
rtnl_unlock();
err_free_netdev:
free_netdev(dev);
return ERR_PTR(err);
}
void nsim_destroy(struct netdevsim *ns)
{
struct net_device *dev = ns->netdev;
rtnl_lock();
unregister_netdevice(dev);
nsim_ipsec_teardown(ns);
nsim_bpf_uninit(ns);
rtnl_unlock();
free_netdev(dev);
}
static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
NL_SET_ERR_MSG_MOD(extack, "Please use: echo \"[ID] [PORT_COUNT]\" > /sys/bus/netdevsim/new_device");
return -EOPNOTSUPP;
}
static struct rtnl_link_ops nsim_link_ops __read_mostly = {
.kind = DRV_NAME,
.priv_size = sizeof(struct netdevsim),
.setup = nsim_setup,
.validate = nsim_validate,
.newlink = nsim_newlink,
};
static int __init nsim_module_init(void)
{
int err;
nsim_ddir = debugfs_create_dir(DRV_NAME, NULL);
if (IS_ERR_OR_NULL(nsim_ddir))
return -ENOMEM;
err = nsim_sdev_init();
err = nsim_dev_init();
if (err)
goto err_debugfs_destroy;
return err;
err = bus_register(&nsim_bus);
err = nsim_bus_init();
if (err)
goto err_sdev_exit;
err = nsim_devlink_init();
if (err)
goto err_unreg_bus;
goto err_dev_exit;
err = rtnl_link_register(&nsim_link_ops);
if (err)
goto err_dl_fini;
goto err_bus_exit;
return 0;
err_dl_fini:
nsim_devlink_exit();
err_unreg_bus:
bus_unregister(&nsim_bus);
err_sdev_exit:
nsim_sdev_exit();
err_debugfs_destroy:
debugfs_remove_recursive(nsim_ddir);
err_bus_exit:
nsim_bus_exit();
err_dev_exit:
nsim_dev_exit();
return err;
}
static void __exit nsim_module_exit(void)
{
rtnl_link_unregister(&nsim_link_ops);
nsim_devlink_exit();
bus_unregister(&nsim_bus);
nsim_sdev_exit();
debugfs_remove_recursive(nsim_ddir);
nsim_bus_exit();
nsim_dev_exit();
}
module_init(nsim_module_init);

View File

@ -18,6 +18,7 @@
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/u64_stats_sync.h>
#include <net/devlink.h>
#include <net/xdp.h>
#define DRV_NAME "netdevsim"
@ -26,36 +27,6 @@
#define NSIM_EA(extack, msg) NL_SET_ERR_MSG_MOD((extack), msg)
struct bpf_prog;
struct bpf_offload_dev;
struct dentry;
struct nsim_vf_config;
struct netdevsim_shared_dev {
unsigned int refcnt;
u32 switch_id;
struct dentry *ddir;
struct bpf_offload_dev *bpf_dev;
bool bpf_bind_accept;
u32 bpf_bind_verifier_delay;
struct dentry *ddir_bpf_bound_progs;
u32 prog_id_gen;
struct list_head bpf_bound_progs;
struct list_head bpf_bound_maps;
};
struct netdevsim;
struct netdevsim_shared_dev *nsim_sdev_get(struct netdevsim *joinns);
void nsim_sdev_put(struct netdevsim_shared_dev *sdev);
int nsim_sdev_init(void);
void nsim_sdev_exit(void);
#define NSIM_IPSEC_MAX_SA_COUNT 33
#define NSIM_IPSEC_VALID BIT(31)
@ -79,18 +50,14 @@ struct nsim_ipsec {
struct netdevsim {
struct net_device *netdev;
struct nsim_dev *nsim_dev;
struct nsim_dev_port *nsim_dev_port;
u64 tx_packets;
u64 tx_bytes;
struct u64_stats_sync syncp;
struct device dev;
struct netdevsim_shared_dev *sdev;
struct dentry *ddir;
unsigned int num_vfs;
struct nsim_vf_config *vfconfigs;
struct nsim_bus_dev *nsim_bus_dev;
struct bpf_prog *bpf_offloaded;
u32 bpf_offloaded_id;
@ -104,11 +71,16 @@ struct netdevsim {
bool bpf_xdpoffload_accept;
bool bpf_map_accept;
struct devlink *devlink;
struct nsim_ipsec ipsec;
};
struct netdevsim *
nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port);
void nsim_destroy(struct netdevsim *ns);
#ifdef CONFIG_BPF_SYSCALL
int nsim_bpf_dev_init(struct nsim_dev *nsim_dev);
void nsim_bpf_dev_exit(struct nsim_dev *nsim_dev);
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);
@ -116,6 +88,15 @@ 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);
#else
static inline int nsim_bpf_dev_init(struct nsim_dev *nsim_dev)
{
return 0;
}
static inline void nsim_bpf_dev_exit(struct nsim_dev *nsim_dev)
{
}
static inline int nsim_bpf_init(struct netdevsim *ns)
{
return 0;
@ -153,16 +134,46 @@ enum nsim_resource_id {
NSIM_RESOURCE_IPV6_FIB_RULES,
};
int nsim_devlink_setup(struct netdevsim *ns);
void nsim_devlink_teardown(struct netdevsim *ns);
struct nsim_dev_port {
struct list_head list;
struct devlink_port devlink_port;
unsigned int port_index;
struct dentry *ddir;
struct netdevsim *ns;
};
int nsim_devlink_init(void);
void nsim_devlink_exit(void);
struct nsim_dev {
struct nsim_bus_dev *nsim_bus_dev;
struct nsim_fib_data *fib_data;
struct dentry *ddir;
struct dentry *ports_ddir;
struct bpf_offload_dev *bpf_dev;
bool bpf_bind_accept;
u32 bpf_bind_verifier_delay;
struct dentry *ddir_bpf_bound_progs;
u32 prog_id_gen;
struct list_head bpf_bound_progs;
struct list_head bpf_bound_maps;
struct netdev_phys_item_id switch_id;
struct list_head port_list;
struct mutex port_list_lock; /* protects port list */
};
int nsim_fib_init(void);
void nsim_fib_exit(void);
u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max);
int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
int nsim_dev_init(void);
void nsim_dev_exit(void);
int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev);
void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev);
int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
unsigned int port_index);
int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
unsigned int port_index);
struct nsim_fib_data *nsim_fib_create(void);
void nsim_fib_destroy(struct nsim_fib_data *fib_data);
u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
enum nsim_resource_id res_id, bool max);
int nsim_fib_set_max(struct nsim_fib_data *fib_data,
enum nsim_resource_id res_id, u64 val,
struct netlink_ext_ack *extack);
#if IS_ENABLED(CONFIG_XFRM_OFFLOAD)
@ -184,7 +195,26 @@ static inline bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb)
}
#endif
static inline struct netdevsim *to_nsim(struct device *ptr)
{
return container_of(ptr, struct netdevsim, dev);
}
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;
};
struct nsim_bus_dev {
struct device dev;
struct list_head list;
unsigned int port_count;
unsigned int num_vfs;
struct nsim_vf_config *vfconfigs;
};
int nsim_bus_init(void);
void nsim_bus_exit(void);

View File

@ -1,6 +1,7 @@
#!/usr/bin/python3
# Copyright (C) 2017 Netronome Systems, Inc.
# Copyright (c) 2019 Mellanox Technologies. All rights reserved
#
# 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
@ -15,10 +16,12 @@
from datetime import datetime
import argparse
import errno
import json
import os
import pprint
import random
import re
import string
import struct
import subprocess
@ -306,6 +309,8 @@ class DebugfsDir:
_, out = cmd('ls ' + path)
for f in out.split():
if f == "ports":
continue
p = os.path.join(path, f)
if os.path.isfile(p):
_, out = cmd('cat %s/%s' % (path, f))
@ -321,42 +326,112 @@ class DebugfsDir:
return dfs
class NetdevSimDev:
"""
Class for netdevsim bus device and its attributes.
"""
def __init__(self, port_count=1):
addr = 0
while True:
try:
with open("/sys/bus/netdevsim/new_device", "w") as f:
f.write("%u %u" % (addr, port_count))
except OSError as e:
if e.errno == errno.ENOSPC:
addr += 1
continue
raise e
break
self.addr = addr
# As probe of netdevsim device might happen from a workqueue,
# so wait here until all netdevs appear.
self.wait_for_netdevs(port_count)
ret, out = cmd("udevadm settle", fail=False)
if ret:
raise Exception("udevadm settle failed")
ifnames = self.get_ifnames()
devs.append(self)
self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
self.nsims = []
for port_index in range(port_count):
self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
def get_ifnames(self):
ifnames = []
listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
for ifname in listdir:
ifnames.append(ifname)
ifnames.sort()
return ifnames
def wait_for_netdevs(self, port_count):
timeout = 5
timeout_start = time.time()
while True:
try:
ifnames = self.get_ifnames()
except FileNotFoundError as e:
ifnames = []
if len(ifnames) == port_count:
break
if time.time() < timeout_start + timeout:
continue
raise Exception("netdevices did not appear within timeout")
def dfs_num_bound_progs(self):
path = os.path.join(self.dfs_dir, "bpf_bound_progs")
_, progs = cmd('ls %s' % (path))
return len(progs.split())
def dfs_get_bound_progs(self, expected):
progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
if expected is not None:
if len(progs) != expected:
fail(True, "%d BPF programs bound, expected %d" %
(len(progs), expected))
return progs
def remove(self):
with open("/sys/bus/netdevsim/del_device", "w") as f:
f.write("%u" % self.addr)
devs.remove(self)
def remove_nsim(self, nsim):
self.nsims.remove(nsim)
with open("/sys/bus/netdevsim/devices/netdevsim%u/del_port" % self.addr ,"w") as f:
f.write("%u" % nsim.port_index)
class NetdevSim:
"""
Class for netdevsim netdevice and its attributes.
"""
def __init__(self, link=None):
self.link = link
self.dev = self._netdevsim_create()
devs.append(self)
def __init__(self, nsimdev, port_index, ifname):
# In case udev renamed the netdev to according to new schema,
# check if the name matches the port_index.
nsimnamere = re.compile("eni\d+np(\d+)")
match = nsimnamere.match(ifname)
if match and int(match.groups()[0]) != port_index + 1:
raise Exception("netdevice name mismatches the expected one")
self.nsimdev = nsimdev
self.port_index = port_index
self.ns = ""
self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
self.sdev_dir = self.dfs_dir + '/sdev/'
self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
self.dfs_refresh()
_, [self.dev] = ip("link show dev %s" % ifname)
def __getitem__(self, key):
return self.dev[key]
def _netdevsim_create(self):
link = "" if self.link is None else "link " + self.link.dev['ifname']
_, old = ip("link show")
ip("link add sim%d {link} type netdevsim".format(link=link))
_, new = ip("link show")
for dev in new:
f = filter(lambda x: x["ifname"] == dev["ifname"], old)
if len(list(f)) == 0:
return dev
raise Exception("failed to create netdevsim device")
def remove(self):
devs.remove(self)
ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
self.nsimdev.remove_nsim(self)
def dfs_refresh(self):
self.dfs = DebugfsDir(self.dfs_dir)
@ -367,22 +442,9 @@ class NetdevSim:
_, data = cmd('cat %s' % (path))
return data.strip()
def dfs_num_bound_progs(self):
path = os.path.join(self.sdev_dir, "bpf_bound_progs")
_, progs = cmd('ls %s' % (path))
return len(progs.split())
def dfs_get_bound_progs(self, expected):
progs = DebugfsDir(os.path.join(self.sdev_dir, "bpf_bound_progs"))
if expected is not None:
if len(progs) != expected:
fail(True, "%d BPF programs bound, expected %d" %
(len(progs), expected))
return progs
def wait_for_flush(self, bound=0, total=0, n_retry=20):
for i in range(n_retry):
nbound = self.dfs_num_bound_progs()
nbound = self.nsimdev.dfs_num_bound_progs()
nprogs = len(bpftool_prog_list())
if nbound == bound and nprogs == total:
return
@ -612,7 +674,7 @@ def test_spurios_extack(sim, obj, skip_hw, needle):
include_stderr=True)
check_no_extack(res, needle)
def test_multi_prog(sim, obj, modename, modeid):
def test_multi_prog(simdev, sim, obj, modename, modeid):
start_test("Test multi-attachment XDP - %s + offload..." %
(modename or "default", ))
sim.set_xdp(obj, "offload")
@ -668,11 +730,12 @@ def test_multi_prog(sim, obj, modename, modeid):
check_multi_basic(two_xdps)
start_test("Test multi-attachment XDP - device remove...")
sim.remove()
simdev.remove()
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.set_ethtool_tc_offloads(True)
return sim
return [simdev, sim]
# Parse command line
parser = argparse.ArgumentParser()
@ -729,12 +792,14 @@ try:
bytecode = bpf_bytecode("1,6 0 0 4294967295,")
start_test("Test destruction of generic XDP...")
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.set_xdp(obj, "generic")
sim.remove()
simdev.remove()
bpftool_prog_list_wait(expected=0)
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.tc_add_ingress()
start_test("Test TC non-offloaded...")
@ -744,7 +809,7 @@ try:
start_test("Test TC non-offloaded isn't getting bound...")
ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
fail(ret != 0, "Software TC filter did not load")
sim.dfs_get_bound_progs(expected=0)
simdev.dfs_get_bound_progs(expected=0)
sim.tc_flush_filters()
@ -761,7 +826,7 @@ try:
start_test("Test TC offload by default...")
ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
fail(ret != 0, "Software TC filter did not load")
sim.dfs_get_bound_progs(expected=0)
simdev.dfs_get_bound_progs(expected=0)
ingress = sim.tc_show_ingress(expected=1)
fltr = ingress[0]
fail(not fltr["in_hw"], "Filter not offloaded by default")
@ -771,7 +836,7 @@ try:
start_test("Test TC cBPF bytcode tries offload by default...")
ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
fail(ret != 0, "Software TC filter did not load")
sim.dfs_get_bound_progs(expected=0)
simdev.dfs_get_bound_progs(expected=0)
ingress = sim.tc_show_ingress(expected=1)
fltr = ingress[0]
fail(not fltr["in_hw"], "Bytecode not offloaded by default")
@ -839,7 +904,7 @@ try:
check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
start_test("Test TC offload basics...")
dfs = sim.dfs_get_bound_progs(expected=1)
dfs = simdev.dfs_get_bound_progs(expected=1)
progs = bpftool_prog_list(expected=1)
ingress = sim.tc_show_ingress(expected=1)
@ -874,18 +939,20 @@ try:
start_test("Test destroying device gets rid of TC filters...")
sim.cls_bpf_add_filter(obj, skip_sw=True)
sim.remove()
simdev.remove()
bpftool_prog_list_wait(expected=0)
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.set_ethtool_tc_offloads(True)
start_test("Test destroying device gets rid of XDP...")
sim.set_xdp(obj, "offload")
sim.remove()
simdev.remove()
bpftool_prog_list_wait(expected=0)
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.set_ethtool_tc_offloads(True)
start_test("Test XDP prog reporting...")
@ -971,7 +1038,7 @@ try:
check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
start_test("Test XDP offload is device bound...")
dfs = sim.dfs_get_bound_progs(expected=1)
dfs = simdev.dfs_get_bound_progs(expected=1)
dprog = dfs[0]
fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
@ -990,7 +1057,8 @@ try:
bpftool_prog_list_wait(expected=0)
start_test("Test attempt to use a program for a wrong device...")
sim2 = NetdevSim()
simdev2 = NetdevSimDev()
sim2, = simdev2.nsims
sim2.set_xdp(obj, "offload")
pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
@ -998,7 +1066,7 @@ try:
fail=False, include_stderr=True)
fail(ret == 0, "Pinned program loaded for a different device accepted")
check_extack_nsim(err, "program bound to different dev.", args)
sim2.remove()
simdev2.remove()
ret, _, err = sim.set_xdp(pinned, "offload",
fail=False, include_stderr=True)
fail(ret == 0, "Pinned program loaded for a removed device accepted")
@ -1006,9 +1074,9 @@ try:
rm(pin_file)
bpftool_prog_list_wait(expected=0)
sim = test_multi_prog(sim, obj, "", 1)
sim = test_multi_prog(sim, obj, "drv", 1)
sim = test_multi_prog(sim, obj, "generic", 2)
simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
start_test("Test mixing of TC and XDP...")
sim.tc_add_ingress()
@ -1055,15 +1123,15 @@ try:
start_test("Test if netdev removal waits for translation...")
delay_msec = 500
sim.dfs["sdev/bpf_bind_verifier_delay"] = delay_msec
sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
start = time.time()
cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
(sim['ifname'], obj)
tc_proc = cmd(cmd_line, background=True, fail=False)
# Wait for the verifier to start
while sim.dfs_num_bound_progs() <= 2:
while simdev.dfs_num_bound_progs() <= 2:
pass
sim.remove()
simdev.remove()
end = time.time()
ret, _ = cmd_result(tc_proc, fail=False)
time_diff = end - start
@ -1078,7 +1146,8 @@ try:
clean_up()
bpftool_prog_list_wait(expected=0)
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
map_obj = bpf_obj("sample_map_ret0.o")
start_test("Test loading program with maps...")
sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
@ -1100,7 +1169,7 @@ try:
prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
sim.remove()
simdev.remove()
start_test("Test bpftool bound info reporting (removed dev)...")
check_dev_info_removed(prog_file=prog_file, map_file=map_file)
@ -1109,7 +1178,8 @@ try:
clean_up()
bpftool_prog_list_wait(expected=0)
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
start_test("Test map update (no flags)...")
sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
@ -1190,27 +1260,29 @@ try:
start_test("Test map remove...")
sim.unset_xdp("offload")
bpftool_map_list_wait(expected=0)
sim.remove()
simdev.remove()
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
sim.remove()
simdev.remove()
bpftool_map_list_wait(expected=0)
start_test("Test map creation fail path...")
sim = NetdevSim()
simdev = NetdevSimDev()
sim, = simdev.nsims
sim.dfs["bpf_map_accept"] = "N"
ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
fail(ret == 0,
"netdevsim didn't refuse to create a map with offload disabled")
sim.remove()
simdev.remove()
start_test("Test multi-dev ASIC program reuse...")
simA = NetdevSim()
simB1 = NetdevSim()
simB2 = NetdevSim(link=simB1)
simB3 = NetdevSim(link=simB1)
simdevA = NetdevSimDev()
simA, = simdevA.nsims
simdevB = NetdevSimDev(3)
simB1, simB2, simB3 = simdevB.nsims
sims = (simA, simB1, simB2, simB3)
simB = (simB1, simB2, simB3)
@ -1222,13 +1294,13 @@ try:
progB = bpf_pinned("/sys/fs/bpf/nsimB")
simA.set_xdp(progA, "offload", JSON=False)
for d in simB:
for d in simdevB.nsims:
d.set_xdp(progB, "offload", JSON=False)
start_test("Test multi-dev ASIC cross-dev replace...")
ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
fail(ret == 0, "cross-ASIC program allowed")
for d in simB:
for d in simdevB.nsims:
ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
fail(ret == 0, "cross-ASIC program allowed")
@ -1240,7 +1312,7 @@ try:
fail=False, include_stderr=True)
fail(ret == 0, "cross-ASIC program allowed")
check_extack_nsim(err, "program bound to different dev.", args)
for d in simB:
for d in simdevB.nsims:
ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
fail=False, include_stderr=True)
fail(ret == 0, "cross-ASIC program allowed")
@ -1277,7 +1349,7 @@ try:
start_test("Test multi-dev ASIC cross-dev destruction...")
bpftool_prog_list_wait(expected=2)
simA.remove()
simdevA.remove()
bpftool_prog_list_wait(expected=1)
ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
@ -1295,6 +1367,7 @@ try:
fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
simB3.remove()
simdevB.remove()
bpftool_prog_list_wait(expected=0)
start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")

View File

@ -696,9 +696,9 @@ kci_test_ipsec_offload()
algo="aead rfc4106(gcm(aes)) 0x3132333435363738393031323334353664636261 128"
srcip=192.168.123.3
dstip=192.168.123.4
dev=simx1
sysfsd=/sys/kernel/debug/netdevsim/$dev
sysfsd=/sys/kernel/debug/netdevsim/netdevsim0/ports/0/
sysfsf=$sysfsd/ipsec
sysfsnet=/sys/bus/netdevsim/devices/netdevsim0/net/
# setup netdevsim since dummydev doesn't have offload support
modprobe netdevsim
@ -708,7 +708,11 @@ kci_test_ipsec_offload()
return 1
fi
ip link add $dev type netdevsim
echo "0" > /sys/bus/netdevsim/new_device
while [ ! -d $sysfsnet ] ; do :; done
udevadm settle
dev=`ls $sysfsnet`
ip addr add $srcip dev $dev
ip link set $dev up
if [ ! -d $sysfsd ] ; then
@ -781,7 +785,6 @@ EOF
fi
# clean up any leftovers
ip link del $dev
rmmod netdevsim
if [ $ret -ne 0 ]; then