|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [RFC PATCH] xen/common: cache colored buddy allocator for domains
On Sat, Oct 22, 2022 at 6:08 PM Carlo Nonato
<carlo.nonato@xxxxxxxxxxxxxxx> wrote:
>
> This commit replaces the colored allocator for domains with a simple buddy
> allocator indexed also by colors, so that it can allocate pages based on
> some coloring configuration.
>
> It applies on top of Arm cache coloring (v3) as sent to the mailing list.
>
> This has two benefits:
> - order can now be greater than 0 if the color config contains a
> sufficient number of adjacent colors starting from an order aligned
> one;
> - same benefits of the normal buddy: constant time alloc and free
> (constant with respect to the number of pages, not for the number of
> colors);
>
> But also one "big" cons:
> - given the way Xen queries the allocator, it can only serve larger pages
> first and only when a domain runs out of those, it can go with the smaller
> ones. Let's say that domain 0 has 31 colors out of 32 total (0-30 out of
> 0-31). The order-4 pages (0-15) are allocated first and then the order-3
> (16-23, since 0-7 and 8-15 are all already allocated), and then order-2
> and so on. The result is... the domain practically uses only one half of
> the colors that it should.
In case it isn't clear, less cache colors means less cache given to the
domain which results in lower performances.
> Signed-off-by: Carlo Nonato <carlo.nonato@xxxxxxxxxxxxxxx>
> ---
> xen/common/page_alloc.c | 202 ++++++++++++++++++++++++++++------------
> 1 file changed, 141 insertions(+), 61 deletions(-)
>
> diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c
> index fe214cd6ac..f20de1c891 100644
> --- a/xen/common/page_alloc.c
> +++ b/xen/common/page_alloc.c
> @@ -462,67 +462,133 @@ static void init_free_page_fields(struct page_info *pg)
>
> #ifdef CONFIG_CACHE_COLORING
> /*************************
> - * COLORED SIDE-ALLOCATOR
> + * COLORED BUDDY ALLOCATOR
> *
> - * Pages are stored by their color in separate lists. Each list defines a
> color
> - * and it is initialized during end_boot_allocator, where each page's color
> - * is calculated and the page itself is put in the correct list.
> - * After initialization there will be N lists where N is the number of
> - * available colors on the platform.
> - * The {free|alloc}_color_heap_page overwrite pg->count_info, but they do it
> in
> - * the same way as the buddy allocator corresponding functions do:
> - * protecting the access with a critical section using heap_lock.
> + * A simplified version of the buddy allocator with the addition of colors to
> + * serve pages based on coloring configuration.
> + * Pages are divided in lists by color and order, so the total space needed
> + * for the heap is greater than the normal one. The matrix of colors x orders
> + * page lists is linearized since not every color can use any order.
> */
> typedef struct page_list_head colored_pages_t;
> static colored_pages_t *__ro_after_init _color_heap;
> static unsigned long *__ro_after_init free_colored_pages;
> +static unsigned int max_color_order;
>
> -#define color_heap(color) (&_color_heap[color])
> +#define order_mask(order) ((1UL << (order)) - 1)
> +#define color_index_of_order(color, order) ((color) & order_mask(order))
> +#define color_align_to_order(color, order) ((color) & ~order_mask(order))
> +#define color_heap_pos(color, order) \
> + ((((1 << max_color_order) + (color)) >> (order)) - 1)
> +#define color_heap(color, order) (&_color_heap[color_heap_pos(color, order)])
>
> -static void free_color_heap_page(struct page_info *pg)
> +static void free_color_heap_pages(struct page_info *pg, unsigned int order)
> {
> - struct page_info *pos;
> - unsigned int color = page_to_color(pg);
> - colored_pages_t *head = color_heap(color);
> + unsigned int i, color = page_to_color(pg);
> +
> + ASSERT(color_index_of_order(color, order) == 0);
>
> spin_lock(&heap_lock);
>
> - pg->count_info = PGC_state_free | PGC_colored;
> - page_set_owner(pg, NULL);
> - free_colored_pages[color]++;
> + for ( i = 0; i < (1 << order); i++ )
> + {
> + pg[i].count_info = PGC_state_free | PGC_colored;
> + page_set_owner(&pg[i], NULL);
> + free_colored_pages[page_to_color(&pg[i])]++;
> + }
>
> - page_list_for_each( pos, head )
> + for ( ; order < max_color_order; order++ )
> {
> - if ( page_to_maddr(pos) < page_to_maddr(pg) )
> + unsigned long mask = 1UL << order;
> + struct page_info *merge = pg + ((color & mask) ? -mask : mask);
> +
> + if ( !page_state_is(merge, free) || (PFN_ORDER(merge) != order) )
> break;
> +
> + if ( color & mask )
> + {
> + pg = merge;
> + color -= mask;
> + }
> +
> + page_list_del(merge, color_heap(color, order));
> }
>
> - page_list_add_next(pg, pos, head);
> + PFN_ORDER(pg) = order;
> + page_list_add(pg, color_heap(color, order));
>
> spin_unlock(&heap_lock);
> }
>
> -static struct page_info *alloc_color_heap_page(unsigned int memflags,
> - const unsigned int *colors,
> - unsigned int num_colors)
> +static struct page_info *find_free_color_heap_page(unsigned int color,
> + unsigned int order)
> +{
> + struct page_info *pg = NULL;
> + unsigned int buddy_order, buddy_color;
> +
> + ASSERT(color_index_of_order(color, order) == 0);
> +
> + for ( buddy_order = order; buddy_order <= max_color_order; buddy_order++
> )
> + {
> + pg = page_list_remove_head(color_heap(
> + color_align_to_order(color, buddy_order), buddy_order));
> + if ( pg )
> + break;
> + }
> +
> + if ( !pg )
> + return NULL;
> +
> + buddy_color = page_to_color(pg);
> +
> + while ( buddy_order != order )
> + {
> + unsigned long mask = (1U << --buddy_order);
> + struct page_info *half = pg + mask;
> +
> + if ( color & mask )
> + {
> + page_list_add(pg, color_heap(buddy_color, buddy_order));
> + pg = half;
> + buddy_color += mask;
> + }
> + else
> + page_list_add(half, color_heap(buddy_color + mask, buddy_order));
> + }
> +
> + return pg;
> +}
> +
> +static struct page_info *alloc_color_heap_pages(unsigned int order,
> + unsigned int memflags,
> + unsigned int *colors,
> + unsigned int num_colors)
> {
> struct page_info *pg = NULL;
> - unsigned int i, color;
> + unsigned int i, color = 0;
> + unsigned long mask = order_mask(order);
> bool need_tlbflush = false;
> uint32_t tlbflush_timestamp = 0;
> + mfn_t mfn;
>
> spin_lock(&heap_lock);
>
> + /* Search for 2^order contiguous colors */
> for ( i = 0; i < num_colors; i++ )
> {
> - struct page_info *tmp;
> + unsigned int index = colors[i] & mask;
> +
> + if ( index == 0 )
> + color = colors[i];
>
> - if ( page_list_empty(color_heap(colors[i])) )
> + if ( colors[i] != color )
> continue;
>
> - tmp = page_list_first(color_heap(colors[i]));
> - if ( !pg || page_to_maddr(tmp) > page_to_maddr(pg) )
> - pg = tmp;
> + if ( index == mask &&
> + (pg = find_free_color_heap_page(colors[i] & ~mask, order)) )
> + break;
> +
> + color++;
> }
>
> if ( !pg )
> @@ -531,59 +597,77 @@ static struct page_info *alloc_color_heap_page(unsigned
> int memflags,
> return NULL;
> }
>
> - pg->count_info = PGC_state_inuse | PGC_colored;
> -
> - if ( !(memflags & MEMF_no_tlbflush) )
> - accumulate_tlbflush(&need_tlbflush, pg, &tlbflush_timestamp);
> + for ( i = 0; i < (1 << order); i++ )
> + {
> + pg[i].count_info = PGC_state_inuse | PGC_colored;
>
> - init_free_page_fields(pg);
> - flush_page_to_ram(mfn_x(page_to_mfn(pg)),
> - !(memflags & MEMF_no_icache_flush));
> + if ( !(memflags & MEMF_no_tlbflush) )
> + accumulate_tlbflush(&need_tlbflush, &pg[i], &tlbflush_timestamp);
>
> - color = page_to_color(pg);
> - free_colored_pages[color]--;
> - page_list_del(pg, color_heap(color));
> + init_free_page_fields(&pg[i]);
> + free_colored_pages[page_to_color(&pg[i])]--;
> + }
>
> spin_unlock(&heap_lock);
>
> if ( need_tlbflush )
> filtered_flush_tlb_mask(tlbflush_timestamp);
>
> + mfn = page_to_mfn(pg);
> + for ( i = 0; i < (1U << order); i++ )
> + flush_page_to_ram(mfn_x(mfn) + i, !(memflags &
> MEMF_no_icache_flush));
> +
> return pg;
> }
>
> static void __init init_color_heap_pages(struct page_info *pg,
> unsigned long nr_pages)
> {
> - unsigned int i;
> + unsigned long s, e;
>
> if ( !_color_heap )
> {
> unsigned int max_colors = get_max_colors();
> + unsigned int color_heap_num = (2 * max_colors) - 1;
> + unsigned int i;
>
> - _color_heap = xmalloc_array(colored_pages_t, max_colors);
> + max_color_order = flsl(max_colors) - 1;
> +
> + _color_heap = xmalloc_array(colored_pages_t, color_heap_num);
> BUG_ON(!_color_heap);
> free_colored_pages = xzalloc_array(unsigned long, max_colors);
> BUG_ON(!free_colored_pages);
> -
> - for ( i = 0; i < max_colors; i++ )
> - INIT_PAGE_LIST_HEAD(color_heap(i));
> + for ( i = 0; i < color_heap_num; i++ )
> + INIT_PAGE_LIST_HEAD(&_color_heap[i]);
> }
>
> printk(XENLOG_DEBUG
> "Init color heap with %lu pages starting from: %#"PRIx64"\n",
> nr_pages, page_to_maddr(pg));
>
> - for ( i = 0; i < nr_pages; i++ )
> - free_color_heap_page(&pg[i]);
> + s = mfn_x(page_to_mfn(pg));
> + e = mfn_x(mfn_add(page_to_mfn(pg + nr_pages - 1), 1));
> +
> + while ( s < e )
> + {
> + unsigned int inc_order = min_t(unsigned int, max_color_order,
> + flsl(e - s) - 1);
> +
> + if ( s )
> + inc_order = min(inc_order, ffsl(s) - 1U);
> + free_color_heap_pages(mfn_to_page(_mfn(s)), inc_order);
> + s += (1UL << inc_order);
> + }
> }
>
> -static struct page_info *alloc_color_domheap_page(struct domain *d,
> - unsigned int memflags)
> +static struct page_info *alloc_color_domheap_pages(struct domain *d,
> + unsigned int order,
> + unsigned int memflags)
> {
> struct page_info *pg;
>
> - pg = alloc_color_heap_page(memflags, d->arch.colors, d->arch.num_colors);
> + pg = alloc_color_heap_pages(order, memflags, d->arch.colors,
> + d->arch.num_colors);
> if ( !pg )
> return NULL;
>
> @@ -591,9 +675,9 @@ static struct page_info *alloc_color_domheap_page(struct
> domain *d,
> {
> if ( memflags & MEMF_no_refcount )
> pg->count_info |= PGC_extra;
> - if ( assign_page(pg, 0, d, memflags) )
> + if ( assign_page(pg, order, d, memflags) )
> {
> - free_color_heap_page(pg);
> + free_color_heap_pages(pg, order);
> return NULL;
> }
> }
> @@ -616,12 +700,13 @@ integer_param("buddy-alloc-size", buddy_alloc_size);
>
> static void __init init_color_heap_pages(struct page_info *pg,
> unsigned long nr_pages) {}
> -static struct page_info *alloc_color_domheap_page(struct domain *d,
> - unsigned int memflags)
> +static struct page_info *alloc_color_domheap_pages(struct domain *d,
> + unsigned int order,
> + unsigned int memflags)
> {
> return NULL;
> }
> -static void free_color_heap_page(struct page_info *pg) {}
> +static void free_color_heap_pages(struct page_info *pg, unsigned int order)
> {}
> static void dump_color_heap(void) {}
>
> #endif /* CONFIG_CACHE_COLORING */
> @@ -2637,12 +2722,7 @@ struct page_info *alloc_domheap_pages(
>
> /* Only domains are supported for coloring */
> if ( IS_ENABLED(CONFIG_CACHE_COLORING) && d )
> - {
> - /* Colored allocation must be done on 0 order */
> - if ( order )
> - return NULL;
> - return alloc_color_domheap_page(d, memflags);
> - }
> + return alloc_color_domheap_pages(d, order, memflags);
>
> bits = domain_clamp_alloc_bitsize(memflags & MEMF_no_owner ? NULL : d,
> bits ? : (BITS_PER_LONG+PAGE_SHIFT));
> @@ -2762,7 +2842,7 @@ void free_domheap_pages(struct page_info *pg, unsigned
> int order)
> }
>
> if ( pg->count_info & PGC_colored )
> - free_color_heap_page(pg);
> + free_color_heap_pages(pg, order);
> else
> free_heap_pages(pg, order, scrub);
> }
> --
> 2.34.1
>
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |