diff mbox series

bug: Fix no-return-statement warning with !CONFIG_BUG

Message ID 20240410153212.127477-1-adrian.hunter@intel.com (mailing list archive)
State New
Headers show
Series bug: Fix no-return-statement warning with !CONFIG_BUG | expand

Checks

Context Check Description
snowpatch_ozlabs/github-powerpc_sparse success Successfully ran 4 jobs.
snowpatch_ozlabs/github-powerpc_clang success Successfully ran 6 jobs.
snowpatch_ozlabs/github-powerpc_kernel_qemu success Successfully ran 23 jobs.

Commit Message

Adrian Hunter April 10, 2024, 3:32 p.m. UTC
BUG() does not return, and arch implementations of BUG() use unreachable()
or other non-returning code. However with !CONFIG_BUG, the default
implementation is often used instead, and that does not do that. x86 always
uses its own implementation, but powerpc with !CONFIG_BUG gives a build
error:

  kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
  kernel/time/timekeeping.c:286:1: error: no return statement in function
  returning non-void [-Werror=return-type]

Add unreachable() to default !CONFIG_BUG BUG() implementation.

Fixes: e8e9d21a5df6 ("timekeeping: Refactor timekeeping helpers")
Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Closes: https://lore.kernel.org/all/CA+G9fYvjdZCW=7ZGxS6A_3bysjQ56YF7S-+PNLQ_8a4DKh1Bhg@mail.gmail.com/
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 include/asm-generic/bug.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Comments

Naresh Kamboju April 10, 2024, 5:02 p.m. UTC | #1
On Wed, 10 Apr 2024 at 21:02, Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> BUG() does not return, and arch implementations of BUG() use unreachable()
> or other non-returning code. However with !CONFIG_BUG, the default
> implementation is often used instead, and that does not do that. x86 always
> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
> error:
>
>   kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>   kernel/time/timekeeping.c:286:1: error: no return statement in function
>   returning non-void [-Werror=return-type]
>
> Add unreachable() to default !CONFIG_BUG BUG() implementation.
>
> Fixes: e8e9d21a5df6 ("timekeeping: Refactor timekeeping helpers")
> Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
> Closes: https://lore.kernel.org/all/CA+G9fYvjdZCW=7ZGxS6A_3bysjQ56YF7S-+PNLQ_8a4DKh1Bhg@mail.gmail.com/
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>

This patch applied on top of today's Linux next-20240410 tag and
build test pass.

Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>

> ---
>  include/asm-generic/bug.h | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)

--
Linaro LKFT
https://lkft.linaro.org
Arnd Bergmann April 11, 2024, 7:04 a.m. UTC | #2
On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
> BUG() does not return, and arch implementations of BUG() use unreachable()
> or other non-returning code. However with !CONFIG_BUG, the default
> implementation is often used instead, and that does not do that. x86 always
> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
> error:
>
>   kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>   kernel/time/timekeeping.c:286:1: error: no return statement in function
>   returning non-void [-Werror=return-type]
>
> Add unreachable() to default !CONFIG_BUG BUG() implementation.

I'm a bit worried about this patch, since we have had problems
with unreachable() inside of BUG() in the past, and as far as I
can remember, the current version was the only one that
actually did the right thing on all compilers.

One problem with an unreachable() annotation here is that if
a compiler misanalyses the endless loop, it can decide to
throw out the entire code path leading up to it and just
run into undefined behavior instead of printing a BUG()
message.

Do you know which compiler version show the warning above?

     Arnd
