video: tegra: host: use dynamically allocated wait_queue rel-24-foster-r2
Deepak Nibade [Wed, 20 Jan 2016 13:39:19 +0000 (18:39 +0530)]
In nvhost_syncpt_wait_timeout(), we currently allocate
wait_queue_head on stack using
DECLARE_WAIT_QUEUE_HEAD_ONSTACK()

If wait is complete, then this wait_queue_head will
removed off the stack

But in some rare case if action_wakeup_interruptible()
is called after wait is complete, we try to access
wait_queue_head which is already deleted from stack

To fix this, define wait_queue_head inside nvhost_waitlist
and allocate it dynamically along with waitlist

Bug 200126989

Change-Id: Iad7869323832e6f36c044e0d29fdea62dca762d5
Signed-off-by: Deepak Nibade <dnibade@nvidia.com>
Reviewed-on: http://git-master/r/935161
(cherry picked from commit 80b5c960e95b9f1f4c1401b03d72641ac4b6ccc6)
Reviewed-on: http://git-master/r/1113381
GVS: Gerrit_Virtual_Submit
Reviewed-by: Vinayak Pane <vpane@nvidia.com>

drivers/video/tegra/host/nvhost_intr.c
drivers/video/tegra/host/nvhost_intr.h
drivers/video/tegra/host/nvhost_syncpt.c

index d8d1995..f1cd0a2 100644 (file)
 
 /*** Wait list management ***/
 
-struct nvhost_waitlist {
-       struct list_head list;
-       struct kref refcount;
-       u32 thresh;
-       enum nvhost_intr_action action;
-       atomic_t state;
-       struct timespec isr_recv;
-       void *data;
-       int count;
-};
-
 struct nvhost_waitlist_external_notifier {
        struct nvhost_master *master;
        void (*callback)(void *, int);
@@ -188,7 +177,7 @@ static void action_submit_complete(struct nvhost_waitlist *waiter)
 
 static void action_wakeup(struct nvhost_waitlist *waiter)
 {
-       wait_queue_head_t *wq = waiter->data;
+       wait_queue_head_t *wq = &waiter->wq;
 
        WARN_ON(atomic_xchg(&waiter->state, WLS_HANDLED) != WLS_REMOVED);
        wake_up(wq);
@@ -208,7 +197,7 @@ static void action_notify(struct nvhost_waitlist *waiter)
 
 static void action_wakeup_interruptible(struct nvhost_waitlist *waiter)
 {
-       wait_queue_head_t *wq = waiter->data;
+       wait_queue_head_t *wq = &waiter->wq;
 
        WARN_ON(atomic_xchg(&waiter->state, WLS_HANDLED) != WLS_REMOVED);
        wake_up_interruptible(wq);
@@ -415,6 +404,7 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh,
 
        /* initialize a new waiter */
        INIT_LIST_HEAD(&waiter->list);
+       init_waitqueue_head(&waiter->wq);
        kref_init(&waiter->refcount);
        if (ref)
                kref_get(&waiter->refcount);
index 207ac69..cb15aaf 100644 (file)
@@ -77,6 +77,18 @@ enum nvhost_intr_action {
 
 struct nvhost_intr;
 
+struct nvhost_waitlist {
+       struct list_head list;
+       struct kref refcount;
+       u32 thresh;
+       enum nvhost_intr_action action;
+       atomic_t state;
+       struct timespec isr_recv;
+       void *data;
+       int count;
+       wait_queue_head_t wq;
+};
+
 struct nvhost_intr_syncpt {
        struct nvhost_intr *intr;
        u8 id;
index eaa661d..33a1243 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Tegra Graphics Host Syncpoints
  *
- * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -197,9 +197,8 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
                        u32 thresh, u32 timeout, u32 *value,
                        struct timespec *ts, bool interruptible)
 {
-       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
-       void *ref;
-       void *waiter;
+       void *ref = NULL;
+       struct nvhost_waitlist *waiter = NULL;
        int err = 0, check_count = 0, low_timeout = 0;
        u32 val, old_val, new_val;
        struct nvhost_master *host = syncpt_to_dev(sp);
@@ -255,7 +254,7 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
                                interruptible ?
                                  NVHOST_INTR_ACTION_WAKEUP_INTERRUPTIBLE :
                                  NVHOST_INTR_ACTION_WAKEUP,
-                               &wq,
+                               &waiter->wq,
                                waiter,
                                &ref);
        if (err)
@@ -276,11 +275,11 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
                u32 check = min_t(u32, SYNCPT_CHECK_PERIOD, timeout);
                int remain;
                if (interruptible)
-                       remain = wait_event_interruptible_timeout(wq,
+                       remain = wait_event_interruptible_timeout(waiter->wq,
                                syncpt_is_expired(sp, id, thresh),
                                check);
                else
-                       remain = wait_event_timeout(wq,
+                       remain = wait_event_timeout(waiter->wq,
                                syncpt_is_expired(sp, id, thresh),
                                check);
                if (remain > 0 ||