Re: Memory leak in 2.0.33 ??

Krzysztof Strasburger (strasbur@chkw386.ch.pwr.wroc.pl)
Mon, 29 Dec 97 09:27


Hi all!
Sorry Alan, if you get this twice.
Here is something about the bug.
If you run a single program, which uses even slightly more memory than your
machine has, system starts swapping the pages out to disk. If the page is read
back from swap area, its position (in the swap area) is still kept in the swap
cache and swap entry is not freed. As more and more pages are swapped out and
in, there is less and less free swap entries. Finally you can not start more
programs, despite of lots of free virtual memory (128 MB RAM + 128 MB of swap
space, all programs use, say, 140 MB, but no more new programs can be started).
Why? Because the whole swap area has been consumed by the swap cache.
On the other hand, the locked swap entries have counterparts in real memory
and should be counted as free. Why? Look at the two cases, when try_to_free_page
tries to free a page with a counterpart in the swap cache.
1. The page is dirty. Swap entry is freed, as it does not contain an image
of the page in memory. The page may be swapped out, but may be still kept
in memory. Swap cache entry is deleted.
2. The page is not dirty. We can free it without writing its contents to the
swap area. No additional swap entry is needed.
In both cases the memory management machinery works fine, but if you
try to run a new program, or another program calls mmap or brk,
vm_enough_memory says "no, there is no more free memory" - because swap entries
locked by swap_cache are not counted as free entries. This is all what
my patch does.

Happy New Year
Krzysztof Strasburger

And finally the patch against 2.0.33 - try it:

diff -u -r linux.orig/include/linux/swap.h linux/include/linux/swap.h
--- linux.orig/include/linux/swap.h Mon Jun 3 14:38:37 1996
+++ linux/include/linux/swap.h Fri Apr 18 17:11:22 1997
@@ -34,6 +34,7 @@

extern int nr_swap_pages;
extern int nr_free_pages;
+extern int nr_swap_cache_pages;
extern atomic_t nr_async_pages;
extern int min_free_pages;
extern int free_pages_low;
@@ -113,10 +114,12 @@
swap_cache_find_total++;
#endif
entry = xchg(swap_cache + index, 0);
+ if (entry) {
#ifdef SWAP_CACHE_INFO
- if (entry)
swap_cache_find_success++;
#endif
+ nr_swap_cache_pages--;
+ }
return entry;
}

@@ -133,6 +136,7 @@
swap_cache_del_success++;
#endif
swap_free(entry);
+ nr_swap_cache_pages--;
return 1;
}
return 0;
diff -u -r linux.orig/mm/mmap.c linux/mm/mmap.c
--- linux.orig/mm/mmap.c Fri Nov 7 17:57:31 1997
+++ linux/mm/mmap.c Tue Dec 23 09:26:43 1997
@@ -59,6 +59,7 @@
freepages >>= 1;
freepages += nr_free_pages;
freepages += nr_swap_pages;
+ freepages += nr_swap_cache_pages;
freepages -= MAP_NR(high_memory) >> 4;
return freepages > pages;
}
diff -u -r linux.orig/mm/swap_state.c linux/mm/swap_state.c
--- linux.orig/mm/swap_state.c Wed Mar 13 14:17:23 1996
+++ linux/mm/swap_state.c Fri Apr 18 17:12:11 1997
@@ -32,6 +32,8 @@
*/
unsigned long *swap_cache;

+int nr_swap_cache_pages = 0;
+
#ifdef SWAP_CACHE_INFO
unsigned long swap_cache_add_total = 0;
unsigned long swap_cache_add_success = 0;
@@ -64,6 +66,7 @@
#ifdef SWAP_CACHE_INFO
swap_cache_add_success++;
#endif
+ nr_swap_cache_pages++;
return 1;
}
return 0;
diff -u -r linux.orig/mm/swapfile.c linux/mm/swapfile.c
--- linux.orig/mm/swapfile.c Thu Apr 10 23:47:14 1997
+++ linux/mm/swapfile.c Fri Apr 18 17:12:12 1997
@@ -570,6 +570,7 @@
++val->totalswap;
}
}
+ val->freeswap += nr_swap_cache_pages;
val->freeswap <<= PAGE_SHIFT;
val->totalswap <<= PAGE_SHIFT;
return;