All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tomas Hlavacek <tmshlvck@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v9] [RFC] Add dmmalloc module for DM.
Date: Thu, 25 Oct 2012 21:16:15 +0200	[thread overview]
Message-ID: <CAEB7QLC+A8PAJdtiPoGVn8beqUggunD0DSPF1h4TYaQ0nC9JZg@mail.gmail.com> (raw)
In-Reply-To: <CALButC+kbn-EVE+1odTw-5ebuOPeM_8fxpteLMRKa=g==OGeTg@mail.gmail.com>

Hello Graeme,

On Thu, Oct 25, 2012 at 3:40 AM, Graeme Russ <graeme.russ@gmail.com> wrote:

>> diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h
>> index 2b9af93..9045829 100644
>> --- a/arch/arm/include/asm/global_data.h
>> +++ b/arch/arm/include/asm/global_data.h
>> @@ -82,6 +82,9 @@ typedef       struct  global_data {
>>         unsigned long   post_log_res; /* success of POST test */
>>         unsigned long   post_init_f_time; /* When post_init_f started */
>>  #endif
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>> +       void            *early_heap;    /* heap for early_malloc */
>> +#endif
>
> Why not early_heap_header *early_heap; ?
>

It might be.

Actually it is a good point because I am using 3 different ways of
dealing with addresses: 1) struct early_heap header * or struct
early_block_header * - I am using this when I want to access members
of the stucture or compute address past the structure (which is where
the heap or block starts); 2) phys_addr_t - which is plain integer and
I use this for simple computations when I do not want to worry about
pointer arithmetic; 3) void * when I have just plain address,
especially when I want to pass an addres among logical parts of the
mallocator or outside. This may a bit controversial and perhaps I
should replace it by specific strucutre pointers internally.

I am unable to decide: Should I remove struct early_heap_header from
dmmalloc.h making it publicly unavailable or should I rather change
the void * to struct early_heap_header * in the GD structure? What do
you think is better?

>  diff --git a/common/Makefile b/common/Makefile
>> index fdfead7..bfb4d7a 100644
>> --- a/common/Makefile
>> +++ b/common/Makefile
>> @@ -209,6 +209,7 @@ COBJS-y += dlmalloc.o
>>  COBJS-y += image.o
>>  COBJS-y += memsize.o
>>  COBJS-y += stdio.o
>> +COBJS-$(CONFIG_DM) += dmmalloc.o
>
> COBJS-$(CONFIG_SYS_EARLY_MALLOC) += dmmalloc.o ?

Oh yes, now it is redundant to #ifdef CONFIG_SYS_EARLY_MALLOC inside
the dmmalloc.c file. I had a plan to extend the dmmalloc.c file by
relocation routines and then it would make sense. But I will shufle
the code a bit in the v10 anyway and we will see if the #ifdefs can
still be reduced.

>> +
>> +#include <common.h> /* for ROUND_UP */
>> +#include <asm/u-boot.h>
>> +#include <asm/global_data.h> /* for gd_t and gd */
>> +#include <asm/types.h> /* for phys_addr_t and size_addt_t */
>> +
>> +#include <dmmalloc.h>
>> +#include <malloc.h>
>> +
>> +#include <linux/compiler.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>
> If you use COBJS-$(CONFIG_SYS_EARLY_MALLOC) += dmmalloc.o in the Makefile,
> you can drop this #ifdef

Yes, that is redundant now.