Adrian Hunter April 11, 2024, 7:16 a.m. UTC | #3
On 11/04/24 10:04, Arnd Bergmann wrote:
> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
>> BUG() does not return, and arch implementations of BUG() use unreachable()
>> or other non-returning code. However with !CONFIG_BUG, the default
>> implementation is often used instead, and that does not do that. x86 always
>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
>> error:
>>
>>   kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>>   kernel/time/timekeeping.c:286:1: error: no return statement in function
>>   returning non-void [-Werror=return-type]
>>
>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
> 
> I'm a bit worried about this patch, since we have had problems
> with unreachable() inside of BUG() in the past, and as far as I
> can remember, the current version was the only one that
> actually did the right thing on all compilers.
> 
> One problem with an unreachable() annotation here is that if
> a compiler misanalyses the endless loop, it can decide to
> throw out the entire code path leading up to it and just
> run into undefined behavior instead of printing a BUG()
> message.
> 
> Do you know which compiler version show the warning above?

Original report has a list

	https://lore.kernel.org/all/CA+G9fYvjdZCW=7ZGxS6A_3bysjQ56YF7S-+PNLQ_8a4DKh1Bhg@mail.gmail.com/
Arnd Bergmann April 11, 2024, 7:56 a.m. UTC | #4
On Thu, Apr 11, 2024, at 09:16, Adrian Hunter wrote:
> On 11/04/24 10:04, Arnd Bergmann wrote:
>> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
>>> BUG() does not return, and arch implementations of BUG() use unreachable()
>>> or other non-returning code. However with !CONFIG_BUG, the default
>>> implementation is often used instead, and that does not do that. x86 always
>>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
>>> error:
>>>
>>>   kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>>>   kernel/time/timekeeping.c:286:1: error: no return statement in function
>>>   returning non-void [-Werror=return-type]
>>>
>>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
>> 
>> I'm a bit worried about this patch, since we have had problems
>> with unreachable() inside of BUG() in the past, and as far as I
>> can remember, the current version was the only one that
>> actually did the right thing on all compilers.
>> 
>> One problem with an unreachable() annotation here is that if
>> a compiler misanalyses the endless loop, it can decide to
>> throw out the entire code path leading up to it and just
>> run into undefined behavior instead of printing a BUG()
>> message.
>> 
>> Do you know which compiler version show the warning above?
>
> Original report has a list
>

It looks like it's all versions of gcc, though no versions
of clang show the warnings. I did a few more tests and could
not find any differences on actual code generation, but
I'd still feel more comfortable changing the caller than
the BUG() macro. It's trivial to add a 'return 0' there.

Another interesting observation is that clang-11 and earlier
versions end up skipping the endless loop, both with and
without the __builtin_unreachable, see
https://godbolt.org/z/aqa9zqz8x

clang-12 and above do work like gcc, so I guess that is good.

     Arnd
Christophe Leroy April 11, 2024, 8:13 a.m. UTC | #5
Le 11/04/2024 à 09:16, Adrian Hunter a écrit :
> On 11/04/24 10:04, Arnd Bergmann wrote:
>> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
>>> BUG() does not return, and arch implementations of BUG() use unreachable()
>>> or other non-returning code. However with !CONFIG_BUG, the default
>>> implementation is often used instead, and that does not do that. x86 always
>>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
>>> error:
>>>
>>>    kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>>>    kernel/time/timekeeping.c:286:1: error: no return statement in function
>>>    returning non-void [-Werror=return-type]
>>>
>>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
>>
>> I'm a bit worried about this patch, since we have had problems
>> with unreachable() inside of BUG() in the past, and as far as I
>> can remember, the current version was the only one that
>> actually did the right thing on all compilers.
>>
>> One problem with an unreachable() annotation here is that if
>> a compiler misanalyses the endless loop, it can decide to
>> throw out the entire code path leading up to it and just
>> run into undefined behavior instead of printing a BUG()
>> message.
>>
>> Do you know which compiler version show the warning above?
> 
> Original report has a list
> 
> 	https://lore.kernel.org/all/CA+G9fYvjdZCW=7ZGxS6A_3bysjQ56YF7S-+PNLQ_8a4DKh1Bhg@mail.gmail.com/
> 

