]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: arm_scmi: Fix timeout checks on polling path
authorCristian Marussi <cristian.marussi@arm.com>
Mon, 10 Mar 2025 17:58:00 +0000 (17:58 +0000)
committerSudeep Holla <sudeep.holla@arm.com>
Tue, 8 Apr 2025 09:31:59 +0000 (10:31 +0100)
Polling mode transactions wait for a reply busy-looping without holding a
spinlock, but currently the timeout checks are based only on elapsed time:
as a result we could hit a false positive whenever our busy-looping thread
is pre-empted and scheduled out for a time greater than the polling
timeout.

Change the checks at the end of the busy-loop to make sure that the polling
wasn't indeed successful or an out-of-order reply caused the polling to be
forcibly terminated.

Fixes: 31d2f803c19c ("firmware: arm_scmi: Add sync_cmds_completed_on_ret transport flag")
Reported-by: Huangjie <huangjie1663@phytium.com.cn>
Closes: https://lore.kernel.org/arm-scmi/20250123083323.2363749-1-jackhuang021@gmail.com/
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Cc: stable@vger.kernel.org # 5.18.x
Message-Id: <20250310175800.1444293-1-cristian.marussi@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/driver.c

index 1c75a4c9c3716622f6d7f1d81e794db3a45a9a4b..0390d5ff195ec012daf59821ec30aa8a8406999d 100644 (file)
@@ -1248,7 +1248,8 @@ static void xfer_put(const struct scmi_protocol_handle *ph,
 }
 
 static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
-                                     struct scmi_xfer *xfer, ktime_t stop)
+                                     struct scmi_xfer *xfer, ktime_t stop,
+                                     bool *ooo)
 {
        struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
 
@@ -1257,7 +1258,7 @@ static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
         * in case of out-of-order receptions of delayed responses
         */
        return info->desc->ops->poll_done(cinfo, xfer) ||
-              try_wait_for_completion(&xfer->done) ||
+              (*ooo = try_wait_for_completion(&xfer->done)) ||
               ktime_after(ktime_get(), stop);
 }
 
@@ -1274,15 +1275,17 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
                 * itself to support synchronous commands replies.
                 */
                if (!desc->sync_cmds_completed_on_ret) {
+                       bool ooo = false;
+
                        /*
                         * Poll on xfer using transport provided .poll_done();
                         * assumes no completion interrupt was available.
                         */
                        ktime_t stop = ktime_add_ms(ktime_get(), timeout_ms);
 
-                       spin_until_cond(scmi_xfer_done_no_timeout(cinfo,
-                                                                 xfer, stop));
-                       if (ktime_after(ktime_get(), stop)) {
+                       spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer,
+                                                                 stop, &ooo));
+                       if (!ooo && !info->desc->ops->poll_done(cinfo, xfer)) {
                                dev_err(dev,
                                        "timed out in resp(caller: %pS) - polling\n",
                                        (void *)_RET_IP_);