selftests: pidfd: skip test if unshare fails with EPERM
[linux-2.6-microblaze.git] / mm / memory_hotplug.c
index c4d5c45..da374cd 100644 (file)
@@ -471,11 +471,20 @@ void __ref remove_pfn_range_from_zone(struct zone *zone,
                                      unsigned long start_pfn,
                                      unsigned long nr_pages)
 {
+       const unsigned long end_pfn = start_pfn + nr_pages;
        struct pglist_data *pgdat = zone->zone_pgdat;
-       unsigned long flags;
+       unsigned long pfn, cur_nr_pages, flags;
 
        /* Poison struct pages because they are now uninitialized again. */
-       page_init_poison(pfn_to_page(start_pfn), sizeof(struct page) * nr_pages);
+       for (pfn = start_pfn; pfn < end_pfn; pfn += cur_nr_pages) {
+               cond_resched();
+
+               /* Select all remaining pages up to the next section boundary */
+               cur_nr_pages =
+                       min(end_pfn - pfn, SECTION_ALIGN_UP(pfn + 1) - pfn);
+               page_init_poison(pfn_to_page(pfn),
+                                sizeof(struct page) * cur_nr_pages);
+       }
 
 #ifdef CONFIG_ZONE_DEVICE
        /*
@@ -1201,11 +1210,17 @@ struct zone *test_pages_in_a_zone(unsigned long start_pfn,
 
 /*
  * Scan pfn range [start,end) to find movable/migratable pages (LRU pages,
- * non-lru movable pages and hugepages). We scan pfn because it's much
- * easier than scanning over linked list. This function returns the pfn
- * of the first found movable page if it's found, otherwise 0.
+ * non-lru movable pages and hugepages). Will skip over most unmovable
+ * pages (esp., pages that can be skipped when offlining), but bail out on
+ * definitely unmovable pages.
+ *
+ * Returns:
+ *     0 in case a movable page is found and movable_pfn was updated.
+ *     -ENOENT in case no movable page was found.
+ *     -EBUSY in case a definitely unmovable page was found.
  */
-static unsigned long scan_movable_pages(unsigned long start, unsigned long end)
+static int scan_movable_pages(unsigned long start, unsigned long end,
+                             unsigned long *movable_pfn)
 {
        unsigned long pfn;
 
@@ -1217,18 +1232,30 @@ static unsigned long scan_movable_pages(unsigned long start, unsigned long end)
                        continue;
                page = pfn_to_page(pfn);
                if (PageLRU(page))
-                       return pfn;
+                       goto found;
                if (__PageMovable(page))
-                       return pfn;
+                       goto found;
+
+               /*
+                * PageOffline() pages that are not marked __PageMovable() and
+                * have a reference count > 0 (after MEM_GOING_OFFLINE) are
+                * definitely unmovable. If their reference count would be 0,
+                * they could at least be skipped when offlining memory.
+                */
+               if (PageOffline(page) && page_count(page))
+                       return -EBUSY;
 
                if (!PageHuge(page))
                        continue;
                head = compound_head(page);
                if (page_huge_active(head))
-                       return pfn;
+                       goto found;
                skip = compound_nr(head) - (page - head);
                pfn += skip - 1;
        }
+       return -ENOENT;
+found:
+       *movable_pfn = pfn;
        return 0;
 }
 
@@ -1491,7 +1518,8 @@ static int __ref __offline_pages(unsigned long start_pfn,
        }
 
        do {
-               for (pfn = start_pfn; pfn;) {
+               pfn = start_pfn;
+               do {
                        if (signal_pending(current)) {
                                ret = -EINTR;
                                reason = "signal backoff";
@@ -1501,14 +1529,19 @@ static int __ref __offline_pages(unsigned long start_pfn,
                        cond_resched();
                        lru_add_drain_all();
 
-                       pfn = scan_movable_pages(pfn, end_pfn);
-                       if (pfn) {
+                       ret = scan_movable_pages(pfn, end_pfn, &pfn);
+                       if (!ret) {
                                /*
                                 * TODO: fatal migration failures should bail
                                 * out
                                 */
                                do_migrate_range(pfn, end_pfn);
                        }
+               } while (!ret);
+
+               if (ret != -ENOENT) {
+                       reason = "unmovable page";
+                       goto failed_removal_isolated;
                }
 
                /*
@@ -1774,4 +1807,41 @@ int remove_memory(int nid, u64 start, u64 size)
        return rc;
 }
 EXPORT_SYMBOL_GPL(remove_memory);
+
+/*
+ * Try to offline and remove a memory block. Might take a long time to
+ * finish in case memory is still in use. Primarily useful for memory devices
+ * that logically unplugged all memory (so it's no longer in use) and want to
+ * offline + remove the memory block.
+ */
+int offline_and_remove_memory(int nid, u64 start, u64 size)
+{
+       struct memory_block *mem;
+       int rc = -EINVAL;
+
+       if (!IS_ALIGNED(start, memory_block_size_bytes()) ||
+           size != memory_block_size_bytes())
+               return rc;
+
+       lock_device_hotplug();
+       mem = find_memory_block(__pfn_to_section(PFN_DOWN(start)));
+       if (mem)
+               rc = device_offline(&mem->dev);
+       /* Ignore if the device is already offline. */
+       if (rc > 0)
+               rc = 0;
+
+       /*
+        * In case we succeeded to offline the memory block, remove it.
+        * This cannot fail as it cannot get onlined in the meantime.
+        */
+       if (!rc) {
+               rc = try_remove_memory(nid, start, size);
+               WARN_ON_ONCE(rc);
+       }
+       unlock_device_hotplug();
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(offline_and_remove_memory);
 #endif /* CONFIG_MEMORY_HOTREMOVE */