>
>> +__weak struct early_heap_header *early_brk(size_t size)
>> +{
>> +       struct early_heap_header *h;
>> +       struct early_block_header *b;
>> +
>> +       if (gd->early_heap != NULL)
>> +               return NULL;
>> +
>> +       h = (struct early_heap_header *)CONFIG_SYS_EARLY_HEAP_ADDR;
>> +       b = (struct early_block_header *)(h + 1);
>
> Hmmm, does this really work? I would have thought:
>
> b = (struct early_block_header *)(h + sizeof(struct early_heap_header));
>
> but I could be mistaken

It seems that it works as it is (at least I wrote bunch of tests and I
inspected resulting heaps and it was all right). I believe that since
h is a pointer to the struct early_heap_header then pointer arithmetic
is in effect and h+1 actually means "next element in the array of
struct early_heap_header". Which is the address past the header that
equals beginning of the heap data block. (?)


>> +int early_malloc_active(void)
>> +{
>> +       return ((gd->flags & GD_FLG_RELOC) != GD_FLG_RELOC);
>> +}
>
> I think we need another flag - GD_FLG_RELOC gets set before the permanent
> heap is initialised, so there is a window of opportunity where things may
> break

Well, as I wrote in the commit message - this is only a temporary
implementation. I suppose I am going to change this when we have more
coarse initialization flags wired into DM (which I believe we are
going to have it anyway). So now I am just working around "forward
dependency" here.

>
>> +
>> +void early_heap_dump(struct early_heap_header *h)
>> +{
>> +       struct early_block_header *b;
>> +
>> +       debug("heap: h=%p, h->size=%d\n", h, h->size);
>> +
>> +       b = (struct early_block_header *)(h+1);
>> +       while ((phys_addr_t)b + sizeof(struct early_block_header)
>> +                       < (phys_addr_t)h + h->size) {
>> +               debug("block: h=%p h->size=%d b=%p b->size=%d b->(used)=%d\n",
>> +                               h, h->size, b, BLOCK_SIZE(b->size),
>> +                               BLOCK_USED(b->size));
>> +               assert(BLOCK_SIZE(b->size) > 0);
>> +               b = (struct early_block_header *)((phys_addr_t)b +
>> +                               sizeof(struct early_block_header) +
>> +                               BLOCK_SIZE(b->size));
>> +       }
>> +       debug("--- heap dump end ---\n");
>> +}
>
> Nice touch, but could we just iterate through all ealry heap chunks starting
> from gd->early_heap?

Or I can have two functions. One heap specific and one for all heaps.
I think both might be useful when somebody needs to debug early_malloc
or memory usage etc. in the early stage. Thanks.

>> +
>> +#ifndef __INCLUDE_DMMALLOC_H
>> +#define __INCLUDE_DMMALLOC_H
>> +
>> +#include <config.h>
>> +#include <linux/stddef.h> /* for size_t */
>> +#include <malloc.h>
>> +
>> +#if (!defined(CONFIG_SYS_EARLY_HEAP_ADDR)) || \
>> +       (!defined(CONFIG_SYS_EARLY_HEAP_SIZE))
>> +#undef CONFIG_SYS_EARLY_MALLOC
>> +#endif /* CONFIG_SYS_EARLY_HEAP_ADDR */
>
> If a board implements early_brk() using non-fixed a address and/or size
> then this is going to cause trouble

Right, I will drop this.

>
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>
> I see no need for the #ifdef
>
>> +struct early_heap_header {
>> +       size_t size;
>> +       void *early_heap_next;
>> +};
>> +
>> +struct early_block_header {
>> +       size_t size;
>> +};
>> +
>
>> +#define BLOCK_USED_FLAG 0x80000000
>> +#define BLOCK_SIZE(size) (size & (~BLOCK_USED_FLAG))
>> +#define BLOCK_USED(size) ((size & BLOCK_USED_FLAG) == BLOCK_USED_FLAG)
>> +#define BLOCK_FREE(size) (!BLOCK_USED(size))
>> +#define BLOCK_SET_FREE(size) BLOCK_SIZE(size)
>> +#define BLOCK_SET_USED(size) (size | BLOCK_USED_FLAG)
>
> Hmmm, I'm not sure what the general policy is about where to put 'stuff'
> that is only used by the implementation and does not 'need' to be made
> publically available. i.e. I don't know if these #defines and the
> early_block_header struct belong here or in the .c file...

You are right, I will put it to the .c.

>
>> +
>> +/**
>> + * early_brk() - obtain address of the heap
>> + * @size:      Minimal size of the new early heap to be allocated.
>> + *
>> + * Function returns a new heap pointer.
>> + *
>> + * Allocate and initialize early_heap at least size bytes long.
>> + * This function can be platform dependent or board dependent but sensible
>> + * default is provided.
>> + */
>> +struct early_heap_header *early_brk(size_t size);
>> +
>> +/**
>> + * early_malloc() - malloc operating on the early_heap(s)
>> + * @size:      Size in bytes.
>> + *
>> + * Function returns a pointer to the allocated block.
>> + */
>> +void *early_malloc(size_t size);
>> +
>> +/**
>> + * early_free() - free operating on the early_heap(s)
>> + * @addr:      Pointer to the allocated block to be released.
>> + */
>> +void early_free(void *addr);
>> +
>> +/**
>> + * early_malloc_active() - indicate if the early mallocator is active
>> + *
>> + * Function returns true when the early_malloc and early_free are used and
>> + * false otherwise.
>> + */
>> +int early_malloc_active(void);
>> +
>> +/**
>> + * early_heap_dump() - print blocks contained in an early_heap
>> + * @h:         Address of the early heap.
>> + */
>> +void early_heap_dump(struct early_heap_header *h);
>> +
>> +#endif /* CONFIG_SYS_EARLY_MALLOC */
>> +
>> +#ifdef CONFIG_DM
>> +
>> +/*
>> + * DM versions of malloc* functions. In early init it calls early_malloc.
>> + * It wraps around normal malloc* functions afterwards.
>> + */
>
> I don't think these _need_ to be inline functions - The compiler should be
> smart enough no (non-)inline them as appropriate.

Yes. Now I am thinking about pulling all this into the .c file and
making the functions non-static. Actually, this "static inline"
evolved from really simple "if(...) early_malloc() else malloc()", but
now it is not always straight-forward and even more future extensions
are expected to these functions due to relocation-related code.

>
>> +
>> +/**
>> + * dmmalloc() - malloc working seamlessly in early as well as in RAM stages
>> + * @size:      Size of the block to be allocated.
>> + *
>> + * Function returns an address of the newly allocated block when successful
>> + * or NULL otherwise.
>> + */
>> +static inline void *dmmalloc(size_t size)
>> +{
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>> +       if (early_malloc_active())
>> +               return early_malloc(size);
>> +#endif /* CONFIG_SYS_EARLY_MALLOC */
>> +       return malloc(size);
>> +}
>> +
>> +/**
>> + * dmfree() - free working seamlessly in early as well as in RAM stages
>> + * @ptr:       Pointer to the allocated block to be released.
>> + */
>> +static inline void dmfree(void *ptr)
>> +{
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>> +       if (early_malloc_active()) {
>> +               early_free(ptr);
>> +               return;
>> +       }
>> +#endif /* CONFIG_SYS_EARLY_MALLOC */
>> +       free(ptr);
>> +}
>> +
>> +/**
>> + * dmcalloc() - calloc working seamlessly in early as well as in RAM stages
>> + * @n:         Number of elements to be allocated.
>> + * @elem_size: Size of elements to be allocated.
>> + *
>> + * Function returns a pointer to newly the allocated area (n*elem_size) long.
>> + */
>> +static inline void *dmcalloc(size_t n, size_t elem_size)
>> +{
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>> +       char *addr;
>> +       int size = elem_size * n;
>> +
>> +       if (early_malloc_active()) {
>> +               addr = early_malloc(size);
>> +               memset(addr, 0, size);
>> +               return addr;
>> +       }
>> +#endif /* CONFIG_SYS_EARLY_MALLOC */
>> +       return calloc(n, elem_size);
>> +}
>> +
>> +/**
>> + * dmrealloc() - realloc working seamlessly in early as well as in RAM stages
>> + * @oldaddr:   Pointer to the old memory block.
>> + * @bytes:     New size to of the block to be reallocated.
>> + *
>> + * Function returns an address of the newly allocated block when successful
>> + * or NULL otherwise.
>> + *
>> + * Data are copied from the block specified by oldaddr to the new block.
>> + */
>> +static inline void *dmrealloc(void *oldaddr, size_t bytes)
>> +{
>> +#ifdef CONFIG_SYS_EARLY_MALLOC
>> +       char *addr;
>> +       struct early_block_header *h;
>> +       if (early_malloc_active()) {
>> +               addr = dmmalloc(bytes);
>> +               if (addr == NULL)
>> +                       return NULL;
>> +
>> +               h = BLOCK_HEADER(oldaddr);
>> +               if (BLOCK_FREE(h->size))
>> +                       return NULL;
>> +
>> +               if (bytes > BLOCK_SIZE(h->size))
>> +                       bytes = BLOCK_SIZE(h->size);
>> +
>> +               memcpy(addr, oldaddr, bytes);
>> +               dmfree(oldaddr);
>> +               return addr;
>> +       }
>> +#endif /* CONFIG_SYS_EARLY_MALLOC */
>> +       return realloc(oldaddr, bytes);
>> +}
>
> Hmmm, we have a very intersting corner case to deal with. What if we use
> dmrealloc() to move allocated data from the early malloc pool to the final
> malloc pool?
>
> At some point, we need to assume the developer is only ever going to pass
> early malloc'd memory to dmrealloc()

Good point! The current code would do the job assuming that the
early_malloc can still access the proper early_heap (or a copy, but in
that case some additional magic is needed) and the real malloc is
already initialized.

As you can see I am still sticking with the double-copy method. It is
maybe due to lack of insight. But the discussion here was not
absolutely conclusive last time. I have even some experimental code
(not ready for submitting at all) for double copy method but I would
prefer to discuss it separately when the early_malloc() is done.

>
> The other option is to use the gd->flags...
>
> Define two flags - something like:
>
> GD_FLG_HEAP_INIT -> Final heap has been initialised
> GD_FLG_EH_DONE    -> free(), realloc() refer to final heap
>
> (I don't like the names, I'm just not up to thinking of anything better)
>
> This way we can use dmalloc() prior to the heap being initialised and then
> set GD_FLG_HEAP_INIT. Once GD_FLG_HEAP_INIT has been set, do a bunch of
> dmrealloc() calls to essentially move the data into the permanent heap (of
> course pointers int the structures need to be fixed up by the drivers) and
> finally set GD_FLG_EH_DONE (and call early_heap_dump)
>
> One problem I see is what happens of you call malloc() and free() on the
> same block between the setting of GD_FLG_HEAP_INIT and GD_FLG_EH_DONE? The
> code will try to reference the block as if it is in the early heap, but it
> won't be.
>
> One solution is, on detection of GD_FLG_HEAP_INIT being set, dmfree() and
> dmrealloc() could search the early heap chunks instead of just assuming
> that the referenced block is in the early heap (if GD_FLG_HEAP_INIT has not
> been set, it will be safe to assume the memory is on the early heap)

Sure we will have that flags. But I think we can use them as well for
switching from DM driver instance list to tree for example. Or other
way around: I can use DM flags for early_malloc. Therefore I would
like to synchronize with DM cores for PCI and another low-level things
which are certainly going to start in early stage. It would be best to
use the same flags and switch on/off early_malloc based on DM internal
state.

Best regards,
Tomas

  reply	other threads:[~2012-10-25 19:16 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-27 12:12 [U-Boot] [PATCH 1/1] [RFC] DM: early_malloc for DM added Tomas Hlavacek
2012-08-27 12:18 ` Marek Vasut
2012-08-27 12:37   ` Tomas Hlavacek
2012-08-27 12:42 ` [U-Boot] [PATCHv2 " Tomas Hlavacek
2012-08-27 23:02   ` Graeme Russ
2012-08-28  0:11     ` Graeme Russ
2012-08-28  0:18     ` Graeme Russ
2012-09-18  7:13 ` [U-Boot] [PATCHv4] " Tomas Hlavacek
2012-09-18 10:57   ` Marek Vasut
2012-09-18 23:29     ` Graeme Russ
2012-09-18 23:33       ` Marek Vasut
2012-09-18 23:44         ` Graeme Russ
2012-09-18 23:53           ` Marek Vasut
2012-09-19 18:29       ` Tom Rini
2012-09-22  0:37       ` Tomas Hlavacek
2012-09-18 23:25   ` Graeme Russ
2012-09-22  0:25 ` [U-Boot] [PATCH v5] [RFC] " Tomas Hlavacek
2012-09-22  0:28   ` Marek Vasut
2012-09-22  7:52     ` Tomas Hlavacek
2012-09-22 13:19       ` Marek Vasut
2012-09-22 22:09 ` [U-Boot] [PATCH v6] " Tomas Hlavacek
2012-09-23 13:06   ` Graeme Russ
2012-09-23 15:30     ` Tomas Hlavacek
2012-09-23 15:38 ` [U-Boot] [PATCH v7] " Tomas Hlavacek
2012-09-23 16:15 ` [U-Boot] [PATCH v8] " Tomas Hlavacek
2012-09-23 16:32   ` Wolfgang Denk
2012-09-23 16:47     ` Tomas Hlavacek
2012-09-24 21:48       ` Tom Rini
2012-09-23 23:11   ` Marek Vasut
2012-09-24 14:16     ` Tomas Hlavacek
2012-09-24 14:19       ` Marek Vasut
2012-09-25  0:37         ` Graeme Russ
2012-09-25  8:43           ` Tomas Hlavacek
2012-09-25  9:09             ` Graeme Russ
2012-09-25 23:04               ` Graeme Russ
2012-09-26 10:16                 ` Tomas Hlavacek
2012-09-26 23:03                   ` Graeme Russ
2012-09-24  0:00   ` Graeme Russ
2012-09-24  0:35     ` Tomas Hlavacek
2012-09-24  0:46       ` Graeme Russ
2012-10-24 23:49 ` [U-Boot] [PATCH v9] [RFC] Add dmmalloc module for DM Tomas Hlavacek
2012-10-25  1:40   ` Graeme Russ
2012-10-25 19:16     ` Tomas Hlavacek [this message]
2012-10-25 23:04       ` Graeme Russ
2012-10-28 23:20 ` [U-Boot] [PATCH v10] " Tomas Hlavacek
2013-11-05 15:26   ` Mateusz Zalega
2013-11-05 17:17     ` Tom Rini
2013-11-05 17:26       ` Mateusz Zalega

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAEB7QLC+A8PAJdtiPoGVn8beqUggunD0DSPF1h4TYaQ0nC9JZg@mail.gmail.com \
    --to=tmshlvck@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.