diff options
Diffstat (limited to 'drivers/net/netdevsim')
| -rw-r--r-- | drivers/net/netdevsim/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/netdevsim/bus.c | 1 | ||||
| -rw-r--r-- | drivers/net/netdevsim/dev.c | 395 | ||||
| -rw-r--r-- | drivers/net/netdevsim/fib.c | 176 | ||||
| -rw-r--r-- | drivers/net/netdevsim/health.c | 319 | ||||
| -rw-r--r-- | drivers/net/netdevsim/netdev.c | 10 | ||||
| -rw-r--r-- | drivers/net/netdevsim/netdevsim.h | 33 | 
7 files changed, 670 insertions, 266 deletions
diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile index 09f1315d2f2a..f4d8f62f28c2 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 fib.o bus.o +	netdev.o dev.o fib.o bus.o health.o  ifeq ($(CONFIG_BPF_SYSCALL),y)  netdevsim-objs += \ diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c index 1a0ff3d7747b..6aeed0c600f8 100644 --- a/drivers/net/netdevsim/bus.c +++ b/drivers/net/netdevsim/bus.c @@ -283,6 +283,7 @@ nsim_bus_dev_new(unsigned int id, unsigned int port_count)  	nsim_bus_dev->dev.bus = &nsim_bus;  	nsim_bus_dev->dev.type = &nsim_bus_dev_type;  	nsim_bus_dev->port_count = port_count; +	nsim_bus_dev->initial_net = current->nsproxy->net_ns;  	err = device_register(&nsim_bus_dev->dev);  	if (err) diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 44c2d857a7fa..059711edfc61 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -90,6 +90,10 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)  			    &nsim_dev->test1);  	debugfs_create_file("take_snapshot", 0200, nsim_dev->ddir, nsim_dev,  			    &nsim_dev_take_snapshot_fops); +	debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, +			    &nsim_dev->dont_allow_reload); +	debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, +			    &nsim_dev->fail_reload);  	return 0;  } @@ -123,39 +127,6 @@ static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)  	debugfs_remove_recursive(nsim_dev_port->ddir);  } -static struct net *nsim_devlink_net(struct devlink *devlink) -{ -	return &init_net; -} - -static u64 nsim_dev_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_dev_ipv4_fib_rules_res_occ_get(void *priv) -{ -	struct net *net = priv; - -	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false); -} - -static u64 nsim_dev_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_dev_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 nsim_dev_resources_register(struct devlink *devlink)  {  	struct devlink_resource_size_params params = { @@ -163,9 +134,7 @@ static int nsim_dev_resources_register(struct devlink *devlink)  		.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, @@ -177,8 +146,7 @@ static int nsim_dev_resources_register(struct devlink *devlink)  		goto out;  	} -	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true); -	err = devlink_resource_register(devlink, "fib", n, +	err = devlink_resource_register(devlink, "fib", (u64)-1,  					NSIM_RESOURCE_IPV4_FIB,  					NSIM_RESOURCE_IPV4, ¶ms);  	if (err) { @@ -186,8 +154,7 @@ static int nsim_dev_resources_register(struct devlink *devlink)  		return err;  	} -	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true); -	err = devlink_resource_register(devlink, "fib-rules", n, +	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,  					NSIM_RESOURCE_IPV4_FIB_RULES,  					NSIM_RESOURCE_IPV4, ¶ms);  	if (err) { @@ -205,8 +172,7 @@ static int nsim_dev_resources_register(struct devlink *devlink)  		goto out;  	} -	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true); -	err = devlink_resource_register(devlink, "fib", n, +	err = devlink_resource_register(devlink, "fib", (u64)-1,  					NSIM_RESOURCE_IPV6_FIB,  					NSIM_RESOURCE_IPV6, ¶ms);  	if (err) { @@ -214,8 +180,7 @@ static int nsim_dev_resources_register(struct devlink *devlink)  		return err;  	} -	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true); -	err = devlink_resource_register(devlink, "fib-rules", n, +	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,  					NSIM_RESOURCE_IPV6_FIB_RULES,  					NSIM_RESOURCE_IPV6, ¶ms);  	if (err) { @@ -223,22 +188,6 @@ static int nsim_dev_resources_register(struct devlink *devlink)  		return err;  	} -	devlink_resource_occ_get_register(devlink, -					  NSIM_RESOURCE_IPV4_FIB, -					  nsim_dev_ipv4_fib_resource_occ_get, -					  net); -	devlink_resource_occ_get_register(devlink, -					  NSIM_RESOURCE_IPV4_FIB_RULES, -					  nsim_dev_ipv4_fib_rules_res_occ_get, -					  net); -	devlink_resource_occ_get_register(devlink, -					  NSIM_RESOURCE_IPV6_FIB, -					  nsim_dev_ipv6_fib_resource_occ_get, -					  net); -	devlink_resource_occ_get_register(devlink, -					  NSIM_RESOURCE_IPV6_FIB_RULES, -					  nsim_dev_ipv6_fib_rules_res_occ_get, -					  net);  out:  	return err;  } @@ -524,36 +473,48 @@ static void nsim_dev_traps_exit(struct devlink *devlink)  	kfree(nsim_dev->trap_data);  } -static int nsim_dev_reload_down(struct devlink *devlink, +static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, +				  struct netlink_ext_ack *extack); +static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev); + +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); + +	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"); +		return -EOPNOTSUPP; +	} + +	nsim_dev_reload_destroy(nsim_dev);  	return 0;  }  static int nsim_dev_reload_up(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; +	struct nsim_dev *nsim_dev = devlink_priv(devlink); -		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; -		} +	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"); +		return -EINVAL;  	} -	nsim_devlink_param_load_driverinit_values(devlink); -	return 0; +	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);  }  #define NSIM_DEV_FLASH_SIZE 500000 @@ -649,6 +610,7 @@ nsim_dev_devlink_trap_action_set(struct devlink *devlink,  static const struct devlink_ops nsim_dev_devlink_ops = {  	.reload_down = nsim_dev_reload_down,  	.reload_up = nsim_dev_reload_up, +	.info_get = nsim_dev_info_get,  	.flash_update = nsim_dev_flash_update,  	.trap_init = nsim_dev_devlink_trap_init,  	.trap_action_set = nsim_dev_devlink_trap_action_set, @@ -657,95 +619,6 @@ static const struct devlink_ops nsim_dev_devlink_ops = {  #define NSIM_DEV_MAX_MACS_DEFAULT 32  #define NSIM_DEV_TEST1_DEFAULT true -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->fw_update_status = true; -	nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; -	nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; - -	err = nsim_dev_resources_register(devlink); -	if (err) -		goto err_devlink_free; - -	err = devlink_register(devlink, &nsim_bus_dev->dev); -	if (err) -		goto err_resources_unregister; - -	err = devlink_params_register(devlink, nsim_devlink_params, -				      ARRAY_SIZE(nsim_devlink_params)); -	if (err) -		goto err_dl_unregister; -	nsim_devlink_set_params_init_values(nsim_dev, devlink); - -	err = nsim_dev_dummy_region_init(nsim_dev, devlink); -	if (err) -		goto err_params_unregister; - -	err = nsim_dev_traps_init(devlink); -	if (err) -		goto err_dummy_region_exit; - -	err = nsim_dev_debugfs_init(nsim_dev); -	if (err) -		goto err_traps_exit; - -	err = nsim_bpf_dev_init(nsim_dev); -	if (err) -		goto err_debugfs_exit; - -	devlink_params_publish(devlink); -	devlink_reload_enable(devlink); -	return nsim_dev; - -err_debugfs_exit: -	nsim_dev_debugfs_exit(nsim_dev); -err_traps_exit: -	nsim_dev_traps_exit(devlink); -err_dummy_region_exit: -	nsim_dev_dummy_region_exit(nsim_dev); -err_params_unregister: -	devlink_params_unregister(devlink, nsim_devlink_params, -				  ARRAY_SIZE(nsim_devlink_params)); -err_dl_unregister: -	devlink_unregister(devlink); -err_resources_unregister: -	devlink_resources_unregister(devlink, NULL); -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); - -	devlink_reload_disable(devlink); -	nsim_bpf_dev_exit(nsim_dev); -	nsim_dev_debugfs_exit(nsim_dev); -	nsim_dev_traps_exit(devlink); -	nsim_dev_dummy_region_exit(nsim_dev); -	devlink_params_unregister(devlink, nsim_devlink_params, -				  ARRAY_SIZE(nsim_devlink_params)); -	devlink_unregister(devlink); -	devlink_resources_unregister(devlink, NULL); -	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)  { @@ -815,39 +688,195 @@ static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)  	mutex_unlock(&nsim_dev->port_list_lock);  } -int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) +static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, +				 unsigned int port_count)  { -	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); +	int i, err; -	mutex_lock(&nsim_dev->port_list_lock); -	for (i = 0; i < nsim_bus_dev->port_count; i++) { +	for (i = 0; i < port_count; i++) {  		err = __nsim_dev_port_add(nsim_dev, i);  		if (err)  			goto err_port_del_all;  	} -	mutex_unlock(&nsim_dev->port_list_lock);  	return 0;  err_port_del_all: -	mutex_unlock(&nsim_dev->port_list_lock);  	nsim_dev_port_del_all(nsim_dev); -	nsim_dev_destroy(nsim_dev);  	return err;  } +static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, +				  struct netlink_ext_ack *extack) +{ +	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; +	struct devlink *devlink; +	int err; + +	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->fib_data = nsim_fib_create(devlink, extack); +	if (IS_ERR(nsim_dev->fib_data)) +		return PTR_ERR(nsim_dev->fib_data); + +	nsim_devlink_param_load_driverinit_values(devlink); + +	err = nsim_dev_dummy_region_init(nsim_dev, devlink); +	if (err) +		goto err_fib_destroy; + +	err = nsim_dev_traps_init(devlink); +	if (err) +		goto err_dummy_region_exit; + +	err = nsim_dev_health_init(nsim_dev, devlink); +	if (err) +		goto err_traps_exit; + +	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); +	if (err) +		goto err_health_exit; + +	return 0; + +err_health_exit: +	nsim_dev_health_exit(nsim_dev); +err_traps_exit: +	nsim_dev_traps_exit(devlink); +err_dummy_region_exit: +	nsim_dev_dummy_region_exit(nsim_dev); +err_fib_destroy: +	nsim_fib_destroy(devlink, nsim_dev->fib_data); +	return err; +} + +int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) +{ +	struct nsim_dev *nsim_dev; +	struct devlink *devlink; +	int err; + +	devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev)); +	if (!devlink) +		return -ENOMEM; +	devlink_net_set(devlink, nsim_bus_dev->initial_net); +	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->fw_update_status = true; +	nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; +	nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; + +	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); + +	err = nsim_dev_resources_register(devlink); +	if (err) +		goto err_devlink_free; + +	nsim_dev->fib_data = nsim_fib_create(devlink, NULL); +	if (IS_ERR(nsim_dev->fib_data)) { +		err = PTR_ERR(nsim_dev->fib_data); +		goto err_resources_unregister; +	} + +	err = devlink_register(devlink, &nsim_bus_dev->dev); +	if (err) +		goto err_fib_destroy; + +	err = devlink_params_register(devlink, nsim_devlink_params, +				      ARRAY_SIZE(nsim_devlink_params)); +	if (err) +		goto err_dl_unregister; +	nsim_devlink_set_params_init_values(nsim_dev, devlink); + +	err = nsim_dev_dummy_region_init(nsim_dev, devlink); +	if (err) +		goto err_params_unregister; + +	err = nsim_dev_traps_init(devlink); +	if (err) +		goto err_dummy_region_exit; + +	err = nsim_dev_debugfs_init(nsim_dev); +	if (err) +		goto err_traps_exit; + +	err = nsim_dev_health_init(nsim_dev, devlink); +	if (err) +		goto err_debugfs_exit; + +	err = nsim_bpf_dev_init(nsim_dev); +	if (err) +		goto err_health_exit; + +	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); +	if (err) +		goto err_bpf_dev_exit; + +	devlink_params_publish(devlink); +	devlink_reload_enable(devlink); +	return 0; + +err_bpf_dev_exit: +	nsim_bpf_dev_exit(nsim_dev); +err_health_exit: +	nsim_dev_health_exit(nsim_dev); +err_debugfs_exit: +	nsim_dev_debugfs_exit(nsim_dev); +err_traps_exit: +	nsim_dev_traps_exit(devlink); +err_dummy_region_exit: +	nsim_dev_dummy_region_exit(nsim_dev); +err_params_unregister: +	devlink_params_unregister(devlink, nsim_devlink_params, +				  ARRAY_SIZE(nsim_devlink_params)); +err_dl_unregister: +	devlink_unregister(devlink); +err_fib_destroy: +	nsim_fib_destroy(devlink, nsim_dev->fib_data); +err_resources_unregister: +	devlink_resources_unregister(devlink, NULL); +err_devlink_free: +	devlink_free(devlink); +	return err; +} + +static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) +{ +	struct devlink *devlink = priv_to_devlink(nsim_dev); + +	if (devlink_is_reload_failed(devlink)) +		return; +	nsim_dev_port_del_all(nsim_dev); +	nsim_dev_health_exit(nsim_dev); +	nsim_dev_traps_exit(devlink); +	nsim_dev_dummy_region_exit(nsim_dev); +	mutex_destroy(&nsim_dev->port_list_lock); +	nsim_fib_destroy(devlink, nsim_dev->fib_data); +} +  void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)  {  	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); +	struct devlink *devlink = priv_to_devlink(nsim_dev); -	nsim_dev_port_del_all(nsim_dev); -	nsim_dev_destroy(nsim_dev); +	devlink_reload_disable(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_unregister(devlink); +	devlink_resources_unregister(devlink, NULL); +	devlink_free(devlink);  }  static struct nsim_dev_port * diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 1a251f76d09b..13540dee7364 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -18,7 +18,7 @@  #include <net/ip_fib.h>  #include <net/ip6_fib.h>  #include <net/fib_rules.h> -#include <net/netns/generic.h> +#include <net/net_namespace.h>  #include "netdevsim.h" @@ -33,15 +33,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,12 +63,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, -		     struct netlink_ext_ack *extack) +static void nsim_fib_set_max(struct nsim_fib_data *fib_data, +			     enum nsim_resource_id res_id, u64 val)  { -	struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);  	struct nsim_fib_entry *entry; -	int err = 0;  	switch (res_id) {  	case NSIM_RESOURCE_IPV4_FIB: @@ -85,20 +82,10 @@ int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,  		entry = &fib_data->ipv6.rules;  		break;  	default: -		return 0; -	} - -	/* not allowing a new max to be less than curren occupancy -	 * --> no means of evicting entries -	 */ -	if (val < entry->num) { -		NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy"); -		err = -EINVAL; -	} else { -		entry->max = val; +		WARN_ON(1); +		return;  	} - -	return err; +	entry->max = val;  }  static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add, @@ -120,9 +107,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 +144,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 +165,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,69 +190,116 @@ static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,  /* inconsistent dump, trying again */  static void nsim_fib_dump_inconsistent(struct notifier_block *nb)  { -	struct nsim_fib_data *data; -	struct net *net; +	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, +						  fib_nb); -	rcu_read_lock(); -	for_each_net_rcu(net) { -		data = net_generic(net, nsim_fib_net_id); +	data->ipv4.fib.num = 0ULL; +	data->ipv4.rules.num = 0ULL; +	data->ipv6.fib.num = 0ULL; +	data->ipv6.rules.num = 0ULL; +} -		data->ipv4.fib.num = 0ULL; -		data->ipv4.rules.num = 0ULL; +static u64 nsim_fib_ipv4_resource_occ_get(void *priv) +{ +	struct nsim_fib_data *data = priv; -		data->ipv6.fib.num = 0ULL; -		data->ipv6.rules.num = 0ULL; -	} -	rcu_read_unlock(); +	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);  } -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) +static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)  { -	struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id); +	struct nsim_fib_data *data = priv; -	data->ipv4.fib.max = (u64)-1; -	data->ipv4.rules.max = (u64)-1; +	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false); +} -	data->ipv6.fib.max = (u64)-1; -	data->ipv6.rules.max = (u64)-1; +static u64 nsim_fib_ipv6_resource_occ_get(void *priv) +{ +	struct nsim_fib_data *data = priv; -	return 0; +	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);  } -static struct pernet_operations nsim_fib_net_ops = { -	.init = nsim_fib_netns_init, -	.id   = &nsim_fib_net_id, -	.size = sizeof(struct nsim_fib_data), -}; +static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv) +{ +	struct nsim_fib_data *data = priv; + +	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false); +} -void nsim_fib_exit(void) +static void nsim_fib_set_max_all(struct nsim_fib_data *data, +				 struct devlink *devlink)  { -	unregister_fib_notifier(&nsim_fib_nb); -	unregister_pernet_subsys(&nsim_fib_net_ops); +	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) +			val = (u64) -1; +		nsim_fib_set_max(data, res_ids[i], val); +	}  } -int nsim_fib_init(void) +struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, +				      struct netlink_ext_ack *extack)  { +	struct nsim_fib_data *data;  	int err; -	err = register_pernet_subsys(&nsim_fib_net_ops); -	if (err < 0) { -		pr_err("Failed to register pernet subsystem\n"); -		goto err_out; -	} +	data = kzalloc(sizeof(*data), GFP_KERNEL); +	if (!data) +		return ERR_PTR(-ENOMEM); + +	nsim_fib_set_max_all(data, devlink); -	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(devlink_net(devlink), &data->fib_nb, +				    nsim_fib_dump_inconsistent, extack); +	if (err) {  		pr_err("Failed to register fib notifier\n"); -		unregister_pernet_subsys(&nsim_fib_net_ops);  		goto err_out;  	} +	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); +	return data; +  err_out: -	return err; +	kfree(data); +	return ERR_PTR(err); +} + +void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) +{ +	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); +	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); +	kfree(data);  } diff --git a/drivers/net/netdevsim/health.c b/drivers/net/netdevsim/health.c new file mode 100644 index 000000000000..9aa637d162eb --- /dev/null +++ b/drivers/net/netdevsim/health.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ + +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "netdevsim.h" + +static int +nsim_dev_empty_reporter_dump(struct devlink_health_reporter *reporter, +			     struct devlink_fmsg *fmsg, void *priv_ctx, +			     struct netlink_ext_ack *extack) +{ +	return 0; +} + +static int +nsim_dev_empty_reporter_diagnose(struct devlink_health_reporter *reporter, +				 struct devlink_fmsg *fmsg, +				 struct netlink_ext_ack *extack) +{ +	return 0; +} + +static const +struct devlink_health_reporter_ops nsim_dev_empty_reporter_ops = { +	.name = "empty", +	.dump = nsim_dev_empty_reporter_dump, +	.diagnose = nsim_dev_empty_reporter_diagnose, +}; + +struct nsim_dev_dummy_reporter_ctx { +	char *break_msg; +}; + +static int +nsim_dev_dummy_reporter_recover(struct devlink_health_reporter *reporter, +				void *priv_ctx, +				struct netlink_ext_ack *extack) +{ +	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter); +	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx; + +	if (health->fail_recover) { +		/* For testing purposes, user set debugfs fail_recover +		 * value to true. Fail right away. +		 */ +		NL_SET_ERR_MSG_MOD(extack, "User setup the recover to fail for testing purposes"); +		return -EINVAL; +	} +	if (ctx) { +		kfree(health->recovered_break_msg); +		health->recovered_break_msg = kstrdup(ctx->break_msg, +						      GFP_KERNEL); +		if (!health->recovered_break_msg) +			return -ENOMEM; +	} +	return 0; +} + +static int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len) +{ +	char *binary; +	int err; +	int i; + +	err = devlink_fmsg_bool_pair_put(fmsg, "test_bool", true); +	if (err) +		return err; +	err = devlink_fmsg_u8_pair_put(fmsg, "test_u8", 1); +	if (err) +		return err; +	err = devlink_fmsg_u32_pair_put(fmsg, "test_u32", 3); +	if (err) +		return err; +	err = devlink_fmsg_u64_pair_put(fmsg, "test_u64", 4); +	if (err) +		return err; +	err = devlink_fmsg_string_pair_put(fmsg, "test_string", "somestring"); +	if (err) +		return err; + +	binary = kmalloc(binary_len, GFP_KERNEL); +	if (!binary) +		return -ENOMEM; +	get_random_bytes(binary, binary_len); +	err = devlink_fmsg_binary_pair_put(fmsg, "test_binary", binary, binary_len); +	kfree(binary); +	if (err) +		return err; + +	err = devlink_fmsg_pair_nest_start(fmsg, "test_nest"); +	if (err) +		return err; +	err = devlink_fmsg_obj_nest_start(fmsg); +	if (err) +		return err; +	err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false); +	if (err) +		return err; +	err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false); +	if (err) +		return err; +	err = devlink_fmsg_obj_nest_end(fmsg); +	if (err) +		return err; +	err = devlink_fmsg_pair_nest_end(fmsg); +	if (err) +		return err; + +	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_bool_array"); +	if (err) +		return err; +	for (i = 0; i < 10; i++) { +		err = devlink_fmsg_bool_put(fmsg, true); +		if (err) +			return err; +	} +	err = devlink_fmsg_arr_pair_nest_end(fmsg); +	if (err) +		return err; + +	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u8_array"); +	if (err) +		return err; +	for (i = 0; i < 10; i++) { +		err = devlink_fmsg_u8_put(fmsg, i); +		if (err) +			return err; +	} +	err = devlink_fmsg_arr_pair_nest_end(fmsg); +	if (err) +		return err; + +	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array"); +	if (err) +		return err; +	for (i = 0; i < 10; i++) { +		err = devlink_fmsg_u32_put(fmsg, i); +		if (err) +			return err; +	} +	err = devlink_fmsg_arr_pair_nest_end(fmsg); +	if (err) +		return err; + +	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u64_array"); +	if (err) +		return err; +	for (i = 0; i < 10; i++) { +		err = devlink_fmsg_u64_put(fmsg, i); +		if (err) +			return err; +	} +	err = devlink_fmsg_arr_pair_nest_end(fmsg); +	if (err) +		return err; + +	err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects"); +	if (err) +		return err; +	for (i = 0; i < 10; i++) { +		err = devlink_fmsg_obj_nest_start(fmsg); +		if (err) +			return err; +		err = devlink_fmsg_bool_pair_put(fmsg, +						 "in_array_nested_test_bool", +						 false); +		if (err) +			return err; +		err = devlink_fmsg_u8_pair_put(fmsg, +					       "in_array_nested_test_u8", +					       i); +		if (err) +			return err; +		err = devlink_fmsg_obj_nest_end(fmsg); +		if (err) +			return err; +	} +	return devlink_fmsg_arr_pair_nest_end(fmsg); +} + +static int +nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter, +			     struct devlink_fmsg *fmsg, void *priv_ctx, +			     struct netlink_ext_ack *extack) +{ +	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter); +	struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx; +	int err; + +	if (ctx) { +		err = devlink_fmsg_string_pair_put(fmsg, "break_message", +						   ctx->break_msg); +		if (err) +			return err; +	} +	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len); +} + +static int +nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter, +				 struct devlink_fmsg *fmsg, +				 struct netlink_ext_ack *extack) +{ +	struct nsim_dev_health *health = devlink_health_reporter_priv(reporter); +	int err; + +	if (health->recovered_break_msg) { +		err = devlink_fmsg_string_pair_put(fmsg, +						   "recovered_break_message", +						   health->recovered_break_msg); +		if (err) +			return err; +	} +	return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len); +} + +static const +struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = { +	.name = "dummy", +	.recover = nsim_dev_dummy_reporter_recover, +	.dump = nsim_dev_dummy_reporter_dump, +	.diagnose = nsim_dev_dummy_reporter_diagnose, +}; + +static ssize_t nsim_dev_health_break_write(struct file *file, +					   const char __user *data, +					   size_t count, loff_t *ppos) +{ +	struct nsim_dev_health *health = file->private_data; +	struct nsim_dev_dummy_reporter_ctx ctx; +	char *break_msg; +	int err; + +	break_msg = kmalloc(count + 1, GFP_KERNEL); +	if (!break_msg) +		return -ENOMEM; + +	if (copy_from_user(break_msg, data, count)) { +		err = -EFAULT; +		goto out; +	} +	break_msg[count] = '\0'; +	if (break_msg[count - 1] == '\n') +		break_msg[count - 1] = '\0'; + +	ctx.break_msg = break_msg; +	err = devlink_health_report(health->dummy_reporter, break_msg, &ctx); +	if (err) +		goto out; + +out: +	kfree(break_msg); +	return err ?: count; +} + +static const struct file_operations nsim_dev_health_break_fops = { +	.open = simple_open, +	.write = nsim_dev_health_break_write, +	.llseek = generic_file_llseek, +}; + +int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink) +{ +	struct nsim_dev_health *health = &nsim_dev->health; +	int err; + +	health->empty_reporter = +		devlink_health_reporter_create(devlink, +					       &nsim_dev_empty_reporter_ops, +					       0, false, health); +	if (IS_ERR(health->empty_reporter)) +		return PTR_ERR(health->empty_reporter); + +	health->dummy_reporter = +		devlink_health_reporter_create(devlink, +					       &nsim_dev_dummy_reporter_ops, +					       0, false, health); +	if (IS_ERR(health->dummy_reporter)) { +		err = PTR_ERR(health->dummy_reporter); +		goto err_empty_reporter_destroy; +	} + +	health->ddir = debugfs_create_dir("health", nsim_dev->ddir); +	if (IS_ERR_OR_NULL(health->ddir)) { +		err = PTR_ERR_OR_ZERO(health->ddir) ?: -EINVAL; +		goto err_dummy_reporter_destroy; +	} + +	health->recovered_break_msg = NULL; +	debugfs_create_file("break_health", 0200, health->ddir, health, +			    &nsim_dev_health_break_fops); +	health->binary_len = 16; +	debugfs_create_u32("binary_len", 0600, health->ddir, +			   &health->binary_len); +	health->fail_recover = false; +	debugfs_create_bool("fail_recover", 0600, health->ddir, +			    &health->fail_recover); +	return 0; + +err_dummy_reporter_destroy: +	devlink_health_reporter_destroy(health->dummy_reporter); +err_empty_reporter_destroy: +	devlink_health_reporter_destroy(health->empty_reporter); +	return err; +} + +void nsim_dev_health_exit(struct nsim_dev *nsim_dev) +{ +	struct nsim_dev_health *health = &nsim_dev->health; + +	debugfs_remove_recursive(health->ddir); +	kfree(health->recovered_break_msg); +	devlink_health_reporter_destroy(health->dummy_reporter); +	devlink_health_reporter_destroy(health->empty_reporter); +} diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 55f57f76d01b..2908e0a0d6e1 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -290,6 +290,7 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port)  	if (!dev)  		return ERR_PTR(-ENOMEM); +	dev_net_set(dev, nsim_dev_net(nsim_dev));  	ns = netdev_priv(dev);  	ns->netdev = dev;  	ns->nsim_dev = nsim_dev; @@ -357,18 +358,12 @@ static int __init nsim_module_init(void)  	if (err)  		goto err_dev_exit; -	err = nsim_fib_init(); -	if (err) -		goto err_bus_exit; -  	err = rtnl_link_register(&nsim_link_ops);  	if (err) -		goto err_fib_exit; +		goto err_bus_exit;  	return 0; -err_fib_exit: -	nsim_fib_exit();  err_bus_exit:  	nsim_bus_exit();  err_dev_exit: @@ -379,7 +374,6 @@ err_dev_exit:  static void __exit nsim_module_exit(void)  {  	rtnl_link_unregister(&nsim_link_ops); -	nsim_fib_exit();  	nsim_bus_exit();  	nsim_dev_exit();  } diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 66bf13765ad0..94df795ef4d3 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -134,6 +134,18 @@ enum nsim_resource_id {  	NSIM_RESOURCE_IPV6_FIB_RULES,  }; +struct nsim_dev_health { +	struct devlink_health_reporter *empty_reporter; +	struct devlink_health_reporter *dummy_reporter; +	struct dentry *ddir; +	char *recovered_break_msg; +	u32 binary_len; +	bool fail_recover; +}; + +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_port {  	struct list_head list;  	struct devlink_port devlink_port; @@ -161,9 +173,17 @@ struct nsim_dev {  	bool fw_update_status;  	u32 max_macs;  	bool test1; +	bool dont_allow_reload; +	bool fail_reload;  	struct devlink_region *dummy_region; +	struct nsim_dev_health health;  }; +static inline struct net *nsim_dev_net(struct nsim_dev *nsim_dev) +{ +	return devlink_net(priv_to_devlink(nsim_dev)); +} +  int nsim_dev_init(void);  void nsim_dev_exit(void);  int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev); @@ -173,11 +193,11 @@ int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,  int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,  		      unsigned int port_index); -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, -		     struct netlink_ext_ack *extack); +struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, +				      struct netlink_ext_ack *extack); +void nsim_fib_destroy(struct devlink *devlink, 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);  #if IS_ENABLED(CONFIG_XFRM_OFFLOAD)  void nsim_ipsec_init(struct netdevsim *ns); @@ -215,6 +235,9 @@ struct nsim_bus_dev {  	struct device dev;  	struct list_head list;  	unsigned int port_count; +	struct net *initial_net; /* Purpose of this is to carry net pointer +				  * during the probe time only. +				  */  	unsigned int num_vfs;  	struct nsim_vf_config *vfconfigs;  };  | 

