summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonsta Holtta <kholtta@nvidia.com>2017-01-04 13:59:01 -0500
committermobile promotions <svcmobile_promotions@nvidia.com>2017-01-11 12:13:43 -0500
commit5e68c6e971d98fc9d4beaf69c5ca58f39f8db1a7 (patch)
tree6b67a949668f21858a27f0546068659ff680cc46
parent318524ee2f8bc117db25608d432bcc60d92d8c08 (diff)
gpu: nvgpu: add support for refcount tracking
If enabled, track actions (gets and puts) on channel reference counters. Dump the most recent actions to syslog when gk20a_wait_until_counter_is_N gets stuck when closing a channel. GK20A_CHANNEL_REFCOUNT_TRACKING specifies the size of the action history. Default is to disable completely, as this has some runtime overhead. Bug 1826754 Change-Id: I880b0efe8881044d02ae224c243a51cb6c2db8c1 Signed-off-by: Konsta Holtta <kholtta@nvidia.com> Reviewed-on: http://git-master/r/1262424 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
-rw-r--r--drivers/gpu/nvgpu/gk20a/channel_gk20a.c80
-rw-r--r--drivers/gpu/nvgpu/gk20a/channel_gk20a.h48
2 files changed, 126 insertions, 2 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c
index 5e6ddb2e..a731e29c 100644
--- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c
+++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * GK20A Graphics channel 2 * GK20A Graphics channel
3 * 3 *
4 * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. 4 * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify it 6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License, 7 * under the terms and conditions of the GNU General Public License,
@@ -62,6 +62,7 @@ struct channel_priv {
62 62
63static struct channel_gk20a *allocate_channel(struct fifo_gk20a *f); 63static struct channel_gk20a *allocate_channel(struct fifo_gk20a *f);
64static void free_channel(struct fifo_gk20a *f, struct channel_gk20a *c); 64static void free_channel(struct fifo_gk20a *f, struct channel_gk20a *c);
65static void gk20a_channel_dump_ref_actions(struct channel_gk20a *c);
65 66
66static void free_priv_cmdbuf(struct channel_gk20a *c, 67static void free_priv_cmdbuf(struct channel_gk20a *c,
67 struct priv_cmd_entry *e); 68 struct priv_cmd_entry *e);
@@ -886,6 +887,8 @@ static void gk20a_wait_until_counter_is_N(
886 "%s: channel %d, still waiting, %s left: %d, waiting for: %d", 887 "%s: channel %d, still waiting, %s left: %d, waiting for: %d",
887 caller, ch->hw_chid, counter_name, 888 caller, ch->hw_chid, counter_name,
888 atomic_read(counter), wait_value); 889 atomic_read(counter), wait_value);
890
891 gk20a_channel_dump_ref_actions(ch);
889 } 892 }
890} 893}
891 894
@@ -1054,6 +1057,11 @@ unbind:
1054 if (channel_gk20a_is_prealloc_enabled(ch)) 1057 if (channel_gk20a_is_prealloc_enabled(ch))
1055 channel_gk20a_free_prealloc_resources(ch); 1058 channel_gk20a_free_prealloc_resources(ch);
1056 1059
1060#if GK20A_CHANNEL_REFCOUNT_TRACKING
1061 memset(ch->ref_actions, 0, sizeof(ch->ref_actions));
1062 ch->ref_actions_put = 0;
1063#endif
1064
1057 /* make sure we catch accesses of unopened channels in case 1065 /* make sure we catch accesses of unopened channels in case
1058 * there's non-refcounted channel pointers hanging around */ 1066 * there's non-refcounted channel pointers hanging around */
1059 ch->g = NULL; 1067 ch->g = NULL;
@@ -1063,6 +1071,71 @@ unbind:
1063 free_channel(f, ch); 1071 free_channel(f, ch);
1064} 1072}
1065 1073
1074static void gk20a_channel_dump_ref_actions(struct channel_gk20a *ch)
1075{
1076#if GK20A_CHANNEL_REFCOUNT_TRACKING
1077 size_t i, get;
1078 unsigned long now = jiffies;
1079 unsigned long prev_jiffies = 0;
1080 struct device *dev = dev_from_gk20a(ch->g);
1081
1082 spin_lock(&ch->ref_actions_lock);
1083
1084 dev_info(d, "ch %d: refs %d. Actions, most recent last:\n",
1085 ch->hw_chid, atomic_read(&ch->ref_count));
1086
1087 /* start at the oldest possible entry. put is next insertion point */
1088 get = ch->ref_actions_put;
1089
1090 /*
1091 * If the buffer is not full, this will first loop to the oldest entry,
1092 * skipping not-yet-initialized entries. There is no ref_actions_get.
1093 */
1094 for (i = 0; i < GK20A_CHANNEL_REFCOUNT_TRACKING; i++) {
1095 struct channel_gk20a_ref_action *act = &ch->ref_actions[get];
1096
1097 if (act->trace.nr_entries) {
1098 dev_info(d, "%s ref %zu steps ago (age %d ms, diff %d ms)\n",
1099 act->type == channel_gk20a_ref_action_get
1100 ? "GET" : "PUT",
1101 GK20A_CHANNEL_REFCOUNT_TRACKING - 1 - i,
1102 jiffies_to_msecs(now - act->jiffies),
1103 jiffies_to_msecs(act->jiffies - prev_jiffies));
1104
1105 print_stack_trace(&act->trace, 0);
1106 prev_jiffies = act->jiffies;
1107 }
1108
1109 get = (get + 1) % GK20A_CHANNEL_REFCOUNT_TRACKING;
1110 }
1111
1112 spin_unlock(&ch->ref_actions_lock);
1113#endif
1114}
1115
1116static void gk20a_channel_save_ref_source(struct channel_gk20a *ch,
1117 enum channel_gk20a_ref_action_type type)
1118{
1119#if GK20A_CHANNEL_REFCOUNT_TRACKING
1120 struct channel_gk20a_ref_action *act;
1121
1122 spin_lock(&ch->ref_actions_lock);
1123
1124 act = &ch->ref_actions[ch->ref_actions_put];
1125 act->type = type;
1126 act->trace.max_entries = GK20A_CHANNEL_REFCOUNT_TRACKING_STACKLEN;
1127 act->trace.nr_entries = 0;
1128 act->trace.skip = 3; /* onwards from the caller of this */
1129 act->trace.entries = act->trace_entries;
1130 save_stack_trace(&act->trace);
1131 act->jiffies = jiffies;
1132 ch->ref_actions_put = (ch->ref_actions_put + 1) %
1133 GK20A_CHANNEL_REFCOUNT_TRACKING;
1134
1135 spin_unlock(&ch->ref_actions_lock);
1136#endif
1137}
1138
1066/* Try to get a reference to the channel. Return nonzero on success. If fails, 1139/* Try to get a reference to the channel. Return nonzero on success. If fails,
1067 * the channel is dead or being freed elsewhere and you must not touch it. 1140 * the channel is dead or being freed elsewhere and you must not touch it.
1068 * 1141 *
@@ -1082,6 +1155,7 @@ struct channel_gk20a *_gk20a_channel_get(struct channel_gk20a *ch,
1082 spin_lock(&ch->ref_obtain_lock); 1155 spin_lock(&ch->ref_obtain_lock);
1083 1156
1084 if (likely(ch->referenceable)) { 1157 if (likely(ch->referenceable)) {
1158 gk20a_channel_save_ref_source(ch, channel_gk20a_ref_action_get);
1085 atomic_inc(&ch->ref_count); 1159 atomic_inc(&ch->ref_count);
1086 ret = ch; 1160 ret = ch;
1087 } else 1161 } else
@@ -1097,6 +1171,7 @@ struct channel_gk20a *_gk20a_channel_get(struct channel_gk20a *ch,
1097 1171
1098void _gk20a_channel_put(struct channel_gk20a *ch, const char *caller) 1172void _gk20a_channel_put(struct channel_gk20a *ch, const char *caller)
1099{ 1173{
1174 gk20a_channel_save_ref_source(ch, channel_gk20a_ref_action_put);
1100 trace_gk20a_channel_put(ch->hw_chid, caller); 1175 trace_gk20a_channel_put(ch->hw_chid, caller);
1101 atomic_dec(&ch->ref_count); 1176 atomic_dec(&ch->ref_count);
1102 wake_up_all(&ch->ref_count_dec_wq); 1177 wake_up_all(&ch->ref_count_dec_wq);
@@ -2861,6 +2936,9 @@ int gk20a_init_channel_support(struct gk20a *g, u32 chid)
2861 atomic_set(&c->ref_count, 0); 2936 atomic_set(&c->ref_count, 0);
2862 c->referenceable = false; 2937 c->referenceable = false;
2863 init_waitqueue_head(&c->ref_count_dec_wq); 2938 init_waitqueue_head(&c->ref_count_dec_wq);
2939#if GK20A_CHANNEL_REFCOUNT_TRACKING
2940 spin_lock_init(&c->ref_actions_lock);
2941#endif
2864 mutex_init(&c->ioctl_lock); 2942 mutex_init(&c->ioctl_lock);
2865 mutex_init(&c->error_notifier_mutex); 2943 mutex_init(&c->error_notifier_mutex);
2866 spin_lock_init(&c->joblist.dynamic.lock); 2944 spin_lock_init(&c->joblist.dynamic.lock);
diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h
index 697d1603..44a989da 100644
--- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h
+++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * GK20A graphics channel 2 * GK20A graphics channel
3 * 3 *
4 * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. 4 * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved.
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify it 6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License, 7 * under the terms and conditions of the GNU General Public License,
@@ -24,6 +24,7 @@
24#include <linux/semaphore.h> 24#include <linux/semaphore.h>
25#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/spinlock.h> 26#include <linux/spinlock.h>
27#include <linux/stacktrace.h>
27#include <linux/wait.h> 28#include <linux/wait.h>
28#include <uapi/linux/nvgpu.h> 29#include <uapi/linux/nvgpu.h>
29 30
@@ -115,6 +116,40 @@ struct channel_gk20a_clean_up {
115 struct delayed_work wq; 116 struct delayed_work wq;
116}; 117};
117 118
119/*
120 * Track refcount actions, saving their stack traces. This number specifies how
121 * many most recent actions are stored in a buffer. Set to 0 to disable. 128
122 * should be enough to track moderately hard problems from the start.
123 */
124#define GK20A_CHANNEL_REFCOUNT_TRACKING 0
125/* Stack depth for the saved actions. */
126#define GK20A_CHANNEL_REFCOUNT_TRACKING_STACKLEN 8
127
128/*
129 * Because the puts and gets are not linked together explicitly (although they
130 * should always come in pairs), it's not possible to tell which ref holder to
131 * delete from the list when doing a put. So, just store some number of most
132 * recent gets and puts in a ring buffer, to obtain a history.
133 *
134 * These are zeroed when a channel is closed, so a new one starts fresh.
135 */
136
137enum channel_gk20a_ref_action_type {
138 channel_gk20a_ref_action_get,
139 channel_gk20a_ref_action_put
140};
141
142struct channel_gk20a_ref_action {
143 enum channel_gk20a_ref_action_type type;
144 unsigned long jiffies;
145 /*
146 * Many of these traces will be similar. Simpler to just capture
147 * duplicates than to have a separate database for the entries.
148 */
149 struct stack_trace trace;
150 unsigned long trace_entries[GK20A_CHANNEL_REFCOUNT_TRACKING_STACKLEN];
151};
152
118/* this is the priv element of struct nvhost_channel */ 153/* this is the priv element of struct nvhost_channel */
119struct channel_gk20a { 154struct channel_gk20a {
120 struct gk20a *g; /* set only when channel is active */ 155 struct gk20a *g; /* set only when channel is active */
@@ -125,6 +160,17 @@ struct channel_gk20a {
125 bool referenceable; 160 bool referenceable;
126 atomic_t ref_count; 161 atomic_t ref_count;
127 wait_queue_head_t ref_count_dec_wq; 162 wait_queue_head_t ref_count_dec_wq;
163#if GK20A_CHANNEL_REFCOUNT_TRACKING
164 /*
165 * Ring buffer for most recent refcount gets and puts. Protected by
166 * ref_actions_lock when getting or putting refs (i.e., adding
167 * entries), and when reading entries.
168 */
169 struct channel_gk20a_ref_action ref_actions[
170 GK20A_CHANNEL_REFCOUNT_TRACKING];
171 size_t ref_actions_put; /* index of next write */
172 spinlock_t ref_actions_lock;
173#endif
128 174
129 struct gk20a_semaphore_int *hw_sema; 175 struct gk20a_semaphore_int *hw_sema;
130 176