Re: [PATCH 2/4] drm/i915/dmc_wl: Add debugfs for untracked offsets

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Quoting Jani Nikula (2025-01-27 08:59:11-03:00)
>On Mon, 27 Jan 2025, Gustavo Sousa <gustavo.sousa@xxxxxxxxx> wrote:
>> Quoting Jani Nikula (2025-01-27 06:47:58-03:00)
>>>On Fri, 17 Jan 2025, Gustavo Sousa <gustavo.sousa@xxxxxxxxx> wrote:
>>>> diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.h b/drivers/gpu/drm/i915/display/intel_dmc_wl.h
>>>> index 5488fbdf29b8..d11b0ab50b3c 100644
>>>> --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.h
>>>> +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.h
>>>> @@ -11,6 +11,7 @@
>>>>  #include <linux/refcount.h>
>>>>  
>>>>  #include "i915_reg_defs.h"
>>>> +#include "intel_dmc_wl_debugfs.h"
>>>>  
>>>>  struct intel_display;
>>>>  
>>>> @@ -27,6 +28,7 @@ struct intel_dmc_wl {
>>>>           */
>>>>          u32 dc_state;
>>>>          struct delayed_work work;
>>>> +        struct intel_dmc_wl_dbg dbg;
>>>>  };
>>>>  
>>>
>>>With intel_de.h including intel_dmc_wl.h, we'll have almost all of
>>>display include intel_dmc_wl_debugfs.h, and getting the definition of
>>>struct intel_dmc_wl_dbg, really for no good reason.
>>>
>>>I really like to flip this around. You need to have a *good reason* to
>>>expose stuff to the entire display driver all of a sudden. Instead of
>>>requiring a good reason to hide stuff.
>>
>> Maybe make dbg a pointer and have only intel_dmc_wl.c knowing its guts?
>>
>> Or do you have some other suggestion?
>
>Yes, using an opaque pointer is usually the way to go.

Okay. I was hoping not to have to have a separate dynamic memory
allocation for it, but that works and helps isolating the definition.

>
>Lately we've been adding debugfs next to the implementation. Often the
>debugfs needs access to the same stuff as the implementation, you can
>hide stuff and not have to expose a ton of interfaces. This could work
>here too... but I have to say this debugfs looks a bit, uh, bloated for
>want of a better word. Maybe having the separate file is worth it.

Yep, I do think it deserves a separate file.

--
Gustavo Sousa

