Creation of workqueues in Rust
Subsystems (2)
Alice Ryhl
reviewerv3 cover Creation of workqueues in Rust
GPU drivers often need to create their own workqueues for various reasons. Add the ability to do so.
---
Changes in v3:
- Switch to builder pattern.
- Drop BH workqueues for now.
- Mark delayed wq change as fix.
- Link to v2: https://lore.kernel.org/r/[email protected]
Changes in v2:
- Redo how flagging works.
- Restrict delayed work to not be usable on custom workqueues.
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Alice Ryhl (2):
rust: workqueue: restrict delayed work to global wqs
rust: workqueue: add creation of workqueues
rust/helpers/workqueue.c | 7 ++ rust/kernel/workqueue.rs | 199 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 4 deletions(-)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20250411-create-workqueue-d053158c7a4b
Best regards,
Signature · 1 line
Alice Ryhl <[email protected]>
Alice Ryhl
reviewerPATCH v3 1/2 rust: workqueue: restrict delayed work to global wqs
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use enqueue_delayed on a workqueue that might be destroyed. To fix this, restricted enqueue_delayed to static queues.
--- rust/kernel/workqueue.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
rust/kernel/workqueue.rs +8 -1
@@ -296,8 +296,15 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput/// /// This may fail if the work item is already enqueued in a workqueue. /// +/// This is only valid for global workqueues (with static lifetimes) because those are the only +/// ones that outlive all possible delayed work items. +/// /// The work item will be submitted using `WORK_CPU_UNBOUND`. -pub fn enqueue_delayed <W ,const ID :u64 >(&self, w:W , delay:Jiffies ) ->W ::EnqueueOutput +pub fn enqueue_delayed <W ,const ID :u64 >( + &'static self, + w:W , + delay:Jiffies , + ) ->W ::EnqueueOutput where W :RawDelayedWorkItem <ID > +Send + 'static, {
Signature · 1 line
2.53.0.473.g4a7958ca14-goog
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 4 lines · Alice Ryhl
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use
enqueue_delayedon a workqueue that might be destroyed. To fix this, restrictedenqueue_delayedto static queues.
:(
Quoted 13 lines
Fixes 7c098cd5eaae ("workqueue: rust: add delayed work items")Reviewed-by John Hubbard <[email protected]>Signed-off-by Alice Ryhl <[email protected]>--- rust/kernel/workqueue.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)rust/kernel/workqueue.rs +0
-0
rust/kernel/workqueue.rs pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput · line 300
Quoted 5 lines
rust/kernel/workqueue.rs +2
-0@@ -296,8 +296,15 @@ pub fn enqueue<W, const ID: u64>(&self, w: W) -> W::EnqueueOutput/// /// This may fail if the work item is already enqueued in a workqueue. /// +/// This is only valid for global workqueues (with static lifetimes) because those are the only +/// ones that outlive all possible delayed work items.
We should probably add a FIXME comment pointing out that this should be fixed in the C code.
Maybe also link your approach?
Quoted 14 lines
rust/kernel/workqueue.rs +6
-1+/// /// The work item will be submitted using `WORK_CPU_UNBOUND`. -pub fn enqueue_delayed <W ,const ID :u64 >(&self, w:W , delay:Jiffies ) ->W ::EnqueueOutput +pub fn enqueue_delayed <W ,const ID :u64 >( + &'static self, + w:W , + delay:Jiffies , + ) ->W ::EnqueueOutput where W :RawDelayedWorkItem <ID > +Send + 'static, {Signature · 2 lines
-- 2.53.0.473.g4a7958ca14-goog
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
In reply to Alice RyhlQuoted 9 lines · Alice Ryhl
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use
enqueue_delayedon a workqueue that might be destroyed. To fix this, restrictedenqueue_delayedto static queues.Fixes 7c098cd5eaae ("workqueue: rust: add delayed work items")Reviewed-by John Hubbard <[email protected]>Signed-off-by Alice Ryhl <[email protected]>
Quoted 3 lines
--- rust/kernel/workqueue.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
In reply to Alice RyhlQuoted 4 lines · Alice Ryhl
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use
enqueue_delayedon a workqueue that might be destroyed. To fix this, restrictedenqueue_delayedto static queues.
C being C, we've been just chalking this up as "user error", but please feel free to add per-workqueue percpu ref for pending delayed work items if that'd help. That shouldn't be noticeably expensive and should help straighten this out for rust hopefully.
Thanks.
Signature · 1 line
tejun
Alice Ryhl
reviewerRe: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 10 lines · Tejun Heo
Quoted 4 lines · Alice Ryhl
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use
enqueue_delayedon a workqueue that might be destroyed. To fix this, restrictedenqueue_delayedto static queues.C being C, we've been just chalking this up as "user error", but please feel free to add per-workqueue percpu ref for pending delayed work items if that'd help. That shouldn't be noticeably expensive and should help straighten this out for rust hopefully.
I had been thinking I would pick up this patch again:
https://lore.kernel.org/all/[email protected]/
but it sounds like you're suggesting a different solution?
Alice
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 12 lines · Alice Ryhl
Quoted 10 lines · Tejun Heo
Quoted 4 lines · Alice Ryhl
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use
enqueue_delayedon a workqueue that might be destroyed. To fix this, restrictedenqueue_delayedto static queues.C being C, we've been just chalking this up as "user error", but please feel free to add per-workqueue percpu ref for pending delayed work items if that'd help. That shouldn't be noticeably expensive and should help straighten this out for rust hopefully.
Quoted 4 lines
I had been thinking I would pick up this patch again:
https://lore.kernel.org/all/[email protected]/but it sounds like you're suggesting a different solution?
I'm not remembering much context at this point, but if it *could* work, percpu refcnt counting the number of delayed work items would be cheaper. Again, I could easily be forgetting why we didn't do that in the first place.
Thanks.
Signature · 1 line
tejun
Alice Ryhl
reviewerRe: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 16 lines · Tejun Heo
Quoted 15 lines · Alice Ryhl
Quoted 10 lines · Tejun Heo
Quoted 4 lines · Alice Ryhl
When a workqueue is shut down, delayed work that is pending but not scheduled does not get properly cleaned up, so it's not safe to use
enqueue_delayedon a workqueue that might be destroyed. To fix this, restrictedenqueue_delayedto static queues.C being C, we've been just chalking this up as "user error", but please feel free to add per-workqueue percpu ref for pending delayed work items if that'd help. That shouldn't be noticeably expensive and should help straighten this out for rust hopefully.
I had been thinking I would pick up this patch again:
https://lore.kernel.org/all/[email protected]/
Quoted 6 lines
Quoted 1 line
but it sounds like you're suggesting a different solution?
I'm not remembering much context at this point, but if it *could* work, percpu refcnt counting the number of delayed work items would be cheaper. Again, I could easily be forgetting why we didn't do that in the first place.
I guess the question is, what does destroy_workqueue() do?
- Does it wait for the timers to finish?
- Does it immediately run the delayed works?
- Does it exit without waiting for timers?
It sounds like the refcount approach is the last solution, where destroy_workqueue() just exits without waiting for timers, but then keeping the workqueue alive until the timers elapse.
The main concern I can see is that this means that delayed work can run after destroy_workqueue() is called. That may be a problem if destroy_workqueue() is used to guard module unload (or device unbind).
Alice
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Hello,
Quoted 6 lines · Alice Ryhl
I guess the question is, what does
destroy_workqueue()do?- Does it wait for the timers to finish?
- Does it immediately run the delayed works?
- Does it exit without waiting for timers?
Quoted 7 lines
It sounds like the refcount approach is the last solution, where
destroy_workqueue()just exits without waiting for timers, but then keeping the workqueue alive until the timers elapse.The main concern I can see is that this means that delayed work can run after
destroy_workqueue()is called. That may be a problem ifdestroy_workqueue()is used to guard module unload (or device unbind).
delayed_work is just pointing to the wq pointer. On destroy_workqueue(), we can shut it down and free all the supporting stuff while leaving zombie wq struct which noops execution and let the whole thing go away when refs reach zero?
Thanks.
Signature · 1 line
tejun
Alice Ryhl
reviewerRe: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 13 lines · Tejun Heo
Hello,
Quoted 10 lines · Alice Ryhl
I guess the question is, what does
destroy_workqueue()do?- Does it wait for the timers to finish?
- Does it immediately run the delayed works?
- Does it exit without waiting for timers?It sounds like the refcount approach is the last solution, where
destroy_workqueue()just exits without waiting for timers, but then keeping the workqueue alive until the timers elapse.
Quoted 8 lines
Quoted 3 lines
The main concern I can see is that this means that delayed work can run after
destroy_workqueue()is called. That may be a problem ifdestroy_workqueue()is used to guard module unload (or device unbind).
delayed_workis just pointing to the wq pointer. Ondestroy_workqueue(), we can shut it down and free all the supporting stuff while leaving zombie wq struct which noops execution and let the whole thing go away when refs reach zero?
But isn't that a problem for e.g. self-freeing work? If we don't run the work, then its memory is just leaked.
Alice
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 7 lines · Alice Ryhl
Quoted 4 lines
delayed_workis just pointing to the wq pointer. Ondestroy_workqueue(), we can shut it down and free all the supporting stuff while leaving zombie wq struct which noops execution and let the whole thing go away when refs reach zero?But isn't that a problem for e.g. self-freeing work? If we don't run the work, then its memory is just leaked.
Yeah, good point. Maybe we should just keep the whole thing up while removing it from sysfs. Would that work?
Thanks.
Signature · 1 line
tejun
Alice Ryhl
reviewerRe: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 7 lines · Tejun Heo
Quoted 5 lines · Alice Ryhl
Quoted 4 lines
delayed_workis just pointing to the wq pointer. Ondestroy_workqueue(), we can shut it down and free all the supporting stuff while leaving zombie wq struct which noops execution and let the whole thing go away when refs reach zero?
Quoted 5 lines
Quoted 2 lines
But isn't that a problem for e.g. self-freeing work? If we don't run the work, then its memory is just leaked.
Yeah, good point. Maybe we should just keep the whole thing up while removing it from sysfs. Would that work?
We can but there are two variants of that:
If destroy_workqueue() waits for delayed work, then it may take a long time.
If destroy_workqueue() does not wait for delayed work, then I'm worried about bugs resulting from module unload and similar.
Alice
Re: [PATCH v3 1/2] rust: workqueue: restrict delayed work to global wqs
Quoted 14 lines · Alice Ryhl
Quoted 7 lines · Tejun Heo
Quoted 5 lines · Alice Ryhl
Quoted 4 lines
delayed_workis just pointing to the wq pointer. Ondestroy_workqueue(), we can shut it down and free all the supporting stuff while leaving zombie wq struct which noops execution and let the whole thing go away when refs reach zero?Quoted 5 lines
Quoted 2 lines
But isn't that a problem for e.g. self-freeing work? If we don't run the work, then its memory is just leaked.
Yeah, good point. Maybe we should just keep the whole thing up while removing it from sysfs. Would that work?
Quoted 6 lines
We can but there are two variants of that:
If
destroy_workqueue()waits for delayed work, then it may take a long time.If
destroy_workqueue()does not wait for delayed work, then I'm worried about bugs resulting from module unload and similar.
I see. Yeah, neither seems workable. We should be able to flush the delayed work items. Maybe we can make that an optional feature so that rust wrappers can turn it on for safety.
Thanks.
Signature · 1 line
tejun
Alice Ryhl
reviewerPATCH v3 2/2 rust: workqueue: add creation of workqueues
In reply to Alice RyhlCreating workqueues is needed by various GPU drivers. Not only does it give you better control over execution, it also allows devices to ensure that all tasks have exited before the device is unbound (or similar) by running the workqueue destructor.
--- rust/helpers/workqueue.c | 7 ++ rust/kernel/workqueue.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 3 deletions(-)
rust/helpers/workqueue.c +7 -0
@@ -14,3 +14,10 @@ __rust_helper void rust_helper_init_work_with_key(struct work_struct *work,INIT_LIST_HEAD (&work->entry); work->func = func; } + +__rust_helper +struct workqueue_struct *rust_helper_alloc_workqueue(const char *fmt, unsigned int flags, +int max_active,const void *data) +{ +return alloc_workqueue (fmt, flags, max_active, data); +}
rust/kernel/workqueue.rs +187 -3
214 lines not on this page · raw message
Signature · 1 line
2.53.0.473.g4a7958ca14-goog
Danilo Krummrich
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 396
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`.
Should we just mention the default value?
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 399
Quoted 3 lines
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );
The workqueue code prints a warning for max_active > WQ_MAX_ACTIVE. Maybe use debug_assert()?
It's also a bit unfortunate that alloc_ordered_workqueue() becomes .max_active(1).
At the same time having a separate ordered() method competes with max_active().
Mybe a type state, i.e. Builder<Ordered> that doesn't have max_active()?
Quoted 2 lines
(quoted diff) +2
-0+ self + }
Gary Guo
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 11 lines · Danilo Krummrich
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 396
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`. Should we just mention the default value?
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 399
Quoted 3 lines
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );
Quoted 5 lines
The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).
I think ordered workqueue still to have __WQ_ORDERED flag, too?
Quoted 2 lines
At the same time having a separate
ordered()method competes withmax_active().
I don't think its too bad to have both, and before constructing checks that it's used correctly:
warn_on!(self.flags & __WQ_ORDERED != 0 && self.max_active != 1);
but type state works, too.
Best,
Gary
Quoted 5 lines
Mybe a type state, i.e. Builder<Ordered> that doesn't have
max_active()?Quoted 2 lines
(quoted diff) +2
-0+ self + }
Danilo Krummrich
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 21 lines · Gary Guo
Quoted 11 lines · Danilo Krummrich
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 396
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`. Should we just mention the default value?
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 399
Quoted 3 lines
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );Quoted 5 lines
The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).I think ordered workqueue still to have
__WQ_ORDEREDflag, too?Quoted 1 line
Quoted 8 lines
Quoted 1 line
At the same time having a separate
ordered()method competes withmax_active().I don't think its too bad to have both, and before constructing checks that it's used correctly:
warn_on!(self.flags & __WQ_ORDERED != 0 && self.max_active != 1);but type state works, too.
I think we can ensure this at compile time, so we should do it.
Alice Ryhl
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
In reply to Danilo KrummrichQuoted 6 lines · Danilo Krummrich
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 396
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`. Should we just mention the default value?
I can mention the constant name, but I'd like to avoid mentioning a value that might change.
Quoted 6 lines
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 399
Quoted 3 lines
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?
What's wrong with just making use of the C-side warning?
Quoted 6 lines
It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).At the same time having a separate
ordered()method competes withmax_active().Mybe a type state, i.e. Builder<Ordered> that doesn't have
max_active()?
Sorry I'm a bit confused by this. Why does an ordered() compete with max_active()?
Alice
Danilo Krummrich
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 10 lines · Alice Ryhl
Quoted 6 lines · Danilo Krummrich
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 396
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`. Should we just mention the default value?
I can mention the constant name, but I'd like to avoid mentioning a value that might change.
Yes, that's what I meant.
Quoted 8 lines
Quoted 6 lines
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 399
Quoted 3 lines
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?What's wrong with just making use of the C-side warning?
IIRC, we have the same pattern in other Rust code that we use debug_assert() when a value got clamped, e.g. in udelay().
Quoted 9 lines
Quoted 6 lines
It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).At the same time having a separate
ordered()method competes withmax_active().Mybe a type state, i.e. Builder<Ordered> that doesn't have
max_active()?Sorry I'm a bit confused by this. Why does an
ordered()compete withmax_active()?
Because you could get an inconsistent state with __WQ_ORDERED and max_active > 1.
It also conflicts with sysfs() I think [1].
[1] https://elixir.bootlin.com/linux/v6.19.3/source/kernel/workqueue.c#L7417
Alice Ryhl
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 7 lines · Danilo Krummrich
Quoted 6 lines · Alice Ryhl
Quoted 5 lines · Danilo Krummrich
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?What's wrong with just making use of the C-side warning?
IIRC, we have the same pattern in other Rust code that we use
debug_assert()when a value got clamped, e.g. inudelay().
In udelay(), the clamping happens on the Rust side, so it makes sense that Rust is the one to warn about it.
Here, the clamping happens in C code. To warn about it, I'd have to duplicate the existing C-side check to clamp in Rust.
Quoted 10 lines
Quoted 9 lines
Quoted 6 lines
It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).At the same time having a separate
ordered()method competes withmax_active().Mybe a type state, i.e. Builder<Ordered> that doesn't have
max_active()?Sorry I'm a bit confused by this. Why does an
ordered()compete withmax_active()?
Quoted 6 lines
Because you could get an inconsistent state with
__WQ_ORDEREDandmax_active> 1.It also conflicts with
sysfs()I think [1].[1] https://elixir.bootlin.com/linux/v6.19.3/source/kernel/workqueue.c#L7417
And I guess the further argument is that we have a use-case for ordered workqueues?
Alice
Danilo Krummrich
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 13 lines · Alice Ryhl
Quoted 12 lines · Danilo Krummrich
Quoted 10 lines · Alice Ryhl
Quoted 7 lines · Danilo Krummrich
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 399
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?What's wrong with just making use of the C-side warning?
Quoted 8 lines
Quoted 2 lines
IIRC, we have the same pattern in other Rust code that we use
debug_assert()when a value got clamped, e.g. inudelay().In
udelay(), the clamping happens on the Rust side, so it makes sense that Rust is the one to warn about it.Here, the clamping happens in C code. To warn about it, I'd have to duplicate the existing C-side check to clamp in Rust.
That's fair, although I also think that it is not unreasonable. Given that this uses the builder pattern, I think it would be nice to ensure that nothing "invalid" can be built in the first place.
Maybe we can use a bounded integer?
Quoted 13 lines
Quoted 13 lines
Quoted 9 lines
Quoted 6 lines
It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).At the same time having a separate
ordered()method competes withmax_active().Mybe a type state, i.e. Builder<Ordered> that doesn't have
max_active()?Sorry I'm a bit confused by this. Why does an
ordered()compete withmax_active()?Because you could get an inconsistent state with
__WQ_ORDEREDandmax_active> 1.
Quoted 6 lines
Quoted 3 lines
It also conflicts with
sysfs()I think [1].[1] https://elixir.bootlin.com/linux/v6.19.3/source/kernel/workqueue.c#L7417
And I guess the further argument is that we have a use-case for ordered workqueues?
In the context of
GPU drivers often need to create their own workqueues for various reasons. Add the ability to do so.
I think we do.
Depending on the final implementation details and the driver it may be needed by the job queue.
They are also pretty common outside the scheduler use-case in GPU drivers. I think panthor has one as well, so you might also need one in Tyr. In nova-core I expect this to be used in MM code.
But even without that, I think it would be reasonble to consider ordered queues for this abstraction, since alloc_ordered_workqueue() and create_singlethread_workqueue() seem to have more users than the non-ordered constructors (without checking whether alloc_workqueue() is also used directly to create ordered queues).
Alice Ryhl
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 20 lines · Danilo Krummrich
Quoted 19 lines · Alice Ryhl
Quoted 7 lines · Danilo Krummrich
Quoted 6 lines · Alice Ryhl
Quoted 5 lines · Danilo Krummrich
Quoted 3 lines · Alice Ryhl
rust/kernel/workqueue.rs +3
-0+#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX );Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?What's wrong with just making use of the C-side warning?
IIRC, we have the same pattern in other Rust code that we use
debug_assert()when a value got clamped, e.g. inudelay().In
udelay(), the clamping happens on the Rust side, so it makes sense that Rust is the one to warn about it.
Quoted 8 lines
Quoted 2 lines
Here, the clamping happens in C code. To warn about it, I'd have to duplicate the existing C-side check to clamp in Rust.
That's fair, although I also think that it is not unreasonable. Given that this uses the builder pattern, I think it would be nice to ensure that nothing "invalid" can be built in the first place.
Maybe we can use a bounded integer?
Bounded integers allow zero, which is also illegal.
I think it's a bit much honestly.
Quoted 34 lines
Quoted 13 lines
Quoted 13 lines
Quoted 9 lines
Quoted 6 lines
It's also a bit unfortunate that
alloc_ordered_workqueue()becomes .max_active(1).At the same time having a separate
ordered()method competes withmax_active().Mybe a type state, i.e. Builder<Ordered> that doesn't have
max_active()?Sorry I'm a bit confused by this. Why does an
ordered()compete withmax_active()?Because you could get an inconsistent state with
__WQ_ORDEREDandmax_active> 1.Quoted 6 lines
Quoted 3 lines
It also conflicts with
sysfs()I think [1].[1] https://elixir.bootlin.com/linux/v6.19.3/source/kernel/workqueue.c#L7417
And I guess the further argument is that we have a use-case for ordered workqueues?
In the context of
GPU drivers often need to create their own workqueues for various reasons. Add the ability to do so.
I think we do.
Depending on the final implementation details and the driver it may be needed by the job queue.
They are also pretty common outside the scheduler use-case in GPU drivers. I think panthor has one as well, so you might also need one in Tyr. In nova-core I expect this to be used in MM code.
Quoted 5 lines
But even without that, I think it would be reasonble to consider ordered queues for this abstraction, since
alloc_ordered_workqueue()andcreate_singlethread_workqueue()seem to have more users than the non-ordered constructors (without checking whetheralloc_workqueue()is also used directly to create ordered queues).
Ok, I'll consider this for next version.
Alice
Re: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 28 lines · Alice Ryhl
Quoted 27 lines · Danilo Krummrich
Quoted 13 lines · Alice Ryhl
Quoted 12 lines · Danilo Krummrich
Quoted 10 lines · Alice Ryhl
Quoted 7 lines · Danilo Krummrich
Quoted 3 lines · Alice Ryhl
+ #[inline]
+ pub fnmax_active(mut self, max_active: u32)-> Builder { + self.max_active = i32::try_from(max_active).unwrap_or(i32::MAX);The workqueue code prints a warning for
max_active>WQ_MAX_ACTIVE. Maybe usedebug_assert()?What's wrong with just making use of the C-side warning?
Quoted 8 lines
Quoted 2 lines
IIRC, we have the same pattern in other Rust code that we use
debug_assert()when a value got clamped, e.g. inudelay().In
udelay(), the clamping happens on the Rust side, so it makes sense that Rust is the one to warn about it.Here, the clamping happens in C code. To warn about it, I'd have to duplicate the existing C-side check to clamp in Rust.
That's fair, although I also think that it is not unreasonable. Given that this uses the builder pattern, I think it would be nice to ensure that nothing "invalid" can be built in the first place.
Quoted 5 lines
Quoted 1 line
Maybe we can use a bounded integer?
Bounded integers allow zero, which is also illegal.
I think it's a bit much honestly.
My two cents here would be too that it's more elegant to just leverage the C side's warning.
P.
Gary Guo
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
In reply to Alice RyhlQuoted 74 lines · Alice Ryhl
Creating workqueues is needed by various GPU drivers. Not only does it give you better control over execution, it also allows devices to ensure that all tasks have exited before the device is unbound (or similar) by running the workqueue destructor.
Signed-off-by Alice Ryhl <[email protected]>--- rust/helpers/workqueue.c | 7 ++ rust/kernel/workqueue.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 3 deletions(-)rust/helpers/workqueue.c +7
-0@@ -14,3 +14,10 @@ __rust_helper void rust_helper_init_work_with_key(struct work_struct *work,INIT_LIST_HEAD (&work->entry); work->func = func; } + +__rust_helper +struct workqueue_struct *rust_helper_alloc_workqueue(const char *fmt, unsigned int flags, +int max_active,const void *data) +{ +return alloc_workqueue (fmt, flags, max_active, data); +}rust/kernel/workqueue.rs +16
-3@@ -186,7 +186,10 @@//! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h) use crate ::{ - alloc::{AllocError ,Flags }, + alloc::{ + self, +AllocError ,// + }, container_of, prelude::*, sync::Arc ,@@ -194,7 +197,11 @@ time::Jiffies, types::Opaque, }; -use core::marker::PhantomData ; +use core::{ + marker::PhantomData , + ops::Deref , + ptr::NonNull ,// +};/// Creates a [`Work`] initialiser with the given name and a newly-created lock class. #[macro_export] @@ -340,7 +347,7 @@ pub fn enqueue_delayed<W, const ID: u64>(/// This method can fail because it allocates memory to store the work item. pub fn try_spawn <T : 'static +Send +FnOnce ()>( &self, - flags:Flags , + flags: alloc::Flags , func:T , ) ->Result <(),AllocError > { let init = pin_init!(ClosureWork {@@ -353,6 +360,183 @@ pub fn try_spawn<T: 'static + Send + FnOnce()>( } } +/// Workqueue builder. +/// +/// A valid combination of workqueue flags contains one of the base flags (`WQ_UNBOUND`, `WQ_BH`, +/// or `WQ_PERCPU`) and a combination of modifier flags that are compatible with the selected base +/// flag. +///
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 378
Quoted 10 lines
rust/kernel/workqueue.rs +10
-0+/// For details, please refer to `Documentation/core-api/workqueue.rst`. +pub struct Builder { + flags: bindings::wq_flags , + max_active:i32 , +} + +impl Builder { +/// Not bound to any cpu. +#[inline] +pub fn unbound () ->Builder {
These can all be "-> Self".
Quoted 81 lines
rust/kernel/workqueue.rs +81
-0+Builder { + flags: bindings::wq_flags_WQ_UNBOUND, + max_active:0 , + } + } + +/// Bound to a specific cpu. +#[inline] +pub fn percpu () ->Builder { +Builder { + flags: bindings::wq_flags_WQ_PERCPU, + max_active:0 , + } + } + +/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`. +#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX ); + self + } + +/// Allow this workqueue to be frozen during suspend. +#[inline] +pub fn freezable (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_FREEZABLE; + self + } + +/// This workqueue may be used during memory reclaim. +#[inline] +pub fn mem_reclaim (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_MEM_RECLAIM; + self + } + +/// Mark this workqueue as cpu intensive. +#[inline] +pub fn cpu_intensive (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_CPU_INTENSIVE; + self + } + +/// Make this workqueue visible in sysfs. +#[inline] +pub fn sysfs (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_SYSFS; + self + } + +/// Mark this workqueue high priority. +#[inline] +pub fn highpri (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_HIGHPRI; + self + } + +/// Allocates a new workqueue. +/// +/// The provided name is used verbatim as the workqueue name. +/// +/// # Examples +/// +/// ``` +/// use kernel::workqueue; +/// +/// // create an unbound workqueue registered with sysfs +/// let wq = workqueue::Builder::unbound().sysfs().build(c"my-wq")?; +/// +/// // spawn a work item on it +/// wq.try_spawn( +/// GFP_KERNEL, +/// || pr_warn!("Printing from my-wq"), +/// )?; +/// # Ok::<(), Error>(()) +/// ``` +#[inline] +pub fn build (self, name: &CStr ) ->Result <OwnedQueue ,AllocError > { +// SAFETY:
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 469
Quoted 10 lines
rust/kernel/workqueue.rs +10
-0+// * c"%s" is compatible with passing the name as a c-string. +// * the builder only permits valid flag combinations +let ptr =unsafe { + bindings::alloc_workqueue ( +c"%s" .as_char_ptr (), + self.flags, + self.max_active, + name.as_char_ptr ().cast ::<c_void >(), + ) + };
INVARIANT comments?
Quoted 56 lines
rust/kernel/workqueue.rs +56
-0+ +Ok (OwnedQueue { + queue:NonNull ::new (ptr).ok_or (AllocError )?.cast (), + }) + } + +/// Allocates a new workqueue. +/// +/// # Examples +/// +/// This example shows how to pass a Rust string formatter to the workqueue name, creating +/// workqueues with names such as `my-wq-1` and `my-wq-2`. +/// +/// ``` +/// use kernel::{ +/// alloc::AllocError, +/// workqueue::{self, OwnedQueue}, +/// }; +/// +/// fn my_wq(num: u32) -> Result<OwnedQueue, AllocError> { +/// // create a percpu workqueue called my-wq-{num} +/// workqueue::Builder::percpu().build_fmt(fmt!("my-wq-{num}")) +/// } +/// ``` +#[inline] +pub fn build_fmt (self, name: kernel::fmt::Arguments <'_>) ->Result <OwnedQueue ,AllocError > { +// SAFETY: +// * c"%pA" is compatible with passing an `Arguments` pointer. +// * the builder only permits valid flag combinations +let ptr =unsafe { + bindings::alloc_workqueue ( +c"%pA" .as_char_ptr (), + self.flags, + self.max_active, + core::ptr::from_ref (&name).cast ::<c_void >(), + ) + }; + +Ok (OwnedQueue { + queue:NonNull ::new (ptr).ok_or (AllocError )?.cast (), + }) + } +} + +/// An owned kernel work queue. +/// +/// Dropping a workqueue blocks on all pending work. +/// +/// # Invariants +/// +/// `queue` points at a valid workqueue that is owned by this `OwnedQueue`. +pub struct OwnedQueue { + queue:NonNull <Queue >, +} + +impl Deref for OwnedQueue {
rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 535
Quoted 10 lines
rust/kernel/workqueue.rs +10
-0+type Target =Queue ; +fn deref (&self) -> &Queue { +// SAFETY: By the type invariants, this pointer references a valid queue. +unsafe { &*self.queue.as_ptr () } + } +} + +impl Drop for OwnedQueue { +fn drop (&mut self) { +// SAFETY: The `OwnedQueue` is being destroyed, so we can destroy the workqueue it owns.
Hmm, is this correct? This should say *why* the call is safe, not what it is doing.
I think this should mention: (1) the pointer is valid and (2) no delayed work is being scheduled on this queue.
Best,
Gary
Quoted 7 lines
rust/kernel/workqueue.rs +4
-0+unsafe { bindings::destroy_workqueue (self.queue.as_ptr ().cast ()) } + } +} +/// A helper type used in [`try_spawn`]. /// /// [`try_spawn`]: Queue::try_spawn
Alice Ryhl
reviewerRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
Quoted 252 lines · Gary Guo
Quoted 74 lines · Alice Ryhl
Creating workqueues is needed by various GPU drivers. Not only does it give you better control over execution, it also allows devices to ensure that all tasks have exited before the device is unbound (or similar) by running the workqueue destructor.
Signed-off-by Alice Ryhl <[email protected]>--- rust/helpers/workqueue.c | 7 ++ rust/kernel/workqueue.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 3 deletions(-)rust/helpers/workqueue.c +7
-0@@ -14,3 +14,10 @@ __rust_helper void rust_helper_init_work_with_key(struct work_struct *work,INIT_LIST_HEAD (&work->entry); work->func = func; } + +__rust_helper +struct workqueue_struct *rust_helper_alloc_workqueue(const char *fmt, unsigned int flags, +int max_active,const void *data) +{ +return alloc_workqueue (fmt, flags, max_active, data); +}rust/kernel/workqueue.rs +16
-3@@ -186,7 +186,10 @@//! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h) use crate ::{ - alloc::{AllocError ,Flags }, + alloc::{ + self, +AllocError ,// + }, container_of, prelude::*, sync::Arc ,@@ -194,7 +197,11 @@ time::Jiffies, types::Opaque, }; -use core::marker::PhantomData ; +use core::{ + marker::PhantomData , + ops::Deref , + ptr::NonNull ,// +};/// Creates a [`Work`] initialiser with the given name and a newly-created lock class. #[macro_export] @@ -340,7 +347,7 @@ pub fn enqueue_delayed<W, const ID: u64>(/// This method can fail because it allocates memory to store the work item. pub fn try_spawn <T : 'static +Send +FnOnce ()>( &self, - flags:Flags , + flags: alloc::Flags , func:T , ) ->Result <(),AllocError > { let init = pin_init!(ClosureWork {@@ -353,6 +360,183 @@ pub fn try_spawn<T: 'static + Send + FnOnce()>( } } +/// Workqueue builder. +/// +/// A valid combination of workqueue flags contains one of the base flags (`WQ_UNBOUND`, `WQ_BH`, +/// or `WQ_PERCPU`) and a combination of modifier flags that are compatible with the selected base +/// flag. +/// rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 378
Quoted 10 lines
rust/kernel/workqueue.rs +10
-0+/// For details, please refer to `Documentation/core-api/workqueue.rst`. +pub struct Builder { + flags: bindings::wq_flags , + max_active:i32 , +} + +impl Builder { +/// Not bound to any cpu. +#[inline] +pub fn unbound () ->Builder {These can all be "-> Self".
Quoted 81 lines
rust/kernel/workqueue.rs +81
-0+Builder { + flags: bindings::wq_flags_WQ_UNBOUND, + max_active:0 , + } + } + +/// Bound to a specific cpu. +#[inline] +pub fn percpu () ->Builder { +Builder { + flags: bindings::wq_flags_WQ_PERCPU, + max_active:0 , + } + } + +/// Set the maximum number of active cpus. +/// +/// If not set, a reasonable default value is used. The maximum value is `WQ_MAX_ACTIVE`. +#[inline] +pub fn max_active (mut self, max_active:u32 ) ->Builder { + self.max_active = i32::try_from (max_active).unwrap_or (i32::MAX ); + self + } + +/// Allow this workqueue to be frozen during suspend. +#[inline] +pub fn freezable (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_FREEZABLE; + self + } + +/// This workqueue may be used during memory reclaim. +#[inline] +pub fn mem_reclaim (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_MEM_RECLAIM; + self + } + +/// Mark this workqueue as cpu intensive. +#[inline] +pub fn cpu_intensive (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_CPU_INTENSIVE; + self + } + +/// Make this workqueue visible in sysfs. +#[inline] +pub fn sysfs (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_SYSFS; + self + } + +/// Mark this workqueue high priority. +#[inline] +pub fn highpri (mut self) ->Self { + self.flags |= bindings::wq_flags_WQ_HIGHPRI; + self + } + +/// Allocates a new workqueue. +/// +/// The provided name is used verbatim as the workqueue name. +/// +/// # Examples +/// +/// ``` +/// use kernel::workqueue; +/// +/// // create an unbound workqueue registered with sysfs +/// let wq = workqueue::Builder::unbound().sysfs().build(c"my-wq")?; +/// +/// // spawn a work item on it +/// wq.try_spawn( +/// GFP_KERNEL, +/// || pr_warn!("Printing from my-wq"), +/// )?; +/// # Ok::<(), Error>(()) +/// ``` +#[inline] +pub fn build (self, name: &CStr ) ->Result <OwnedQueue ,AllocError > { +// SAFETY: rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 469
Quoted 10 lines
rust/kernel/workqueue.rs +10
-0+// * c"%s" is compatible with passing the name as a c-string. +// * the builder only permits valid flag combinations +let ptr =unsafe { + bindings::alloc_workqueue ( +c"%s" .as_char_ptr (), + self.flags, + self.max_active, + name.as_char_ptr ().cast ::<c_void >(), + ) + };INVARIANT comments?
Quoted 56 lines
rust/kernel/workqueue.rs +56
-0+ +Ok (OwnedQueue { + queue:NonNull ::new (ptr).ok_or (AllocError )?.cast (), + }) + } + +/// Allocates a new workqueue. +/// +/// # Examples +/// +/// This example shows how to pass a Rust string formatter to the workqueue name, creating +/// workqueues with names such as `my-wq-1` and `my-wq-2`. +/// +/// ``` +/// use kernel::{ +/// alloc::AllocError, +/// workqueue::{self, OwnedQueue}, +/// }; +/// +/// fn my_wq(num: u32) -> Result<OwnedQueue, AllocError> { +/// // create a percpu workqueue called my-wq-{num} +/// workqueue::Builder::percpu().build_fmt(fmt!("my-wq-{num}")) +/// } +/// ``` +#[inline] +pub fn build_fmt (self, name: kernel::fmt::Arguments <'_>) ->Result <OwnedQueue ,AllocError > { +// SAFETY: +// * c"%pA" is compatible with passing an `Arguments` pointer. +// * the builder only permits valid flag combinations +let ptr =unsafe { + bindings::alloc_workqueue ( +c"%pA" .as_char_ptr (), + self.flags, + self.max_active, + core::ptr::from_ref (&name).cast ::<c_void >(), + ) + }; + +Ok (OwnedQueue { + queue:NonNull ::new (ptr).ok_or (AllocError )?.cast (), + }) + } +} + +/// An owned kernel work queue. +/// +/// Dropping a workqueue blocks on all pending work. +/// +/// # Invariants +/// +/// `queue` points at a valid workqueue that is owned by this `OwnedQueue`. +pub struct OwnedQueue { + queue:NonNull <Queue >, +} + +impl Deref for OwnedQueue {rust/kernel/workqueue.rs pub fn try_spawn<T: 'static + Send + FnOnce()>( · line 535
Quoted 10 lines
rust/kernel/workqueue.rs +10
-0+type Target =Queue ; +fn deref (&self) -> &Queue { +// SAFETY: By the type invariants, this pointer references a valid queue. +unsafe { &*self.queue.as_ptr () } + } +} + +impl Drop for OwnedQueue { +fn drop (&mut self) { +// SAFETY: The `OwnedQueue` is being destroyed, so we can destroy the workqueue it owns. Hmm, is this correct? This should say *why* the call is safe, not what it is doing.
Quoted 2 lines
I think this should mention: (1) the pointer is valid and (2) no delayed work is being scheduled on this queue.
Although it is missing the part about delayed work, I do think this talks about why. The pointer being valid is far insufficient. We need actual ownership of the workqueue - nobody can use it after this. We can destroy the inner workqueue *because* the OwnedQueue owning it is being dropped.
Alice
kernel test robot
build botRe: [PATCH v3 2/2] rust: workqueue: add creation of workqueues
In reply to Alice Ryhlkernel test robot noticed the following build warnings:
Hi Alice,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f]
url: https://github.com/intel-lab-lkp/linux/commits/Alice-Ryhl/rust-workqueue-restrict-delayed-work-to-global-wqs/20260227-230029 base: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f patch link: https://lore.kernel.org/r/20260227-create-workqueue-v3-2-87de133f7849%40google.com patch subject: [PATCH v3 2/2] rust: workqueue: add creation of workqueues config: riscv-randconfig-r063-20260228 (https://download.01.org/0day-ci/archive/20260228/[email protected]/config) compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 9a109fbb6e184ec9bcce10615949f598f4c974a9) rustc: rustc 1.88.0 (6b00bc388 2025-06-23) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260228/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> clang diag: include/linux/workqueue.h:513:76: warning: diagnostic behavior may be improved by adding the 'format(printf, 1, 4)' attribute to the declaration of 'rust_helper_alloc_workqueue' [-Wmissing-format-attribute]
--
In file included from rust/helpers/helpers.c:66:
>> rust/helpers/workqueue.c:22:9: warning: diagnostic behavior may be improved by adding the 'format(printf, 1, 4)' attribute to the declaration of 'rust_helper_alloc_workqueue' [-Wmissing-format-attribute]
19 | struct workqueue_struct *rust_helper_alloc_workqueue(const char *fmt, unsigned int flags,
| __attribute__((format(printf, 1, 4)))
20 | int max_active, const void *data)
21 | {
22 | return alloc_workqueue(fmt, flags, max_active, data);
| ^
include/linux/workqueue.h:513:76: note: expanded from macro 'alloc_workqueue'
513 | #define alloc_workqueue(...) alloc_hooks(alloc_workqueue_noprof(__VA_ARGS__))
| ^
include/linux/alloc_tag.h:265:31: note: expanded from macro 'alloc_hooks'
265 | alloc_hooks_tag(&_alloc_tag, _do_alloc); \
| ^
include/linux/alloc_tag.h:251:9: note: expanded from macro 'alloc_hooks_tag'
251 | typeof(_do_alloc) _res; \
| ^ rust/helpers/workqueue.c:19:26: note: 'rust_helper_alloc_workqueue' declared here
19 | struct workqueue_struct *rust_helper_alloc_workqueue(const char *fmt, unsigned int flags,
| ^
1 warning generated.
--
llvm-nm: error: rust/helpers/helpers.o: No such file or directory
>> clang diag: include/linux/workqueue.h:513:76: warning: diagnostic behavior may be improved by adding the 'format(printf, 1, 4)' attribute to the declaration of 'rust_helper_alloc_workqueue' [-Wmissing-format-attribute]
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki