Linux slow with more memory - solution & patch

Pavel Machek (pavel@atrey.karlin.mff.cuni.cz)
Wed, 30 Apr 1997 15:36:19 +0200


Hi!

Hmm - so I probably should apologize for my previous postings. Not
only that my mainboard is unable to cache > 16MB of RAM, it even
prevents CPU to cache it in L1. Which is bad, bad, BAD.

I made following patch, which tries to solve the problem: You can
specify amount of fastmem= on commandline, and it will try to use slow
memory for buffers, and fast memory for everything else. This patch
probably damages performance of __get_free_pages badly, otherwise it
should be ok.

It helped to my machine: (Compilation of kernel:)
mem=20M 32 minutes (if I recall correctly)
mem=20M fastmem=16M 24 minutes
mem=16M 22 minutes

Note that there still is some performance loosage in there. But if
something more than kernel compilation happened, performance might be
better with this than with memory completely disabled.

Please, some wizard, take a look at this. (Patch is against 2.1.36)

Pavel

diff -ur clean/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- clean/arch/i386/kernel/setup.c Wed Apr 16 19:11:00 1997
+++ linux/arch/i386/kernel/setup.c Wed Apr 30 13:01:13 1997
@@ -43,7 +43,8 @@
int x86_capability = 0; /* set by kernel/head.S */
int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */
int have_cpuid = 0; /* set if CPUID instruction works */
-
+int end_fast_mem = 0; /* end of fast memory - in case something
+ is not cached this is interesting */
char x86_vendor_id[13] = "unknown";

char ignore_irq13 = 0; /* set if exception 16 works */
@@ -148,6 +151,7 @@
#else
memory_end = (1<<20) + (EXT_MEM_K*64L*1024L); /* 64kb chunks */
#endif
+ end_fast_mem = memory_end;
memory_end &= PAGE_MASK;
#ifdef CONFIG_BLK_DEV_RAM
rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
@@ -188,6 +192,20 @@
}
}
}
+ if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"fastmem=") {
+ if (to != command_line) to--;
+ {
+ end_fast_mem = simple_strtoul(from+8, &from, 0);
+ if ( *from == 'K' || *from == 'k' ) {
+ end_fast_mem = end_fast_mem << 10;
+ from++;
+ } else if ( *from == 'M' || *from == 'm' ) {
+ end_fast_mem = end_fast_mem << 20;
+ from++;
+ }
+ end_fast_mem += PAGE_OFFSET;
+ }
+ }
c = *(from++);
if (!c)
break;
diff -ur clean/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- clean/arch/i386/mm/init.c Sat Apr 26 20:19:26 1997
+++ linux/arch/i386/mm/init.c Wed Apr 30 12:58:07 1997
@@ -31,6 +31,7 @@

extern void die_if_kernel(char *,struct pt_regs *,long);
extern void show_net_buffers(void);
+extern int end_fast_mem;

/*
* BAD_PAGE is the page that is used for page faults when linux
@@ -257,6 +258,7 @@
int reservedpages = 0;
int datapages = 0;
int initpages = 0;
+ int slowpages = 0;
unsigned long tmp;

end_mem &= PAGE_MASK;
@@ -295,9 +297,17 @@
clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
start_mem += PAGE_SIZE;
}
+#ifdef CONFIG_RAM_OK
for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) {
+#else
+ for (tmp = end_mem - PAGE_SIZE; tmp >= PAGE_OFFSET ; tmp -= PAGE_SIZE) {
+#endif
if (tmp >= MAX_DMA_ADDRESS)
clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags);
+ if (tmp >= end_fast_mem)
+ { clear_bit( PG_fast, &mem_map[MAP_NR(tmp)].flags); slowpages++; }
+ else
+ set_bit( PG_fast, &mem_map[MAP_NR(tmp)].flags);
if (PageReserved(mem_map+MAP_NR(tmp))) {
if (tmp >= (unsigned long) &_text && tmp < (unsigned long) &_edata) {
if (tmp < (unsigned long) &_etext)
@@ -321,18 +331,19 @@
#endif
free_page(tmp);
}
- printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n",
+ printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %dk slow)\n",
(unsigned long) nr_free_pages << (PAGE_SHIFT-10),
max_mapnr << (PAGE_SHIFT-10),
codepages << (PAGE_SHIFT-10),
reservedpages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10),
- initpages << (PAGE_SHIFT-10));
+ initpages << (PAGE_SHIFT-10),
+ slowpages << (PAGE_SHIFT-10));
/* test if the WP bit is honoured in supervisor mode */
if (wp_works_ok < 0) {
unsigned char tmp_reg;
unsigned long old = pg0[0];
- printk("Checking if this processor honours the WP bit even in supervisor mode... ");
+ printk("Checking WP bit in supervisor mode... ");
pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY));
local_flush_tlb();
current->mm->mmap->vm_start += PAGE_SIZE;
@@ -347,7 +358,7 @@
current->mm->mmap->vm_start -= PAGE_SIZE;
if (wp_works_ok < 0) {
wp_works_ok = 0;
- printk("No.\n");
+ printk("ignored.\n");
} else
printk("Ok.\n");
}
diff -ur clean/mm/page_alloc.c linux/mm/page_alloc.c
--- clean/mm/page_alloc.c Sat Apr 26 20:20:27 1997
+++ linux/mm/page_alloc.c Wed Apr 30 12:39:20 1997
@@ -3,6 +3,7 @@
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
* Swap reorganised 29.12.95, Stephen Tweedie
+ * Fast/slow memory 29.4.97, Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>
*/

