Subject: x86: use dynamically adjusted upper bound for contiguous regions After years of not causing problems, the 2Mb (order 9) limit on contiguous regions' size was recently found to prevent booting of certain systems - at least the FC variant of the MPT driver can (possibly only for out-of-tree and/or post-2.6.18 versions) require allocation of a buffer quite a bit larger than 2Mb. Rather than increasing the limit on the order to 10, make the whole logic dynamic - start out with a static order and respective argument buffers large enough to allow initialization up to the point where core_initcall()-s get processed, and from then on dynamically allocate (and re-size) argument buffers on demand. Signed-off-by: Jan Beulich --- a/arch/i386/mm/hypervisor.c +++ b/arch/i386/mm/hypervisor.c @@ -191,17 +191,71 @@ void xen_set_ldt(const void *ptr, unsign } /* Protected by balloon_lock. */ -#define MAX_CONTIG_ORDER 9 /* 2MB */ -static unsigned long discontig_frames[1<= 32)) +#else + if (unlikely(order > BITS_PER_LONG - fls(sizeof(*cr_mcl)))) +#endif + return -ENOMEM; + + if (unlikely(order > max_contig_order)) + { + unsigned long *df = __vmalloc((sizeof(*discontig_frames) + + sizeof(*cr_mcl)) << order, + GFP_ATOMIC, PAGE_KERNEL); + unsigned long flags; + + if (!df) { + vfree(df); + return -ENOMEM; + } + balloon_lock(flags); + if (order > max_contig_order) { + void *temp = discontig_frames; + + discontig_frames = df; + cr_mcl = (void *)(df + (1U << order)); + df = temp; + + wmb(); + max_contig_order = order; + } + balloon_unlock(flags); + vfree(df); + printk(KERN_INFO + "Adjusted maximum contiguous region order to %u\n", + order); + } + + return 0; +} /* Ensure multi-page extents are contiguous in machine memory. */ int xen_create_contiguous_region( unsigned long vstart, unsigned int order, unsigned int address_bits) { - unsigned long *in_frames = discontig_frames, out_frame; - unsigned long frame, flags; + unsigned long *in_frames, out_frame, frame, flags; unsigned int i; int rc, success; struct xen_memory_exchange exchange = { @@ -226,16 +280,19 @@ int xen_create_contiguous_region( if (xen_feature(XENFEAT_auto_translated_physmap)) return 0; - if (unlikely(order > MAX_CONTIG_ORDER)) - return -ENOMEM; + rc = check_contig_order(order); + if (unlikely(rc)) + return rc; - set_xen_guest_handle(exchange.in.extent_start, in_frames); set_xen_guest_handle(exchange.out.extent_start, &out_frame); scrub_pages((void *)vstart, 1 << order); balloon_lock(flags); + in_frames = discontig_frames; + set_xen_guest_handle(exchange.in.extent_start, in_frames); + /* 1. Zap current PTEs, remembering MFNs. */ for (i = 0; i < (1U<> PAGE_SHIFT) + i); @@ -294,8 +351,7 @@ EXPORT_SYMBOL_GPL(xen_create_contiguous_ void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order) { - unsigned long *out_frames = discontig_frames, in_frame; - unsigned long frame, flags; + unsigned long *out_frames, in_frame, frame, flags; unsigned int i; int rc, success; struct xen_memory_exchange exchange = { @@ -314,16 +370,18 @@ void xen_destroy_contiguous_region(unsig if (xen_feature(XENFEAT_auto_translated_physmap)) return; - if (unlikely(order > MAX_CONTIG_ORDER)) + if (unlikely(order > max_contig_order)) return; set_xen_guest_handle(exchange.in.extent_start, &in_frame); - set_xen_guest_handle(exchange.out.extent_start, out_frames); scrub_pages((void *)vstart, 1 << order); balloon_lock(flags); + out_frames = discontig_frames; + set_xen_guest_handle(exchange.out.extent_start, out_frames); + /* 1. Find start MFN of contiguous extent. */ in_frame = pfn_to_mfn(__pa(vstart) >> PAGE_SHIFT); @@ -384,7 +432,7 @@ EXPORT_SYMBOL_GPL(xen_destroy_contiguous static void undo_limit_pages(struct page *pages, unsigned int order) { BUG_ON(xen_feature(XENFEAT_auto_translated_physmap)); - BUG_ON(order > MAX_CONTIG_ORDER); + BUG_ON(order > max_contig_order); xen_limit_pages_to_max_mfn(pages, order, 0); ClearPageForeign(pages); __free_pages(pages, order); @@ -393,12 +451,11 @@ static void undo_limit_pages(struct page int xen_limit_pages_to_max_mfn( struct page *pages, unsigned int order, unsigned int address_bits) { - unsigned long flags, frame; - unsigned long *in_frames = discontig_frames, *out_frames = limited_frames; + unsigned long flags, frame, *limit_map, _limit_map; + unsigned long *in_frames, *out_frames; struct page *page; unsigned int i, n, nr_mcl; int rc, success; - DECLARE_BITMAP(limit_map, 1 << MAX_CONTIG_ORDER); struct xen_memory_exchange exchange = { .in = { @@ -415,23 +472,30 @@ int xen_limit_pages_to_max_mfn( if (xen_feature(XENFEAT_auto_translated_physmap)) return 0; - if (unlikely(order > MAX_CONTIG_ORDER)) - return -ENOMEM; + if (address_bits && address_bits < PAGE_SHIFT) + return -EINVAL; + + rc = check_contig_order(order + 1); + if (unlikely(rc)) + return rc; + + if (BITS_PER_LONG >> order) { + limit_map = kmalloc(BITS_TO_LONGS(1U << order) + * sizeof(*limit_map), GFP_ATOMIC); + if (unlikely(!limit_map)) + return -ENOMEM; + } else + limit_map = &_limit_map; - if (address_bits) { - if (address_bits < PAGE_SHIFT) - return -EINVAL; + if (address_bits) bitmap_zero(limit_map, 1U << order); - } else if (order) { + else if (order) { BUILD_BUG_ON(sizeof(pages->index) != sizeof(*limit_map)); for (i = 0; i < BITS_TO_LONGS(1U << order); ++i) limit_map[i] = pages[i + 1].index; } else __set_bit(0, limit_map); - set_xen_guest_handle(exchange.in.extent_start, in_frames); - set_xen_guest_handle(exchange.out.extent_start, out_frames); - /* 0. Scrub the pages. */ for (i = 0, n = 0; i < 1U<index)); for (i = 0; i < BITS_TO_LONGS(1U << order); ++i) @@ -537,7 +606,10 @@ int xen_limit_pages_to_max_mfn( SetPageForeign(pages, undo_limit_pages); } - return 0; + if (limit_map != &_limit_map) + kfree(limit_map); + + return success ? 0 : -ENOMEM; } EXPORT_SYMBOL_GPL(xen_limit_pages_to_max_mfn);