Looking at the report, I think the correct fix should be to use 
BUILD_BUG() instead of BUG()
Christophe Leroy April 11, 2024, 8:22 a.m. UTC | #6
Le 11/04/2024 à 10:12, Christophe Leroy a écrit :
> 
> 
> Le 11/04/2024 à 09:16, Adrian Hunter a écrit :
>> On 11/04/24 10:04, Arnd Bergmann wrote:
>>> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
>>>> BUG() does not return, and arch implementations of BUG() use 
>>>> unreachable()
>>>> or other non-returning code. However with !CONFIG_BUG, the default
>>>> implementation is often used instead, and that does not do that. x86 
>>>> always
>>>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
>>>> error:
>>>>
>>>>    kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>>>>    kernel/time/timekeeping.c:286:1: error: no return statement in 
>>>> function
>>>>    returning non-void [-Werror=return-type]
>>>>
>>>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
>>>
>>> I'm a bit worried about this patch, since we have had problems
>>> with unreachable() inside of BUG() in the past, and as far as I
>>> can remember, the current version was the only one that
>>> actually did the right thing on all compilers.
>>>
>>> One problem with an unreachable() annotation here is that if
>>> a compiler misanalyses the endless loop, it can decide to
>>> throw out the entire code path leading up to it and just
>>> run into undefined behavior instead of printing a BUG()
>>> message.
>>>
>>> Do you know which compiler version show the warning above?
>>
>> Original report has a list
>>
>>     https://lore.kernel.org/all/CA+G9fYvjdZCW=7ZGxS6A_3bysjQ56YF7S-+PNLQ_8a4DKh1Bhg@mail.gmail.com/
>>
> 
> Looking at the report, I think the correct fix should be to use 
> BUILD_BUG() instead of BUG()

I confirm the error goes away with the following change to next-20240411 
on powerpc tinyconfig with gcc 13.2

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 4e18db1819f8..3d5ac0cdd721 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -282,7 +282,7 @@ static inline void timekeeping_check_update(struct 
timekeeper *tk, u64 offset)
  }
  static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
  {
-	BUG();
+	BUILD_BUG();
  }
  #endif
Adrian Hunter April 11, 2024, 9:03 a.m. UTC | #7
On 11/04/24 10:56, Arnd Bergmann wrote:
> On Thu, Apr 11, 2024, at 09:16, Adrian Hunter wrote:
>> On 11/04/24 10:04, Arnd Bergmann wrote:
>>> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
>>>> BUG() does not return, and arch implementations of BUG() use unreachable()
>>>> or other non-returning code. However with !CONFIG_BUG, the default
>>>> implementation is often used instead, and that does not do that. x86 always
>>>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
>>>> error:
>>>>
>>>>   kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>>>>   kernel/time/timekeeping.c:286:1: error: no return statement in function
>>>>   returning non-void [-Werror=return-type]
>>>>
>>>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
>>>
>>> I'm a bit worried about this patch, since we have had problems
>>> with unreachable() inside of BUG() in the past, and as far as I
>>> can remember, the current version was the only one that
>>> actually did the right thing on all compilers.
>>>
>>> One problem with an unreachable() annotation here is that if
>>> a compiler misanalyses the endless loop, it can decide to
>>> throw out the entire code path leading up to it and just
>>> run into undefined behavior instead of printing a BUG()
>>> message.
>>>
>>> Do you know which compiler version show the warning above?
>>
>> Original report has a list
>>
> 
> It looks like it's all versions of gcc, though no versions
> of clang show the warnings. I did a few more tests and could
> not find any differences on actual code generation, but
> I'd still feel more comfortable changing the caller than
> the BUG() macro. It's trivial to add a 'return 0' there.

AFAICT every implementation of BUG() except this one has
unreachable() or equivalent, so that inconsistency seems
wrong.

Could add 'return 0', but I do notice other cases
where a function does not have a return value, such as
cpus_have_final_boot_cap(), so there is already an expectation
that that is OK.

