linux/drivers/staging/android/sync_debug.c
<<
>>
Prefs
   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/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