lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 28 Oct 2022 09:26:34 +0300
From:   Gwan-gyeong Mun <gwan-gyeong.mun@...el.com>
To:     "Dixit, Ashutosh" <ashutosh.dixit@...el.com>,
        Nick Desaulniers <ndesaulniers@...gle.com>
CC:     <anshuman.gupta@...el.com>, <intel-gfx@...ts.freedesktop.org>,
        <llvm@...ts.linux.dev>, <linux-kernel@...r.kernel.org>,
        Andi Shyti <andi.shyti@...ux.intel.com>
Subject: Re: [PATCH] drm/i915/hwmon: Fix a build error used with clang
 compiler

Hi all,

I should have written the commit message more accurately, but it seems 
that it was written inaccurately.

If the FIELD_PREP macro is expanded, the following macros are used.

#define FIELD_PREP(_mask, _val)						\
	({								\
		__BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: ");	\
		((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);	\
	})


#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx)			\
	({								\
		BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask),		\
				 _pfx "mask is not constant");		\
		BUILD_BUG_ON_MSG((_mask) == 0, _pfx "mask is zero");	\
		BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ?		\
				 ~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \
				 _pfx "value too large for the field"); \
		BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) >	\
				 __bf_cast_unsigned(_reg, ~0ull),	\
				 _pfx "type of reg too small for mask"); \
		__BUILD_BUG_ON_NOT_POWER_OF_2((_mask) +			\
					      (1ULL << __bf_shf(_mask))); \
	})

Among them, a build error is generated by the lower part of the 
__BF_FIELD_CHECK() macro.

		BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) >	\
				 __bf_cast_unsigned(_reg, ~0ull),	\
				 _pfx "type of reg too small for mask"); \


Here, if you apply an argument to this macro, it will look like the 
following.

__bf_cast_unsigned(field_msk, field_msk) > __bf_cast_unsigned(0ULL, ~0ull)

The result is always false because an unsigned int value of type 
field_msk is not always greater than the maximum value of unsigned long 
long .
So, a build error occurs due to the following part of the clang compiler 
option.

[-Werror,-Wtautological-constant-out-of-range-compare]

You can simply override this warning in Clang by adding the build option 
below, but this seems like a bad attempt

i915/Makefile
CFLAGS_i915_hwmon.o += -Wno-tautological-constant-out-of-range-compare

The easiest way to solve this is to use a constant value, not a 
variable, as an argument to FIELD_PREP.

And since the REG_FIELD_PREP() macro suggested by Jani requires a const 
expression as the first argument, it cannot be changed with this macro 
alone in the existing code, it must be changed to input a constant value 
as shown below.

