diff options
author | Hugh Dickins <hugh@veritas.com> | 2006-02-14 16:52:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-14 19:09:33 -0500 |
commit | 41d78ba55037468e6c86c53e3076d1a74841de39 (patch) | |
tree | d970f18d18532009b17c736583429401dbd64ade /mm | |
parent | 7277232374680595cdbc774fd246b206f56db015 (diff) |
[PATCH] compound page: use page[1].lru
If a compound page has its own put_page_testzero destructor (the only current
example is free_huge_page), that is noted in page[1].mapping of the compound
page. But that's rather a poor place to keep it: functions which call
set_page_dirty_lock after get_user_pages (e.g. Infiniband's
__ib_umem_release) ought to be checking first, otherwise set_page_dirty is
liable to crash on what's not the address of a struct address_space.
And now I'm about to make that worse: it turns out that every compound page
needs a destructor, so we can no longer rely on hugetlb pages going their own
special way, to avoid further problems of page->mapping reuse. For example,
not many people know that: on 50% of i386 -Os builds, the first tail page of a
compound page purports to be PageAnon (when its destructor has an odd
address), which surprises page_add_file_rmap.
Keep the compound page destructor in page[1].lru.next instead. And to free up
the common pairing of mapping and index, also move compound page order from
index to lru.prev. Slab reuses page->lru too: but if we ever need slab to use
compound pages, it can easily stack its use above this.
(akpm: decoded version of the above: the tail pages of a compound page now
have ->mapping==NULL, so there's no need for the set_page_dirty[_lock]()
caller to check that they're not compund pages before doing the dirty).
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/hugetlb.c | 4 | ||||
-rw-r--r-- | mm/page_alloc.c | 15 | ||||
-rw-r--r-- | mm/swap.c | 2 |
3 files changed, 9 insertions, 12 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 67f29516662a..508707704d2c 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c | |||
@@ -85,7 +85,7 @@ void free_huge_page(struct page *page) | |||
85 | BUG_ON(page_count(page)); | 85 | BUG_ON(page_count(page)); |
86 | 86 | ||
87 | INIT_LIST_HEAD(&page->lru); | 87 | INIT_LIST_HEAD(&page->lru); |
88 | page[1].mapping = NULL; | 88 | page[1].lru.next = NULL; /* reset dtor */ |
89 | 89 | ||
90 | spin_lock(&hugetlb_lock); | 90 | spin_lock(&hugetlb_lock); |
91 | enqueue_huge_page(page); | 91 | enqueue_huge_page(page); |
@@ -105,7 +105,7 @@ struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr) | |||
105 | } | 105 | } |
106 | spin_unlock(&hugetlb_lock); | 106 | spin_unlock(&hugetlb_lock); |
107 | set_page_count(page, 1); | 107 | set_page_count(page, 1); |
108 | page[1].mapping = (void *)free_huge_page; | 108 | page[1].lru.next = (void *)free_huge_page; /* set dtor */ |
109 | for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); ++i) | 109 | for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); ++i) |
110 | clear_user_highpage(&page[i], addr); | 110 | clear_user_highpage(&page[i], addr); |
111 | return page; | 111 | return page; |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index dde04ff4be31..eec89ab39bb6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -169,20 +169,17 @@ static void bad_page(struct page *page) | |||
169 | * All pages have PG_compound set. All pages have their ->private pointing at | 169 | * All pages have PG_compound set. All pages have their ->private pointing at |
170 | * the head page (even the head page has this). | 170 | * the head page (even the head page has this). |
171 | * | 171 | * |
172 | * The first tail page's ->mapping, if non-zero, holds the address of the | 172 | * The first tail page's ->lru.next holds the address of the compound page's |
173 | * compound page's put_page() function. | 173 | * put_page() function. Its ->lru.prev holds the order of allocation. |
174 | * | 174 | * This usage means that zero-order pages may not be compound. |
175 | * The order of the allocation is stored in the first tail page's ->index | ||
176 | * This is only for debug at present. This usage means that zero-order pages | ||
177 | * may not be compound. | ||
178 | */ | 175 | */ |
179 | static void prep_compound_page(struct page *page, unsigned long order) | 176 | static void prep_compound_page(struct page *page, unsigned long order) |
180 | { | 177 | { |
181 | int i; | 178 | int i; |
182 | int nr_pages = 1 << order; | 179 | int nr_pages = 1 << order; |
183 | 180 | ||
184 | page[1].mapping = NULL; | 181 | page[1].lru.next = NULL; /* set dtor */ |
185 | page[1].index = order; | 182 | page[1].lru.prev = (void *)order; |
186 | for (i = 0; i < nr_pages; i++) { | 183 | for (i = 0; i < nr_pages; i++) { |
187 | struct page *p = page + i; | 184 | struct page *p = page + i; |
188 | 185 | ||
@@ -196,7 +193,7 @@ static void destroy_compound_page(struct page *page, unsigned long order) | |||
196 | int i; | 193 | int i; |
197 | int nr_pages = 1 << order; | 194 | int nr_pages = 1 << order; |
198 | 195 | ||
199 | if (unlikely(page[1].index != order)) | 196 | if (unlikely((unsigned long)page[1].lru.prev != order)) |
200 | bad_page(page); | 197 | bad_page(page); |
201 | 198 | ||
202 | for (i = 0; i < nr_pages; i++) { | 199 | for (i = 0; i < nr_pages; i++) { |
@@ -40,7 +40,7 @@ static void put_compound_page(struct page *page) | |||
40 | if (put_page_testzero(page)) { | 40 | if (put_page_testzero(page)) { |
41 | void (*dtor)(struct page *page); | 41 | void (*dtor)(struct page *page); |
42 | 42 | ||
43 | dtor = (void (*)(struct page *))page[1].mapping; | 43 | dtor = (void (*)(struct page *))page[1].lru.next; |
44 | (*dtor)(page); | 44 | (*dtor)(page); |
45 | } | 45 | } |
46 | } | 46 | } |