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

Re: [Minios-devel] [UNIKRAFT PATCH v2] lib/ukallocbbuddy: Correct region bitmap allocation and usage



Hey Costin,

there are some comments inline.

-Yuri.

Costin Lupu <costin.lupu@xxxxxxxxx> writes:

> The usage of a each memory region that is added to the binary
> buddy allocator is tracked with a bitmap. This patch corrects
> wrong size calculation for the bitmap and wrong calculations
> of bit postitions.
>
> Signed-off-by: Costin Lupu <costin.lupu@xxxxxxxxx>
> ---
>  lib/ukallocbbuddy/bbuddy.c | 83 
> +++++++++++++++++++++++++++++++---------------
>  1 file changed, 57 insertions(+), 26 deletions(-)
>
> diff --git a/lib/ukallocbbuddy/bbuddy.c b/lib/ukallocbbuddy/bbuddy.c
> index 20a9b70..63288f0 100644
> --- a/lib/ukallocbbuddy/bbuddy.c
> +++ b/lib/ukallocbbuddy/bbuddy.c
> @@ -69,7 +69,7 @@ struct uk_bbpalloc_memr {
>       unsigned long first_page;
>       unsigned long nr_pages;
>       unsigned long mm_alloc_bitmap_size;
> -     unsigned long mm_alloc_bitmap[];
> +     unsigned long *mm_alloc_bitmap;
>  };
>  
>  struct uk_bbpalloc {
> @@ -93,10 +93,11 @@ struct uk_bbpalloc {
>   *  *_idx == Index into `mm_alloc_bitmap' array.
>   *  *_off == Bit offset within an element of the `mm_alloc_bitmap' array.
>   */
> -#define PAGES_PER_MAPWORD (sizeof(unsigned long) * 8)
> +#define BITS_PER_BYTE     8
> +#define PAGES_PER_MAPWORD (sizeof(unsigned long) * BITS_PER_BYTE)
>  
>  static inline struct uk_bbpalloc_memr *map_get_memr(struct uk_bbpalloc *b,
> -                                                 unsigned long page_num)
> +                                                 unsigned long page_va)
>  {
>       struct uk_bbpalloc_memr *memr = NULL;
>  
> @@ -106,8 +107,9 @@ static inline struct uk_bbpalloc_memr 
> *map_get_memr(struct uk_bbpalloc *b,
>        * of them. It should be just one region in most cases
>        */
>       for (memr = b->memr_head; memr != NULL; memr = memr->next) {
> -             if ((page_num >= memr->first_page)
> -                 && (page_num < (memr->first_page + memr->nr_pages)))
> +             if ((page_va >= memr->first_page)
> +                 && (page_va < (memr->first_page +
> +                 memr->nr_pages * __PAGE_SIZE)))
Is not a huge performance improvement, but better to use
   memr->nr_pages << __PAGE_SHIFT

>                       return memr;
>       }
>  
> @@ -117,24 +119,29 @@ static inline struct uk_bbpalloc_memr 
> *map_get_memr(struct uk_bbpalloc *b,
>       return NULL;
>  }
>  
> -static inline int allocated_in_map(struct uk_bbpalloc *b,
> -                                unsigned long page_num)
> +static inline unsigned long allocated_in_map(struct uk_bbpalloc *b,
> +                                unsigned long page_va)
>  {
> -     struct uk_bbpalloc_memr *memr = map_get_memr(b, page_num);
> +     struct uk_bbpalloc_memr *memr = map_get_memr(b, page_va);
> +     unsigned long page_idx;
> +     unsigned long bm_idx, bm_off;
>  
>       /* treat pages outside of region as allocated */
>       if (!memr)
>               return 1;
>  
> -     page_num -= memr->first_page;
> -     return ((memr)->mm_alloc_bitmap[(page_num) / PAGES_PER_MAPWORD]
> -             & (1UL << ((page_num) & (PAGES_PER_MAPWORD - 1))));
> +     page_idx = (page_va - memr->first_page) / __PAGE_SIZE;
> +     bm_idx = page_idx / PAGES_PER_MAPWORD;
> +     bm_off = page_idx & (PAGES_PER_MAPWORD - 1);
> +
> +     return ((memr)->mm_alloc_bitmap[bm_idx] & (1UL << bm_off));
>  }
>  
>  static void map_alloc(struct uk_bbpalloc *b, uintptr_t first_page,
>                     unsigned long nr_pages)
>  {
>       struct uk_bbpalloc_memr *memr;
> +     unsigned long first_page_idx, end_page_idx;
>       unsigned long start_off, end_off, curr_idx, end_idx;
>  
>       /*
> @@ -144,14 +151,16 @@ static void map_alloc(struct uk_bbpalloc *b, uintptr_t 
> first_page,
>        */
>       memr = map_get_memr(b, first_page);
>       UK_ASSERT(memr != NULL);
> -     UK_ASSERT((first_page + nr_pages)
> -               <= (memr->first_page + memr->nr_pages));
> +     UK_ASSERT((first_page + nr_pages * __PAGE_SIZE)
> +               <= (memr->first_page + memr->nr_pages * __PAGE_SIZE));
>  
>       first_page -= memr->first_page;
> -     curr_idx = first_page / PAGES_PER_MAPWORD;
> -     start_off = first_page & (PAGES_PER_MAPWORD - 1);
> -     end_idx = (first_page + nr_pages) / PAGES_PER_MAPWORD;
> -     end_off = (first_page + nr_pages) & (PAGES_PER_MAPWORD - 1);
> +     first_page_idx = first_page / __PAGE_SIZE;
> +     curr_idx = first_page_idx / PAGES_PER_MAPWORD;
> +     start_off = first_page_idx & (PAGES_PER_MAPWORD - 1);
> +     end_page_idx = first_page_idx + nr_pages;
> +     end_idx = end_page_idx / PAGES_PER_MAPWORD;
> +     end_off = end_page_idx & (PAGES_PER_MAPWORD - 1);
Same preference of shifts over divisions. I stop pointing it out here,
but I think using shift in other places in the code would improve a
little bit performance, and readability. 

>  
>       if (curr_idx == end_idx) {
>               memr->mm_alloc_bitmap[curr_idx] |=
> @@ -170,6 +179,7 @@ static void map_free(struct uk_bbpalloc *b, uintptr_t 
> first_page,
>                    unsigned long nr_pages)
>  {
>       struct uk_bbpalloc_memr *memr;
> +     unsigned long first_page_idx, end_page_idx;
>       unsigned long start_off, end_off, curr_idx, end_idx;
>  
>       /*
> @@ -179,14 +189,16 @@ static void map_free(struct uk_bbpalloc *b, uintptr_t 
> first_page,
>        */
>       memr = map_get_memr(b, first_page);
>       UK_ASSERT(memr != NULL);
> -     UK_ASSERT((first_page + nr_pages)
> -               <= (memr->first_page + memr->nr_pages));
> +     UK_ASSERT((first_page + nr_pages * __PAGE_SIZE)
> +               <= (memr->first_page + memr->nr_pages * __PAGE_SIZE));
>  
>       first_page -= memr->first_page;
> -     curr_idx = first_page / PAGES_PER_MAPWORD;
> -     start_off = first_page & (PAGES_PER_MAPWORD - 1);
> -     end_idx = (first_page + nr_pages) / PAGES_PER_MAPWORD;
> -     end_off = (first_page + nr_pages) & (PAGES_PER_MAPWORD - 1);
> +     first_page_idx = first_page / __PAGE_SIZE;
> +     curr_idx = first_page_idx / PAGES_PER_MAPWORD;
> +     start_off = first_page_idx & (PAGES_PER_MAPWORD - 1);
> +     end_page_idx = first_page_idx + nr_pages;
> +     end_idx = end_page_idx / PAGES_PER_MAPWORD;
> +     end_off = end_page_idx & (PAGES_PER_MAPWORD - 1);
>  
>       if (curr_idx == end_idx) {
>               memr->mm_alloc_bitmap[curr_idx] &=
> @@ -345,10 +357,25 @@ static int bbuddy_addmem(struct uk_alloc *a, void 
> *base, size_t len)
>       min = round_pgup((uintptr_t)base);
>       max = round_pgdown((uintptr_t)base + (uintptr_t)len);
>       range = max - min;
> -     memr_size =
> -         round_pgup(sizeof(*memr) + DIV_ROUND_UP(range >> __PAGE_SHIFT, 8));
>  
>       memr = (struct uk_bbpalloc_memr *)min;
> +
> +     /*
> +      * The number of pages is found by solving the inequality:
> +      *
> +      * sizeof(*memr) + bitmap_size + page_num * page_size <= range
> +      *
> +      * where: bitmap_size = page_num / BITS_PER_BYTE
> +      *
> +      */
> +     memr->nr_pages =
> +             BITS_PER_BYTE * (range - sizeof(*memr)) /
> +             (BITS_PER_BYTE * __PAGE_SIZE + 1);
I had a bad time trying to understand this math. I would like to propose
a comment like this here
/* The available amount of memory in bits is:
 * BITS_PER_BYTE * (range - sizeof(*memr))
 *
 * Every page occupies one bit in the bitmap. So total number of bits
 * used by one page is:
 * BITS_PER_BYTE * __PAGE_SIZE + 1
 */

> +     memr->mm_alloc_bitmap = (unsigned long *) (min + sizeof(*memr));
> +     memr->mm_alloc_bitmap_size  =
> +             round_pgup(memr->nr_pages / BITS_PER_BYTE) - sizeof(*memr);
I think I found a problem in the math..

Let's assume this function is called with len=132952064 (32459 pages). In
this case memr->nr_pages=32457,
memr->mm_alloc_bitmap_size=4056. However, to represent 32457 pages we
are going to need 32457/8 = 4057.125 = 4058 bytes.

This math probably could be replaced with an easier one. Currently it is
a bit too complicated. It is difficult to verify, and quite easy to make
a mistake.

Here is an idea. What if we approach the problem from the other side. We
know how many pages one page of a bitmap can handle. I wrote a small
snippet to demonstrate:

#define BITMAP_FIRST_PAGE_BYTES ( __PAGE_SIZE -                         \
                                  sizeof(struct uk_bbpalloc_memr))
#define BITMAP_FIRST_PAGE_BITS ((ssize_t) (BITMAP_FIRST_PAGE_BYTES << 3))
#define BITS_PER_PAGE (__PAGE_SIZE << 3)

        ssize_t memr_pg_num = 1;
        ssize_t rem_pages = range >> __PAGE_SHIFT;

        /* The very first page is special - it is shared between memr
         * and initial portion of the bitmap.
         *
         * This page is already taken from the page budget, we
         * decrease number of pages we should care about.
         */
        if (rem_pages > BITMAP_FIRST_PAGE_BITS) {
                /* We have a bitmap that is capable of handling
                 * BITMAP_FIRST_PAGE_BITS pages anyways.  If we are
                 * here that was not enough
                 */

                /* We don not need to care about pages handled by
                 * bitmap in the first page
                 */
                rem_pages -= BITMAP_FIRST_PAGE_BITS;

                /* To handle the remaining part we going to need
                 * (rem_pages / BITS_PER_PAGE) pages. But with every
                 * next bitmap page, the number of usable pages
                 * reduces by 1. That is why we actually need to
                 * divide by (BITS_PER_PAGE +1)
                 */
                memr_pg_num += (rem_pages + BITS_PER_PAGE - 1) /
                        (BITS_PER_PAGE + 1);
        }


What do you think?

> +     memr_size = sizeof(*memr) + memr->mm_alloc_bitmap_size;
> +
>       min += memr_size;
>       range -= memr_size;
>       if (max < min) {
> @@ -362,10 +389,14 @@ static int bbuddy_addmem(struct uk_alloc *a, void 
> *base, size_t len)
>        * Initialize region's bitmap
>        */
>       memr->first_page = min;
> -     memr->nr_pages = max - min;
>       /* add to list */
>       memr->next = b->memr_head;
>       b->memr_head = memr;
> +
> +     /* All allocated by default. */
> +     memset(memr->mm_alloc_bitmap, (unsigned char) ~0,
> +                     memr->mm_alloc_bitmap_size);
Very minor thing. Probably '0xff' is nicer then '(unsigned char) ~0'

> +
>       /* free up the memory we've been given to play with */
>       map_free(b, min, (unsigned long)(range >> __PAGE_SHIFT));
>  
> -- 
> 2.11.0
>

-- 
Yuri Volchkov
Software Specialist

NEC Europe Ltd
Kurfürsten-Anlage 36
D-69115 Heidelberg

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

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