]> rtime.felk.cvut.cz Git - zynq/linux.git/blobdiff - mm/page_alloc.c
Merge branch '4.0.8-rt6'
[zynq/linux.git] / mm / page_alloc.c
index 40e29429e7b0995bd5799bd6263b18d6ce8261cb..700dc451a75b0124970ed5fafd6581faefd10806 100644 (file)
@@ -60,6 +60,7 @@
 #include <linux/page_ext.h>
 #include <linux/hugetlb.h>
 #include <linux/sched/rt.h>
+#include <linux/locallock.h>
 #include <linux/page_owner.h>
 
 #include <asm/sections.h>
@@ -233,6 +234,18 @@ EXPORT_SYMBOL(nr_node_ids);
 EXPORT_SYMBOL(nr_online_nodes);
 #endif
 
+static DEFINE_LOCAL_IRQ_LOCK(pa_lock);
+
+#ifdef CONFIG_PREEMPT_RT_BASE
+# define cpu_lock_irqsave(cpu, flags)          \
+       local_lock_irqsave_on(pa_lock, flags, cpu)
+# define cpu_unlock_irqrestore(cpu, flags)     \
+       local_unlock_irqrestore_on(pa_lock, flags, cpu)
+#else
+# define cpu_lock_irqsave(cpu, flags)          local_irq_save(flags)
+# define cpu_unlock_irqrestore(cpu, flags)     local_irq_restore(flags)
+#endif
+
 int page_group_by_mobility_disabled __read_mostly;
 
 void set_pageblock_migratetype(struct page *page, int migratetype)
@@ -681,7 +694,7 @@ static inline int free_pages_check(struct page *page)
 }
 
 /*
- * Frees a number of pages from the PCP lists
+ * Frees a number of pages which have been collected from the pcp lists.
  * Assumes all pages on list are in same zone, and of same order.
  * count is the number of pages to free.
  *
@@ -692,18 +705,51 @@ static inline int free_pages_check(struct page *page)
  * pinned" detection logic.
  */
 static void free_pcppages_bulk(struct zone *zone, int count,
-                                       struct per_cpu_pages *pcp)
+                              struct list_head *list)
 {
-       int migratetype = 0;
-       int batch_free = 0;
        int to_free = count;
        unsigned long nr_scanned;
+       unsigned long flags;
+
+       spin_lock_irqsave(&zone->lock, flags);
 
-       spin_lock(&zone->lock);
        nr_scanned = zone_page_state(zone, NR_PAGES_SCANNED);
        if (nr_scanned)
                __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned);
 
