[PATCH] nommu page refcount bug fixing

From: Luke Yang
Date: Wed Mar 29 2006 - 22:02:21 EST


Hi all,

The previous "nommu use compound pages" patch has a problem: when
the pages allocated is not compound page (eg: slab allocator), the
refcount value of every page still need to be set, otherwise the
get/put_page() would free a single page improperly, such as in
access_process_vm().

Signed-off-by: Luke Yang <luke.adi@xxxxxxxxx>

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index b7f14a4..fc8b544 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -436,6 +436,14 @@ static void __free_pages_ok(struct page
mutex_debug_check_no_locks_freed(page_address(page),
PAGE_SIZE<<order);

+#ifndef CONFIG_MMU
+ if (!PageCompound(page)) {
+ for (i = 1 ; i < (1 << order) ; ++i) {
+ __put_page(page + i);
+ }
+ }
+#endif
+
for (i = 0 ; i < (1 << order) ; ++i)
reserved += free_pages_check(page + i);
if (reserved)
@@ -453,6 +461,8 @@ static void __free_pages_ok(struct page
*/
void fastcall __init __free_pages_bootmem(struct page *page, unsigned
int order)
{
+ int i;
+
if (order == 0) {
__ClearPageReserved(page);
set_page_count(page, 0);
@@ -472,6 +482,11 @@ void fastcall __init __free_pages_bootme
}

set_page_refcounted(page);
+
+#ifndef CONFIG_MMU
+ for (i = 1; i < (1 << order); i++)
+ set_page_refcounted(page + i);
+#endif
__free_pages(page, order);
}
}
@@ -512,6 +527,8 @@ static inline void expand(struct zone *z
*/
static int prep_new_page(struct page *page, int order, gfp_t gfp_flags)
{
+ int i;
+
if (unlikely(page_mapcount(page) |
(page->mapping != NULL) |
(page_count(page) != 0) |
@@ -539,7 +556,21 @@ static int prep_new_page(struct page *pa
1 << PG_referenced | 1 << PG_arch_1 |
1 << PG_checked | 1 << PG_mappedtodisk);
set_page_private(page, 0);
+
set_page_refcounted(page);
+
+#ifndef CONFIG_MMU
+ if (!(gfp_flags & __GFP_COMP)) {
+ /*
+ * Reference all the pages for this order, otherwise if
+ * anyone accesses one of the pages with (get/put) it
+ * will be freed. - eg: access_process_vm()
+ */
+ for (i = 1; i < (1 << order); i++)
+ set_page_refcounted(page + i);
+ }
+#endif
+
kernel_map_pages(page, 1 << order, 1);

if (gfp_flags & __GFP_ZERO)

--
Best regards,
Luke Yang
luke.adi@xxxxxxxxx
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index b7f14a4..fc8b544 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -436,6 +436,14 @@ static void __free_pages_ok(struct page
mutex_debug_check_no_locks_freed(page_address(page),
PAGE_SIZE<<order);

+#ifndef CONFIG_MMU
+ if (!PageCompound(page)) {
+ for (i = 1 ; i < (1 << order) ; ++i) {
+ __put_page(page + i);
+ }
+ }
+#endif
+
for (i = 0 ; i < (1 << order) ; ++i)
reserved += free_pages_check(page + i);
if (reserved)
@@ -453,6 +461,8 @@ static void __free_pages_ok(struct page
*/
void fastcall __init __free_pages_bootmem(struct page *page, unsigned int order)
{
+ int i;
+
if (order == 0) {
__ClearPageReserved(page);
set_page_count(page, 0);
@@ -472,6 +482,11 @@ void fastcall __init __free_pages_bootme
}

set_page_refcounted(page);
+
+#ifndef CONFIG_MMU
+ for (i = 1; i < (1 << order); i++)
+ set_page_refcounted(page + i);
+#endif
__free_pages(page, order);
}
}
@@ -512,6 +527,8 @@ static inline void expand(struct zone *z
*/
static int prep_new_page(struct page *page, int order, gfp_t gfp_flags)
{
+ int i;
+
if (unlikely(page_mapcount(page) |
(page->mapping != NULL) |
(page_count(page) != 0) |
@@ -539,7 +556,21 @@ static int prep_new_page(struct page *pa
1 << PG_referenced | 1 << PG_arch_1 |
1 << PG_checked | 1 << PG_mappedtodisk);
set_page_private(page, 0);
+
set_page_refcounted(page);
+
+#ifndef CONFIG_MMU
+ if (!(gfp_flags & __GFP_COMP)) {
+ /*
+ * Reference all the pages for this order, otherwise if
+ * anyone accesses one of the pages with (get/put) it
+ * will be freed. - eg: access_process_vm()
+ */
+ for (i = 1; i < (1 << order); i++)
+ set_page_refcounted(page + i);
+ }
+#endif
+
kernel_map_pages(page, 1 << order, 1);

if (gfp_flags & __GFP_ZERO)