]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/android/sync_debug.c
Merge branch 'akpm-current/current'
[karo-tx-linux.git] / drivers / staging / android / sync_debug.c
1 /*
2  * drivers/base/sync.c
3  *
4  * Copyright (C) 2012 Google, Inc.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/debugfs.h>
18 #include <linux/module.h>
19 #include <linux/export.h>
20 #include <linux/file.h>
21 #include <linux/fs.h>
22 #include <linux/kernel.h>
23 #include <linux/poll.h>
24 #include <linux/sched.h>
25 #include <linux/seq_file.h>
26 #include <linux/slab.h>
27 #include <linux/uaccess.h>
28 #include <linux/anon_inodes.h>
29 #include <linux/time64.h>
30 #include "sw_sync.h"
31
32 #ifdef CONFIG_DEBUG_FS
33
34 static struct dentry *dbgfs;
35
36 static LIST_HEAD(sync_timeline_list_head);
37 static DEFINE_SPINLOCK(sync_timeline_list_lock);
38 static LIST_HEAD(sync_file_list_head);
39 static DEFINE_SPINLOCK(sync_file_list_lock);
40
41 void sync_timeline_debug_add(struct sync_timeline *obj)
42 {
43         unsigned long flags;
44
45         spin_lock_irqsave(&sync_timeline_list_lock, flags);
46         list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
47         spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
48 }
49
50 void sync_timeline_debug_remove(struct sync_timeline *obj)
51 {
52         unsigned long flags;
53
54         spin_lock_irqsave(&sync_timeline_list_lock, flags);
55         list_del(&obj->sync_timeline_list);
56         spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
57 }
58
59 void sync_file_debug_add(struct sync_file *sync_file)
60 {
61         unsigned long flags;
62
63         spin_lock_irqsave(&sync_file_list_lock, flags);
64         list_add_tail(&sync_file->sync_file_list, &sync_file_list_head);
65         spin_unlock_irqrestore(&sync_file_list_lock, flags);
66 }
67
68 void sync_file_debug_remove(struct sync_file *sync_file)
69 {
70         unsigned long flags;
71
72         spin_lock_irqsave(&sync_file_list_lock, flags);
73         list_del(&sync_file->sync_file_list);
74         spin_unlock_irqrestore(&sync_file_list_lock, flags);
75 }
76
77 static const char *sync_status_str(int status)
78 {
79         if (status == 0)
80                 return "signaled";
81
82         if (status > 0)
83                 return "active";
84
85         return "error";
86 }
87
88 static void sync_print_fence(struct seq_file *s, struct fence *fence, bool show)
89 {
90         int status = 1;
91         struct sync_timeline *parent = fence_parent(fence);
92
93         if (fence_is_signaled_locked(fence))
94                 status = fence->status;
95
96         seq_printf(s, "  %s%sfence %s",
97                    show ? parent->name : "",
98                    show ? "_" : "",
99                    sync_status_str(status));
100
101         if (status <= 0) {
102                 struct timespec64 ts64 =
103                         ktime_to_timespec64(fence->timestamp);
104
105                 seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
106         }
107
108         if ((!fence || fence->ops->timeline_value_str) &&
109                 fence->ops->fence_value_str) {
110                 char value[64];
111                 bool success;
112
113                 fence->ops->fence_value_str(fence, value, sizeof(value));
114                 success = strlen(value);
115
116                 if (success)
117                         seq_printf(s, ": %s", value);
118
119                 if (success && fence) {
120                         fence->ops->timeline_value_str(fence, value,
121                                                        sizeof(value));
122
123                         if (strlen(value))
124                                 seq_printf(s, " / %s", value);
125                 }
126         }
127
128         seq_puts(s, "\n");
129 }
130
131 static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
132 {
133         struct list_head *pos;
134         unsigned long flags;
135
136         seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
137
138         if (obj->ops->timeline_value_str) {
139                 char value[64];
140
141                 obj->ops->timeline_value_str(obj, value, sizeof(value));
142                 seq_printf(s, ": %s", value);
143         }
144
145         seq_puts(s, "\n");
146
147         spin_lock_irqsave(&obj->child_list_lock, flags);
148         list_for_each(pos, &obj->child_list_head) {
149                 struct fence *fence =
150                         container_of(pos, struct fence, child_list);
151                 sync_print_fence(s, fence, false);
152         }
153         spin_unlock_irqrestore(&obj->child_list_lock, flags);
154 }
155
156 static void sync_print_sync_file(struct seq_file *s,
157                                   struct sync_file *sync_file)
158 {
159         int i;
160
161         seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file->name,
162                    sync_status_str(atomic_read(&sync_file->status)));
163
164         for (i = 0; i < sync_file->num_fences; ++i)
165                 sync_print_fence(s, sync_file->cbs[i].fence, true);
166 }
167
168 static int sync_debugfs_show(struct seq_file *s, void *unused)
169 {
170         unsigned long flags;
171         struct list_head *pos;
172
173         seq_puts(s, "objs:\n--------------\n");
174
175         spin_lock_irqsave(&sync_timeline_list_lock, flags);
176         list_for_each(pos, &sync_timeline_list_head) {
177                 struct sync_timeline *obj =
178                         container_of(pos, struct sync_timeline,
179                                      sync_timeline_list);
180
181                 sync_print_obj(s, obj);
182                 seq_puts(s, "\n");
183         }
184         spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
185
186         seq_puts(s, "fences:\n--------------\n");
187
188         spin_lock_irqsave(&sync_file_list_lock, flags);
189         list_for_each(pos, &sync_file_list_head) {
190                 struct sync_file *sync_file =
191                         container_of(pos, struct sync_file, sync_file_list);
192
193                 sync_print_sync_file(s, sync_file);
194                 seq_puts(s, "\n");
195         }
196         spin_unlock_irqrestore(&sync_file_list_lock, flags);
197         return 0;
198 }
199
200 static int sync_info_debugfs_open(struct inode *inode, struct file *file)
201 {
202         return single_open(file, sync_debugfs_show, inode->i_private);
203 }
204
205 static const struct file_operations sync_info_debugfs_fops = {
206         .open           = sync_info_debugfs_open,
207         .read           = seq_read,
208         .llseek         = seq_lseek,
209         .release        = single_release,
210 };
211
212 /*
213  * *WARNING*
214  *
215  * improper use of this can result in deadlocking kernel drivers from userspace.
216  */
217
218 /* opening sw_sync create a new sync obj */
219 static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
220 {
221         struct sw_sync_timeline *obj;
222         char task_comm[TASK_COMM_LEN];
223
224         get_task_comm(task_comm, current);
225
226         obj = sw_sync_timeline_create(task_comm);
227         if (!obj)
228                 return -ENOMEM;
229
230         file->private_data = obj;
231
232         return 0;
233 }
234
235 static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
236 {
237         struct sw_sync_timeline *obj = file->private_data;
238
239         sync_timeline_destroy(&obj->obj);
240         return 0;
241 }
242
243 static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj,
244                                        unsigned long arg)
245 {
246         int fd = get_unused_fd_flags(O_CLOEXEC);
247         int err;
248         struct fence *fence;
249         struct sync_file *sync_file;
250         struct sw_sync_create_fence_data data;
251
252         if (fd < 0)
253                 return fd;
254
255         if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
256                 err = -EFAULT;
257                 goto err;
258         }
259
260         fence = sw_sync_pt_create(obj, data.value);
261         if (!fence) {
262                 err = -ENOMEM;
263                 goto err;
264         }
265
266         data.name[sizeof(data.name) - 1] = '\0';
267         sync_file = sync_file_create(data.name, fence);
268         if (!sync_file) {
269                 fence_put(fence);
270                 err = -ENOMEM;
271                 goto err;
272         }
273
274         data.fence = fd;
275         if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
276                 sync_file_put(sync_file);
277                 err = -EFAULT;
278                 goto err;
279         }
280
281         sync_file_install(sync_file, fd);
282
283         return 0;
284
285 err:
286         put_unused_fd(fd);
287         return err;
288 }
289
290 static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg)
291 {
292         u32 value;
293
294         if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
295                 return -EFAULT;
296
297         sw_sync_timeline_inc(obj, value);
298
299         return 0;
300 }
301
302 static long sw_sync_ioctl(struct file *file, unsigned int cmd,
303                           unsigned long arg)
304 {
305         struct sw_sync_timeline *obj = file->private_data;
306
307         switch (cmd) {
308         case SW_SYNC_IOC_CREATE_FENCE:
309                 return sw_sync_ioctl_create_fence(obj, arg);
310
311         case SW_SYNC_IOC_INC:
312                 return sw_sync_ioctl_inc(obj, arg);
313
314         default:
315                 return -ENOTTY;
316         }
317 }
318
319 static const struct file_operations sw_sync_debugfs_fops = {
320         .open           = sw_sync_debugfs_open,
321         .release        = sw_sync_debugfs_release,
322         .unlocked_ioctl = sw_sync_ioctl,
323         .compat_ioctl = sw_sync_ioctl,
324 };
325
326 static __init int sync_debugfs_init(void)
327 {
328         dbgfs = debugfs_create_dir("sync", NULL);
329
330         debugfs_create_file("info", 0444, dbgfs, NULL, &sync_info_debugfs_fops);
331         debugfs_create_file("sw_sync", 0644, dbgfs, NULL,
332                             &sw_sync_debugfs_fops);
333
334         return 0;
335 }
336 late_initcall(sync_debugfs_init);
337
338 static __exit void sync_debugfs_exit(void)
339 {
340         if (dbgfs)
341                 debugfs_remove_recursive(dbgfs);
342 }
343 module_exit(sync_debugfs_exit);
344
345 #define DUMP_CHUNK 256
346 static char sync_dump_buf[64 * 1024];
347 void sync_dump(void)
348 {
349         struct seq_file s = {
350                 .buf = sync_dump_buf,
351                 .size = sizeof(sync_dump_buf) - 1,
352         };
353         int i;
354
355         sync_debugfs_show(&s, NULL);
356
357         for (i = 0; i < s.count; i += DUMP_CHUNK) {
358                 if ((s.count - i) > DUMP_CHUNK) {
359                         char c = s.buf[i + DUMP_CHUNK];
360
361                         s.buf[i + DUMP_CHUNK] = 0;
362                         pr_cont("%s", s.buf + i);
363                         s.buf[i + DUMP_CHUNK] = c;
364                 } else {
365                         s.buf[s.count] = 0;
366                         pr_cont("%s", s.buf + i);
367                 }
368         }
369 }
370
371 #endif