pgtable_32.c 9.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 *  linux/arch/i386/mm/pgtable.c
 */

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>
9
#include <linux/nmi.h>
L
Linus Torvalds 已提交
10 11 12 13 14 15
#include <linux/swap.h>
#include <linux/smp.h>
#include <linux/highmem.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/spinlock.h>
16
#include <linux/module.h>
C
Christoph Lameter 已提交
17
#include <linux/quicklist.h>
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/fixmap.h>
#include <asm/e820.h>
#include <asm/tlb.h>
#include <asm/tlbflush.h>

void show_mem(void)
{
	int total = 0, reserved = 0;
	int shared = 0, cached = 0;
	int highmem = 0;
	struct page *page;
	pg_data_t *pgdat;
	unsigned long i;
35
	unsigned long flags;
L
Linus Torvalds 已提交
36

37
	printk(KERN_INFO "Mem-info:\n");
L
Linus Torvalds 已提交
38
	show_free_areas();
39
	printk(KERN_INFO "Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
40
	for_each_online_pgdat(pgdat) {
41
		pgdat_resize_lock(pgdat, &flags);
L
Linus Torvalds 已提交
42
		for (i = 0; i < pgdat->node_spanned_pages; ++i) {
43 44
			if (unlikely(i % MAX_ORDER_NR_PAGES == 0))
				touch_nmi_watchdog();
45
			page = pgdat_page_nr(pgdat, i);
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55
			total++;
			if (PageHighMem(page))
				highmem++;
			if (PageReserved(page))
				reserved++;
			else if (PageSwapCache(page))
				cached++;
			else if (page_count(page))
				shared += page_count(page) - 1;
		}
56
		pgdat_resize_unlock(pgdat, &flags);
L
Linus Torvalds 已提交
57
	}
58 59 60 61 62
	printk(KERN_INFO "%d pages of RAM\n", total);
	printk(KERN_INFO "%d pages of HIGHMEM\n", highmem);
	printk(KERN_INFO "%d reserved pages\n", reserved);
	printk(KERN_INFO "%d pages shared\n", shared);
	printk(KERN_INFO "%d pages swap cached\n", cached);
63

64
	printk(KERN_INFO "%lu pages dirty\n", global_page_state(NR_FILE_DIRTY));
65 66
	printk(KERN_INFO "%lu pages writeback\n",
					global_page_state(NR_WRITEBACK));
67
	printk(KERN_INFO "%lu pages mapped\n", global_page_state(NR_FILE_MAPPED));
68 69 70
	printk(KERN_INFO "%lu pages slab\n",
		global_page_state(NR_SLAB_RECLAIMABLE) +
		global_page_state(NR_SLAB_UNRECLAIMABLE));
71 72
	printk(KERN_INFO "%lu pages pagetables\n",
					global_page_state(NR_PAGETABLE));
L
Linus Torvalds 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
}

/*
 * Associate a virtual page frame with a given physical page frame 
 * and protection flags for that frame.
 */ 
static void set_pte_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;

	pgd = swapper_pg_dir + pgd_index(vaddr);
	if (pgd_none(*pgd)) {
		BUG();
		return;
	}
	pud = pud_offset(pgd, vaddr);
	if (pud_none(*pud)) {
		BUG();
		return;
	}
	pmd = pmd_offset(pud, vaddr);
	if (pmd_none(*pmd)) {
		BUG();
		return;
	}
	pte = pte_offset_kernel(pmd, vaddr);
102
	if (pgprot_val(flags))
103
		set_pte_present(&init_mm, vaddr, pte, pfn_pte(pfn, flags));
104 105
	else
		pte_clear(&init_mm, vaddr, pte);
L
Linus Torvalds 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

	/*
	 * It's enough to flush this one mapping.
	 * (PGE mappings get flushed as well)
	 */
	__flush_tlb_one(vaddr);
}

/*
 * Associate a large virtual page frame with a given physical page frame 
 * and protection flags for that frame. pfn is for the base of the page,
 * vaddr is what the page gets mapped to - both must be properly aligned. 
 * The pmd must already be instantiated. Assumes PAE mode.
 */ 
