diff options
| author | Parav Pandit <parav@nvidia.com> | 2021-01-05 12:32:01 +0200 | 
|---|---|---|
| committer | Michael S. Tsirkin <mst@redhat.com> | 2021-02-23 07:52:56 -0500 | 
| commit | 903f7bcaedb84ca47998e609015a34ddde93742e (patch) | |
| tree | fa2fc0a67ad89cc6e8b6900bc8e1a49b65a5e985 /drivers/vdpa/vdpa.c | |
| parent | 33b347503f014ebf76257327cbc7001c6b721956 (diff) | |
vdpa: Enable a user to add and delete a vdpa device
Add the ability to add and delete a vdpa device.
Examples:
Create a vdpa device of type network named "foo2" from
the management device vdpasim:
$ vdpa dev add mgmtdev vdpasim_net name foo2
Delete the vdpa device after its use:
$ vdpa dev del foo2
Signed-off-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Eli Cohen <elic@nvidia.com>
Reviewed-by: Jason Wang <jasowang@redhat.com>
Link: https://lore.kernel.org/r/20210105103203.82508-5-parav@nvidia.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'drivers/vdpa/vdpa.c')
| -rw-r--r-- | drivers/vdpa/vdpa.c | 143 | 
1 files changed, 133 insertions, 10 deletions
| diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index 586e66b7f671..f1228189814f 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -136,6 +136,37 @@ static int vdpa_name_match(struct device *dev, const void *data)  	return (strcmp(dev_name(&vdev->dev), data) == 0);  } +static int __vdpa_register_device(struct vdpa_device *vdev) +{ +	struct device *dev; + +	lockdep_assert_held(&vdpa_dev_mutex); +	dev = bus_find_device(&vdpa_bus, NULL, dev_name(&vdev->dev), vdpa_name_match); +	if (dev) { +		put_device(dev); +		return -EEXIST; +	} +	return device_add(&vdev->dev); +} + +/** + * _vdpa_register_device - register a vDPA device with vdpa lock held + * Caller must have a succeed call of vdpa_alloc_device() before. + * Caller must invoke this routine in the management device dev_add() + * callback after setting up valid mgmtdev for this vdpa device. + * @vdev: the vdpa device to be registered to vDPA bus + * + * Returns an error when fail to add device to vDPA bus + */ +int _vdpa_register_device(struct vdpa_device *vdev) +{ +	if (!vdev->mdev) +		return -EINVAL; + +	return __vdpa_register_device(vdev); +} +EXPORT_SYMBOL_GPL(_vdpa_register_device); +  /**   * vdpa_register_device - register a vDPA device   * Callers must have a succeed call of vdpa_alloc_device() before. @@ -145,25 +176,30 @@ static int vdpa_name_match(struct device *dev, const void *data)   */  int vdpa_register_device(struct vdpa_device *vdev)  { -	struct device *dev;  	int err;  	mutex_lock(&vdpa_dev_mutex); -	dev = bus_find_device(&vdpa_bus, NULL, dev_name(&vdev->dev), vdpa_name_match); -	if (dev) { -		put_device(dev); -		err = -EEXIST; -		goto name_err; -	} - -	err = device_add(&vdev->dev); -name_err: +	err = __vdpa_register_device(vdev);  	mutex_unlock(&vdpa_dev_mutex);  	return err;  }  EXPORT_SYMBOL_GPL(vdpa_register_device);  /** + * _vdpa_unregister_device - unregister a vDPA device + * Caller must invoke this routine as part of management device dev_del() + * callback. + * @vdev: the vdpa device to be unregisted from vDPA bus + */ +void _vdpa_unregister_device(struct vdpa_device *vdev) +{ +	lockdep_assert_held(&vdpa_dev_mutex); +	WARN_ON(!vdev->mdev); +	device_unregister(&vdev->dev); +} +EXPORT_SYMBOL_GPL(_vdpa_unregister_device); + +/**   * vdpa_unregister_device - unregister a vDPA device   * @vdev: the vdpa device to be unregisted from vDPA bus   */ @@ -221,10 +257,25 @@ int vdpa_mgmtdev_register(struct vdpa_mgmt_dev *mdev)  }  EXPORT_SYMBOL_GPL(vdpa_mgmtdev_register); +static int vdpa_match_remove(struct device *dev, void *data) +{ +	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev); +	struct vdpa_mgmt_dev *mdev = vdev->mdev; + +	if (mdev == data) +		mdev->ops->dev_del(mdev, vdev); +	return 0; +} +  void vdpa_mgmtdev_unregister(struct vdpa_mgmt_dev *mdev)  {  	mutex_lock(&vdpa_dev_mutex); +  	list_del(&mdev->list); + +	/* Filter out all the entries belong to this management device and delete it. */ +	bus_for_each_dev(&vdpa_bus, NULL, mdev, vdpa_match_remove); +  	mutex_unlock(&vdpa_dev_mutex);  }  EXPORT_SYMBOL_GPL(vdpa_mgmtdev_unregister); @@ -368,9 +419,69 @@ out:  	return msg->len;  } +static int vdpa_nl_cmd_dev_add_set_doit(struct sk_buff *skb, struct genl_info *info) +{ +	struct vdpa_mgmt_dev *mdev; +	const char *name; +	int err = 0; + +	if (!info->attrs[VDPA_ATTR_DEV_NAME]) +		return -EINVAL; + +	name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]); + +	mutex_lock(&vdpa_dev_mutex); +	mdev = vdpa_mgmtdev_get_from_attr(info->attrs); +	if (IS_ERR(mdev)) { +		NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified management device"); +		err = PTR_ERR(mdev); +		goto err; +	} + +	err = mdev->ops->dev_add(mdev, name); +err: +	mutex_unlock(&vdpa_dev_mutex); +	return err; +} + +static int vdpa_nl_cmd_dev_del_set_doit(struct sk_buff *skb, struct genl_info *info) +{ +	struct vdpa_mgmt_dev *mdev; +	struct vdpa_device *vdev; +	struct device *dev; +	const char *name; +	int err = 0; + +	if (!info->attrs[VDPA_ATTR_DEV_NAME]) +		return -EINVAL; +	name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]); + +	mutex_lock(&vdpa_dev_mutex); +	dev = bus_find_device(&vdpa_bus, NULL, name, vdpa_name_match); +	if (!dev) { +		NL_SET_ERR_MSG_MOD(info->extack, "device not found"); +		err = -ENODEV; +		goto dev_err; +	} +	vdev = container_of(dev, struct vdpa_device, dev); +	if (!vdev->mdev) { +		NL_SET_ERR_MSG_MOD(info->extack, "Only user created device can be deleted by user"); +		err = -EINVAL; +		goto mdev_err; +	} +	mdev = vdev->mdev; +	mdev->ops->dev_del(mdev, vdev); +mdev_err: +	put_device(dev); +dev_err: +	mutex_unlock(&vdpa_dev_mutex); +	return err; +} +  static const struct nla_policy vdpa_nl_policy[VDPA_ATTR_MAX + 1] = {  	[VDPA_ATTR_MGMTDEV_BUS_NAME] = { .type = NLA_NUL_STRING },  	[VDPA_ATTR_MGMTDEV_DEV_NAME] = { .type = NLA_STRING }, +	[VDPA_ATTR_DEV_NAME] = { .type = NLA_STRING },  };  static const struct genl_ops vdpa_nl_ops[] = { @@ -380,6 +491,18 @@ static const struct genl_ops vdpa_nl_ops[] = {  		.doit = vdpa_nl_cmd_mgmtdev_get_doit,  		.dumpit = vdpa_nl_cmd_mgmtdev_get_dumpit,  	}, +	{ +		.cmd = VDPA_CMD_DEV_NEW, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +		.doit = vdpa_nl_cmd_dev_add_set_doit, +		.flags = GENL_ADMIN_PERM, +	}, +	{ +		.cmd = VDPA_CMD_DEV_DEL, +		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, +		.doit = vdpa_nl_cmd_dev_del_set_doit, +		.flags = GENL_ADMIN_PERM, +	},  };  static struct genl_family vdpa_nl_family __ro_after_init = { | 
