From: xiongxin xiongxin@kylinos.cn
Added a check on the return value of preallocate_image_highmem(). If memory preallocate is insufficient, S4 cannot be done;
I am playing 4K video on a machine with AMD or other graphics card and only 8GiB memory, and the kernel is not configured with CONFIG_HIGHMEM. When doing the S4 test, the analysis found that when the pages get from minimum_image_size() is large enough, The preallocate_image_memory() and preallocate_image_highmem() calls failed to obtain enough memory. Add the judgment that memory preallocate is insufficient;
"pages -= free_unnecessary_pages()" below will let pages to drop a lot, so I wonder if it makes sense to add a judgment here.
Cc: stable@vger.kernel.org Signed-off-by: xiongxin xiongxin@kylinos.cn Signed-off-by: huanglei huanglei@kylinos.cn --- kernel/power/snapshot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index c20ca5fb9adc..670abf89cf31 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1738,6 +1738,7 @@ int hibernate_preallocate_memory(void) struct zone *zone; unsigned long saveable, size, max_size, count, highmem, pages = 0; unsigned long alloc, save_highmem, pages_highmem, avail_normal; + unsigned long size_highmem; ktime_t start, stop; int error;
@@ -1863,7 +1864,13 @@ int hibernate_preallocate_memory(void) pages_highmem += size; alloc -= size; size = preallocate_image_memory(alloc, avail_normal); - pages_highmem += preallocate_image_highmem(alloc - size); + size_highmem += preallocate_image_highmem(alloc - size); + if (size_highmem < (alloc - size)) { + pr_err("Image allocation is %lu pages short, exit\n", + alloc - size - pages_highmem); + goto err_out; + } + pages_highmem += size_highmem; pages += pages_highmem + size; }
Hi TGSP,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on next-20221031]
url: https://github.com/intel-lab-lkp/linux/commits/TGSP/Fixes-to-the-hibernate_p... patch link: https://lore.kernel.org/r/20221101022840.1351163-3-tgsp002%40gmail.com patch subject: [PATCH -next 2/2] PM: hibernate: add check of preallocate mem for image size pages config: i386-randconfig-a011 compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/1c277961f43154d671520086d9626e... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review TGSP/Fixes-to-the-hibernate_preallocate_memory/20221101-105416 git checkout 1c277961f43154d671520086d9626e081e072b90 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash kernel/power/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
kernel/power/snapshot.c:1867:3: warning: variable 'size_highmem' is uninitialized when used here [-Wuninitialized]
size_highmem += preallocate_image_highmem(alloc - size); ^~~~~~~~~~~~ kernel/power/snapshot.c:1741:28: note: initialize the variable 'size_highmem' to silence this warning unsigned long size_highmem; ^ = 0 1 warning generated.
vim +/size_highmem +1867 kernel/power/snapshot.c
1713 1714 /** 1715 * hibernate_preallocate_memory - Preallocate memory for hibernation image. 1716 * 1717 * To create a hibernation image it is necessary to make a copy of every page 1718 * frame in use. We also need a number of page frames to be free during 1719 * hibernation for allocations made while saving the image and for device 1720 * drivers, in case they need to allocate memory from their hibernation 1721 * callbacks (these two numbers are given by PAGES_FOR_IO (which is a rough 1722 * estimate) and reserved_size divided by PAGE_SIZE (which is tunable through 1723 * /sys/power/reserved_size, respectively). To make this happen, we compute the 1724 * total number of available page frames and allocate at least 1725 * 1726 * ([page frames total] - PAGES_FOR_IO - [metadata pages]) / 2 1727 * - 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE) 1728 * 1729 * of them, which corresponds to the maximum size of a hibernation image. 1730 * 1731 * If image_size is set below the number following from the above formula, 1732 * the preallocation of memory is continued until the total number of saveable 1733 * pages in the system is below the requested image size or the minimum 1734 * acceptable image size returned by minimum_image_size(), whichever is greater. 1735 */ 1736 int hibernate_preallocate_memory(void) 1737 { 1738 struct zone *zone; 1739 unsigned long saveable, size, max_size, count, highmem, pages = 0; 1740 unsigned long alloc, save_highmem, pages_highmem, avail_normal; 1741 unsigned long size_highmem; 1742 ktime_t start, stop; 1743 int error; 1744 1745 pr_info("Preallocating image memory\n"); 1746 start = ktime_get(); 1747 1748 error = memory_bm_create(&orig_bm, GFP_IMAGE, PG_ANY); 1749 if (error) { 1750 pr_err("Cannot allocate original bitmap\n"); 1751 goto err_out; 1752 } 1753 1754 error = memory_bm_create(©_bm, GFP_IMAGE, PG_ANY); 1755 if (error) { 1756 pr_err("Cannot allocate copy bitmap\n"); 1757 goto err_out; 1758 } 1759 1760 alloc_normal = 0; 1761 alloc_highmem = 0; 1762 1763 /* Count the number of saveable data pages. */ 1764 save_highmem = count_highmem_pages(); 1765 saveable = count_data_pages(); 1766 1767 /* 1768 * Compute the total number of page frames we can use (count) and the 1769 * number of pages needed for image metadata (size). 1770 */ 1771 count = saveable; 1772 saveable += save_highmem; 1773 highmem = save_highmem; 1774 size = 0; 1775 for_each_populated_zone(zone) { 1776 size += snapshot_additional_pages(zone); 1777 if (is_highmem(zone)) 1778 highmem += zone_page_state(zone, NR_FREE_PAGES); 1779 else 1780 count += zone_page_state(zone, NR_FREE_PAGES); 1781 } 1782 avail_normal = count; 1783 count += highmem; 1784 count -= totalreserve_pages; 1785 1786 /* Compute the maximum number of saveable pages to leave in memory. */ 1787 max_size = (count - (size + PAGES_FOR_IO)) / 2 1788 - 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE); 1789 /* Compute the desired number of image pages specified by image_size. */ 1790 size = DIV_ROUND_UP(image_size, PAGE_SIZE); 1791 if (size > max_size) 1792 size = max_size; 1793 /* 1794 * If the desired number of image pages is at least as large as the 1795 * current number of saveable pages in memory, allocate page frames for 1796 * the image and we're done. 1797 */ 1798 if (size >= saveable) { 1799 pages = preallocate_image_highmem(save_highmem); 1800 pages += preallocate_image_memory(saveable - pages, avail_normal); 1801 goto out; 1802 } 1803 1804 /* Estimate the minimum size of the image. */ 1805 pages = minimum_image_size(saveable); 1806 /* 1807 * To avoid excessive pressure on the normal zone, leave room in it to 1808 * accommodate an image of the minimum size (unless it's already too 1809 * small, in which case don't preallocate pages from it at all). 1810 */ 1811 if (avail_normal > pages) 1812 avail_normal -= pages; 1813 else 1814 avail_normal = 0; 1815 if (size < pages) 1816 size = min_t(unsigned long, pages, max_size); 1817 1818 /* 1819 * Let the memory management subsystem know that we're going to need a 1820 * large number of page frames to allocate and make it free some memory. 1821 * NOTE: If this is not done, performance will be hurt badly in some 1822 * test cases. 1823 */ 1824 shrink_all_memory(saveable - size); 1825 1826 /* 1827 * The number of saveable pages in memory was too high, so apply some 1828 * pressure to decrease it. First, make room for the largest possible 1829 * image and fail if that doesn't work. Next, try to decrease the size 1830 * of the image as much as indicated by 'size' using allocations from 1831 * highmem and non-highmem zones separately. 1832 */ 1833 pages_highmem = preallocate_image_highmem(highmem / 2); 1834 alloc = count - max_size; 1835 if (alloc > pages_highmem) 1836 alloc -= pages_highmem; 1837 else 1838 alloc = 0; 1839 pages = preallocate_image_memory(alloc, avail_normal); 1840 if (pages < alloc) { 1841 /* We have exhausted non-highmem pages, try highmem. */ 1842 alloc -= pages; 1843 pages += pages_highmem; 1844 pages_highmem = preallocate_image_highmem(alloc); 1845 if (pages_highmem < alloc) { 1846 pr_err("Image allocation is %lu pages short\n", 1847 alloc - pages_highmem); 1848 goto err_out; 1849 } 1850 pages += pages_highmem; 1851 /* 1852 * size is the desired number of saveable pages to leave in 1853 * memory, so try to preallocate (all memory - size) pages. 1854 */ 1855 alloc = (count - pages) - size; 1856 pages += preallocate_image_highmem(alloc); 1857 } else { 1858 /* 1859 * There are approximately max_size saveable pages at this point 1860 * and we want to reduce this number down to size. 1861 */ 1862 alloc = max_size - size; 1863 size = preallocate_highmem_fraction(alloc, highmem, count); 1864 pages_highmem += size; 1865 alloc -= size; 1866 size = preallocate_image_memory(alloc, avail_normal);
1867 size_highmem += preallocate_image_highmem(alloc - size);
1868 if (size_highmem < (alloc - size)) { 1869 pr_err("Image allocation is %lu pages short, exit\n", 1870 alloc - size - pages_highmem); 1871 goto err_out; 1872 } 1873 pages_highmem += size_highmem; 1874 pages += pages_highmem + size; 1875 } 1876 1877 /* 1878 * We only need as many page frames for the image as there are saveable 1879 * pages in memory, but we have allocated more. Release the excessive 1880 * ones now. 1881 */ 1882 pages -= free_unnecessary_pages(); 1883 1884 out: 1885 stop = ktime_get(); 1886 pr_info("Allocated %lu pages for snapshot\n", pages); 1887 swsusp_show_speed(start, stop, pages, "Allocated"); 1888 1889 return 0; 1890 1891 err_out: 1892 swsusp_free(); 1893 return -ENOMEM; 1894 } 1895
On Tue, Nov 1, 2022 at 3:28 AM TGSP tgsp002@gmail.com wrote:
From: xiongxin xiongxin@kylinos.cn
Added a check on the return value of preallocate_image_highmem(). If memory preallocate is insufficient, S4 cannot be done;
I am playing 4K video on a machine with AMD or other graphics card and only 8GiB memory, and the kernel is not configured with CONFIG_HIGHMEM. When doing the S4 test, the analysis found that when the pages get from minimum_image_size() is large enough, The preallocate_image_memory() and preallocate_image_highmem() calls failed to obtain enough memory. Add the judgment that memory preallocate is insufficient;
"pages -= free_unnecessary_pages()" below will let pages to drop a lot, so I wonder if it makes sense to add a judgment here.
Cc: stable@vger.kernel.org Signed-off-by: xiongxin xiongxin@kylinos.cn Signed-off-by: huanglei huanglei@kylinos.cn
kernel/power/snapshot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index c20ca5fb9adc..670abf89cf31 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1738,6 +1738,7 @@ int hibernate_preallocate_memory(void) struct zone *zone; unsigned long saveable, size, max_size, count, highmem, pages = 0; unsigned long alloc, save_highmem, pages_highmem, avail_normal;
unsigned long size_highmem;
Please define this in the block where it will be used.
ktime_t start, stop; int error;
@@ -1863,7 +1864,13 @@ int hibernate_preallocate_memory(void) pages_highmem += size;
The line above can be dropped.
alloc -= size; size = preallocate_image_memory(alloc, avail_normal);
pages_highmem += preallocate_image_highmem(alloc - size);
size_highmem += preallocate_image_highmem(alloc - size);
Did you mean "="?
Assuming you did, this could be
size_highmem = size + preallocate_image_highmem(alloc - size); if (size_highmem < alloc) {
if (size_highmem < (alloc - size)) {
The inner parens were not necessary.
pr_err("Image allocation is %lu pages short, exit\n",
alloc - size - pages_highmem);
goto err_out;
}
pages_highmem += size_highmem; pages += pages_highmem + size; }
--
But overall it would be better to avoid bailing out.
On Thu, Nov 3, 2022 at 5:47 PM Rafael J. Wysocki rafael@kernel.org wrote:
On Tue, Nov 1, 2022 at 3:28 AM TGSP tgsp002@gmail.com wrote:
From: xiongxin xiongxin@kylinos.cn
Added a check on the return value of preallocate_image_highmem(). If memory preallocate is insufficient, S4 cannot be done;
I am playing 4K video on a machine with AMD or other graphics card and only 8GiB memory, and the kernel is not configured with CONFIG_HIGHMEM. When doing the S4 test, the analysis found that when the pages get from minimum_image_size() is large enough, The preallocate_image_memory() and preallocate_image_highmem() calls failed to obtain enough memory. Add the judgment that memory preallocate is insufficient;
"pages -= free_unnecessary_pages()" below will let pages to drop a lot, so I wonder if it makes sense to add a judgment here.
Cc: stable@vger.kernel.org Signed-off-by: xiongxin xiongxin@kylinos.cn Signed-off-by: huanglei huanglei@kylinos.cn
kernel/power/snapshot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index c20ca5fb9adc..670abf89cf31 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1738,6 +1738,7 @@ int hibernate_preallocate_memory(void) struct zone *zone; unsigned long saveable, size, max_size, count, highmem, pages = 0; unsigned long alloc, save_highmem, pages_highmem, avail_normal;
unsigned long size_highmem;
Please define this in the block where it will be used.
ktime_t start, stop; int error;
@@ -1863,7 +1864,13 @@ int hibernate_preallocate_memory(void) pages_highmem += size;
The line above can be dropped.
Not really, it needs to be replaced with
size_highmem = size ;
alloc -= size; size = preallocate_image_memory(alloc, avail_normal);
pages_highmem += preallocate_image_highmem(alloc - size);
size_highmem += preallocate_image_highmem(alloc - size);
Did you mean "="?
Assuming you did, this could be
size_highmem = size + preallocate_image_highmem(alloc - size);
And here
size_highmem += preallocate_image_highmem(alloc - size);
which is what you had in the original patch.
if (size_highmem < alloc) {
if (size_highmem < (alloc - size)) {
The inner parens were not necessary.
pr_err("Image allocation is %lu pages short, exit\n",
alloc - size - pages_highmem);
goto err_out;
}
pages_highmem += size_highmem; pages += pages_highmem + size; }
--
But overall it would be better to avoid bailing out.
On Tue, Nov 1, 2022 at 3:28 AM TGSP tgsp002@gmail.com wrote:
From: xiongxin xiongxin@kylinos.cn
Added a check on the return value of preallocate_image_highmem(). If memory preallocate is insufficient, S4 cannot be done;
I am playing 4K video on a machine with AMD or other graphics card and only 8GiB memory, and the kernel is not configured with CONFIG_HIGHMEM. When doing the S4 test, the analysis found that when the pages get from minimum_image_size() is large enough, The preallocate_image_memory() and preallocate_image_highmem() calls failed to obtain enough memory. Add the judgment that memory preallocate is insufficient;
So I'm not sure what the problem is. Can you please explain it in more detail?
The if (pages < alloc) appears to be false in your case, so there should be enough free pages to create an image.
Maybe reserved_size is too low?
linux-stable-mirror@lists.linaro.org