kthread_worker: reimplement flush_kthread_work() to allow freeing during execution
Subsystems (3)
PATCH 1/1 kthread_worker: reimplement flush_kthread_work() to allow freeing during execution
Hello,
kthread_worker was introduced together with concurrency managed workqueue to serve workqueue users which need a special dedicated worker - e.g. RT scheduling. This is minimal queue / flush / flush all iterface on top of kthread and each provided interface matches the workqueue counterpart so that switching isn't difficult.
However, one noticeable difference was that kthread_worker doesn't allow a work item to be freed while being executed. The intention was to keep the code simpler but it didn't really and the restriction is subtle and does prevent some valid use cases.
This two-patch series reimplements flush_kthread_work() so that it uses an extra work item for flushing. While this takes a bit more lines, this is easier to understand and removes the annoying difference.
This patchset contains the following two patches.
0001-kthread_worker-reorganize-to-prepare-for-flush_kthre.patch 0002-kthread_worker-reimplement-flush_kthread_work-to-all.patch
The first one is a prep patch which makes no functional changes. The second reimplements flush_kthread_work().
All current kthread_worker users are cc'd. If no one objects, I'll push it through the workqueue branch. This patchset is also available in the following git branch.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git review-kthread_worker-flush
diffstat follows. Thanks.
include/linux/kthread.h | 8 +--- kernel/kthread.c | 86 +++++++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 42 deletions(-)
Signature · 8 lines
tejun ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
PATCH 1/2 kthread_worker: reorganize to prepare for flush_kthread_work() reimplementation
Quoted 1 line
From
c9bba34243a86fb3ac82d1bdd0ce4bf796b79559Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Thu, 19 Jul 2012 13:52:53 -0700
Make the following two non-functional changes.
* Separate out insert_kthread_work() from queue_kthread_work().
* Relocate struct kthread_flush_work and kthread_flush_work_fn() definitions above flush_kthread_work().
--- kernel/kthread.c | 40 ++++++++++++++++++++++++---------------- 1 files changed, 24 insertions(+), 16 deletions(-)
kernel/kthread.c +24 -16
@@ -378,6 +378,17 @@ repeat: }EXPORT_SYMBOL_GPL (kthread_worker_fn); +/* insert @work before @pos in @worker */ +static void insert_kthread_work (struct kthread_worker *worker, +struct kthread_work *work, +struct list_head *pos) +{ +list_add_tail (&work->node, pos); + work->queue_seq++; +if (likely (worker->task)) +wake_up_process (worker->task); +} +/** * queue_kthread_work - queue a kthread_work * @worker: target kthread_worker
@@ -395,10 +406,7 @@ bool queue_kthread_work(struct kthread_worker *worker,spin_lock_irqsave (&worker->lock, flags);if (list_empty (&work->node)) { -list_add_tail (&work->node, &worker->work_list); - work->queue_seq++; -if (likely (worker->task)) -wake_up_process (worker->task); +insert_kthread_work (worker, work, &worker->work_list); ret = true; }spin_unlock_irqrestore (&worker->lock, flags);
@@ -406,6 +414,18 @@ bool queue_kthread_work(struct kthread_worker *worker, }EXPORT_SYMBOL_GPL (queue_kthread_work); +struct kthread_flush_work { +struct kthread_work work; +struct completion done; +}; + +static void kthread_flush_work_fn (struct kthread_work *work) +{ +struct kthread_flush_work *fwork = + container_of(work, struct kthread_flush_work, work); +complete (&fwork->done); +} +/** * flush_kthread_work - flush a kthread_work * @work: work to flush
@@ -436,18 +456,6 @@ void flush_kthread_work(struct kthread_work *work) }EXPORT_SYMBOL_GPL (flush_kthread_work); -struct kthread_flush_work { -struct kthread_work work; -struct completion done; -}; - -static void kthread_flush_work_fn (struct kthread_work *work) -{ -struct kthread_flush_work *fwork = - container_of(work, struct kthread_flush_work, work); -complete (&fwork->done); -} -/** * flush_kthread_worker - flush all current works on a kthread_worker * @worker: worker to flush
Signature · 9 lines
1.7.7.3 ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
Re: [PATCH 1/2] kthread_worker: reorganize to prepare for flush_kthread_work() reimplementation
Quoted 21 lines · Tejun Heo
From
c9bba34243a86fb3ac82d1bdd0ce4bf796b79559Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Thu, 19 Jul 2012 13:52:53 -0700Make the following two non-functional changes.
* Separate out
insert_kthread_work()fromqueue_kthread_work().* Relocate struct
kthread_flush_workandkthread_flush_work_fn()definitions aboveflush_kthread_work().Signed-off-by Tejun Heo <[email protected]>--- kernel/kthread.c | 40 ++++++++++++++++++++++++---------------- 1 files changed, 24 insertions(+), 16 deletions(-)kernel/kthread.c +0
-0
kernel/kthread.c repeat: · line 381
Quoted 4 lines
kernel/kthread.c +1
-0@@ -378,6 +378,17 @@ repeat: }EXPORT_SYMBOL_GPL (kthread_worker_fn); +/* insert @work before @pos in @worker */
Hi Tejun,
Would a comment that the caller should be holding worker->lock be useful here? Anyway, comment or not:
Regards,
Andy
Quoted 63 lines
kernel/kthread.c +23
-16+static void insert_kthread_work (struct kthread_worker *worker, +struct kthread_work *work, +struct list_head *pos) +{ +list_add_tail (&work->node, pos); + work->queue_seq++; +if (likely (worker->task)) +wake_up_process (worker->task); +} +/** * queue_kthread_work - queue a kthread_work * @worker: target kthread_worker @@ -395,10 +406,7 @@ bool queue_kthread_work(struct kthread_worker *worker,spin_lock_irqsave (&worker->lock, flags);if (list_empty (&work->node)) { -list_add_tail (&work->node, &worker->work_list); - work->queue_seq++; -if (likely (worker->task)) -wake_up_process (worker->task); +insert_kthread_work (worker, work, &worker->work_list); ret = true; }spin_unlock_irqrestore (&worker->lock, flags);@@ -406,6 +414,18 @@ bool queue_kthread_work(struct kthread_worker *worker, }EXPORT_SYMBOL_GPL (queue_kthread_work); +struct kthread_flush_work { +struct kthread_work work; +struct completion done; +}; + +static void kthread_flush_work_fn (struct kthread_work *work) +{ +struct kthread_flush_work *fwork = + container_of(work, struct kthread_flush_work, work); +complete (&fwork->done); +} +/** * flush_kthread_work - flush a kthread_work * @work: work to flush @@ -436,18 +456,6 @@ void flush_kthread_work(struct kthread_work *work) }EXPORT_SYMBOL_GPL (flush_kthread_work); -struct kthread_flush_work { -struct kthread_work work; -struct completion done; -}; - -static void kthread_flush_work_fn (struct kthread_work *work) -{ -struct kthread_flush_work *fwork = - container_of(work, struct kthread_flush_work, work); -complete (&fwork->done); -} -/** * flush_kthread_worker - flush all current works on a kthread_worker * @worker: worker to flush
Re: [PATCH 1/2] kthread_worker: reorganize to prepare for flush_kthread_work() reimplementation
Hello,
Quoted 8 lines · Andy Walls
Quoted 1 line
+/* insert @work before @pos in @worker */
Hi Tejun,
Would a comment that the caller should be holding
worker->lockbe useful here? Anyway, comment or not:Acked-by Andy Walls <[email protected]>
Will add lockdep_assert_held(). Thanks!
Signature · 1 line
tejun
Re: [PATCH 1/2] kthread_worker: reorganize to prepare for flush_kthread_work() reimplementation
Quoted 5 lines · Tejun Heo
Hello,
Quoted 2 lines · Andy Walls
Quoted 1 line
+/* insert @work before @pos in @worker */
Quoted 9 lines
Quoted 6 lines
Hi Tejun,
Would a comment that the caller should be holding
worker->lockbe useful here? Anyway, comment or not:Will add
lockdep_assert_held(). Thanks!
Great! Thank you.
Regards,
Andy
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
PATCH 1/2 kthread_worker: reorganize to prepare for flush_kthread_work() reimplementation
In reply to Tejun HeoQuoted 1 line
From
9a2e03d8ed518a61154f18d83d6466628e519f94Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Thu, 19 Jul 2012 13:52:53 -0700
Make the following two non-functional changes.
* Separate out insert_kthread_work() from queue_kthread_work().
* Relocate struct kthread_flush_work and kthread_flush_work_fn() definitions above flush_kthread_work().
v2: Added lockdep_assert_held() in insert_kthread_work() as suggested
by Andy Walls.
--- kernel/kthread.c | 42 ++++++++++++++++++++++++++---------------- 1 files changed, 26 insertions(+), 16 deletions(-)
kernel/kthread.c +26 -16
@@ -378,6 +378,19 @@ repeat: }EXPORT_SYMBOL_GPL (kthread_worker_fn); +/* insert @work before @pos in @worker */ +static void insert_kthread_work (struct kthread_worker *worker, +struct kthread_work *work, +struct list_head *pos) +{ +lockdep_assert_held (&worker->lock); + +list_add_tail (&work->node, pos); + work->queue_seq++; +if (likely (worker->task)) +wake_up_process (worker->task); +} +/** * queue_kthread_work - queue a kthread_work * @worker: target kthread_worker
@@ -395,10 +408,7 @@ bool queue_kthread_work(struct kthread_worker *worker,spin_lock_irqsave (&worker->lock, flags);if (list_empty (&work->node)) { -list_add_tail (&work->node, &worker->work_list); - work->queue_seq++; -if (likely (worker->task)) -wake_up_process (worker->task); +insert_kthread_work (worker, work, &worker->work_list); ret = true; }spin_unlock_irqrestore (&worker->lock, flags);
@@ -406,6 +416,18 @@ bool queue_kthread_work(struct kthread_worker *worker, }EXPORT_SYMBOL_GPL (queue_kthread_work); +struct kthread_flush_work { +struct kthread_work work; +struct completion done; +}; + +static void kthread_flush_work_fn (struct kthread_work *work) +{ +struct kthread_flush_work *fwork = + container_of(work, struct kthread_flush_work, work); +complete (&fwork->done); +} +/** * flush_kthread_work - flush a kthread_work * @work: work to flush
@@ -436,18 +458,6 @@ void flush_kthread_work(struct kthread_work *work) }EXPORT_SYMBOL_GPL (flush_kthread_work); -struct kthread_flush_work { -struct kthread_work work; -struct completion done; -}; - -static void kthread_flush_work_fn (struct kthread_work *work) -{ -struct kthread_flush_work *fwork = - container_of(work, struct kthread_flush_work, work); -complete (&fwork->done); -} -/** * flush_kthread_worker - flush all current works on a kthread_worker * @worker: worker to flush
Signature · 1 line
1.7.7.3
PATCH 2/2 kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
In reply to Tejun HeoQuoted 1 line
From
06f9a06f4aeecdb9d07014713ab41b548ae219b5Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Thu, 19 Jul 2012 13:52:53 -0700
kthread_worker provides minimalistic workqueue-like interface for users which need a dedicated worker thread (e.g. for realtime priority). It has basic queue, flush_work, flush_worker operations which mostly match the workqueue counterparts; however, due to the way flush_work() is implemented, it has a noticeable difference of not allowing work items to be freed while being executed.
While the current users of kthread_worker are okay with the current behavior, the restriction does impede some valid use cases. Also, removing this difference isn't difficult and actually makes the code easier to understand.
This patch reimplements flush_kthread_work() such that it uses a flush_work item instead of queue/done sequence numbers.
--- include/linux/kthread.h | 8 +----- kernel/kthread.c | 48 ++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 27 deletions(-)
include/linux/kthread.h +2 -6
@@ -49,8 +49,6 @@ extern int tsk_fork_get_node(struct task_struct *tsk);* can be queued and flushed using queue/flush_kthread_work() * respectively. Queued kthread_works are processed by a kthread * running kthread_worker_fn(). -* -* A kthread_work can't be freed while it is executing. */ struct kthread_work ;typedef void (*kthread_work_func_t )(struct kthread_work *work);
@@ -59,15 +57,14 @@ struct kthread_worker {spinlock_t lock;struct list_head work_list;struct task_struct *task; +struct kthread_work *current_work; };struct kthread_work {struct list_head node;kthread_work_func_t func;wait_queue_head_t done; -atomic_t flushing; -int queue_seq; -int done_seq; +struct kthread_worker *worker; };#define KTHREAD_WORKER_INIT (worker) { \
@@ -79,7 +76,6 @@ struct kthread_work { .node = LIST_HEAD_INIT((work).node), \ .func = (fn), \ .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done), \ - .flushing = ATOMIC_INIT(0), \ }#define DEFINE_KTHREAD_WORKER (worker) \
kernel/kthread.c +27 -21
75 lines not on this page · raw message
Signature · 1 line
1.7.7.3
Re: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
Quoted 16 lines · Tejun Heo
From
06f9a06f4aeecdb9d07014713ab41b548ae219b5Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Thu, 19 Jul 2012 13:52:53 -0700
kthread_workerprovides minimalistic workqueue-like interface for users which need a dedicated worker thread (e.g. for realtime priority). It has basic queue,flush_work,flush_workeroperations which mostly match the workqueue counterparts; however, due to the wayflush_work()is implemented, it has a noticeable difference of not allowing work items to be freed while being executed.While the current users of
kthread_workerare okay with the current behavior, the restriction does impede some valid use cases. Also, removing this difference isn't difficult and actually makes the code easier to understand.
Quoted 8 lines
This patch reimplements
flush_kthread_work()such that it uses aflush_workitem instead of queue/done sequence numbers.Signed-off-by Tejun Heo <[email protected]>--- include/linux/kthread.h | 8 +----- kernel/kthread.c | 48 ++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 27 deletions(-)
Hi Tejun,
I have a question and comment below.
Quoted 44 lines
include/linux/kthread.h +2
-6@@ -49,8 +49,6 @@ extern int tsk_fork_get_node(struct task_struct *tsk);* can be queued and flushed using queue/flush_kthread_work() * respectively. Queued kthread_works are processed by a kthread * running kthread_worker_fn(). -* -* A kthread_work can't be freed while it is executing. */ struct kthread_work ;typedef void (*kthread_work_func_t )(struct kthread_work *work);@@ -59,15 +57,14 @@ struct kthread_worker {spinlock_t lock;struct list_head work_list;struct task_struct *task; +struct kthread_work *current_work; };struct kthread_work {struct list_head node;kthread_work_func_t func;wait_queue_head_t done; -atomic_t flushing; -int queue_seq; -int done_seq; +struct kthread_worker *worker; };#define KTHREAD_WORKER_INIT (worker) { \@@ -79,7 +76,6 @@ struct kthread_work { .node = LIST_HEAD_INIT((work).node), \ .func = (fn), \ .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done), \ - .flushing = ATOMIC_INIT(0), \ }#define DEFINE_KTHREAD_WORKER (worker) \kernel/kthread.c +0
-0
kernel/kthread.c repeat: · line 368
Quoted 9 lines
kernel/kthread.c +1
-0@@ -360,16 +360,12 @@ repeat: struct kthread_work, node);list_del_init (&work->node); } + worker->current_work = work;spin_unlock_irq (&worker->lock);if (work) {__set_current_state (TASK_RUNNING ); work->func (work);
If the call to 'work->func(work);' frees the memory pointed to by 'work', 'worker->current_work' points to deallocated memory. So 'worker->current_work' will only ever used as a unique 'work' identifier to handle, correct?
Quoted 19 lines
kernel/kthread.c +1
-6-smp_wmb ();/* wmb worker-b0 paired with flush-b1 */ - work->done_seq = work->queue_seq; -smp_mb ();/* mb worker-b1 paired with flush-b0 */ -if (atomic_read (&work->flushing)) -wake_up_all (&work->done); }else if (!freezing (current))schedule ();@@ -384,7 +380,7 @@ static void insert_kthread_work(struct kthread_worker *worker, struct list_head *pos) {list_add_tail (&work->node, pos); - work->queue_seq++; + work->worker = worker;if (likely (worker->task))wake_up_process (worker->task); }@@ -434,25 +430,35 @@ static void kthread_flush_work_fn(struct kthread_work *work)*/
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 439
Quoted 10 lines
kernel/kthread.c +7
-1void flush_kthread_work (struct kthread_work *work) { -int seq = work->queue_seq; +struct kthread_flush_work fwork = { +KTHREAD_WORK_INIT (fwork.work, kthread_flush_work_fn), +COMPLETION_INITIALIZER_ONSTACK (fwork.done), + }; +struct kthread_worker *worker; +bool noop = false; +
You might want a check for 'work == NULL' here, to gracefully handle code like the following:
void driver_work_handler(struct kthread_work *work)
{
...
kfree(work);
}struct kthread_work *driver_queue_batch(void)
{
struct kthread_work *work = NULL;
...
while (driver_more_stuff_todo()) {
work = kzalloc(sizeof(struct kthread work), GFP_WHATEVER);
...
queue_kthread_work(&driver_worker, work);
}
return work;
}void driver_foobar(void)
{
...
flush_kthread_work(driver_queue_batch());
...
}Otherwise, things look OK to me.
Regards,
Andy
Quoted 29 lines
kernel/kthread.c +16
-9+retry: + worker = work->worker; +if (!worker) +return ; -atomic_inc (&work->flushing); +spin_lock_irq (&worker->lock); +if (work->worker != worker) { +spin_unlock_irq (&worker->lock); + goto retry; + } -/* -* mb flush-b0 paired with worker-b1, to make sure either -* worker sees the above increment or we see done_seq update. -*/ -smp_mb__after_atomic_inc (); +if (!list_empty (&work->node)) +insert_kthread_work (worker, &fwork.work, work->node.next); +else if (worker->current_work == work) +insert_kthread_work (worker, &fwork.work, worker->work_list.next); +else + noop = true; -/* A - B <= 0 tests whether B is in front of A regardless of overflow */ -wait_event (work->done, seq - work->done_seq <=0 ); -atomic_dec (&work->flushing); +spin_unlock_irq (&worker->lock);
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 464
Quoted 10 lines
kernel/kthread.c +2
-5-/* -* rmb flush-b1 paired with worker-b0, to make sure our caller -* sees every change made by work->func(). -*/ -smp_mb__after_atomic_dec (); +if (!noop) +wait_for_completion (&fwork.done); }EXPORT_SYMBOL_GPL (flush_kthread_work);
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
Re: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
Hello,
Quoted 11 lines · Andy Walls
kernel/kthread.c repeat: · line 368
Quoted 6 lines
kernel/kthread.c +1
-0+ worker->current_work = work;spin_unlock_irq (&worker->lock);if (work) {__set_current_state (TASK_RUNNING ); work->func (work);If the call to '
work->func(work);' frees the memory pointed to by 'work', 'worker->current_work' points to deallocated memory. So 'worker->current_work' will only ever used as a unique 'work' identifier to handle, correct?
Yeah. flush_kthread_work(@work) which can only be called if @work is known to be alive looks at the pointer to determine whether it's the current work item on the worker.
Quoted 32 lines
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 439
Quoted 10 lines
void
flush_kthread_work(struct kthread_work *work){kernel/kthread.c +7
-1-int seq = work->queue_seq; +struct kthread_flush_work fwork = { +KTHREAD_WORK_INIT (fwork.work, kthread_flush_work_fn), +COMPLETION_INITIALIZER_ONSTACK (fwork.done), + }; +struct kthread_worker *worker; +bool noop = false; +You might want a check for 'work == NULL' here, to gracefully handle code like the following:
void
driver_work_handler(struct kthread_work *work){ ... kfree(work); }struct
kthread_work*driver_queue_batch(void){ struct kthread_work *work = NULL; ... while (driver_more_stuff_todo()) { work = kzalloc(sizeof(struct kthread work), GFP_WHATEVER); ... queue_kthread_work(&driver_worker, work); } return work; }
Quoted 6 lines
void
driver_foobar(void){ ... flush_kthread_work(driver_queue_batch()); ... }
workqueue's flush_work() doesn't allow %NULL pointer. I don't want to make the behaviors deviate and don't see much point in changing workqueue's behavior at this point.
Thanks.
Signature · 8 lines
tejun ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
Re: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
Quoted 15 lines · Tejun Heo
Hello,
Quoted 11 lines · Andy Walls
kernel/kthread.c repeat: · line 368
Quoted 6 lines
kernel/kthread.c +1
-0+ worker->current_work = work;spin_unlock_irq (&worker->lock);if (work) {__set_current_state (TASK_RUNNING ); work->func (work);If the call to '
work->func(work);' frees the memory pointed to by 'work', 'worker->current_work' points to deallocated memory. So 'worker->current_work' will only ever used as a unique 'work' identifier to handle, correct?
Quoted 3 lines
Yeah.
flush_kthread_work(@work)which can only be called if @work is known to be alive looks at the pointer to determine whether it's the current work item on the worker.
OK. Thanks.
Hmmm, I didn't know about the constraint about 'known to be alive' in the other email I just sent.
That might make calling flush_kthread_work() hard for a user to use, if the user lets the work get freed by another thread executing the work.
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
void
flush_kthread_work(struct kthread_work *work){kernel/kthread.c +2
-1-int seq = work->queue_seq; +struct kthread_flush_work fwork = { +KTHREAD_WORK_INIT (fwork.work, kthread_flush_work_fn),Quoted 8 lines
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 439
Quoted 5 lines
kernel/kthread.c +5
-0+ COMPLETION_INITIALIZER_ONSTACK(fwork.done), + }; +struct kthread_worker *worker; +bool noop = false; +You might want a check for 'work == NULL' here, to gracefully handle code like the following:
Quoted 3 lines
workqueue's
flush_work()doesn't allow %NULL pointer. I don't want to make the behaviors deviate and don't see much point in changing workqueue's behavior at this point.
OK. Fair enough.
Thanks.
Regards,
Andy
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
Re: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
Hello,
Quoted 5 lines · Andy Walls
Hmmm, I didn't know about the constraint about 'known to be alive' in the other email I just sent.
That might make calling
flush_kthread_work()hard for a user to use, if the user lets the work get freed by another thread executing the work.
Umm... flushing a freed work item doesn't make any sense at all. The pointer itself loses the ability to identify anything. What if it gets recycled to another work item which happens to depend on the flusher to make forward progress? You now have a circular dependency through a recycled memory area. Good luck hunting that down.
For pretty much any API, allowing dangling pointers as argument is insane. If you want to flush self-freeing work items, flush the kthread_worker. That's how it is with workqueue and how it should be with kthread_worker too.
Thanks.
Signature · 1 line
tejun
Andy Walls
Reviewed-byRe: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
Quoted 15 lines · Tejun Heo
Hello,
Quoted 5 lines · Andy Walls
Hmmm, I didn't know about the constraint about 'known to be alive' in the other email I just sent.
That might make calling
flush_kthread_work()hard for a user to use, if the user lets the work get freed by another thread executing the work.Umm... flushing a freed work item doesn't make any sense at all. The pointer itself loses the ability to identify anything. What if it gets recycled to another work item which happens to depend on the flusher to make forward progress? You now have a circular dependency through a recycled memory area. Good luck hunting that down.
Quoted 4 lines
For pretty much any API, allowing dangling pointers as argument is insane. If you want to flush self-freeing work items, flush the
kthread_worker. That's how it is with workqueue and how it should be withkthread_workertoo.
Hi,
Ah. My problem was that I mentally assigned the wrong rationale for why you reworked flush_kthread_work().
Thank you for your patience and explanations.
Sorry for the noise.
For patch 2/2:
Regards,
Andy
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
Re: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
In reply to Tejun HeoHi Tejun,
Thanks for responding to my previous questions. I have one more.
Quoted 11 lines · Andy Walls
Quoted 10 lines · Tejun Heo
From
06f9a06f4aeecdb9d07014713ab41b548ae219b5Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Thu, 19 Jul 2012 13:52:53 -0700
kthread_workerprovides minimalistic workqueue-like interface for users which need a dedicated worker thread (e.g. for realtime priority). It has basic queue,flush_work,flush_workeroperations which mostly match the workqueue counterparts; however, due to the wayflush_work()is implemented, it has a noticeable difference of not allowing work items to be freed while being executed.
[snip]
Quoted 40 lines
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 439
Quoted 12 lines
kernel/kthread.c +7
-1@@ -434,25 +430,35 @@ static void kthread_flush_work_fn(struct kthread_work *work)*/ void flush_kthread_work (struct kthread_work *work) { -int seq = work->queue_seq; +struct kthread_flush_work fwork = { +KTHREAD_WORK_INIT (fwork.work, kthread_flush_work_fn), +COMPLETION_INITIALIZER_ONSTACK (fwork.done), + }; +struct kthread_worker *worker; +bool noop = false; +You might want a check for 'work == NULL' here, to gracefully handle code like the following:
void
driver_work_handler(struct kthread_work *work){ ... kfree(work); }struct
kthread_work*driver_queue_batch(void){ struct kthread_work *work = NULL; ... while (driver_more_stuff_todo()) { work = kzalloc(sizeof(struct kthread work), GFP_WHATEVER); ... queue_kthread_work(&driver_worker, work); } return work; }void
driver_foobar(void){ ... flush_kthread_work(driver_queue_batch()); ... }
[snip]
Quoted 12 lines
Quoted 12 lines
kernel/kthread.c +9
-1+retry: + worker = work->worker; +if (!worker) +return ; -atomic_inc (&work->flushing); +spin_lock_irq (&worker->lock); +if (work->worker != worker) { +spin_unlock_irq (&worker->lock); + goto retry; + }
Quoted 11 lines
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 456
Quoted 11 lines
kernel/kthread.c +6
-5-/* -* mb flush-b0 paired with worker-b1, to make sure either -* worker sees the above increment or we see done_seq update. -*/ -smp_mb__after_atomic_inc (); +if (!list_empty (&work->node)) +insert_kthread_work (worker, &fwork.work, work->node.next); +else if (worker->current_work == work) +insert_kthread_work (worker, &fwork.work, worker->work_list.next); +else + noop = true;
The objective is "allowing work items to be freed while being executed", to me, it does not seem safe to me to allow flush_kthread_work() to actually dereference the passed in work pointer.
flush_kthread_work() could theoretically be executed after the work function was executed by the worker kthread which frees the 'work' object, and that the memory 'work' points to could theoretically already be reallocated for something else. (I admit the above likely has very low probability of occuring.)
Is there a way to avoid dereferencing 'work' here?
Regards,
Andy
Quoted 6 lines
Quoted 6 lines
kernel/kthread.c +1
-3-/* A - B <= 0 tests whether B is in front of A regardless of overflow */ -wait_event (work->done, seq - work->done_seq <=0 ); -atomic_dec (&work->flushing); +spin_unlock_irq (&worker->lock);
Quoted 11 lines
kernel/kthread.c static void kthread_flush_work_fn(struct kthread_work *work) · line 463
Quoted 10 lines
kernel/kthread.c +2
-5-/* -* rmb flush-b1 paired with worker-b0, to make sure our caller -* sees every change made by work->func(). -*/ -smp_mb__after_atomic_dec (); +if (!noop) +wait_for_completion (&fwork.done); }EXPORT_SYMBOL_GPL (flush_kthread_work);
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
Quoted 32 lines · Tejun Heo
Hello,
kthread_workerwas introduced together with concurrency managed workqueue to serve workqueue users which need a special dedicated worker - e.g. RT scheduling. This is minimal queue / flush / flush all iterface on top of kthread and each provided interface matches the workqueue counterpart so that switching isn't difficult.However, one noticeable difference was that
kthread_workerdoesn't allow a work item to be freed while being executed. The intention was to keep the code simpler but it didn't really and the restriction is subtle and does prevent some valid use cases.This two-patch series reimplements
flush_kthread_work()so that it uses an extra work item for flushing. While this takes a bit more lines, this is easier to understand and removes the annoying difference.This patchset contains the following two patches.
0001-kthread_worker-reorganize-to-prepare-for-flush_kthre.patch 0002-kthread_worker-reimplement-flush_kthread_work-to-all.patch
The first one is a prep patch which makes no functional changes. The second reimplements
flush_kthread_work().All current
kthread_workerusers are cc'd. If no one objects, I'll push it through the workqueue branch. This patchset is also available in the following git branch.
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.gitreview-kthread_worker-flush
Quoted 8 lines
diffstat follows. Thanks.
include/linux/kthread.h | 8 +--- kernel/kthread.c | 86 +++++++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 42 deletions(-)--
tejun
This patch set fixes a reproducible crash I'm seeing on a 3.4.10 kernel. flush_kthread_worker (which is different from flush_kthread_work) is initializing a kthread_work and a completion on the stack, then queuing it and calling wait_for_completion. Once the completion is signaled, flush_kthread_worker exits and the stack region used by the kthread_work may be immediately reused by another object on the stack, but kthread_worker_fn continues accessing its work pointer:
work->func(work); <- calls complete,
effectively frees work
smp_wmb(); /* wmb worker-b0 paired with flush-b1 */
work->done_seq = work->queue_seq; <- overwrites a
new stack object
smp_mb(); /* mb worker-b1 paired with flush-b0 */
if (atomic_read(&work->flushing))
wake_up_all(&work->done); <- or crashes hereThese patches fix the problem by not accessing work after work->func is called, and should be backported to stable. They apply cleanly to 3.4.10. Upstream commits are 9a2e03d8ed518a61154f18d83d6466628e519f94 and 46f3d976213452350f9d10b0c2780c2681f7075b.
Quoted 17 lines · Colin Cross
This patch set fixes a reproducible crash I'm seeing on a 3.4.10 kernel.
flush_kthread_worker(which is different fromflush_kthread_work) is initializing akthread_workand a completion on the stack, then queuing it and callingwait_for_completion. Once the completion is signaled,flush_kthread_workerexits and the stack region used by thekthread_workmay be immediately reused by another object on the stack, butkthread_worker_fncontinues accessing its work pointer:work->func(work); <- calls complete, effectively frees work smp_wmb(); /* wmb worker-b0 paired with flush-b1 */ work->done_seq = work->queue_seq; <- overwrites a new stack object smp_mb(); /* mb worker-b1 paired with flush-b0 */ if (atomic_read(&work->flushing)) wake_up_all(&work->done); <- or crashes here
Quoted 4 lines
These patches fix the problem by not accessing work after
work->funcis called, and should be backported to stable. They apply cleanly to 3.4.10. Upstream commits are9a2e03d8ed518a61154f18d83d6466628e519f94and46f3d976213452350f9d10b0c2780c2681f7075b.
Yeah, you're right. I wonder why this didn't come up before. Greg, can you please pick up these two commits?
Thanks.
Signature · 1 line
tejun
Quoted 18 lines · Tejun Heo
Quoted 17 lines · Colin Cross
This patch set fixes a reproducible crash I'm seeing on a 3.4.10 kernel.
flush_kthread_worker(which is different fromflush_kthread_work) is initializing akthread_workand a completion on the stack, then queuing it and callingwait_for_completion. Once the completion is signaled,flush_kthread_workerexits and the stack region used by thekthread_workmay be immediately reused by another object on the stack, butkthread_worker_fncontinues accessing its work pointer:work->func(work); <- calls complete, effectively frees work smp_wmb(); /* wmb worker-b0 paired with flush-b1 */ work->done_seq = work->queue_seq; <- overwrites a new stack object smp_mb(); /* mb worker-b1 paired with flush-b0 */ if (atomic_read(&work->flushing)) wake_up_all(&work->done); <- or crashes here
Quoted 7 lines
Quoted 4 lines
These patches fix the problem by not accessing work after
work->funcis called, and should be backported to stable. They apply cleanly to 3.4.10. Upstream commits are9a2e03d8ed518a61154f18d83d6466628e519f94and46f3d976213452350f9d10b0c2780c2681f7075b.Yeah, you're right. I wonder why this didn't come up before. Greg, can you please pick up these two commits?
Ok, will do, thanks for letting me know.
greg k-h
Quoted 24 lines · Greg KH
Quoted 23 lines · Tejun Heo
Quoted 21 lines · Colin Cross
This patch set fixes a reproducible crash I'm seeing on a 3.4.10 kernel.
flush_kthread_worker(which is different fromflush_kthread_work) is initializing akthread_workand a completion on the stack, then queuing it and callingwait_for_completion. Once the completion is signaled,flush_kthread_workerexits and the stack region used by thekthread_workmay be immediately reused by another object on the stack, butkthread_worker_fncontinues accessing its work pointer:work->func(work); <- calls complete, effectively frees work smp_wmb(); /* wmb worker-b0 paired with flush-b1 */ work->done_seq = work->queue_seq; <- overwrites a new stack object smp_mb(); /* mb worker-b1 paired with flush-b0 */ if (atomic_read(&work->flushing)) wake_up_all(&work->done); <- or crashes hereThese patches fix the problem by not accessing work after
work->funcis called, and should be backported to stable. They apply cleanly to 3.4.10. Upstream commits are9a2e03d8ed518a61154f18d83d6466628e519f94and46f3d976213452350f9d10b0c2780c2681f7075b.
Quoted 4 lines
Quoted 2 lines
Yeah, you're right. I wonder why this didn't come up before. Greg, can you please pick up these two commits?
Ok, will do, thanks for letting me know.
Now applied, thanks.
greg k-h
------------------------------------------------------------------------------
Got visibility?
Most devs has no idea what their production app looks like. Find out how fast your code is with AppDynamics Lite.
http://ad.doubleclick.net/clk;262219671;13503038;y?
http://info.appdynamics.com/FreeJavaPerformanceDownload.html