13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
23d0407baSopenharmony_ci/*
33d0407baSopenharmony_ci * Sync File validation framework and debug information
43d0407baSopenharmony_ci *
53d0407baSopenharmony_ci * Copyright (C) 2012 Google, Inc.
63d0407baSopenharmony_ci */
73d0407baSopenharmony_ci
83d0407baSopenharmony_ci#include <linux/debugfs.h>
93d0407baSopenharmony_ci#include "sync_debug.h"
103d0407baSopenharmony_ci
113d0407baSopenharmony_ci#ifdef CONFIG_DEBUG_FS
123d0407baSopenharmony_cistatic struct dentry *dbgfs;
133d0407baSopenharmony_ci
143d0407baSopenharmony_cistatic LIST_HEAD(sync_timeline_list_head);
153d0407baSopenharmony_cistatic DEFINE_SPINLOCK(sync_timeline_list_lock);
163d0407baSopenharmony_cistatic LIST_HEAD(sync_file_list_head);
173d0407baSopenharmony_cistatic DEFINE_SPINLOCK(sync_file_list_lock);
183d0407baSopenharmony_ci
193d0407baSopenharmony_civoid sync_timeline_debug_add(struct sync_timeline *obj)
203d0407baSopenharmony_ci{
213d0407baSopenharmony_ci    unsigned long flags;
223d0407baSopenharmony_ci
233d0407baSopenharmony_ci    spin_lock_irqsave(&sync_timeline_list_lock, flags);
243d0407baSopenharmony_ci    list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
253d0407baSopenharmony_ci    spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
263d0407baSopenharmony_ci}
273d0407baSopenharmony_ci
283d0407baSopenharmony_civoid sync_timeline_debug_remove(struct sync_timeline *obj)
293d0407baSopenharmony_ci{
303d0407baSopenharmony_ci    unsigned long flags;
313d0407baSopenharmony_ci
323d0407baSopenharmony_ci    spin_lock_irqsave(&sync_timeline_list_lock, flags);
333d0407baSopenharmony_ci    list_del(&obj->sync_timeline_list);
343d0407baSopenharmony_ci    spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
353d0407baSopenharmony_ci}
363d0407baSopenharmony_ci
373d0407baSopenharmony_civoid sync_file_debug_add(struct sync_file *sync_file)
383d0407baSopenharmony_ci{
393d0407baSopenharmony_ci    unsigned long flags;
403d0407baSopenharmony_ci
413d0407baSopenharmony_ci    spin_lock_irqsave(&sync_file_list_lock, flags);
423d0407baSopenharmony_ci    list_add_tail(&sync_file->sync_file_list, &sync_file_list_head);
433d0407baSopenharmony_ci    spin_unlock_irqrestore(&sync_file_list_lock, flags);
443d0407baSopenharmony_ci}
453d0407baSopenharmony_ci
463d0407baSopenharmony_civoid sync_file_debug_remove(struct sync_file *sync_file)
473d0407baSopenharmony_ci{
483d0407baSopenharmony_ci    unsigned long flags;
493d0407baSopenharmony_ci
503d0407baSopenharmony_ci    spin_lock_irqsave(&sync_file_list_lock, flags);
513d0407baSopenharmony_ci    list_del(&sync_file->sync_file_list);
523d0407baSopenharmony_ci    spin_unlock_irqrestore(&sync_file_list_lock, flags);
533d0407baSopenharmony_ci}
543d0407baSopenharmony_ci
553d0407baSopenharmony_cistatic const char *sync_status_str(int status)
563d0407baSopenharmony_ci{
573d0407baSopenharmony_ci    if (status < 0) {
583d0407baSopenharmony_ci        return "error";
593d0407baSopenharmony_ci    }
603d0407baSopenharmony_ci
613d0407baSopenharmony_ci    if (status > 0) {
623d0407baSopenharmony_ci        return "signaled";
633d0407baSopenharmony_ci    }
643d0407baSopenharmony_ci
653d0407baSopenharmony_ci    return "active";
663d0407baSopenharmony_ci}
673d0407baSopenharmony_ci
683d0407baSopenharmony_cistatic void sync_print_fence(struct seq_file *s, struct dma_fence *fence, bool show)
693d0407baSopenharmony_ci{
703d0407baSopenharmony_ci    struct sync_timeline *parent = dma_fence_parent(fence);
713d0407baSopenharmony_ci    int status;
723d0407baSopenharmony_ci
733d0407baSopenharmony_ci    status = dma_fence_get_status_locked(fence);
743d0407baSopenharmony_ci
753d0407baSopenharmony_ci    seq_printf(s, "  %s%sfence %s", show ? parent->name : "", show ? "_" : "", sync_status_str(status));
763d0407baSopenharmony_ci
773d0407baSopenharmony_ci    if (test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) {
783d0407baSopenharmony_ci        struct timespec64 ts64 = ktime_to_timespec64(fence->timestamp);
793d0407baSopenharmony_ci
803d0407baSopenharmony_ci        seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
813d0407baSopenharmony_ci    }
823d0407baSopenharmony_ci
833d0407baSopenharmony_ci    if (fence->ops->timeline_value_str && fence->ops->fence_value_str) {
843d0407baSopenharmony_ci        char value[64];
853d0407baSopenharmony_ci        bool success;
863d0407baSopenharmony_ci
873d0407baSopenharmony_ci        fence->ops->fence_value_str(fence, value, sizeof(value));
883d0407baSopenharmony_ci        success = strlen(value);
893d0407baSopenharmony_ci        if (success) {
903d0407baSopenharmony_ci            seq_printf(s, ": %s", value);
913d0407baSopenharmony_ci
923d0407baSopenharmony_ci            fence->ops->timeline_value_str(fence, value, sizeof(value));
933d0407baSopenharmony_ci
943d0407baSopenharmony_ci            if (strlen(value)) {
953d0407baSopenharmony_ci                seq_printf(s, " / %s", value);
963d0407baSopenharmony_ci            }
973d0407baSopenharmony_ci        }
983d0407baSopenharmony_ci    }
993d0407baSopenharmony_ci
1003d0407baSopenharmony_ci    seq_putc(s, '\n');
1013d0407baSopenharmony_ci}
1023d0407baSopenharmony_ci
1033d0407baSopenharmony_cistatic void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
1043d0407baSopenharmony_ci{
1053d0407baSopenharmony_ci    struct list_head *pos;
1063d0407baSopenharmony_ci
1073d0407baSopenharmony_ci    seq_printf(s, "%s: %d\n", obj->name, obj->value);
1083d0407baSopenharmony_ci
1093d0407baSopenharmony_ci    spin_lock_irq(&obj->lock);
1103d0407baSopenharmony_ci    list_for_each(pos, &obj->pt_list)
1113d0407baSopenharmony_ci    {
1123d0407baSopenharmony_ci        struct sync_pt *pt = container_of(pos, struct sync_pt, link);
1133d0407baSopenharmony_ci        sync_print_fence(s, &pt->base, false);
1143d0407baSopenharmony_ci    }
1153d0407baSopenharmony_ci    spin_unlock_irq(&obj->lock);
1163d0407baSopenharmony_ci}
1173d0407baSopenharmony_ci
1183d0407baSopenharmony_cistatic void sync_print_sync_file(struct seq_file *s, struct sync_file *sync_file)
1193d0407baSopenharmony_ci{
1203d0407baSopenharmony_ci    char buf[128];
1213d0407baSopenharmony_ci    int i;
1223d0407baSopenharmony_ci
1233d0407baSopenharmony_ci    seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file_get_name(sync_file, buf, sizeof(buf)),
1243d0407baSopenharmony_ci               sync_status_str(dma_fence_get_status(sync_file->fence)));
1253d0407baSopenharmony_ci
1263d0407baSopenharmony_ci    if (dma_fence_is_array(sync_file->fence)) {
1273d0407baSopenharmony_ci        struct dma_fence_array *array = to_dma_fence_array(sync_file->fence);
1283d0407baSopenharmony_ci
1293d0407baSopenharmony_ci        for (i = 0; i < array->num_fences; ++i) {
1303d0407baSopenharmony_ci            sync_print_fence(s, array->fences[i], true);
1313d0407baSopenharmony_ci        }
1323d0407baSopenharmony_ci    } else {
1333d0407baSopenharmony_ci        sync_print_fence(s, sync_file->fence, true);
1343d0407baSopenharmony_ci    }
1353d0407baSopenharmony_ci}
1363d0407baSopenharmony_ci
1373d0407baSopenharmony_cistatic int sync_info_debugfs_show(struct seq_file *s, void *unused)
1383d0407baSopenharmony_ci{
1393d0407baSopenharmony_ci    struct list_head *pos;
1403d0407baSopenharmony_ci
1413d0407baSopenharmony_ci    seq_puts(s, "objs:\n--------------\n");
1423d0407baSopenharmony_ci
1433d0407baSopenharmony_ci    spin_lock_irq(&sync_timeline_list_lock);
1443d0407baSopenharmony_ci    list_for_each(pos, &sync_timeline_list_head)
1453d0407baSopenharmony_ci    {
1463d0407baSopenharmony_ci        struct sync_timeline *obj = container_of(pos, struct sync_timeline, sync_timeline_list);
1473d0407baSopenharmony_ci
1483d0407baSopenharmony_ci        sync_print_obj(s, obj);
1493d0407baSopenharmony_ci        seq_putc(s, '\n');
1503d0407baSopenharmony_ci    }
1513d0407baSopenharmony_ci    spin_unlock_irq(&sync_timeline_list_lock);
1523d0407baSopenharmony_ci
1533d0407baSopenharmony_ci    seq_puts(s, "fences:\n--------------\n");
1543d0407baSopenharmony_ci
1553d0407baSopenharmony_ci    spin_lock_irq(&sync_file_list_lock);
1563d0407baSopenharmony_ci    list_for_each(pos, &sync_file_list_head)
1573d0407baSopenharmony_ci    {
1583d0407baSopenharmony_ci        struct sync_file *sync_file = container_of(pos, struct sync_file, sync_file_list);
1593d0407baSopenharmony_ci
1603d0407baSopenharmony_ci        sync_print_sync_file(s, sync_file);
1613d0407baSopenharmony_ci        seq_putc(s, '\n');
1623d0407baSopenharmony_ci    }
1633d0407baSopenharmony_ci    spin_unlock_irq(&sync_file_list_lock);
1643d0407baSopenharmony_ci    return 0;
1653d0407baSopenharmony_ci}
1663d0407baSopenharmony_ci
1673d0407baSopenharmony_ciDEFINE_SHOW_ATTRIBUTE(sync_info_debugfs);
1683d0407baSopenharmony_ci
1693d0407baSopenharmony_cistatic __init int sync_debugfs_init(void)
1703d0407baSopenharmony_ci{
1713d0407baSopenharmony_ci    dbgfs = debugfs_create_dir("sync", NULL);
1723d0407baSopenharmony_ci
1733d0407baSopenharmony_ci    /*
1743d0407baSopenharmony_ci     * The debugfs files won't ever get removed and thus, there is
1753d0407baSopenharmony_ci     * no need to protect it against removal races. The use of
1763d0407baSopenharmony_ci     * debugfs_create_file_unsafe() is actually safe here.
1773d0407baSopenharmony_ci     */
1783d0407baSopenharmony_ci    debugfs_create_file_unsafe("info", 0444, dbgfs, NULL, &sync_info_debugfs_fops);
1793d0407baSopenharmony_ci    debugfs_create_file_unsafe("sw_sync", 0644, dbgfs, NULL, &sw_sync_debugfs_fops);
1803d0407baSopenharmony_ci
1813d0407baSopenharmony_ci    return 0;
1823d0407baSopenharmony_ci}
1833d0407baSopenharmony_cilate_initcall(sync_debugfs_init);
1843d0407baSopenharmony_ci#endif
185