Commit 1eff6929 authored by Cristian Marussi's avatar Cristian Marussi Committed by Sudeep Holla
Browse files

firmware: arm_scmi: Fix deferred_tx_wq release on error paths



Use devres to allocate the dedicated deferred_tx_wq polling workqueue so
as to automatically trigger the proper resource release on error path.

Reported-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Fixes: 5a3b7185 ("firmware: arm_scmi: Add atomic mode support to virtio transport")
Signed-off-by: default avatarCristian Marussi <cristian.marussi@arm.com>
Link: https://lore.kernel.org/r/20221028140833.280091-6-cristian.marussi@arm.com


Signed-off-by: default avatarSudeep Holla <sudeep.holla@arm.com>
parent 5ffc1c4c
Loading
Loading
Loading
Loading
+13 −7
Original line number Diff line number Diff line
@@ -148,7 +148,6 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
{
	unsigned long flags;
	DECLARE_COMPLETION_ONSTACK(vioch_shutdown_done);
	void *deferred_wq = NULL;

	/*
	 * Prepare to wait for the last release if not already released
@@ -162,16 +161,11 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)

	vioch->shutdown_done = &vioch_shutdown_done;
	virtio_break_device(vioch->vqueue->vdev);
	if (!vioch->is_rx && vioch->deferred_tx_wq) {
		deferred_wq = vioch->deferred_tx_wq;
	if (!vioch->is_rx && vioch->deferred_tx_wq)
		/* Cannot be kicked anymore after this...*/
		vioch->deferred_tx_wq = NULL;
	}
	spin_unlock_irqrestore(&vioch->lock, flags);

	if (deferred_wq)
		destroy_workqueue(deferred_wq);

	scmi_vio_channel_release(vioch);

	/* Let any possibly concurrent RX path release the channel */
@@ -416,6 +410,11 @@ static bool virtio_chan_available(struct device *dev, int idx)
	return vioch && !vioch->cinfo;
}

static void scmi_destroy_tx_workqueue(void *deferred_tx_wq)
{
	destroy_workqueue(deferred_tx_wq);
}

static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
			     bool tx)
{
@@ -430,6 +429,8 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,

	/* Setup a deferred worker for polling. */
	if (tx && !vioch->deferred_tx_wq) {
		int ret;

		vioch->deferred_tx_wq =
			alloc_workqueue(dev_name(&scmi_vdev->dev),
					WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS,
@@ -437,6 +438,11 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
		if (!vioch->deferred_tx_wq)
			return -ENOMEM;

		ret = devm_add_action_or_reset(dev, scmi_destroy_tx_workqueue,
					       vioch->deferred_tx_wq);
		if (ret)
			return ret;

		INIT_WORK(&vioch->deferred_tx_work,
			  scmi_vio_deferred_tx_worker);
	}