[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |