mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 09:12:24 +03:00
gpu: nvgpu: fix the usermode mappings deadlock during railgate and munmap
Following locking sequence leads to deadlock: 1. gk20a_pm_prepare_poweroff (alter_usermode_mappings): ctrl_privs_lock -> mmap_lock 2. __do_munmap (usermode_vma_close): mmap_lock -> ctrl_privs_lock This lock contention can be resolved by retrying the usermode mapping alteration after a while releasing the ctrl_priv_lock for munmap to proceed. Below is the kernel panic log with deadlock. [] INFO: task kworker/1:1:116 blocked for more than 120 seconds. [] Tainted: G W 5.10.17-tegra #1 [] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [] task:kworker/1:1 state:D stack: 0 pid: 116 ppid: 2 flags:0x00000028 [] Workqueue: pm pm_runtime_work [] Call trace: [] __switch_to+0x104/0x160 [] __schedule+0x3d4/0x900 [] schedule+0x74/0x100 [] rwsem_down_write_slowpath+0x250/0x4b0 [] down_write+0x6c/0x80 [] alter_usermode_mappings+0xb4/0x160 [nvgpu] [] nvgpu_hide_usermode_for_poweroff+0x24/0x30 [nvgpu] [] gk20a_pm_prepare_poweroff+0xe8/0x140 [nvgpu] [] gk20a_pm_runtime_suspend+0x78/0xf0 [nvgpu] [] pm_generic_runtime_suspend+0x3c/0x60 [] genpd_runtime_suspend+0xb0/0x2c0 [] __rpm_callback+0x90/0x150 [] rpm_callback+0x34/0xa0 [] rpm_suspend+0xe0/0x5e0 [] pm_runtime_work+0xbc/0xc0 [] process_one_work+0x1c0/0x4a0 [] worker_thread+0x11c/0x430 [] kthread+0x148/0x170 [] ret_from_fork+0x10/0x18 [] INFO: task nvrm_gpu_tests:1273 blocked for more than 121 seconds. [] Tainted: G W 5.10.17-tegra #1 [] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [] task:nvrm_gpu_tests state:D stack: 0 pid: 1273 ppid: 1245 flags:0x00000000 [] Call trace: [] __switch_to+0x104/0x160 [] __schedule+0x3d4/0x900 [] schedule+0x74/0x100 [] schedule_preempt_disabled+0x28/0x40 [] __mutex_lock.isra.0+0x184/0x5c0 [] __mutex_lock_slowpath+0x24/0x30 [] mutex_lock+0x5c/0x70 [] usermode_vma_close+0x30/0x50 [nvgpu] [] remove_vma+0x34/0x60 [] __do_munmap+0x1f4/0x4a0 [] __vm_munmap+0x74/0xd0 [] __arm64_sys_munmap+0x3c/0x50 [] el0_svc_common.constprop.0+0x7c/0x1a0 [] do_el0_svc+0x34/0xa0 [] el0_svc+0x1c/0x30 [] el0_sync_handler+0xa8/0xb0 [] el0_sync+0x160/0x180 [] ---[ end Kernel panic - not syncing: hung_task: blocked tasks ]--- Bug 200703921 Change-Id: Ie7f017c92f20061d3bf891079f7fc7fe390f7cf7 Signed-off-by: Sagar Kamble <skamble@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2533853 Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com> Reviewed-by: Konsta Holtta <kholtta@nvidia.com> Reviewed-by: Alex Waterman <alexw@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
419a65965b
commit
1dd3e0761c
@@ -2300,31 +2300,42 @@ int gk20a_ctrl_dev_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void alter_usermode_mapping(struct gk20a *g,
|
||||
static int alter_usermode_mapping(struct gk20a *g,
|
||||
struct gk20a_ctrl_priv *priv,
|
||||
bool poweroff)
|
||||
{
|
||||
struct vm_area_struct *vma = priv->usermode_vma.vma;
|
||||
bool vma_mapped = priv->usermode_vma.vma_mapped;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (!vma) {
|
||||
/* Nothing to do - no mmap called */
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
down_write(&vma->vm_mm->mmap_lock);
|
||||
#else
|
||||
down_write(&vma->vm_mm->mmap_sem);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is a no-op for the below cases
|
||||
* a) poweroff and !vma_mapped - > do nothing as no map exists
|
||||
* b) !poweroff and vmap_mapped -> do nothing as already mapped
|
||||
*/
|
||||
if (poweroff && vma_mapped) {
|
||||
if (poweroff != vma_mapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use trylock due to lock inversion: we need to acquire
|
||||
* mmap_lock while holding ctrl_privs_lock. usermode_vma_close
|
||||
* does it in reverse order. Trylock is a way to avoid deadlock.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
if (!mmap_write_trylock(vma->vm_mm)) {
|
||||
#else
|
||||
if (!down_write_trylock(&vma->vm_mm->mmap_sem)) {
|
||||
#endif
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (poweroff) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||
zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start);
|
||||
err = 0;
|
||||
@@ -2338,7 +2349,7 @@ static void alter_usermode_mapping(struct gk20a *g,
|
||||
} else {
|
||||
nvgpu_err(g, "can't remove usermode mapping");
|
||||
}
|
||||
} else if (!poweroff && !vma_mapped) {
|
||||
} else {
|
||||
vma->vm_flags = priv->usermode_vma.flags;
|
||||
err = io_remap_pfn_range(vma, vma->vm_start,
|
||||
g->usermode_regs_bus_addr >> PAGE_SHIFT,
|
||||
@@ -2352,23 +2363,38 @@ static void alter_usermode_mapping(struct gk20a *g,
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
up_write(&vma->vm_mm->mmap_lock);
|
||||
mmap_write_unlock(vma->vm_mm);
|
||||
#else
|
||||
up_write(&vma->vm_mm->mmap_sem);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void alter_usermode_mappings(struct gk20a *g, bool poweroff)
|
||||
{
|
||||
struct gk20a_ctrl_priv *priv;
|
||||
struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
|
||||
int err = 0;
|
||||
|
||||
do {
|
||||
nvgpu_mutex_acquire(&l->ctrl_privs_lock);
|
||||
nvgpu_list_for_each_entry(priv, &l->ctrl_privs,
|
||||
gk20a_ctrl_priv, list) {
|
||||
alter_usermode_mapping(g, priv, poweroff);
|
||||
err = alter_usermode_mapping(g, priv, poweroff);
|
||||
if (err != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
nvgpu_mutex_release(&l->ctrl_privs_lock);
|
||||
|
||||
if (err == -EBUSY) {
|
||||
nvgpu_log_info(g, "ctrl_privs_lock lock contended. retry altering usermode mappings");
|
||||
nvgpu_udelay(10);
|
||||
} else if (err != 0) {
|
||||
nvgpu_err(g, "can't alter usermode mapping. err = %d", err);
|
||||
}
|
||||
} while (err == -EBUSY);
|
||||
}
|
||||
|
||||
void nvgpu_hide_usermode_for_poweroff(struct gk20a *g)
|
||||
|
||||
Reference in New Issue
Block a user