Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ab25d35 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab
Browse files

[media] cec: improve locking



- The global lock was used in cec_get_device when it should have
  used the devnode lock.
- cec_put_device also took the global lock, but since the release
  function takes that lock as well this could lead to a deadlock.
  Just don't take the lock here since there is no reason for it.
- cec_devnode_register() should take the global lock when clearing
  the bit in the global bitmap.
- In cec_devnode_unregister() place the devnode->(un)register tests
  and assignments under the devnode lock as well: this has to be
  in a critical block.

Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 62148f09
Loading
Loading
Loading
Loading
+14 −11
Original line number Diff line number Diff line
@@ -51,31 +51,29 @@ int cec_get_device(struct cec_devnode *devnode)
{
	/*
	 * Check if the cec device is available. This needs to be done with
	 * the cec_devnode_lock held to prevent an open/unregister race:
	 * the devnode->lock held to prevent an open/unregister race:
	 * without the lock, the device could be unregistered and freed between
	 * the devnode->registered check and get_device() calls, leading to
	 * a crash.
	 */
	mutex_lock(&cec_devnode_lock);
	mutex_lock(&devnode->lock);
	/*
	 * return ENXIO if the cec device has been removed
	 * already or if it is not registered anymore.
	 */
	if (!devnode->registered) {
		mutex_unlock(&cec_devnode_lock);
		mutex_unlock(&devnode->lock);
		return -ENXIO;
	}
	/* and increase the device refcount */
	get_device(&devnode->dev);
	mutex_unlock(&cec_devnode_lock);
	mutex_unlock(&devnode->lock);
	return 0;
}

void cec_put_device(struct cec_devnode *devnode)
{
	mutex_lock(&cec_devnode_lock);
	put_device(&devnode->dev);
	mutex_unlock(&cec_devnode_lock);
}

/* Called when the last user of the cec device exits. */
@@ -84,11 +82,10 @@ static void cec_devnode_release(struct device *cd)
	struct cec_devnode *devnode = to_cec_devnode(cd);

	mutex_lock(&cec_devnode_lock);

	/* Mark device node number as free */
	clear_bit(devnode->minor, cec_devnode_nums);

	mutex_unlock(&cec_devnode_lock);

	cec_delete_adapter(to_cec_adapter(devnode));
}

@@ -160,7 +157,9 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
cdev_del:
	cdev_del(&devnode->cdev);
clr_bit:
	mutex_lock(&cec_devnode_lock);
	clear_bit(devnode->minor, cec_devnode_nums);
	mutex_unlock(&cec_devnode_lock);
	return ret;
}

@@ -177,17 +176,21 @@ static void cec_devnode_unregister(struct cec_devnode *devnode)
{
	struct cec_fh *fh;

	mutex_lock(&devnode->lock);

	/* Check if devnode was never registered or already unregistered */
	if (!devnode->registered || devnode->unregistered)
	if (!devnode->registered || devnode->unregistered) {
		mutex_unlock(&devnode->lock);
		return;
	}

	mutex_lock(&devnode->lock);
	list_for_each_entry(fh, &devnode->fhs, list)
		wake_up_interruptible(&fh->wait);
	mutex_unlock(&devnode->lock);

	devnode->registered = false;
	devnode->unregistered = true;
	mutex_unlock(&devnode->lock);

	device_del(&devnode->dev);
	cdev_del(&devnode->cdev);
	put_device(&devnode->dev);