How to fix suspend with high RAM usage for AMD GPU on Linux

When utilizing so much memory that swap is used, suspend can sometimes fail with AMD GPUs, because for some reason the amdgpu driver cannot allocate enough memory for storing the VRAM. Older amdgpu driver versions used to crash in this scenario, but nowadays they just prevent entering suspend. It will show up in the logs like this:

Reached target sleep.target - Sleep.
Starting systemd-suspend.service - System Suspend...
Successfully froze unit 'user.slice'.
Performing sleep operation 'suspend'...
kernel: PM: suspend entry (deep)
kernel: Filesystems sync: 0.043 seconds
kernel: Freezing user space processes
kernel: Freezing user space processes completed (elapsed 0.002 seconds)
kernel: OOM killer disabled.
kernel: Freezing remaining freezable tasks
kernel: Freezing remaining freezable tasks completed (elapsed 0.365 seconds)
kernel: printk: Suspending console(s) (use no_console_suspend to debug)
kernel: systemd-sleep: page allocation failure: order:0, mode:0xc00(GFP_NOIO), nodemask=(null),cpuset=/,mems_allowed=0
kernel: Call Trace:
kernel:  <TASK>
kernel:  dump_stack_lvl+0x60/0x80
kernel:  warn_alloc+0x135/0x1b0
kernel:  __alloc_pages+0x102d/0x1070
kernel:  alloc_pages_mpol+0x1b2/0x360
kernel:  shmem_alloc_and_add_folio+0x379/0x420
kernel:  shmem_get_folio_gfp+0x213/0x530
kernel:  shmem_read_mapping_page_gfp+0x38/0xc0
kernel:  ttm_tt_swapout+0xa6/0x190 [ttm]
kernel:  ttm_bo_swapout+0x355/0x430 [ttm]
kernel:  ttm_device_swapout+0xc2/0x110 [ttm]
kernel:  ttm_global_swapout+0x49/0xa0 [ttm]
kernel:  ttm_tt_populate+0x7e/0x130 [ttm]
kernel:  ttm_bo_handle_move_mem+0x1a0/0x1b0 [ttm]
kernel:  ttm_mem_evict_first+0x3ef/0x670 [ttm]
kernel:  ? preempt_schedule+0x3d/0x60
kernel:  ? preempt_schedule_thunk+0x16/0x30
kernel:  ttm_resource_manager_evict_all+0xa3/0x1d0 [ttm]
kernel:  ? __pfx_pci_pm_prepare+0x10/0x10
kernel:  amdgpu_device_prepare+0x54/0xf0 [amdgpu]
kernel:  pci_pm_prepare+0x30/0x70
kernel:  dpm_prepare+0x239/0x410
kernel:  dpm_suspend_start+0x1a/0x170
kernel:  suspend_devices_and_enter+0x164/0x9e0
kernel:  pm_suspend+0x220/0x4d0
kernel:  state_store+0xa4/0x140
kernel:  kernfs_fop_write_iter+0x11e/0x200
kernel:  vfs_write+0x27a/0x470
kernel:  __x64_sys_write+0x68/0xf0
kernel:  do_syscall_64+0x4d/0x140
kernel:  entry_SYSCALL_64_after_hwframe+0x76/0x7e

This can be solved by making sure that enough RAM is available right before entering suspend. Either by closing some applications or by using this cool tool memreserver from Lorenz Brun.

Download the source code from https://git.dolansoft.org/lorenz/memreserver

On Debian you probably need to install the following dependencies:

sudo apt install meson libdrm-dev

To build memreserver run:

meson setup builddir
meson compile -C builddir

Then install the systemd service to execute memreserver right before suspending:

meson install -C builddir
sudo cp memreserver.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable memreserver.service 

That’s it, the next time you want to suspend, this service will forcefully reserve enough memory from RAM such that the VRAM fits in it, which will make the kernel to swap out some other stuff.

Leave a Reply

Your email address will not be published. Required fields are marked *