#include <linux/config.h>
@@ -166,7 +167,7 @@
unsigned long new_order = order; \
do { struct page *prev = memory_head(area), *ret; \
while (memory_head(area) != (ret = prev->next)) { \
- if (!dma || CAN_DMA(ret)) { \
+ if ( (!dma || CAN_DMA(ret)) && (!carespeed || ((PageFast(ret)>0) == speed)) ){ \
unsigned long map_nr = ret->map_nr; \
(prev->next = ret->next)->prev = prev; \
MARK_USED(map_nr, new_order, area); \
@@ -194,7 +195,7 @@
map->age = PAGE_INITIAL_AGE; \
} while (0)

-unsigned long __get_free_pages(int priority, unsigned long order, int dma)
+unsigned __inline__ long internal__get_free_pages(int priority, unsigned long order, int dma, int carespeed, int speed)
{
unsigned long flags;
int reserved_pages;
@@ -228,6 +229,37 @@
return 0;
}

+#define COMPLAIN( s ) \
+ { \
+ static unsigned long last = 0; \
+ if (last + 10 * HZ < jiffies) { \
+ last = jiffies; \
+ printk( s ); \
+ } \
+ }
+
+int fast_in_fast, fast_failed, slow_in_slow, slow_failed;
+
+unsigned long __get_free_pages(int priority, unsigned long order, int dma)
+{
+unsigned long ret;
+
+if (dma) return internal__get_free_pages( priority, order, dma, 0, 0 );
+if (priority != GFP_BUFFER) {
+ ret = internal__get_free_pages( priority, order, 0, 1, 1 );
+ if (ret) { fast_in_fast++; return ret; }
+ COMPLAIN( "Could not get fast page!\n" );
+ fast_failed++;
+ return internal__get_free_pages( priority, order, 0, 0, 0 );
+} else {
+ ret = internal__get_free_pages( priority, order, 0, 1, 0 );
+ if (ret) { slow_in_slow++; return ret; }
+ COMPLAIN( "Could not get slow page for buffers!\n" );
+ slow_failed++;
+ return internal__get_free_pages( priority, order, 0, 0, 0 );
+ }
+}
+
/*
* Show free area list (used inside shift_scroll-lock stuff)
* We also calculate the percentage fragmentation. We do this by counting the
@@ -238,6 +270,8 @@
unsigned long order, flags;
unsigned long total = 0;

+ printk("Slow/fast allocation: fast ok: %d, fast failed: %d, slow ok %d, slow failed: %d\n",
+ fast_in_fast, fast_failed, slow_in_slow, slow_failed );
printk("Free pages: %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10));
save_flags(flags);
cli();
@@ -346,4 +380,3 @@
swap_free(entry);
return;
}
-
diff -ur clean/include/linux/mm.h linux/include/linux/mm.h
--- clean/include/linux/mm.h Sat Apr 26 20:20:23 1997
+++ linux/include/linux/mm.h Tue Apr 29 23:59:00 1997
@@ -8,6 +8,7 @@
#ifdef __KERNEL__

#include <linux/string.h>
+#include <linux/config.h>

extern unsigned long max_mapnr;
extern unsigned long num_physpages;
@@ -140,6 +141,7 @@
#define PG_decr_after 5
#define PG_swap_unlock_after 6
#define PG_DMA 7
+#define PG_fast 8
#define PG_reserved 31

/* Make it prettier to test the above... */
@@ -152,6 +154,7 @@
#define PageDecrAfter(page) (test_bit(PG_decr_after, &(page)->flags))
#define PageSwapUnlockAfter(page) (test_bit(PG_swap_unlock_after, &(page)->flags))
#define PageDMA(page) (test_bit(PG_DMA, &(page)->flags))
+#define PageFast(page) (test_bit(PG_fast, &(page)->flags))
#define PageReserved(page) (test_bit(PG_reserved, &(page)->flags))

/*

--
I'm really pavel@atrey.karlin.mff.cuni.cz. 	   Pavel
Look at http://atrey.karlin.mff.cuni.cz/~pavel ;-).