pgtable_32.c 9.2 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
}

C
Christoph Lameter 已提交
222 223


224 225
#if (PTRS_PER_PMD == 1)
/* Non-PAE pgd constructor */
A
Adrian Bunk 已提交
226
static void pgd_ctor(void *pgd)
L
Linus Torvalds 已提交
227 228 229
{
	unsigned long flags;

230 231 232 233
	/* !PAE, no pagetable sharing */
	memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t));

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

235
	/* must happen under lock */
236
	clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD,
L
Linus Torvalds 已提交
237
			swapper_pg_dir + USER_PTRS_PER_PGD,
238
			KERNEL_PGD_PTRS);
239
	paravirt_alloc_pd_clone(__pa(pgd) >> PAGE_SHIFT,
240 241 242
				__pa(swapper_pg_dir) >> PAGE_SHIFT,
				USER_PTRS_PER_PGD,
				KERNEL_PGD_PTRS);
L
Linus Torvalds 已提交
243 244 245
	pgd_list_add(pgd);
	spin_unlock_irqrestore(&pgd_lock, flags);
}
246 247
#else  /* PTRS_PER_PMD > 1 */
/* PAE pgd constructor */
A
Adrian Bunk 已提交
248
static void pgd_ctor(void *pgd)
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
{
	/* PAE, kernel PMD may be shared */

	if (SHARED_KERNEL_PMD) {
		clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD,
				swapper_pg_dir + USER_PTRS_PER_PGD,
				KERNEL_PGD_PTRS);
	} else {
		unsigned long flags;

		memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t));
		spin_lock_irqsave(&pgd_lock, flags);
		pgd_list_add(pgd);
		spin_unlock_irqrestore(&pgd_lock, flags);
	}
}
#endif	/* PTRS_PER_PMD */
L
Linus Torvalds 已提交
266

A
Adrian Bunk 已提交
267
static void pgd_dtor(void *pgd)
L
Linus Torvalds 已提交
268 269 270
{
	unsigned long flags; /* can be called from interrupt context */

C
Christoph Lameter 已提交
271 272
	if (SHARED_KERNEL_PMD)
		return;
273

L
Linus Torvalds 已提交
274 275 276 277 278
	spin_lock_irqsave(&pgd_lock, flags);
	pgd_list_del(pgd);
	spin_unlock_irqrestore(&pgd_lock, flags);
}

279 280 281
#define UNSHARED_PTRS_PER_PGD				\
	(SHARED_KERNEL_PMD ? USER_PTRS_PER_PGD : PTRS_PER_PGD)

282 283 284 285 286 287 288 289 290 291 292
#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;

293
	for(i = 0; i < UNSHARED_PTRS_PER_PGD; i++) {
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
		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.
313 314 315 316
 *
 * 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.
317 318 319 320 321 322 323 324
 */
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);
325 326
 	for (addr = i = 0; i < UNSHARED_PTRS_PER_PGD;
	     i++, pud++, addr += PUD_SIZE) {
327 328 329 330 331 332 333
		pmd_t *pmd = pmd_alloc_one(mm, addr);

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

334 335 336 337
		if (i >= USER_PTRS_PER_PGD)
			memcpy(pmd, (pmd_t *)pgd_page_vaddr(swapper_pg_dir[i]),
			       sizeof(pmd_t) * PTRS_PER_PMD);

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
		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 已提交
355 356
pgd_t *pgd_alloc(struct mm_struct *mm)
{
C
Christoph Lameter 已提交
357
	pgd_t *pgd = quicklist_alloc(0, GFP_KERNEL, pgd_ctor);
L
Linus Torvalds 已提交
358

359 360
	mm->pgd = pgd;		/* so that alloc_pd can use it */

361 362 363 364 365
	if (pgd && !pgd_prepopulate_pmd(mm, pgd)) {
		quicklist_free(0, pgd_dtor, pgd);
		pgd = NULL;
	}

L
Linus Torvalds 已提交
366 367 368 369 370
	return pgd;
}

void pgd_free(pgd_t *pgd)
{
371
	pgd_mop_up_pmds(pgd);
C
Christoph Lameter 已提交
372
	quicklist_free(0, pgd_dtor, pgd);
L
Linus Torvalds 已提交
373
}
C
Christoph Lameter 已提交
374 375 376 377 378

void check_pgt_cache(void)
{
	quicklist_trim(0, pgd_dtor, 25, 16);
}