1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/debugfs.h>
18#include <linux/export.h>
19#include <linux/file.h>
20#include <linux/fs.h>
21#include <linux/kernel.h>
22#include <linux/poll.h>
23#include <linux/sched.h>
24#include <linux/seq_file.h>
25#include <linux/slab.h>
26#include <linux/uaccess.h>
27#include <linux/anon_inodes.h>
28#include <linux/time64.h>
29#include "sync.h"
30
31#ifdef CONFIG_DEBUG_FS
32
33static LIST_HEAD(sync_timeline_list_head);
34static DEFINE_SPINLOCK(sync_timeline_list_lock);
35static LIST_HEAD(sync_fence_list_head);
36static DEFINE_SPINLOCK(sync_fence_list_lock);
37
38void sync_timeline_debug_add(struct sync_timeline *obj)
39{
40 unsigned long flags;
41
42 spin_lock_irqsave(&sync_timeline_list_lock, flags);
43 list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
44 spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
45}
46
47void sync_timeline_debug_remove(struct sync_timeline *obj)
48{
49 unsigned long flags;
50
51 spin_lock_irqsave(&sync_timeline_list_lock, flags);
52 list_del(&obj->sync_timeline_list);
53 spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
54}
55
56void sync_fence_debug_add(struct sync_fence *fence)
57{
58 unsigned long flags;
59
60 spin_lock_irqsave(&sync_fence_list_lock, flags);
61 list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
62 spin_unlock_irqrestore(&sync_fence_list_lock, flags);
63}
64
65void sync_fence_debug_remove(struct sync_fence *fence)
66{
67 unsigned long flags;
68
69 spin_lock_irqsave(&sync_fence_list_lock, flags);
70 list_del(&fence->sync_fence_list);
71 spin_unlock_irqrestore(&sync_fence_list_lock, flags);
72}
73
74static const char *sync_status_str(int status)
75{
76 if (status == 0)
77 return "signaled";
78
79 if (status > 0)
80 return "active";
81
82 return "error";
83}
84
85static void sync_print_pt(struct seq_file *s, struct fence *pt, bool fence)
86{
87 int status = 1;
88
89 if (fence_is_signaled_locked(pt))
90 status = pt->status;
91
92 seq_printf(s, " %s%spt %s",
93 fence && pt->ops->get_timeline_name ?
94 pt->ops->get_timeline_name(pt) : "",
95 fence ? "_" : "",
96 sync_status_str(status));
97
98 if (status <= 0) {
99 struct timespec64 ts64 =
100 ktime_to_timespec64(pt->timestamp);
101
102 seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
103 }
104
105 if ((!fence || pt->ops->timeline_value_str) &&
106 pt->ops->fence_value_str) {
107 char value[64];
108 bool success;
109
110 pt->ops->fence_value_str(pt, value, sizeof(value));
111 success = strlen(value);
112
113 if (success)
114 seq_printf(s, ": %s", value);
115
116 if (success && fence) {
117 pt->ops->timeline_value_str(pt, value, sizeof(value));
118
119 if (strlen(value))
120 seq_printf(s, " / %s", value);
121 }
122 }
123
124 seq_puts(s, "\n");
125}
126
127static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
128{
129 struct list_head *pos;
130 unsigned long flags;
131
132 seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
133
134 if (obj->ops->timeline_value_str) {
135 char value[64];
136
137 obj->ops->timeline_value_str(obj, value, sizeof(value));
138 seq_printf(s, ": %s", value);
139 }
140
141 seq_puts(s, "\n");
142
143 spin_lock_irqsave(&obj->child_list_lock, flags);
144 list_for_each(pos, &obj->child_list_head) {
145 struct sync_pt *pt =
146 container_of(pos, struct sync_pt, child_list);
147 sync_print_pt(s, &pt->base, false);
148 }
149 spin_unlock_irqrestore(&obj->child_list_lock, flags);
150}
151
152static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
153{
154 wait_queue_t *pos;
155 unsigned long flags;
156 int i;
157
158 seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
159 sync_status_str(atomic_read(&fence->status)));
160
161 for (i = 0; i < fence->num_fences; ++i) {
162 sync_print_pt(s, fence->cbs[i].sync_pt, true);
163 }
164
165 spin_lock_irqsave(&fence->wq.lock, flags);
166 list_for_each_entry(pos, &fence->wq.task_list, task_list) {
167 struct sync_fence_waiter *waiter;
168
169 if (pos->func != &sync_fence_wake_up_wq)
170 continue;
171
172 waiter = container_of(pos, struct sync_fence_waiter, work);
173
174 seq_printf(s, "waiter %pF\n", waiter->callback);
175 }
176 spin_unlock_irqrestore(&fence->wq.lock, flags);
177}
178
179static int sync_debugfs_show(struct seq_file *s, void *unused)
180{
181 unsigned long flags;
182 struct list_head *pos;
183
184 seq_puts(s, "objs:\n--------------\n");
185
186 spin_lock_irqsave(&sync_timeline_list_lock, flags);
187 list_for_each(pos, &sync_timeline_list_head) {
188 struct sync_timeline *obj =
189 container_of(pos, struct sync_timeline,
190 sync_timeline_list);
191
192 sync_print_obj(s, obj);
193 seq_puts(s, "\n");
194 }
195 spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
196
197 seq_puts(s, "fences:\n--------------\n");
198
199 spin_lock_irqsave(&sync_fence_list_lock, flags);
200 list_for_each(pos, &sync_fence_list_head) {
201 struct sync_fence *fence =
202 container_of(pos, struct sync_fence, sync_fence_list);
203
204 sync_print_fence(s, fence);
205 seq_puts(s, "\n");
206 }
207 spin_unlock_irqrestore(&sync_fence_list_lock, flags);
208 return 0;
209}
210
211static int sync_debugfs_open(struct inode *inode, struct file *file)
212{
213 return single_open(file, sync_debugfs_show, inode->i_private);
214}
215
216static const struct file_operations sync_debugfs_fops = {
217 .open = sync_debugfs_open,
218 .read = seq_read,
219 .llseek = seq_lseek,
220 .release = single_release,
221};
222
223static __init int sync_debugfs_init(void)
224{
225 debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
226 return 0;
227}
228late_initcall(sync_debugfs_init);
229
230#define DUMP_CHUNK 256
231static char sync_dump_buf[64 * 1024];
232void sync_dump(void)
233{
234 struct seq_file s = {
235 .buf = sync_dump_buf,
236 .size = sizeof(sync_dump_buf) - 1,
237 };
238 int i;
239
240 sync_debugfs_show(&s, NULL);
241
242 for (i = 0; i < s.count; i += DUMP_CHUNK) {
243 if ((s.count - i) > DUMP_CHUNK) {
244 char c = s.buf[i + DUMP_CHUNK];
245
246 s.buf[i + DUMP_CHUNK] = 0;
247 pr_cont("%s", s.buf + i);
248 s.buf[i + DUMP_CHUNK] = c;
249 } else {
250 s.buf[s.count] = 0;
251 pr_cont("%s", s.buf + i);
252 }
253 }
254}
255
256#endif
257