firmware: arm_scmi: Fix deferred_tx_wq release on error paths
authorCristian Marussi <cristian.marussi@arm.com>
Fri, 28 Oct 2022 14:08:31 +0000 (15:08 +0100)
committerSudeep Holla <sudeep.holla@arm.com>
Tue, 1 Nov 2022 11:36:20 +0000 (11:36 +0000)
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: Dan Carpenter <dan.carpenter@oracle.com>
Fixes: 5a3b7185c47c ("firmware: arm_scmi: Add atomic mode support to virtio transport")
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Link: https://lore.kernel.org/r/20221028140833.280091-6-cristian.marussi@arm.com
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/virtio.c

index 36b7686..33c9b81 100644 (file)
@@ -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);
        }