Subject: Re: [PATCH 5/6] powerpc/powernv: Create OPAL sglist helper functions and fix endian issues

From: Vasant Hegde <[email-masked]>
Date: Tue, 22 Apr 2014 14:46:10 +0530

On 04/22/2014 10:31 AM, Anton Blanchard wrote:
> We have two copies of code that creates an OPAL sg list. Consolidate
> these into a common set of helpers and fix the endian issues.
>
> The flash interface embedded a version number in the num_entries
> field, whereas the dump interface did did not. Since versioning
> wasn't added to the flash interface and it is impossible to add
> this in a backwards compatible way, just remove it.
>
> Signed-off-by: Anton Blanchard <[email-masked]>
> ---
>   arch/powerpc/include/asm/opal.h             |  14 ++--
>   arch/powerpc/platforms/powernv/opal-dump.c  |  81 +--------------------
>   arch/powerpc/platforms/powernv/opal-flash.c | 106 +---------------------------
>   arch/powerpc/platforms/powernv/opal.c       |  63 +++++++++++++++++
>   4 files changed, 76 insertions(+), 188 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
> index 1a752ac..afb0fed 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -41,14 +41,14 @@ struct opal_takeover_args {
>    * size except the last one in the list to be as well.
>    */
>   struct opal_sg_entry {
> -	void    *data;
> -	long    length;
> +	__be64 data;
> +	__be64 length;
>   };
>
> -/* sg list */
> +/* SG list */
>   struct opal_sg_list {
> -	unsigned long num_entries;
> -	struct opal_sg_list *next;
> +	__be64 length;
> +	__be64 next;
>   	struct opal_sg_entry entry[];
>   };
>
> @@ -929,6 +929,10 @@ extern int opal_resync_timebase(void);
>
>   extern void opal_lpc_init(void);
>
> +struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
> +					     unsigned long vmalloc_size);
> +void opal_free_sg_list(struct opal_sg_list *sg);
> +
>   #endif /* __ASSEMBLY__ */
>
>   #endif /* __OPAL_H */
> diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c
> index b9827b0..f0b4724 100644
> --- a/arch/powerpc/platforms/powernv/opal-dump.c
> +++ b/arch/powerpc/platforms/powernv/opal-dump.c
> @@ -209,80 +209,6 @@ static struct kobj_type dump_ktype = {
>   	.default_attrs = dump_default_attrs,
>   };
>
> -static void free_dump_sg_list(struct opal_sg_list *list)
> -{
> -	struct opal_sg_list *sg1;
> -	while (list) {
> -		sg1 = list->next;
> -		kfree(list);
> -		list = sg1;
> -	}
> -	list = NULL;
> -}
> -
> -static struct opal_sg_list *dump_data_to_sglist(struct dump_obj *dump)
> -{
> -	struct opal_sg_list *sg1, *list = NULL;
> -	void *addr;
> -	int64_t size;
> -
> -	addr = dump->buffer;
> -	size = dump->size;
> -
> -	sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -	if (!sg1)
> -		goto nomem;
> -
> -	list = sg1;
> -	sg1->num_entries = 0;
> -	while (size > 0) {
> -		/* Translate virtual address to physical address */
> -		sg1->entry[sg1->num_entries].data =
> -			(void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
> -
> -		if (size > PAGE_SIZE)
> -			sg1->entry[sg1->num_entries].length = PAGE_SIZE;
> -		else
> -			sg1->entry[sg1->num_entries].length = size;
> -
> -		sg1->num_entries++;
> -		if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
> -			sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -			if (!sg1->next)
> -				goto nomem;
> -
> -			sg1 = sg1->next;
> -			sg1->num_entries = 0;
> -		}
> -		addr += PAGE_SIZE;
> -		size -= PAGE_SIZE;
> -	}
> -	return list;
> -
> -nomem:
> -	pr_err("%s : Failed to allocate memory\n", __func__);
> -	free_dump_sg_list(list);
> -	return NULL;
> -}
> -
> -static void sglist_to_phy_addr(struct opal_sg_list *list)
> -{
> -	struct opal_sg_list *sg, *next;
> -
> -	for (sg = list; sg; sg = next) {
> -		next = sg->next;
> -		/* Don't translate NULL pointer for last entry */
> -		if (sg->next)
> -			sg->next = (struct opal_sg_list *)__pa(sg->next);
> -		else
> -			sg->next = NULL;
> -
> -		/* Convert num_entries to length */
> -		sg->num_entries =
> -			sg->num_entries * sizeof(struct opal_sg_entry) + 16;
> -	}
> -}
> -
>   static int64_t dump_read_info(uint32_t *id, uint32_t *size, uint32_t *type)
>   {
>   	int rc;
> @@ -314,15 +240,12 @@ static int64_t dump_read_data(struct dump_obj *dump)
>   	}
>
>   	/* Generate SG list */
> -	list = dump_data_to_sglist(dump);
> +	list = opal_vmalloc_to_sg_list(dump->buffer, dump->size);
>   	if (!list) {
>   		rc = -ENOMEM;
>   		goto out;
>   	}
>
> -	/* Translate sg list addr to real address */
> -	sglist_to_phy_addr(list);
> -
>   	/* First entry address */
>   	addr = __pa(list);
>
> @@ -341,7 +264,7 @@ static int64_t dump_read_data(struct dump_obj *dump)
>   			__func__, dump->id);
>

