Re: [PATCH 1/3] soc: qcom: smem: Support global partition

From: Bjorn Andersson
Date: Mon Aug 21 2017 - 13:17:41 EST


On Thu 17 Aug 18:15 PDT 2017, Chris Lew wrote:

> SMEM V12 creates a global partition to allocate global
> smem items from instead of a global heap. The global
> partition has the same structure as a private partition.
>

Welcome to LKML!

This looks quite reasonable, just some minor comments below.

> Signed-off-by: Chris Lew <clew@xxxxxxxxxxxxxx>
> ---
> drivers/soc/qcom/smem.c | 134 +++++++++++++++++++++++++++++++++++++-----------
> 1 file changed, 105 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index c28275be0038..fed2934d6bda 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -65,11 +65,20 @@
> /*
> * Item 3 of the global heap contains an array of versions for the various
> * software components in the SoC. We verify that the boot loader version is
> - * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check.
> + * a valid version as a sanity check.
> */
> #define SMEM_ITEM_VERSION 3

SMEM_ITEM_VERSION is now unused, which means that the comment should be
updated with respect to item 3 and the versions array of smem_header.

> #define SMEM_MASTER_SBL_VERSION_INDEX 7
> -#define SMEM_EXPECTED_VERSION 11
> +#define SMEM_GLOBAL_HEAP_VERSION 11
> +
> +/*
> + * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
> + * for the global heap. A new global partition is created from the global heap
> + * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
> + * set by the bootloader.
> + */

Please incorporate this paragraph in the larger comment at the top of
the file, so we keep that up to date.

[..]
> @@ -419,6 +430,7 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
> {
> unsigned long flags;
> int ret;
> + struct smem_partition_header *phdr;

Sorry for my OCD, but please maintain my reverse XMAS tree (put this
declaration first, as it's the longest).

>
> if (!__smem)
> return -EPROBE_DEFER;
[..]
> @@ -604,21 +631,61 @@ int qcom_smem_get_free_space(unsigned host)
>
> static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
> {
> + struct smem_header *header;
> __le32 *versions;
> - size_t size;
>
> - versions = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, &size);
> - if (IS_ERR(versions)) {
> - dev_err(smem->dev, "Unable to read the version item\n");
> - return -ENOENT;
> + header = smem->regions[0].virt_base;
> + versions = header->version;
> +
> + return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
> +}

I would prefer if you split the change of this function into its own
patch, just to clarify that it's unrelated to the new version support.

> +
> +static int qcom_smem_set_global_partition(struct qcom_smem *smem,
> + struct smem_ptable_entry *entry)
> +{
> + struct smem_partition_header *header;
> + u32 host0, host1;
> +
> + if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
> + dev_err(smem->dev, "Invalid entry for gloabl partition\n");
> + return -EINVAL;
> }
>
> - if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) {
> - dev_err(smem->dev, "Version item is too small\n");
> + if (smem->global_partition) {
> + dev_err(smem->dev, "Already found the global partition\n");
> return -EINVAL;
> }
> + header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
> + host0 = le16_to_cpu(header->host0);
> + host1 = le16_to_cpu(header->host1);
>
> - return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
> + if (memcmp(header->magic, SMEM_PART_MAGIC,
> + sizeof(header->magic))) {
> + dev_err(smem->dev, "Gloal partition has invalid magic\n");
> + return -EINVAL;
> + }
> +
> + if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
> + dev_err(smem->dev, "Global partition hosts are invalid\n");
> + return -EINVAL;
> + }
> +
> + if (header->size != entry->size) {

This happens to work, because they are both in the same endian. But
please sprinkle some le32_to_cpu() here as well.

> + dev_err(smem->dev, "Global partition has invalid size\n");
> + return -EINVAL;
> + }
> +
> + if (le32_to_cpu(header->offset_free_uncached) >
> + le32_to_cpu(header->size)) {

Consider using local variables in favor of wrapping lines.

> + dev_err(smem->dev,
> + "Global partition has invalid free pointer\n");
> + return -EINVAL;
> + }
> +
> + smem->global_partition = header;
> + smem->global_cacheline = le32_to_cpu(entry->cacheline);
> +
> + return 0;
> }
>
> static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
> @@ -647,6 +714,12 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
> host0 = le16_to_cpu(entry->host0);
> host1 = le16_to_cpu(entry->host1);
>
> + if (host0 == SMEM_GLOBAL_HOST && host0 == host1) {
> + if (qcom_smem_set_global_partition(smem, entry))
> + return -EINVAL;
> + continue;
> + }
> +

As you're not able to leverage any of the checks from this loop I think
it's cleaner to duplicate the traversal of the partition table in both
functions and call the "search for global partition" directly from
probe - if the version indicates there should be one.

> if (host0 != local_host && host1 != local_host)
> continue;
>
> @@ -782,7 +855,10 @@ static int qcom_smem_probe(struct platform_device *pdev)
> }
>
> version = qcom_smem_get_sbl_version(smem);
> - if (version >> 16 != SMEM_EXPECTED_VERSION) {
> + switch (version >> 16) {
> + case SMEM_GLOBAL_PART_VERSION:
> + case SMEM_GLOBAL_HEAP_VERSION:

As Arun pointed out, there should be a "break" here.

> + default:
> dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
> return -EINVAL;
> }

Regards,
Bjorn