> Another interesting observation is that clang-11 and earlier
> versions end up skipping the endless loop, both with and
> without the __builtin_unreachable, see
> https://godbolt.org/z/aqa9zqz8x

Adding volatile asm("") to the loop would probably fix that,
but it seems like a separate issue.

> 
> clang-12 and above do work like gcc, so I guess that is good.
> 
>      Arnd
Adrian Hunter April 11, 2024, 9:27 a.m. UTC | #8
On 11/04/24 11:22, Christophe Leroy wrote:
> 
> 
> Le 11/04/2024 à 10:12, Christophe Leroy a écrit :
>>
>>
>> Le 11/04/2024 à 09:16, Adrian Hunter a écrit :
>>> On 11/04/24 10:04, Arnd Bergmann wrote:
>>>> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
>>>>> BUG() does not return, and arch implementations of BUG() use 
>>>>> unreachable()
>>>>> or other non-returning code. However with !CONFIG_BUG, the default
>>>>> implementation is often used instead, and that does not do that. x86 
>>>>> always
>>>>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
>>>>> error:
>>>>>
>>>>>    kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
>>>>>    kernel/time/timekeeping.c:286:1: error: no return statement in 
>>>>> function
>>>>>    returning non-void [-Werror=return-type]
>>>>>
>>>>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
>>>>
>>>> I'm a bit worried about this patch, since we have had problems
>>>> with unreachable() inside of BUG() in the past, and as far as I
>>>> can remember, the current version was the only one that
>>>> actually did the right thing on all compilers.
>>>>
>>>> One problem with an unreachable() annotation here is that if
>>>> a compiler misanalyses the endless loop, it can decide to
>>>> throw out the entire code path leading up to it and just
>>>> run into undefined behavior instead of printing a BUG()
>>>> message.
>>>>
>>>> Do you know which compiler version show the warning above?
>>>
>>> Original report has a list
>>>
>>>     https://lore.kernel.org/all/CA+G9fYvjdZCW=7ZGxS6A_3bysjQ56YF7S-+PNLQ_8a4DKh1Bhg@mail.gmail.com/
>>>
>>
>> Looking at the report, I think the correct fix should be to use 
>> BUILD_BUG() instead of BUG()
> 
> I confirm the error goes away with the following change to next-20240411 
> on powerpc tinyconfig with gcc 13.2
> 
> diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
> index 4e18db1819f8..3d5ac0cdd721 100644
> --- a/kernel/time/timekeeping.c
> +++ b/kernel/time/timekeeping.c
> @@ -282,7 +282,7 @@ static inline void timekeeping_check_update(struct 
> timekeeper *tk, u64 offset)
>   }
>   static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
>   {
> -	BUG();
> +	BUILD_BUG();
>   }
>   #endif
> 

That is fragile because it depends on defined(__OPTIMIZE__),
so it should still be:

	BUILD_BUG();
	return 0;
