diff mbox series

[v10,1/8] sched/core: Add __might_sleep_precision()

Message ID 20250207132623.168854-2-fujita.tomonori@gmail.com (mailing list archive)
State Superseded
Headers show
Series rust: Add IO polling | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 547 this patch: 547
netdev/build_tools success Errors and warnings before: 26 (+1) this patch: 26 (+1)
netdev/cc_maintainers warning 2 maintainers not CCed: kees@kernel.org andriy.shevchenko@linux.intel.com
netdev/build_clang success Errors and warnings before: 24108 this patch: 24108
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 15348 this patch: 15348
netdev/checkpatch warning CHECK: Alignment should match open parenthesis CHECK: extern prototypes should be avoided in .h files WARNING: line length of 83 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 2 this patch: 2
netdev/source_inline success Was 0 now: 0

Commit Message

FUJITA Tomonori Feb. 7, 2025, 1:26 p.m. UTC
Add __might_sleep_precision(), Rust friendly version of
__might_sleep(), which takes a pointer to a string with the length
instead of a null-terminated string.

Rust's core::panic::Location::file(), which gives the file name of a
caller, doesn't provide a null-terminated
string. __might_sleep_precision() uses a precision specifier in the
printk format, which specifies the length of a string; a string
doesn't need to be a null-terminated.

Modify __might_sleep() to call __might_sleep_precision() but the
impact should be negligible. strlen() isn't called in a normal case;
it's called only when printing the error (sleeping function called
from invalid context).

Note that Location::file() providing a null-terminated string for
better C interoperability is under discussion [1].

Link: https://github.com/rust-lang/libs-team/issues/466 [1]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Co-developed-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 include/linux/kernel.h |  2 ++
 kernel/sched/core.c    | 55 ++++++++++++++++++++++++++----------------
 2 files changed, 36 insertions(+), 21 deletions(-)

Comments

David Laight Feb. 7, 2025, 6:12 p.m. UTC | #1
On Fri,  7 Feb 2025 22:26:16 +0900
FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:

