diff options
author | ddutta <ddutta@nvidia.com> | 2020-03-12 03:22:07 -0400 |
---|---|---|
committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2020-03-19 05:54:43 -0400 |
commit | 06942bd268fca1bbe37f11710cfc655708291ecc (patch) | |
tree | 9a0921185694fcc00131a7187eb79054e576c73a /drivers/gpu/nvgpu | |
parent | 505251d6969d8305629d122d0afbef5e520ddd5b (diff) |
gpu: nvgpu: handle ioctl l2_fb_ops better
Background: There is a race that occurs when l2_fb_ops ioctl is
invoked. The race occurs as part of the flush() call while a
gk20_idle() is in progress.
This patch handles the race by making changes in the l2_fb_ops
ioctl itself. For cases where pm_runtime is disabled or railgate is
disabled, we allow this ioctl call to always go ahead as power is
assumed to be always on.
For the other case, we first check the status of g->power_on. In the
driver, g->power_on is set to true, once unrailgate is completed and is
set to false just before calling railgate.
For linux, the driver invokes gk20a_idle() but there is a delay after
which the call to the rpm_suspend()'s callback gets triggered. This
leads to a scenario where we cannot efficiently rely on the
runtime_pm's APIs to allow us to block an imminent suspend or exit if
the suspend is currently in progress. Previous attempts at solving this
has lead to ineffective solutions and make it much complicated to
maintain the code.
With regards to the above, this patch attempts to simplify the way this
can be solved. The patch calls gk20a_busy() when g->power_on = true.
This prevents the race with gk20a_idle(). Based on the rpm_resume and
rpm_suspend's upstream code, resume is prioritized over a suspend
unless a suspend is already in progress i.e. the delay period has been
served and the suspend invokes the callback. There is a very small
window for this to happen and the ioctl can then power_up the device as
evident from the gk20a_busy's calls.
A new function gk20a_check_poweron() is added. This function protects
the access to g->power_on via a mutex. By preventing a read from
happening simulatenously as a write on g->power_on, the likelihood of
an runtime_suspend triggering before a runtime_resume is further
reduced.
Bug 200507468
Change-Id: I5c02dfa8ea855732e59b759d167152cf45a1131f
Signed-off-by:Debarshi Dutta <ddutta@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2299545
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu')
-rw-r--r-- | drivers/gpu/nvgpu/include/nvgpu/gk20a.h | 1 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/os/linux/ioctl_ctrl.c | 32 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/os/linux/module.c | 16 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/os/posix/nvgpu.c | 6 |
4 files changed, 49 insertions, 6 deletions
diff --git a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h index 8b6de266..61fe4fb5 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h +++ b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h | |||
@@ -1787,6 +1787,7 @@ int gk20a_wait_for_idle(struct gk20a *g); | |||
1787 | 1787 | ||
1788 | int gk20a_init_gpu_characteristics(struct gk20a *g); | 1788 | int gk20a_init_gpu_characteristics(struct gk20a *g); |
1789 | 1789 | ||
1790 | bool gk20a_check_poweron(struct gk20a *g); | ||
1790 | int gk20a_prepare_poweroff(struct gk20a *g); | 1791 | int gk20a_prepare_poweroff(struct gk20a *g); |
1791 | int gk20a_finalize_poweron(struct gk20a *g); | 1792 | int gk20a_finalize_poweron(struct gk20a *g); |
1792 | 1793 | ||
diff --git a/drivers/gpu/nvgpu/os/linux/ioctl_ctrl.c b/drivers/gpu/nvgpu/os/linux/ioctl_ctrl.c index 6fa15421..271c5d92 100644 --- a/drivers/gpu/nvgpu/os/linux/ioctl_ctrl.c +++ b/drivers/gpu/nvgpu/os/linux/ioctl_ctrl.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/file.h> | 19 | #include <linux/file.h> |
20 | #include <linux/anon_inodes.h> | 20 | #include <linux/anon_inodes.h> |
21 | #include <linux/fs.h> | 21 | #include <linux/fs.h> |
22 | #include <linux/pm_runtime.h> | ||
22 | #include <uapi/linux/nvgpu.h> | 23 | #include <uapi/linux/nvgpu.h> |
23 | 24 | ||
24 | #include <nvgpu/bitops.h> | 25 | #include <nvgpu/bitops.h> |
@@ -596,19 +597,46 @@ static int gk20a_ctrl_get_fbp_l2_masks( | |||
596 | static int nvgpu_gpu_ioctl_l2_fb_ops(struct gk20a *g, | 597 | static int nvgpu_gpu_ioctl_l2_fb_ops(struct gk20a *g, |
597 | struct nvgpu_gpu_l2_fb_args *args) | 598 | struct nvgpu_gpu_l2_fb_args *args) |
598 | { | 599 | { |
599 | int err = 0; | 600 | int ret; |
601 | bool always_poweron; | ||
600 | 602 | ||
601 | if ((!args->l2_flush && !args->fb_flush) || | 603 | if ((!args->l2_flush && !args->fb_flush) || |
602 | (!args->l2_flush && args->l2_invalidate)) | 604 | (!args->l2_flush && args->l2_invalidate)) |
603 | return -EINVAL; | 605 | return -EINVAL; |
604 | 606 | ||
607 | /* Handle this case for joint rails or DGPU */ | ||
608 | always_poweron = (!nvgpu_is_enabled(g, NVGPU_CAN_RAILGATE) || | ||
609 | !pm_runtime_enabled(dev_from_gk20a(g))); | ||
610 | |||
611 | /* In case of not always power_on, exit if g->power_on is false */ | ||
612 | if (!always_poweron && !gk20a_check_poweron(g)) { | ||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | /* There is a small window between a call to gk20a_idle() has occured | ||
617 | * and railgate being actually triggered(setting g->power_on = false), | ||
618 | * when l2_flush can race with railgate. Its better to take a busy_lock | ||
619 | * to prevent the gk20a_idle() from proceeding. There is a very small | ||
620 | * chance that gk20a_idle() might begin before gk20a_busy(). Having | ||
621 | * a locked access to g->power_on further reduces the probability of | ||
622 | * gk20a_idle() being triggered before gk20a_busy() | ||
623 | */ | ||
624 | ret = gk20a_busy(g); | ||
625 | |||
626 | if (ret != 0) { | ||
627 | nvgpu_err(g, "failed to take power ref"); | ||
628 | return ret; | ||
629 | } | ||
630 | |||
605 | if (args->l2_flush) | 631 | if (args->l2_flush) |
606 | g->ops.mm.l2_flush(g, args->l2_invalidate ? true : false); | 632 | g->ops.mm.l2_flush(g, args->l2_invalidate ? true : false); |
607 | 633 | ||
608 | if (args->fb_flush) | 634 | if (args->fb_flush) |
609 | g->ops.mm.fb_flush(g); | 635 | g->ops.mm.fb_flush(g); |
610 | 636 | ||
611 | return err; | 637 | gk20a_idle(g); |
638 | |||
639 | return 0; | ||
612 | } | 640 | } |
613 | 641 | ||
614 | static int nvgpu_gpu_ioctl_set_mmu_debug_mode( | 642 | static int nvgpu_gpu_ioctl_set_mmu_debug_mode( |
diff --git a/drivers/gpu/nvgpu/os/linux/module.c b/drivers/gpu/nvgpu/os/linux/module.c index 18345ada..5627abfd 100644 --- a/drivers/gpu/nvgpu/os/linux/module.c +++ b/drivers/gpu/nvgpu/os/linux/module.c | |||
@@ -107,10 +107,7 @@ struct device_node *nvgpu_get_node(struct gk20a *g) | |||
107 | 107 | ||
108 | void gk20a_busy_noresume(struct gk20a *g) | 108 | void gk20a_busy_noresume(struct gk20a *g) |
109 | { | 109 | { |
110 | int ret = pm_runtime_get_if_in_use(dev_from_gk20a(g)); | 110 | pm_runtime_get_noresume(dev_from_gk20a(g)); |
111 | |||
112 | if (ret <= 0) | ||
113 | pm_runtime_get_noresume(dev_from_gk20a(g)); | ||
114 | } | 111 | } |
115 | 112 | ||
116 | /* | 113 | /* |
@@ -262,6 +259,17 @@ int nvgpu_finalize_poweron_linux(struct nvgpu_os_linux *l) | |||
262 | return 0; | 259 | return 0; |
263 | } | 260 | } |
264 | 261 | ||
262 | bool gk20a_check_poweron(struct gk20a *g) | ||
263 | { | ||
264 | bool ret; | ||
265 | |||
266 | nvgpu_mutex_acquire(&g->power_lock); | ||
267 | ret = g->power_on; | ||
268 | nvgpu_mutex_release(&g->power_lock); | ||
269 | |||
270 | return ret; | ||
271 | } | ||
272 | |||
265 | int gk20a_pm_finalize_poweron(struct device *dev) | 273 | int gk20a_pm_finalize_poweron(struct device *dev) |
266 | { | 274 | { |
267 | struct gk20a *g = get_gk20a(dev); | 275 | struct gk20a *g = get_gk20a(dev); |
diff --git a/drivers/gpu/nvgpu/os/posix/nvgpu.c b/drivers/gpu/nvgpu/os/posix/nvgpu.c index 2f84dc6e..e485ed73 100644 --- a/drivers/gpu/nvgpu/os/posix/nvgpu.c +++ b/drivers/gpu/nvgpu/os/posix/nvgpu.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <stdlib.h> | 24 | #include <stdlib.h> |
25 | #include <pthread.h> | 25 | #include <pthread.h> |
26 | 26 | ||
27 | #include <nvgpu/gk20a.h> | ||
27 | #include <nvgpu/bug.h> | 28 | #include <nvgpu/bug.h> |
28 | #include <nvgpu/types.h> | 29 | #include <nvgpu/types.h> |
29 | #include <nvgpu/atomic.h> | 30 | #include <nvgpu/atomic.h> |
@@ -92,6 +93,11 @@ void gk20a_idle_nosuspend(struct gk20a *g) | |||
92 | { | 93 | { |
93 | } | 94 | } |
94 | 95 | ||
96 | bool gk20a_check_poweron(struct gk20a *g) | ||
97 | { | ||
98 | return false; | ||
99 | } | ||
100 | |||
95 | int gk20a_busy(struct gk20a *g) | 101 | int gk20a_busy(struct gk20a *g) |
96 | { | 102 | { |
97 | nvgpu_atomic_inc(&g->usage_count); | 103 | nvgpu_atomic_inc(&g->usage_count); |