]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - mm/memory_hotplug.c
Merge tag 'vfio-v5.6-rc1' of git://github.com/awilliam/linux-vfio
[linux.git] / mm / memory_hotplug.c
index 55ac23ef11c1cbf1e13f187b5c850d4075128ad5..36d80915ddc282e4fa25b79d0b768ee708e61c48 100644 (file)
@@ -483,8 +483,9 @@ static void update_pgdat_span(struct pglist_data *pgdat)
        pgdat->node_spanned_pages = node_end_pfn - node_start_pfn;
 }
 
-static void __remove_zone(struct zone *zone, unsigned long start_pfn,
-               unsigned long nr_pages)
+void __ref remove_pfn_range_from_zone(struct zone *zone,
+                                     unsigned long start_pfn,
+                                     unsigned long nr_pages)
 {
        struct pglist_data *pgdat = zone->zone_pgdat;
        unsigned long flags;
@@ -499,28 +500,30 @@ static void __remove_zone(struct zone *zone, unsigned long start_pfn,
                return;
 #endif
 
+       clear_zone_contiguous(zone);
+
        pgdat_resize_lock(zone->zone_pgdat, &flags);
        shrink_zone_span(zone, start_pfn, start_pfn + nr_pages);
        update_pgdat_span(pgdat);
        pgdat_resize_unlock(zone->zone_pgdat, &flags);
+
+       set_zone_contiguous(zone);
 }
 
-static void __remove_section(struct zone *zone, unsigned long pfn,
-               unsigned long nr_pages, unsigned long map_offset,
-               struct vmem_altmap *altmap)
+static void __remove_section(unsigned long pfn, unsigned long nr_pages,
+                            unsigned long map_offset,
+                            struct vmem_altmap *altmap)
 {
        struct mem_section *ms = __nr_to_section(pfn_to_section_nr(pfn));
 
        if (WARN_ON_ONCE(!valid_section(ms)))
                return;
 
-       __remove_zone(zone, pfn, nr_pages);
        sparse_remove_section(ms, pfn, nr_pages, map_offset, altmap);
 }
 
 /**
- * __remove_pages() - remove sections of pages from a zone
- * @zone: zone from which pages need to be removed
+ * __remove_pages() - remove sections of pages
  * @pfn: starting pageframe (must be aligned to start of a section)
  * @nr_pages: number of pages to remove (must be multiple of section size)
  * @altmap: alternative device page map or %NULL if default memmap is used
@@ -530,16 +533,14 @@ static void __remove_section(struct zone *zone, unsigned long pfn,
  * sure that pages are marked reserved and zones are adjust properly by
  * calling offline_pages().
  */
-void __remove_pages(struct zone *zone, unsigned long pfn,
-                   unsigned long nr_pages, struct vmem_altmap *altmap)
+void __remove_pages(unsigned long pfn, unsigned long nr_pages,
+                   struct vmem_altmap *altmap)
 {
        unsigned long map_offset = 0;
        unsigned long nr, start_sec, end_sec;
 
        map_offset = vmem_altmap_offset(altmap);
 
-       clear_zone_contiguous(zone);
-
        if (check_pfn_span(pfn, nr_pages, "remove"))
                return;
 
@@ -551,13 +552,11 @@ void __remove_pages(struct zone *zone, unsigned long pfn,
                cond_resched();
                pfns = min(nr_pages, PAGES_PER_SECTION
                                - (pfn & ~PAGE_SECTION_MASK));
-               __remove_section(zone, pfn, pfns, map_offset, altmap);
+               __remove_section(pfn, pfns, map_offset, altmap);
                pfn += pfns;
                nr_pages -= pfns;
                map_offset = 0;
        }
-
-       set_zone_contiguous(zone);
 }
 
 int set_online_page_callback(online_page_callback_t callback)
@@ -784,27 +783,18 @@ struct zone * zone_for_pfn_range(int online_type, int nid, unsigned start_pfn,
        return default_zone_for_pfn(nid, start_pfn, nr_pages);
 }
 
-int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type)
+int __ref online_pages(unsigned long pfn, unsigned long nr_pages,
+                      int online_type, int nid)
 {
        unsigned long flags;
        unsigned long onlined_pages = 0;
        struct zone *zone;
        int need_zonelists_rebuild = 0;
-       int nid;
        int ret;
        struct memory_notify arg;
-       struct memory_block *mem;
 
        mem_hotplug_begin();
 
-       /*
-        * We can't use pfn_to_nid() because nid might be stored in struct page
-        * which is not yet initialized. Instead, we find nid from memory block.
-        */
-       mem = find_memory_block(__pfn_to_section(pfn));
-       nid = mem->nid;
-       put_device(&mem->dev);
-
        /* associate pfn range with the zone */
        zone = zone_for_pfn_range(online_type, nid, pfn, nr_pages);
        move_pfn_range_to_zone(zone, pfn, nr_pages, NULL);
@@ -869,6 +859,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
                 (unsigned long long) pfn << PAGE_SHIFT,
                 (((unsigned long long) pfn + nr_pages) << PAGE_SHIFT) - 1);
        memory_notify(MEM_CANCEL_ONLINE, &arg);
+       remove_pfn_range_from_zone(zone, pfn, nr_pages);
        mem_hotplug_done();
        return ret;
 }
@@ -1182,7 +1173,7 @@ static bool is_pageblock_removable_nolock(unsigned long pfn)
        if (!zone_spans_pfn(zone, pfn))
                return false;
 
-       return !has_unmovable_pages(zone, page, 0, MIGRATE_MOVABLE,
+       return !has_unmovable_pages(zone, page, MIGRATE_MOVABLE,
                                    MEMORY_OFFLINE);
 }
 
@@ -1628,6 +1619,7 @@ static int __ref __offline_pages(unsigned long start_pfn,
        writeback_set_ratelimit();
 
        memory_notify(MEM_OFFLINE, &arg);
+       remove_pfn_range_from_zone(zone, start_pfn, nr_pages);
        mem_hotplug_done();
        return 0;
 
@@ -1763,8 +1755,6 @@ static int __ref try_remove_memory(int nid, u64 start, u64 size)
 
        BUG_ON(check_hotplug_memory_range(start, size));
 
-       mem_hotplug_begin();
-
        /*
         * All memory blocks must be offlined before removing memory.  Check
         * whether all memory blocks in question are offline and return error
@@ -1777,9 +1767,14 @@ static int __ref try_remove_memory(int nid, u64 start, u64 size)
        /* remove memmap entry */
        firmware_map_remove(start, start + size, "System RAM");
 
-       /* remove memory block devices before removing memory */
+       /*
+        * Memory block device removal under the device_hotplug_lock is
+        * a barrier against racing online attempts.
+        */
        remove_memory_block_devices(start, size);
 
+       mem_hotplug_begin();
+
        arch_remove_memory(nid, start, size, NULL);
        memblock_free(start, size);
        memblock_remove(start, size);