]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
video: tegra: host: Fix race in timeout
authorArto Merilainen <amerilainen@nvidia.com>
Wed, 14 May 2014 07:18:48 +0000 (10:18 +0300)
committerSimone Willett <swillett@nvidia.com>
Thu, 15 May 2014 17:19:30 +0000 (10:19 -0700)
Timeout handler locks a single cdma mutex. However, as part
of debug dump we need to take a lock also all other cdma locks.

If we concurrently execute two timeout handlers for two different
channels, mutex locking ordering is not same and therefore we
risk getting deadlock.

This patch adds global timeout handler mutex to ensure that only
a single timeout handler is active at a time. This ensures that
- despite differing ordering - two handlers are not able to
race.

Change-Id: I64b203478c175afa36f396d4dc4b3838130974cb
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Reviewed-on: http://git-master/r/409596
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
drivers/video/tegra/host/host1x/host1x.c
drivers/video/tegra/host/host1x/host1x.h
drivers/video/tegra/host/host1x/host1x_cdma.c

index 660715cd14d7a899a9843801ef94d55d47ae4165..e02c659c7a73b475f45e3c8a0377dd394b35706e 100644 (file)
@@ -773,6 +773,8 @@ static int nvhost_probe(struct platform_device *dev)
 
 #endif
 
+       mutex_init(&host->timeout_mutex);
+
        nvhost_module_busy(dev);
 
        nvhost_syncpt_reset(&host->syncpt);
index 0b3bfd8b784b554ad38e554f7ecf4e2a2d06fa2c..4d63063d6cc10607a498b27b53f85c4047f429ae 100644 (file)
@@ -55,6 +55,7 @@ struct nvhost_master {
        struct host1x_device_info info;
        struct kobject *caps_kobj;
        struct nvhost_capability_node *caps_nodes;
+       struct mutex timeout_mutex;
 };
 
 extern struct nvhost_master *nvhost;
index c56bbf31e85584c22c376aad56fa00cdd7200811..196af9952f983ededa4d71626ea29d20f29a372d 100644 (file)
@@ -443,9 +443,12 @@ static void cdma_timeout_handler(struct work_struct *work)
        sp = &dev->syncpt;
        ch = cdma_to_channel(cdma);
 
+       mutex_lock(&dev->timeout_mutex);
+
        ret = mutex_trylock(&cdma->lock);
        if (!ret) {
                schedule_delayed_work(&cdma->timeout.wq, msecs_to_jiffies(10));
+               mutex_unlock(&dev->timeout_mutex);
                return;
        }
 
@@ -461,6 +464,7 @@ static void cdma_timeout_handler(struct work_struct *work)
                schedule_delayed_work(&cdma->timeout.wq,
                                      msecs_to_jiffies(cdma->timeout.timeout));
                mutex_unlock(&cdma->lock);
+               mutex_unlock(&dev->timeout_mutex);
                return;
        }
 
@@ -468,6 +472,7 @@ static void cdma_timeout_handler(struct work_struct *work)
                dev_dbg(&dev->dev->dev,
                         "cdma_timeout: expired, but has no clientid\n");
                mutex_unlock(&cdma->lock);
+               mutex_unlock(&dev->timeout_mutex);
                return;
        }
 
@@ -498,6 +503,7 @@ static void cdma_timeout_handler(struct work_struct *work)
                writel(cmdproc_stop,
                        dev->sync_aperture + host1x_sync_cmdproc_stop_r());
                mutex_unlock(&cdma->lock);
+               mutex_unlock(&dev->timeout_mutex);
                return;
        }
 
@@ -517,6 +523,7 @@ static void cdma_timeout_handler(struct work_struct *work)
 
        nvhost_cdma_update_sync_queue(cdma, sp, ch->dev);
        mutex_unlock(&cdma->lock);
+       mutex_unlock(&dev->timeout_mutex);
 }
 
 static const struct nvhost_cdma_ops host1x_cdma_ops = {