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 <linux/sync_file.h>
  30#include "sw_sync.h"
  31
  32#ifdef CONFIG_DEBUG_FS
  33
  34static struct dentry *dbgfs;
  35
  36static LIST_HEAD(sync_timeline_list_head);
  37static DEFINE_SPINLOCK(sync_timeline_list_lock);
  38static LIST_HEAD(sync_file_list_head);
  39static DEFINE_SPINLOCK(sync_file_list_lock);
  40
  41void 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
  50void 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
  59void 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
  68void 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
  77static 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
  88static 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
 131static 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
 156static 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
 168static 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
 200static 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
 205static 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 */
 219static 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
 235static 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
 243static 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        sync_file = sync_file_create(fence);
 267        if (!sync_file) {
 268                fence_put(fence);
 269                err = -ENOMEM;
 270                goto err;
 271        }
 272
 273        data.fence = fd;
 274        if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
 275                fput(sync_file->file);
 276                err = -EFAULT;
 277                goto err;
 278        }
 279
 280        fd_install(fd, sync_file->file);
 281
 282        return 0;
 283
 284err:
 285        put_unused_fd(fd);
 286        return err;
 287}
 288
 289static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg)
 290{
 291        u32 value;
 292
 293        if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
 294                return -EFAULT;
 295
 296        sw_sync_timeline_inc(obj, value);
 297
 298        return 0;
 299}
 300
 301static long sw_sync_ioctl(struct file *file, unsigned int cmd,
 302                          unsigned long arg)
 303{
 304        struct sw_sync_timeline *obj = file->private_data;
 305
 306        switch (cmd) {
 307        case SW_SYNC_IOC_CREATE_FENCE:
 308                return sw_sync_ioctl_create_fence(obj, arg);
 309
 310        case SW_SYNC_IOC_INC:
 311                return sw_sync_ioctl_inc(obj, arg);
 312
 313        default:
 314                return -ENOTTY;
 315        }
 316}
 317
 318static const struct file_operations sw_sync_debugfs_fops = {
 319        .open           = sw_sync_debugfs_open,
 320        .release        = sw_sync_debugfs_release,
 321        .unlocked_ioctl = sw_sync_ioctl,
 322        .compat_ioctl = sw_sync_ioctl,
 323};
 324
 325static __init int sync_debugfs_init(void)
 326{
 327        dbgfs = debugfs_create_dir("sync", NULL);
 328
 329        debugfs_create_file("info", 0444, dbgfs, NULL, &sync_info_debugfs_fops);
 330        debugfs_create_file("sw_sync", 0644, dbgfs, NULL,
 331                            &sw_sync_debugfs_fops);
 332
 333        return 0;
 334}
 335late_initcall(sync_debugfs_init);
 336
 337#define DUMP_CHUNK 256
 338static char sync_dump_buf[64 * 1024];
 339void sync_dump(void)
 340{
 341        struct seq_file s = {
 342                .buf = sync_dump_buf,
 343                .size = sizeof(sync_dump_buf) - 1,
 344        };
 345        int i;
 346
 347        sync_debugfs_show(&s, NULL);
 348
 349        for (i = 0; i < s.count; i += DUMP_CHUNK) {
 350                if ((s.count - i) > DUMP_CHUNK) {
 351                        char c = s.buf[i + DUMP_CHUNK];
 352
 353                        s.buf[i + DUMP_CHUNK] = 0;
 354                        pr_cont("%s", s.buf + i);
 355                        s.buf[i + DUMP_CHUNK] = c;
 356                } else {
 357                        s.buf[s.count] = 0;
 358                        pr_cont("%s", s.buf + i);
 359                }
 360        }
 361}
 362
 363#endif
 364