David Laight April 11, 2024, 10:27 a.m. UTC | #9
From: Adrian Hunter
> Sent: 11 April 2024 10:04
> 
> On 11/04/24 10:56, Arnd Bergmann wrote:
> > On Thu, Apr 11, 2024, at 09:16, Adrian Hunter wrote:
> >> On 11/04/24 10:04, Arnd Bergmann wrote:
> >>> On Wed, Apr 10, 2024, at 17:32, Adrian Hunter wrote:
> >>>> BUG() does not return, and arch implementations of BUG() use unreachable()
> >>>> or other non-returning code. However with !CONFIG_BUG, the default
> >>>> implementation is often used instead, and that does not do that. x86 always
> >>>> uses its own implementation, but powerpc with !CONFIG_BUG gives a build
> >>>> error:
> >>>>
> >>>>   kernel/time/timekeeping.c: In function ‘timekeeping_debug_get_ns’:
> >>>>   kernel/time/timekeeping.c:286:1: error: no return statement in function
> >>>>   returning non-void [-Werror=return-type]
> >>>>
> >>>> Add unreachable() to default !CONFIG_BUG BUG() implementation.
> >>>
> >>> I'm a bit worried about this patch, since we have had problems
> >>> with unreachable() inside of BUG() in the past, and as far as I
> >>> can remember, the current version was the only one that
> >>> actually did the right thing on all compilers.
> >>>
> >>> One problem with an unreachable() annotation here is that if
> >>> a compiler misanalyses the endless loop, it can decide to
> >>> throw out the entire code path leading up to it and just
> >>> run into undefined behavior instead of printing a BUG()
> >>> message.
> >>>
> >>> Do you know which compiler version show the warning above?
> >>
> >> Original report has a list
> >>
> >
> > It looks like it's all versions of gcc, though no versions
> > of clang show the warnings. I did a few more tests and could
> > not find any differences on actual code generation, but
> > I'd still feel more comfortable changing the caller than
> > the BUG() macro. It's trivial to add a 'return 0' there.
> 
> AFAICT every implementation of BUG() except this one has
> unreachable() or equivalent, so that inconsistency seems
> wrong.
> 
> Could add 'return 0', but I do notice other cases
> where a function does not have a return value, such as
> cpus_have_final_boot_cap(), so there is already an expectation
> that that is OK.

Isn't that likely to generate an 'unreachable code' warning?
I any case the compiler can generate better code for the non-BUG()
path if it knows BUG() doesn't return.
(And confuse stack back-trace code in the process.)

> 
> > Another interesting observation is that clang-11 and earlier
> > versions end up skipping the endless loop, both with and
> > without the __builtin_unreachable, see
> > https://godbolt.org/z/aqa9zqz8x
> 
> Adding volatile asm("") to the loop would probably fix that,
> but it seems like a separate issue.

I'd guess barrier() would be better.
It might be needed before the loopstop for other reasons.
The compiler might be just moving code below the loop and then
discarding it as unreachable.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Arnd Bergmann April 11, 2024, 11:26 a.m. UTC | #10
On Thu, Apr 11, 2024, at 11:27, Adrian Hunter wrote:
> On 11/04/24 11:22, Christophe Leroy wrote:
>> Le 11/04/2024 à 10:12, Christophe Leroy a écrit :
>>>
>>> Looking at the report, I think the correct fix should be to use 
>>> BUILD_BUG() instead of BUG()
>> 
>> I confirm the error goes away with the following change to next-20240411 
>> on powerpc tinyconfig with gcc 13.2
>> 
>> diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
>> index 4e18db1819f8..3d5ac0cdd721 100644
>> --- a/kernel/time/timekeeping.c
>> +++ b/kernel/time/timekeeping.c
>> @@ -282,7 +282,7 @@ static inline void timekeeping_check_update(struct 
>> timekeeper *tk, u64 offset)
>>   }
>>   static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
>>   {
>> -	BUG();
>> +	BUILD_BUG();
>>   }
>>   #endif
>> 
>
> That is fragile because it depends on defined(__OPTIMIZE__),
> so it should still be:

If there is a function that is defined but that must never be
called, I think we are doing something wrong. Before
e8e9d21a5df6 ("timekeeping: Refactor timekeeping helpers"),
the #ifdef made some sense, but now the #else is not really
that useful.

Ideally we would make timekeeping_debug_get_delta() and
timekeeping_check_update() just return in case of
!IS_ENABLED(CONFIG_DEBUG_TIMEKEEPING), but unfortunately
the code uses some struct members that are undefined then.

The patch below moves the #ifdef check into these functions,
which is not great, but it avoids defining useless
functions. Maybe there is a better way here. How about
just removing the BUG()?

     Arnd

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 4e18db1819f8..16c6dba64dd6 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -195,12 +195,11 @@ static inline u64 tk_clock_read(const struct tk_read_base *tkr)
        return clock->read(clock);
 }
 
