diff options
-rw-r--r-- | drivers/net/netdevsim/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/netdevsim/bpf.c | 92 | ||||
-rw-r--r-- | drivers/net/netdevsim/bus.c | 341 | ||||
-rw-r--r-- | drivers/net/netdevsim/dev.c | 447 | ||||
-rw-r--r-- | drivers/net/netdevsim/devlink.c | 295 | ||||
-rw-r--r-- | drivers/net/netdevsim/fib.c | 102 | ||||
-rw-r--r-- | drivers/net/netdevsim/ipsec.c | 3 | ||||
-rw-r--r-- | drivers/net/netdevsim/netdev.c | 396 | ||||
-rw-r--r-- | drivers/net/netdevsim/netdevsim.h | 130 | ||||
-rwxr-xr-x | tools/testing/selftests/bpf/test_offload.py | 227 | ||||
-rwxr-xr-x | tools/testing/selftests/net/rtnetlink.sh | 11 |
11 files changed, 1213 insertions, 833 deletions
diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile index cdf8611d2811..09f1315d2f2a 100644 --- a/drivers/net/netdevsim/Makefile +++ b/drivers/net/netdevsim/Makefile @@ -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 += \ diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index a93aafe87db3..2b74425822ab 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -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); } diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c new file mode 100644 index 000000000000..ae482347b67b --- /dev/null +++ b/drivers/net/netdevsim/bus.c @@ -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); +} diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c new file mode 100644 index 000000000000..b509b941d5ca --- /dev/null +++ b/drivers/net/netdevsim/dev.c @@ -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, + ¶ms); + 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, ¶ms); + 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, ¶ms); + 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, + ¶ms); + 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, ¶ms); + 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, ¶ms); + 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); +} diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c deleted file mode 100644 index 5135fc371f01..000000000000 --- a/drivers/net/netdevsim/devlink.c +++ /dev/null @@ -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, - ¶ms); - 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, ¶ms); - 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, ¶ms); - 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, - ¶ms); - 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, ¶ms); - 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, ¶ms); - 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; -} diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index f61d094746c0..8c57ba747772 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -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; - - 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; + struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, + fib_nb); - 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); } diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c index 76e11d889bb6..e27fc1a4516d 100644 --- a/drivers/net/netdevsim/ipsec.c +++ b/drivers/net/netdevsim/ipsec.c @@ -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); } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 7805fa840383..e5c8aa08e1cd 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -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(); - if (err) - goto err_debugfs_destroy; - - err = bus_register(&nsim_bus); + err = nsim_dev_init(); if (err) - goto err_sdev_exit; + return err; - err = nsim_devlink_init(); + err = nsim_bus_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); diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 2667f9b0e1f9..3f398797c2bc 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -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); diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index a7f95106119f..425f9ed27c3b 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py @@ -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 NetdevSim: +class NetdevSimDev: """ - Class for netdevsim netdevice and its attributes. + Class for netdevsim bus device and its attributes. """ - def __init__(self, link=None): - self.link = link + 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() - self.dev = self._netdevsim_create() 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") - self.ns = "" + 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()) - self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) - self.sdev_dir = self.dfs_dir + '/sdev/' - self.dfs_refresh() + 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 __getitem__(self, key): - return self.dev[key] + def remove(self): + with open("/sys/bus/netdevsim/del_device", "w") as f: + f.write("%u" % self.addr) + devs.remove(self) - 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") + 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) - for dev in new: - f = filter(lambda x: x["ifname"] == dev["ifname"], old) - if len(list(f)) == 0: - return dev +class NetdevSim: + """ + Class for netdevsim netdevice and its attributes. + """ + + 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 = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index) + self.dfs_refresh() + _, [self.dev] = ip("link show dev %s" % ifname) - raise Exception("failed to create netdevsim device") + def __getitem__(self, key): + return self.dev[key] 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...") diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index b447803f3f8a..b25c9fe019d2 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -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 |