diff options
Diffstat (limited to 'arch/i386/mm/pgtable.c')
-rw-r--r-- | arch/i386/mm/pgtable.c | 24 |
1 files changed, 20 insertions, 4 deletions
diff --git a/arch/i386/mm/pgtable.c b/arch/i386/mm/pgtable.c index f349eaf450b0..b5f538f52272 100644 --- a/arch/i386/mm/pgtable.c +++ b/arch/i386/mm/pgtable.c | |||
@@ -248,9 +248,15 @@ void pgd_ctor(void *pgd, struct kmem_cache *cache, unsigned long unused) | |||
248 | clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD, | 248 | clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD, |
249 | swapper_pg_dir + USER_PTRS_PER_PGD, | 249 | swapper_pg_dir + USER_PTRS_PER_PGD, |
250 | KERNEL_PGD_PTRS); | 250 | KERNEL_PGD_PTRS); |
251 | |||
251 | if (PTRS_PER_PMD > 1) | 252 | if (PTRS_PER_PMD > 1) |
252 | return; | 253 | return; |
253 | 254 | ||
255 | /* must happen under lock */ | ||
256 | paravirt_alloc_pd_clone(__pa(pgd) >> PAGE_SHIFT, | ||
257 | __pa(swapper_pg_dir) >> PAGE_SHIFT, | ||
258 | USER_PTRS_PER_PGD, PTRS_PER_PGD - USER_PTRS_PER_PGD); | ||
259 | |||
254 | pgd_list_add(pgd); | 260 | pgd_list_add(pgd); |
255 | spin_unlock_irqrestore(&pgd_lock, flags); | 261 | spin_unlock_irqrestore(&pgd_lock, flags); |
256 | } | 262 | } |
@@ -260,6 +266,7 @@ void pgd_dtor(void *pgd, struct kmem_cache *cache, unsigned long unused) | |||
260 | { | 266 | { |
261 | unsigned long flags; /* can be called from interrupt context */ | 267 | unsigned long flags; /* can be called from interrupt context */ |
262 | 268 | ||
269 | paravirt_release_pd(__pa(pgd) >> PAGE_SHIFT); | ||
263 | spin_lock_irqsave(&pgd_lock, flags); | 270 | spin_lock_irqsave(&pgd_lock, flags); |
264 | pgd_list_del(pgd); | 271 | pgd_list_del(pgd); |
265 | spin_unlock_irqrestore(&pgd_lock, flags); | 272 | spin_unlock_irqrestore(&pgd_lock, flags); |
@@ -277,13 +284,18 @@ pgd_t *pgd_alloc(struct mm_struct *mm) | |||
277 | pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL); | 284 | pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL); |
278 | if (!pmd) | 285 | if (!pmd) |
279 | goto out_oom; | 286 | goto out_oom; |
287 | paravirt_alloc_pd(__pa(pmd) >> PAGE_SHIFT); | ||
280 | set_pgd(&pgd[i], __pgd(1 + __pa(pmd))); | 288 | set_pgd(&pgd[i], __pgd(1 + __pa(pmd))); |
281 | } | 289 | } |
282 | return pgd; | 290 | return pgd; |
283 | 291 | ||
284 | out_oom: | 292 | out_oom: |
285 | for (i--; i >= 0; i--) | 293 | for (i--; i >= 0; i--) { |
286 | kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1)); | 294 | pgd_t pgdent = pgd[i]; |
295 | void* pmd = (void *)__va(pgd_val(pgdent)-1); | ||
296 | paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT); | ||
297 | kmem_cache_free(pmd_cache, pmd); | ||
298 | } | ||
287 | kmem_cache_free(pgd_cache, pgd); | 299 | kmem_cache_free(pgd_cache, pgd); |
288 | return NULL; | 300 | return NULL; |
289 | } | 301 | } |
@@ -294,8 +306,12 @@ void pgd_free(pgd_t *pgd) | |||
294 | 306 | ||
295 | /* in the PAE case user pgd entries are overwritten before usage */ | 307 | /* in the PAE case user pgd entries are overwritten before usage */ |
296 | if (PTRS_PER_PMD > 1) | 308 | if (PTRS_PER_PMD > 1) |
297 | for (i = 0; i < USER_PTRS_PER_PGD; ++i) | 309 | for (i = 0; i < USER_PTRS_PER_PGD; ++i) { |
298 | kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1)); | 310 | pgd_t pgdent = pgd[i]; |
311 | void* pmd = (void *)__va(pgd_val(pgdent)-1); | ||
312 | paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT); | ||
313 | kmem_cache_free(pmd_cache, pmd); | ||
314 | } | ||
299 | /* in the non-PAE case, free_pgtables() clears user pgd entries */ | 315 | /* in the non-PAE case, free_pgtables() clears user pgd entries */ |
300 | kmem_cache_free(pgd_cache, pgd); | 316 | kmem_cache_free(pgd_cache, pgd); |
301 | } | 317 | } |