aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/netdevsim
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/netdevsim')
-rw-r--r--drivers/net/netdevsim/Makefile2
-rw-r--r--drivers/net/netdevsim/bpf.c9
-rw-r--r--drivers/net/netdevsim/bus.c28
-rw-r--r--drivers/net/netdevsim/dev.c267
-rw-r--r--drivers/net/netdevsim/ethtool.c13
-rw-r--r--drivers/net/netdevsim/fib.c116
-rw-r--r--drivers/net/netdevsim/hwstats.c486
-rw-r--r--drivers/net/netdevsim/ipsec.c2
-rw-r--r--drivers/net/netdevsim/netdev.c4
-rw-r--r--drivers/net/netdevsim/netdevsim.h28
10 files changed, 755 insertions, 200 deletions
diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
index a1cbfa44a1e1..5735e5b1a2cb 100644
--- a/drivers/net/netdevsim/Makefile
+++ b/drivers/net/netdevsim/Makefile
@@ -3,7 +3,7 @@
obj-$(CONFIG_NETDEVSIM) += netdevsim.o
netdevsim-objs := \
- netdev.o dev.o ethtool.o fib.o bus.o health.o udp_tunnels.o
+ netdev.o dev.o ethtool.o fib.o bus.o health.o hwstats.o udp_tunnels.o
ifeq ($(CONFIG_BPF_SYSCALL),y)
netdevsim-objs += \
diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c
index 90aafb56f140..50854265864d 100644
--- a/drivers/net/netdevsim/bpf.c
+++ b/drivers/net/netdevsim/bpf.c
@@ -351,10 +351,12 @@ nsim_map_alloc_elem(struct bpf_offloaded_map *offmap, unsigned int idx)
{
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
- nmap->entry[idx].key = kmalloc(offmap->map.key_size, GFP_USER);
+ nmap->entry[idx].key = kmalloc(offmap->map.key_size,
+ GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
if (!nmap->entry[idx].key)
return -ENOMEM;
- nmap->entry[idx].value = kmalloc(offmap->map.value_size, GFP_USER);
+ nmap->entry[idx].value = kmalloc(offmap->map.value_size,
+ GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
if (!nmap->entry[idx].value) {
kfree(nmap->entry[idx].key);
nmap->entry[idx].key = NULL;
@@ -496,7 +498,7 @@ nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap)
if (offmap->map.map_flags)
return -EINVAL;
- nmap = kzalloc(sizeof(*nmap), GFP_USER);
+ nmap = kzalloc(sizeof(*nmap), GFP_KERNEL_ACCOUNT);
if (!nmap)
return -ENOMEM;
@@ -514,6 +516,7 @@ nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap)
goto err_free;
key = nmap->entry[i].key;
*key = i;
+ memset(nmap->entry[i].value, 0, offmap->map.value_size);
}
}
diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c
index 25cb2e600d53..0052968e881e 100644
--- a/drivers/net/netdevsim/bus.c
+++ b/drivers/net/netdevsim/bus.c
@@ -72,16 +72,7 @@ new_port_store(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- if (!mutex_trylock(&nsim_bus_dev->nsim_bus_reload_lock))
- return -EBUSY;
-
- if (nsim_bus_dev->in_reload) {
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
- return -EBUSY;
- }
-
ret = nsim_drv_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_PF, port_index);
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return ret ? ret : count;
}
@@ -102,16 +93,7 @@ del_port_store(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- if (!mutex_trylock(&nsim_bus_dev->nsim_bus_reload_lock))
- return -EBUSY;
-
- if (nsim_bus_dev->in_reload) {
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
- return -EBUSY;
- }
-
ret = nsim_drv_port_del(nsim_bus_dev, NSIM_DEV_PORT_TYPE_PF, port_index);
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return ret ? ret : count;
}
@@ -135,6 +117,10 @@ static const struct attribute_group *nsim_bus_dev_attr_groups[] = {
static void nsim_bus_dev_release(struct device *dev)
{
+ struct nsim_bus_dev *nsim_bus_dev;
+
+ nsim_bus_dev = container_of(dev, struct nsim_bus_dev, dev);
+ kfree(nsim_bus_dev);
}
static struct device_type nsim_bus_dev_type = {
@@ -298,7 +284,6 @@ nsim_bus_dev_new(unsigned int id, unsigned int port_count, unsigned int num_queu
nsim_bus_dev->num_queues = num_queues;
nsim_bus_dev->initial_net = current->nsproxy->net_ns;
nsim_bus_dev->max_vfs = NSIM_BUS_DEV_MAX_VFS;
- mutex_init(&nsim_bus_dev->nsim_bus_reload_lock);
/* Disallow using nsim_bus_dev */
smp_store_release(&nsim_bus_dev->init, false);
@@ -310,6 +295,8 @@ nsim_bus_dev_new(unsigned int id, unsigned int port_count, unsigned int num_queu
err_nsim_bus_dev_id_free:
ida_free(&nsim_bus_dev_ids, nsim_bus_dev->dev.id);
+ put_device(&nsim_bus_dev->dev);
+ nsim_bus_dev = NULL;
err_nsim_bus_dev_free:
kfree(nsim_bus_dev);
return ERR_PTR(err);
@@ -319,9 +306,8 @@ static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev)
{
/* Disallow using nsim_bus_dev */
smp_store_release(&nsim_bus_dev->init, false);
- device_unregister(&nsim_bus_dev->dev);
ida_free(&nsim_bus_dev_ids, nsim_bus_dev->dev.id);
- kfree(nsim_bus_dev);
+ device_unregister(&nsim_bus_dev->dev);
}
static struct device_driver nsim_driver = {
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index 54345c096a16..a7880c7ce94c 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -59,7 +59,7 @@ static struct dentry *nsim_dev_ddir;
unsigned int nsim_dev_get_vfs(struct nsim_dev *nsim_dev)
{
WARN_ON(!lockdep_rtnl_is_held() &&
- !lockdep_is_held(&nsim_dev->vfs_lock));
+ !devl_lock_is_held(priv_to_devlink(nsim_dev)));
return nsim_dev->nsim_bus_dev->num_vfs;
}
@@ -275,7 +275,7 @@ static ssize_t nsim_bus_dev_max_vfs_write(struct file *file,
return -ENOMEM;
nsim_dev = file->private_data;
- mutex_lock(&nsim_dev->vfs_lock);
+ devl_lock(priv_to_devlink(nsim_dev));
/* Reject if VFs are configured */
if (nsim_dev_get_vfs(nsim_dev)) {
ret = -EBUSY;
@@ -285,7 +285,7 @@ static ssize_t nsim_bus_dev_max_vfs_write(struct file *file,
*ppos += count;
ret = count;
}
- mutex_unlock(&nsim_dev->vfs_lock);
+ devl_unlock(priv_to_devlink(nsim_dev));
kfree(vfconfigs);
return ret;
@@ -309,8 +309,10 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
if (IS_ERR(nsim_dev->ddir))
return PTR_ERR(nsim_dev->ddir);
nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
- if (IS_ERR(nsim_dev->ports_ddir))
- return PTR_ERR(nsim_dev->ports_ddir);
+ if (IS_ERR(nsim_dev->ports_ddir)) {
+ err = PTR_ERR(nsim_dev->ports_ddir);
+ goto err_ddir;
+ }
debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
&nsim_dev->fw_update_status);
debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir,
@@ -339,13 +341,14 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
debugfs_create_bool("fail_trap_policer_counter_get", 0600,
nsim_dev->ddir,
&nsim_dev->fail_trap_policer_counter_get);
+ /* caution, dev_max_vfs write takes devlink lock */
debugfs_create_file("max_vfs", 0600, nsim_dev->ddir,
nsim_dev, &nsim_dev_max_vfs_fops);
nsim_dev->nodes_ddir = debugfs_create_dir("rate_nodes", nsim_dev->ddir);
if (IS_ERR(nsim_dev->nodes_ddir)) {
err = PTR_ERR(nsim_dev->nodes_ddir);
- goto err_out;
+ goto err_ports_ddir;
}
debugfs_create_bool("fail_trap_drop_counter_get", 0600,
nsim_dev->ddir,
@@ -353,8 +356,9 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
nsim_udp_tunnels_debugfs_create(nsim_dev);
return 0;
-err_out:
+err_ports_ddir:
debugfs_remove_recursive(nsim_dev->ports_ddir);
+err_ddir:
debugfs_remove_recursive(nsim_dev->ddir);
return err;
}
@@ -435,64 +439,70 @@ static int nsim_dev_resources_register(struct devlink *devlink)
int err;
/* Resources for IPv4 */
- err = devlink_resource_register(devlink, "IPv4", (u64)-1,
- NSIM_RESOURCE_IPV4,
- DEVLINK_RESOURCE_ID_PARENT_TOP,
- &params);
+ err = devl_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;
+ goto err_out;
}
- err = devlink_resource_register(devlink, "fib", (u64)-1,
- NSIM_RESOURCE_IPV4_FIB,
- NSIM_RESOURCE_IPV4, &params);
+ err = devl_resource_register(devlink, "fib", (u64)-1,
+ NSIM_RESOURCE_IPV4_FIB,
+ NSIM_RESOURCE_IPV4, &params);
if (err) {
pr_err("Failed to register IPv4 FIB resource\n");
- return err;
+ goto err_out;
}
- err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
- NSIM_RESOURCE_IPV4_FIB_RULES,
- NSIM_RESOURCE_IPV4, &params);
+ err = devl_resource_register(devlink, "fib-rules", (u64)-1,
+ NSIM_RESOURCE_IPV4_FIB_RULES,
+ NSIM_RESOURCE_IPV4, &params);
if (err) {
pr_err("Failed to register IPv4 FIB rules resource\n");
- return err;
+ goto err_out;
}
/* Resources for IPv6 */
- err = devlink_resource_register(devlink, "IPv6", (u64)-1,
- NSIM_RESOURCE_IPV6,
- DEVLINK_RESOURCE_ID_PARENT_TOP,
- &params);
+ err = devl_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;
+ goto err_out;
}
- err = devlink_resource_register(devlink, "fib", (u64)-1,
- NSIM_RESOURCE_IPV6_FIB,
- NSIM_RESOURCE_IPV6, &params);
+ err = devl_resource_register(devlink, "fib", (u64)-1,
+ NSIM_RESOURCE_IPV6_FIB,
+ NSIM_RESOURCE_IPV6, &params);
if (err) {
pr_err("Failed to register IPv6 FIB resource\n");
- return err;
+ goto err_out;
}
- err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
- NSIM_RESOURCE_IPV6_FIB_RULES,
- NSIM_RESOURCE_IPV6, &params);
+ err = devl_resource_register(devlink, "fib-rules", (u64)-1,
+ NSIM_RESOURCE_IPV6_FIB_RULES,
+ NSIM_RESOURCE_IPV6, &params);
if (err) {
pr_err("Failed to register IPv6 FIB rules resource\n");
- return err;
+ goto err_out;
}
/* Resources for nexthops */
- err = devlink_resource_register(devlink, "nexthops", (u64)-1,
- NSIM_RESOURCE_NEXTHOPS,
- DEVLINK_RESOURCE_ID_PARENT_TOP,
- &params);
+ err = devl_resource_register(devlink, "nexthops", (u64)-1,
+ NSIM_RESOURCE_NEXTHOPS,
+ DEVLINK_RESOURCE_ID_PARENT_TOP,
+ &params);
+ if (err) {
+ pr_err("Failed to register NEXTHOPS resource\n");
+ goto err_out;
+ }
+ return 0;
-out:
+err_out:
+ devl_resources_unregister(devlink);
return err;
}
@@ -556,17 +566,20 @@ static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,
struct devlink *devlink)
{
nsim_dev->dummy_region =
- devlink_region_create(devlink, &dummy_region_ops,
- NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
- NSIM_DEV_DUMMY_REGION_SIZE);
+ devl_region_create(devlink, &dummy_region_ops,
+ NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
+ NSIM_DEV_DUMMY_REGION_SIZE);
return PTR_ERR_OR_ZERO(nsim_dev->dummy_region);
}
static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)
{
- devlink_region_destroy(nsim_dev->dummy_region);
+ devl_region_destroy(nsim_dev->dummy_region);
}
+static int
+__nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type,
+ unsigned int port_index);
static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port);
static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev,
@@ -575,12 +588,10 @@ static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev,
struct devlink *devlink = priv_to_devlink(nsim_dev);
struct nsim_dev_port *nsim_dev_port, *tmp;
- devlink_rate_nodes_destroy(devlink);
- mutex_lock(&nsim_dev->port_list_lock);
+ devl_rate_nodes_destroy(devlink);
list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list)
if (nsim_dev_port_is_vf(nsim_dev_port))
__nsim_dev_port_del(nsim_dev_port);
- mutex_unlock(&nsim_dev->port_list_lock);
nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY;
return 0;
}
@@ -588,11 +599,11 @@ static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev,
static int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev,
struct netlink_ext_ack *extack)
{
- struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;
+ struct nsim_dev_port *nsim_dev_port, *tmp;
int i, err;
for (i = 0; i < nsim_dev_get_vfs(nsim_dev); i++) {
- err = nsim_drv_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i);
+ err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_VF, i);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to initialize VFs' netdevsim ports");
pr_err("Failed to initialize VF id=%d. %d.\n", i, err);
@@ -603,8 +614,9 @@ static int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev,
return 0;
err_port_add_vfs:
- for (i--; i >= 0; i--)
- nsim_drv_port_del(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i);
+ list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list)
+ if (nsim_dev_port_is_vf(nsim_dev_port))
+ __nsim_dev_port_del(nsim_dev_port);
return err;
}
@@ -612,22 +624,16 @@ static int nsim_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
struct netlink_ext_ack *extack)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
- int err = 0;
- mutex_lock(&nsim_dev->vfs_lock);
if (mode == nsim_dev->esw_mode)
- goto unlock;
+ return 0;
if (mode == DEVLINK_ESWITCH_MODE_LEGACY)
- err = nsim_esw_legacy_enable(nsim_dev, extack);
- else if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
- err = nsim_esw_switchdev_enable(nsim_dev, extack);
- else
- err = -EINVAL;
+ return nsim_esw_legacy_enable(nsim_dev, extack);
+ if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
+ return nsim_esw_switchdev_enable(nsim_dev, extack);
-unlock:
- mutex_unlock(&nsim_dev->vfs_lock);
- return err;
+ return -EINVAL;
}
static int nsim_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
@@ -835,14 +841,18 @@ static void nsim_dev_trap_report_work(struct work_struct *work)
/* For each running port and enabled packet trap, generate a UDP
* packet with a random 5-tuple and report it.
*/
- mutex_lock(&nsim_dev->port_list_lock);
+ if (!devl_trylock(priv_to_devlink(nsim_dev))) {
+ schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 0);
+ return;
+ }
+
list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) {
if (!netif_running(nsim_dev_port->ns->netdev))
continue;
nsim_dev_trap_report(nsim_dev_port);
}
- mutex_unlock(&nsim_dev->port_list_lock);
+ devl_unlock(priv_to_devlink(nsim_dev));
schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
@@ -883,18 +893,18 @@ static int nsim_dev_traps_init(struct devlink *devlink)
nsim_trap_data->nsim_dev = nsim_dev;
nsim_dev->trap_data = nsim_trap_data;
- err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr,
- policers_count);
+ err = devl_trap_policers_register(devlink, nsim_trap_policers_arr,
+ policers_count);
if (err)
goto err_trap_policers_cnt_free;
- err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr,
- ARRAY_SIZE(nsim_trap_groups_arr));
+ err = devl_trap_groups_register(devlink, nsim_trap_groups_arr,
+ ARRAY_SIZE(nsim_trap_groups_arr));
if (err)
goto err_trap_policers_unregister;
- err = devlink_traps_register(devlink, nsim_traps_arr,
- ARRAY_SIZE(nsim_traps_arr), NULL);
+ err = devl_traps_register(devlink, nsim_traps_arr,
+ ARRAY_SIZE(nsim_traps_arr), NULL);
if (err)
goto err_trap_groups_unregister;
@@ -906,11 +916,11 @@ static int nsim_dev_traps_init(struct devlink *devlink)
return 0;
err_trap_groups_unregister:
- devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
- ARRAY_SIZE(nsim_trap_groups_arr));
+ devl_trap_groups_unregister(devlink, nsim_trap_groups_arr,
+ ARRAY_SIZE(nsim_trap_groups_arr));
err_trap_policers_unregister:
- devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
- ARRAY_SIZE(nsim_trap_policers_arr));
+ devl_trap_policers_unregister(devlink, nsim_trap_policers_arr,
+ ARRAY_SIZE(nsim_trap_policers_arr));
err_trap_policers_cnt_free:
kfree(nsim_trap_data->trap_policers_cnt_arr);
err_trap_items_free:
@@ -924,13 +934,14 @@ static void nsim_dev_traps_exit(struct devlink *devlink)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
+ /* caution, trap work takes devlink lock */
cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw);
- devlink_traps_unregister(devlink, nsim_traps_arr,
- ARRAY_SIZE(nsim_traps_arr));
- devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
- ARRAY_SIZE(nsim_trap_groups_arr));
- devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
- ARRAY_SIZE(nsim_trap_policers_arr));
+ devl_traps_unregister(devlink, nsim_traps_arr,
+ ARRAY_SIZE(nsim_traps_arr));
+ devl_trap_groups_unregister(devlink, nsim_trap_groups_arr,
+ ARRAY_SIZE(nsim_trap_groups_arr));
+ devl_trap_policers_unregister(devlink, nsim_trap_policers_arr,
+ ARRAY_SIZE(nsim_trap_policers_arr));
kfree(nsim_dev->trap_data->trap_policers_cnt_arr);
kfree(nsim_dev->trap_data->trap_items_arr);
kfree(nsim_dev->trap_data);
@@ -945,24 +956,16 @@ static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change,
struct netlink_ext_ack *extack)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
- struct nsim_bus_dev *nsim_bus_dev;
-
- nsim_bus_dev = nsim_dev->nsim_bus_dev;
- if (!mutex_trylock(&nsim_bus_dev->nsim_bus_reload_lock))
- return -EOPNOTSUPP;
if (nsim_dev->dont_allow_reload) {
/* For testing purposes, user set debugfs dont_allow_reload
* value to true. So forbid it.
*/
NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes");
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return -EOPNOTSUPP;
}
- nsim_bus_dev->in_reload = true;
nsim_dev_reload_destroy(nsim_dev);
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return 0;
}
@@ -971,33 +974,35 @@ static int nsim_dev_reload_up(struct devlink *devlink, enum devlink_reload_actio
struct netlink_ext_ack *extack)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
- struct nsim_bus_dev *nsim_bus_dev;
- int ret;
-
- nsim_bus_dev = nsim_dev->nsim_bus_dev;
- mutex_lock(&nsim_bus_dev->nsim_bus_reload_lock);
- nsim_bus_dev->in_reload = false;
if (nsim_dev->fail_reload) {
/* For testing purposes, user set debugfs fail_reload
* value to true. Fail right away.
*/
NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes");
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
return -EINVAL;
}
*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
- ret = nsim_dev_reload_create(nsim_dev, extack);
- mutex_unlock(&nsim_bus_dev->nsim_bus_reload_lock);
- return ret;
+
+ return nsim_dev_reload_create(nsim_dev, extack);
}
static int nsim_dev_info_get(struct devlink *devlink,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
- return devlink_info_driver_name_put(req, DRV_NAME);
+ int err;
+
+ err = devlink_info_driver_name_put(req, DRV_NAME);
+ if (err)
+ return err;
+ err = devlink_info_version_stored_put_ext(req, "fw.mgmt", "10.20.30",
+ DEVLINK_INFO_VERSION_TYPE_COMPONENT);
+ if (err)
+ return err;
+ return devlink_info_version_running_put_ext(req, "fw.mgmt", "10.20.30",
+ DEVLINK_INFO_VERSION_TYPE_COMPONENT);
}
#define NSIM_DEV_FLASH_SIZE 500000
@@ -1325,8 +1330,7 @@ nsim_dev_devlink_trap_drop_counter_get(struct devlink *devlink,
static const struct devlink_ops nsim_dev_devlink_ops = {
.eswitch_mode_set = nsim_devlink_eswitch_mode_set,
.eswitch_mode_get = nsim_devlink_eswitch_mode_get,
- .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT |
- DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
+ .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
.reload_down = nsim_dev_reload_down,
.reload_up = nsim_dev_reload_up,
@@ -1380,8 +1384,8 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ
memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
attrs.switch_id.id_len = nsim_dev->switch_id.id_len;
devlink_port_attrs_set(devlink_port, &attrs);
- err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
- nsim_dev_port->port_index);
+ err = devl_port_register(priv_to_devlink(nsim_dev), devlink_port,
+ nsim_dev_port->port_index);
if (err)
goto err_port_free;
@@ -1396,8 +1400,8 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ
}
if (nsim_dev_port_is_vf(nsim_dev_port)) {
- err = devlink_rate_leaf_create(&nsim_dev_port->devlink_port,
- nsim_dev_port);
+ err = devl_rate_leaf_create(&nsim_dev_port->devlink_port,
+ nsim_dev_port);
if (err)
goto err_nsim_destroy;
}
@@ -1412,7 +1416,7 @@ err_nsim_destroy:
err_port_debugfs_exit:
nsim_dev_port_debugfs_exit(nsim_dev_port);
err_dl_port_unregister:
- devlink_port_unregister(devlink_port);
+ devl_port_unregister(devlink_port);
err_port_free:
kfree(nsim_dev_port);
return err;
@@ -1424,11 +1428,11 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
list_del(&nsim_dev_port->list);
if (nsim_dev_port_is_vf(nsim_dev_port))
- devlink_rate_leaf_destroy(&nsim_dev_port->devlink_port);
+ devl_rate_leaf_destroy(&nsim_dev_port->devlink_port);
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);
+ devl_port_unregister(devlink_port);
kfree(nsim_dev_port);
}
@@ -1436,11 +1440,9 @@ static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
{
struct nsim_dev_port *nsim_dev_port, *tmp;
- mutex_lock(&nsim_dev->port_list_lock);
list_for_each_entry_safe(nsim_dev_port, tmp,
&nsim_dev->port_list, list)
__nsim_dev_port_del(nsim_dev_port);
- mutex_unlock(&nsim_dev->port_list_lock);
}
static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev,
@@ -1470,7 +1472,6 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
devlink = priv_to_devlink(nsim_dev);
nsim_dev = devlink_priv(devlink);
INIT_LIST_HEAD(&nsim_dev->port_list);
- mutex_init(&nsim_dev->port_list_lock);
nsim_dev->fw_update_status = true;
nsim_dev->fw_update_overwrite_mask = 0;
@@ -1498,10 +1499,14 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
if (err)
goto err_health_exit;
- err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
+ err = nsim_dev_hwstats_init(nsim_dev);
if (err)
goto err_psample_exit;
+ err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
+ if (err)
+ goto err_hwstats_exit;
+
nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
0200,
nsim_dev->ddir,
@@ -1509,6 +1514,8 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
&nsim_dev_take_snapshot_fops);
return 0;
+err_hwstats_exit:
+ nsim_dev_hwstats_exit(nsim_dev);
err_psample_exit:
nsim_dev_psample_exit(nsim_dev);
err_health_exit:
@@ -1532,13 +1539,12 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
nsim_bus_dev->initial_net, &nsim_bus_dev->dev);
if (!devlink)
return -ENOMEM;
+ devl_lock(devlink);
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->vfs_lock);
- mutex_init(&nsim_dev->port_list_lock);
nsim_dev->fw_update_status = true;
nsim_dev->fw_update_overwrite_mask = 0;
nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
@@ -1552,7 +1558,7 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
GFP_KERNEL | __GFP_NOWARN);
if (!nsim_dev->vfconfigs) {
err = -ENOMEM;
- goto err_devlink_free;
+ goto err_devlink_unlock;
}
err = nsim_dev_resources_register(devlink);
@@ -1595,15 +1601,22 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
if (err)
goto err_bpf_dev_exit;
- err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
+ err = nsim_dev_hwstats_init(nsim_dev);
if (err)
goto err_psample_exit;
+ err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
+ if (err)
+ goto err_hwstats_exit;
+
nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY;
devlink_set_features(devlink, DEVLINK_F_RELOAD);
+ devl_unlock(devlink);
devlink_register(devlink);
return 0;
+err_hwstats_exit:
+ nsim_dev_hwstats_exit(nsim_dev);
err_psample_exit:
nsim_dev_psample_exit(nsim_dev);
err_bpf_dev_exit:
@@ -1622,10 +1635,11 @@ err_params_unregister:
devlink_params_unregister(devlink, nsim_devlink_params,
ARRAY_SIZE(nsim_devlink_params));
err_dl_unregister:
- devlink_resources_unregister(devlink, NULL);
+ devl_resources_unregister(devlink);
err_vfc_free:
kfree(nsim_dev->vfconfigs);
-err_devlink_free:
+err_devlink_unlock:
+ devl_unlock(devlink);
devlink_free(devlink);
dev_set_drvdata(&nsim_bus_dev->dev, NULL);
return err;
@@ -1639,21 +1653,19 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
return;
debugfs_remove(nsim_dev->take_snapshot);
- mutex_lock(&nsim_dev->vfs_lock);
if (nsim_dev_get_vfs(nsim_dev)) {
nsim_bus_dev_set_vfs(nsim_dev->nsim_bus_dev, 0);
if (nsim_esw_mode_is_switchdev(nsim_dev))
nsim_esw_legacy_enable(nsim_dev, NULL);
}
- mutex_unlock(&nsim_dev->vfs_lock);
nsim_dev_port_del_all(nsim_dev);
+ nsim_dev_hwstats_exit(nsim_dev);
nsim_dev_psample_exit(nsim_dev);
nsim_dev_health_exit(nsim_dev);
nsim_fib_destroy(devlink, nsim_dev->fib_data);
nsim_dev_traps_exit(devlink);
nsim_dev_dummy_region_exit(nsim_dev);
- mutex_destroy(&nsim_dev->port_list_lock);
}
void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
@@ -1662,14 +1674,16 @@ void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
struct devlink *devlink = priv_to_devlink(nsim_dev);
devlink_unregister(devlink);
+ devl_lock(devlink);
nsim_dev_reload_destroy(nsim_dev);
nsim_bpf_dev_exit(nsim_dev);
nsim_dev_debugfs_exit(nsim_dev);
devlink_params_unregister(devlink, nsim_devlink_params,
ARRAY_SIZE(nsim_devlink_params));
- devlink_resources_unregister(devlink, NULL);
+ devl_resources_unregister(devlink);
kfree(nsim_dev->vfconfigs);
+ devl_unlock(devlink);
devlink_free(devlink);
dev_set_drvdata(&nsim_bus_dev->dev, NULL);
}
@@ -1693,12 +1707,12 @@ int nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type
struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
int err;
- mutex_lock(&nsim_dev->port_list_lock);
+ devl_lock(priv_to_devlink(nsim_dev));
if (__nsim_dev_port_lookup(nsim_dev, type, port_index))
err = -EEXIST;
else
err = __nsim_dev_port_add(nsim_dev, type, port_index);
- mutex_unlock(&nsim_dev->port_list_lock);
+ devl_unlock(priv_to_devlink(nsim_dev));
return err;
}
@@ -1709,13 +1723,13 @@ int nsim_drv_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type
struct nsim_dev_port *nsim_dev_port;
int err = 0;
- mutex_lock(&nsim_dev->port_list_lock);
+ devl_lock(priv_to_devlink(nsim_dev));
nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, type, port_index);
if (!nsim_dev_port)
err = -ENOENT;
else
__nsim_dev_port_del(nsim_dev_port);
- mutex_unlock(&nsim_dev->port_list_lock);
+ devl_unlock(priv_to_devlink(nsim_dev));
return err;
}
@@ -1723,9 +1737,10 @@ int nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev,
unsigned int num_vfs)
{
struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
+ struct devlink *devlink = priv_to_devlink(nsim_dev);
int ret = 0;
- mutex_lock(&nsim_dev->vfs_lock);
+ devl_lock(devlink);
if (nsim_bus_dev->num_vfs == num_vfs)
goto exit_unlock;
if (nsim_bus_dev->num_vfs && num_vfs) {
@@ -1751,7 +1766,7 @@ int nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev,
}
exit_unlock:
- mutex_unlock(&nsim_dev->vfs_lock);
+ devl_unlock(devlink);
return ret;
}
diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c
index 0ab6a40be611..ffd9f84b6644 100644
--- a/drivers/net/netdevsim/ethtool.c
+++ b/drivers/net/netdevsim/ethtool.c
@@ -65,7 +65,9 @@ static int nsim_set_coalesce(struct net_device *dev,
}
static void nsim_get_ringparam(struct net_device *dev,
- struct ethtool_ringparam *ring)
+ struct ethtool_ringparam *ring,
+ struct kernel_ethtool_ringparam *kernel_ring,
+ struct netlink_ext_ack *extack)
{
struct netdevsim *ns = netdev_priv(dev);
@@ -73,11 +75,16 @@ static void nsim_get_ringparam(struct net_device *dev,
}
static int nsim_set_ringparam(struct net_device *dev,
- struct ethtool_ringparam *ring)
+ struct ethtool_ringparam *ring,
+ struct kernel_ethtool_ringparam *kernel_ring,
+ struct netlink_ext_ack *extack)
{
struct netdevsim *ns = netdev_priv(dev);
- memcpy(&ns->ethtool.ring, ring, sizeof(ns->ethtool.ring));
+ ns->ethtool.ring.rx_pending = ring->rx_pending;
+ ns->ethtool.ring.rx_jumbo_pending = ring->rx_jumbo_pending;
+ ns->ethtool.ring.rx_mini_pending = ring->rx_mini_pending;
+ ns->ethtool.ring.tx_pending = ring->tx_pending;
return 0;
}
diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c
index 4300261e2f9e..a1f91ff8ec56 100644
--- a/drivers/net/netdevsim/fib.c
+++ b/drivers/net/netdevsim/fib.c
@@ -22,6 +22,7 @@
#include <linux/spinlock_types.h>
#include <linux/types.h>
#include <net/fib_notifier.h>
+#include <net/inet_dscp.h>
#include <net/ip_fib.h>
#include <net/ip6_fib.h>
#include <net/fib_rules.h>
@@ -53,6 +54,7 @@ struct nsim_fib_data {
struct rhashtable nexthop_ht;
struct devlink *devlink;
struct work_struct fib_event_work;
+ struct work_struct fib_flush_work;
struct list_head fib_event_queue;
spinlock_t fib_event_queue_lock; /* Protects fib event queue list */
struct mutex nh_lock; /* Protects NH HT */
@@ -60,6 +62,7 @@ struct nsim_fib_data {
bool fail_route_offload;
bool fail_res_nexthop_group_replace;
bool fail_nexthop_bucket_replace;
+ bool fail_route_delete;
};
struct nsim_fib_rt_key {
@@ -78,7 +81,7 @@ struct nsim_fib_rt {
struct nsim_fib4_rt {
struct nsim_fib_rt common;
struct fib_info *fi;
- u8 tos;
+ dscp_t dscp;
u8 type;
};
@@ -283,7 +286,7 @@ nsim_fib4_rt_create(struct nsim_fib_data *data,
fib4_rt->fi = fen_info->fi;
fib_info_hold(fib4_rt->fi);
- fib4_rt->tos = fen_info->tos;
+ fib4_rt->dscp = fen_info->dscp;
fib4_rt->type = fen_info->type;
return fib4_rt;
@@ -322,7 +325,7 @@ nsim_fib4_rt_offload_failed_flag_set(struct net *net,
fri.tb_id = fen_info->tb_id;
fri.dst = cpu_to_be32(*p_dst);
fri.dst_len = fen_info->dst_len;
- fri.tos = fen_info->tos;
+ fri.dscp = fen_info->dscp;
fri.type = fen_info->type;
fri.offload = false;
fri.trap = false;
@@ -342,7 +345,7 @@ static void nsim_fib4_rt_hw_flags_set(struct net *net,
fri.tb_id = fib4_rt->common.key.tb_id;
fri.dst = cpu_to_be32(*p_dst);
fri.dst_len = dst_len;
- fri.tos = fib4_rt->tos;
+ fri.dscp = fib4_rt->dscp;
fri.type = fib4_rt->type;
fri.offload = false;
fri.trap = trap;
@@ -623,14 +626,14 @@ static int nsim_fib6_rt_append(struct nsim_fib_data *data,
if (err)
goto err_fib6_rt_nh_del;
- fib6_event->rt_arr[i]->trap = true;
+ WRITE_ONCE(fib6_event->rt_arr[i]->trap, true);
}
return 0;
err_fib6_rt_nh_del:
for (i--; i >= 0; i--) {
- fib6_event->rt_arr[i]->trap = false;
+ WRITE_ONCE(fib6_event->rt_arr[i]->trap, false);
nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
}
return err;
@@ -913,6 +916,10 @@ static int nsim_fib4_prepare_event(struct fib_notifier_info *info,
}
break;
case FIB_EVENT_ENTRY_DEL:
+ if (data->fail_route_delete) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion");
+ return -EINVAL;
+ }
nsim_fib_account(&data->ipv4.fib, false);
break;
}
@@ -951,6 +958,11 @@ static int nsim_fib6_prepare_event(struct fib_notifier_info *info,
}
break;
case FIB_EVENT_ENTRY_DEL:
+ if (data->fail_route_delete) {
+ err = -EINVAL;
+ NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion");
+ goto err_fib6_event_fini;
+ }
nsim_fib_account(&data->ipv6.fib, false);
break;
}
@@ -977,7 +989,7 @@ static int nsim_fib_event_schedule_work(struct nsim_fib_data *data,
fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC);
if (!fib_event)
- return NOTIFY_BAD;
+ goto err_fib_event_alloc;
fib_event->data = data;
fib_event->event = event;
@@ -1005,6 +1017,9 @@ static int nsim_fib_event_schedule_work(struct nsim_fib_data *data,
err_fib_prepare_event:
kfree(fib_event);
+err_fib_event_alloc:
+ if (event == FIB_EVENT_ENTRY_DEL)
+ schedule_work(&data->fib_flush_work);
return NOTIFY_BAD;
}
@@ -1452,7 +1467,7 @@ static void nsim_fib_set_max_all(struct nsim_fib_data *data,
int err;
u64 val;
- err = devlink_resource_size_get(devlink, res_ids[i], &val);
+ err = devl_resource_size_get(devlink, res_ids[i], &val);
if (err)
val = (u64) -1;
nsim_fib_set_max(data, res_ids[i], val);
@@ -1482,6 +1497,24 @@ static void nsim_fib_event_work(struct work_struct *work)
mutex_unlock(&data->fib_lock);
}
+static void nsim_fib_flush_work(struct work_struct *work)
+{
+ struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
+ fib_flush_work);
+ struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
+
+ /* Process pending work. */
+ flush_work(&data->fib_event_work);
+
+ mutex_lock(&data->fib_lock);
+ list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
+ rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
+ nsim_fib_rt_ht_params);
+ nsim_fib_rt_free(fib_rt, data);
+ }
+ mutex_unlock(&data->fib_lock);
+}
+
static int
nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
{
@@ -1503,6 +1536,10 @@ nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
debugfs_create_file("nexthop_bucket_activity", 0200, data->ddir,
data, &nsim_nexthop_bucket_activity_fops);
+
+ data->fail_route_delete = false;
+ debugfs_create_bool("fail_route_delete", 0600, data->ddir,
+ &data->fail_route_delete);
return 0;
}
@@ -1540,6 +1577,7 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
goto err_rhashtable_nexthop_destroy;
INIT_WORK(&data->fib_event_work, nsim_fib_event_work);
+ INIT_WORK(&data->fib_flush_work, nsim_fib_flush_work);
INIT_LIST_HEAD(&data->fib_event_queue);
spin_lock_init(&data->fib_event_queue_lock);
@@ -1561,31 +1599,32 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
goto err_nexthop_nb_unregister;
}
- devlink_resource_occ_get_register(devlink,
- NSIM_RESOURCE_IPV4_FIB,
- nsim_fib_ipv4_resource_occ_get,
- data);
- devlink_resource_occ_get_register(devlink,
- NSIM_RESOURCE_IPV4_FIB_RULES,
- nsim_fib_ipv4_rules_res_occ_get,
- data);
- devlink_resource_occ_get_register(devlink,
- NSIM_RESOURCE_IPV6_FIB,
- nsim_fib_ipv6_resource_occ_get,
- data);
- devlink_resource_occ_get_register(devlink,
- NSIM_RESOURCE_IPV6_FIB_RULES,
- nsim_fib_ipv6_rules_res_occ_get,
- data);
- devlink_resource_occ_get_register(devlink,
- NSIM_RESOURCE_NEXTHOPS,
- nsim_fib_nexthops_res_occ_get,
- data);
+ devl_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV4_FIB,
+ nsim_fib_ipv4_resource_occ_get,
+ data);
+ devl_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV4_FIB_RULES,
+ nsim_fib_ipv4_rules_res_occ_get,
+ data);
+ devl_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV6_FIB,
+ nsim_fib_ipv6_resource_occ_get,
+ data);
+ devl_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_IPV6_FIB_RULES,
+ nsim_fib_ipv6_rules_res_occ_get,
+ data);
+ devl_resource_occ_get_register(devlink,
+ NSIM_RESOURCE_NEXTHOPS,
+ nsim_fib_nexthops_res_occ_get,
+ data);
return data;
err_nexthop_nb_unregister:
unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
err_rhashtable_fib_destroy:
+ cancel_work_sync(&data->fib_flush_work);
flush_work(&data->fib_event_work);
rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
data);
@@ -1603,18 +1642,19 @@ err_data_free:
void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
{
- devlink_resource_occ_get_unregister(devlink,
- NSIM_RESOURCE_NEXTHOPS);
- devlink_resource_occ_get_unregister(devlink,
- NSIM_RESOURCE_IPV6_FIB_RULES);
- devlink_resource_occ_get_unregister(devlink,
- NSIM_RESOURCE_IPV6_FIB);
- devlink_resource_occ_get_unregister(devlink,
- NSIM_RESOURCE_IPV4_FIB_RULES);
- devlink_resource_occ_get_unregister(devlink,
- NSIM_RESOURCE_IPV4_FIB);
+ devl_resource_occ_get_unregister(devlink,
+ NSIM_RESOURCE_NEXTHOPS);
+ devl_resource_occ_get_unregister(devlink,
+ NSIM_RESOURCE_IPV6_FIB_RULES);
+ devl_resource_occ_get_unregister(devlink,
+ NSIM_RESOURCE_IPV6_FIB);
+ devl_resource_occ_get_unregister(devlink,
+ NSIM_RESOURCE_IPV4_FIB_RULES);
+ devl_resource_occ_get_unregister(devlink,
+ NSIM_RESOURCE_IPV4_FIB);
unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
+ cancel_work_sync(&data->fib_flush_work);
flush_work(&data->fib_event_work);
rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
data);
diff --git a/drivers/net/netdevsim/hwstats.c b/drivers/net/netdevsim/hwstats.c
new file mode 100644
index 000000000000..0e58aa7f0374
--- /dev/null
+++ b/drivers/net/netdevsim/hwstats.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/debugfs.h>
+
+#include "netdevsim.h"
+
+#define NSIM_DEV_HWSTATS_TRAFFIC_MS 100
+
+static struct list_head *
+nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats,
+ enum netdev_offload_xstats_type type)
+{
+ switch (type) {
+ case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
+ return &hwstats->l3_list;
+ }
+
+ WARN_ON_ONCE(1);
+ return NULL;
+}
+
+static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats,
+ enum netdev_offload_xstats_type type)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev;
+ struct list_head *hwsdev_list;
+
+ hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
+ if (WARN_ON(!hwsdev_list))
+ return;
+
+ list_for_each_entry(hwsdev, hwsdev_list, list) {
+ if (hwsdev->enabled) {
+ hwsdev->stats.rx_packets += 1;
+ hwsdev->stats.tx_packets += 2;
+ hwsdev->stats.rx_bytes += 100;
+ hwsdev->stats.tx_bytes += 300;
+ }
+ }
+}
+
+static void nsim_dev_hwstats_traffic_work(struct work_struct *work)
+{
+ struct nsim_dev_hwstats *hwstats;
+
+ hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work);
+ mutex_lock(&hwstats->hwsdev_list_lock);
+ nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+
+ schedule_delayed_work(&hwstats->traffic_dw,
+ msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
+}
+
+static struct nsim_dev_hwstats_netdev *
+nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list,
+ int ifindex)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev;
+
+ list_for_each_entry(hwsdev, hwsdev_list, list) {
+ if (hwsdev->netdev->ifindex == ifindex)
+ return hwsdev;
+ }
+
+ return NULL;
+}
+
+static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev,
+ struct netlink_ext_ack *extack)
+{
+ if (hwsdev->fail_enable) {
+ hwsdev->fail_enable = false;
+ NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail");
+ return -ECANCELED;
+ }
+
+ hwsdev->enabled = true;
+ return 0;
+}
+
+static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev)
+{
+ hwsdev->enabled = false;
+ memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
+}
+
+static int
+nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev,
+ struct netdev_notifier_offload_xstats_info *info)
+{
+ netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats);
+ memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
+ return 0;
+}
+
+static void
+nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev,
+ struct netdev_notifier_offload_xstats_info *info)
+{
+ if (hwsdev->enabled)
+ netdev_offload_xstats_report_used(info->report_used);
+}
+
+static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats,
+ struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ struct netdev_notifier_offload_xstats_info *info;
+ struct nsim_dev_hwstats_netdev *hwsdev;
+ struct list_head *hwsdev_list;
+ int err = 0;
+
+ info = ptr;
+ hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type);
+ if (!hwsdev_list)
+ return 0;
+
+ mutex_lock(&hwstats->hwsdev_list_lock);
+
+ hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
+ if (!hwsdev)
+ goto out;
+
+ switch (event) {
+ case NETDEV_OFFLOAD_XSTATS_ENABLE:
+ err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack);
+ break;
+ case NETDEV_OFFLOAD_XSTATS_DISABLE:
+ nsim_dev_hwsdev_disable(hwsdev);
+ break;
+ case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
+ nsim_dev_hwsdev_report_used(hwsdev, info);
+ break;
+ case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
+ err = nsim_dev_hwsdev_report_delta(hwsdev, info);
+ break;
+ }
+
+out:
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+ return err;
+}
+
+static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev)
+{
+ dev_put(hwsdev->netdev);
+ kfree(hwsdev);
+}
+
+static void
+__nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
+ struct net_device *dev,
+ enum netdev_offload_xstats_type type)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev;
+ struct list_head *hwsdev_list;
+
+ hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
+ if (WARN_ON(!hwsdev_list))
+ return;
+
+ hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
+ if (!hwsdev)
+ return;
+
+ list_del(&hwsdev->list);
+ nsim_dev_hwsdev_fini(hwsdev);
+}
+
+static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
+ struct net_device *dev)
+{
+ mutex_lock(&hwstats->hwsdev_list_lock);
+ __nsim_dev_hwstats_event_unregister(hwstats, dev,
+ NETDEV_OFFLOAD_XSTATS_TYPE_L3);
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+}
+
+static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats,
+ struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ switch (event) {
+ case NETDEV_OFFLOAD_XSTATS_ENABLE:
+ case NETDEV_OFFLOAD_XSTATS_DISABLE:
+ case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
+ case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
+ return nsim_dev_hwstats_event_off_xstats(hwstats, dev,
+ event, ptr);
+ case NETDEV_UNREGISTER:
+ nsim_dev_hwstats_event_unregister(hwstats, dev);
+ break;
+ }
+
+ return 0;
+}
+
+static int nsim_dev_netdevice_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct nsim_dev_hwstats *hwstats;
+ int err = 0;
+
+ hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb);
+ err = nsim_dev_hwstats_event(hwstats, dev, event, ptr);
+ if (err)
+ return notifier_from_errno(err);
+
+ return NOTIFY_OK;
+}
+
+static int
+nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats,
+ int ifindex,
+ enum netdev_offload_xstats_type type,
+ struct list_head *hwsdev_list)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev;
+ struct nsim_dev *nsim_dev;
+ struct net_device *netdev;
+ bool notify = false;
+ struct net *net;
+ int err = 0;
+
+ nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
+ net = nsim_dev_net(nsim_dev);
+
+ rtnl_lock();
+ mutex_lock(&hwstats->hwsdev_list_lock);
+ hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
+ if (hwsdev)
+ goto out_unlock_list;
+
+ netdev = dev_get_by_index(net, ifindex);
+ if (!netdev) {
+ err = -ENODEV;
+ goto out_unlock_list;
+ }
+
+ hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL);
+ if (!hwsdev) {
+ err = -ENOMEM;
+ goto out_put_netdev;
+ }
+
+ hwsdev->netdev = netdev;
+ list_add_tail(&hwsdev->list, hwsdev_list);
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+
+ if (netdev_offload_xstats_enabled(netdev, type)) {
+ nsim_dev_hwsdev_enable(hwsdev, NULL);
+ notify = true;
+ }
+
+ if (notify)
+ rtnl_offload_xstats_notify(netdev);
+ rtnl_unlock();
+ return err;
+
+out_put_netdev:
+ dev_put(netdev);
+out_unlock_list:
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+ rtnl_unlock();
+ return err;
+}
+
+static int
+nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
+ int ifindex,
+ enum netdev_offload_xstats_type type,
+ struct list_head *hwsdev_list)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev;
+ int err = 0;
+
+ rtnl_lock();
+ mutex_lock(&hwstats->hwsdev_list_lock);
+ hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
+ if (hwsdev)
+ list_del(&hwsdev->list);
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+
+ if (!hwsdev) {
+ err = -ENOENT;
+ goto unlock_out;
+ }
+
+ if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
+ netdev_offload_xstats_push_delta(hwsdev->netdev, type,
+ &hwsdev->stats);
+ rtnl_offload_xstats_notify(hwsdev->netdev);
+ }
+ nsim_dev_hwsdev_fini(hwsdev);
+
+unlock_out:
+ rtnl_unlock();
+ return err;
+}
+
+static int
+nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
+ int ifindex,
+ enum netdev_offload_xstats_type type,
+ struct list_head *hwsdev_list)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev;
+ int err = 0;
+
+ mutex_lock(&hwstats->hwsdev_list_lock);
+
+ hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
+ if (!hwsdev) {
+ err = -ENOENT;
+ goto err_hwsdev_list_unlock;
+ }
+
+ hwsdev->fail_enable = true;
+
+err_hwsdev_list_unlock:
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+ return err;
+}
+
+enum nsim_dev_hwstats_do {
+ NSIM_DEV_HWSTATS_DO_DISABLE,
+ NSIM_DEV_HWSTATS_DO_ENABLE,
+ NSIM_DEV_HWSTATS_DO_FAIL,
+};
+
+struct nsim_dev_hwstats_fops {
+ const struct file_operations fops;
+ enum nsim_dev_hwstats_do action;
+ enum netdev_offload_xstats_type type;
+};
+
+static ssize_t
+nsim_dev_hwstats_do_write(struct file *file,
+ const char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct nsim_dev_hwstats *hwstats = file->private_data;
+ struct nsim_dev_hwstats_fops *hwsfops;
+ struct list_head *hwsdev_list;
+ int ifindex;
+ int err;
+
+ hwsfops = container_of(debugfs_real_fops(file),
+ struct nsim_dev_hwstats_fops, fops);
+
+ err = kstrtoint_from_user(data, count, 0, &ifindex);
+ if (err)
+ return err;
+
+ hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
+ if (WARN_ON(!hwsdev_list))
+ return -EINVAL;
+
+ switch (hwsfops->action) {
+ case NSIM_DEV_HWSTATS_DO_DISABLE:
+ err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
+ hwsfops->type,
+ hwsdev_list);
+ break;
+ case NSIM_DEV_HWSTATS_DO_ENABLE:
+ err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
+ hwsfops->type,
+ hwsdev_list);
+ break;
+ case NSIM_DEV_HWSTATS_DO_FAIL:
+ err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
+ hwsfops->type,
+ hwsdev_list);
+ break;
+ }
+ if (err)
+ return err;
+
+ return count;
+}
+
+#define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \
+ { \
+ .fops = { \
+ .open = simple_open, \
+ .write = nsim_dev_hwstats_do_write, \
+ .llseek = generic_file_llseek, \
+ .owner = THIS_MODULE, \
+ }, \
+ .action = ACTION, \
+ .type = TYPE, \
+ }
+
+static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
+ NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
+ NETDEV_OFFLOAD_XSTATS_TYPE_L3);
+
+static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
+ NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
+ NETDEV_OFFLOAD_XSTATS_TYPE_L3);
+
+static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
+ NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
+ NETDEV_OFFLOAD_XSTATS_TYPE_L3);
+
+#undef NSIM_DEV_HWSTATS_FOPS
+
+int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
+{
+ struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
+ struct net *net = nsim_dev_net(nsim_dev);
+ int err;
+
+ mutex_init(&hwstats->hwsdev_list_lock);
+ INIT_LIST_HEAD(&hwstats->l3_list);
+
+ hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
+ err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
+ if (err)
+ goto err_mutex_destroy;
+
+ hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
+ if (IS_ERR(hwstats->ddir)) {
+ err = PTR_ERR(hwstats->ddir);
+ goto err_unregister_notifier;
+ }
+
+ hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
+ if (IS_ERR(hwstats->l3_ddir)) {
+ err = PTR_ERR(hwstats->l3_ddir);
+ goto err_remove_hwstats_recursive;
+ }
+
+ debugfs_create_file("enable_ifindex", 0200, hwstats->l3_ddir, hwstats,
+ &nsim_dev_hwstats_l3_enable_fops.fops);
+ debugfs_create_file("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,
+ &nsim_dev_hwstats_l3_disable_fops.fops);
+ debugfs_create_file("fail_next_enable", 0200, hwstats->l3_ddir, hwstats,
+ &nsim_dev_hwstats_l3_fail_fops.fops);
+
+ INIT_DELAYED_WORK(&hwstats->traffic_dw,
+ &nsim_dev_hwstats_traffic_work);
+ schedule_delayed_work(&hwstats->traffic_dw,
+ msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
+ return 0;
+
+err_remove_hwstats_recursive:
+ debugfs_remove_recursive(hwstats->ddir);
+err_unregister_notifier:
+ unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
+err_mutex_destroy:
+ mutex_destroy(&hwstats->hwsdev_list_lock);
+ return err;
+}
+
+static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
+ enum netdev_offload_xstats_type type)
+{
+ struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
+ struct list_head *hwsdev_list;
+
+ hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
+ if (WARN_ON(!hwsdev_list))
+ return;
+
+ mutex_lock(&hwstats->hwsdev_list_lock);
+ list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
+ list_del(&hwsdev->list);
+ nsim_dev_hwsdev_fini(hwsdev);
+ }
+ mutex_unlock(&hwstats->hwsdev_list_lock);
+}
+
+void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
+{
+ struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
+ struct net *net = nsim_dev_net(nsim_dev);
+
+ cancel_delayed_work_sync(&hwstats->traffic_dw);
+ debugfs_remove_recursive(hwstats->ddir);
+ unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
+ nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
+ mutex_destroy(&hwstats->hwsdev_list_lock);
+}
diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c
index b80ed2ffd45e..386336a38f34 100644
--- a/drivers/net/netdevsim/ipsec.c
+++ b/drivers/net/netdevsim/ipsec.c
@@ -171,7 +171,7 @@ static int nsim_ipsec_add_sa(struct xfrm_state *xs)
return ret;
}
- if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) {
+ if (xs->xso.dir == XFRM_DEV_OFFLOAD_IN) {
sa.rx = true;
if (xs->props.family == AF_INET6)
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index e470e3398abc..9a1a5b203624 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -67,10 +67,10 @@ nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
unsigned int start;
do {
- start = u64_stats_fetch_begin(&ns->syncp);
+ start = u64_stats_fetch_begin_irq(&ns->syncp);
stats->tx_bytes = ns->tx_bytes;
stats->tx_packets = ns->tx_packets;
- } while (u64_stats_fetch_retry(&ns->syncp, start));
+ } while (u64_stats_fetch_retry_irq(&ns->syncp, start));
}
static int
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index c49771f27f17..7d8ed8d8df5c 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -184,6 +184,28 @@ struct nsim_dev_health {
int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink);
void nsim_dev_health_exit(struct nsim_dev *nsim_dev);
+struct nsim_dev_hwstats_netdev {
+ struct list_head list;
+ struct net_device *netdev;
+ struct rtnl_hw_stats64 stats;
+ bool enabled;
+ bool fail_enable;
+};
+
+struct nsim_dev_hwstats {
+ struct dentry *ddir;
+ struct dentry *l3_ddir;
+
+ struct mutex hwsdev_list_lock; /* protects hwsdev list(s) */
+ struct list_head l3_list;
+
+ struct notifier_block netdevice_nb;
+ struct delayed_work traffic_dw;
+};
+
+int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev);
+void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev);
+
#if IS_ENABLED(CONFIG_PSAMPLE)
int nsim_dev_psample_init(struct nsim_dev *nsim_dev);
void nsim_dev_psample_exit(struct nsim_dev *nsim_dev);
@@ -239,7 +261,6 @@ struct nsim_dev {
struct dentry *take_snapshot;
struct dentry *nodes_ddir;
- struct mutex vfs_lock; /* Protects vfconfigs */
struct nsim_vf_config *vfconfigs;
struct bpf_offload_dev *bpf_dev;
@@ -252,7 +273,6 @@ struct nsim_dev {
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 */
bool fw_update_status;
u32 fw_update_overwrite_mask;
u32 max_macs;
@@ -261,6 +281,7 @@ struct nsim_dev {
bool fail_reload;
struct devlink_region *dummy_region;
struct nsim_dev_health health;
+ struct nsim_dev_hwstats hwstats;
struct flow_action_cookie *fa_cookie;
spinlock_t fa_cookie_lock; /* protects fa_cookie */
bool fail_trap_group_set;
@@ -355,9 +376,6 @@ struct nsim_bus_dev {
*/
unsigned int max_vfs;
unsigned int num_vfs;
- /* Lock for devlink->reload_enabled in netdevsim module */
- struct mutex nsim_bus_reload_lock;
- bool in_reload;
bool init;
};