Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 316726
b: refs/heads/master
c: 46f3d97
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo committed Jul 22, 2012
1 parent b75bcf3 commit f23cd02
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 28 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 9a2e03d8ed518a61154f18d83d6466628e519f94
refs/heads/master: 46f3d976213452350f9d10b0c2780c2681f7075b
8 changes: 2 additions & 6 deletions trunk/include/linux/kthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) { \
Expand All @@ -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) \
Expand Down
48 changes: 27 additions & 21 deletions trunk/kernel/kthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,12 @@ int kthread_worker_fn(void *worker_ptr)
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);
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();

Expand All @@ -386,7 +382,7 @@ static void insert_kthread_work(struct kthread_worker *worker,
lockdep_assert_held(&worker->lock);

list_add_tail(&work->node, pos);
work->queue_seq++;
work->worker = worker;
if (likely(worker->task))
wake_up_process(worker->task);
}
Expand Down Expand Up @@ -436,25 +432,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;

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);

/*
* 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);

Expand Down

0 comments on commit f23cd02

Please sign in to comment.