Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6961613 authored by Vikram Mulukutla's avatar Vikram Mulukutla
Browse files

qcom: scm: Implement a retry mechanism



There are times when the secure world may be processing
requests that require one particular client on the HLOS
to invoke an scm_call. While waiting for this particular
scm_call, no other scm requests can be processed and the
secure world will return an error code indicating this
situation.

When this error code is encountered, release the scm_call
mutex, wait for a specified amount of time and retry the
scm_call. As much as possible, avoid overhead in the first
scm_call attempt.

Change-Id: I6edce93bb70620fdffcb3ef3fb639cf5b928845b
Signed-off-by: default avatarVikram Mulukutla <markivx@codeaurora.org>
parent 8c0c40d9
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/delay.h>

#include <asm/cacheflush.h>
#include <asm/compiler.h>
@@ -29,9 +30,13 @@
#define SCM_EINVAL_ARG		-2
#define SCM_ERROR		-1
#define SCM_INTERRUPTED		1
#define SCM_EBUSY		-55

static DEFINE_MUTEX(scm_lock);

#define SCM_EBUSY_WAIT_MS 30
#define SCM_EBUSY_MAX_RETRY 20

#define SCM_BUF_LEN(__cmd_size, __resp_size)	\
	(sizeof(struct scm_command) + sizeof(struct scm_response) + \
		__cmd_size + __resp_size)
@@ -139,6 +144,7 @@ static inline void *scm_get_response_buffer(const struct scm_response *rsp)

static int scm_remap_error(int err)
{
	if (err != SCM_EBUSY)
		pr_err("scm_call failed with error code %d\n", err);
	switch (err) {
	case SCM_ERROR:
@@ -150,6 +156,8 @@ static int scm_remap_error(int err)
		return -EOPNOTSUPP;
	case SCM_ENOMEM:
		return -ENOMEM;
	case SCM_EBUSY:
		return SCM_EBUSY;
	}
	return -EINVAL;
}
@@ -281,6 +289,33 @@ static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf,
	return ret;
}

/*
 * Sometimes the secure world may be busy waiting for a particular resource.
 * In those situations, it is expected that the secure world returns a special
 * error code (SCM_EBUSY). Retry any scm_call that fails with this error code,
 * but with a timeout in place. Also, don't move this into scm_call_common,
 * since we want the first attempt to be the "fastpath".
 */
static int _scm_call_retry(u32 svc_id, u32 cmd_id, const void *cmd_buf,
				size_t cmd_len, void *resp_buf, size_t resp_len,
				struct scm_command *cmd,
				size_t len)
{
	int ret, retry_count = 0;

	do {
		ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len,
					resp_buf, resp_len, cmd, len);
		if (ret == SCM_EBUSY)
			msleep(SCM_EBUSY_WAIT_MS);
	} while (ret == SCM_EBUSY && (retry_count++ < SCM_EBUSY_MAX_RETRY));

	if (ret == SCM_EBUSY)
		pr_err("scm: secure world busy (rc = SCM_EBUSY)\n");

	return ret;
}

/**
 * scm_call_noalloc - Send an SCM command
 *
@@ -344,6 +379,9 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,

	ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,
				resp_len, cmd, len);
	if (unlikely(ret == SCM_EBUSY))
		ret = _scm_call_retry(svc_id, cmd_id, cmd_buf, cmd_len,
				      resp_buf, resp_len, cmd, PAGE_ALIGN(len));
	kfree(cmd);
	return ret;
}