firmware: arm_scmi: Make tx_prepare time out eventually
authorCristian Marussi <cristian.marussi@arm.com>
Fri, 28 Oct 2022 14:08:28 +0000 (15:08 +0100)
committerSudeep Holla <sudeep.holla@arm.com>
Tue, 1 Nov 2022 11:33:24 +0000 (11:33 +0000)
SCMI transports based on shared memory, at start of transmissions, have
to wait for the shared Tx channel area to be eventually freed by the
SCMI platform before accessing the channel. In fact the channel is owned
by the SCMI platform until marked as free by the platform itself and,
as such, cannot be used by the agent until relinquished.

As a consequence a badly misbehaving SCMI platform firmware could lock
the channel indefinitely and make the kernel side SCMI stack loop
forever waiting for such channel to be freed, possibly hanging the
whole boot sequence.

Add a timeout to the existent Tx waiting spin-loop so that, when the
system ends up in this situation, the SCMI stack can at least bail-out,
nosily warn the user, and abort the transmission.

Reported-by: YaxiongTian <iambestgod@outlook.com>
Suggested-by: YaxiongTian <iambestgod@outlook.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Etienne Carriere <etienne.carriere@linaro.org>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Link: https://lore.kernel.org/r/20221028140833.280091-3-cristian.marussi@arm.com
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/common.h
drivers/firmware/arm_scmi/driver.c
drivers/firmware/arm_scmi/mailbox.c
drivers/firmware/arm_scmi/optee.c
drivers/firmware/arm_scmi/shmem.c
drivers/firmware/arm_scmi/smc.c

index 9b87b5b..a1c0154 100644 (file)
@@ -118,6 +118,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
  *
  * @dev: Reference to device in the SCMI hierarchy corresponding to this
  *      channel
+ * @rx_timeout_ms: The configured RX timeout in milliseconds.
  * @handle: Pointer to SCMI entity handle
  * @no_completion_irq: Flag to indicate that this channel has no completion
  *                    interrupt mechanism for synchronous commands.
@@ -127,6 +128,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
  */
 struct scmi_chan_info {
        struct device *dev;
+       unsigned int rx_timeout_ms;
        struct scmi_handle *handle;
        bool no_completion_irq;
        void *transport_info;
@@ -233,7 +235,7 @@ void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
 struct scmi_shared_mem;
 
 void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
-                     struct scmi_xfer *xfer);
+                     struct scmi_xfer *xfer, struct scmi_chan_info *cinfo);
 u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
 void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
                          struct scmi_xfer *xfer);
index 94be633..985775f 100644 (file)
@@ -2013,6 +2013,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device *dev,
                return -ENOMEM;
 
        cinfo->dev = dev;
+       cinfo->rx_timeout_ms = info->desc->max_rx_timeout_ms;
 
        ret = info->desc->ops->chan_setup(cinfo, info->dev, tx);
        if (ret)
index 08ff4d1..1e40cb0 100644 (file)
@@ -36,7 +36,7 @@ static void tx_prepare(struct mbox_client *cl, void *m)
 {
        struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
 
-       shmem_tx_prepare(smbox->shmem, m);
+       shmem_tx_prepare(smbox->shmem, m, smbox->cinfo);
 }
 
 static void rx_callback(struct mbox_client *cl, void *m)
index f42dad9..2a7aeab 100644 (file)
@@ -498,7 +498,7 @@ static int scmi_optee_send_message(struct scmi_chan_info *cinfo,
                msg_tx_prepare(channel->req.msg, xfer);
                ret = invoke_process_msg_channel(channel, msg_command_size(xfer));
        } else {
-               shmem_tx_prepare(channel->req.shmem, xfer);
+               shmem_tx_prepare(channel->req.shmem, xfer, cinfo);
                ret = invoke_process_smt_channel(channel);
        }
 
index 0e3eaea..1dfe534 100644 (file)
@@ -5,10 +5,13 @@
  * Copyright (C) 2019 ARM Ltd.
  */
 
+#include <linux/ktime.h>
 #include <linux/io.h>
 #include <linux/processor.h>
 #include <linux/types.h>
 
+#include <asm-generic/bug.h>
+
 #include "common.h"
 
 /*
@@ -30,16 +33,36 @@ struct scmi_shared_mem {
 };
 
 void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
-                     struct scmi_xfer *xfer)
+                     struct scmi_xfer *xfer, struct scmi_chan_info *cinfo)
 {
+       ktime_t stop;
+
        /*
         * Ideally channel must be free by now unless OS timeout last
         * request and platform continued to process the same, wait
         * until it releases the shared memory, otherwise we may endup
-        * overwriting its response with new message payload or vice-versa
+        * overwriting its response with new message payload or vice-versa.
+        * Giving up anyway after twice the expected channel timeout so as
+        * not to bail-out on intermittent issues where the platform is
+        * occasionally a bit slower to answer.
+        *
+        * Note that after a timeout is detected we bail-out and carry on but
+        * the transport functionality is probably permanently compromised:
+        * this is just to ease debugging and avoid complete hangs on boot
+        * due to a misbehaving SCMI firmware.
         */
-       spin_until_cond(ioread32(&shmem->channel_status) &
-                       SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+       stop = ktime_add_ms(ktime_get(), 2 * cinfo->rx_timeout_ms);
+       spin_until_cond((ioread32(&shmem->channel_status) &
+                        SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ||
+                        ktime_after(ktime_get(), stop));
+       if (!(ioread32(&shmem->channel_status) &
+             SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
+               WARN_ON_ONCE(1);
+               dev_err(cinfo->dev,
+                       "Timeout waiting for a free TX channel !\n");
+               return;
+       }
+
        /* Mark channel busy + clear error */
        iowrite32(0x0, &shmem->channel_status);
        iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
index 745acfd..87a7b13 100644 (file)
@@ -188,7 +188,7 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
         */
        smc_channel_lock_acquire(scmi_info, xfer);
 
-       shmem_tx_prepare(scmi_info->shmem, xfer);
+       shmem_tx_prepare(scmi_info->shmem, xfer, cinfo);
 
        arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res);