+       while (!list_empty(list)) {
+               struct page *page = list_first_entry(list, struct page, lru);
+               int mt; /* migratetype of the to-be-freed page */
+
+               /* must delete as __free_one_page list manipulates */
+               list_del(&page->lru);
+
+               mt = get_freepage_migratetype(page);
+               if (unlikely(has_isolate_pageblock(zone)))
+                       mt = get_pageblock_migratetype(page);
+
+               /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */
+               __free_one_page(page, page_to_pfn(page), zone, 0, mt);
+               trace_mm_page_pcpu_drain(page, 0, mt);
+               to_free--;
+       }
+       WARN_ON(to_free != 0);
+       spin_unlock_irqrestore(&zone->lock, flags);
+}
+
+/*
+ * Moves a number of pages from the PCP lists to free list which
+ * is freed outside of the locked region.
+ *
+ * Assumes all pages on list are in same zone, and of same order.
+ * count is the number of pages to free.
+ */
+static void isolate_pcp_pages(int to_free, struct per_cpu_pages *src,
+                             struct list_head *dst)
+{
+       int migratetype = 0;
+       int batch_free = 0;
+
        while (to_free) {
                struct page *page;
                struct list_head *list;
@@ -719,7 +765,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
                        batch_free++;
                        if (++migratetype == MIGRATE_PCPTYPES)
                                migratetype = 0;
-                       list = &pcp->lists[migratetype];
+                       list = &src->lists[migratetype];
                } while (list_empty(list));
 
                /* This is the only non-empty list. Free them all. */
@@ -727,21 +773,11 @@ static void free_pcppages_bulk(struct zone *zone, int count,
                        batch_free = to_free;
 
                do {
-                       int mt; /* migratetype of the to-be-freed page */
-
-                       page = list_entry(list->prev, struct page, lru);
-                       /* must delete as __free_one_page list manipulates */
+                       page = list_last_entry(list, struct page, lru);
                        list_del(&page->lru);
-                       mt = get_freepage_migratetype(page);
-                       if (unlikely(has_isolate_pageblock(zone)))
-                               mt = get_pageblock_migratetype(page);
-
-                       /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */
-                       __free_one_page(page, page_to_pfn(page), zone, 0, mt);
-                       trace_mm_page_pcpu_drain(page, 0, mt);
+                       list_add(&page->lru, dst);
                } while (--to_free && --batch_free && !list_empty(list));
        }
-       spin_unlock(&zone->lock);
 }
 
 static void free_one_page(struct zone *zone,
@@ -750,7 +786,9 @@ static void free_one_page(struct zone *zone,
                                int migratetype)
 {
        unsigned long nr_scanned;
-       spin_lock(&zone->lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&zone->lock, flags);
        nr_scanned = zone_page_state(zone, NR_PAGES_SCANNED);
        if (nr_scanned)
                __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned);
@@ -760,7 +798,7 @@ static void free_one_page(struct zone *zone,
                migratetype = get_pfnblock_migratetype(page, pfn);
        }
        __free_one_page(page, pfn, zone, order, migratetype);
-       spin_unlock(&zone->lock);
+       spin_unlock_irqrestore(&zone->lock, flags);
 }
 
 static int free_tail_pages_check(struct page *head_page, struct page *page)
@@ -825,11 +863,11 @@ static void __free_pages_ok(struct page *page, unsigned int order)
                return;
 
        migratetype = get_pfnblock_migratetype(page, pfn);
-       local_irq_save(flags);
+       local_lock_irqsave(pa_lock, flags);
        __count_vm_events(PGFREE, 1 << order);
        set_freepage_migratetype(page, migratetype);
        free_one_page(page_zone(page), page, pfn, order, migratetype);
-       local_irq_restore(flags);
+       local_unlock_irqrestore(pa_lock, flags);
 }
 
 void __init __free_pages_bootmem(struct page *page, unsigned int order)
@@ -1318,16 +1356,18 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
 void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
 {
        unsigned long flags;
+       LIST_HEAD(dst);
        int to_drain, batch;
 
-       local_irq_save(flags);
+       local_lock_irqsave(pa_lock, flags);
        batch = ACCESS_ONCE(pcp->batch);
        to_drain = min(pcp->count, batch);
        if (to_drain > 0) {
-               free_pcppages_bulk(zone, to_drain, pcp);
+               isolate_pcp_pages(to_drain, pcp, &dst);
                pcp->count -= to_drain;
        }
-       local_irq_restore(flags);
+       local_unlock_irqrestore(pa_lock, flags);
+       free_pcppages_bulk(zone, to_drain, &dst);
 }
 #endif
 
@@ -1343,16 +1383,21 @@ static void drain_pages_zone(unsigned int cpu, struct zone *zone)
        unsigned long flags;
        struct per_cpu_pageset *pset;
        struct per_cpu_pages *pcp;
+       LIST_HEAD(dst);
+       int count;
 
-       local_irq_save(flags);
+       cpu_lock_irqsave(cpu, flags);
        pset = per_cpu_ptr(zone->pageset, cpu);
 
        pcp = &pset->pcp;
