[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [UNIKRAFT PATCH v2] lib/ukalloc: add ifmalloc compatibility interface



On 30.07.20, 23:53, "Hugo Lefeuvre" <Hugo.Lefeuvre@xxxxxxxxx> wrote:

    Hi Simon,
    
    thanks a lot for your comments, answers inline.
    
    regards,
    Hugo
    
    On Thu, 2020-07-30 at 18:20 +0200, Simon Kuenzer wrote:
    > 
    > Hey, I found to open questions. Let me know what you think.
    > 
    > Thanks,
    > 
    > Simon
    > 
    > On 30.07.20 16:48, Hugo Lefeuvre wrote:
    > > 
    > > 
    > > Add ifmalloc, the malloc compatibility interface. This interface
    > > implements
    > > a POSIX compliant allocation interface on top of a simple malloc()
    > > and
    > > free() interface. This interface is similar to ifpages, which does
    > > the same
    > > for palloc() and pfree().
    > > 
    > > ifmalloc will be used for the port of TLSF and tinyalloc which do
    > > not
    > > support (posix_)memalign().
    > > 
    > > Signed-off-by: Hugo Lefeuvre <hugo.lefeuvre@xxxxxxxxx>
    > > ---
    > > Changed since v1:
    > >   - update alloc.c file header
    > >   - use METADATA_IFMALLOC_SIZE_POW2 instead of sizeof(struct
    > > metadata_ifmalloc)
    > >   - add unlikely where appropriate
    > >   - use align < sizeof(void *) instead of (align % sizeof(void *))
    > > != 0
    > >   - minor optimizations and style-related changes
    > > 
    > >   lib/ukalloc/Config.uk               |   5 ++
    > >   lib/ukalloc/alloc.c                 | 148
    > > +++++++++++++++++++++++++++++++++++-
    > >   lib/ukalloc/exportsyms.uk           |   4 +
    > >   lib/ukalloc/include/uk/alloc.h      |   5 ++
    > >   lib/ukalloc/include/uk/alloc_impl.h |  27 +++++++
    > >   5 files changed, 186 insertions(+), 3 deletions(-)
    > > 
    > > diff --git a/lib/ukalloc/Config.uk b/lib/ukalloc/Config.uk
    > > index 0de7464..c965d08 100644
    > > --- a/lib/ukalloc/Config.uk
    > > +++ b/lib/ukalloc/Config.uk
    > > @@ -5,6 +5,11 @@ menuconfig LIBUKALLOC
    > >         select LIBUKDEBUG
    > >   
    > >   if LIBUKALLOC
    > > +       config LIBUKALLOC_IFMALLOC
    > > +               bool "Malloc compatibility interface"
    > > +               default n
    > > +               help
    > > +                       Provide helpers for allocators defining
    > > exclusively malloc and free
    > >         config LIBUKALLOC_IFSTATS
    > >                 bool "Statistics interface"
    > >                 default n
    > > diff --git a/lib/ukalloc/alloc.c b/lib/ukalloc/alloc.c
    > > index 127bc6f..160a0a4 100644
    > > --- a/lib/ukalloc/alloc.c
    > > +++ b/lib/ukalloc/alloc.c
    > > @@ -1,8 +1,10 @@
    > >   /* SPDX-License-Identifier: BSD-3-Clause */
    > >   /*
    > >    * Authors: Florian Schmidt <florian.schmidt@xxxxxxxxx>
    > > + *          Hugo Lefeuvre <hugo.lefeuvre@xxxxxxxxx>
    > >    *
    > > - * Copyright (c) 2017, NEC Europe Ltd., NEC Corporation. All
    > > rights reserved.
    > > + * Copyright (c) 2017-2020, NEC Laboratories Europe GmbH, NEC
    > > Corporation,
    > > + *                          All rights reserved.
    > >    *
    > >    * Redistribution and use in source and binary forms, with or
    > > without
    > >    * modification, are permitted provided that the following
    > > conditions
    > > @@ -28,8 +30,6 @@
    > >    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
    > > OTHERWISE)
    > >    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    > > ADVISED OF THE
    > >    * POSSIBILITY OF SUCH DAMAGE.
    > > - *
    > > - * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
    > >    */
    > >   
    > >   /* This is a very simple, naive implementation of malloc.
    > > @@ -52,6 +52,7 @@
    > >   #include <uk/essentials.h>
    > >   #include <uk/assert.h>
    > >   #include <uk/arch/limits.h>
    > > +#include <uk/arch/lcpu.h>
    > >   
    > >   #define size_to_num_pages(size) \
    > >         (ALIGN_UP((unsigned long)(size), __PAGE_SIZE) /
    > > __PAGE_SIZE)
    > > @@ -267,6 +268,147 @@ int uk_posix_memalign_ifpages(struct uk_alloc
    > > *a,
    > >         return 0;
    > >   }
    > >   
    > > +#if CONFIG_LIBUKALLOC_IFMALLOC
    > > +
    > > +struct metadata_ifmalloc {
    > > +       size_t  size;
    > > +       void    *base;
    > > +};
    > > +
    > > +#define METADATA_IFMALLOC_SIZE_POW2 16
    > > +UK_CTASSERT(!(sizeof(struct metadata_ifmalloc) >
    > > METADATA_IFMALLOC_SIZE_POW2));
    > > +
    > > +static struct metadata_ifmalloc *uk_get_metadata_ifmalloc(const
    > > void *ptr)
    > > +{
    > > +       return (struct metadata_ifmalloc *)((uintptr_t) ptr -
    > > +               METADATA_IFMALLOC_SIZE_POW2);
    > > +}
    > > +
    > > +static size_t uk_getmallocsize_ifmalloc(const void *ptr)
    > > +{
    > > +       struct metadata_ifmalloc *metadata =
    > > uk_get_metadata_ifmalloc(ptr);
    > > +       return (size_t) ((uintptr_t) metadata->base + metadata-
    > > > 
    > > > size -
    > > +                        (uintptr_t) ptr);
    > > +}
    > > +
    > > +void uk_free_ifmalloc(struct uk_alloc *a, void *ptr)
    > > +{
    > > +       struct metadata_ifmalloc *metadata;
    > > +
    > > +       UK_ASSERT(a);
    > > +       UK_ASSERT(a->free_backend);
    > > +       if (!ptr)
    > > +               return;
    > > +
    > > +       metadata = uk_get_metadata_ifmalloc(ptr);
    > > +       a->free_backend(a, metadata->base);
    > > +}
    > > +
    > > +void *uk_malloc_ifmalloc(struct uk_alloc *a, size_t size)
    > > +{
    > > +       struct metadata_ifmalloc *metadata;
    > > +       size_t realsize = size + METADATA_IFMALLOC_SIZE_POW2;
    > > +       void *ptr;
    > > +
    > > +       UK_ASSERT(a);
    > > +       UK_ASSERT(a->malloc_backend);
    > > +
    > > +       /* check for overflow */
    > > +       if (unlikely(realsize < size))
    > > +               return NULL;
    > > +
    > > +       ptr = a->malloc_backend(a, realsize);
    > > +       if (!ptr)
    > > +               return NULL;
    > > +
    > > +       metadata = ptr;
    > > +       metadata->size = realsize;
    > > +       metadata->base = ptr;
    > > +
    > > +       return (void *) ((uintptr_t) ptr +
    > > METADATA_IFMALLOC_SIZE_POW2);
    > > +}
    > > +
    > > +void *uk_realloc_ifmalloc(struct uk_alloc *a, void *ptr, size_t
    > > size)
    > > +{
    > > +       void *retptr;
    > > +       size_t mallocsize;
    > > +
    > > +       UK_ASSERT(a);
    > > +       if (!ptr)
    > > +               return uk_malloc_ifmalloc(a, size);
    > > +
    > > +       if (ptr && !size) {
    > > +               uk_free_ifmalloc(a, ptr);
    > > +               return NULL;
    > > +       }
    > > +
    > > +       retptr = uk_malloc_ifmalloc(a, size);
    > > +       if (!retptr)
    > > +               return NULL;
    > > +
    > > +       mallocsize = uk_getmallocsize_ifmalloc(ptr);
    > > +
    > > +       memcpy(retptr, ptr, MIN(size, mallocsize));
    > > +
    > > +       uk_free_ifmalloc(a, ptr);
    > > +       return retptr;
    > > +}
    > > +
    > > +int uk_posix_memalign_ifmalloc(struct uk_alloc *a,
    > > +                                    void **memptr, size_t align,
    > > size_t size)
    > > +{
    > > +       struct metadata_ifmalloc *metadata;
    > > +       size_t realsize, padding;
    > > +       uintptr_t intptr;
    > > +
    > > +       UK_ASSERT(a);
    > > +       if (((align - 1) & align) != 0
    > > +           || align < sizeof(void *))
    > > +               return EINVAL;
    > > +
    > > +       if (!size) {
    > > +               *memptr = NULL;
    > Should we touch *memptr in case of failures? You seem not to do it
    > in 
    > the other cases.
    > https://man7.org/linux/man-pages/man3/posix_memalign.3.html
    
    Taking a look at https://pubs.opengroup.org/onlinepubs/9699919799/funct
    ions/posix_memalign.html, specifically:
    
    "If size is 0, either:
    
     * posix_memalign() shall not attempt to allocate any space, in which
    case either an implementation-defined error number shall be returned,
    or zero shall be returned with a null pointer returned in memptr, or
    
     * posix_memalign() shall attempt to allocate some space and, if the
    allocation succeeds, zero shall be returned and a pointer to the
    allocated space shall be returned in memptr. The application shall
    ensure that the pointer is not used to access an object."
    
    Indeed, I prefer to return an error code instead of setting memptr and
    returning zero. My fear is that some users might dereference memptr
    after observing a zero return value. Do you think EINVAL is fine in
    this case? In this case we could just remove `*memptr = NULL;`.
    
    Note that this bug was already present in uk_posix_memalign_ifpages, I
    intentionally reproduced it in the ifmalloc wrapper. It was introduced
    in https://github.com/unikraft/unikraft/commit/a323fbd67ac3cb21139633b3
    00a024538167493e
    
Good point. Personally, I prefer returning an error and not touching memptr,
since we have choice. That feels a bit more well-defined than the other 
behavior.
In case we will come across while porting an application that expect the other
behavior, we may think of adding a menu config option to switch between them.
I really hope it is rare that someone calls posix_memalign() with size 0...

Can you add a brief comment that two described behavior exist for size = 0?
In order to keep uk_posix_memalign_ifpages() equivalent, can you remove
setting memptr also there for this case? With having the comment it is
fine to do this in the same patch.

    > > +               return EINVAL;
    > > +}      
    > > +
    > > +       /* Store size information preceeding the memory block.
    > > Since we return
    > > +        * pointers aligned at `align` we need to reserve at least
    > > that much
    > > +        * space for the size information.
    > > +        */
    > > +       if (align < METADATA_IFMALLOC_SIZE_POW2) {
    > > +               align = METADATA_IFMALLOC_SIZE_POW2;
    > > +               padding = 0;
    > > +       } else {
    > > +               padding = METADATA_IFMALLOC_SIZE_POW2;
    > > +       }
    > > +
    > > +       realsize = size + padding + align;
    > > +
    > > +       /* check for overflow */
    > > +       if (unlikely(realsize < size))
    > > +               return NULL;
    > Hum, you should return an errno. I would choose ENOMEM, thats closer 
    > inline with malloc:
    > 
    >           return ENOMEM;
    Sounds good.
    > 
    > > 
    > > 
    > > +
    > > +       intptr = (uintptr_t) a->malloc_backend(a, realsize);
    > > +
    > > +       if (!intptr)
    > > +               return ENOMEM;
    > > +
    > > +       *memptr = (void *) ALIGN_UP(intptr +
    > > METADATA_IFMALLOC_SIZE_POW2,
    > > +                                   (uintptr_t) align);
    > > +
    > > +       metadata = uk_get_metadata_ifmalloc(*memptr);
    > > +
    > > +       /* check for underflow */
    > > +       UK_ASSERT(intptr <= (uintptr_t) metadata);
    > > +
    > > +       metadata->size = realsize;
    > > +       metadata->base = (void *) intptr;
    > > +
    > > +       return 0;
    > > +}
    > > +
    > > +#endif
    > > +
    > >   void uk_pfree_compat(struct uk_alloc *a, void *ptr,
    > >                      unsigned long num_pages __unused)
    > >   {
    > > diff --git a/lib/ukalloc/exportsyms.uk b/lib/ukalloc/exportsyms.uk
    > > index 2c8a90f..21c1996 100644
    > > --- a/lib/ukalloc/exportsyms.uk
    > > +++ b/lib/ukalloc/exportsyms.uk
    > > @@ -4,6 +4,10 @@ uk_malloc_ifpages
    > >   uk_free_ifpages
    > >   uk_realloc_ifpages
    > >   uk_posix_memalign_ifpages
    > > +uk_malloc_ifmalloc
    > > +uk_realloc_ifmalloc
    > > +uk_posix_memalign_ifmalloc
    > > +uk_free_ifmalloc
    > >   uk_calloc_compat
    > >   uk_memalign_compat
    > >   uk_realloc_compat
    > > diff --git a/lib/ukalloc/include/uk/alloc.h
    > > b/lib/ukalloc/include/uk/alloc.h
    > > index 5bfa2f2..1457922 100644
    > > --- a/lib/ukalloc/include/uk/alloc.h
    > > +++ b/lib/ukalloc/include/uk/alloc.h
    > > @@ -85,6 +85,11 @@ struct uk_alloc {
    > >         uk_alloc_memalign_func_t memalign;
    > >         uk_alloc_free_func_t free;
    > >   
    > > +#if CONFIG_LIBUKALLOC_IFMALLOC
    > > +       uk_alloc_free_func_t free_backend;
    > > +       uk_alloc_malloc_func_t malloc_backend;
    > > +#endif
    > > +
    > >         /* page allocation interface */
    > >         uk_alloc_palloc_func_t palloc;
    > >         uk_alloc_pfree_func_t pfree;
    > > diff --git a/lib/ukalloc/include/uk/alloc_impl.h
    > > b/lib/ukalloc/include/uk/alloc_impl.h
    > > index 8bcca94..b15f49e 100644
    > > --- a/lib/ukalloc/include/uk/alloc_impl.h
    > > +++ b/lib/ukalloc/include/uk/alloc_impl.h
    > > @@ -62,6 +62,14 @@ int uk_posix_memalign_ifpages(struct uk_alloc
    > > *a, void **memptr,
    > >                                 size_t align, size_t size);
    > >   void uk_free_ifpages(struct uk_alloc *a, void *ptr);
    > >   
    > > +#if CONFIG_LIBUKALLOC_IFMALLOC
    > > +void *uk_malloc_ifmalloc(struct uk_alloc *a, size_t size);
    > > +void *uk_realloc_ifmalloc(struct uk_alloc *a, void *ptr, size_t
    > > size);
    > > +int uk_posix_memalign_ifmalloc(struct uk_alloc *a, void **memptr,
    > > +                                    size_t align, size_t size);
    > > +void uk_free_ifmalloc(struct uk_alloc *a, void *ptr);
    > > +#endif
    > > +
    > >   /* Functionality that is provided based on malloc() and
    > > posix_memalign() */
    > >   void *uk_calloc_compat(struct uk_alloc *a, size_t num, size_t
    > > len);
    > >   void *uk_realloc_compat(struct uk_alloc *a, void *ptr, size_t
    > > size);
    > > @@ -88,6 +96,25 @@ void uk_pfree_compat(struct uk_alloc *a, void
    > > *ptr, unsigned long num_pages);
    > >                 uk_alloc_register((a));                         
    > >         \
    > >         } while (0)
    > >   
    > > +#if CONFIG_LIBUKALLOC_IFMALLOC
    > > +#define uk_alloc_init_malloc_ifmalloc(a, malloc_f, free_f,
    > > addmem_f)       \
    > > +       do {                                                    
    > >         \
    > > +               (a)->malloc         = uk_malloc_ifmalloc;               
    > > \
    > > +               (a)->calloc         = uk_calloc_compat;         
    > >         \
    > > +               (a)->realloc        = uk_realloc_ifmalloc;              
    > > \
    > > +               (a)->posix_memalign = uk_posix_memalign_ifmalloc;       
    > > \
    > > +               (a)->memalign       = uk_memalign_compat;               
    > > \
    > > +               (a)->malloc_backend = (malloc_f);                       
    > > \
    > > +               (a)->free_backend   = (free_f);                 
    > >         \
    > > +               (a)->free           = uk_free_ifmalloc;         
    > >         \
    > > +               (a)->palloc         = uk_palloc_compat;         
    > >         \
    > > +               (a)->pfree          = uk_pfree_compat;          
    > >         \
    > > +               (a)->addmem         = (addmem_f);                       
    > > \
    > > +                                                                       
    > > \
    > > +               uk_alloc_register((a));                         
    > >         \
    > > +       } while (0)
    > > +#endif
    > > +
    > >   /* Shortcut for doing a registration of an allocator that only
    > >    * implements palloc(), pfree(), addmem()
    > >    */

Thanks,

Simon
    


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.