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
  17#include <asm/pgtable.h>
  18
  19/* 
  20 * Keep a reserved false inode which we will use to mark pages in the
  21 * page cache are acting as swap cache instead of file cache. 
  22 *
  23 * We only need a unique pointer to satisfy the page cache, but we'll
  24 * reserve an entire zeroed inode structure for the purpose just to
  25 * ensure that any mistaken dereferences of this structure cause a
  26 * kernel oops.
  27 */
  28struct inode swapper_inode;
  29
  30#ifdef SWAP_CACHE_INFO
  31unsigned long swap_cache_add_total = 0;
  32unsigned long swap_cache_del_total = 0;
  33unsigned long swap_cache_find_total = 0;
  34unsigned long swap_cache_find_success = 0;
  35
  36void show_swap_cache_info(void)
  37{
  38        printk("Swap cache: add %ld, delete %ld, find %ld/%ld\n",
  39                swap_cache_add_total, 
  40                swap_cache_del_total,
  41                swap_cache_find_success, swap_cache_find_total);
  42}
  43#endif
  44
  45int add_to_swap_cache(struct page *page, unsigned long entry)
  46{
  47#ifdef SWAP_CACHE_INFO
  48        swap_cache_add_total++;
  49#endif
  50#ifdef DEBUG_SWAP
  51        printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n",
  52               page_address(page), atomic_read(&page->count), entry);
  53#endif
  54        if (PageTestandSetSwapCache(page)) {
  55                printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx "
  56                       "on page %08lx\n",
  57                       page->offset, page_address(page));
  58                return 0;
  59        }
  60        if (page->inode) {
  61                printk(KERN_ERR "swap_cache: replacing page-cached entry "
  62                       "on page %08lx\n", page_address(page));
  63                return 0;
  64        }
  65        atomic_inc(&page->count);
  66        page->flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
  67        page->inode = &swapper_inode;
  68        page->offset = entry;
  69        add_page_to_hash_queue(page, &swapper_inode, entry);
  70        add_page_to_inode_queue(&swapper_inode, page);
  71        return 1;
  72}
  73
  74/*
  75 * Verify that a swap entry is valid and increment its swap map count.
  76 *
  77 * Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as
  78 * "permanent", but will be reclaimed by the next swapoff.
  79 */
  80int swap_duplicate(unsigned long entry)
  81{
  82        struct swap_info_struct * p;
  83        unsigned long offset, type;
  84        int result = 0;
  85
  86        if (!entry)
  87                goto out;
  88        type = SWP_TYPE(entry);
  89        if (type & SHM_SWP_TYPE)
  90                goto out;
  91        if (type >= nr_swapfiles)
  92                goto bad_file;
  93        p = type + swap_info;
  94        offset = SWP_OFFSET(entry);
  95        if (offset >= p->max)
  96                goto bad_offset;
  97        if (!p->swap_map[offset])
  98                goto bad_unused;
  99        /*
 100         * Entry is valid, so increment the map count.
 101         */
 102        if (p->swap_map[offset] < SWAP_MAP_MAX)
 103                p->swap_map[offset]++;
 104        else {
 105                static int overflow = 0;
 106                if (overflow++ < 5)
 107                        printk(KERN_WARNING
 108                                "swap_duplicate: entry %08lx map count=%d\n",
 109                                entry, p->swap_map[offset]);
 110                p->swap_map[offset] = SWAP_MAP_MAX;
 111        }
 112        result = 1;
 113#ifdef DEBUG_SWAP
 114        printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
 115               entry, p->swap_map[offset]);
 116#endif
 117out:
 118        return result;
 119
 120bad_file:
 121        printk(KERN_ERR
 122                "swap_duplicate: entry %08lx, nonexistent swap file\n", entry);
 123        goto out;
 124bad_offset:
 125        printk(KERN_ERR
 126                "swap_duplicate: entry %08lx, offset exceeds max\n", entry);
 127        goto out;
 128bad_unused:
 129        printk(KERN_ERR
 130                "swap_duplicate at %8p: entry %08lx, unused page\n", 
 131               __builtin_return_address(0), entry);
 132        goto out;
 133}
 134
 135int swap_count(unsigned long entry)
 136{
 137        struct swap_info_struct * p;
 138        unsigned long offset, type;
 139        int retval = 0;
 140
 141        if (!entry)
 142                goto bad_entry;
 143        type = SWP_TYPE(entry);
 144        if (type & SHM_SWP_TYPE)
 145                goto out;
 146        if (type >= nr_swapfiles)
 147                goto bad_file;
 148        p = type + swap_info;
 149        offset = SWP_OFFSET(entry);
 150        if (offset >= p->max)
 151                goto bad_offset;
 152        if (!p->swap_map[offset])
 153                goto bad_unused;
 154        retval = p->swap_map[offset];
 155#ifdef DEBUG_SWAP
 156        printk("DebugVM: swap_count(entry %08lx, count %d)\n",
 157               entry, retval);
 158#endif
 159out:
 160        return retval;
 161
 162bad_entry:
 163        printk(KERN_ERR "swap_count: null entry!\n");
 164        goto out;
 165bad_file:
 166        printk(KERN_ERR
 167               "swap_count: entry %08lx, nonexistent swap file!\n", entry);
 168        goto out;
 169bad_offset:
 170        printk(KERN_ERR
 171               "swap_count: entry %08lx, offset exceeds max!\n", entry);
 172        goto out;
 173bad_unused:
 174        printk(KERN_ERR
 175               "swap_count at %8p: entry %08lx, unused page!\n", 
 176               __builtin_return_address(0), entry);
 177        goto out;
 178}
 179
 180static inline void remove_from_swap_cache(struct page *page)
 181{
 182        if (!page->inode) {
 183                printk ("VM: Removing swap cache page with zero inode hash "
 184                        "on page %08lx\n", page_address(page));
 185                return;
 186        }
 187        if (page->inode != &swapper_inode) {
 188                printk ("VM: Removing swap cache page with wrong inode hash "
 189                        "on page %08lx\n", page_address(page));
 190        }
 191
 192#ifdef DEBUG_SWAP
 193        printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
 194               page_address(page), atomic_read(&page->count));
 195#endif
 196        PageClearSwapCache (page);
 197        remove_inode_page(page);
 198}
 199
 200
 201/*
 202 * This must be called only on pages that have
 203 * been verified to be in the swap cache.
 204 */
 205void delete_from_swap_cache(struct page *page)
 206{
 207        long entry = page->offset;
 208
 209#ifdef SWAP_CACHE_INFO
 210        swap_cache_del_total++;
 211#endif
 212#ifdef DEBUG_SWAP
 213        printk("DebugVM: delete_from_swap_cache(%08lx count %d, "
 214               "entry %08lx)\n",
 215               page_address(page), atomic_read(&page->count), entry);
 216#endif
 217        remove_from_swap_cache (page);
 218        swap_free (entry);
 219}
 220
 221/* 
 222 * Perform a free_page(), also freeing any swap cache associated with
 223 * this page if it is the last user of the page. 
 224 */
 225
 226void free_page_and_swap_cache(unsigned long addr)
 227{
 228        struct page *page = mem_map + MAP_NR(addr);
 229
 230        /* 
 231         * If we are the only user, then free up the swap cache. 
 232         */
 233        if (PageSwapCache(page) && !is_page_shared(page)) {
 234                delete_from_swap_cache(page);
 235        }
 236        
 237        __free_page(page);
 238}
 239
 240
 241/*
 242 * Lookup a swap entry in the swap cache.  We need to be careful about
 243 * locked pages.  A found page will be returned with its refcount
 244 * incremented.
 245 */
 246
 247struct page * lookup_swap_cache(unsigned long entry)
 248{
 249        struct page *found;
 250
 251#ifdef SWAP_CACHE_INFO
 252        swap_cache_find_total++;
 253#endif
 254        while (1) {
 255                found = find_page(&swapper_inode, entry);
 256                if (!found)
 257                        return 0;
 258                if (found->inode != &swapper_inode || !PageSwapCache(found))
 259                        goto out_bad;
 260                if (!PageLocked(found)) {
 261#ifdef SWAP_CACHE_INFO
 262                        swap_cache_find_success++;
 263#endif
 264                        return found;
 265                }
 266                __free_page(found);
 267                __wait_on_page(found);
 268        }
 269
 270out_bad:
 271        printk (KERN_ERR "VM: Found a non-swapper swap page!\n");
 272        __free_page(found);
 273        return 0;
 274}
 275
 276/* 
 277 * Locate a page of swap in physical memory, reserving swap cache space
 278 * and reading the disk if it is not already cached.  If wait==0, we are
 279 * only doing readahead, so don't worry if the page is already locked.
 280 *
 281 * A failure return means that either the page allocation failed or that
 282 * the swap entry is no longer in use.
 283 */
 284
 285struct page * read_swap_cache_async(unsigned long entry, int wait)
 286{
 287        struct page *found_page = 0, *new_page;
 288        unsigned long new_page_addr;
 289        
 290#ifdef DEBUG_SWAP
 291        printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
 292               entry, wait ? ", wait" : "");
 293#endif
 294        /*
 295         * Make sure the swap entry is still in use.
 296         */
 297        if (!swap_duplicate(entry))     /* Account for the swap cache */
 298                goto out;
 299        /*
 300         * Look for the page in the swap cache.
 301         */
 302        found_page = lookup_swap_cache(entry);
 303        if (found_page)
 304                goto out_free_swap;
 305
 306        new_page_addr = __get_free_page(GFP_USER);
 307        if (!new_page_addr)
 308                goto out_free_swap;     /* Out of memory */
 309        new_page = mem_map + MAP_NR(new_page_addr);
 310
 311        /*
 312         * Check the swap cache again, in case we stalled above.
 313         */
 314        found_page = lookup_swap_cache(entry);
 315        if (found_page)
 316                goto out_free_page;
 317        /* 
 318         * Add it to the swap cache and read its contents.
 319         */
 320        if (!add_to_swap_cache(new_page, entry))
 321                goto out_free_page;
 322
 323        set_bit(PG_locked, &new_page->flags);
 324        rw_swap_page(READ, entry, (char *) new_page_addr, wait);
 325#ifdef DEBUG_SWAP
 326        printk("DebugVM: read_swap_cache_async created "
 327               "entry %08lx at %p\n",
 328               entry, (char *) page_address(new_page));
 329#endif
 330        return new_page;
 331
 332out_free_page:
 333        __free_page(new_page);
 334out_free_swap:
 335        swap_free(entry);
 336out:
 337        return found_page;
 338}
 339
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.