-       if (pcp->count) {
-               free_pcppages_bulk(zone, pcp->count, pcp);
+       count = pcp->count;
+       if (count) {
+               isolate_pcp_pages(count, pcp, &dst);
                pcp->count = 0;
        }
-       local_irq_restore(flags);
+       cpu_unlock_irqrestore(cpu, flags);
+       if (count)
+               free_pcppages_bulk(zone, count, &dst);
 }
 
 /*
@@ -1438,8 +1483,17 @@ void drain_all_pages(struct zone *zone)
                else
                        cpumask_clear_cpu(cpu, &cpus_with_pcps);
        }
+#ifndef CONFIG_PREEMPT_RT_BASE
        on_each_cpu_mask(&cpus_with_pcps, (smp_call_func_t) drain_local_pages,
                                                                zone, 1);
+#else
+       for_each_cpu(cpu, &cpus_with_pcps) {
+               if (zone)
+                       drain_pages_zone(cpu, zone);
+               else
+                       drain_pages(cpu);
+       }
+#endif
 }
 
 #ifdef CONFIG_HIBERNATION
@@ -1495,7 +1549,7 @@ void free_hot_cold_page(struct page *page, bool cold)
 
        migratetype = get_pfnblock_migratetype(page, pfn);
        set_freepage_migratetype(page, migratetype);
-       local_irq_save(flags);
+       local_lock_irqsave(pa_lock, flags);
        __count_vm_event(PGFREE);
 
        /*
@@ -1521,12 +1575,17 @@ void free_hot_cold_page(struct page *page, bool cold)
        pcp->count++;
        if (pcp->count >= pcp->high) {
                unsigned long batch = ACCESS_ONCE(pcp->batch);
-               free_pcppages_bulk(zone, batch, pcp);
+               LIST_HEAD(dst);
+
+               isolate_pcp_pages(batch, pcp, &dst);
                pcp->count -= batch;
+               local_unlock_irqrestore(pa_lock, flags);
+               free_pcppages_bulk(zone, batch, &dst);
+               return;
        }
 
 out:
-       local_irq_restore(flags);
+       local_unlock_irqrestore(pa_lock, flags);
 }
 
 /*
@@ -1657,7 +1716,7 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
                struct per_cpu_pages *pcp;
                struct list_head *list;
 
-               local_irq_save(flags);
+               local_lock_irqsave(pa_lock, flags);
                pcp = &this_cpu_ptr(zone->pageset)->pcp;
                list = &pcp->lists[migratetype];
                if (list_empty(list)) {
@@ -1689,13 +1748,15 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
                         */
                        WARN_ON_ONCE(order > 1);
                }
-               spin_lock_irqsave(&zone->lock, flags);
+               local_spin_lock_irqsave(pa_lock, &zone->lock, flags);
                page = __rmqueue(zone, order, migratetype);
-               spin_unlock(&zone->lock);
-               if (!page)
+               if (!page) {
+                       spin_unlock(&zone->lock);
                        goto failed;
+               }
                __mod_zone_freepage_state(zone, -(1 << order),
                                          get_freepage_migratetype(page));
+               spin_unlock(&zone->lock);
        }
 
        __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
@@ -1705,13 +1766,13 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
 
        __count_zone_vm_events(PGALLOC, zone, 1 << order);
        zone_statistics(preferred_zone, zone, gfp_flags);
-       local_irq_restore(flags);
+       local_unlock_irqrestore(pa_lock, flags);
 
        VM_BUG_ON_PAGE(bad_range(zone, page), page);
        return page;
 
 failed:
-       local_irq_restore(flags);
+       local_unlock_irqrestore(pa_lock, flags);
        return NULL;
 }
 
@@ -5607,6 +5668,7 @@ static int page_alloc_cpu_notify(struct notifier_block *self,
 void __init page_alloc_init(void)
 {
        hotcpu_notifier(page_alloc_cpu_notify, 0);
+       local_irq_lock_init(pa_lock);
 }
 
 /*
@@ -6501,7 +6563,7 @@ void zone_pcp_reset(struct zone *zone)
        struct per_cpu_pageset *pset;
 
        /* avoid races with drain_pages()  */
-       local_irq_save(flags);
+       local_lock_irqsave(pa_lock, flags);
        if (zone->pageset != &boot_pageset) {
                for_each_online_cpu(cpu) {
                        pset = per_cpu_ptr(zone->pageset, cpu);
@@ -6510,7 +6572,7 @@ void zone_pcp_reset(struct zone *zone)
                free_percpu(zone->pageset);
                zone->pageset = &boot_pageset;
        }
-       local_irq_restore(flags);
+       local_unlock_irqrestore(pa_lock, flags);
 }
 
 #ifdef CONFIG_MEMORY_HOTREMOVE