i915: Enable IMR passthrough of vblank events before enabling it in pipestat.
[safe/jmp/linux-2.6] / mm / swapfile.c
index 35e00c3..1e330f2 100644 (file)
 #include <asm/tlbflush.h>
 #include <linux/swapops.h>
 
-DEFINE_SPINLOCK(swap_lock);
-unsigned int nr_swapfiles;
+static DEFINE_SPINLOCK(swap_lock);
+static unsigned int nr_swapfiles;
 long total_swap_pages;
 static int swap_overflow;
+static int least_priority;
 
 static const char Bad_file[] = "Bad swap file entry ";
 static const char Unused_file[] = "Unused swap file entry ";
 static const char Bad_offset[] = "Bad swap offset entry ";
 static const char Unused_offset[] = "Unused swap offset entry ";
 
-struct swap_list_t swap_list = {-1, -1};
+static struct swap_list_t swap_list = {-1, -1};
 
 static struct swap_info_struct swap_info[MAX_SWAPFILES];
 
@@ -368,13 +369,13 @@ int remove_exclusive_swap_page(struct page *page)
        retval = 0;
        if (p->swap_map[swp_offset(entry)] == 1) {
                /* Recheck the page count with the swapcache lock held.. */
-               write_lock_irq(&swapper_space.tree_lock);
+               spin_lock_irq(&swapper_space.tree_lock);
                if ((page_count(page) == 2) && !PageWriteback(page)) {
                        __delete_from_swap_cache(page);
                        SetPageDirty(page);
                        retval = 1;
                }
-               write_unlock_irq(&swapper_space.tree_lock);
+               spin_unlock_irq(&swapper_space.tree_lock);
        }
        spin_unlock(&swap_lock);
 
