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 */ |
