summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorddutta <ddutta@nvidia.com>2020-03-12 03:22:07 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2020-03-19 05:54:43 -0400
commit06942bd268fca1bbe37f11710cfc655708291ecc (patch)
tree9a0921185694fcc00131a7187eb79054e576c73a
parent505251d6969d8305629d122d0afbef5e520ddd5b (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>
-rw-r--r--drivers/gpu/nvgpu/include/nvgpu/gk20a.h1
-rw-r--r--drivers/gpu/nvgpu/os/linux/ioctl_ctrl.c32
-rw-r--r--drivers/gpu/nvgpu/os/linux/module.c16
-rw-r--r--drivers/gpu/nvgpu/os/posix/nvgpu.c6
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
1788int gk20a_init_gpu_characteristics(struct gk20a *g); 1788int gk20a_init_gpu_characteristics(struct gk20a *g);
1789 1789
1790bool gk20a_check_poweron(struct gk20a *g);
1790int gk20a_prepare_poweroff(struct gk20a *g); 1791int gk20a_prepare_poweroff(struct gk20a *g);
1791int gk20a_finalize_poweron(struct gk20a *g); 1792int 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(
596static int nvgpu_gpu_ioctl_l2_fb_ops(struct gk20a *g, 597static 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
614static int nvgpu_gpu_ioctl_set_mmu_debug_mode( 642static 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
108void gk20a_busy_noresume(struct gk20a *g) 108void 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
262bool 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
265int gk20a_pm_finalize_poweron(struct device *dev) 273int 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
96bool gk20a_check_poweron(struct gk20a *g)
97{
98 return false;
99}
100
95int gk20a_busy(struct gk20a *g) 101int gk20a_busy(struct gk20a *g)
96{ 102{
97 nvgpu_atomic_inc(&g->usage_count); 103 nvgpu_atomic_inc(&g->usage_count);