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