diff --git a/drivers/gpu/drm/i915/i915_hwmon.c 
b/drivers/gpu/drm/i915/i915_hwmon.c
index 08c921421a5f..abb3a194c548 100644
--- a/drivers/gpu/drm/i915/i915_hwmon.c
+++ b/drivers/gpu/drm/i915/i915_hwmon.c
@@ -101,7 +101,7 @@ hwm_field_read_and_scale(struct hwm_drvdata *ddat, 
i915_reg_t rgadr,

  static void
  hwm_field_scale_and_write(struct hwm_drvdata *ddat, i915_reg_t rgadr,
-                         const u32 field_msk, int nshift,
+                         int nshift,
                           unsigned int scale_factor, long lval)
  {
         u32 nval;
@@ -111,8 +111,8 @@ hwm_field_scale_and_write(struct hwm_drvdata *ddat, 
i915_reg_t rgadr,
         /* Computation in 64-bits to avoid overflow. Round to nearest. */
         nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);

-       bits_to_clear = field_msk;
-       bits_to_set = REG_FIELD_PREP(field_msk, nval);
+       bits_to_clear = PKG_PWR_LIM_1;
+       bits_to_set = REG_FIELD_PREP(PKG_PWR_LIM_1, nval);

         hwm_locked_with_pm_intel_uncore_rmw(ddat, rgadr,
                                             bits_to_clear, bits_to_set);
@@ -406,7 +406,6 @@ hwm_power_write(struct hwm_drvdata *ddat, u32 attr, 
int chan, long val)
         case hwmon_power_max:
                 hwm_field_scale_and_write(ddat,
                                           hwmon->rg.pkg_rapl_limit,
-                                         PKG_PWR_LIM_1,
                                           hwmon->scl_shift_power,
                                           SF_POWER, val);
                 return 0;



In addition, if there is no build problem regardless of the size of the 
type as the first argument in FIELD_PREP, it is possible through the 
following modification.
(Since this modification modifies include/linux/bitfield.h , I will send 
it as a separate patch.
   )

However, it seems that we need to have Jani's confirm whether it is okay 
to use FIELD_PREP() instead of REG_FIELD_PREP() which is forced to u32 
return type.

diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h
index c9be1657f03d..6e96799b6f38 100644
--- a/include/linux/bitfield.h
+++ b/include/linux/bitfield.h
@@ -9,7 +9,7 @@

  #include <linux/build_bug.h>
  #include <asm/byteorder.h>
-
+#include <linux/overflow.h>
  /*
   * Bitfield access macros
   *
@@ -69,7 +69,7 @@
                                  ~((_mask) >> __bf_shf(_mask)) & (_val) 
: 0, \
                                  _pfx "value too large for the field"); \
                 BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) >     \
-                                __bf_cast_unsigned(_reg, ~0ull),       \
+                                __bf_cast_unsigned(_reg, 
type_max(__unsigned_scalar_typeof(_reg))),    \
                                  _pfx "type of reg too small for mask"); \
                 __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) +                 \
                                               (1ULL << __bf_shf(_mask))); \
@@ -84,7 +84,7 @@
   */
  #define FIELD_MAX(_mask)                                               \
         ({                                                              \
-               __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_MAX: ");     \
+               __BF_FIELD_CHECK(_mask, 
type_min(__unsigned_scalar_typeof(_mask)), 
type_min(__unsigned_scalar_typeof(_mask)), "FIELD_MAX: ");   \
                 (typeof(_mask))((_mask) >> __bf_shf(_mask));            \
         })

@@ -97,7 +97,7 @@
   */
  #define FIELD_FIT(_mask, _val)                                         \
         ({                                                              \
-               __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_FIT: ");     \
+               __BF_FIELD_CHECK(_mask, 
type_min(__unsigned_scalar_typeof(_mask)), 
type_min(__unsigned_scalar_typeof(_val)), "FIELD_FIT: ");    \
                 !((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \
         })

@@ -111,7 +111,7 @@
   */
  #define FIELD_PREP(_mask, _val) 
          \
         ({                                                              \
-               __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: ");    \
+               __BF_FIELD_CHECK(_mask, 
type_min(__unsigned_scalar_typeof(_mask)), _val, "FIELD_PREP: ");       \
                 ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);   \
         })

@@ -125,7 +125,7 @@
   */
  #define FIELD_GET(_mask, _reg)                                         \
         ({                                                              \
-               __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: ");       \
+               __BF_FIELD_CHECK(_mask, _reg, 
type_min(__unsigned_scalar_typeof(_reg)), "FIELD_GET: "); \
                 (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
         })


Br,

G.G.


