linux-old/mm/swap_state.c
<<
>>
Prefs
   1/*
   2 *  linux/mm/swap_state.c
   3 *
   4 *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
   5 *  Swap reorganised 29.12.95, Stephen Tweedie
   6 *
   7 *  Rewritten to use page cache, (C) 1998 Stephen Tweedie
   8 */
   9
  10#include <linux/mm.h>
  11#include <linux/kernel_stat.h>
  12#include <linux/swap.h>
  13#include <linux/swapctl.h>
  14#include <linux/init.h>
  15#include <linux/pagemap.h>
  16#include <linux/smp_lock.h>
  17
  18#include <asm/pgtable.h>
  19
  20/*
  21 * We may have stale swap cache pages in memory: notice
  22 * them here and get rid of the unnecessary final write.
  23 */
  24static int swap_writepage(struct page *page)
  25{
  26        if (remove_exclusive_swap_page(page)) {
  27                UnlockPage(page);
  28                return 0;
  29        }
  30        rw_swap_page(WRITE, page);
  31        return 0;
  32}
  33
  34static struct address_space_operations swap_aops = {
  35        writepage: swap_writepage,
  36        sync_page: block_sync_page,
  37};
  38
  39struct address_space swapper_space = {
  40        LIST_HEAD_INIT(swapper_space.clean_pages),
  41        LIST_HEAD_INIT(swapper_space.dirty_pages),
  42        LIST_HEAD_INIT(swapper_space.locked_pages),
  43        0,                              /* nrpages      */
  44        &swap_aops,
  45};
  46
  47#ifdef SWAP_CACHE_INFO
  48#define INC_CACHE_INFO(x)       (swap_cache_info.x++)
  49
  50static struct {
  51        unsigned long add_total;
  52        unsigned long del_total;
  53        unsigned long find_success;
  54        unsigned long find_total;
  55        unsigned long noent_race;
  56        unsigned long exist_race;
  57} swap_cache_info;
  58
  59void show_swap_cache_info(void)
  60{
  61        printk("Swap cache: add %lu, delete %lu, find %lu/%lu, race %lu+%lu\n",
  62                swap_cache_info.add_total, swap_cache_info.del_total,
  63                swap_cache_info.find_success, swap_cache_info.find_total,
  64                swap_cache_info.noent_race, swap_cache_info.exist_race);
  65}
  66#else
  67#define INC_CACHE_INFO(x)       do { } while (0)
  68#endif
  69
  70int add_to_swap_cache(struct page *page, swp_entry_t entry)
  71{
  72        if (page->mapping)
  73                BUG();
  74        if (!swap_duplicate(entry)) {
  75                INC_CACHE_INFO(noent_race);
  76                return -ENOENT;
  77        }
  78        if (add_to_page_cache_unique(page, &swapper_space, entry.val,
  79                        page_hash(&swapper_space, entry.val)) != 0) {
  80                swap_free(entry);
  81                INC_CACHE_INFO(exist_race);
  82                return -EEXIST;
  83        }
  84        if (!PageLocked(page))
  85                BUG();
  86        if (!PageSwapCache(page))
  87                BUG();
  88        INC_CACHE_INFO(add_total);
  89        return 0;
  90}
  91
  92/*
  93 * This must be called only on pages that have
  94 * been verified to be in the swap cache.
  95 */
  96void __delete_from_swap_cache(struct page *page)
  97{
  98        if (!PageLocked(page))
  99                BUG();
 100        if (!PageSwapCache(page))
 101                BUG();
 102        ClearPageDirty(page);
 103        __remove_inode_page(page);
 104        INC_CACHE_INFO(del_total);
 105}
 106
 107/*
 108 * This must be called only on pages that have
 109 * been verified to be in the swap cache and locked.
 110 * It will never put the page into the free list,
 111 * the caller has a reference on the page.
 112 */
 113void delete_from_swap_cache(struct page *page)
 114{
 115        swp_entry_t entry;
 116
 117        if (!PageLocked(page))
 118                BUG();
 119
 120        if (unlikely(!block_flushpage(page, 0)))
 121                BUG();  /* an anonymous page cannot have page->buffers set */
 122
 123        entry.val = page->index;
 124
 125        spin_lock(&pagecache_lock);
 126        __delete_from_swap_cache(page);
 127        spin_unlock(&pagecache_lock);
 128
 129        swap_free(entry);
 130        page_cache_release(page);
 131}
 132
 133/* 
 134 * Perform a free_page(), also freeing any swap cache associated with
 135 * this page if it is the last user of the page. Can not do a lock_page,
 136 * as we are holding the page_table_lock spinlock.
 137 */
 138void free_page_and_swap_cache(struct page *page)
 139{
 140        /* 
 141         * If we are the only user, then try to free up the swap cache. 
 142         * 
 143         * Its ok to check for PageSwapCache without the page lock
 144         * here because we are going to recheck again inside 
 145         * exclusive_swap_page() _with_ the lock. 
 146         *                                      - Marcelo
 147         */
 148        if (PageSwapCache(page) && !TryLockPage(page)) {
 149                remove_exclusive_swap_page(page);
 150                UnlockPage(page);
 151        }
 152        page_cache_release(page);
 153}
 154
 155/*
 156 * Lookup a swap entry in the swap cache. A found page will be returned
 157 * unlocked and with its refcount incremented - we rely on the kernel
 158 * lock getting page table operations atomic even if we drop the page
 159 * lock before returning.
 160 */
 161struct page * lookup_swap_cache(swp_entry_t entry)
 162{
 163        struct page *found;
 164
 165        found = find_get_page(&swapper_space, entry.val);
 166        /*
 167         * Unsafe to assert PageSwapCache and mapping on page found:
 168         * if SMP nothing prevents swapoff from deleting this page from
 169         * the swap cache at this moment.  find_lock_page would prevent
 170         * that, but no need to change: we _have_ got the right page.
 171         */
 172        INC_CACHE_INFO(find_total);
 173        if (found)
 174                INC_CACHE_INFO(find_success);
 175        return found;
 176}
 177
 178/* 
 179 * Locate a page of swap in physical memory, reserving swap cache space
 180 * and reading the disk if it is not already cached.
 181 * A failure return means that either the page allocation failed or that
 182 * the swap entry is no longer in use.
 183 */
 184struct page * read_swap_cache_async(swp_entry_t entry)
 185{
 186        struct page *found_page, *new_page = NULL;
 187        int err;
 188
 189        do {
 190                /*
 191                 * First check the swap cache.  Since this is normally
 192                 * called after lookup_swap_cache() failed, re-calling
 193                 * that would confuse statistics: use find_get_page()
 194                 * directly.
 195                 */
 196                found_page = find_get_page(&swapper_space, entry.val);
 197                if (found_page)
 198                        break;
 199
 200                /*
 201                 * Get a new page to read into from swap.
 202                 */
 203                if (!new_page) {
 204                        new_page = alloc_page(GFP_HIGHUSER);
 205                        if (!new_page)
 206                                break;          /* Out of memory */
 207                }
 208
 209                /*
 210                 * Associate the page with swap entry in the swap cache.
 211                 * May fail (-ENOENT) if swap entry has been freed since
 212                 * our caller observed it.  May fail (-EEXIST) if there
 213                 * is already a page associated with this entry in the
 214                 * swap cache: added by a racing read_swap_cache_async,
 215                 * or by try_to_swap_out (or shmem_writepage) re-using
 216                 * the just freed swap entry for an existing page.
 217                 */
 218                err = add_to_swap_cache(new_page, entry);
 219                if (!err) {
 220                        /*
 221                         * Initiate read into locked page and return.
 222                         */
 223                        rw_swap_page(READ, new_page);
 224                        return new_page;
 225                }
 226        } while (err != -ENOENT);
 227
 228        if (new_page)
 229                page_cache_release(new_page);
 230        return found_page;
 231}
 232
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.