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

Commit a441381c authored by Konstantin Dorfman's avatar Konstantin Dorfman
Browse files

spcom: fix deadlock when add/remove new channel



This change resolves following 2 deadlocks:

The register_rpmsg_drv() called under ch->lock. When newly created (by
rpmsg layer) rpmsg device probe arrives during the rpmsg driver is
registering - deadlock on channel lock occurs.

When channel is closing (by unregister_rpmsg_drv(), under ch->lock) this
causes call to spcom_rpdev_remove() for the rpmsg device (managed by
unregestered driver). The spcom_rpdev_remove() causes deadlock on ch->lock.

To resolve these deadlocks move register/unregister_rpmsg_drv() calls out
of the ch->lock.

Also, do not need to unregister_rpmsg_drv() on spcom_device_release() -
since SP side never initiates glink_close()

Change-Id: I77661f82f3719d1153483ef31f8ff9fef7fb99a3
Signed-off-by: default avatarKonstantin Dorfman <kdorfman@codeaurora.org>
parent d65289ee
Loading
Loading
Loading
Loading
+6 −14
Original line number Diff line number Diff line
@@ -1300,13 +1300,6 @@ static int spcom_device_release(struct inode *inode, struct file *filp)
	ch->is_busy = false;
	ch->pid = 0;
	ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */

	if (strcmp(name, "sp_kernel") != 0) {
		ret = spcom_unregister_rpmsg_drv(ch);
		if (ret != 0)
			pr_err("can't unregister rpmsg drv\n", ret);
	}

	mutex_unlock(&ch->lock);
	filp->private_data = NULL;

@@ -1621,7 +1614,6 @@ static int spcom_create_channel_chardev(const char *name)
		return ret;
	}

	mutex_lock(&ch->lock);
	ret = spcom_register_rpmsg_drv(ch);
	if (ret < 0) {
		pr_err("register rpmsg driver failed %d\n", ret);
@@ -1653,6 +1645,7 @@ static int spcom_create_channel_chardev(const char *name)
		goto exit_destroy_device;
	}
	atomic_inc(&spcom_dev->chdev_count);
	mutex_lock(&ch->lock);
	ch->cdev = cdev;
	ch->dev = dev;
	mutex_unlock(&ch->lock);
@@ -1939,10 +1932,7 @@ static void spcom_rpdev_remove(struct rpmsg_device *rpdev)
	dev_info(&rpdev->dev, "rpmsg device %s removed\n", rpdev->id.name);
}

/*
 * register rpmsg driver to match with channel ch_name
 * function shold be called under ch->lock
 */
/* register rpmsg driver to match with channel ch_name */
static int spcom_register_rpmsg_drv(struct spcom_channel *ch)
{
	struct rpmsg_driver *rpdrv;
@@ -1989,25 +1979,27 @@ static int spcom_register_rpmsg_drv(struct spcom_channel *ch)
		kfree(drv_name);
		return ret;
	}
	// the function caller should mutex_lock(&ch->lock)
	mutex_lock(&ch->lock);
	ch->rpdrv = rpdrv;
	ch->rpmsg_abort = false;
	mutex_unlock(&ch->lock);

	return 0;
}

/* function shold be called under ch->lock */
static int spcom_unregister_rpmsg_drv(struct spcom_channel *ch)
{
	if (!ch->rpdrv)
		return -ENODEV;
	unregister_rpmsg_driver(ch->rpdrv);

	mutex_lock(&ch->lock);
	kfree(ch->rpdrv->drv.name);
	kfree((void *)ch->rpdrv->id_table);
	kfree(ch->rpdrv);
	ch->rpdrv = NULL;
	ch->rpmsg_abort = true; /* will unblock spcom_rx() */
	mutex_unlock(&ch->lock);
	return 0;
}