xref: /linux/drivers/dma-buf/sync_debug.c (revision e78f70bad29c5ae1e1076698b690b15794e9b81e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Sync File validation framework and debug information
4  *
5  * Copyright (C) 2012 Google, Inc.
6  */
7 
8 #include <linux/debugfs.h>
9 #include "sync_debug.h"
10 
11 static struct dentry *dbgfs;
12 
13 static LIST_HEAD(sync_timeline_list_head);
14 static DEFINE_SPINLOCK(sync_timeline_list_lock);
15 
16 void sync_timeline_debug_add(struct sync_timeline *obj)
17 {
18 	unsigned long flags;
19 
20 	spin_lock_irqsave(&sync_timeline_list_lock, flags);
21 	list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
22 	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
23 }
24 
25 void sync_timeline_debug_remove(struct sync_timeline *obj)
26 {
27 	unsigned long flags;
28 
29 	spin_lock_irqsave(&sync_timeline_list_lock, flags);
30 	list_del(&obj->sync_timeline_list);
31 	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
32 }
33 
34 static const char *sync_status_str(int status)
35 {
36 	if (status < 0)
37 		return "error";
38 
39 	if (status > 0)
40 		return "signaled";
41 
42 	return "active";
43 }
44 
45 static void sync_print_fence(struct seq_file *s,
46 			     struct dma_fence *fence, bool show)
47 {
48 	struct sync_timeline *parent = dma_fence_parent(fence);
49 	int status;
50 
51 	status = dma_fence_get_status_locked(fence);
52 
53 	seq_printf(s, "  %s%sfence %s",
54 		   show ? parent->name : "",
55 		   show ? "_" : "",
56 		   sync_status_str(status));
57 
58 	if (test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) {
59 		struct timespec64 ts64 =
60 			ktime_to_timespec64(fence->timestamp);
61 
62 		seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
63 	}
64 
65 	seq_printf(s, ": %lld", fence->seqno);
66 	seq_printf(s, " / %d", parent->value);
67 	seq_putc(s, '\n');
68 }
69 
70 static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
71 {
72 	struct list_head *pos;
73 
74 	seq_printf(s, "%s: %d\n", obj->name, obj->value);
75 
76 	spin_lock(&obj->lock); /* Caller already disabled IRQ. */
77 	list_for_each(pos, &obj->pt_list) {
78 		struct sync_pt *pt = container_of(pos, struct sync_pt, link);
79 		sync_print_fence(s, &pt->base, false);
80 	}
81 	spin_unlock(&obj->lock);
82 }
83 
84 static int sync_info_debugfs_show(struct seq_file *s, void *unused)
85 {
86 	struct list_head *pos;
87 
88 	seq_puts(s, "objs:\n--------------\n");
89 
90 	spin_lock_irq(&sync_timeline_list_lock);
91 	list_for_each(pos, &sync_timeline_list_head) {
92 		struct sync_timeline *obj =
93 			container_of(pos, struct sync_timeline,
94 				     sync_timeline_list);
95 
96 		sync_print_obj(s, obj);
97 		seq_putc(s, '\n');
98 	}
99 	spin_unlock_irq(&sync_timeline_list_lock);
100 
101 	seq_puts(s, "fences:\n--------------\n");
102 
103 	return 0;
104 }
105 
106 DEFINE_SHOW_ATTRIBUTE(sync_info_debugfs);
107 
108 static __init int sync_debugfs_init(void)
109 {
110 	dbgfs = debugfs_create_dir("sync", NULL);
111 
112 	/*
113 	 * The debugfs files won't ever get removed and thus, there is
114 	 * no need to protect it against removal races. The use of
115 	 * debugfs_create_file_unsafe() is actually safe here.
116 	 */
117 	debugfs_create_file_unsafe("info", 0444, dbgfs, NULL,
118 				   &sync_info_debugfs_fops);
119 	debugfs_create_file_unsafe("sw_sync", 0644, dbgfs, NULL,
120 				   &sw_sync_debugfs_fops);
121 
122 	return 0;
123 }
124 late_initcall(sync_debugfs_init);
125