With this my patch, swap cache pages are removed only when needed by
shrink_swap_cache(). This function doesn' t care at all about the priority
(first dummy arg of the function), but it always browse the whole swap
cache list searching for a page to free, since it' s always better remove
a swap page than doing other things to get free memory. I am not sure
about the atomicity of the browsing of the list and about SMP issues
(since I used last_page static for example).
The patch seems stable. I suggest to Simon and everybody to try this patch
and make performance comparision with other ones.
Here the patch against 122. If you are going to apply the patch to your
kernel be sure to remove my swapin_parent stuff because it' s not needed
anymore (in 2.1 ;-).
diff -urN /home/andrea/devel/kernel-tree/linux-2.1.122/include/linux/swap.h linux/include/linux/swap.h
--- /home/andrea/devel/kernel-tree/linux-2.1.122/include/linux/swap.h Sat Sep 5 14:17:56 1998
+++ linux/include/linux/swap.h Thu Sep 24 18:43:53 1998
@@ -89,6 +89,7 @@
extern int swap_check_entry(unsigned long);
extern struct page * read_swap_cache_async(unsigned long, int);
#define read_swap_cache(entry) read_swap_cache_async(entry, 1);
+extern int shrink_swap_cache(int, int);
/*
* Make these inline later once they are working properly.
*/
@@ -146,14 +147,9 @@
*/
static inline int is_page_shared(struct page *page)
{
- int count = atomic_read(&page->count);
if (PageReserved(page))
return 1;
- if (page->inode == &swapper_inode)
- count--;
- if (PageFreeAfter(page))
- count--;
- return (count > 1);
+ return atomic_read(&page->count) > 1;
}
#endif /* __KERNEL__*/
diff -urN /home/andrea/devel/kernel-tree/linux-2.1.122/mm/page_alloc.c linux/mm/page_alloc.c
--- /home/andrea/devel/kernel-tree/linux-2.1.122/mm/page_alloc.c Thu Sep 10 23:56:48 1998
+++ linux/mm/page_alloc.c Thu Sep 24 17:55:28 1998
@@ -163,9 +163,11 @@
free_pages_ok(page->map_nr, 0);
return;
}
+#if 0
if (PageSwapCache(page) && atomic_read(&page->count) == 1)
printk(KERN_WARNING "VM: Releasing swap cache page at %p",
__builtin_return_address(0));
+#endif
}
void free_pages(unsigned long addr, unsigned long order)
@@ -182,10 +184,12 @@
free_pages_ok(map_nr, order);
return;
}
+#if 0
if (PageSwapCache(map) && atomic_read(&map->count) == 1)
printk(KERN_WARNING
"VM: Releasing swap cache pages at %p",
__builtin_return_address(0));
+#endif
}
}
diff -urN /home/andrea/devel/kernel-tree/linux-2.1.122/mm/swap_state.c linux/mm/swap_state.c
--- /home/andrea/devel/kernel-tree/linux-2.1.122/mm/swap_state.c Thu Sep 10 23:56:48 1998
+++ linux/mm/swap_state.c Thu Sep 24 20:43:28 1998
@@ -5,6 +5,7 @@
* Swap reorganised 29.12.95, Stephen Tweedie
*
* Rewritten to use page cache, (C) 1998 Stephen Tweedie
+ * Shrinking of the swap cache, Andrea Arcangeli
*/
#include <linux/mm.h>
@@ -155,6 +156,7 @@
printk ("VM: Removing swap cache page with wrong inode hash "
"on page %08lx\n", page_address(page));
}
+#if 0
/*
* This is a legal case, but warn about it.
*/
@@ -163,6 +165,7 @@
"VM: Removing page cache on unshared page %08lx\n",
page_address(page));
}
+#endif
#ifdef DEBUG_SWAP
printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
@@ -300,4 +303,61 @@
__free_page(new_page);
out:
return found_page;
+}
+
+/*
+ * This routine deal with shrinking the swap cache. -arca
+ */
+int shrink_swap_cache(int priority, int gfp_mask)
+{
+ unsigned int cycles;
+ struct page *page;
+ static struct page *last_page = NULL;
+
+ cycles = swapper_inode.i_nrpages;
+
+ if (last_page && PageSwapCache(last_page) &&
+ last_page->next && last_page->prev)
+ page = last_page;
+ else
+ page = swapper_inode.i_pages;
+
+ /* PARANOID */
+ if (!page && cycles)
+ {
+ printk(KERN_ERR "VM: swap cache page NULL but not empty "
+ "swap cache list!\n");
+ goto out;
+ }
+ while (cycles--)
+ {
+ /* PARANOID */
+ if (!PageSwapCache(page) ||
+ page->inode != &swapper_inode)
+ {
+ printk(KERN_ERR "VM: found a no swap_cache page "
+ "in the swap cache!\n");
+ goto out;
+ }
+ if ((gfp_mask & __GFP_DMA) && !PageDMA(page))
+ goto next;
+ switch (atomic_read(&page->count))
+ {
+ case 0:
+ printk(KERN_ERR "VM: found a now swap page cache "
+ "in the swap cache!\n");
+ goto out;
+ case 1:
+ delete_from_swap_cache(page);
+ last_page = page;
+ return 1;
+ }
+ next:
+ if (!(page = page->next))
+ if (!(page = swapper_inode.i_pages))
+ goto out;
+ }
+ out:
+ last_page = page;
+ return 0;
}
diff -urN /home/andrea/devel/kernel-tree/linux-2.1.122/mm/vmscan.c linux/mm/vmscan.c
--- /home/andrea/devel/kernel-tree/linux-2.1.122/mm/vmscan.c Thu Sep 10 23:56:48 1998
+++ linux/mm/vmscan.c Thu Sep 24 18:40:55 1998
@@ -465,18 +465,22 @@
switch (state) {
do {
case 0:
- if (shrink_mmap(i, gfp_mask))
+ if (shrink_swap_cache(i, gfp_mask))
return 1;
state = 1;
case 1:
- if (shm_swap(i, gfp_mask))
+ if (shrink_mmap(i, gfp_mask))
return 1;
state = 2;
case 2:
- if (swap_out(i, gfp_mask))
+ if (shm_swap(i, gfp_mask))
return 1;
state = 3;
case 3:
+ if (swap_out(i, gfp_mask))
+ return 1;
+ state = 4;
+ case 4:
shrink_dcache_memory(i, gfp_mask);
state = 0;
i--;
Andrea[s] Arcangeli
-
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.tux.org/lkml/