pgalloc.c 3.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/* pgalloc.c: page directory & page table allocation
 *
 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/sched.h>
13
#include <linux/gfp.h>
L
Linus Torvalds 已提交
14 15
#include <linux/mm.h>
#include <linux/highmem.h>
16
#include <linux/quicklist.h>
L
Linus Torvalds 已提交
17 18 19 20 21 22 23 24
#include <asm/pgalloc.h>
#include <asm/page.h>
#include <asm/cacheflush.h>

pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((aligned(PAGE_SIZE)));

pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
{
25
	pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL);
L
Linus Torvalds 已提交
26 27 28 29 30
	if (pte)
		clear_page(pte);
	return pte;
}

31
pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
L
Linus Torvalds 已提交
32 33 34 35
{
	struct page *page;

#ifdef CONFIG_HIGHPTE
36
	page = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM, 0);
L
Linus Torvalds 已提交
37
#else
38
	page = alloc_pages(GFP_KERNEL, 0);
L
Linus Torvalds 已提交
39
#endif
40 41 42 43 44 45 46
	if (!page)
		return NULL;

	clear_highpage(page);
	if (!pgtable_page_ctor(page)) {
		__free_page(page);
		return NULL;
47
	}
48
	flush_dcache_page(page);
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
	return page;
}

void __set_pmd(pmd_t *pmdptr, unsigned long pmd)
{
	unsigned long *__ste_p = pmdptr->ste;
	int loop;

	if (!pmd) {
		memset(__ste_p, 0, PME_SIZE);
	}
	else {
		BUG_ON(pmd & (0x3f00 | xAMPRx_SS | 0xe));

		for (loop = PME_SIZE; loop > 0; loop -= 4) {
			*__ste_p++ = pmd;
			pmd += __frv_PT_SIZE;
		}
	}

	frv_dcache_writeback((unsigned long) pmdptr, (unsigned long) (pmdptr + 1));
}

/*
 * 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.
 * If the locking proves to be non-performant, a ticketing scheme with
 * checks at dup_mmap(), exec(), and other mmlist addition points
 * could be used. The locking scheme was chosen on the basis of
 * manfred's recommendations and having no core impact whatsoever.
84
 * -- nyc
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93
 */
DEFINE_SPINLOCK(pgd_lock);
struct page *pgd_list;

static inline void pgd_list_add(pgd_t *pgd)
{
	struct page *page = virt_to_page(pgd);
	page->index = (unsigned long) pgd_list;
	if (pgd_list)
94
		set_page_private(pgd_list, (unsigned long) &page->index);
L
Linus Torvalds 已提交
95
	pgd_list = page;
H
Hugh Dickins 已提交
96
	set_page_private(page, (unsigned long)&pgd_list);
L
Linus Torvalds 已提交
97 98 99 100 101 102
}

static inline void pgd_list_del(pgd_t *pgd)
{
	struct page *next, **pprev, *page = virt_to_page(pgd);
	next = (struct page *) page->index;
103
	pprev = (struct page **) page_private(page);
L
Linus Torvalds 已提交
104 105
	*pprev = next;
	if (next)
106
		set_page_private(next, (unsigned long) pprev);
L
Linus Torvalds 已提交
107 108
}

109
void pgd_ctor(void *pgd)
L
Linus Torvalds 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
{
	unsigned long flags;

	if (PTRS_PER_PMD == 1)
		spin_lock_irqsave(&pgd_lock, flags);

	memcpy((pgd_t *) pgd + USER_PGDS_IN_LAST_PML4,
	       swapper_pg_dir + USER_PGDS_IN_LAST_PML4,
	       (PTRS_PER_PGD - USER_PGDS_IN_LAST_PML4) * sizeof(pgd_t));

	if (PTRS_PER_PMD > 1)
		return;

	pgd_list_add(pgd);
	spin_unlock_irqrestore(&pgd_lock, flags);
	memset(pgd, 0, USER_PGDS_IN_LAST_PML4 * sizeof(pgd_t));
}

/* never called when PTRS_PER_PMD > 1 */
129
void pgd_dtor(void *pgd)
L
Linus Torvalds 已提交
130 131 132 133 134 135 136 137 138 139
{
	unsigned long flags; /* can be called from interrupt context */

	spin_lock_irqsave(&pgd_lock, flags);
	pgd_list_del(pgd);
	spin_unlock_irqrestore(&pgd_lock, flags);
}

pgd_t *pgd_alloc(struct mm_struct *mm)
{
G
Greg Dietsche 已提交
140
	return quicklist_alloc(0, GFP_KERNEL, pgd_ctor);
L
Linus Torvalds 已提交
141 142
}

143
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
L
Linus Torvalds 已提交
144 145
{
	/* in the non-PAE case, clear_page_tables() clears user pgd entries */
146
 	quicklist_free(0, pgd_dtor, pgd);
L
Linus Torvalds 已提交
147 148 149 150 151
}

void __init pgtable_cache_init(void)
{
}
152 153 154 155 156 157

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