diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /mm/swap_state.c | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (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.c | 219 |
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; +} + |