Shouldn't we convert addr and id before passing to opal_dump_read() here ?

>   	/* Free SG list */
> -	free_dump_sg_list(list);
> +	opal_free_sg_list(list);
>
>   out:
>   	return rc;
> diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c
> index 714ef97..0ae9e5f 100644
> --- a/arch/powerpc/platforms/powernv/opal-flash.c
> +++ b/arch/powerpc/platforms/powernv/opal-flash.c
> @@ -79,9 +79,6 @@
>   /* XXX: Assume candidate image size is <= 1GB */
>   #define MAX_IMAGE_SIZE	0x40000000
>
> -/* Flash sg list version */
> -#define SG_LIST_VERSION (1UL)
> -
>   /* Image status */
>   enum {
>   	IMAGE_INVALID,
> @@ -268,93 +265,11 @@ static ssize_t manage_store(struct kobject *kobj,
>   }
>
>   /*
> - * Free sg list
> - */
> -static void free_sg_list(struct opal_sg_list *list)
> -{
> -	struct opal_sg_list *sg1;
> -	while (list) {
> -		sg1 = list->next;
> -		kfree(list);
> -		list = sg1;
> -	}
> -	list = NULL;
> -}
> -
> -/*
> - * Build candidate image scatter gather list
> - *
> - * list format:
> - *   -----------------------------------
> - *  |  VER (8) | Entry length in bytes  |
> - *   -----------------------------------
> - *  |  Pointer to next entry            |
> - *   -----------------------------------
> - *  |  Address of memory area 1         |
> - *   -----------------------------------
> - *  |  Length of memory area 1          |
> - *   -----------------------------------
> - *  |   .........                       |
> - *   -----------------------------------
> - *  |   .........                       |
> - *   -----------------------------------
> - *  |  Address of memory area N         |
> - *   -----------------------------------
> - *  |  Length of memory area N          |
> - *   -----------------------------------
> - */
> -static struct opal_sg_list *image_data_to_sglist(void)
> -{
> -	struct opal_sg_list *sg1, *list = NULL;
> -	void *addr;
> -	int size;
> -
> -	addr = image_data.data;
> -	size = image_data.size;
> -
> -	sg1 = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -	if (!sg1)
> -		return NULL;
> -
> -	list = sg1;
> -	sg1->num_entries = 0;
> -	while (size > 0) {
> -		/* Translate virtual address to physical address */
> -		sg1->entry[sg1->num_entries].data =
> -			(void *)(vmalloc_to_pfn(addr) << PAGE_SHIFT);
> -
> -		if (size > PAGE_SIZE)
> -			sg1->entry[sg1->num_entries].length = PAGE_SIZE;
> -		else
> -			sg1->entry[sg1->num_entries].length = size;
> -
> -		sg1->num_entries++;
> -		if (sg1->num_entries >= SG_ENTRIES_PER_NODE) {
> -			sg1->next = kzalloc(PAGE_SIZE, GFP_KERNEL);
> -			if (!sg1->next) {
> -				pr_err("%s : Failed to allocate memory\n",
> -				       __func__);
> -				goto nomem;
> -			}
> -
> -			sg1 = sg1->next;
> -			sg1->num_entries = 0;
> -		}
> -		addr += PAGE_SIZE;
> -		size -= PAGE_SIZE;
> -	}
> -	return list;
> -nomem:
> -	free_sg_list(list);
> -	return NULL;
> -}
> -
> -/*
>    * OPAL update flash
>    */
>   static int opal_flash_update(int op)
>   {
> -	struct opal_sg_list *sg, *list, *next;
> +	struct opal_sg_list *list;
>   	unsigned long addr;
>   	int64_t rc = OPAL_PARAMETER;
>
> @@ -364,30 +279,13 @@ static int opal_flash_update(int op)
>   		goto flash;
>   	}
>
> -	list = image_data_to_sglist();
> +	list = opal_vmalloc_to_sg_list(image_data.data, image_data.size);
>   	if (!list)
>   		goto invalid_img;
>
>   	/* First entry address */
>   	addr = __pa(list);
>
> -	/* Translate sg list address to absolute */
> -	for (sg = list; sg; sg = next) {
> -		next = sg->next;
> -		/* Don't translate NULL pointer for last entry */
> -		if (sg->next)
> -			sg->next = (struct opal_sg_list *)__pa(sg->next);
> -		else
> -			sg->next = NULL;
> -
> -		/*
> -		 * Convert num_entries to version/length format
> -		 * to satisfy OPAL.
> -		 */
> -		sg->num_entries = (SG_LIST_VERSION << 56) |
> -			(sg->num_entries * sizeof(struct opal_sg_entry) + 16);
> -	}
> -
>   	pr_alert("FLASH: Image is %u bytes\n", image_data.size);
>   	pr_alert("FLASH: Image update requested\n");
>   	pr_alert("FLASH: Image will be updated during system reboot\n");
> diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
> index 17cfc70..360ad80c 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -638,3 +638,66 @@ void opal_shutdown(void)
>
>   /* Export this so that test modules can use it */
>   EXPORT_SYMBOL_GPL(opal_invalid_call);
> +
> +/* Convert a region of vmalloc memory to an opal sg list */
> +struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
> +					     unsigned long vmalloc_size)
> +{
> +	struct opal_sg_list *sg, *first = NULL;
> +	unsigned long i = 0;
> +
> +	sg = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!sg)
> +		goto nomem;
> +
> +	first = sg;
> +
> +	while (vmalloc_size > 0) {
> +		uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT;
> +		uint64_t length = min(vmalloc_size, PAGE_SIZE);
> +
> +		sg->entry[i].data = cpu_to_be64(data);
> +		sg->entry[i].length = cpu_to_be64(length);
> +		i++;
> +
> +		if (i >= SG_ENTRIES_PER_NODE) {
> +			struct opal_sg_list *next;
> +
> +			next = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +			if (!next)
> +				goto nomem;
> +
> +			sg->length = cpu_to_be64(
> +					i * sizeof(struct opal_sg_entry) + 16);
> +			i = 0;
> +			sg->next = cpu_to_be64(__pa(next));
> +			sg = next;
> +		}
> +
> +		vmalloc_addr += length;
> +		vmalloc_size -= length;
> +	}
> +
> +	sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16);
> +
> +	return first;
> +
> +nomem:
> +	pr_err("%s : Failed to allocate memory\n", __func__);
> +	opal_free_sg_list(first);
> +	return NULL;
> +}
> +
> +void opal_free_sg_list(struct opal_sg_list *sg)
> +{
> +	while (sg) {
> +		uint64_t next = be64_to_cpu(sg->next);
> +
> +		kfree(sg);
> +
> +		if (next)
> +			sg = __va(next);

This for this fix..

-Vasant

> +		else
> +			sg = NULL;
> +	}
> +}