]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - mm/swap_state.c
xen-blkfront: use a right index when checking requests
[karo-tx-linux.git] / mm / swap_state.c
index 539b8885e3d1d4942dbb905b60b87d40726e3de5..b68c93014f50c681b5bc1f3d9da2cd8db1fe025a 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/migrate.h>
 #include <linux/vmalloc.h>
 #include <linux/swap_slots.h>
+#include <linux/huge_mm.h>
 
 #include <asm/pgtable.h>
 
@@ -38,6 +39,7 @@ struct address_space *swapper_spaces[MAX_SWAPFILES];
 static unsigned int nr_swapper_spaces[MAX_SWAPFILES];
 
 #define INC_CACHE_INFO(x)      do { swap_cache_info.x++; } while (0)
+#define ADD_CACHE_INFO(x, nr)  do { swap_cache_info.x += (nr); } while (0)
 
 static struct {
        unsigned long add_total;
@@ -90,39 +92,46 @@ void show_swap_cache_info(void)
  */
 int __add_to_swap_cache(struct page *page, swp_entry_t entry)
 {
-       int error;
+       int error, i, nr = hpage_nr_pages(page);
        struct address_space *address_space;
+       pgoff_t idx = swp_offset(entry);
 
        VM_BUG_ON_PAGE(!PageLocked(page), page);
        VM_BUG_ON_PAGE(PageSwapCache(page), page);
        VM_BUG_ON_PAGE(!PageSwapBacked(page), page);
 
-       get_page(page);
+       page_ref_add(page, nr);
        SetPageSwapCache(page);
-       set_page_private(page, entry.val);
 
        address_space = swap_address_space(entry);
        spin_lock_irq(&address_space->tree_lock);
-       error = radix_tree_insert(&address_space->page_tree,
-                                 swp_offset(entry), page);
-       if (likely(!error)) {
-               address_space->nrpages++;
-               __inc_node_page_state(page, NR_FILE_PAGES);
-               INC_CACHE_INFO(add_total);
+       for (i = 0; i < nr; i++) {
+               set_page_private(page + i, entry.val + i);
+               error = radix_tree_insert(&address_space->page_tree,
+                                         idx + i, page + i);
+               if (unlikely(error))
+                       break;
        }
-       spin_unlock_irq(&address_space->tree_lock);
-
-       if (unlikely(error)) {
+       if (likely(!error)) {
+               address_space->nrpages += nr;
+               __mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, nr);
+               ADD_CACHE_INFO(add_total, nr);
+       } else {
                /*
                 * Only the context which have set SWAP_HAS_CACHE flag
                 * would call add_to_swap_cache().
                 * So add_to_swap_cache() doesn't returns -EEXIST.
                 */
                VM_BUG_ON(error == -EEXIST);
-               set_page_private(page, 0UL);
+               set_page_private(page + i, 0UL);
+               while (i--) {
+                       radix_tree_delete(&address_space->page_tree, idx + i);
+                       set_page_private(page + i, 0UL);
+               }
                ClearPageSwapCache(page);
-               put_page(page);
+               page_ref_sub(page, nr);
        }
+       spin_unlock_irq(&address_space->tree_lock);
 
        return error;
 }
@@ -132,7 +141,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
 {
        int error;
 
-       error = radix_tree_maybe_preload(gfp_mask);
+       error = radix_tree_maybe_preload_order(gfp_mask, compound_order(page));
        if (!error) {
                error = __add_to_swap_cache(page, entry);
                radix_tree_preload_end();
@@ -146,8 +155,10 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
  */
 void __delete_from_swap_cache(struct page *page)
 {
-       swp_entry_t entry;
        struct address_space *address_space;
+       int i, nr = hpage_nr_pages(page);
+       swp_entry_t entry;
+       pgoff_t idx;
 
        VM_BUG_ON_PAGE(!PageLocked(page), page);
        VM_BUG_ON_PAGE(!PageSwapCache(page), page);
@@ -155,12 +166,15 @@ void __delete_from_swap_cache(struct page *page)
 
        entry.val = page_private(page);
        address_space = swap_address_space(entry);
-       radix_tree_delete(&address_space->page_tree, swp_offset(entry));
-       set_page_private(page, 0);
+       idx = swp_offset(entry);
+       for (i = 0; i < nr; i++) {
+               radix_tree_delete(&address_space->page_tree, idx + i);
+               set_page_private(page + i, 0);
+       }
        ClearPageSwapCache(page);
-       address_space->nrpages--;
-       __dec_node_page_state(page, NR_FILE_PAGES);
-       INC_CACHE_INFO(del_total);
+       address_space->nrpages -= nr;
+       __mod_node_page_state(page_pgdat(page), NR_FILE_PAGES, -nr);
+       ADD_CACHE_INFO(del_total, nr);
 }
 
 /**
@@ -170,7 +184,7 @@ void __delete_from_swap_cache(struct page *page)
  * Allocate swap space for the page and add the page to the
  * swap cache.  Caller needs to hold the page lock. 
  */
-int add_to_swap(struct page *page, struct list_head *list)
+int add_to_swap(struct page *page)
 {
        swp_entry_t entry;
        int err;
@@ -178,20 +192,12 @@ int add_to_swap(struct page *page, struct list_head *list)
        VM_BUG_ON_PAGE(!PageLocked(page), page);
        VM_BUG_ON_PAGE(!PageUptodate(page), page);
 
-       entry = get_swap_page();
+       entry = get_swap_page(page);
        if (!entry.val)
                return 0;
 
-       if (mem_cgroup_try_charge_swap(page, entry)) {
-               swapcache_free(entry);
-               return 0;
-       }
-
-       if (unlikely(PageTransHuge(page)))
-               if (unlikely(split_huge_page_to_list(page, list))) {
-                       swapcache_free(entry);
-                       return 0;
-               }
+       if (mem_cgroup_try_charge_swap(page, entry))
+               goto fail;
 
        /*
         * Radix-tree node allocations from PF_MEMALLOC contexts could
@@ -206,17 +212,19 @@ int add_to_swap(struct page *page, struct list_head *list)
         */
        err = add_to_swap_cache(page, entry,
                        __GFP_HIGH|__GFP_NOMEMALLOC|__GFP_NOWARN);
-
-       if (!err) {
-               return 1;
-       } else {        /* -ENOMEM radix-tree allocation failure */
+       /* -ENOMEM radix-tree allocation failure */
+       if (err)
                /*
                 * add_to_swap_cache() doesn't return -EEXIST, so we can safely
                 * clear SWAP_HAS_CACHE flag.
                 */
-               swapcache_free(entry);
-               return 0;
-       }
+               goto fail;
+
+       return 1;
+
+fail:
+       put_swap_page(page, entry);
+       return 0;
 }
 
 /*
@@ -237,8 +245,8 @@ void delete_from_swap_cache(struct page *page)
        __delete_from_swap_cache(page);
        spin_unlock_irq(&address_space->tree_lock);
 
-       swapcache_free(entry);
-       put_page(page);
+       put_swap_page(page, entry);
+       page_ref_sub(page, hpage_nr_pages(page));
 }
 
 /* 
@@ -295,7 +303,7 @@ struct page * lookup_swap_cache(swp_entry_t entry)
 
        page = find_get_page(swap_address_space(entry), swp_offset(entry));
 
-       if (page) {
+       if (page && likely(!PageTransCompound(page))) {
                INC_CACHE_INFO(find_success);
                if (TestClearPageReadahead(page))
                        atomic_inc(&swapin_readahead_hits);
@@ -389,7 +397,7 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
                 * add_to_swap_cache() doesn't return -EEXIST, so we can safely
                 * clear SWAP_HAS_CACHE flag.
                 */
-               swapcache_free(entry);
+               put_swap_page(new_page, entry);
        } while (err != -ENOMEM);
 
        if (new_page)
@@ -404,14 +412,14 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
  * the swap entry is no longer in use.
  */
 struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
-                       struct vm_area_struct *vma, unsigned long addr)
+               struct vm_area_struct *vma, unsigned long addr, bool do_poll)
 {
        bool page_was_allocated;
        struct page *retpage = __read_swap_cache_async(entry, gfp_mask,
                        vma, addr, &page_was_allocated);
 
        if (page_was_allocated)
-               swap_readpage(retpage);
+               swap_readpage(retpage, do_poll);
 
        return retpage;
 }
@@ -488,11 +496,13 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
        unsigned long start_offset, end_offset;
        unsigned long mask;
        struct blk_plug plug;
+       bool do_poll = true;
 
        mask = swapin_nr_pages(offset) - 1;
        if (!mask)
                goto skip;
 
+       do_poll = false;
        /* Read a page_cluster sized and aligned cluster around offset. */
        start_offset = offset & ~mask;
        end_offset = offset | mask;
@@ -503,10 +513,10 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
        for (offset = start_offset; offset <= end_offset ; offset++) {
                /* Ok, do the async read-ahead now */
                page = read_swap_cache_async(swp_entry(swp_type(entry), offset),
-                                               gfp_mask, vma, addr);
+                                               gfp_mask, vma, addr, false);
                if (!page)
                        continue;
-               if (offset != entry_offset)
+               if (offset != entry_offset && likely(!PageTransCompound(page)))
                        SetPageReadahead(page);
                put_page(page);
        }
@@ -514,7 +524,7 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
 
        lru_add_drain();        /* Push any new pages onto the LRU now */
 skip:
-       return read_swap_cache_async(entry, gfp_mask, vma, addr);
+       return read_swap_cache_async(entry, gfp_mask, vma, addr, do_poll);
 }
 
 int init_swap_address_space(unsigned int type, unsigned long nr_pages)