diff options
-rw-r--r-- | arch/x86/xen/mmu.c | 201 | ||||
-rw-r--r-- | include/xen/interface/memory.h | 42 | ||||
-rw-r--r-- | include/xen/xen-ops.h | 6 |
3 files changed, 249 insertions, 0 deletions
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 9e0d82fc21e4..eb51402dd99a 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include <asm/paravirt.h> | 53 | #include <asm/paravirt.h> |
54 | #include <asm/e820.h> | 54 | #include <asm/e820.h> |
55 | #include <asm/linkage.h> | 55 | #include <asm/linkage.h> |
56 | #include <asm/page.h> | ||
56 | 57 | ||
57 | #include <asm/xen/hypercall.h> | 58 | #include <asm/xen/hypercall.h> |
58 | #include <asm/xen/hypervisor.h> | 59 | #include <asm/xen/hypervisor.h> |
@@ -2027,6 +2028,206 @@ void __init xen_init_mmu_ops(void) | |||
2027 | pv_mmu_ops = xen_mmu_ops; | 2028 | pv_mmu_ops = xen_mmu_ops; |
2028 | } | 2029 | } |
2029 | 2030 | ||
2031 | /* Protected by xen_reservation_lock. */ | ||
2032 | #define MAX_CONTIG_ORDER 9 /* 2MB */ | ||
2033 | static unsigned long discontig_frames[1<<MAX_CONTIG_ORDER]; | ||
2034 | |||
2035 | #define VOID_PTE (mfn_pte(0, __pgprot(0))) | ||
2036 | static void xen_zap_pfn_range(unsigned long vaddr, unsigned int order, | ||
2037 | unsigned long *in_frames, | ||
2038 | unsigned long *out_frames) | ||
2039 | { | ||
2040 | int i; | ||
2041 | struct multicall_space mcs; | ||
2042 | |||
2043 | xen_mc_batch(); | ||
2044 | for (i = 0; i < (1UL<<order); i++, vaddr += PAGE_SIZE) { | ||
2045 | mcs = __xen_mc_entry(0); | ||
2046 | |||
2047 | if (in_frames) | ||
2048 | in_frames[i] = virt_to_mfn(vaddr); | ||
2049 | |||
2050 | MULTI_update_va_mapping(mcs.mc, vaddr, VOID_PTE, 0); | ||
2051 | set_phys_to_machine(virt_to_pfn(vaddr), INVALID_P2M_ENTRY); | ||
2052 | |||
2053 | if (out_frames) | ||
2054 | out_frames[i] = virt_to_pfn(vaddr); | ||
2055 | } | ||
2056 | xen_mc_issue(0); | ||
2057 | } | ||
2058 | |||
2059 | /* | ||
2060 | * Update the pfn-to-mfn mappings for a virtual address range, either to | ||
2061 | * point to an array of mfns, or contiguously from a single starting | ||
2062 | * mfn. | ||
2063 | */ | ||
2064 | static void xen_remap_exchanged_ptes(unsigned long vaddr, int order, | ||
2065 | unsigned long *mfns, | ||
2066 | unsigned long first_mfn) | ||
2067 | { | ||
2068 | unsigned i, limit; | ||
2069 | unsigned long mfn; | ||
2070 | |||
2071 | xen_mc_batch(); | ||
2072 | |||
2073 | limit = 1u << order; | ||
2074 | for (i = 0; i < limit; i++, vaddr += PAGE_SIZE) { | ||
2075 | struct multicall_space mcs; | ||
2076 | unsigned flags; | ||
2077 | |||
2078 | mcs = __xen_mc_entry(0); | ||
2079 | if (mfns) | ||
2080 | mfn = mfns[i]; | ||
2081 | else | ||
2082 | mfn = first_mfn + i; | ||
2083 | |||
2084 | if (i < (limit - 1)) | ||
2085 | flags = 0; | ||
2086 | else { | ||
2087 | if (order == 0) | ||
2088 | flags = UVMF_INVLPG | UVMF_ALL; | ||
2089 | else | ||
2090 | flags = UVMF_TLB_FLUSH | UVMF_ALL; | ||
2091 | } | ||
2092 | |||
2093 | MULTI_update_va_mapping(mcs.mc, vaddr, | ||
2094 | mfn_pte(mfn, PAGE_KERNEL), flags); | ||
2095 | |||
2096 | set_phys_to_machine(virt_to_pfn(vaddr), mfn); | ||
2097 | } | ||
2098 | |||
2099 | xen_mc_issue(0); | ||
2100 | } | ||
2101 | |||
2102 | /* | ||
2103 | * Perform the hypercall to exchange a region of our pfns to point to | ||
2104 | * memory with the required contiguous alignment. Takes the pfns as | ||
2105 | * input, and populates mfns as output. | ||
2106 | * | ||
2107 | * Returns a success code indicating whether the hypervisor was able to | ||
2108 | * satisfy the request or not. | ||
2109 | */ | ||
2110 | static int xen_exchange_memory(unsigned long extents_in, unsigned int order_in, | ||
2111 | unsigned long *pfns_in, | ||
2112 | unsigned long extents_out, | ||
2113 | unsigned int order_out, | ||
2114 | unsigned long *mfns_out, | ||
2115 | unsigned int address_bits) | ||
2116 | { | ||
2117 | long rc; | ||
2118 | int success; | ||
2119 | |||
2120 | struct xen_memory_exchange exchange = { | ||
2121 | .in = { | ||
2122 | .nr_extents = extents_in, | ||
2123 | .extent_order = order_in, | ||
2124 | .extent_start = pfns_in, | ||
2125 | .domid = DOMID_SELF | ||
2126 | }, | ||
2127 | .out = { | ||
2128 | .nr_extents = extents_out, | ||
2129 | .extent_order = order_out, | ||
2130 | .extent_start = mfns_out, | ||
2131 | .address_bits = address_bits, | ||
2132 | .domid = DOMID_SELF | ||
2133 | } | ||
2134 | }; | ||
2135 | |||
2136 | BUG_ON(extents_in << order_in != extents_out << order_out); | ||
2137 | |||
2138 | rc = HYPERVISOR_memory_op(XENMEM_exchange, &exchange); | ||
2139 | success = (exchange.nr_exchanged == extents_in); | ||
2140 | |||
2141 | BUG_ON(!success && ((exchange.nr_exchanged != 0) || (rc == 0))); | ||
2142 | BUG_ON(success && (rc != 0)); | ||
2143 | |||
2144 | return success; | ||
2145 | } | ||
2146 | |||
2147 | int xen_create_contiguous_region(unsigned long vstart, unsigned int order, | ||
2148 | unsigned int address_bits) | ||
2149 | { | ||
2150 | unsigned long *in_frames = discontig_frames, out_frame; | ||
2151 | unsigned long flags; | ||
2152 | int success; | ||
2153 | |||
2154 | /* | ||
2155 | * Currently an auto-translated guest will not perform I/O, nor will | ||
2156 | * it require PAE page directories below 4GB. Therefore any calls to | ||
2157 | * this function are redundant and can be ignored. | ||
2158 | */ | ||
2159 | |||
2160 | if (xen_feature(XENFEAT_auto_translated_physmap)) | ||
2161 | return 0; | ||
2162 | |||
2163 | if (unlikely(order > MAX_CONTIG_ORDER)) | ||
2164 | return -ENOMEM; | ||
2165 | |||
2166 | memset((void *) vstart, 0, PAGE_SIZE << order); | ||
2167 | |||
2168 | vm_unmap_aliases(); | ||
2169 | |||
2170 | spin_lock_irqsave(&xen_reservation_lock, flags); | ||
2171 | |||
2172 | /* 1. Zap current PTEs, remembering MFNs. */ | ||
2173 | xen_zap_pfn_range(vstart, order, in_frames, NULL); | ||
2174 | |||
2175 | /* 2. Get a new contiguous memory extent. */ | ||
2176 | out_frame = virt_to_pfn(vstart); | ||
2177 | success = xen_exchange_memory(1UL << order, 0, in_frames, | ||
2178 | 1, order, &out_frame, | ||
2179 | address_bits); | ||
2180 | |||
2181 | /* 3. Map the new extent in place of old pages. */ | ||
2182 | if (success) | ||
2183 | xen_remap_exchanged_ptes(vstart, order, NULL, out_frame); | ||
2184 | else | ||
2185 | xen_remap_exchanged_ptes(vstart, order, in_frames, 0); | ||
2186 | |||
2187 | spin_unlock_irqrestore(&xen_reservation_lock, flags); | ||
2188 | |||
2189 | return success ? 0 : -ENOMEM; | ||
2190 | } | ||
2191 | EXPORT_SYMBOL_GPL(xen_create_contiguous_region); | ||
2192 | |||
2193 | void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order) | ||
2194 | { | ||
2195 | unsigned long *out_frames = discontig_frames, in_frame; | ||
2196 | unsigned long flags; | ||
2197 | int success; | ||
2198 | |||
2199 | if (xen_feature(XENFEAT_auto_translated_physmap)) | ||
2200 | return; | ||
2201 | |||
2202 | if (unlikely(order > MAX_CONTIG_ORDER)) | ||
2203 | return; | ||
2204 | |||
2205 | memset((void *) vstart, 0, PAGE_SIZE << order); | ||
2206 | |||
2207 | vm_unmap_aliases(); | ||
2208 | |||
2209 | spin_lock_irqsave(&xen_reservation_lock, flags); | ||
2210 | |||
2211 | /* 1. Find start MFN of contiguous extent. */ | ||
2212 | in_frame = virt_to_mfn(vstart); | ||
2213 | |||
2214 | /* 2. Zap current PTEs. */ | ||
2215 | xen_zap_pfn_range(vstart, order, NULL, out_frames); | ||
2216 | |||
2217 | /* 3. Do the exchange for non-contiguous MFNs. */ | ||
2218 | success = xen_exchange_memory(1, order, &in_frame, 1UL << order, | ||
2219 | 0, out_frames, 0); | ||
2220 | |||
2221 | /* 4. Map new pages in place of old pages. */ | ||
2222 | if (success) | ||
2223 | xen_remap_exchanged_ptes(vstart, order, out_frames, 0); | ||
2224 | else | ||
2225 | xen_remap_exchanged_ptes(vstart, order, NULL, in_frame); | ||
2226 | |||
2227 | spin_unlock_irqrestore(&xen_reservation_lock, flags); | ||
2228 | } | ||
2229 | EXPORT_SYMBOL_GPL(xen_destroy_contiguous_region); | ||
2230 | |||
2030 | #ifdef CONFIG_XEN_DEBUG_FS | 2231 | #ifdef CONFIG_XEN_DEBUG_FS |
2031 | 2232 | ||
2032 | static struct dentry *d_mmu_debug; | 2233 | static struct dentry *d_mmu_debug; |
diff --git a/include/xen/interface/memory.h b/include/xen/interface/memory.h index e6adce6bc75c..d3938d3e71f8 100644 --- a/include/xen/interface/memory.h +++ b/include/xen/interface/memory.h | |||
@@ -55,6 +55,48 @@ struct xen_memory_reservation { | |||
55 | DEFINE_GUEST_HANDLE_STRUCT(xen_memory_reservation); | 55 | DEFINE_GUEST_HANDLE_STRUCT(xen_memory_reservation); |
56 | 56 | ||
57 | /* | 57 | /* |
58 | * An atomic exchange of memory pages. If return code is zero then | ||
59 | * @out.extent_list provides GMFNs of the newly-allocated memory. | ||
60 | * Returns zero on complete success, otherwise a negative error code. | ||
61 | * On complete success then always @nr_exchanged == @in.nr_extents. | ||
62 | * On partial success @nr_exchanged indicates how much work was done. | ||
63 | */ | ||
64 | #define XENMEM_exchange 11 | ||
65 | struct xen_memory_exchange { | ||
66 | /* | ||
67 | * [IN] Details of memory extents to be exchanged (GMFN bases). | ||
68 | * Note that @in.address_bits is ignored and unused. | ||
69 | */ | ||
70 | struct xen_memory_reservation in; | ||
71 | |||
72 | /* | ||
73 | * [IN/OUT] Details of new memory extents. | ||
74 | * We require that: | ||
75 | * 1. @in.domid == @out.domid | ||
76 | * 2. @in.nr_extents << @in.extent_order == | ||
77 | * @out.nr_extents << @out.extent_order | ||
78 | * 3. @in.extent_start and @out.extent_start lists must not overlap | ||
79 | * 4. @out.extent_start lists GPFN bases to be populated | ||
80 | * 5. @out.extent_start is overwritten with allocated GMFN bases | ||
81 | */ | ||
82 | struct xen_memory_reservation out; | ||
83 | |||
84 | /* | ||
85 | * [OUT] Number of input extents that were successfully exchanged: | ||
86 | * 1. The first @nr_exchanged input extents were successfully | ||
87 | * deallocated. | ||
88 | * 2. The corresponding first entries in the output extent list correctly | ||
89 | * indicate the GMFNs that were successfully exchanged. | ||
90 | * 3. All other input and output extents are untouched. | ||
91 | * 4. If not all input exents are exchanged then the return code of this | ||
92 | * command will be non-zero. | ||
93 | * 5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER! | ||
94 | */ | ||
95 | unsigned long nr_exchanged; | ||
96 | }; | ||
97 | |||
98 | DEFINE_GUEST_HANDLE_STRUCT(xen_memory_exchange); | ||
99 | /* | ||
58 | * Returns the maximum machine frame number of mapped RAM in this system. | 100 | * Returns the maximum machine frame number of mapped RAM in this system. |
59 | * This command always succeeds (it never returns an error code). | 101 | * This command always succeeds (it never returns an error code). |
60 | * arg == NULL. | 102 | * arg == NULL. |
diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 883a21bba24b..d789c937c48a 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h | |||
@@ -14,4 +14,10 @@ void xen_mm_unpin_all(void); | |||
14 | void xen_timer_resume(void); | 14 | void xen_timer_resume(void); |
15 | void xen_arch_resume(void); | 15 | void xen_arch_resume(void); |
16 | 16 | ||
17 | extern unsigned long *xen_contiguous_bitmap; | ||
18 | int xen_create_contiguous_region(unsigned long vstart, unsigned int order, | ||
19 | unsigned int address_bits); | ||
20 | |||
21 | void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order); | ||
22 | |||
17 | #endif /* INCLUDE_XEN_OPS_H */ | 23 | #endif /* INCLUDE_XEN_OPS_H */ |