-#ifdef CONFIG_DEBUG_TIMEKEEPING
 #define WARNING_FREQ (HZ*300) /* 5 minute rate-limiting */
 
 static void timekeeping_check_update(struct timekeeper *tk, u64 offset)
 {
-
+#ifdef CONFIG_DEBUG_TIMEKEEPING
        u64 max_cycles = tk->tkr_mono.clock->max_cycles;
        const char *name = tk->tkr_mono.clock->name;
 
@@ -235,12 +234,19 @@ static void timekeeping_check_update(struct timekeeper *tk, u64 offset)
                }
                tk->overflow_seen = 0;
        }
+#endif
 }
 
 static inline u64 timekeeping_cycles_to_ns(const struct tk_read_base *tkr, u64 cycles);
 
-static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
+static u64 __timekeeping_get_ns(const struct tk_read_base *tkr)
+{
+       return timekeeping_cycles_to_ns(tkr, tk_clock_read(tkr));
+}
+
+static inline u64 timekeeping_get_ns(const struct tk_read_base *tkr)
 {
+#ifdef CONFIG_DEBUG_TIMEKEEPING
        struct timekeeper *tk = &tk_core.timekeeper;
        u64 now, last, mask, max, delta;
        unsigned int seq;
@@ -275,16 +281,10 @@ static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
 
        /* timekeeping_cycles_to_ns() handles both under and overflow */
        return timekeeping_cycles_to_ns(tkr, now);
-}
 #else
-static inline void timekeeping_check_update(struct timekeeper *tk, u64 offset)
-{
-}
-static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
-{
-       BUG();
-}
+       return __timekeeping_get_ns(tkr);
 #endif
