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  static LIST_HEAD(sync_file_list_head);
16  static DEFINE_SPINLOCK(sync_file_list_lock);
17  
sync_timeline_debug_add(struct sync_timeline * obj)18  void sync_timeline_debug_add(struct sync_timeline *obj)
19  {
20  	unsigned long flags;
21  
22  	spin_lock_irqsave(&sync_timeline_list_lock, flags);
23  	list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
24  	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
25  }
26  
sync_timeline_debug_remove(struct sync_timeline * obj)27  void sync_timeline_debug_remove(struct sync_timeline *obj)
28  {
29  	unsigned long flags;
30  
31  	spin_lock_irqsave(&sync_timeline_list_lock, flags);
32  	list_del(&obj->sync_timeline_list);
33  	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
34  }
35  
sync_file_debug_add(struct sync_file * sync_file)36  void sync_file_debug_add(struct sync_file *sync_file)
37  {
38  	unsigned long flags;
39  
40  	spin_lock_irqsave(&sync_file_list_lock, flags);
41  	list_add_tail(&sync_file->sync_file_list, &sync_file_list_head);
42  	spin_unlock_irqrestore(&sync_file_list_lock, flags);
43  }
44  
sync_file_debug_remove(struct sync_file * sync_file)45  void sync_file_debug_remove(struct sync_file *sync_file)
46  {
47  	unsigned long flags;
48  
49  	spin_lock_irqsave(&sync_file_list_lock, flags);
50  	list_del(&sync_file->sync_file_list);
51  	spin_unlock_irqrestore(&sync_file_list_lock, flags);
52  }
53  
sync_status_str(int status)54  static const char *sync_status_str(int status)
55  {
56  	if (status < 0)
57  		return "error";
58  
59  	if (status > 0)
60  		return "signaled";
61  
62  	return "active";
63  }
64  
sync_print_fence(struct seq_file * s,struct dma_fence * fence,bool show)65  static void sync_print_fence(struct seq_file *s,
66  			     struct dma_fence *fence, bool show)
67  {
68  	struct sync_timeline *parent = dma_fence_parent(fence);
69  	int status;
70  
71  	status = dma_fence_get_status_locked(fence);
72  
73  	seq_printf(s, "  %s%sfence %s",
74  		   show ? parent->name : "",
75  		   show ? "_" : "",
76  		   sync_status_str(status));
77  
78  	if (test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) {
79  		struct timespec64 ts64 =
80  			ktime_to_timespec64(fence->timestamp);
81  
82  		seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
83  	}
84  
85  	if (fence->ops->timeline_value_str &&
86  		fence->ops->fence_value_str) {
87  		char value[64];
88  		bool success;
89  
90  		fence->ops->fence_value_str(fence, value, sizeof(value));
91  		success = strlen(value);
92  
93  		if (success) {
94  			seq_printf(s, ": %s", value);
95  
96  			fence->ops->timeline_value_str(fence, value,
97  						       sizeof(value));
98  
99  			if (strlen(value))
100  				seq_printf(s, " / %s", value);
101  		}
102  	}
103  
104  	seq_putc(s, '\n');
105  }
106  
sync_print_obj(struct seq_file * s,struct sync_timeline * obj)107  static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
108  {
109  	struct list_head *pos;
110  
111  	seq_printf(s, "%s: %d\n", obj->name, obj->value);
112  
113  	spin_lock(&obj->lock); /* Caller already disabled IRQ. */
114  	list_for_each(pos, &obj->pt_list) {
115  		struct sync_pt *pt = container_of(pos, struct sync_pt, link);
116  		sync_print_fence(s, &pt->base, false);
117  	}
118  	spin_unlock(&obj->lock);
119  }
120  
sync_print_sync_file(struct seq_file * s,struct sync_file * sync_file)121  static void sync_print_sync_file(struct seq_file *s,
122  				  struct sync_file *sync_file)
123  {
124  	char buf[128];
125  	int i;
126  
127  	seq_printf(s, "[%p] %s: %s\n", sync_file,
128  		   sync_file_get_name(sync_file, buf, sizeof(buf)),
129  		   sync_status_str(dma_fence_get_status(sync_file->fence)));
130  
131  	if (dma_fence_is_array(sync_file->fence)) {
132  		struct dma_fence_array *array = to_dma_fence_array(sync_file->fence);
133  
134  		for (i = 0; i < array->num_fences; ++i)
135  			sync_print_fence(s, array->fences[i], true);
136  	} else {
137  		sync_print_fence(s, sync_file->fence, true);
138  	}
139  }
140  
sync_info_debugfs_show(struct seq_file * s,void * unused)141  static int sync_info_debugfs_show(struct seq_file *s, void *unused)
142  {
143  	struct list_head *pos;
144  
145  	seq_puts(s, "objs:\n--------------\n");
146  
147  	spin_lock_irq(&sync_timeline_list_lock);
148  	list_for_each(pos, &sync_timeline_list_head) {
149  		struct sync_timeline *obj =
150  			container_of(pos, struct sync_timeline,
151  				     sync_timeline_list);
152  
153  		sync_print_obj(s, obj);
154  		seq_putc(s, '\n');
155  	}
156  	spin_unlock_irq(&sync_timeline_list_lock);
157  
158  	seq_puts(s, "fences:\n--------------\n");
159  
160  	spin_lock_irq(&sync_file_list_lock);
161  	list_for_each(pos, &sync_file_list_head) {
162  		struct sync_file *sync_file =
163  			container_of(pos, struct sync_file, sync_file_list);
164  
165  		sync_print_sync_file(s, sync_file);
166  		seq_putc(s, '\n');
167  	}
168  	spin_unlock_irq(&sync_file_list_lock);
169  	return 0;
170  }
171  
172  DEFINE_SHOW_ATTRIBUTE(sync_info_debugfs);
173  
sync_debugfs_init(void)174  static __init int sync_debugfs_init(void)
175  {
176  	dbgfs = debugfs_create_dir("sync", NULL);
177  
178  	/*
179  	 * The debugfs files won't ever get removed and thus, there is
180  	 * no need to protect it against removal races. The use of
181  	 * debugfs_create_file_unsafe() is actually safe here.
182  	 */
183  	debugfs_create_file_unsafe("info", 0444, dbgfs, NULL,
184  				   &sync_info_debugfs_fops);
185  	debugfs_create_file_unsafe("sw_sync", 0644, dbgfs, NULL,
186  				   &sw_sync_debugfs_fops);
187  
188  	return 0;
189  }
190  late_initcall(sync_debugfs_init);
191