diff options
| -rw-r--r-- | drivers/base/memory.c | 19 | ||||
| -rw-r--r-- | include/linux/memory_hotplug.h | 6 | ||||
| -rw-r--r-- | mm/memory_hotplug.c | 33 |
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 | */ |
| 223 | static int | 224 | static int |
| 224 | memory_block_action(unsigned long phys_index, unsigned long action, int online_type) | 225 | memory_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(); | ||
| 352 | err: | 357 | err: |
| 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, | |||
| 192 | void get_online_mems(void); | 192 | void get_online_mems(void); |
| 193 | void put_online_mems(void); | 193 | void put_online_mems(void); |
| 194 | 194 | ||
| 195 | void mem_hotplug_begin(void); | ||
| 196 | void 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) | |||
| 231 | static inline void get_online_mems(void) {} | 234 | static inline void get_online_mems(void) {} |
| 232 | static inline void put_online_mems(void) {} | 235 | static inline void put_online_mems(void) {} |
| 233 | 236 | ||
| 237 | static inline void mem_hotplug_begin(void) {} | ||
| 238 | static 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 | ||
| 107 | static void mem_hotplug_begin(void) | 107 | void 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 | ||
| 122 | static void mem_hotplug_done(void) | 122 | void 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() */ | ||
| 962 | int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type) | 963 | int __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); |
| 1064 | out: | 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 | ||
| 1801 | failed_removal: | 1794 | failed_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 | |||
| 1809 | out: | ||
| 1810 | mem_hotplug_done(); | ||
| 1811 | return ret; | 1801 | return ret; |
| 1812 | } | 1802 | } |
| 1813 | 1803 | ||
| 1804 | /* Must be protected by mem_hotplug_begin() */ | ||
| 1814 | int offline_pages(unsigned long start_pfn, unsigned long nr_pages) | 1805 | int 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); |
