video: tegra: gk20a: Fix protected elpg race.
Prashant Malani [Tue, 4 Feb 2014 20:39:45 +0000 (12:39 -0800)]
Protected elpg calls can be pre-empted in the middle by the deferred
pg_init. This can lead to refcounting errors, which will result in elpg
always remaining on.

Therefore we protect the relevant sections of elpg setup in the deferred
work with a mutex.

Change-Id: I55746412885a09f7dbccd577f0c30d0d1102fe54
Signed-off-by: Prashant Malani <pmalani@nvidia.com>
Reviewed-on: http://git-master/r/363424
(cherry picked from commit 782db693dfc5c70a1d327e52bec167309344ac19)
Reviewed-on: http://git-master/r/368259
Tested-by: Preetham Chandru <pchandru@nvidia.com>
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

drivers/video/tegra/host/gk20a/gr_gk20a.h
drivers/video/tegra/host/gk20a/pmu_gk20a.c
drivers/video/tegra/host/gk20a/pmu_gk20a.h

index ce726e9..49cd7b4 100644 (file)
@@ -350,9 +350,11 @@ bool gk20a_gr_sm_debugger_attached(struct gk20a *g);
 #define gr_gk20a_elpg_protected_call(g, func) \
        ({ \
                int err; \
+               mutex_lock(&g->pmu.pg_init_mutex); \
                gk20a_pmu_disable_elpg(g); \
                err = func; \
                gk20a_pmu_enable_elpg(g); \
+               mutex_unlock(&g->pmu.pg_init_mutex); \
                err; \
        })
 
index dbe2d41..9ad1abd 100644 (file)
@@ -1123,6 +1123,7 @@ skip_init:
        mutex_init(&pmu->elpg_mutex);
        mutex_init(&pmu->isr_mutex);
        mutex_init(&pmu->pmu_copy_lock);
+       mutex_init(&pmu->pg_init_mutex);
 
        pmu->perfmon_counter.index = 3; /* GR & CE2 */
        pmu->perfmon_counter.group_id = PMU_DOMAIN_GROUP_PSTATE;
@@ -1389,6 +1390,7 @@ int gk20a_init_pmu_setup_hw2(struct gk20a *g)
         */
        gk20a_writel(g, 0x10a164, 0x109ff);
 
+       mutex_lock(&pmu->pg_init_mutex);
        pmu->initialized = true;
        pmu->zbc_ready = true;
 
@@ -1404,6 +1406,7 @@ int gk20a_init_pmu_setup_hw2(struct gk20a *g)
 
        if (g->elpg_enabled)
                gk20a_pmu_enable_elpg(g);
+       mutex_unlock(&pmu->pg_init_mutex);
 
        return 0;
 
@@ -2759,7 +2762,10 @@ int gk20a_pmu_destroy(struct gk20a *g)
        gk20a_pmu_get_elpg_residency_gating(g, &elpg_ingating_time,
                &elpg_ungating_time, &gating_cnt);
 
+       mutex_lock(&pmu->pg_init_mutex);
        gk20a_pmu_disable_elpg_defer_enable(g, false);
+       pmu->initialized = false;
+       mutex_unlock(&pmu->pg_init_mutex);
 
        /* update the s/w ELPG residency counters */
        g->pg_ingating_time_us += (u64)elpg_ingating_time;
index 0750c15..29bf381 100644 (file)
@@ -696,6 +696,7 @@ struct pmu_gk20a {
        struct work_struct pg_init;
        bool elpg_enable_allow; /* true after init, false after disable, true after delay */
        struct mutex elpg_mutex; /* protect elpg enable/disable */
+       struct mutex pg_init_mutex; /* protect pmu pg_initialization routine */
        int elpg_refcnt; /* disable -1, enable +1, <=0 elpg disabled, > 0 elpg enabled */
 
        struct pmu_perfmon_counter perfmon_counter;