Skip to content

Commit e93ec87

Browse files
committed
x86/xen: allow larger contiguous memory regions in PV guests
Today a PV guest (including dom0) can create 2MB contiguous memory regions for DMA buffers at max. This has led to problems at least with the megaraid_sas driver, which wants to allocate a 2.3MB DMA buffer. The limiting factor is the frame array used to do the hypercall for making the memory contiguous, which has 512 entries and is just a static array in mmu_pv.c. In order to not waste memory for non-PV guests, put the initial frame array into .init.data section and dynamically allocate an array from the .init_after_bootmem hook of PV guests. In case a contiguous memory area larger than the initially supported 2MB is requested, allocate a larger buffer for the frame list. Note that such an allocation is tried only after memory management has been initialized properly, which is tested via a flag being set in the .init_after_bootmem hook. Fixes: 9f40ec8 ("xen/swiotlb: add alignment check for dma buffers") Signed-off-by: Juergen Gross <[email protected]> Tested-by: Alan Robinson <[email protected]> Reviewed-by: Jan Beulich <[email protected]> Signed-off-by: Juergen Gross <[email protected]>
1 parent 85fcb57 commit e93ec87

File tree

1 file changed

+62
-9
lines changed

1 file changed

+62
-9
lines changed

arch/x86/xen/mmu_pv.c

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,51 @@ static pud_t level3_user_vsyscall[PTRS_PER_PUD] __page_aligned_bss;
111111
*/
112112
static DEFINE_SPINLOCK(xen_reservation_lock);
113113

114+
/* Protected by xen_reservation_lock. */
115+
#define MIN_CONTIG_ORDER 9 /* 2MB */
116+
static unsigned int discontig_frames_order = MIN_CONTIG_ORDER;
117+
static unsigned long discontig_frames_early[1UL << MIN_CONTIG_ORDER] __initdata;
118+
static unsigned long *discontig_frames __refdata = discontig_frames_early;
119+
static bool discontig_frames_dyn;
120+
121+
static int alloc_discontig_frames(unsigned int order)
122+
{
123+
unsigned long *new_array, *old_array;
124+
unsigned int old_order;
125+
unsigned long flags;
126+
127+
BUG_ON(order < MIN_CONTIG_ORDER);
128+
BUILD_BUG_ON(sizeof(discontig_frames_early) != PAGE_SIZE);
129+
130+
new_array = (unsigned long *)__get_free_pages(GFP_KERNEL,
131+
order - MIN_CONTIG_ORDER);
132+
if (!new_array)
133+
return -ENOMEM;
134+
135+
spin_lock_irqsave(&xen_reservation_lock, flags);
136+
137+
old_order = discontig_frames_order;
138+
139+
if (order > discontig_frames_order || !discontig_frames_dyn) {
140+
if (!discontig_frames_dyn)
141+
old_array = NULL;
142+
else
143+
old_array = discontig_frames;
144+
145+
discontig_frames = new_array;
146+
discontig_frames_order = order;
147+
discontig_frames_dyn = true;
148+
} else {
149+
old_array = new_array;
150+
}
151+
152+
spin_unlock_irqrestore(&xen_reservation_lock, flags);
153+
154+
free_pages((unsigned long)old_array, old_order - MIN_CONTIG_ORDER);
155+
156+
return 0;
157+
}
158+
114159
/*
115160
* Note about cr3 (pagetable base) values:
116161
*
@@ -814,6 +859,9 @@ static void __init xen_after_bootmem(void)
814859
SetPagePinned(virt_to_page(level3_user_vsyscall));
815860
#endif
816861
xen_pgd_walk(&init_mm, xen_mark_pinned, FIXADDR_TOP);
862+
863+
if (alloc_discontig_frames(MIN_CONTIG_ORDER))
864+
BUG();
817865
}
818866

819867
static void xen_unpin_page(struct mm_struct *mm, struct page *page,
@@ -2203,10 +2251,6 @@ void __init xen_init_mmu_ops(void)
22032251
memset(dummy_mapping, 0xff, PAGE_SIZE);
22042252
}
22052253

2206-
/* Protected by xen_reservation_lock. */
2207-
#define MAX_CONTIG_ORDER 9 /* 2MB */
2208-
static unsigned long discontig_frames[1<<MAX_CONTIG_ORDER];
2209-
22102254
#define VOID_PTE (mfn_pte(0, __pgprot(0)))
22112255
static void xen_zap_pfn_range(unsigned long vaddr, unsigned int order,
22122256
unsigned long *in_frames,
@@ -2323,18 +2367,25 @@ int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
23232367
unsigned int address_bits,
23242368
dma_addr_t *dma_handle)
23252369
{
2326-
unsigned long *in_frames = discontig_frames, out_frame;
2370+
unsigned long *in_frames, out_frame;
23272371
unsigned long flags;
23282372
int success;
23292373
unsigned long vstart = (unsigned long)phys_to_virt(pstart);
23302374

2331-
if (unlikely(order > MAX_CONTIG_ORDER))
2332-
return -ENOMEM;
2375+
if (unlikely(order > discontig_frames_order)) {
2376+
if (!discontig_frames_dyn)
2377+
return -ENOMEM;
2378+
2379+
if (alloc_discontig_frames(order))
2380+
return -ENOMEM;
2381+
}
23332382

23342383
memset((void *) vstart, 0, PAGE_SIZE << order);
23352384

23362385
spin_lock_irqsave(&xen_reservation_lock, flags);
23372386

2387+
in_frames = discontig_frames;
2388+
23382389
/* 1. Zap current PTEs, remembering MFNs. */
23392390
xen_zap_pfn_range(vstart, order, in_frames, NULL);
23402391

@@ -2358,19 +2409,21 @@ int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
23582409

23592410
void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)
23602411
{
2361-
unsigned long *out_frames = discontig_frames, in_frame;
2412+
unsigned long *out_frames, in_frame;
23622413
unsigned long flags;
23632414
int success;
23642415
unsigned long vstart;
23652416

2366-
if (unlikely(order > MAX_CONTIG_ORDER))
2417+
if (unlikely(order > discontig_frames_order))
23672418
return;
23682419

23692420
vstart = (unsigned long)phys_to_virt(pstart);
23702421
memset((void *) vstart, 0, PAGE_SIZE << order);
23712422

23722423
spin_lock_irqsave(&xen_reservation_lock, flags);
23732424

2425+
out_frames = discontig_frames;
2426+
23742427
/* 1. Find start MFN of contiguous extent. */
23752428
in_frame = virt_to_mfn((void *)vstart);
23762429

0 commit comments

Comments
 (0)