diff options
Diffstat (limited to 'mm/memory_hotplug.c')
-rw-r--r-- | mm/memory_hotplug.c | 81 |
1 files changed, 11 insertions, 70 deletions
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 1ad92b46753e..081b4d654ed6 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -1621,6 +1621,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) | |||
1621 | { | 1621 | { |
1622 | return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); | 1622 | return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); |
1623 | } | 1623 | } |
1624 | #endif /* CONFIG_MEMORY_HOTREMOVE */ | ||
1624 | 1625 | ||
1625 | /** | 1626 | /** |
1626 | * walk_memory_range - walks through all mem sections in [start_pfn, end_pfn) | 1627 | * walk_memory_range - walks through all mem sections in [start_pfn, end_pfn) |
@@ -1634,7 +1635,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) | |||
1634 | * | 1635 | * |
1635 | * Returns the return value of func. | 1636 | * Returns the return value of func. |
1636 | */ | 1637 | */ |
1637 | static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, | 1638 | int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, |
1638 | void *arg, int (*func)(struct memory_block *, void *)) | 1639 | void *arg, int (*func)(struct memory_block *, void *)) |
1639 | { | 1640 | { |
1640 | struct memory_block *mem = NULL; | 1641 | struct memory_block *mem = NULL; |
@@ -1671,24 +1672,7 @@ static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, | |||
1671 | return 0; | 1672 | return 0; |
1672 | } | 1673 | } |
1673 | 1674 | ||
1674 | /** | 1675 | #ifdef CONFIG_MEMORY_HOTREMOVE |
1675 | * offline_memory_block_cb - callback function for offlining memory block | ||
1676 | * @mem: the memory block to be offlined | ||
1677 | * @arg: buffer to hold error msg | ||
1678 | * | ||
1679 | * Always return 0, and put the error msg in arg if any. | ||
1680 | */ | ||
1681 | static int offline_memory_block_cb(struct memory_block *mem, void *arg) | ||
1682 | { | ||
1683 | int *ret = arg; | ||
1684 | int error = offline_memory_block(mem); | ||
1685 | |||
1686 | if (error != 0 && *ret == 0) | ||
1687 | *ret = error; | ||
1688 | |||
1689 | return 0; | ||
1690 | } | ||
1691 | |||
1692 | static int is_memblock_offlined_cb(struct memory_block *mem, void *arg) | 1676 | static int is_memblock_offlined_cb(struct memory_block *mem, void *arg) |
1693 | { | 1677 | { |
1694 | int ret = !is_memblock_offlined(mem); | 1678 | int ret = !is_memblock_offlined(mem); |
@@ -1814,54 +1798,22 @@ void try_offline_node(int nid) | |||
1814 | } | 1798 | } |
1815 | EXPORT_SYMBOL(try_offline_node); | 1799 | EXPORT_SYMBOL(try_offline_node); |
1816 | 1800 | ||
1817 | int __ref remove_memory(int nid, u64 start, u64 size) | 1801 | void __ref remove_memory(int nid, u64 start, u64 size) |
1818 | { | 1802 | { |
1819 | unsigned long start_pfn, end_pfn; | 1803 | int ret; |
1820 | int ret = 0; | ||
1821 | int retry = 1; | ||
1822 | |||
1823 | start_pfn = PFN_DOWN(start); | ||
1824 | end_pfn = PFN_UP(start + size - 1); | ||
1825 | |||
1826 | /* | ||
1827 | * When CONFIG_MEMCG is on, one memory block may be used by other | ||
1828 | * blocks to store page cgroup when onlining pages. But we don't know | ||
1829 | * in what order pages are onlined. So we iterate twice to offline | ||
1830 | * memory: | ||
1831 | * 1st iterate: offline every non primary memory block. | ||
1832 | * 2nd iterate: offline primary (i.e. first added) memory block. | ||
1833 | */ | ||
1834 | repeat: | ||
1835 | walk_memory_range(start_pfn, end_pfn, &ret, | ||
1836 | offline_memory_block_cb); | ||
1837 | if (ret) { | ||
1838 | if (!retry) | ||
1839 | return ret; | ||
1840 | |||
1841 | retry = 0; | ||
1842 | ret = 0; | ||
1843 | goto repeat; | ||
1844 | } | ||
1845 | 1804 | ||
1846 | lock_memory_hotplug(); | 1805 | lock_memory_hotplug(); |
1847 | 1806 | ||
1848 | /* | 1807 | /* |
1849 | * we have offlined all memory blocks like this: | 1808 | * All memory blocks must be offlined before removing memory. Check |
1850 | * 1. lock memory hotplug | 1809 | * whether all memory blocks in question are offline and trigger a BUG() |
1851 | * 2. offline a memory block | 1810 | * if this is not the case. |
1852 | * 3. unlock memory hotplug | ||
1853 | * | ||
1854 | * repeat step1-3 to offline the memory block. All memory blocks | ||
1855 | * must be offlined before removing memory. But we don't hold the | ||
1856 | * lock in the whole operation. So we should check whether all | ||
1857 | * memory blocks are offlined. | ||
1858 | */ | 1811 | */ |
1859 | 1812 | ret = walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1), NULL, | |
1860 | ret = walk_memory_range(start_pfn, end_pfn, NULL, | ||
1861 | is_memblock_offlined_cb); | 1813 | is_memblock_offlined_cb); |
1862 | if (ret) { | 1814 | if (ret) { |
1863 | unlock_memory_hotplug(); | 1815 | unlock_memory_hotplug(); |
1864 | return ret; | 1816 | BUG(); |
1865 | } | 1817 | } |
1866 | 1818 | ||
1867 | /* remove memmap entry */ | 1819 | /* remove memmap entry */ |
@@ -1872,17 +1824,6 @@ repeat: | |||
1872 | try_offline_node(nid); | 1824 | try_offline_node(nid); |
1873 | 1825 | ||
1874 | unlock_memory_hotplug(); | 1826 | unlock_memory_hotplug(); |
1875 | |||
1876 | return 0; | ||
1877 | } | 1827 | } |
1878 | #else | ||
1879 | int offline_pages(unsigned long start_pfn, unsigned long nr_pages) | ||
1880 | { | ||
1881 | return -EINVAL; | ||
1882 | } | ||
1883 | int remove_memory(int nid, u64 start, u64 size) | ||
1884 | { | ||
1885 | return -EINVAL; | ||
1886 | } | ||
1887 | #endif /* CONFIG_MEMORY_HOTREMOVE */ | ||
1888 | EXPORT_SYMBOL_GPL(remove_memory); | 1828 | EXPORT_SYMBOL_GPL(remove_memory); |
1829 | #endif /* CONFIG_MEMORY_HOTREMOVE */ | ||