mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* Re: [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends
       [not found] ` <dfe6e774883e6ef93cfaa2b6fe92b804061ab9d9.1737644530.git.jani.nikula@intel.com>
@ 2025-05-15 20:18   ` Bill Wendling
  2025-05-15 23:52     ` Bill Wendling
  2025-05-16  9:48     ` Jani Nikula
  0 siblings, 2 replies; 4+ messages in thread
From: Bill Wendling @ 2025-05-15 20:18 UTC (permalink / raw)
  To: Jani Nikula, dri-devel
  Cc: intel-gfx, gustavo.sousa, maarten.lankhorst, mripard,
	tzimmermann, airlied, simona, linux-kernel, kees

On 1/23/25 7:09 AM, Jani Nikula wrote:
> The expectation is that the struct drm_device based logging helpers get
> passed an actual struct drm_device pointer rather than some random
> struct pointer where you can dereference the ->dev member.
> 
> Add a static inline helper to convert struct drm_device to struct
> device, with the main benefit being the type checking of the macro
> argument.
> 
> As a side effect, this also reduces macro argument double references.
> 
> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> ---
>   include/drm/drm_print.h | 41 +++++++++++++++++++++++------------------
>   1 file changed, 23 insertions(+), 18 deletions(-)
> 
> diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
> index 9732f514566d..f31eba1c7cab 100644
> --- a/include/drm/drm_print.h
> +++ b/include/drm/drm_print.h
> @@ -584,9 +584,15 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
>    * Prefer drm_device based logging over device or prink based logging.
>    */
>   
> +/* Helper to enforce struct drm_device type */
> +static inline struct device *__drm_to_dev(const struct drm_device *drm)
> +{
> +	return drm ? drm->dev : NULL;
> +}
> +
>   /* Helper for struct drm_device based logging. */
>   #define __drm_printk(drm, level, type, fmt, ...)			\
> -	dev_##level##type((drm) ? (drm)->dev : NULL, "[drm] " fmt, ##__VA_ARGS__)
> +	dev_##level##type(__drm_to_dev(drm), "[drm] " fmt, ##__VA_ARGS__)
>   
>   
>   #define drm_info(drm, fmt, ...)					\
> @@ -620,25 +626,25 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
>   
>   
>   #define drm_dbg_core(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_CORE, fmt, ##__VA_ARGS__)
> -#define drm_dbg_driver(drm, fmt, ...)						\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__)
> +#define drm_dbg_driver(drm, fmt, ...)					\
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
>   #define drm_dbg_kms(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_KMS, fmt, ##__VA_ARGS__)
>   #define drm_dbg_prime(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_PRIME, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_PRIME, fmt, ##__VA_ARGS__)
>   #define drm_dbg_atomic(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
>   #define drm_dbg_vbl(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_VBL, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_VBL, fmt, ##__VA_ARGS__)
>   #define drm_dbg_state(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_STATE, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_STATE, fmt, ##__VA_ARGS__)
>   #define drm_dbg_lease(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_LEASE, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_LEASE, fmt, ##__VA_ARGS__)
>   #define drm_dbg_dp(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DP, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DP, fmt, ##__VA_ARGS__)
>   #define drm_dbg_drmres(drm, fmt, ...)					\
> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
>   
>   #define drm_dbg(drm, fmt, ...)	drm_dbg_driver(drm, fmt, ##__VA_ARGS__)
>   
> @@ -727,10 +733,9 @@ void __drm_err(const char *format, ...);
>   #define __DRM_DEFINE_DBG_RATELIMITED(category, drm, fmt, ...)					\
>   ({												\
>   	static DEFINE_RATELIMIT_STATE(rs_, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);\
> -	const struct drm_device *drm_ = (drm);							\
>   												\
>   	if (drm_debug_enabled(DRM_UT_ ## category) && __ratelimit(&rs_))			\
> -		drm_dev_printk(drm_ ? drm_->dev : NULL, KERN_DEBUG, fmt, ## __VA_ARGS__);	\
> +		drm_dev_printk(__drm_to_dev(drm), KERN_DEBUG, fmt, ## __VA_ARGS__);		\
>   })
>   
>   #define drm_dbg_ratelimited(drm, fmt, ...) \
> @@ -752,13 +757,13 @@ void __drm_err(const char *format, ...);
>   /* Helper for struct drm_device based WARNs */
>   #define drm_WARN(drm, condition, format, arg...)			\
>   	WARN(condition, "%s %s: [drm] " format,				\
> -			dev_driver_string((drm)->dev),			\
> -			dev_name((drm)->dev), ## arg)
> +			dev_driver_string(__drm_to_dev(drm)),		\
> +			dev_name(__drm_to_dev(drm)), ## arg)
>   
>   #define drm_WARN_ONCE(drm, condition, format, arg...)			\
>   	WARN_ONCE(condition, "%s %s: [drm] " format,			\
> -			dev_driver_string((drm)->dev),			\
> -			dev_name((drm)->dev), ## arg)
> +			dev_driver_string(__drm_to_dev(drm)),		\
> +			dev_name(__drm_to_dev(drm)), ## arg)
>
Hi Jani,

These two changes introduce undefined behavior into these macros. The final
code generation becomes this (from 'bxt_port_to_phy_channel'):

	__warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
		      dev_driver_string(__drm_to_dev(display->drm)),
		      dev_name(__drm_to_dev(display->drm)),
		      (port + 'A'));

The issue lies in 'dev_name(__drm_to_dev(display->drm))'. After inlining, it
becomes this (pseudo code):

	struct device *device = display->drm ? display->drm->dev : NULL;
	const char *name = device->init_name ? device->init_name
					     : kobject_name(&device->kobj);

	__warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
		      dev_driver_string(device), name, (port + 'A'));

The issue, of course, is that the 'device' may be NULL when attempting 
to get
'device->init_name'. The compiler sees this as undefined behavior, which may
lead to unexpected outcomes, especially with Clang where paths 
determined to be
undefined are removed entirely under certain conditions.

(Note, I'm working on making this behavior less draconian by adopting a GCC
pass, but this will take time to filter out to Linux devs.)

Regards,
-bw


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends
  2025-05-15 20:18   ` [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends Bill Wendling
@ 2025-05-15 23:52     ` Bill Wendling
  2025-05-16  9:48     ` Jani Nikula
  1 sibling, 0 replies; 4+ messages in thread
From: Bill Wendling @ 2025-05-15 23:52 UTC (permalink / raw)
  To: Jani Nikula, dri-devel
  Cc: intel-gfx, gustavo.sousa, maarten.lankhorst, mripard,
	tzimmermann, airlied, simona, linux-kernel, kees

On Thu, May 15, 2025 at 1:18 PM Bill Wendling <isanbard@gmail.com> wrote:
> On 1/23/25 7:09 AM, Jani Nikula wrote:
> > The expectation is that the struct drm_device based logging helpers get
> > passed an actual struct drm_device pointer rather than some random
> > struct pointer where you can dereference the ->dev member.
> >
> > Add a static inline helper to convert struct drm_device to struct
> > device, with the main benefit being the type checking of the macro
> > argument.
> >
> > As a side effect, this also reduces macro argument double references.
> >
> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> > ---
> >   include/drm/drm_print.h | 41 +++++++++++++++++++++++------------------
> >   1 file changed, 23 insertions(+), 18 deletions(-)
> >
> > diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
> > index 9732f514566d..f31eba1c7cab 100644
> > --- a/include/drm/drm_print.h
> > +++ b/include/drm/drm_print.h
> > @@ -584,9 +584,15 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
> >    * Prefer drm_device based logging over device or prink based logging.
> >    */
> >
> > +/* Helper to enforce struct drm_device type */
> > +static inline struct device *__drm_to_dev(const struct drm_device *drm)
> > +{
> > +     return drm ? drm->dev : NULL;
> > +}
> > +
> >   /* Helper for struct drm_device based logging. */
> >   #define __drm_printk(drm, level, type, fmt, ...)                    \
> > -     dev_##level##type((drm) ? (drm)->dev : NULL, "[drm] " fmt, ##__VA_ARGS__)
> > +     dev_##level##type(__drm_to_dev(drm), "[drm] " fmt, ##__VA_ARGS__)
> >
> >
> >   #define drm_info(drm, fmt, ...)                                     \
> > @@ -620,25 +626,25 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
> >
> >
> >   #define drm_dbg_core(drm, fmt, ...)                                 \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_CORE, fmt, ##__VA_ARGS__)
> > -#define drm_dbg_driver(drm, fmt, ...)                                                \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__)
> > +#define drm_dbg_driver(drm, fmt, ...)                                        \
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_kms(drm, fmt, ...)                                  \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_KMS, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_prime(drm, fmt, ...)                                        \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_PRIME, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_PRIME, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_atomic(drm, fmt, ...)                                       \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_vbl(drm, fmt, ...)                                  \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_VBL, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_VBL, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_state(drm, fmt, ...)                                        \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_STATE, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_STATE, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_lease(drm, fmt, ...)                                        \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_LEASE, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_LEASE, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_dp(drm, fmt, ...)                                   \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DP, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DP, fmt, ##__VA_ARGS__)
> >   #define drm_dbg_drmres(drm, fmt, ...)                                       \
> > -     drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
> > +     drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
> >
> >   #define drm_dbg(drm, fmt, ...)      drm_dbg_driver(drm, fmt, ##__VA_ARGS__)
> >
> > @@ -727,10 +733,9 @@ void __drm_err(const char *format, ...);
> >   #define __DRM_DEFINE_DBG_RATELIMITED(category, drm, fmt, ...)                                       \
> >   ({                                                                                          \
> >       static DEFINE_RATELIMIT_STATE(rs_, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);\
> > -     const struct drm_device *drm_ = (drm);                                                  \
> >                                                                                               \
> >       if (drm_debug_enabled(DRM_UT_ ## category) && __ratelimit(&rs_))                        \
> > -             drm_dev_printk(drm_ ? drm_->dev : NULL, KERN_DEBUG, fmt, ## __VA_ARGS__);       \
> > +             drm_dev_printk(__drm_to_dev(drm), KERN_DEBUG, fmt, ## __VA_ARGS__);             \
> >   })
> >
> >   #define drm_dbg_ratelimited(drm, fmt, ...) \
> > @@ -752,13 +757,13 @@ void __drm_err(const char *format, ...);
> >   /* Helper for struct drm_device based WARNs */
> >   #define drm_WARN(drm, condition, format, arg...)                    \
> >       WARN(condition, "%s %s: [drm] " format,                         \
> > -                     dev_driver_string((drm)->dev),                  \
> > -                     dev_name((drm)->dev), ## arg)
> > +                     dev_driver_string(__drm_to_dev(drm)),           \
> > +                     dev_name(__drm_to_dev(drm)), ## arg)
> >
> >   #define drm_WARN_ONCE(drm, condition, format, arg...)                       \
> >       WARN_ONCE(condition, "%s %s: [drm] " format,                    \
> > -                     dev_driver_string((drm)->dev),                  \
> > -                     dev_name((drm)->dev), ## arg)
> > +                     dev_driver_string(__drm_to_dev(drm)),           \
> > +                     dev_name(__drm_to_dev(drm)), ## arg)
> >
> Hi Jani,
>
> These two changes introduce undefined behavior into these macros. The final
> code generation becomes this (from 'bxt_port_to_phy_channel'):
>
>         __warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
>                       dev_driver_string(__drm_to_dev(display->drm)),
>                       dev_name(__drm_to_dev(display->drm)),
>                       (port + 'A'));
>
> The issue lies in 'dev_name(__drm_to_dev(display->drm))'. After inlining, it
> becomes this (pseudo code):
>
>         struct device *device = display->drm ? display->drm->dev : NULL;
>         const char *name = device->init_name ? device->init_name
>                                              : kobject_name(&device->kobj);
>
>         __warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
>                       dev_driver_string(device), name, (port + 'A'));
>
> The issue, of course, is that the 'device' may be NULL when attempting
> to get
> 'device->init_name'. The compiler sees this as undefined behavior, which may
> lead to unexpected outcomes, especially with Clang where paths
> determined to be
> undefined are removed entirely under certain conditions.
>
> (Note, I'm working on making this behavior less draconian by adopting a GCC
> pass, but this will take time to filter out to Linux devs.)
>
I potential fix for this would be something like this (untested). I'm
not familiar with how 'dev_name' is used to know whether or not this
could cause issues:

diff --git a/include/linux/device.h b/include/linux/device.h
index 79e49fe494b7..ea20d439fe8e 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -778,6 +778,9 @@ static inline bool device_iommu_mapped(struct device *dev)
  */
 static inline const char *dev_name(const struct device *dev)
 {
+       if (!dev)
+               return "default";
+
        /* Use the init name until the kobject becomes available */
        if (dev->init_name)
                return dev->init_name;

-bw

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends
  2025-05-15 20:18   ` [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends Bill Wendling
  2025-05-15 23:52     ` Bill Wendling
@ 2025-05-16  9:48     ` Jani Nikula
  2025-05-16 20:41       ` Bill Wendling
  1 sibling, 1 reply; 4+ messages in thread
From: Jani Nikula @ 2025-05-16  9:48 UTC (permalink / raw)
  To: Bill Wendling, dri-devel
  Cc: intel-gfx, gustavo.sousa, maarten.lankhorst, mripard,
	tzimmermann, airlied, simona, linux-kernel, kees

On Thu, 15 May 2025, Bill Wendling <isanbard@gmail.com> wrote:
> On 1/23/25 7:09 AM, Jani Nikula wrote:
>> The expectation is that the struct drm_device based logging helpers get
>> passed an actual struct drm_device pointer rather than some random
>> struct pointer where you can dereference the ->dev member.
>> 
>> Add a static inline helper to convert struct drm_device to struct
>> device, with the main benefit being the type checking of the macro
>> argument.
>> 
>> As a side effect, this also reduces macro argument double references.
>> 
>> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
>> ---
>>   include/drm/drm_print.h | 41 +++++++++++++++++++++++------------------
>>   1 file changed, 23 insertions(+), 18 deletions(-)
>> 
>> diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
>> index 9732f514566d..f31eba1c7cab 100644
>> --- a/include/drm/drm_print.h
>> +++ b/include/drm/drm_print.h
>> @@ -584,9 +584,15 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
>>    * Prefer drm_device based logging over device or prink based logging.
>>    */
>>   
>> +/* Helper to enforce struct drm_device type */
>> +static inline struct device *__drm_to_dev(const struct drm_device *drm)
>> +{
>> +	return drm ? drm->dev : NULL;
>> +}
>> +
>>   /* Helper for struct drm_device based logging. */
>>   #define __drm_printk(drm, level, type, fmt, ...)			\
>> -	dev_##level##type((drm) ? (drm)->dev : NULL, "[drm] " fmt, ##__VA_ARGS__)
>> +	dev_##level##type(__drm_to_dev(drm), "[drm] " fmt, ##__VA_ARGS__)
>>   
>>   
>>   #define drm_info(drm, fmt, ...)					\
>> @@ -620,25 +626,25 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
>>   
>>   
>>   #define drm_dbg_core(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_CORE, fmt, ##__VA_ARGS__)
>> -#define drm_dbg_driver(drm, fmt, ...)						\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__)
>> +#define drm_dbg_driver(drm, fmt, ...)					\
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_kms(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_KMS, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_prime(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_PRIME, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_PRIME, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_atomic(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_vbl(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_VBL, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_VBL, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_state(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_STATE, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_STATE, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_lease(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_LEASE, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_LEASE, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_dp(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DP, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DP, fmt, ##__VA_ARGS__)
>>   #define drm_dbg_drmres(drm, fmt, ...)					\
>> -	drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
>> +	drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
>>   
>>   #define drm_dbg(drm, fmt, ...)	drm_dbg_driver(drm, fmt, ##__VA_ARGS__)
>>   
>> @@ -727,10 +733,9 @@ void __drm_err(const char *format, ...);
>>   #define __DRM_DEFINE_DBG_RATELIMITED(category, drm, fmt, ...)					\
>>   ({												\
>>   	static DEFINE_RATELIMIT_STATE(rs_, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);\
>> -	const struct drm_device *drm_ = (drm);							\
>>   												\
>>   	if (drm_debug_enabled(DRM_UT_ ## category) && __ratelimit(&rs_))			\
>> -		drm_dev_printk(drm_ ? drm_->dev : NULL, KERN_DEBUG, fmt, ## __VA_ARGS__);	\
>> +		drm_dev_printk(__drm_to_dev(drm), KERN_DEBUG, fmt, ## __VA_ARGS__);		\
>>   })
>>   
>>   #define drm_dbg_ratelimited(drm, fmt, ...) \
>> @@ -752,13 +757,13 @@ void __drm_err(const char *format, ...);
>>   /* Helper for struct drm_device based WARNs */
>>   #define drm_WARN(drm, condition, format, arg...)			\
>>   	WARN(condition, "%s %s: [drm] " format,				\
>> -			dev_driver_string((drm)->dev),			\
>> -			dev_name((drm)->dev), ## arg)
>> +			dev_driver_string(__drm_to_dev(drm)),		\
>> +			dev_name(__drm_to_dev(drm)), ## arg)
>>   
>>   #define drm_WARN_ONCE(drm, condition, format, arg...)			\
>>   	WARN_ONCE(condition, "%s %s: [drm] " format,			\
>> -			dev_driver_string((drm)->dev),			\
>> -			dev_name((drm)->dev), ## arg)
>> +			dev_driver_string(__drm_to_dev(drm)),		\
>> +			dev_name(__drm_to_dev(drm)), ## arg)
>>
> Hi Jani,
>
> These two changes introduce undefined behavior into these macros. The final
> code generation becomes this (from 'bxt_port_to_phy_channel'):
>
> 	__warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
> 		      dev_driver_string(__drm_to_dev(display->drm)),
> 		      dev_name(__drm_to_dev(display->drm)),
> 		      (port + 'A'));
>
> The issue lies in 'dev_name(__drm_to_dev(display->drm))'. After inlining, it
> becomes this (pseudo code):
>
> 	struct device *device = display->drm ? display->drm->dev : NULL;
> 	const char *name = device->init_name ? device->init_name
> 					     : kobject_name(&device->kobj);
>
> 	__warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
> 		      dev_driver_string(device), name, (port + 'A'));
>
> The issue, of course, is that the 'device' may be NULL when attempting 
> to get
> 'device->init_name'. The compiler sees this as undefined behavior, which may
> lead to unexpected outcomes, especially with Clang where paths 
> determined to be
> undefined are removed entirely under certain conditions.

Would it be better to just revert the drm_WARN() and drm_WARN_ONCE()
macros to use (drm)->dev directly?

It's not ideal, but as the quick fix.

I don't think adding the check in dev_name() would go down well, as
there are roughly 5k users of it, and feels like unnecessary code size
bloat.


BR,
Jani.



>
> (Note, I'm working on making this behavior less draconian by adopting a GCC
> pass, but this will take time to filter out to Linux devs.)
>
> Regards,
> -bw
>

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends
  2025-05-16  9:48     ` Jani Nikula
@ 2025-05-16 20:41       ` Bill Wendling
  0 siblings, 0 replies; 4+ messages in thread
From: Bill Wendling @ 2025-05-16 20:41 UTC (permalink / raw)
  To: Jani Nikula
  Cc: dri-devel, intel-gfx, gustavo.sousa, maarten.lankhorst, mripard,
	tzimmermann, airlied, simona, linux-kernel, kees

On Fri, May 16, 2025 at 2:48 AM Jani Nikula <jani.nikula@intel.com> wrote:
> On Thu, 15 May 2025, Bill Wendling <isanbard@gmail.com> wrote:
> > On 1/23/25 7:09 AM, Jani Nikula wrote:
> >> The expectation is that the struct drm_device based logging helpers get
> >> passed an actual struct drm_device pointer rather than some random
> >> struct pointer where you can dereference the ->dev member.
> >>
> >> Add a static inline helper to convert struct drm_device to struct
> >> device, with the main benefit being the type checking of the macro
> >> argument.
> >>
> >> As a side effect, this also reduces macro argument double references.
> >>
> >> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> >> ---
> >>   include/drm/drm_print.h | 41 +++++++++++++++++++++++------------------
> >>   1 file changed, 23 insertions(+), 18 deletions(-)
> >>
> >> diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h
> >> index 9732f514566d..f31eba1c7cab 100644
> >> --- a/include/drm/drm_print.h
> >> +++ b/include/drm/drm_print.h
> >> @@ -584,9 +584,15 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
> >>    * Prefer drm_device based logging over device or prink based logging.
> >>    */
> >>
> >> +/* Helper to enforce struct drm_device type */
> >> +static inline struct device *__drm_to_dev(const struct drm_device *drm)
> >> +{
> >> +    return drm ? drm->dev : NULL;
> >> +}
> >> +
> >>   /* Helper for struct drm_device based logging. */
> >>   #define __drm_printk(drm, level, type, fmt, ...)                   \
> >> -    dev_##level##type((drm) ? (drm)->dev : NULL, "[drm] " fmt, ##__VA_ARGS__)
> >> +    dev_##level##type(__drm_to_dev(drm), "[drm] " fmt, ##__VA_ARGS__)
> >>
> >>
> >>   #define drm_info(drm, fmt, ...)                                    \
> >> @@ -620,25 +626,25 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
> >>
> >>
> >>   #define drm_dbg_core(drm, fmt, ...)                                        \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_CORE, fmt, ##__VA_ARGS__)
> >> -#define drm_dbg_driver(drm, fmt, ...)                                               \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__)
> >> +#define drm_dbg_driver(drm, fmt, ...)                                       \
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_kms(drm, fmt, ...)                                 \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_KMS, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_prime(drm, fmt, ...)                                       \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_PRIME, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_PRIME, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_atomic(drm, fmt, ...)                                      \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_vbl(drm, fmt, ...)                                 \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_VBL, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_VBL, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_state(drm, fmt, ...)                                       \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_STATE, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_STATE, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_lease(drm, fmt, ...)                                       \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_LEASE, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_LEASE, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_dp(drm, fmt, ...)                                  \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DP, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DP, fmt, ##__VA_ARGS__)
> >>   #define drm_dbg_drmres(drm, fmt, ...)                                      \
> >> -    drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
> >> +    drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
> >>
> >>   #define drm_dbg(drm, fmt, ...)     drm_dbg_driver(drm, fmt, ##__VA_ARGS__)
> >>
> >> @@ -727,10 +733,9 @@ void __drm_err(const char *format, ...);
> >>   #define __DRM_DEFINE_DBG_RATELIMITED(category, drm, fmt, ...)                                      \
> >>   ({                                                                                         \
> >>      static DEFINE_RATELIMIT_STATE(rs_, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);\
> >> -    const struct drm_device *drm_ = (drm);                                                  \
> >>                                                                                              \
> >>      if (drm_debug_enabled(DRM_UT_ ## category) && __ratelimit(&rs_))                        \
> >> -            drm_dev_printk(drm_ ? drm_->dev : NULL, KERN_DEBUG, fmt, ## __VA_ARGS__);       \
> >> +            drm_dev_printk(__drm_to_dev(drm), KERN_DEBUG, fmt, ## __VA_ARGS__);             \
> >>   })
> >>
> >>   #define drm_dbg_ratelimited(drm, fmt, ...) \
> >> @@ -752,13 +757,13 @@ void __drm_err(const char *format, ...);
> >>   /* Helper for struct drm_device based WARNs */
> >>   #define drm_WARN(drm, condition, format, arg...)                   \
> >>      WARN(condition, "%s %s: [drm] " format,                         \
> >> -                    dev_driver_string((drm)->dev),                  \
> >> -                    dev_name((drm)->dev), ## arg)
> >> +                    dev_driver_string(__drm_to_dev(drm)),           \
> >> +                    dev_name(__drm_to_dev(drm)), ## arg)
> >>
> >>   #define drm_WARN_ONCE(drm, condition, format, arg...)                      \
> >>      WARN_ONCE(condition, "%s %s: [drm] " format,                    \
> >> -                    dev_driver_string((drm)->dev),                  \
> >> -                    dev_name((drm)->dev), ## arg)
> >> +                    dev_driver_string(__drm_to_dev(drm)),           \
> >> +                    dev_name(__drm_to_dev(drm)), ## arg)
> >>
> > Hi Jani,
> >
> > These two changes introduce undefined behavior into these macros. The final
> > code generation becomes this (from 'bxt_port_to_phy_channel'):
> >
> >       __warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
> >                     dev_driver_string(__drm_to_dev(display->drm)),
> >                     dev_name(__drm_to_dev(display->drm)),
> >                     (port + 'A'));
> >
> > The issue lies in 'dev_name(__drm_to_dev(display->drm))'. After inlining, it
> > becomes this (pseudo code):
> >
> >       struct device *device = display->drm ? display->drm->dev : NULL;
> >       const char *name = device->init_name ? device->init_name
> >                                            : kobject_name(&device->kobj);
> >
> >       __warn_printk("%s %s: [drm] " "PHY not found for PORT %c",
> >                     dev_driver_string(device), name, (port + 'A'));
> >
> > The issue, of course, is that the 'device' may be NULL when attempting
> > to get
> > 'device->init_name'. The compiler sees this as undefined behavior, which may
> > lead to unexpected outcomes, especially with Clang where paths
> > determined to be
> > undefined are removed entirely under certain conditions.
>
> Would it be better to just revert the drm_WARN() and drm_WARN_ONCE()
> macros to use (drm)->dev directly?
>
> It's not ideal, but as the quick fix.
>
> I don't think adding the check in dev_name() would go down well, as
> there are roughly 5k users of it, and feels like unnecessary code size
> bloat.
>
I did a quick check and vmlinux size changed by only about 0.0078%.
Most modules didn't change size, some did increase, but typically less
than 1%, and a few actually shrank in size (??). The largest change
was 6.5840% : counter.ko.

Reverting the patches would probably work, but that relies upon 'drm'
never being NULL. Indeed, it looks like 'dev_driver_string' is wary of
a NULL 'drm', though it still accesses the argument as if it couldn't
be NULL... This all seems like a disaster waiting to happen, to be
honest. There should either be no way for 'drm' to be NULL or handling
for when it is. What happened with this series of patches is adding
only partial handling for when it is NULL.

-bw

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-05-16 20:42 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <cover.1737644530.git.jani.nikula@intel.com>
     [not found] ` <dfe6e774883e6ef93cfaa2b6fe92b804061ab9d9.1737644530.git.jani.nikula@intel.com>
2025-05-15 20:18   ` [PATCH 5/5] drm/print: require struct drm_device for drm_err() and friends Bill Wendling
2025-05-15 23:52     ` Bill Wendling
2025-05-16  9:48     ` Jani Nikula
2025-05-16 20:41       ` Bill Wendling

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®