>
>BR,
>Jani.
>
>
>>
>> --
>> Gustavo Sousa
>>
>>>
>>>BR,
>>>Jani.
>>>
>>>
>>>
>>>>  void intel_dmc_wl_init(struct intel_display *display);
>>>> diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.c b/drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.c
>>>> new file mode 100644
>>>> index 000000000000..41e59d775fe5
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.c
>>>> @@ -0,0 +1,251 @@
>>>> +// SPDX-License-Identifier: MIT
>>>> +/*
>>>> + * Copyright (C) 2025 Intel Corporation
>>>> + */
>>>> +
>>>> +#include <linux/debugfs.h>
>>>> +
>>>> +#include <drm/drm_device.h>
>>>> +#include <drm/drm_managed.h>
>>>> +#include <drm/drm_print.h>
>>>> +
>>>> +#include "intel_display_core.h"
>>>> +#include "intel_dmc_wl_debugfs.h"
>>>> +
>>>> +#define DEBUGFS_UNTRACKED_BUFFER_SIZE_MAX 65536
>>>> +
>>>> +/*
>>>> + * DOC: DMC wakelock debugfs
>>>> + *
>>>> + * The DMC wakelock code needs to keep track of register offsets that need the
>>>> + * wakelock for proper access. If one of the necessary offsets are missed, then
>>>> + * the failure in asserting the wakelock is very likely to cause problems down
>>>> + * the road.
>>>> + *
>>>> + * A miss could happen for at least two different reasons:
>>>> + *
>>>> + * - We might have forgotten to add the offset (or range) to the relevant
>>>> + *   tables tracked by the driver in the first place.
>>>> + *
>>>> + * - Or updates to either the DMC firmware or the display IP that require new
>>>> + *   offsets to be tracked and we fail to realize that.
>>>> + *
>>>> + * To help capture these cases, we provide the intel_dmc_wl/ debugfs directory,
>>>> + * which exports a buffer of untracked register offsets.
>>>> + *
>>>> + * Untracked offsets
>>>> + * -----------------
>>>> + *
>>>> + * This is a buffer that records every register offset that went through the
>>>> + * DMC wakelock check and was deemed not needing the wakelock for MMIO access.
>>>> + *
>>>> + * To activate the logging of offsets into such a buffer, one can do::
>>>> + *
>>>> + *   # echo -1 > /sys/kernel/debug/dri/(...)/intel_dmc_wl/untracked_size
>>>> + *
>>>> + * This will create a buffer with the maximum number of entries allowed
>>>> + * (DEBUGFS_UNTRACKED_BUFFER_SIZE_MAX). A positive value can be used instead to
>>>> + * define a different size:
>>>> + *
>>>> + *   # echo 1024 > /sys/kernel/debug/dri/(...)/intel_dmc_wl/untracked_size
>>>> + *
>>>> + * Every write to untracked_size will cause the buffer to be reset.
>>>> + *
>>>> + * It is also possible to read untracked_size in order to get the current
>>>> + * value.
>>>> + *
>>>> + * After enabled, the buffer starts getting filled with offsets as MMIOs are
>>>> + * performed by the driver.
>>>> + *
>>>> + * In order to view the content of the buffer, one can do::
>>>> + *
>>>> + *   # cat /sys/kernel/debug/dri/(...)/intel_dmc_wl/untracked
>>>> + *   0x000c4000
>>>> + *   0x0016fe50
>>>> + *   0x000c7200
>>>> + *   0x000c7204
>>>> + *   0x00045230
>>>> + *   0x00046440
>>>> + *   0x00045234
>>>> + *   0x0016fa48
>>>> + *   0x0016fa40
>>>> + *   0x0016fa5c
>>>> + *   (...)
>>>> + *
>>>> + * The order of those offsets does not reflect the order the checks were done
>>>> + * (some recently seen offsets are skipped to save space).
>>>> + *
>>>> + * Once done with it, the logging can be disabled with::
>>>> + *
>>>> + *   # echo 0 > /sys/kernel/debug/dri/(...)/intel_dmc_wl/untracked_size
>>>> + */
>>>> +
>>>> +static int untracked_size_get(void *data, u64 *val)
>>>> +{
>>>> +        struct intel_display *display = data;
>>>> +        struct intel_dmc_wl_dbg *dbg = &display->wl.dbg;
>>>> +        unsigned long flags;
>>>> +
>>>> +        spin_lock_irqsave(&dbg->lock, flags);
>>>> +        *val = dbg->untracked.size;
>>>> +        spin_unlock_irqrestore(&dbg->lock, flags);
>>>> +
>>>> +        return 0;
>>>> +}
>>>> +
>>>> +static int untracked_size_set(void *data, u64 val)
>>>> +{
>>>> +        struct intel_display *display = data;
>>>> +        struct intel_dmc_wl_dbg *dbg = &display->wl.dbg;
>>>> +        s64 new_size;
>>>> +        u32 *old_offsets;
>>>> +        u32 *new_offsets;
>>>> +        unsigned long flags;
>>>> +
>>>> +        new_size = (s64)val;
>>>> +
>>>> +        if (new_size == -1) {
>>>> +                new_size = DEBUGFS_UNTRACKED_BUFFER_SIZE_MAX;
>>>> +        } else if (new_size < 0) {
>>>> +                drm_err(display->drm,
>>>> +                        "%lld is invalid for untracked_size, the only negative value allowed is -1\n",
>>>> +                        new_size);
>>>> +                return -EINVAL;
>>>> +        } else if (new_size > DEBUGFS_UNTRACKED_BUFFER_SIZE_MAX) {
>>>> +                drm_err(display->drm,
>>>> +                        "%lld too big for untracked_size, maximum allowed value is %d\n",
>>>> +                        new_size,
>>>> +                        DEBUGFS_UNTRACKED_BUFFER_SIZE_MAX);
>>>> +                return -EINVAL;
>>>> +        }
>>>> +
>>>> +        if (new_size == 0) {
>>>> +                new_offsets = NULL;
>>>> +        } else {
>>>> +                new_offsets = drmm_kmalloc_array(display->drm, new_size, sizeof(*new_offsets),
>>>> +                                                 GFP_KERNEL);
>>>> +
>>>> +                if (!new_offsets)
>>>> +                        return -ENOMEM;
>>>> +        }
>>>> +
>>>> +        spin_lock_irqsave(&dbg->lock, flags);
>>>> +        old_offsets = dbg->untracked.offsets;
>>>> +        dbg->untracked.offsets = new_offsets;
>>>> +        dbg->untracked.size = new_size;
>>>> +        dbg->untracked.head = 0;
>>>> +        dbg->untracked.len = 0;
>>>> +        dbg->untracked.overflow = false;
>>>> +        spin_unlock_irqrestore(&dbg->lock, flags);
>>>> +
>>>> +        if (old_offsets)
>>>> +                drmm_kfree(display->drm, old_offsets);
>>>> +
>>>> +        return 0;
>>>> +}
>>>> +
>>>> +DEFINE_SIMPLE_ATTRIBUTE_SIGNED(untracked_size_fops,
>>>> +                               untracked_size_get,
>>>> +                               untracked_size_set,
>>>> +                               "%lld\n");
>>>> +
>>>> +static int untracked_show(struct seq_file *m, void *data)
>>>> +{
>>>> +        struct intel_display *display = m->private;
>>>> +        struct intel_dmc_wl_dbg *dbg = &display->wl.dbg;
>>>> +        unsigned long flags;
>>>> +        size_t remaining;
>>>> +        size_t i;
>>>> +
>>>> +        spin_lock_irqsave(&dbg->lock, flags);
>>>> +
>>>> +        remaining = dbg->untracked.len;
>>>> +        i = dbg->untracked.head;
>>>> +
>>>> +        while (remaining--) {
>>>> +                if (i == 0)
>>>> +                        i = dbg->untracked.size;
>>>> +
>>>> +                seq_printf(m, "0x%08x\n", dbg->untracked.offsets[--i]);
>>>> +        }
>>>> +
>>>> +        spin_unlock_irqrestore(&dbg->lock, flags);
>>>> +
>>>> +        return 0;
>>>> +}
>>>> +
>>>> +DEFINE_SHOW_ATTRIBUTE(untracked);
>>>> +
>>>> +void intel_dmc_wl_debugfs_init(struct intel_display *display)
>>>> +{
>>>> +        struct intel_dmc_wl_dbg *dbg = &display->wl.dbg;
>>>> +
>>>> +        spin_lock_init(&dbg->lock);
>>>> +}
>>>> +
>>>> +void intel_dmc_wl_debugfs_register(struct intel_display *display)
>>>> +{
>>>> +        struct dentry *dir;
>>>> +
>>>> +        if (!HAS_DMC_WAKELOCK(display))
>>>> +                return;
>>>> +
>>>> +        dir = debugfs_create_dir("intel_dmc_wl", display->drm->debugfs_root);
>>>> +        if (IS_ERR(dir))
>>>> +                return;
>>>> +
>>>> +        debugfs_create_file("untracked_size", 0644, dir, display,
>>>> +                            &untracked_size_fops);
>>>> +        debugfs_create_file("untracked", 0644, dir, display,
>>>> +                            &untracked_fops);
>>>> +}
>>>> +
>>>> +static bool untracked_has_recent_offset(struct intel_display *display, u32 offset)
>>>> +{
>>>> +        struct intel_dmc_wl_dbg *dbg = &display->wl.dbg;
>>>> +        int look_back = 32;
>>>> +        size_t i;
>>>> +
>>>> +        if (look_back > dbg->untracked.len)
>>>> +                look_back = dbg->untracked.len;
>>>> +
>>>> +        i = dbg->untracked.head;
>>>> +
>>>> +        while (look_back--) {
>>>> +                if (i == 0)
>>>> +                        i = dbg->untracked.size;
>>>> +
>>>> +                if (dbg->untracked.offsets[--i] == offset)
>>>> +                        return true;
>>>> +        }
>>>> +
>>>> +        return false;
>>>> +}
>>>> +
>>>> +void intel_dmc_wl_debugfs_log_untracked(struct intel_display *display, u32 offset)
>>>> +{
>>>> +        struct intel_dmc_wl_dbg *dbg = &display->wl.dbg;
>>>> +        unsigned long flags;
>>>> +
>>>> +        spin_lock_irqsave(&dbg->lock, flags);
>>>> +
>>>> +        if (!dbg->untracked.size)
>>>> +                goto out_unlock;
>>>> +
>>>> +        /* Save some space by not repeating recent offsets. */
>>>> +        if (untracked_has_recent_offset(display, offset))
>>>> +                goto out_unlock;
>>>> +
>>>> +        dbg->untracked.offsets[dbg->untracked.head] = offset;
>>>> +        dbg->untracked.head = (dbg->untracked.head + 1) % dbg->untracked.size;
>>>> +        if (dbg->untracked.len < dbg->untracked.size)
>>>> +                dbg->untracked.len++;
>>>> +
>>>> +        if (dbg->untracked.len == dbg->untracked.size && !dbg->untracked.overflow) {
>>>> +                dbg->untracked.overflow = true;
>>>> +                drm_warn(display->drm, "Overflow detected in DMC wakelock debugfs untracked offsets\n");
>>>> +        }
>>>> +
>>>> +out_unlock:
>>>> +        spin_unlock_irqrestore(&dbg->lock, flags);
>>>> +}
>>>> diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.h b/drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.h
>>>> new file mode 100644
>>>> index 000000000000..9437c324966f
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.h
>>>> @@ -0,0 +1,29 @@
>>>> +/* SPDX-License-Identifier: MIT */
>>>> +/*
>>>> + * Copyright (C) 2025 Intel Corporation
>>>> + */
>>>> +
>>>> +#ifndef __INTEL_DMC_WL_DEBUGFS_H__
>>>> +#define __INTEL_DMC_WL_DEBUGFS_H__
>>>> +
>>>> +#include <linux/types.h>
>>>> +#include <linux/spinlock.h>
>>>> +
>>>> +struct intel_display;
>>>> +
>>>> +struct intel_dmc_wl_dbg {
>>>> +        spinlock_t lock; /* protects everything below */
>>>> +        struct {
>>>> +                u32 *offsets;
>>>> +                size_t head;
>>>> +                size_t len;
>>>> +                size_t size;
>>>> +                bool overflow;
>>>> +        } untracked;
>>>> +};
>>>> +
>>>> +void intel_dmc_wl_debugfs_init(struct intel_display *display);
>>>> +void intel_dmc_wl_debugfs_register(struct intel_display *display);
>>>> +void intel_dmc_wl_debugfs_log_untracked(struct intel_display *display, u32 offset);
>>>> +
>>>> +#endif /* __INTEL_DMC_WL_DEBUGFS_H__ */
>>>> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
>>>> index 81f63258a7e1..f03fbdbcb1a4 100644
>>>> --- a/drivers/gpu/drm/xe/Makefile
>>>> +++ b/drivers/gpu/drm/xe/Makefile
>>>> @@ -221,6 +221,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
>>>>          i915-display/intel_display_wa.o \
>>>>          i915-display/intel_dkl_phy.o \
>>>>          i915-display/intel_dmc.o \
>>>> +        i915-display/intel_dmc_wl_debugfs.o \
>>>>          i915-display/intel_dp.o \
>>>>          i915-display/intel_dp_aux.o \
>>>>          i915-display/intel_dp_aux_backlight.o \
>>>
>>>-- 
>>>Jani Nikula, Intel
>
>-- 
>Jani Nikula, Intel




[Index of Archives]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux