video: tegra: host: Fix debug dump
Arto Merilainen [Wed, 28 May 2014 06:00:43 +0000 (09:00 +0300)]
Currently the debug dump routine is vulnerable against tricky races.
As normal operation is more important than getting the full dump
always, this patch reworks mutex usage in debug dump routine:

 - lock the channel list is now before going through channels. This
   ensures that channel states remain valid even if some of the
   channels were finished during dump.
 - this patch modifies mutex_lock() calls to mutex_trylock() calls.
   This ensures that the function call cannot block.

Bug 1517429

Change-Id: Idf170de196bcededbaec6c9031d268cf2d8bc35d
(cherry picked from commit 544cbc483b973ca1c78173edcf2072434c02b5dd)
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Reviewed-on: http://git-master/r/498508
Reviewed-on: http://git-master/r/538724
GVS: Gerrit_Virtual_Submit
Reviewed-by: Shreshtha Sahu <ssahu@nvidia.com>
Tested-by: Shreshtha Sahu <ssahu@nvidia.com>
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Reviewed-by: Shridhar Rasal <srasal@nvidia.com>

drivers/video/tegra/host/debug.c

index 900d30a..eb33946 100644 (file)
@@ -56,7 +56,7 @@ static int show_channels(struct platform_device *pdev, void *data,
        struct output *o = data;
        struct nvhost_master *m;
        struct nvhost_device_data *pdata;
-       int index;
+       int index, locked;
 
        if (pdev == NULL)
                return 0;
@@ -64,6 +64,13 @@ static int show_channels(struct platform_device *pdev, void *data,
        m = nvhost_get_host(pdev);
        pdata = platform_get_drvdata(pdev);
 
+       /* acquire lock to prevent channel modifications */
+       locked = mutex_trylock(&m->chlist_mutex);
+       if (!locked) {
+               nvhost_debug_output(o, "unable to lock channel list\n");
+               return 0;
+       }
+
        for (index = 0; index < pdata->num_channels; index++) {
                ch = pdata->channels[index];
                if (!ch || !ch->dev) {
@@ -71,19 +78,27 @@ static int show_channels(struct platform_device *pdev, void *data,
                                        index + 1, pdev->name);
                        continue;
                }
-               nvhost_getchannel(ch);
-               if (ch->chid != locked_id)
-                       mutex_lock(&ch->cdma.lock);
+
+               /* ensure that we get a lock */
+               locked = mutex_trylock(&ch->cdma.lock);
+               if (!(locked || ch->chid == locked_id)) {
+                       nvhost_debug_output(o, "failed to lock channel %d cdma\n",
+                                           ch->chid);
+                       continue;
+               }
+
                if (fifo)
                        nvhost_get_chip_ops()->debug.show_channel_fifo(
                                m, ch, o, ch->chid);
                nvhost_get_chip_ops()->debug.show_channel_cdma(
                        m, ch, o, ch->chid);
+
                if (ch->chid != locked_id)
                        mutex_unlock(&ch->cdma.lock);
-               nvhost_putchannel(ch);
        }
 
+       mutex_unlock(&m->chlist_mutex);
+
        return 0;
 }