diff options
Diffstat (limited to 'drivers/net/netdevsim/dev.c')
| -rw-r--r-- | drivers/net/netdevsim/dev.c | 204 | 
1 files changed, 165 insertions, 39 deletions
| diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 54313bd57797..54345c096a16 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -56,6 +56,22 @@ static inline unsigned int nsim_dev_port_index_to_vf_index(unsigned int port_ind  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)); + +	return nsim_dev->nsim_bus_dev->num_vfs; +} + +static void +nsim_bus_dev_set_vfs(struct nsim_bus_dev *nsim_bus_dev, unsigned int num_vfs) +{ +	rtnl_lock(); +	nsim_bus_dev->num_vfs = num_vfs; +	rtnl_unlock(); +} +  #define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32)  static int @@ -211,6 +227,70 @@ static const struct file_operations nsim_dev_trap_fa_cookie_fops = {  	.owner = THIS_MODULE,  }; +static ssize_t nsim_bus_dev_max_vfs_read(struct file *file, char __user *data, +					 size_t count, loff_t *ppos) +{ +	struct nsim_dev *nsim_dev = file->private_data; +	char buf[11]; +	ssize_t len; + +	len = scnprintf(buf, sizeof(buf), "%u\n", +			READ_ONCE(nsim_dev->nsim_bus_dev->max_vfs)); + +	return simple_read_from_buffer(data, count, ppos, buf, len); +} + +static ssize_t nsim_bus_dev_max_vfs_write(struct file *file, +					  const char __user *data, +					  size_t count, loff_t *ppos) +{ +	struct nsim_vf_config *vfconfigs; +	struct nsim_dev *nsim_dev; +	char buf[10]; +	ssize_t ret; +	u32 val; + +	if (*ppos != 0) +		return 0; + +	if (count >= sizeof(buf)) +		return -ENOSPC; + +	ret = copy_from_user(buf, data, count); +	if (ret) +		return -EFAULT; +	buf[count] = '\0'; + +	ret = kstrtouint(buf, 10, &val); +	if (ret) +		return -EINVAL; + +	/* max_vfs limited by the maximum number of provided port indexes */ +	if (val > NSIM_DEV_VF_PORT_INDEX_MAX - NSIM_DEV_VF_PORT_INDEX_BASE) +		return -ERANGE; + +	vfconfigs = kcalloc(val, sizeof(struct nsim_vf_config), +			    GFP_KERNEL | __GFP_NOWARN); +	if (!vfconfigs) +		return -ENOMEM; + +	nsim_dev = file->private_data; +	mutex_lock(&nsim_dev->vfs_lock); +	/* Reject if VFs are configured */ +	if (nsim_dev_get_vfs(nsim_dev)) { +		ret = -EBUSY; +	} else { +		swap(nsim_dev->vfconfigs, vfconfigs); +		WRITE_ONCE(nsim_dev->nsim_bus_dev->max_vfs, val); +		*ppos += count; +		ret = count; +	} +	mutex_unlock(&nsim_dev->vfs_lock); + +	kfree(vfconfigs); +	return ret; +} +  static const struct file_operations nsim_dev_max_vfs_fops = {  	.open = simple_open,  	.read = nsim_bus_dev_max_vfs_read, @@ -259,11 +339,9 @@ 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); -	nsim_dev->max_vfs = debugfs_create_file("max_vfs", -						0600, -						nsim_dev->ddir, -						nsim_dev->nsim_bus_dev, -						&nsim_dev_max_vfs_fops); +	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); @@ -328,9 +406,9 @@ static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,  		unsigned int vf_id = nsim_dev_port_index_to_vf_index(port_index);  		debugfs_create_u16("tx_share", 0400, nsim_dev_port->ddir, -				   &nsim_bus_dev->vfconfigs[vf_id].min_tx_rate); +				   &nsim_dev->vfconfigs[vf_id].min_tx_rate);  		debugfs_create_u16("tx_max", 0400, nsim_dev_port->ddir, -				   &nsim_bus_dev->vfconfigs[vf_id].max_tx_rate); +				   &nsim_dev->vfconfigs[vf_id].max_tx_rate);  		nsim_dev_port->rate_parent = debugfs_create_file("rate_parent",  								 0400,  								 nsim_dev_port->ddir, @@ -490,7 +568,9 @@ static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)  }  static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port); -int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack *extack) + +static int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, +				  struct netlink_ext_ack *extack)  {  	struct devlink *devlink = priv_to_devlink(nsim_dev);  	struct nsim_dev_port *nsim_dev_port, *tmp; @@ -505,13 +585,14 @@ int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack *ex  	return 0;  } -int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack *extack) +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;  	int i, err; -	for (i = 0; i < nsim_bus_dev->num_vfs; i++) { -		err = nsim_dev_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i); +	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);  		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); @@ -523,7 +604,7 @@ int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack  err_port_add_vfs:  	for (i--; i >= 0; i--) -		nsim_dev_port_del(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i); +		nsim_drv_port_del(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i);  	return err;  } @@ -533,7 +614,7 @@ static int nsim_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,  	struct nsim_dev *nsim_dev = devlink_priv(devlink);  	int err = 0; -	mutex_lock(&nsim_dev->nsim_bus_dev->vfs_lock); +	mutex_lock(&nsim_dev->vfs_lock);  	if (mode == nsim_dev->esw_mode)  		goto unlock; @@ -545,7 +626,7 @@ static int nsim_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,  		err = -EINVAL;  unlock: -	mutex_unlock(&nsim_dev->nsim_bus_dev->vfs_lock); +	mutex_unlock(&nsim_dev->vfs_lock);  	return err;  } @@ -1093,7 +1174,7 @@ static int nsim_leaf_tx_share_set(struct devlink_rate *devlink_rate, void *priv,  				  u64 tx_share, struct netlink_ext_ack *extack)  {  	struct nsim_dev_port *nsim_dev_port = priv; -	struct nsim_bus_dev *nsim_bus_dev = nsim_dev_port->ns->nsim_bus_dev; +	struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;  	int vf_id = nsim_dev_port_index_to_vf_index(nsim_dev_port->port_index);  	int err; @@ -1101,7 +1182,7 @@ static int nsim_leaf_tx_share_set(struct devlink_rate *devlink_rate, void *priv,  	if (err)  		return err; -	nsim_bus_dev->vfconfigs[vf_id].min_tx_rate = tx_share; +	nsim_dev->vfconfigs[vf_id].min_tx_rate = tx_share;  	return 0;  } @@ -1109,7 +1190,7 @@ static int nsim_leaf_tx_max_set(struct devlink_rate *devlink_rate, void *priv,  				u64 tx_max, struct netlink_ext_ack *extack)  {  	struct nsim_dev_port *nsim_dev_port = priv; -	struct nsim_bus_dev *nsim_bus_dev = nsim_dev_port->ns->nsim_bus_dev; +	struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;  	int vf_id = nsim_dev_port_index_to_vf_index(nsim_dev_port->port_index);  	int err; @@ -1117,7 +1198,7 @@ static int nsim_leaf_tx_max_set(struct devlink_rate *devlink_rate, void *priv,  	if (err)  		return err; -	nsim_bus_dev->vfconfigs[vf_id].max_tx_rate = tx_max; +	nsim_dev->vfconfigs[vf_id].max_tx_rate = tx_max;  	return 0;  } @@ -1273,13 +1354,12 @@ static const struct devlink_ops nsim_dev_devlink_ops = {  static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type,  			       unsigned int port_index)  { -	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;  	struct devlink_port_attrs attrs = {};  	struct nsim_dev_port *nsim_dev_port;  	struct devlink_port *devlink_port;  	int err; -	if (type == NSIM_DEV_PORT_TYPE_VF && !nsim_bus_dev->num_vfs) +	if (type == NSIM_DEV_PORT_TYPE_VF && !nsim_dev_get_vfs(nsim_dev))  		return -EINVAL;  	nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); @@ -1442,7 +1522,7 @@ err_dummy_region_exit:  	return err;  } -int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) +int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)  {  	struct nsim_dev *nsim_dev;  	struct devlink *devlink; @@ -1457,6 +1537,7 @@ int nsim_dev_probe(struct 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; @@ -1466,13 +1547,17 @@ int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)  	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); -	err = nsim_dev_resources_register(devlink); -	if (err) +	nsim_dev->vfconfigs = kcalloc(nsim_bus_dev->max_vfs, +				      sizeof(struct nsim_vf_config), +				      GFP_KERNEL | __GFP_NOWARN); +	if (!nsim_dev->vfconfigs) { +		err = -ENOMEM;  		goto err_devlink_free; +	} -	err = devlink_register(devlink); +	err = nsim_dev_resources_register(devlink);  	if (err) -		goto err_resources_unregister; +		goto err_vfc_free;  	err = devlink_params_register(devlink, nsim_devlink_params,  				      ARRAY_SIZE(nsim_devlink_params)); @@ -1514,9 +1599,9 @@ int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)  	if (err)  		goto err_psample_exit; -	devlink_params_publish(devlink); -	devlink_reload_enable(devlink);  	nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; +	devlink_set_features(devlink, DEVLINK_F_RELOAD); +	devlink_register(devlink);  	return 0;  err_psample_exit: @@ -1537,11 +1622,12 @@ 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_vfc_free: +	kfree(nsim_dev->vfconfigs);  err_devlink_free:  	devlink_free(devlink); +	dev_set_drvdata(&nsim_bus_dev->dev, NULL);  	return err;  } @@ -1553,10 +1639,13 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)  		return;  	debugfs_remove(nsim_dev->take_snapshot); -	mutex_lock(&nsim_dev->nsim_bus_dev->vfs_lock); -	if (nsim_dev->nsim_bus_dev->num_vfs) -		nsim_bus_dev_vfs_disable(nsim_dev->nsim_bus_dev); -	mutex_unlock(&nsim_dev->nsim_bus_dev->vfs_lock); +	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_psample_exit(nsim_dev); @@ -1567,22 +1656,22 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)  	mutex_destroy(&nsim_dev->port_list_lock);  } -void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) +void nsim_drv_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); -	devlink_reload_disable(devlink); - +	devlink_unregister(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); +	kfree(nsim_dev->vfconfigs);  	devlink_free(devlink); +	dev_set_drvdata(&nsim_bus_dev->dev, NULL);  }  static struct nsim_dev_port * @@ -1598,7 +1687,7 @@ __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type,  	return NULL;  } -int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, +int nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type,  		      unsigned int port_index)  {  	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); @@ -1613,7 +1702,7 @@ int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type  	return err;  } -int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, +int nsim_drv_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type,  		      unsigned int port_index)  {  	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); @@ -1630,6 +1719,43 @@ int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type  	return err;  } +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); +	int ret = 0; + +	mutex_lock(&nsim_dev->vfs_lock); +	if (nsim_bus_dev->num_vfs == num_vfs) +		goto exit_unlock; +	if (nsim_bus_dev->num_vfs && num_vfs) { +		ret = -EBUSY; +		goto exit_unlock; +	} +	if (nsim_bus_dev->max_vfs < num_vfs) { +		ret = -ENOMEM; +		goto exit_unlock; +	} + +	nsim_bus_dev_set_vfs(nsim_bus_dev, num_vfs); +	if (nsim_esw_mode_is_switchdev(nsim_dev)) { +		if (num_vfs) { +			ret = nsim_esw_switchdev_enable(nsim_dev, NULL); +			if (ret) { +				nsim_bus_dev_set_vfs(nsim_bus_dev, 0); +				goto exit_unlock; +			} +		} else { +			nsim_esw_legacy_enable(nsim_dev, NULL); +		} +	} + +exit_unlock: +	mutex_unlock(&nsim_dev->vfs_lock); + +	return ret; +} +  int nsim_dev_init(void)  {  	nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); |