Skip to content

Commit

Permalink
firmware: arm_scmi: Make tx_prepare time out eventually
Browse files Browse the repository at this point in the history
[ Upstream commit 59172b2 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
freefall75 authored and gregkh committed Nov 26, 2022
1 parent a0d64b8 commit 583cc50
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 8 deletions.
4 changes: 3 additions & 1 deletion drivers/firmware/arm_scmi/common.h
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions drivers/firmware/arm_scmi/driver.c
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion drivers/firmware/arm_scmi/mailbox.c
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion drivers/firmware/arm_scmi/optee.c
Expand Up @@ -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);
}

Expand Down
31 changes: 27 additions & 4 deletions drivers/firmware/arm_scmi/shmem.c
Expand Up @@ -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"

/*
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion drivers/firmware/arm_scmi/smc.c
Expand Up @@ -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);

Expand Down

0 comments on commit 583cc50

Please sign in to comment.