> Add __might_sleep_precision(), Rust friendly version of
> __might_sleep(), which takes a pointer to a string with the length
> instead of a null-terminated string.
> 
> Rust's core::panic::Location::file(), which gives the file name of a
> caller, doesn't provide a null-terminated
> string. __might_sleep_precision() uses a precision specifier in the
> printk format, which specifies the length of a string; a string
> doesn't need to be a null-terminated.
> 
> Modify __might_sleep() to call __might_sleep_precision() but the
> impact should be negligible. strlen() isn't called in a normal case;
> it's called only when printing the error (sleeping function called
> from invalid context).
> 
> Note that Location::file() providing a null-terminated string for
> better C interoperability is under discussion [1].
> 
> Link: https://github.com/rust-lang/libs-team/issues/466 [1]
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Co-developed-by: Boqun Feng <boqun.feng@gmail.com>
> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
>  include/linux/kernel.h |  2 ++
>  kernel/sched/core.c    | 55 ++++++++++++++++++++++++++----------------
>  2 files changed, 36 insertions(+), 21 deletions(-)
> 
> diff --git a/include/linux/kernel.h b/include/linux/kernel.h
> index be2e8c0a187e..086ee1dc447e 100644
> --- a/include/linux/kernel.h
> +++ b/include/linux/kernel.h
> @@ -87,6 +87,7 @@ extern int dynamic_might_resched(void);
>  #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
>  extern void __might_resched(const char *file, int line, unsigned int offsets);
>  extern void __might_sleep(const char *file, int line);
> +extern void __might_sleep_precision(const char *file, int len, int line);
>  extern void __cant_sleep(const char *file, int line, int preempt_offset);
>  extern void __cant_migrate(const char *file, int line);
>  
> @@ -145,6 +146,7 @@ extern void __cant_migrate(const char *file, int line);
>    static inline void __might_resched(const char *file, int line,
>  				     unsigned int offsets) { }
>  static inline void __might_sleep(const char *file, int line) { }
> +static inline void __might_sleep_precision(const char *file, int len, int line) { }
>  # define might_sleep() do { might_resched(); } while (0)
>  # define cant_sleep() do { } while (0)
>  # define cant_migrate()		do { } while (0)
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 165c90ba64ea..d308f2a8692e 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -8678,24 +8678,6 @@ void __init sched_init(void)
>  
>  #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
>  
> -void __might_sleep(const char *file, int line)
> -{
> -	unsigned int state = get_current_state();
> -	/*
> -	 * Blocking primitives will set (and therefore destroy) current->state,
> -	 * since we will exit with TASK_RUNNING make sure we enter with it,
> -	 * otherwise we will destroy state.
> -	 */
> -	WARN_ONCE(state != TASK_RUNNING && current->task_state_change,
> -			"do not call blocking ops when !TASK_RUNNING; "
> -			"state=%x set at [<%p>] %pS\n", state,
> -			(void *)current->task_state_change,
> -			(void *)current->task_state_change);
> -
> -	__might_resched(file, line, 0);
> -}
> -EXPORT_SYMBOL(__might_sleep);
> -
>  static void print_preempt_disable_ip(int preempt_offset, unsigned long ip)
>  {
>  	if (!IS_ENABLED(CONFIG_DEBUG_PREEMPT))
> @@ -8717,7 +8699,8 @@ static inline bool resched_offsets_ok(unsigned int offsets)
>  	return nested == offsets;
>  }
>  
> -void __might_resched(const char *file, int line, unsigned int offsets)
> +static void __might_resched_precision(const char *file, int len, int line,

For clarity that ought to be file_len.

> +				      unsigned int offsets)
>  {
>  	/* Ratelimiting timestamp: */
>  	static unsigned long prev_jiffy;
> @@ -8740,8 +8723,10 @@ void __might_resched(const char *file, int line, unsigned int offsets)
>  	/* Save this before calling printk(), since that will clobber it: */
>  	preempt_disable_ip = get_preempt_disable_ip(current);
>  
> -	pr_err("BUG: sleeping function called from invalid context at %s:%d\n",
> -	       file, line);
> +	if (len < 0)
> +		len = strlen(file);

No need for strlen(), just use a big number instead of -1.
Anything bigger than a sane upper limit on the filename length will do.

	David

> +	pr_err("BUG: sleeping function called from invalid context at %.*s:%d\n",
> +	       len, file, line);
>  	pr_err("in_atomic(): %d, irqs_disabled(): %d, non_block: %d, pid: %d, name: %s\n",
>  	       in_atomic(), irqs_disabled(), current->non_block_count,
>  	       current->pid, current->comm);
> @@ -8766,8 +8751,36 @@ void __might_resched(const char *file, int line, unsigned int offsets)
>  	dump_stack();
>  	add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
>  }
> +
> +void __might_resched(const char *file, int line, unsigned int offsets)
> +{
> +	__might_resched_precision(file, -1, line, offsets);
> +}
>  EXPORT_SYMBOL(__might_resched);
>  
> +void __might_sleep_precision(const char *file, int len, int line)
> +{
> +	unsigned int state = get_current_state();
> +	/*
> +	 * Blocking primitives will set (and therefore destroy) current->state,
> +	 * since we will exit with TASK_RUNNING make sure we enter with it,
> +	 * otherwise we will destroy state.
> +	 */
> +	WARN_ONCE(state != TASK_RUNNING && current->task_state_change,
> +			"do not call blocking ops when !TASK_RUNNING; "
> +			"state=%x set at [<%p>] %pS\n", state,
> +			(void *)current->task_state_change,
> +			(void *)current->task_state_change);
> +
> +	__might_resched_precision(file, len, line, 0);
> +}
> +
> +void __might_sleep(const char *file, int line)
> +{
> +	__might_sleep_precision(file, -1, line);
> +}
> +EXPORT_SYMBOL(__might_sleep);
> +
>  void __cant_sleep(const char *file, int line, int preempt_offset)
>  {
>  	static unsigned long prev_jiffy;
FUJITA Tomonori Feb. 8, 2025, 3:01 a.m. UTC | #2
On Fri, 7 Feb 2025 18:12:58 +0000
David Laight <david.laight.linux@gmail.com> wrote:

>>  static void print_preempt_disable_ip(int preempt_offset, unsigned long ip)
>>  {
>>  	if (!IS_ENABLED(CONFIG_DEBUG_PREEMPT))
>> @@ -8717,7 +8699,8 @@ static inline bool resched_offsets_ok(unsigned int offsets)
>>  	return nested == offsets;
>>  }
>>  
>> -void __might_resched(const char *file, int line, unsigned int offsets)
>> +static void __might_resched_precision(const char *file, int len, int line,
> 
> For clarity that ought to be file_len.

Yeah, I'll update.

>> +				      unsigned int offsets)
>>  {
>>  	/* Ratelimiting timestamp: */
>>  	static unsigned long prev_jiffy;
>> @@ -8740,8 +8723,10 @@ void __might_resched(const char *file, int line, unsigned int offsets)
>>  	/* Save this before calling printk(), since that will clobber it: */
>>  	preempt_disable_ip = get_preempt_disable_ip(current);
>>  
>> -	pr_err("BUG: sleeping function called from invalid context at %s:%d\n",
>> -	       file, line);
>> +	if (len < 0)
>> +		len = strlen(file);
> 
> No need for strlen(), just use a big number instead of -1.
> Anything bigger than a sane upper limit on the filename length will do.

Ah, that's right. Just passing the maximum precision (1<<15-1) works.

The precision specifies the maximum length. vsnprintf() always
iterates through a string until it reaches the maximum length or
encounters the null terminator. So strlen() here is useless.

Alice and Boqun, the above change is fine? Can I keep the tags?
Alice Ryhl Feb. 10, 2025, 9:41 a.m. UTC | #3
On Sat, Feb 8, 2025 at 4:01 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> On Fri, 7 Feb 2025 18:12:58 +0000
> David Laight <david.laight.linux@gmail.com> wrote:
>
> >>  static void print_preempt_disable_ip(int preempt_offset, unsigned long ip)
> >>  {
> >>      if (!IS_ENABLED(CONFIG_DEBUG_PREEMPT))
> >> @@ -8717,7 +8699,8 @@ static inline bool resched_offsets_ok(unsigned int offsets)
> >>      return nested == offsets;
> >>  }
> >>
> >> -void __might_resched(const char *file, int line, unsigned int offsets)
> >> +static void __might_resched_precision(const char *file, int len, int line,
> >
> > For clarity that ought to be file_len.
>
> Yeah, I'll update.
>
> >> +                                  unsigned int offsets)
> >>  {
> >>      /* Ratelimiting timestamp: */
> >>      static unsigned long prev_jiffy;
> >> @@ -8740,8 +8723,10 @@ void __might_resched(const char *file, int line, unsigned int offsets)
> >>      /* Save this before calling printk(), since that will clobber it: */
> >>      preempt_disable_ip = get_preempt_disable_ip(current);
> >>
> >> -    pr_err("BUG: sleeping function called from invalid context at %s:%d\n",
> >> -           file, line);
> >> +    if (len < 0)
> >> +            len = strlen(file);
> >
> > No need for strlen(), just use a big number instead of -1.
> > Anything bigger than a sane upper limit on the filename length will do.
>
> Ah, that's right. Just passing the maximum precision (1<<15-1) works.
>
> The precision specifies the maximum length. vsnprintf() always
> iterates through a string until it reaches the maximum length or
> encounters the null terminator. So strlen() here is useless.
>
> Alice and Boqun, the above change is fine? Can I keep the tags?

I'd probably like a comment explaining the meaning of this constant
somewhere, but sure ok with me.

Alice
Boqun Feng Feb. 17, 2025, 1:51 a.m. UTC | #4
On Mon, Feb 10, 2025 at 10:41:00AM +0100, Alice Ryhl wrote:
> On Sat, Feb 8, 2025 at 4:01 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
> >
> > On Fri, 7 Feb 2025 18:12:58 +0000
> > David Laight <david.laight.linux@gmail.com> wrote:
> >
> > >>  static void print_preempt_disable_ip(int preempt_offset, unsigned long ip)
> > >>  {
> > >>      if (!IS_ENABLED(CONFIG_DEBUG_PREEMPT))
> > >> @@ -8717,7 +8699,8 @@ static inline bool resched_offsets_ok(unsigned int offsets)
> > >>      return nested == offsets;
> > >>  }
> > >>
> > >> -void __might_resched(const char *file, int line, unsigned int offsets)
> > >> +static void __might_resched_precision(const char *file, int len, int line,
> > >
> > > For clarity that ought to be file_len.
> >
> > Yeah, I'll update.
> >
> > >> +                                  unsigned int offsets)
> > >>  {
> > >>      /* Ratelimiting timestamp: */
> > >>      static unsigned long prev_jiffy;
> > >> @@ -8740,8 +8723,10 @@ void __might_resched(const char *file, int line, unsigned int offsets)
> > >>      /* Save this before calling printk(), since that will clobber it: */
> > >>      preempt_disable_ip = get_preempt_disable_ip(current);
> > >>
> > >> -    pr_err("BUG: sleeping function called from invalid context at %s:%d\n",
> > >> -           file, line);
> > >> +    if (len < 0)
> > >> +            len = strlen(file);
> > >
> > > No need for strlen(), just use a big number instead of -1.
> > > Anything bigger than a sane upper limit on the filename length will do.
> >
> > Ah, that's right. Just passing the maximum precision (1<<15-1) works.
> >
> > The precision specifies the maximum length. vsnprintf() always
> > iterates through a string until it reaches the maximum length or
> > encounters the null terminator. So strlen() here is useless.
> >
> > Alice and Boqun, the above change is fine? Can I keep the tags?
> 
> I'd probably like a comment explaining the meaning of this constant
> somewhere, but sure ok with me.
> 

Agreed. The code should be fine but need some comments.

Regards,
Boqun

> Alice
FUJITA Tomonori Feb. 17, 2025, 6:44 a.m. UTC | #5
On Sun, 16 Feb 2025 17:51:32 -0800
Boqun Feng <boqun.feng@gmail.com> wrote:

>> > >> +                                  unsigned int offsets)
>> > >>  {
>> > >>      /* Ratelimiting timestamp: */
>> > >>      static unsigned long prev_jiffy;
>> > >> @@ -8740,8 +8723,10 @@ void __might_resched(const char *file, int line, unsigned int offsets)
>> > >>      /* Save this before calling printk(), since that will clobber it: */
>> > >>      preempt_disable_ip = get_preempt_disable_ip(current);
>> > >>
>> > >> -    pr_err("BUG: sleeping function called from invalid context at %s:%d\n",
>> > >> -           file, line);
>> > >> +    if (len < 0)
>> > >> +            len = strlen(file);
>> > >
>> > > No need for strlen(), just use a big number instead of -1.
>> > > Anything bigger than a sane upper limit on the filename length will do.
>> >
>> > Ah, that's right. Just passing the maximum precision (1<<15-1) works.
>> >
>> > The precision specifies the maximum length. vsnprintf() always
>> > iterates through a string until it reaches the maximum length or
>> > encounters the null terminator. So strlen() here is useless.
>> >
>> > Alice and Boqun, the above change is fine? Can I keep the tags?
>> 
>> I'd probably like a comment explaining the meaning of this constant
>> somewhere, but sure ok with me.
>> 
> 
> Agreed. The code should be fine but need some comments.

Of course, I'll add some in the next version.

Thanks!
diff mbox series

Patch

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index be2e8c0a187e..086ee1dc447e 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -87,6 +87,7 @@  extern int dynamic_might_resched(void);
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 extern void __might_resched(const char *file, int line, unsigned int offsets);
 extern void __might_sleep(const char *file, int line);
+extern void __might_sleep_precision(const char *file, int len, int line);
 extern void __cant_sleep(const char *file, int line, int preempt_offset);
 extern void __cant_migrate(const char *file, int line);
 
@@ -145,6 +146,7 @@  extern void __cant_migrate(const char *file, int line);
   static inline void __might_resched(const char *file, int line,
 				     unsigned int offsets) { }
 static inline void __might_sleep(const char *file, int line) { }
+static inline void __might_sleep_precision(const char *file, int len, int line) { }
 # define might_sleep() do { might_resched(); } while (0)
 # define cant_sleep() do { } while (0)
 # define cant_migrate()		do { } while (0)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 165c90ba64ea..d308f2a8692e 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -8678,24 +8678,6 @@  void __init sched_init(void)
 
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 
-void __might_sleep(const char *file, int line)
-{
-	unsigned int state = get_current_state();
-	/*
-	 * Blocking primitives will set (and therefore destroy) current->state,
-	 * since we will exit with TASK_RUNNING make sure we enter with it,
-	 * otherwise we will destroy state.
-	 */
-	WARN_ONCE(state != TASK_RUNNING && current->task_state_change,
-			"do not call blocking ops when !TASK_RUNNING; "
-			"state=%x set at [<%p>] %pS\n", state,
-			(void *)current->task_state_change,
-			(void *)current->task_state_change);
-
-	__might_resched(file, line, 0);
-}
-EXPORT_SYMBOL(__might_sleep);
-
 static void print_preempt_disable_ip(int preempt_offset, unsigned long ip)
 {
 	if (!IS_ENABLED(CONFIG_DEBUG_PREEMPT))
@@ -8717,7 +8699,8 @@  static inline bool resched_offsets_ok(unsigned int offsets)
 	return nested == offsets;
 }
 
-void __might_resched(const char *file, int line, unsigned int offsets)
+static void __might_resched_precision(const char *file, int len, int line,
+				      unsigned int offsets)
 {
 	/* Ratelimiting timestamp: */
 	static unsigned long prev_jiffy;
@@ -8740,8 +8723,10 @@  void __might_resched(const char *file, int line, unsigned int offsets)
 	/* Save this before calling printk(), since that will clobber it: */
 	preempt_disable_ip = get_preempt_disable_ip(current);
 
-	pr_err("BUG: sleeping function called from invalid context at %s:%d\n",
-	       file, line);
+	if (len < 0)
+		len = strlen(file);
+	pr_err("BUG: sleeping function called from invalid context at %.*s:%d\n",
+	       len, file, line);
 	pr_err("in_atomic(): %d, irqs_disabled(): %d, non_block: %d, pid: %d, name: %s\n",
 	       in_atomic(), irqs_disabled(), current->non_block_count,
 	       current->pid, current->comm);
@@ -8766,8 +8751,36 @@  void __might_resched(const char *file, int line, unsigned int offsets)
 	dump_stack();
 	add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
 }
+
+void __might_resched(const char *file, int line, unsigned int offsets)
+{
+	__might_resched_precision(file, -1, line, offsets);
+}
 EXPORT_SYMBOL(__might_resched);
 
+void __might_sleep_precision(const char *file, int len, int line)
+{
+	unsigned int state = get_current_state();
+	/*
+	 * Blocking primitives will set (and therefore destroy) current->state,
+	 * since we will exit with TASK_RUNNING make sure we enter with it,
+	 * otherwise we will destroy state.
+	 */
+	WARN_ONCE(state != TASK_RUNNING && current->task_state_change,
+			"do not call blocking ops when !TASK_RUNNING; "
+			"state=%x set at [<%p>] %pS\n", state,
+			(void *)current->task_state_change,
+			(void *)current->task_state_change);
+
+	__might_resched_precision(file, len, line, 0);
+}
+
+void __might_sleep(const char *file, int line)
+{
+	__might_sleep_precision(file, -1, line);
+}
+EXPORT_SYMBOL(__might_sleep);
+
 void __cant_sleep(const char *file, int line, int preempt_offset)
 {
 	static unsigned long prev_jiffy;