+}
 
 /**
  * tk_setup_internals - Set up internals to use clocksource clock.
@@ -390,19 +390,6 @@ static inline u64 timekeeping_cycles_to_ns(const struct tk_read_base *tkr, u64 c
        return ((delta * tkr->mult) + tkr->xtime_nsec) >> tkr->shift;
 }
 
-static __always_inline u64 __timekeeping_get_ns(const struct tk_read_base *tkr)
-{
-       return timekeeping_cycles_to_ns(tkr, tk_clock_read(tkr));
-}
-
-static inline u64 timekeeping_get_ns(const struct tk_read_base *tkr)
-{
-       if (IS_ENABLED(CONFIG_DEBUG_TIMEKEEPING))
-               return timekeeping_debug_get_ns(tkr);
-
-       return __timekeeping_get_ns(tkr);
-}
-
 /**
  * update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper.
  * @tkr: Timekeeping readout base from which we take the update
Michael Ellerman April 15, 2024, 2:19 a.m. UTC | #11
"Arnd Bergmann" <arnd@arndb.de> writes:
> On Thu, Apr 11, 2024, at 11:27, Adrian Hunter wrote:
>> On 11/04/24 11:22, Christophe Leroy wrote:
>>> Le 11/04/2024 à 10:12, Christophe Leroy a écrit :
>>>>
>>>> Looking at the report, I think the correct fix should be to use 
>>>> BUILD_BUG() instead of BUG()
>>> 
>>> I confirm the error goes away with the following change to next-20240411 
>>> on powerpc tinyconfig with gcc 13.2
>>> 
>>> diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
>>> index 4e18db1819f8..3d5ac0cdd721 100644
>>> --- a/kernel/time/timekeeping.c
>>> +++ b/kernel/time/timekeeping.c
>>> @@ -282,7 +282,7 @@ static inline void timekeeping_check_update(struct 
>>> timekeeper *tk, u64 offset)
>>>   }
>>>   static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
>>>   {
>>> -	BUG();
>>> +	BUILD_BUG();
>>>   }
>>>   #endif
>>> 
>>
>> That is fragile because it depends on defined(__OPTIMIZE__),
>> so it should still be:
>
> If there is a function that is defined but that must never be
> called, I think we are doing something wrong.

It's a pretty inevitable result of using IS_ENABLED(), which the docs
encourage people to use.

In this case it could easily be turned into a build error by just making
it an extern rather than a static inline.

But I think Christophe's solution is actually better, because it's more
explicit, ie. this function should not be called and if it is that's a
build time error.

cheers
Arnd Bergmann April 15, 2024, 3:35 p.m. UTC | #12
On Mon, Apr 15, 2024, at 04:19, Michael Ellerman wrote:
> "Arnd Bergmann" <arnd@arndb.de> writes:
>> On Thu, Apr 11, 2024, at 11:27, Adrian Hunter wrote:
>>> On 11/04/24 11:22, Christophe Leroy wrote:
>>>
>>> That is fragile because it depends on defined(__OPTIMIZE__),
>>> so it should still be:
>>
>> If there is a function that is defined but that must never be
>> called, I think we are doing something wrong.
>
> It's a pretty inevitable result of using IS_ENABLED(), which the docs
> encourage people to use.

Using IS_ENABLED() is usually a good idea, as it helps avoid
adding extra #ifdef checks and just drops static functions as
dead code, or lets you call extern functions that are conditionally
defined in a different file.

The thing is that here it does not do either of those and
adds more complexity than it avoids.

> In this case it could easily be turned into a build error by just making
> it an extern rather than a static inline.
>
> But I think Christophe's solution is actually better, because it's more
> explicit, ie. this function should not be called and if it is that's a
> build time error.

I haven't seen a good solution here. Ideally we'd just define
the functions unconditionally and have IS_ENABLED() take care
of letting the compiler drop them silently, but that doesn't
build because of missing struct members.

I won't object to either an 'extern' declaration or the
'BUILD_BUG_ON()' if you and others prefer that, both are better
than BUG() here. I still think my suggestion would be a little
simpler.

     Arnd
Christophe Leroy April 15, 2024, 5:07 p.m. UTC | #13
Le 15/04/2024 à 17:35, Arnd Bergmann a écrit :
> On Mon, Apr 15, 2024, at 04:19, Michael Ellerman wrote:
>> "Arnd Bergmann" <arnd@arndb.de> writes:
>>> On Thu, Apr 11, 2024, at 11:27, Adrian Hunter wrote:
>>>> On 11/04/24 11:22, Christophe Leroy wrote:
>>>>
>>>> That is fragile because it depends on defined(__OPTIMIZE__),
>>>> so it should still be:
>>>
>>> If there is a function that is defined but that must never be
>>> called, I think we are doing something wrong.
>>
>> It's a pretty inevitable result of using IS_ENABLED(), which the docs
>> encourage people to use.
> 
> Using IS_ENABLED() is usually a good idea, as it helps avoid
> adding extra #ifdef checks and just drops static functions as
> dead code, or lets you call extern functions that are conditionally
> defined in a different file.
> 
> The thing is that here it does not do either of those and
> adds more complexity than it avoids.
> 
>> In this case it could easily be turned into a build error by just making
>> it an extern rather than a static inline.
>>
>> But I think Christophe's solution is actually better, because it's more
>> explicit, ie. this function should not be called and if it is that's a
>> build time error.
> 
> I haven't seen a good solution here. Ideally we'd just define
> the functions unconditionally and have IS_ENABLED() take care
> of letting the compiler drop them silently, but that doesn't
> build because of missing struct members.
> 
> I won't object to either an 'extern' declaration or the
> 'BUILD_BUG_ON()' if you and others prefer that, both are better
> than BUG() here. I still think my suggestion would be a little
> simpler.

The advantage of the BUILD_BUG() against the extern is that the error 
gets detected at buildtime. With the extern it gets detected only at 
link-time.

But agree with you, the missing struct members defeats the advantages of 
IS_ENABLED().

At the end, how many instances of struct timekeeper do we have in the 
system ? With a quick look I see only two instances: tkcore.timekeeper 
and shadow_timekeeper. If I'm correct, wouldn't it just be simpler to 
have the three debug struct members defined at all time ?

Christophe
Arnd Bergmann April 15, 2024, 5:32 p.m. UTC | #14
On Mon, Apr 15, 2024, at 19:07, Christophe Leroy wrote:
> Le 15/04/2024 à 17:35, Arnd Bergmann a écrit :
>> 
>> I haven't seen a good solution here. Ideally we'd just define
>> the functions unconditionally and have IS_ENABLED() take care
>> of letting the compiler drop them silently, but that doesn't
>> build because of missing struct members.
>> 
>> I won't object to either an 'extern' declaration or the
>> 'BUILD_BUG_ON()' if you and others prefer that, both are better
>> than BUG() here. I still think my suggestion would be a little
>> simpler.
>
> The advantage of the BUILD_BUG() against the extern is that the error 
> gets detected at buildtime. With the extern it gets detected only at 
> link-time.
>
> But agree with you, the missing struct members defeats the advantages of 
> IS_ENABLED().
>
> At the end, how many instances of struct timekeeper do we have in the 
> system ? With a quick look I see only two instances: tkcore.timekeeper 
> and shadow_timekeeper. If I'm correct, wouldn't it just be simpler to 
> have the three debug struct members defined at all time ?

Sure, this version looks fine to me, and passes a simple build
test without CONFIG_DEBUG_TIMEKEEPING.

    Arnd

diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h
index 84ff2844df2a..485677a98b0b 100644
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -124,7 +124,6 @@ struct timekeeper {
        u32                     ntp_err_mult;
        /* Flag used to avoid updating NTP twice with same second */
        u32                     skip_second_overflow;
