diff options
Diffstat (limited to 'arch/x86/xen/p2m.c')
| -rw-r--r-- | arch/x86/xen/p2m.c | 121 | 
1 files changed, 105 insertions, 16 deletions
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 64effdc6da9..72213da605f 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -194,6 +194,13 @@ RESERVE_BRK(p2m_mid_mfn, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID   * boundary violation will require three middle nodes. */  RESERVE_BRK(p2m_mid_identity, PAGE_SIZE * 2 * 3); +/* When we populate back during bootup, the amount of pages can vary. The + * max we have is seen is 395979, but that does not mean it can't be more. + * Some machines can have 3GB I/O holes even. With early_can_reuse_p2m_middle + * it can re-use Xen provided mfn_list array, so we only need to allocate at + * most three P2M top nodes. */ +RESERVE_BRK(p2m_populated, PAGE_SIZE * 3); +  static inline unsigned p2m_top_index(unsigned long pfn)  {  	BUG_ON(pfn >= MAX_P2M_PFN); @@ -570,12 +577,99 @@ static bool __init early_alloc_p2m(unsigned long pfn)  	}  	return true;  } + +/* + * Skim over the P2M tree looking at pages that are either filled with + * INVALID_P2M_ENTRY or with 1:1 PFNs. If found, re-use that page and + * replace the P2M leaf with a p2m_missing or p2m_identity. + * Stick the old page in the new P2M tree location. + */ +bool __init early_can_reuse_p2m_middle(unsigned long set_pfn, unsigned long set_mfn) +{ +	unsigned topidx; +	unsigned mididx; +	unsigned ident_pfns; +	unsigned inv_pfns; +	unsigned long *p2m; +	unsigned long *mid_mfn_p; +	unsigned idx; +	unsigned long pfn; + +	/* We only look when this entails a P2M middle layer */ +	if (p2m_index(set_pfn)) +		return false; + +	for (pfn = 0; pfn < MAX_DOMAIN_PAGES; pfn += P2M_PER_PAGE) { +		topidx = p2m_top_index(pfn); + +		if (!p2m_top[topidx]) +			continue; + +		if (p2m_top[topidx] == p2m_mid_missing) +			continue; + +		mididx = p2m_mid_index(pfn); +		p2m = p2m_top[topidx][mididx]; +		if (!p2m) +			continue; + +		if ((p2m == p2m_missing) || (p2m == p2m_identity)) +			continue; + +		if ((unsigned long)p2m == INVALID_P2M_ENTRY) +			continue; + +		ident_pfns = 0; +		inv_pfns = 0; +		for (idx = 0; idx < P2M_PER_PAGE; idx++) { +			/* IDENTITY_PFNs are 1:1 */ +			if (p2m[idx] == IDENTITY_FRAME(pfn + idx)) +				ident_pfns++; +			else if (p2m[idx] == INVALID_P2M_ENTRY) +				inv_pfns++; +			else +				break; +		} +		if ((ident_pfns == P2M_PER_PAGE) || (inv_pfns == P2M_PER_PAGE)) +			goto found; +	} +	return false; +found: +	/* Found one, replace old with p2m_identity or p2m_missing */ +	p2m_top[topidx][mididx] = (ident_pfns ? p2m_identity : p2m_missing); +	/* And the other for save/restore.. */ +	mid_mfn_p = p2m_top_mfn_p[topidx]; +	/* NOTE: Even if it is a p2m_identity it should still be point to +	 * a page filled with INVALID_P2M_ENTRY entries. */ +	mid_mfn_p[mididx] = virt_to_mfn(p2m_missing); + +	/* Reset where we want to stick the old page in. */ +	topidx = p2m_top_index(set_pfn); +	mididx = p2m_mid_index(set_pfn); + +	/* This shouldn't happen */ +	if (WARN_ON(p2m_top[topidx] == p2m_mid_missing)) +		early_alloc_p2m(set_pfn); + +	if (WARN_ON(p2m_top[topidx][mididx] != p2m_missing)) +		return false; + +	p2m_init(p2m); +	p2m_top[topidx][mididx] = p2m; +	mid_mfn_p = p2m_top_mfn_p[topidx]; +	mid_mfn_p[mididx] = virt_to_mfn(p2m); + +	return true; +}  bool __init early_set_phys_to_machine(unsigned long pfn, unsigned long mfn)  {  	if (unlikely(!__set_phys_to_machine(pfn, mfn)))  {  		if (!early_alloc_p2m(pfn))  			return false; +		if (early_can_reuse_p2m_middle(pfn, mfn)) +			return __set_phys_to_machine(pfn, mfn); +  		if (!early_alloc_p2m_middle(pfn, false /* boundary crossover OK!*/))  			return false; @@ -734,9 +828,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,  			xen_mc_issue(PARAVIRT_LAZY_MMU);  		} -		/* let's use dev_bus_addr to record the old mfn instead */ -		kmap_op->dev_bus_addr = page->index; -		page->index = (unsigned long) kmap_op;  	}  	spin_lock_irqsave(&m2p_override_lock, flags);  	list_add(&page->lru,  &m2p_overrides[mfn_hash(mfn)]); @@ -763,7 +854,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,  	return 0;  }  EXPORT_SYMBOL_GPL(m2p_add_override); -int m2p_remove_override(struct page *page, bool clear_pte) +int m2p_remove_override(struct page *page, +		struct gnttab_map_grant_ref *kmap_op)  {  	unsigned long flags;  	unsigned long mfn; @@ -793,10 +885,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)  	WARN_ON(!PagePrivate(page));  	ClearPagePrivate(page); -	if (clear_pte) { -		struct gnttab_map_grant_ref *map_op = -			(struct gnttab_map_grant_ref *) page->index; -		set_phys_to_machine(pfn, map_op->dev_bus_addr); +	set_phys_to_machine(pfn, page->index); +	if (kmap_op != NULL) {  		if (!PageHighMem(page)) {  			struct multicall_space mcs;  			struct gnttab_unmap_grant_ref *unmap_op; @@ -808,13 +898,13 @@ int m2p_remove_override(struct page *page, bool clear_pte)  			 * issued. In this case handle is going to -1 because  			 * it hasn't been modified yet.  			 */ -			if (map_op->handle == -1) +			if (kmap_op->handle == -1)  				xen_mc_flush();  			/* -			 * Now if map_op->handle is negative it means that the +			 * Now if kmap_op->handle is negative it means that the  			 * hypercall actually returned an error.  			 */ -			if (map_op->handle == GNTST_general_error) { +			if (kmap_op->handle == GNTST_general_error) {  				printk(KERN_WARNING "m2p_remove_override: "  						"pfn %lx mfn %lx, failed to modify kernel mappings",  						pfn, mfn); @@ -824,8 +914,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)  			mcs = xen_mc_entry(  					sizeof(struct gnttab_unmap_grant_ref));  			unmap_op = mcs.args; -			unmap_op->host_addr = map_op->host_addr; -			unmap_op->handle = map_op->handle; +			unmap_op->host_addr = kmap_op->host_addr; +			unmap_op->handle = kmap_op->handle;  			unmap_op->dev_bus_addr = 0;  			MULTI_grant_table_op(mcs.mc, @@ -836,10 +926,9 @@ int m2p_remove_override(struct page *page, bool clear_pte)  			set_pte_at(&init_mm, address, ptep,  					pfn_pte(pfn, PAGE_KERNEL));  			__flush_tlb_single(address); -			map_op->host_addr = 0; +			kmap_op->host_addr = 0;  		} -	} else -		set_phys_to_machine(pfn, page->index); +	}  	/* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present  	 * somewhere in this domain, even before being added to the  |