On 10/27/22 9:32 PM, Dixit, Ashutosh wrote:
> On Thu, 27 Oct 2022 10:16:47 -0700, Nick Desaulniers wrote:
>>
> 
> Hi Nick,
> 
>> Thanks, I can repro now.
>>
>> I haven't detangled the macro soup, but I noticed:
>>
>> 1. FIELD_PREP is defined in include/linux/bitfield.h which has the
>> following comment:
>>   18  * Mask must be a compilation time constant.
> 
> I had comments about this here:
> 
> https://lore.kernel.org/intel-gfx/87ilk7pwrw.wl-ashutosh.dixit@intel.com/
> 
> The relevant part being:
> 
> ---- {quote} ----
>>>> ./include/linux/bitfield.h:71:53: note: expanded from macro '__BF_FIELD_CHECK'
>>>>                  BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) >     \
> 
> So clang seems to break here at this line in __BF_FIELD_CHECK (note ~0ull
> also occurs here):
> 
> 		BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) >	\
> 				 __bf_cast_unsigned(_reg, ~0ull),	\
> 				 _pfx "type of reg too small for mask"); \
> 
> So it goes through previous checks including the "mask is not constant"
> check. As Nick Desaulniers mentions "__builtin_constant_p is evaluated
> after most optimizations have run" so by that time both compilers (gcc and
> clang) have figured out that even though _mask is coming in as function
> argument it is really the constant below:
> 
> #define   PKG_PWR_LIM_1		REG_GENMASK(14, 0)
> 
> But it is not clear why clang chokes on this "type of reg too small for
> mask" check (and gcc doesn't) since everything is u32.
> ---- {end quote} ----
> 
>>
>> 2. hwm_field_scale_and_write only has one callsite.
>>
>> The following patch works:
> 
> If we need to fix it at our end yes we can come up with one of these
> patches. But we were hoping someone from clang/llvm can comment about the
> "type of reg too small for mask" stuff. If this is something which needs to
> be fixed in clang/llvm we probably don't want to hide the issue.
> 
>>
>> ```
>> diff --git a/drivers/gpu/drm/i915/i915_hwmon.c
>> b/drivers/gpu/drm/i915/i915_hwmon.c
>> index 9e9781493025..6ac29d90b92a 100644
>> --- a/drivers/gpu/drm/i915/i915_hwmon.c
>> +++ b/drivers/gpu/drm/i915/i915_hwmon.c
>> @@ -101,7 +101,7 @@ hwm_field_read_and_scale(struct hwm_drvdata *ddat,
>> i915_reg_t rgadr,
>>
>>   static void
>>   hwm_field_scale_and_write(struct hwm_drvdata *ddat, i915_reg_t rgadr,
>> -                         u32 field_msk, int nshift,
>> +                         int nshift,
>>                            unsigned int scale_factor, long lval)
>>   {
>>          u32 nval;
>> @@ -111,8 +111,8 @@ hwm_field_scale_and_write(struct hwm_drvdata
>> *ddat, i915_reg_t rgadr,
>>          /* Computation in 64-bits to avoid overflow. Round to nearest. */
>>          nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);
>>
>> -       bits_to_clear = field_msk;
>> -       bits_to_set = FIELD_PREP(field_msk, nval);
>> +       bits_to_clear = PKG_PWR_LIM_1;
>> +       bits_to_set = FIELD_PREP(PKG_PWR_LIM_1, nval);
>>
>>          hwm_locked_with_pm_intel_uncore_rmw(ddat, rgadr,
>>                                              bits_to_clear, bits_to_set);
>> @@ -406,7 +406,6 @@ hwm_power_write(struct hwm_drvdata *ddat, u32
>> attr, int chan, long val)
>>          case hwmon_power_max:
>>                  hwm_field_scale_and_write(ddat,
>>                                            hwmon->rg.pkg_rapl_limit,
>> -                                         PKG_PWR_LIM_1,
>>                                            hwmon->scl_shift_power,
>>                                            SF_POWER, val);
>>                  return 0;
>> ```
>> Though I'm not sure if you're planning to add further callsites of
>> hwm_field_scale_and_write with different field_masks?
> 
> I have reasons for keeping it this way, it's there in the link above if you
> are interested.
> 
>>
>> Alternatively, (without the above diff),
>>
>> ```
>> diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h
>> index c9be1657f03d..6f40f12bcf89 100644
>> --- a/include/linux/bitfield.h
>> +++ b/include/linux/bitfield.h
>> @@ -8,6 +8,7 @@
>>   #define _LINUX_BITFIELD_H
>>
>>   #include <linux/build_bug.h>
>> +#include <linux/const.h>
>>   #include <asm/byteorder.h>
>>
>>   /*
>> @@ -62,7 +63,7 @@
>>
>>   #define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx)                      \
>>          ({                                                              \
>> -               BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask),          \
>> +               BUILD_BUG_ON_MSG(!__is_constexpr(_mask),                \
>>                                   _pfx "mask is not constant");          \
>>                  BUILD_BUG_ON_MSG((_mask) == 0, _pfx "mask is zero");    \
>>                  BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ?           \
>> ```
>> will produce:
>> error: call to __compiletime_assert_407 declared with 'error'
>> attribute: FIELD_PREP: mask is not constant
>>
>> I haven't tested if that change is also feasible (on top of fixing
>> this specific instance), but I think it might help avoid more of these
>> subtleties wrt. __builtin_constant_p that depende heavily on compiler,
>> compiler version, optimization level.
> 
> Not disagreeing, can do something here if needed.
> 
> Thanks.
> --
> Ashutosh

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