Commit de5494af authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Alex Williamson

vfio/mbochs: Fix missing error unwind of mbochs_used_mbytes

Convert mbochs to use an atomic scheme for this like mtty was changed
into. The atomic fixes various race conditions with probing. Add the
missing error unwind. Also add the missing kfree of mdev_state->pages.

Fixes: 681c1615 ("vfio/mbochs: Convert to use vfio_register_group_dev()")
Reported-by: default avatarCornelia Huck <cohuck@redhat.com>
Co-developed-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarCornelia Huck <cohuck@redhat.com>
Link: https://lore.kernel.org/r/2-v4-9ea22c5e6afb+1adf-vfio_reflck_jgg@nvidia.comSigned-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent e1706f07
...@@ -129,7 +129,7 @@ static dev_t mbochs_devt; ...@@ -129,7 +129,7 @@ static dev_t mbochs_devt;
static struct class *mbochs_class; static struct class *mbochs_class;
static struct cdev mbochs_cdev; static struct cdev mbochs_cdev;
static struct device mbochs_dev; static struct device mbochs_dev;
static int mbochs_used_mbytes; static atomic_t mbochs_avail_mbytes;
static const struct vfio_device_ops mbochs_dev_ops; static const struct vfio_device_ops mbochs_dev_ops;
struct vfio_region_info_ext { struct vfio_region_info_ext {
...@@ -507,18 +507,22 @@ static int mbochs_reset(struct mdev_state *mdev_state) ...@@ -507,18 +507,22 @@ static int mbochs_reset(struct mdev_state *mdev_state)
static int mbochs_probe(struct mdev_device *mdev) static int mbochs_probe(struct mdev_device *mdev)
{ {
int avail_mbytes = atomic_read(&mbochs_avail_mbytes);
const struct mbochs_type *type = const struct mbochs_type *type =
&mbochs_types[mdev_get_type_group_id(mdev)]; &mbochs_types[mdev_get_type_group_id(mdev)];
struct device *dev = mdev_dev(mdev); struct device *dev = mdev_dev(mdev);
struct mdev_state *mdev_state; struct mdev_state *mdev_state;
int ret = -ENOMEM; int ret = -ENOMEM;
if (type->mbytes + mbochs_used_mbytes > max_mbytes) do {
return -ENOMEM; if (avail_mbytes < type->mbytes)
return -ENOSPC;
} while (!atomic_try_cmpxchg(&mbochs_avail_mbytes, &avail_mbytes,
avail_mbytes - type->mbytes));
mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL);
if (mdev_state == NULL) if (mdev_state == NULL)
return -ENOMEM; goto err_avail;
vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mbochs_dev_ops); vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mbochs_dev_ops);
mdev_state->vconfig = kzalloc(MBOCHS_CONFIG_SPACE_SIZE, GFP_KERNEL); mdev_state->vconfig = kzalloc(MBOCHS_CONFIG_SPACE_SIZE, GFP_KERNEL);
...@@ -549,17 +553,17 @@ static int mbochs_probe(struct mdev_device *mdev) ...@@ -549,17 +553,17 @@ static int mbochs_probe(struct mdev_device *mdev)
mbochs_create_config_space(mdev_state); mbochs_create_config_space(mdev_state);
mbochs_reset(mdev_state); mbochs_reset(mdev_state);
mbochs_used_mbytes += type->mbytes;
ret = vfio_register_group_dev(&mdev_state->vdev); ret = vfio_register_group_dev(&mdev_state->vdev);
if (ret) if (ret)
goto err_mem; goto err_mem;
dev_set_drvdata(&mdev->dev, mdev_state); dev_set_drvdata(&mdev->dev, mdev_state);
return 0; return 0;
err_mem: err_mem:
kfree(mdev_state->pages);
kfree(mdev_state->vconfig); kfree(mdev_state->vconfig);
kfree(mdev_state); kfree(mdev_state);
err_avail:
atomic_add(type->mbytes, &mbochs_avail_mbytes);
return ret; return ret;
} }
...@@ -567,8 +571,8 @@ static void mbochs_remove(struct mdev_device *mdev) ...@@ -567,8 +571,8 @@ static void mbochs_remove(struct mdev_device *mdev)
{ {
struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev); struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev);
mbochs_used_mbytes -= mdev_state->type->mbytes;
vfio_unregister_group_dev(&mdev_state->vdev); vfio_unregister_group_dev(&mdev_state->vdev);
atomic_add(mdev_state->type->mbytes, &mbochs_avail_mbytes);
kfree(mdev_state->pages); kfree(mdev_state->pages);
kfree(mdev_state->vconfig); kfree(mdev_state->vconfig);
kfree(mdev_state); kfree(mdev_state);
...@@ -1351,7 +1355,7 @@ static ssize_t available_instances_show(struct mdev_type *mtype, ...@@ -1351,7 +1355,7 @@ static ssize_t available_instances_show(struct mdev_type *mtype,
{ {
const struct mbochs_type *type = const struct mbochs_type *type =
&mbochs_types[mtype_get_type_group_id(mtype)]; &mbochs_types[mtype_get_type_group_id(mtype)];
int count = (max_mbytes - mbochs_used_mbytes) / type->mbytes; int count = atomic_read(&mbochs_avail_mbytes) / type->mbytes;
return sprintf(buf, "%d\n", count); return sprintf(buf, "%d\n", count);
} }
...@@ -1433,6 +1437,8 @@ static int __init mbochs_dev_init(void) ...@@ -1433,6 +1437,8 @@ static int __init mbochs_dev_init(void)
{ {
int ret = 0; int ret = 0;
atomic_set(&mbochs_avail_mbytes, max_mbytes);
ret = alloc_chrdev_region(&mbochs_devt, 0, MINORMASK + 1, MBOCHS_NAME); ret = alloc_chrdev_region(&mbochs_devt, 0, MINORMASK + 1, MBOCHS_NAME);
if (ret < 0) { if (ret < 0) {
pr_err("Error: failed to register mbochs_dev, err: %d\n", ret); pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment