From patchwork Thu Jul 19 21:16:29 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tejun Heo X-Patchwork-Id: 1218721 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 796AB3FD48 for ; Thu, 19 Jul 2012 21:16:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752287Ab2GSVQg (ORCPT ); Thu, 19 Jul 2012 17:16:36 -0400 Received: from mail-pb0-f46.google.com ([209.85.160.46]:50155 "EHLO mail-pb0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751525Ab2GSVQd (ORCPT ); Thu, 19 Jul 2012 17:16:33 -0400 Received: by pbbrp8 with SMTP id rp8so4952403pbb.19 for ; Thu, 19 Jul 2012 14:16:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:date:from:to:cc:subject:message-id:references:mime-version :content-type:content-disposition:in-reply-to:user-agent; bh=5LT4rnnJE2IU53SlPPhtj2VRhyaB3Qit6CtB9v4PBEQ=; b=E6J7yQUr3jBDzVkYkeC6HmtjwrTfkiZmEYjMlStOIBGsL7iiiOeNZwyL9sf0ZyS7vF VdP4DQLsGdZ3/Sni0DJaNRUzEFNaPKDJgwzXHubevVVtAuEykGwZQgfOEK2ur9jQ7nvl AqIzpfuA7f7EgLkeYKnnoIvwKdZHTNjHWcbnJOnIhq1aEwSxlImRk9y8wsRmgrpjSRnx T3IT4jJEc/NBqqWbjFCOgOD/VYRejXJKt2MQqmISrmJrcrglhB1pRjdvo0BZZfIO99tt GW7buFFakc2bPFPm2/PHuNKv/rY4FdSwYNa2B3S2f1rDsB3U/wMGChq4mrRmI68VJydQ yGxQ== Received: by 10.68.223.129 with SMTP id qu1mr2221477pbc.165.1342732593341; Thu, 19 Jul 2012 14:16:33 -0700 (PDT) Received: from google.com (wtj.mtv.corp.google.com [172.18.110.84]) by mx.google.com with ESMTPS id jv6sm2464426pbc.40.2012.07.19.14.16.31 (version=SSLv3 cipher=OTHER); Thu, 19 Jul 2012 14:16:32 -0700 (PDT) Date: Thu, 19 Jul 2012 14:16:29 -0700 From: Tejun Heo To: linux-kernel@vger.kernel.org Cc: Andrew Morton , Avi Kivity , kvm@vger.kernel.org, Andy Walls , ivtv-devel@ivtvdriver.org, linux-media@vger.kernel.org, Grant Likely , spi-devel-general@lists.sourceforge.net, Linus Torvalds Subject: [PATCH 2/2] kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed Message-ID: <20120719211629.GC32763@google.com> References: <20120719211510.GA32763@google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20120719211510.GA32763@google.com> User-Agent: Mutt/1.5.20 (2009-06-14) Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From 06f9a06f4aeecdb9d07014713ab41b548ae219b5 Mon Sep 17 00:00:00 2001 From: Tejun Heo 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. Signed-off-by: Tejun Heo Reviewed-by: Andy Walls --- include/linux/kthread.h | 8 +----- kernel/kthread.c | 48 ++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 0714b24..22ccf9d 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -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) \ diff --git a/kernel/kthread.c b/kernel/kthread.c index 7b8a678..4034b2b 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -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); - 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) */ 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);