-#ifdef CONFIG_DEBUG_TIMEKEEPING
        long                    last_warning;
        /*
         * These simple flag variables are managed
@@ -135,7 +134,6 @@ struct timekeeper {
         */
        int                     underflow_seen;
        int                     overflow_seen;
-#endif
 };
 
 #ifdef CONFIG_GENERIC_TIME_VSYSCALL
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 4e18db1819f8..17f7aed807e1 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -195,7 +195,6 @@ static inline u64 tk_clock_read(const struct tk_read_base *tkr)
        return clock->read(clock);
 }
 
-#ifdef CONFIG_DEBUG_TIMEKEEPING
 #define WARNING_FREQ (HZ*300) /* 5 minute rate-limiting */
 
 static void timekeeping_check_update(struct timekeeper *tk, u64 offset)
@@ -276,15 +275,6 @@ static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
        /* timekeeping_cycles_to_ns() handles both under and overflow */
        return timekeeping_cycles_to_ns(tkr, now);
 }
-#else
-static inline void timekeeping_check_update(struct timekeeper *tk, u64 offset)
-{
-}
-static inline u64 timekeeping_debug_get_ns(const struct tk_read_base *tkr)
-{
-       BUG();
-}
-#endif
 
 /**
  * tk_setup_internals - Set up internals to use clocksource clock.
@@ -2173,7 +2163,8 @@ static bool timekeeping_advance(enum timekeeping_adv_mode mode)
                goto out;
 
        /* Do some additional sanity checking */
-       timekeeping_check_update(tk, offset);
+       if (IS_ENABLED(CONFIG_DEBUG_TIMEKEEPING))
+               timekeeping_check_update(tk, offset);
 
        /*
         * With NO_HZ we may have to accumulate many cycle_intervals
diff mbox series

Patch

diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 6e794420bd39..b7de3a4eade1 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -156,7 +156,10 @@  extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
 
 #else /* !CONFIG_BUG */
 #ifndef HAVE_ARCH_BUG
-#define BUG() do {} while (1)
+#define BUG() do {		\
+	do {} while (1);	\
+	unreachable();		\
+} while (0)
 #endif
 
 #ifndef HAVE_ARCH_BUG_ON