diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/channel_gk20a.c | 80 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/channel_gk20a.h | 48 |
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 | ||
63 | static struct channel_gk20a *allocate_channel(struct fifo_gk20a *f); | 63 | static struct channel_gk20a *allocate_channel(struct fifo_gk20a *f); |
64 | static void free_channel(struct fifo_gk20a *f, struct channel_gk20a *c); | 64 | static void free_channel(struct fifo_gk20a *f, struct channel_gk20a *c); |
65 | static void gk20a_channel_dump_ref_actions(struct channel_gk20a *c); | ||
65 | 66 | ||
66 | static void free_priv_cmdbuf(struct channel_gk20a *c, | 67 | static 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 | ||
1074 | static 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 | |||
1116 | static 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 | ||
1098 | void _gk20a_channel_put(struct channel_gk20a *ch, const char *caller) | 1172 | void _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 | |||
137 | enum channel_gk20a_ref_action_type { | ||
138 | channel_gk20a_ref_action_get, | ||
139 | channel_gk20a_ref_action_put | ||
140 | }; | ||
141 | |||
142 | struct 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 */ |
119 | struct channel_gk20a { | 154 | struct 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 | ||