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]

 



On 23.01.2025 13:41, Gustavo Sousa wrote:
> Quoting Vivekanandan, Balasubramani (2025-01-23 13:11:03-03:00)
> >On 17.01.2025 19:06, Gustavo Sousa wrote:
> >> 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, let's introduce a debugfs interface for the
> >> DMC wakelock.
> >> 
> >> In this part, we export a buffer containing offsets of registers that
> >> were considered not needing the wakelock by our driver. In an upcoming
> >> change we will also allow defining an extra set of offset ranges to be
> >> tracked by our driver.
> >> 
> >> Signed-off-by: Gustavo Sousa <gustavo.sousa@xxxxxxxxx>
> >> ---
> >>  drivers/gpu/drm/i915/Makefile                 |   1 +
> >>  .../drm/i915/display/intel_display_debugfs.c  |   2 +
> >>  drivers/gpu/drm/i915/display/intel_dmc_wl.c   |   5 +-
> >>  drivers/gpu/drm/i915/display/intel_dmc_wl.h   |   2 +
> >>  .../drm/i915/display/intel_dmc_wl_debugfs.c   | 251 ++++++++++++++++++
> >>  .../drm/i915/display/intel_dmc_wl_debugfs.h   |  29 ++
> >>  drivers/gpu/drm/xe/Makefile                   |   1 +
> >>  7 files changed, 290 insertions(+), 1 deletion(-)
> >>  create mode 100644 drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.c
> >>  create mode 100644 drivers/gpu/drm/i915/display/intel_dmc_wl_debugfs.h
> >> 
> >> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> >> index 3dda9f0eda82..ac1ab79de9c8 100644
> >> --- a/drivers/gpu/drm/i915/Makefile
> >> +++ b/drivers/gpu/drm/i915/Makefile
> >> @@ -251,6 +251,7 @@ i915-y += \
> >>          display/intel_display_wa.o \
> >>          display/intel_dmc.o \
> >>          display/intel_dmc_wl.o \
> >> +        display/intel_dmc_wl_debugfs.o \
> >>          display/intel_dpio_phy.o \
> >>          display/intel_dpll.o \
> >>          display/intel_dpll_mgr.o \
> >> diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
> >> index f1d76484025a..b032535f4830 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
> >> +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
> >> @@ -26,6 +26,7 @@
> >>  #include "intel_display_power_well.h"
> >>  #include "intel_display_types.h"
> >>  #include "intel_dmc.h"
> >> +#include "intel_dmc_wl_debugfs.h"
> >>  #include "intel_dp.h"
> >>  #include "intel_dp_link_training.h"
> >>  #include "intel_dp_mst.h"
> >> @@ -883,6 +884,7 @@ void intel_display_debugfs_register(struct drm_i915_private *i915)
> >>  
> >>          intel_bios_debugfs_register(display);
> >>          intel_cdclk_debugfs_register(display);
> >> +        intel_dmc_wl_debugfs_register(display);
> >>          intel_dmc_debugfs_register(display);
> >>          intel_dp_test_debugfs_register(display);
> >>          intel_fbc_debugfs_register(display);
> >> diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c
> >> index 330b43a72e08..3686d4e90167 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c
> >> +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c
> >> @@ -338,6 +338,7 @@ void intel_dmc_wl_init(struct intel_display *display)
> >>          spin_lock_init(&wl->lock);
> >>          refcount_set(&wl->refcount,
> >>                       display->params.enable_dmc_wl == ENABLE_DMC_WL_ALWAYS_LOCKED ? 1 : 0);
> >> +        intel_dmc_wl_debugfs_init(display);
> >>  }
> >>  
> >>  /* Must only be called as part of enabling dynamic DC states. */
> >> @@ -444,8 +445,10 @@ void intel_dmc_wl_get(struct intel_display *display, i915_reg_t reg)
> >>          spin_lock_irqsave(&wl->lock, flags);
> >>  
> >>          if (i915_mmio_reg_valid(reg) &&
> >> -            !intel_dmc_wl_check_range(display, reg, wl->dc_state))
> >> +            !intel_dmc_wl_check_range(display, reg, wl->dc_state)) {
> >> +                intel_dmc_wl_debugfs_log_untracked(display, i915_mmio_reg_offset(reg));
> >>                  goto out_unlock;
> >> +        }
> >>  
> >>          if (!wl->enabled) {
> >>                  if (!refcount_inc_not_zero(&wl->refcount))
> >> 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;
> >>  };
> >>  
> >>  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
> >This macro is not actually the size but the count of offsets stored in
> >the buffer. This is used as the array size for drmm_kmalloc_array call.
> >It makes sense to rename this macro as count
> 
> I think size doesn't really have to mean number of bytes, but, in this
> case, I agree, that "buffer size" could be easily thought that way.
> 
> However, couldn't "buffer count" be somewhat ambiguous? One might read
> as the number of buffers and then only later after reading the code that
> it would refer to the number of entries in a single buffer.
> 
> Between the two options, I think I would still go with "buffer size"...

I was thinking something like DEBUGFS_UNTRACKED_OFFSET_COUNT

Regards,
Bala

> 
> >
> >> +
> >> +/*
> >> + * 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 knob is setting the count of offsets to be stored and not the size.
> >I think this should be renamed to indicate it as count.
> >
> >> + *
> >> + * 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
> >
> >For me passing negative number doesn't look intuitive. Thinking do we
> >really need the case of passing default buffer size. We can allow just 0
> >to disable and any positive number to enable with the buffer size set as
> >value passed.
> 
> Well, this is already supported. The use of -1 is just a convenience,
> and the user is not required to use it.
> 
> The current interface doesn't provide a way of letting the user know the
> maximum value and I think it would be a bit overkill adding another
> debugfs file just for that purpose.
> 
> As such, I think -1 is a useful way of letting the user use the maximum
> size, specially when she doesn't have the up-to-date documentation in
> handy to know what value that is. That's certainly better than having
> the user finding the maximum via trial and error.
> 
> >
> >> + *
> >> + * 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;
> >Define a macro for this magic number
> 
> This is very local to this function and I guess the variable name
> already convey it's meaning?
> 
> >
> >> +        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);
> >As this code never gets called by an interrupt, we can use just the
> >spin_lock instead of spin_lock_irqsave. Same applies for all the places
> >where spin_lock/unlock_irqsave/irqrestore is used.
> 
> This code does get called in interrupt context. It is called by
> intel_dmc_wl_get(), which is called for most of display MMIO access
> functions, and that happens both in user and interrupt context.
> 
> >
> >> +
> >> +        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;
> >There is no need of both len and size. head will always give the count
> >of entries in the buffer. During overflow, we are keeping a flag to
> >indicate a overflow, which indicates the we also have date in the buffer
> >above head till the end of buffer.
> 
> Ah, right. If overflow==false, then the length equals head, if
> overflow==true, then length equals size.
> 
> Maybe the inconvenience is that we will need to calculate the length
> every time we need it. But maybe that's not too bad...
> 
> Thanks! I'll remove the "len" member in v2.
> 
> --
> Gustavo Sousa
> 
> >
> >Regards,
> >Bala
> >
> >> +                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 \
> >> -- 
> >> 2.48.0
> >>



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

  Powered by Linux