void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;

	if (vaddr & (PMD_SIZE-1)) {		/* vaddr is misaligned */
127
		printk(KERN_WARNING "set_pmd_pfn: vaddr misaligned\n");
L
Linus Torvalds 已提交
128 129 130
		return; /* BUG(); */
	}
	if (pfn & (PTRS_PER_PTE-1)) {		/* pfn is misaligned */
131
		printk(KERN_WARNING "set_pmd_pfn: pfn misaligned\n");
L
Linus Torvalds 已提交
132 133 134 135
		return; /* BUG(); */
	}
	pgd = swapper_pg_dir + pgd_index(vaddr);
	if (pgd_none(*pgd)) {
136
		printk(KERN_WARNING "set_pmd_pfn: pgd_none\n");
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148
		return; /* BUG(); */
	}
	pud = pud_offset(pgd, vaddr);
	pmd = pmd_offset(pud, vaddr);
	set_pmd(pmd, pfn_pmd(pfn, flags));
	/*
	 * It's enough to flush this one mapping.
	 * (PGE mappings get flushed as well)
	 */
	__flush_tlb_one(vaddr);
}

149 150 151 152
static int fixmaps;
unsigned long __FIXADDR_TOP = 0xfffff000;
EXPORT_SYMBOL(__FIXADDR_TOP);

L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160 161
void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t flags)
{
	unsigned long address = __fix_to_virt(idx);

	if (idx >= __end_of_fixed_addresses) {
		BUG();
		return;
	}
	set_pte_pfn(address, phys >> PAGE_SHIFT, flags);
162 163 164 165 166 167 168 169 170 171 172 173 174
	fixmaps++;
}

/**
 * reserve_top_address - reserves a hole in the top of kernel address space
 * @reserve - size of hole to reserve
 *
 * Can be used to relocate the fixmap area and poke a hole in the top
 * of kernel address space to make room for a hypervisor.
 */
void reserve_top_address(unsigned long reserve)
{
	BUG_ON(fixmaps > 0);
175 176
	printk(KERN_INFO "Reserving virtual address space above 0x%08x\n",
	       (int)-reserve);
177 178
	__FIXADDR_TOP = -reserve - PAGE_SIZE;
	__VMALLOC_RESERVE += reserve;
L
Linus Torvalds 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
}

pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
{
	return (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
}

struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
{
	struct page *pte;

#ifdef CONFIG_HIGHPTE
	pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT|__GFP_ZERO, 0);
#else
	pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
#endif
	return pte;
}

/*
 * List of all pgd's needed for non-PAE so it can invalidate entries
 * in both cached and uncached pgd's; not needed for PAE since the
 * kernel pmd is shared. If PAE were not to share the pmd a similar
 * tactic would be needed. This is essentially codepath-based locking
 * against pageattr.c; it is the unique case in which a valid change
 * of kernel pagetables can't be lazily synchronized by vmalloc faults.
 * vmalloc faults work because attached pagetables are never freed.
 * -- wli
 */
static inline void pgd_list_add(pgd_t *pgd)
{
	struct page *page = virt_to_page(pgd);
211 212

	list_add(&page->lru, &pgd_list);
L
Linus Torvalds 已提交
213 214 215 216
}

static inline void pgd_list_del(pgd_t *pgd)
{
217 218 219
	struct page *page = virt_to_page(pgd);

	list_del(&page->lru);
L
Linus Torvalds 已提交
220 221
}

222 223
#define UNSHARED_PTRS_PER_PGD				\
	(SHARED_KERNEL_PMD ? USER_PTRS_PER_PGD : PTRS_PER_PGD)
C
Christoph Lameter 已提交
224

225
static void pgd_ctor(void *p)
L
Linus Torvalds 已提交
226
{
227
	pgd_t *pgd = p;
L
Linus Torvalds 已提交
228 229
	unsigned long flags;

230
	/* Clear usermode parts of PGD */
231 232 233
	memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t));

	spin_lock_irqsave(&pgd_lock, flags);
L
Linus Torvalds 已提交
234

235 236 237 238 239 240
	/* If the pgd points to a shared pagetable level (either the
	   ptes in non-PAE, or shared PMD in PAE), then just copy the
	   references from swapper_pg_dir. */
	if (PAGETABLE_LEVELS == 2 ||
	    (PAGETABLE_LEVELS == 3 && SHARED_KERNEL_PMD)) {
		clone_pgd_range(pgd + USER_PTRS_PER_PGD,
241 242
				swapper_pg_dir + USER_PTRS_PER_PGD,
				KERNEL_PGD_PTRS);
243 244 245 246 247
		paravirt_alloc_pd_clone(__pa(pgd) >> PAGE_SHIFT,
					__pa(swapper_pg_dir) >> PAGE_SHIFT,
					USER_PTRS_PER_PGD,
					KERNEL_PGD_PTRS);
	}
248

249 250
	/* list required to sync kernel mapping updates */
	if (!SHARED_KERNEL_PMD)
251
		pgd_list_add(pgd);
252 253

	spin_unlock_irqrestore(&pgd_lock, flags);
254
}
L
Linus Torvalds 已提交
255

A
Adrian Bunk 已提交
256
static void pgd_dtor(void *pgd)
L
Linus Torvalds 已提交
257 258 259
{
	unsigned long flags; /* can be called from interrupt context */

C
Christoph Lameter 已提交
260 261
	if (SHARED_KERNEL_PMD)
		return;
262

L
Linus Torvalds 已提交
263 264 265 266 267
	spin_lock_irqsave(&pgd_lock, flags);
	pgd_list_del(pgd);
	spin_unlock_irqrestore(&pgd_lock, flags);
}

268 269 270 271 272 273 274 275 276 277 278
#ifdef CONFIG_X86_PAE
/*
 * Mop up any pmd pages which may still be attached to the pgd.
 * Normally they will be freed by munmap/exit_mmap, but any pmd we
 * preallocate which never got a corresponding vma will need to be
 * freed manually.
 */
static void pgd_mop_up_pmds(pgd_t *pgdp)
{
	int i;

279
	for(i = 0; i < UNSHARED_PTRS_PER_PGD; i++) {
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
		pgd_t pgd = pgdp[i];

		if (pgd_val(pgd) != 0) {
			pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd);

			pgdp[i] = native_make_pgd(0);

			paravirt_release_pd(pgd_val(pgd) >> PAGE_SHIFT);
			pmd_free(pmd);
		}
	}
}

/*
 * In PAE mode, we need to do a cr3 reload (=tlb flush) when
 * updating the top-level pagetable entries to guarantee the
 * processor notices the update.  Since this is expensive, and
 * all 4 top-level entries are used almost immediately in a
 * new process's life, we just pre-populate them here.
299 300 301 302
 *
 * Also, if we're in a paravirt environment where the kernel pmd is
 * not shared between pagetables (!SHARED_KERNEL_PMDS), we allocate
 * and initialize the kernel pmds here.
303 304 305 306 307 308 309 310
 */
static int pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd)
{
	pud_t *pud;
	unsigned long addr;
	int i;

	pud = pud_offset(pgd, 0);
311 312
 	for (addr = i = 0; i < UNSHARED_PTRS_PER_PGD;
	     i++, pud++, addr += PUD_SIZE) {
313 314 315 316 317 318 319
		pmd_t *pmd = pmd_alloc_one(mm, addr);

		if (!pmd) {
			pgd_mop_up_pmds(pgd);
			return 0;
		}

320 321 322 323
		if (i >= USER_PTRS_PER_PGD)
			memcpy(pmd, (pmd_t *)pgd_page_vaddr(swapper_pg_dir[i]),
			       sizeof(pmd_t) * PTRS_PER_PMD);

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
		pud_populate(mm, pud, pmd);
	}

	return 1;
}
#else  /* !CONFIG_X86_PAE */
/* No need to prepopulate any pagetable entries in non-PAE modes. */
static int pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd)
{
	return 1;
}

static void pgd_mop_up_pmds(pgd_t *pgd)
{
}
#endif	/* CONFIG_X86_PAE */

L
Linus Torvalds 已提交
341 342
pgd_t *pgd_alloc(struct mm_struct *mm)
{
C
Christoph Lameter 已提交
343
	pgd_t *pgd = quicklist_alloc(0, GFP_KERNEL, pgd_ctor);
L
Linus Torvalds 已提交
344

345 346
	mm->pgd = pgd;		/* so that alloc_pd can use it */

347 348 349 350 351
	if (pgd && !pgd_prepopulate_pmd(mm, pgd)) {
		quicklist_free(0, pgd_dtor, pgd);
		pgd = NULL;
	}

L
Linus Torvalds 已提交
352 353 354 355 356
	return pgd;
}

void pgd_free(pgd_t *pgd)
{
357
	pgd_mop_up_pmds(pgd);
C
Christoph Lameter 已提交
358
	quicklist_free(0, pgd_dtor, pgd);
L
Linus Torvalds 已提交
359
}
C
Christoph Lameter 已提交
360 361 362 363 364

void check_pgt_cache(void)
{
	quicklist_trim(0, pgd_dtor, 25, 16);
}
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte)
{
	paravirt_release_pt(page_to_pfn(pte));
	tlb_remove_page(tlb, pte);
}

#ifdef CONFIG_X86_PAE

void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
{
	paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT);
	tlb_remove_page(tlb, virt_to_page(pmd));
}

#endif