@@ -402,7 +403,7 @@ void free_swap_and_cache(swp_entry_t entry)
        if (p) {
                if (swap_entry_free(p, swp_offset(entry)) == 1) {
                        page = find_get_page(&swapper_space, entry.val);
-                       if (page && unlikely(TestSetPageLocked(page))) {
+                       if (page && unlikely(!trylock_page(page))) {
                                page_cache_release(page);
                                page = NULL;
                        }
@@ -507,11 +508,23 @@ unsigned int count_swap_pages(int type, int free)
  * just let do_wp_page work it out if a write is requested later - to
  * force COW, vm_page_prot omits write permission from any private vma.
  */
-static int unuse_pte(struct vm_area_struct *vma, pte_t *pte,
+static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
                unsigned long addr, swp_entry_t entry, struct page *page)
 {
+       spinlock_t *ptl;
+       pte_t *pte;
+       int ret = 1;
+
        if (mem_cgroup_charge(page, vma->vm_mm, GFP_KERNEL))
-               return -ENOMEM;
+               ret = -ENOMEM;
+
+       pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
+       if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) {
+               if (ret > 0)
+                       mem_cgroup_uncharge_page(page);
+               ret = 0;
+               goto out;
+       }
 
        inc_mm_counter(vma->vm_mm, anon_rss);
        get_page(page);
@@ -524,7 +537,9 @@ static int unuse_pte(struct vm_area_struct *vma, pte_t *pte,
         * immediately swapped out again after swapon.
         */
        activate_page(page);
-       return 1;
+out:
+       pte_unmap_unlock(pte, ptl);
+       return ret;
 }
 
 static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
@@ -533,21 +548,33 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 {
        pte_t swp_pte = swp_entry_to_pte(entry);
        pte_t *pte;
-       spinlock_t *ptl;
        int ret = 0;
 
-       pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
+       /*
+        * We don't actually need pte lock while scanning for swp_pte: since
+        * we hold page lock and mmap_sem, swp_pte cannot be inserted into the
+        * page table while we're scanning; though it could get zapped, and on
+        * some architectures (e.g. x86_32 with PAE) we might catch a glimpse
+        * of unmatched parts which look like swp_pte, so unuse_pte must
+        * recheck under pte lock.  Scanning without pte lock lets it be
+        * preemptible whenever CONFIG_PREEMPT but not CONFIG_HIGHPTE.
+        */
+       pte = pte_offset_map(pmd, addr);
        do {
                /*
                 * swapoff spends a _lot_ of time in this loop!
                 * Test inline before going to call unuse_pte.
                 */
                if (unlikely(pte_same(*pte, swp_pte))) {
-                       ret = unuse_pte(vma, pte++, addr, entry, page);
-                       break;
+                       pte_unmap(pte);
+                       ret = unuse_pte(vma, pmd, addr, entry, page);
+                       if (ret)
+                               goto out;
+                       pte = pte_offset_map(pmd, addr);
                }
        } while (pte++, addr += PAGE_SIZE, addr != end);
-       pte_unmap_unlock(pte - 1, ptl);
+       pte_unmap(pte - 1);
+out:
        return ret;
 }
 
@@ -629,8 +656,8 @@ static int unuse_mm(struct mm_struct *mm,
 
        if (!down_read_trylock(&mm->mmap_sem)) {
                /*
-                * Activate page so shrink_cache is unlikely to unmap its
-                * ptes while lock is dropped, so swapoff can make progress.
+                * Activate page so shrink_inactive_list is unlikely to unmap
+                * its ptes while lock is dropped, so swapoff can make progress.
                 */
                activate_page(page);
                unlock_page(page);
@@ -1234,6 +1261,11 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
                /* just pick something that's safe... */
                swap_list.next = swap_list.head;
        }
+       if (p->prio < 0) {
+               for (i = p->next; i >= 0; i = swap_info[i].next)
+                       swap_info[i].prio = p->prio--;
+               least_priority++;
+       }
        nr_swap_pages -= p->pages;
        total_swap_pages -= p->pages;
        p->flags &= ~SWP_WRITEOK;
@@ -1246,9 +1278,14 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
        if (err) {
                /* re-insert swap space back into swap_list */
                spin_lock(&swap_lock);
-               for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
+               if (p->prio < 0)
+                       p->prio = --least_priority;
+               prev = -1;
+               for (i = swap_list.head; i >= 0; i = swap_info[i].next) {
                        if (p->prio >= swap_info[i].prio)
                                break;
+                       prev = i;
+               }
                p->next = i;
                if (prev < 0)
                        swap_list.head = swap_list.next = p - swap_info;
@@ -1368,7 +1405,7 @@ static int swap_show(struct seq_file *swap, void *v)
        }
 
        file = ptr->swap_file;
-       len = seq_path(swap, file->f_path.mnt, file->f_path.dentry, " \t\n\\");
+       len = seq_path(swap, &file->f_path, " \t\n\\");
        seq_printf(swap, "%*s%s\t%u\t%u\t%d\n",
                       len < 40 ? 40 - len : 1, " ",
                       S_ISBLK(file->f_path.dentry->d_inode->i_mode) ?
@@ -1400,11 +1437,7 @@ static const struct file_operations proc_swaps_operations = {
 
 static int __init procswaps_init(void)
 {
-       struct proc_dir_entry *entry;
-
-       entry = create_proc_entry("swaps", 0, NULL);
-       if (entry)
-               entry->proc_fops = &proc_swaps_operations;
+       proc_create("swaps", 0, NULL, &proc_swaps_operations);
        return 0;
 }
 __initcall(procswaps_init);
@@ -1425,7 +1458,6 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
        unsigned int type;
        int i, prev;
        int error;
-       static int least_priority;
        union swap_header *swap_header = NULL;
        int swap_header_version;
        unsigned int nr_good_pages = 0;
@@ -1433,7 +1465,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
        sector_t span;
        unsigned long maxpages = 1;
        int swapfilesize;
-       unsigned short *swap_map;
+       unsigned short *swap_map = NULL;
        struct page *page = NULL;
        struct inode *inode = NULL;
        int did_down = 0;
@@ -1452,22 +1484,10 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
        }
        if (type >= nr_swapfiles)
                nr_swapfiles = type+1;
+       memset(p, 0, sizeof(*p));
        INIT_LIST_HEAD(&p->extent_list);
        p->flags = SWP_USED;
-       p->swap_file = NULL;
-       p->old_block_size = 0;
-       p->swap_map = NULL;
-       p->lowest_bit = 0;
-       p->highest_bit = 0;
-       p->cluster_nr = 0;
-       p->inuse_pages = 0;
        p->next = -1;
-       if (swap_flags & SWAP_FLAG_PREFER) {
-               p->prio =
-                 (swap_flags & SWAP_FLAG_PRIO_MASK)>>SWAP_FLAG_PRIO_SHIFT;
-       } else {
-               p->prio = --least_priority;
-       }
        spin_unlock(&swap_lock);
        name = getname(specialfile);
        error = PTR_ERR(name);
@@ -1556,6 +1576,14 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
                error = -EINVAL;
                goto bad_swap;
        case 2:
+               /* swap partition endianess hack... */
+               if (swab32(swap_header->info.version) == 1) {
+                       swab32s(&swap_header->info.version);
+                       swab32s(&swap_header->info.last_page);
+                       swab32s(&swap_header->info.nr_badpages);
+                       for (i = 0; i < swap_header->info.nr_badpages; i++)
+                               swab32s(&swap_header->info.badpages[i]);
+               }
                /* Check the swap header's sub-version and the size of
                    the swap file and bad block lists */
                if (swap_header->info.version != 1) {
@@ -1602,19 +1630,20 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
                        goto bad_swap;
 
                /* OK, set up the swap map and apply the bad block list */
-               if (!(p->swap_map = vmalloc(maxpages * sizeof(short)))) {
+               swap_map = vmalloc(maxpages * sizeof(short));
+               if (!swap_map) {
                        error = -ENOMEM;
                        goto bad_swap;
                }
 
                error = 0;
-               memset(p->swap_map, 0, maxpages * sizeof(short));
+               memset(swap_map, 0, maxpages * sizeof(short));
                for (i = 0; i < swap_header->info.nr_badpages; i++) {
                        int page_nr = swap_header->info.badpages[i];
                        if (page_nr <= 0 || page_nr >= swap_header->info.last_page)
                                error = -EINVAL;
                        else
-                               p->swap_map[page_nr] = SWAP_MAP_BAD;
+                               swap_map[page_nr] = SWAP_MAP_BAD;
                }
                nr_good_pages = swap_header->info.last_page -
                                swap_header->info.nr_badpages -
@@ -1624,7 +1653,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
        }
 
        if (nr_good_pages) {
-               p->swap_map[0] = SWAP_MAP_BAD;
+               swap_map[0] = SWAP_MAP_BAD;
                p->max = maxpages;
                p->pages = nr_good_pages;
                nr_extents = setup_swap_extents(p, &span);
@@ -1642,6 +1671,12 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
 
        mutex_lock(&swapon_mutex);
        spin_lock(&swap_lock);
+       if (swap_flags & SWAP_FLAG_PREFER)
+               p->prio =
+                 (swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT;
+       else
+               p->prio = --least_priority;
+       p->swap_map = swap_map;
        p->flags = SWP_ACTIVE;
        nr_swap_pages += nr_good_pages;
        total_swap_pages += nr_good_pages;
@@ -1677,12 +1712,8 @@ bad_swap:
        destroy_swap_extents(p);
 bad_swap_2:
        spin_lock(&swap_lock);
-       swap_map = p->swap_map;
        p->swap_file = NULL;
-       p->swap_map = NULL;
        p->flags = 0;
-       if (!(swap_flags & SWAP_FLAG_PREFER))
-               ++least_priority;
        spin_unlock(&swap_lock);
        vfree(swap_map);
        if (swap_file)