mm/memcg: warning on !memcg after readahead page charged
[linux-2.6-microblaze.git] / mm / swap.c
index c3187d0..2cca714 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -79,16 +79,14 @@ static DEFINE_PER_CPU(struct lru_pvecs, lru_pvecs) = {
 static void __page_cache_release(struct page *page)
 {
        if (PageLRU(page)) {
-               pg_data_t *pgdat = page_pgdat(page);
                struct lruvec *lruvec;
                unsigned long flags;
 
-               spin_lock_irqsave(&pgdat->lru_lock, flags);
-               lruvec = mem_cgroup_page_lruvec(page, pgdat);
+               lruvec = lock_page_lruvec_irqsave(page, &flags);
                VM_BUG_ON_PAGE(!PageLRU(page), page);
                __ClearPageLRU(page);
                del_page_from_lru_list(page, lruvec, page_off_lru(page));
-               spin_unlock_irqrestore(&pgdat->lru_lock, flags);
+               unlock_page_lruvec_irqrestore(lruvec, flags);
        }
        __ClearPageWaiters(page);
 }
@@ -207,33 +205,30 @@ static void pagevec_lru_move_fn(struct pagevec *pvec,
        void (*move_fn)(struct page *page, struct lruvec *lruvec))
 {
        int i;
-       struct pglist_data *pgdat = NULL;
-       struct lruvec *lruvec;
+       struct lruvec *lruvec = NULL;
        unsigned long flags = 0;
 
        for (i = 0; i < pagevec_count(pvec); i++) {
                struct page *page = pvec->pages[i];
-               struct pglist_data *pagepgdat = page_pgdat(page);
 
-               if (pagepgdat != pgdat) {
-                       if (pgdat)
-                               spin_unlock_irqrestore(&pgdat->lru_lock, flags);
-                       pgdat = pagepgdat;
-                       spin_lock_irqsave(&pgdat->lru_lock, flags);
-               }
+               /* block memcg migration during page moving between lru */
+               if (!TestClearPageLRU(page))
+                       continue;
 
-               lruvec = mem_cgroup_page_lruvec(page, pgdat);
+               lruvec = relock_page_lruvec_irqsave(page, lruvec, &flags);
                (*move_fn)(page, lruvec);
+
+               SetPageLRU(page);
        }
-       if (pgdat)
-               spin_unlock_irqrestore(&pgdat->lru_lock, flags);
+       if (lruvec)
+               unlock_page_lruvec_irqrestore(lruvec, flags);
        release_pages(pvec->pages, pvec->nr);
        pagevec_reinit(pvec);
 }
 
 static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec)
 {
-       if (PageLRU(page) && !PageUnevictable(page)) {
+       if (!PageUnevictable(page)) {
                del_page_from_lru_list(page, lruvec, page_lru(page));
                ClearPageActive(page);
                add_page_to_lru_list_tail(page, lruvec, page_lru(page));
@@ -268,9 +263,15 @@ void lru_note_cost(struct lruvec *lruvec, bool file, unsigned int nr_pages)
 {
        do {
                unsigned long lrusize;
-               struct pglist_data *pgdat = lruvec_pgdat(lruvec);
 
-               spin_lock_irq(&pgdat->lru_lock);
+               /*
+                * Hold lruvec->lru_lock is safe here, since
+                * 1) The pinned lruvec in reclaim, or
+                * 2) From a pre-LRU page during refault (which also holds the
+                *    rcu lock, so would be safe even if the page was on the LRU
+                *    and could move simultaneously to a new lruvec).
+                */
+               spin_lock_irq(&lruvec->lru_lock);
                /* Record cost event */
                if (file)
                        lruvec->file_cost += nr_pages;
@@ -294,7 +295,7 @@ void lru_note_cost(struct lruvec *lruvec, bool file, unsigned int nr_pages)
                        lruvec->file_cost /= 2;
                        lruvec->anon_cost /= 2;
                }
-               spin_unlock_irq(&pgdat->lru_lock);
+               spin_unlock_irq(&lruvec->lru_lock);
        } while ((lruvec = parent_lruvec(lruvec)));
 }
 
@@ -306,7 +307,7 @@ void lru_note_cost_page(struct page *page)
 
 static void __activate_page(struct page *page, struct lruvec *lruvec)
 {
-       if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
+       if (!PageActive(page) && !PageUnevictable(page)) {
                int lru = page_lru_base_type(page);
                int nr_pages = thp_nr_pages(page);
 
@@ -358,12 +359,15 @@ static inline void activate_page_drain(int cpu)
 
 static void activate_page(struct page *page)
 {
-       pg_data_t *pgdat = page_pgdat(page);
+       struct lruvec *lruvec;
 
        page = compound_head(page);
-       spin_lock_irq(&pgdat->lru_lock);
-       __activate_page(page, mem_cgroup_page_lruvec(page, pgdat));
-       spin_unlock_irq(&pgdat->lru_lock);
+       if (TestClearPageLRU(page)) {
+               lruvec = lock_page_lruvec_irq(page);
+               __activate_page(page, lruvec);
+               unlock_page_lruvec_irq(lruvec);
+               SetPageLRU(page);
+       }
 }
 #endif
 
@@ -519,9 +523,6 @@ static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec)
        bool active;
        int nr_pages = thp_nr_pages(page);
 
-       if (!PageLRU(page))
-               return;
-
        if (PageUnevictable(page))
                return;
 
@@ -562,7 +563,7 @@ static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec)
 
 static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec)
 {
-       if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) {
+       if (PageActive(page) && !PageUnevictable(page)) {
                int lru = page_lru_base_type(page);
                int nr_pages = thp_nr_pages(page);
 
@@ -579,7 +580,7 @@ static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec)
 
 static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec)
 {
-       if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) &&
+       if (PageAnon(page) && PageSwapBacked(page) &&
            !PageSwapCache(page) && !PageUnevictable(page)) {
                bool active = PageActive(page);
                int nr_pages = thp_nr_pages(page);
@@ -856,8 +857,7 @@ void release_pages(struct page **pages, int nr)
 {
        int i;
        LIST_HEAD(pages_to_free);
-       struct pglist_data *locked_pgdat = NULL;
-       struct lruvec *lruvec;
+       struct lruvec *lruvec = NULL;
        unsigned long flags;
        unsigned int lock_batch;
 
@@ -867,11 +867,11 @@ void release_pages(struct page **pages, int nr)
                /*
                 * Make sure the IRQ-safe lock-holding time does not get
                 * excessive with a continuous string of pages from the
-                * same pgdat. The lock is held only if pgdat != NULL.
+                * same lruvec. The lock is held only if lruvec != NULL.
                 */
-               if (locked_pgdat && ++lock_batch == SWAP_CLUSTER_MAX) {
-                       spin_unlock_irqrestore(&locked_pgdat->lru_lock, flags);
-                       locked_pgdat = NULL;
+               if (lruvec && ++lock_batch == SWAP_CLUSTER_MAX) {
+                       unlock_page_lruvec_irqrestore(lruvec, flags);
+                       lruvec = NULL;
                }
 
                page = compound_head(page);
@@ -879,10 +879,9 @@ void release_pages(struct page **pages, int nr)
                        continue;
 
                if (is_zone_device_page(page)) {
-                       if (locked_pgdat) {
-                               spin_unlock_irqrestore(&locked_pgdat->lru_lock,
-                                                      flags);
-                               locked_pgdat = NULL;
+                       if (lruvec) {
+                               unlock_page_lruvec_irqrestore(lruvec, flags);
+                               lruvec = NULL;
                        }
                        /*
                         * ZONE_DEVICE pages that return 'false' from
@@ -903,27 +902,22 @@ void release_pages(struct page **pages, int nr)
                        continue;
 
                if (PageCompound(page)) {
-                       if (locked_pgdat) {
-                               spin_unlock_irqrestore(&locked_pgdat->lru_lock, flags);
-                               locked_pgdat = NULL;
+                       if (lruvec) {
+                               unlock_page_lruvec_irqrestore(lruvec, flags);
+                               lruvec = NULL;
                        }
                        __put_compound_page(page);
                        continue;
                }
 
                if (PageLRU(page)) {
-                       struct pglist_data *pgdat = page_pgdat(page);
+                       struct lruvec *prev_lruvec = lruvec;
 
-                       if (pgdat != locked_pgdat) {
-                               if (locked_pgdat)
-                                       spin_unlock_irqrestore(&locked_pgdat->lru_lock,
-                                                                       flags);
+                       lruvec = relock_page_lruvec_irqsave(page, lruvec,
+                                                                       &flags);
+                       if (prev_lruvec != lruvec)
                                lock_batch = 0;
-                               locked_pgdat = pgdat;
-                               spin_lock_irqsave(&locked_pgdat->lru_lock, flags);
-                       }
 
-                       lruvec = mem_cgroup_page_lruvec(page, locked_pgdat);
                        VM_BUG_ON_PAGE(!PageLRU(page), page);
                        __ClearPageLRU(page);
                        del_page_from_lru_list(page, lruvec, page_off_lru(page));
@@ -933,8 +927,8 @@ void release_pages(struct page **pages, int nr)
 
                list_add(&page->lru, &pages_to_free);
        }
-       if (locked_pgdat)
-               spin_unlock_irqrestore(&locked_pgdat->lru_lock, flags);
+       if (lruvec)
+               unlock_page_lruvec_irqrestore(lruvec, flags);
 
        mem_cgroup_uncharge_list(&pages_to_free);
        free_unref_page_list(&pages_to_free);
@@ -1021,7 +1015,20 @@ static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec)
  */
 void __pagevec_lru_add(struct pagevec *pvec)
 {
-       pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn);
+       int i;
+       struct lruvec *lruvec = NULL;
+       unsigned long flags = 0;
+
+       for (i = 0; i < pagevec_count(pvec); i++) {
+               struct page *page = pvec->pages[i];
+
+               lruvec = relock_page_lruvec_irqsave(page, lruvec, &flags);
+               __pagevec_lru_add_fn(page, lruvec);
+       }
+       if (lruvec)
+               unlock_page_lruvec_irqrestore(lruvec, flags);
+       release_pages(pvec->pages, pvec->nr);
+       pagevec_reinit(pvec);
 }
 
 /**