summaryrefslogtreecommitdiffstats
path: root/mm/swap_state.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /mm/swap_state.c
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'mm/swap_state.c')
-rw-r--r--mm/swap_state.c219
1 files changed, 208 insertions, 11 deletions
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 75f284124..4ebc5c05f 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -3,6 +3,8 @@
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
* Swap reorganised 29.12.95, Stephen Tweedie
+ *
+ * Rewritten to use page cache, (C) 1998 Stephen Tweedie
*/
#include <linux/mm.h>
@@ -17,6 +19,7 @@
#include <linux/fs.h>
#include <linux/swapctl.h>
#include <linux/init.h>
+#include <linux/pagemap.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
@@ -29,6 +32,18 @@ unsigned long swap_cache_del_success = 0;
unsigned long swap_cache_find_total = 0;
unsigned long swap_cache_find_success = 0;
+/*
+ * Keep a reserved false inode which we will use to mark pages in the
+ * page cache are acting as swap cache instead of file cache.
+ *
+ * We only need a unique pointer to satisfy the page cache, but we'll
+ * reserve an entire zeroed inode structure for the purpose just to
+ * ensure that any mistaken dereferences of this structure cause a
+ * kernel oops.
+ */
+struct inode swapper_inode;
+
+
void show_swap_cache_info(void)
{
printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
@@ -40,21 +55,33 @@ void show_swap_cache_info(void)
int add_to_swap_cache(struct page *page, unsigned long entry)
{
- struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
-
#ifdef SWAP_CACHE_INFO
swap_cache_add_total++;
#endif
- if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
- page->pg_swap_entry = entry;
- if (PageTestandSetSwapCache(page))
- printk("swap_cache: replacing non-empty entry\n");
-#ifdef SWAP_CACHE_INFO
- swap_cache_add_success++;
+#ifdef DEBUG_SWAP
+ printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n",
+ page_address(page), atomic_read(&page->count), entry);
#endif
- return 1;
+ if (PageTestandSetSwapCache(page)) {
+ printk("swap_cache: replacing non-empty entry %08lx "
+ "on page %08lx",
+ page->offset, page_address(page));
+ return 0;
}
- return 0;
+ if (page->inode) {
+ printk("swap_cache: replacing page-cached entry "
+ "on page %08lx", page_address(page));
+ return 0;
+ }
+ atomic_inc(&page->count);
+ page->inode = &swapper_inode;
+ page->offset = entry;
+ add_page_to_hash_queue(page, &swapper_inode, entry);
+ add_page_to_inode_queue(&swapper_inode, page);
+#ifdef SWAP_CACHE_INFO
+ swap_cache_add_success++;
+#endif
+ return 1;
}
/*
@@ -87,6 +114,10 @@ void swap_duplicate(unsigned long entry)
entry, p->swap_map[offset]);
p->swap_map[offset] = 127;
}
+#ifdef DEBUG_SWAP
+ printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
+ entry, p->swap_map[offset]);
+#endif
out:
return;
@@ -97,7 +128,173 @@ bad_offset:
printk("swap_duplicate: offset exceeds max\n");
goto out;
bad_unused:
- printk("swap_duplicate: unused page\n");
+ printk("swap_duplicate at %8p: unused page\n",
+ __builtin_return_address(0));
goto out;
}
+
+void remove_from_swap_cache(struct page *page)
+{
+ if (!page->inode) {
+ printk ("VM: Removing swap cache page with zero inode hash "
+ "on page %08lx", page_address(page));
+ return;
+ }
+ if (page->inode != &swapper_inode) {
+ printk ("VM: Removing swap cache page with wrong inode hash "
+ "on page %08lx", page_address(page));
+ }
+ /*
+ * This will be a legal case once we have a more mature swap cache.
+ */
+ if (atomic_read(&page->count) == 1) {
+ printk ("VM: Removing page cache on unshared page %08lx",
+ page_address(page));
+ return;
+ }
+
+
+#ifdef DEBUG_SWAP
+ printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
+ page_address(page), atomic_read(&page->count));
+#endif
+ remove_page_from_hash_queue (page);
+ remove_page_from_inode_queue (page);
+ PageClearSwapCache (page);
+ __free_page (page);
+}
+
+
+long find_in_swap_cache(struct page *page)
+{
+#ifdef SWAP_CACHE_INFO
+ swap_cache_find_total++;
+#endif
+ if (PageSwapCache (page)) {
+ long entry = page->offset;
+#ifdef SWAP_CACHE_INFO
+ swap_cache_find_success++;
+#endif
+ remove_from_swap_cache (page);
+ return entry;
+ }
+ return 0;
+}
+
+int delete_from_swap_cache(struct page *page)
+{
+#ifdef SWAP_CACHE_INFO
+ swap_cache_del_total++;
+#endif
+ if (PageSwapCache (page)) {
+ long entry = page->offset;
+#ifdef SWAP_CACHE_INFO
+ swap_cache_del_success++;
+#endif
+#ifdef DEBUG_SWAP
+ printk("DebugVM: delete_from_swap_cache(%08lx count %d, "
+ "entry %08lx)\n",
+ page_address(page), atomic_read(&page->count), entry);
+#endif
+ remove_from_swap_cache (page);
+ swap_free (entry);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Perform a free_page(), also freeing any swap cache associated with
+ * this page if it is the last user of the page.
+ */
+
+void free_page_and_swap_cache(unsigned long addr)
+{
+ struct page *page = mem_map + MAP_NR(addr);
+ /*
+ * If we are the only user, then free up the swap cache.
+ */
+ if (PageSwapCache(page) && !is_page_shared(page)) {
+ delete_from_swap_cache(page);
+ }
+
+ free_page(addr);
+}
+
+
+/*
+ * Lookup a swap entry in the swap cache. We need to be careful about
+ * locked pages. A found page will be returned with its refcount
+ * incremented.
+ */
+
+static struct page * lookup_swap_cache(unsigned long entry)
+{
+ struct page *found;
+
+ while (1) {
+ found = find_page(&swapper_inode, entry);
+ if (!found)
+ return 0;
+ if (found->inode != &swapper_inode
+ || !PageSwapCache(found)) {
+ __free_page(found);
+ printk ("VM: Found a non-swapper swap page!\n");
+ return 0;
+ }
+ if (!PageLocked(found))
+ return found;
+ __free_page(found);
+ __wait_on_page(found);
+ }
+}
+
+/*
+ * Locate a page of swap in physical memory, reserving swap cache space
+ * and reading the disk if it is not already cached. If wait==0, we are
+ * only doing readahead, so don't worry if the page is already locked.
+ */
+
+struct page * read_swap_cache_async(unsigned long entry, int wait)
+{
+ struct page *found_page, *new_page = 0;
+ unsigned long new_page_addr = 0;
+
+#ifdef DEBUG_SWAP
+ printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
+ entry, wait ? ", wait" : "");
+#endif
+repeat:
+ found_page = lookup_swap_cache(entry);
+ if (found_page) {
+ if (new_page)
+ __free_page(new_page);
+ return found_page;
+ }
+
+ /* The entry is not present. Lock down a new page, add it to
+ * the swap cache and read its contents. */
+ if (!new_page) {
+ new_page_addr = __get_free_page(GFP_KERNEL);
+ if (!new_page_addr)
+ return 0; /* Out of memory */
+ new_page = mem_map + MAP_NR(new_page_addr);
+ goto repeat; /* We might have stalled */
+ }
+
+ if (!add_to_swap_cache(new_page, entry)) {
+ free_page(new_page_addr);
+ return 0;
+ }
+ swap_duplicate(entry); /* Account for the swap cache */
+ set_bit(PG_locked, &new_page->flags);
+ rw_swap_page(READ, entry, (char *) new_page_addr, wait);
+#ifdef DEBUG_SWAP
+ printk("DebugVM: read_swap_cache_async created "
+ "entry %08lx at %p\n",
+ entry, (char *) page_address(new_page));
+#endif
+ return new_page;
+}
+