aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/memory.c19
-rw-r--r--include/linux/memory_hotplug.h6
-rw-r--r--mm/memory_hotplug.c33
3 files changed, 30 insertions, 28 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index ab5c9a7dfeca..2804aed3f416 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -219,6 +219,7 @@ static bool pages_correctly_reserved(unsigned long start_pfn)
219/* 219/*
220 * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is 220 * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
221 * OK to have direct references to sparsemem variables in here. 221 * OK to have direct references to sparsemem variables in here.
222 * Must already be protected by mem_hotplug_begin().
222 */ 223 */
223static int 224static int
224memory_block_action(unsigned long phys_index, unsigned long action, int online_type) 225memory_block_action(unsigned long phys_index, unsigned long action, int online_type)
@@ -286,6 +287,7 @@ static int memory_subsys_online(struct device *dev)
286 if (mem->online_type < 0) 287 if (mem->online_type < 0)
287 mem->online_type = MMOP_ONLINE_KEEP; 288 mem->online_type = MMOP_ONLINE_KEEP;
288 289
290 /* Already under protection of mem_hotplug_begin() */
289 ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); 291 ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
290 292
291 /* clear online_type */ 293 /* clear online_type */
@@ -328,17 +330,19 @@ store_mem_state(struct device *dev,
328 goto err; 330 goto err;
329 } 331 }
330 332
333 /*
334 * Memory hotplug needs to hold mem_hotplug_begin() for probe to find
335 * the correct memory block to online before doing device_online(dev),
336 * which will take dev->mutex. Take the lock early to prevent an
337 * inversion, memory_subsys_online() callbacks will be implemented by
338 * assuming it's already protected.
339 */
340 mem_hotplug_begin();
341
331 switch (online_type) { 342 switch (online_type) {
332 case MMOP_ONLINE_KERNEL: 343 case MMOP_ONLINE_KERNEL:
333 case MMOP_ONLINE_MOVABLE: 344 case MMOP_ONLINE_MOVABLE:
334 case MMOP_ONLINE_KEEP: 345 case MMOP_ONLINE_KEEP:
335 /*
336 * mem->online_type is not protected so there can be a
337 * race here. However, when racing online, the first
338 * will succeed and the second will just return as the
339 * block will already be online. The online type
340 * could be either one, but that is expected.
341 */
342 mem->online_type = online_type; 346 mem->online_type = online_type;
343 ret = device_online(&mem->dev); 347 ret = device_online(&mem->dev);
344 break; 348 break;
@@ -349,6 +353,7 @@ store_mem_state(struct device *dev,
349 ret = -EINVAL; /* should never happen */ 353 ret = -EINVAL; /* should never happen */
350 } 354 }
351 355
356 mem_hotplug_done();
352err: 357err:
353 unlock_device_hotplug(); 358 unlock_device_hotplug();
354 359
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 8f1a41951df9..6ffa0ac7f7d6 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -192,6 +192,9 @@ extern void get_page_bootmem(unsigned long ingo, struct page *page,
192void get_online_mems(void); 192void get_online_mems(void);
193void put_online_mems(void); 193void put_online_mems(void);
194 194
195void mem_hotplug_begin(void);
196void mem_hotplug_done(void);
197
195#else /* ! CONFIG_MEMORY_HOTPLUG */ 198#else /* ! CONFIG_MEMORY_HOTPLUG */
196/* 199/*
197 * Stub functions for when hotplug is off 200 * Stub functions for when hotplug is off
@@ -231,6 +234,9 @@ static inline int try_online_node(int nid)
231static inline void get_online_mems(void) {} 234static inline void get_online_mems(void) {}
232static inline void put_online_mems(void) {} 235static inline void put_online_mems(void) {}
233 236
237static inline void mem_hotplug_begin(void) {}
238static inline void mem_hotplug_done(void) {}
239
234#endif /* ! CONFIG_MEMORY_HOTPLUG */ 240#endif /* ! CONFIG_MEMORY_HOTPLUG */
235 241
236#ifdef CONFIG_MEMORY_HOTREMOVE 242#ifdef CONFIG_MEMORY_HOTREMOVE
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index aaec7758eec3..e2e8014fb755 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -104,7 +104,7 @@ void put_online_mems(void)
104 104
105} 105}
106 106
107static void mem_hotplug_begin(void) 107void mem_hotplug_begin(void)
108{ 108{
109 mem_hotplug.active_writer = current; 109 mem_hotplug.active_writer = current;
110 110
@@ -119,7 +119,7 @@ static void mem_hotplug_begin(void)
119 } 119 }
120} 120}
121 121
122static void mem_hotplug_done(void) 122void mem_hotplug_done(void)
123{ 123{
124 mem_hotplug.active_writer = NULL; 124 mem_hotplug.active_writer = NULL;
125 mutex_unlock(&mem_hotplug.lock); 125 mutex_unlock(&mem_hotplug.lock);
@@ -959,6 +959,7 @@ static void node_states_set_node(int node, struct memory_notify *arg)
959} 959}
960 960
961 961
962/* Must be protected by mem_hotplug_begin() */
962int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type) 963int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type)
963{ 964{
964 unsigned long flags; 965 unsigned long flags;
@@ -969,7 +970,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
969 int ret; 970 int ret;
970 struct memory_notify arg; 971 struct memory_notify arg;
971 972
972 mem_hotplug_begin();
973 /* 973 /*
974 * This doesn't need a lock to do pfn_to_page(). 974 * This doesn't need a lock to do pfn_to_page().
975 * The section can't be removed here because of the 975 * The section can't be removed here because of the
@@ -977,21 +977,20 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
977 */ 977 */
978 zone = page_zone(pfn_to_page(pfn)); 978 zone = page_zone(pfn_to_page(pfn));
979 979
980 ret = -EINVAL;
981 if ((zone_idx(zone) > ZONE_NORMAL || 980 if ((zone_idx(zone) > ZONE_NORMAL ||
982 online_type == MMOP_ONLINE_MOVABLE) && 981 online_type == MMOP_ONLINE_MOVABLE) &&
983 !can_online_high_movable(zone)) 982 !can_online_high_movable(zone))
984 goto out; 983 return -EINVAL;
985 984
986 if (online_type == MMOP_ONLINE_KERNEL && 985 if (online_type == MMOP_ONLINE_KERNEL &&
987 zone_idx(zone) == ZONE_MOVABLE) { 986 zone_idx(zone) == ZONE_MOVABLE) {
988 if (move_pfn_range_left(zone - 1, zone, pfn, pfn + nr_pages)) 987 if (move_pfn_range_left(zone - 1, zone, pfn, pfn + nr_pages))
989 goto out; 988 return -EINVAL;
990 } 989 }
991 if (online_type == MMOP_ONLINE_MOVABLE && 990 if (online_type == MMOP_ONLINE_MOVABLE &&
992 zone_idx(zone) == ZONE_MOVABLE - 1) { 991 zone_idx(zone) == ZONE_MOVABLE - 1) {
993 if (move_pfn_range_right(zone, zone + 1, pfn, pfn + nr_pages)) 992 if (move_pfn_range_right(zone, zone + 1, pfn, pfn + nr_pages))
994 goto out; 993 return -EINVAL;
995 } 994 }
996 995
997 /* Previous code may changed the zone of the pfn range */ 996 /* Previous code may changed the zone of the pfn range */
@@ -1007,7 +1006,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
1007 ret = notifier_to_errno(ret); 1006 ret = notifier_to_errno(ret);
1008 if (ret) { 1007 if (ret) {
1009 memory_notify(MEM_CANCEL_ONLINE, &arg); 1008 memory_notify(MEM_CANCEL_ONLINE, &arg);
1010 goto out; 1009 return ret;
1011 } 1010 }
1012 /* 1011 /*
1013 * If this zone is not populated, then it is not in zonelist. 1012 * If this zone is not populated, then it is not in zonelist.
@@ -1031,7 +1030,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
1031 (((unsigned long long) pfn + nr_pages) 1030 (((unsigned long long) pfn + nr_pages)
1032 << PAGE_SHIFT) - 1); 1031 << PAGE_SHIFT) - 1);
1033 memory_notify(MEM_CANCEL_ONLINE, &arg); 1032 memory_notify(MEM_CANCEL_ONLINE, &arg);
1034 goto out; 1033 return ret;
1035 } 1034 }
1036 1035
1037 zone->present_pages += onlined_pages; 1036 zone->present_pages += onlined_pages;
@@ -1061,9 +1060,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
1061 1060
1062 if (onlined_pages) 1061 if (onlined_pages)
1063 memory_notify(MEM_ONLINE, &arg); 1062 memory_notify(MEM_ONLINE, &arg);
1064out: 1063 return 0;
1065 mem_hotplug_done();
1066 return ret;
1067} 1064}
1068#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ 1065#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
1069 1066
@@ -1688,21 +1685,18 @@ static int __ref __offline_pages(unsigned long start_pfn,
1688 if (!test_pages_in_a_zone(start_pfn, end_pfn)) 1685 if (!test_pages_in_a_zone(start_pfn, end_pfn))
1689 return -EINVAL; 1686 return -EINVAL;
1690 1687
1691 mem_hotplug_begin();
1692
1693 zone = page_zone(pfn_to_page(start_pfn)); 1688 zone = page_zone(pfn_to_page(start_pfn));
1694 node = zone_to_nid(zone); 1689 node = zone_to_nid(zone);
1695 nr_pages = end_pfn - start_pfn; 1690 nr_pages = end_pfn - start_pfn;
1696 1691
1697 ret = -EINVAL;
1698 if (zone_idx(zone) <= ZONE_NORMAL && !can_offline_normal(zone, nr_pages)) 1692 if (zone_idx(zone) <= ZONE_NORMAL && !can_offline_normal(zone, nr_pages))
1699 goto out; 1693 return -EINVAL;
1700 1694
1701 /* set above range as isolated */ 1695 /* set above range as isolated */
1702 ret = start_isolate_page_range(start_pfn, end_pfn, 1696 ret = start_isolate_page_range(start_pfn, end_pfn,
1703 MIGRATE_MOVABLE, true); 1697 MIGRATE_MOVABLE, true);
1704 if (ret) 1698 if (ret)
1705 goto out; 1699 return ret;
1706 1700
1707 arg.start_pfn = start_pfn; 1701 arg.start_pfn = start_pfn;
1708 arg.nr_pages = nr_pages; 1702 arg.nr_pages = nr_pages;
@@ -1795,7 +1789,6 @@ repeat:
1795 writeback_set_ratelimit(); 1789 writeback_set_ratelimit();
1796 1790
1797 memory_notify(MEM_OFFLINE, &arg); 1791 memory_notify(MEM_OFFLINE, &arg);
1798 mem_hotplug_done();
1799 return 0; 1792 return 0;
1800 1793
1801failed_removal: 1794failed_removal:
@@ -1805,12 +1798,10 @@ failed_removal:
1805 memory_notify(MEM_CANCEL_OFFLINE, &arg); 1798 memory_notify(MEM_CANCEL_OFFLINE, &arg);
1806 /* pushback to free area */ 1799 /* pushback to free area */
1807 undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE); 1800 undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE);
1808
1809out:
1810 mem_hotplug_done();
1811 return ret; 1801 return ret;
1812} 1802}
1813 1803
1804/* Must be protected by mem_hotplug_begin() */
1814int offline_pages(unsigned long start_pfn, unsigned long nr_pages) 1805int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
1815{ 1806{
1816 return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); 1807 return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ);