From patchwork Fri Jun 5 15:01:10 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Petr Mladek X-Patchwork-Id: 481368 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D04931401F6 for ; Sat, 6 Jun 2015 01:06:50 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Z0tBO-0004dJ-EU; Fri, 05 Jun 2015 15:05:18 +0000 Received: from merlin.infradead.org ([205.233.59.134]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Z0tAs-0003Hi-OT for linux-mtd@bombadil.infradead.org; Fri, 05 Jun 2015 15:04:46 +0000 Received: from cantor2.suse.de ([195.135.220.15] helo=mx2.suse.de) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Z0t8m-0006Ck-NM for linux-mtd@lists.infradead.org; Fri, 05 Jun 2015 15:02:38 +0000 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 48619ADDC; Fri, 5 Jun 2015 15:02:02 +0000 (UTC) From: Petr Mladek To: Andrew Morton , Oleg Nesterov , Tejun Heo , Ingo Molnar , Peter Zijlstra Subject: [RFC PATCH 11/18] jffs2: Convert jffs2_gcd_mtd kthread into the iterant API Date: Fri, 5 Jun 2015 17:01:10 +0200 Message-Id: <1433516477-5153-12-git-send-email-pmladek@suse.cz> X-Mailer: git-send-email 1.8.5.6 In-Reply-To: <1433516477-5153-1-git-send-email-pmladek@suse.cz> References: <1433516477-5153-1-git-send-email-pmladek@suse.cz> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150605_110236_970633_C97634A8 X-CRM114-Status: GOOD ( 21.29 ) X-Spam-Score: -6.9 (------) X-Spam-Report: SpamAssassin version 3.4.0 on merlin.infradead.org summary: Content analysis details: (-6.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [195.135.220.15 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: linux-nfs@vger.kernel.org, Borislav Petkov , Jiri Kosina , Richard Weinberger , Trond Myklebust , linux-kernel@vger.kernel.org, Steven Rostedt , Michal Hocko , Chris Mason , Petr Mladek , linux-mtd@lists.infradead.org, linux-api@vger.kernel.org, Linus Torvalds , live-patching@vger.kernel.org, Thomas Gleixner , "Paul E. McKenney" , David Woodhouse , Anna Schumaker X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org The new iterant kthread API allows to define a common checkpoint for freezing, parking, termination, and even signal handling. It will allow to maintain kthreads more easily and make the operations more reliable[*]. The kthread function is split into optional init(), func(), destroy() parts where func() is called in a cycle. The common check point is after each func() finishes. See kthread_iterant_fn() for more details. func() does not need to call schedule(). Instead, we call set_kthread_iterant_int_sleep() and let the generic code to handle the sleeping properly. There are few small functional changes: + SIGSTOP uses the standard handler and could not print debug messages any longer + there is no need to enable or define sighandler for SIGCONT; it is handled by prepare_signal() and could not get disabled + the debug message for default signal handler was removed; It is handled in kernel_do_signal() a standard way. In reality, this never happens because all signals are disabled except for the four ones enabled by kthread_sigaction(); + we call complete() instead of complete_and_exit(); do_exit() is called in kthread() anyway after the function returns. [*] In fact, there was a bug in the original code. It tried to process a non-existing signal when the system was freezing. See the common check for pending signal and freezing. Signed-off-by: Petr Mladek --- fs/jffs2/background.c | 156 ++++++++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 82 deletions(-) diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 6af076b8f60f..50c16048ba2d 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -21,99 +21,86 @@ #include #include "nodelist.h" -static int jffs2_garbage_collect_thread(void *_c) +static void jffs2_garbage_collect_thread_sighup(int sig) +{ + jffs2_dbg(1, "%s(): SIGHUP received\n", __func__); +} + +static void jffs2_garbage_collect_thread_sigkill(int sig) +{ + jffs2_dbg(1, "%s(): SIGKILL received\n", __func__); + kthread_stop_current(); +} + +static void jffs2_garbage_collect_thread_init(void *_c) { struct jffs2_sb_info *c = _c; - sigset_t hupmask; - siginitset(&hupmask, sigmask(SIGHUP)); - allow_signal(SIGKILL); - allow_signal(SIGSTOP); - allow_signal(SIGCONT); - allow_signal(SIGHUP); + kthread_sigaction(SIGKILL, jffs2_garbage_collect_thread_sigkill); + kthread_sigaction(SIGHUP, jffs2_garbage_collect_thread_sighup); + kthread_sigaction(SIGSTOP, KTHREAD_SIG_DFL); c->gc_task = current; complete(&c->gc_thread_start); set_user_nice(current, 10); +}; + +static void jffs2_garbage_collect_thread_func(void *_c) +{ + struct jffs2_sb_info *c = _c; + sigset_t hupmask; + + siginitset(&hupmask, sigmask(SIGHUP)); - set_freezable(); - for (;;) { - sigprocmask(SIG_UNBLOCK, &hupmask, NULL); - again: - spin_lock(&c->erase_completion_lock); - if (!jffs2_thread_should_wake(c)) { - set_current_state (TASK_INTERRUPTIBLE); - spin_unlock(&c->erase_completion_lock); - jffs2_dbg(1, "%s(): sleeping...\n", __func__); - schedule(); - } else { - spin_unlock(&c->erase_completion_lock); - } - /* Problem - immediately after bootup, the GCD spends a lot - * of time in places like jffs2_kill_fragtree(); so much so - * that userspace processes (like gdm and X) are starved - * despite plenty of cond_resched()s and renicing. Yield() - * doesn't help, either (presumably because userspace and GCD - * are generally competing for a higher latency resource - - * disk). - * This forces the GCD to slow the hell down. Pulling an - * inode in with read_inode() is much preferable to having - * the GC thread get there first. */ - schedule_timeout_interruptible(msecs_to_jiffies(50)); - - if (kthread_should_stop()) { - jffs2_dbg(1, "%s(): kthread_stop() called\n", __func__); - goto die; - } - - /* Put_super will send a SIGKILL and then wait on the sem. - */ - while (signal_pending(current) || freezing(current)) { - siginfo_t info; - unsigned long signr; - - if (try_to_freeze()) - goto again; - - signr = dequeue_signal_lock(current, ¤t->blocked, &info); - - switch(signr) { - case SIGSTOP: - jffs2_dbg(1, "%s(): SIGSTOP received\n", - __func__); - set_current_state(TASK_STOPPED); - schedule(); - break; - - case SIGKILL: - jffs2_dbg(1, "%s(): SIGKILL received\n", - __func__); - goto die; - - case SIGHUP: - jffs2_dbg(1, "%s(): SIGHUP received\n", - __func__); - break; - default: - jffs2_dbg(1, "%s(): signal %ld received\n", - __func__, signr); - } - } - /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ - sigprocmask(SIG_BLOCK, &hupmask, NULL); - - jffs2_dbg(1, "%s(): pass\n", __func__); - if (jffs2_garbage_collect_pass(c) == -ENOSPC) { - pr_notice("No space for garbage collection. Aborting GC thread\n"); - goto die; - } + spin_lock(&c->erase_completion_lock); + if (!jffs2_thread_should_wake(c)) { + set_kthread_iterant_int_sleep(); + spin_unlock(&c->erase_completion_lock); + jffs2_dbg(1, "%s(): sleeping...\n", __func__); + return; + } + spin_unlock(&c->erase_completion_lock); + + /* Problem - immediately after bootup, the GCD spends a lot + * of time in places like jffs2_kill_fragtree(); so much so + * that userspace processes (like gdm and X) are starved + * despite plenty of cond_resched()s and renicing. Yield() + * doesn't help, either (presumably because userspace and GCD + * are generally competing for a higher latency resource - + * disk). + * This forces the GCD to slow the hell down. Pulling an + * inode in with read_inode() is much preferable to having + * the GC thread get there first. + */ + schedule_timeout_interruptible(msecs_to_jiffies(50)); + + if (kthread_should_stop()) { + jffs2_dbg(1, "%s(): kthread_stop() called\n", __func__); + return; } - die: + + try_to_freeze(); + + /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ + sigprocmask(SIG_BLOCK, &hupmask, NULL); + + jffs2_dbg(1, "%s(): pass\n", __func__); + if (jffs2_garbage_collect_pass(c) == -ENOSPC) { + pr_notice("No space for garbage collection. Aborting GC thread\n"); + kthread_stop_current(); + } + sigprocmask(SIG_UNBLOCK, &hupmask, NULL); +} + +static void jffs2_garbage_collect_thread_destroy(void *_c) +{ + struct jffs2_sb_info *c = _c; + spin_lock(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock(&c->erase_completion_lock); - complete_and_exit(&c->gc_thread_exit, 0); + complete(&c->gc_thread_exit); } void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) @@ -126,16 +113,21 @@ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) /* This must only ever be called when no GC thread is currently running */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) { + static struct kthread_iterant kti = { + .init = jffs2_garbage_collect_thread_init, + .func = jffs2_garbage_collect_thread_func, + .destroy = jffs2_garbage_collect_thread_destroy, + }; struct task_struct *tsk; int ret = 0; BUG_ON(c->gc_task); + kti.data = c; init_completion(&c->gc_thread_start); init_completion(&c->gc_thread_exit); - tsk = kthread_run(jffs2_garbage_collect_thread, c, - "jffs2_gcd_mtd%d", c->mtd->index); + tsk = kthread_iterant_run(&kti, "jffs2_gcd_mtd%d", c->mtd->index); if (IS_ERR(tsk)) { pr_warn("fork failed for JFFS2 garbage collect thread: %ld\n", -PTR_ERR(tsk));