Page table cache problem

Bill Hawes (whawes@transmeta.com)
Sun, 26 Jul 1998 13:45:13 -0700


This is a multi-part message in MIME format.
--------------5274F084E86738D8C47483C1
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

I've found a page-table cache problem that's more serious than the
question of whether the pgt should be trimmed by kswapd or not, and IMO
it needs to get fixed right away.

The pgt cache xxx-free_fast routines don't check the high-water mark
when freeing pages, leaving open the possibility that the cache might
grow very large. I added some instrumentation to the check_pgt_cache()
code to check this by printing the cache size if it exceeds the high
water mark, and I got some reports of huge caches -- once it exceeded
1100 pages! (On a 32M UP system.)

The testing was done by starting a large grep and then spawning lots of
tasks doing a sleep 20. When the tasks exit, they dump their page tables
into the cache, but since the cache trimming is done only by the idle
task, the trimming may not happen for a while. The reports of large
sizes were often followed by "fork failed" messages.

I think the correct way to fix this is to directly call
check_pgt_cache() after unloading the page tables. This ensures that the
trimming (to the high water mark) happens immediately, but without
burdening the xxx_free_fast code with extra tests. I've attached a patch
that does this, and after enabling the patch the largest cache size I've
seen is 54 pages.

Regards,
Bill

--------------5274F084E86738D8C47483C1
Content-Type: text/plain; charset=us-ascii; name="mm_checkpgt111-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="mm_checkpgt111-patch"

--- linux-2.1.111/mm/memory.c.old Tue Jun 30 12:42:50 1998
+++ linux-2.1.111/mm/memory.c Sun Jul 26 12:34:34 1998
@@ -127,16 +131,23 @@
*/
void clear_page_tables(struct task_struct * tsk)
{
+ pgd_t * page_dir = tsk->mm->pgd;
int i;
- pgd_t * page_dir;

- page_dir = tsk->mm->pgd;
- if (!page_dir || page_dir == swapper_pg_dir) {
- printk("%s trying to clear kernel page-directory: not good\n", tsk->comm);
- return;
- }
+ if (!page_dir || page_dir == swapper_pg_dir)
+ goto out_bad;
for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
free_one_pgd(page_dir + i);
+
+ /* keep the page table cache within bounds */
+ check_pgt_cache();
+ return;
+
+out_bad:
+ printk(KERN_ERR
+ "clear_page_tables: %s trying to clear kernel pgd\n",
+ tsk->comm);
+ return;
}

/*
@@ -146,19 +157,26 @@
*/
void free_page_tables(struct mm_struct * mm)
{
+ pgd_t * page_dir = mm->pgd;
int i;
- pgd_t * page_dir;

- page_dir = mm->pgd;
- if (page_dir) {
- if (page_dir == swapper_pg_dir) {
- printk("free_page_tables: Trying to free kernel pgd\n");
- return;
- }
- for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
- free_one_pgd(page_dir + i);
- pgd_free(page_dir);
- }
+ if (!page_dir)
+ goto out;
+ if (page_dir == swapper_pg_dir)
+ goto out_bad;
+ for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
+ free_one_pgd(page_dir + i);
+ pgd_free(page_dir);
+
+ /* keep the page table cache within bounds */
+ check_pgt_cache();
+out:
+ return;
+
+out_bad:
+ printk(KERN_ERR
+ "free_page_tables: Trying to free kernel pgd\n");
+ return;
}

int new_page_tables(struct task_struct * tsk)

--------------5274F084E86738D8C47483C1--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.altern.org/andrebalsa/doc/lkml-faq.html