Received: by 2002:a6b:500f:0:0:0:0:0 with SMTP id e15csp6036064iob; Tue, 10 May 2022 08:58:20 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxXkZqbplwbC2T9xsRsGXxzPuhXiQvA8jueYA980Qx0emNuy/MxIlPpQyK5bRyZzGc/ey5H X-Received: by 2002:a17:902:c14a:b0:15b:9c29:935a with SMTP id 10-20020a170902c14a00b0015b9c29935amr21224708plj.2.1652198300133; Tue, 10 May 2022 08:58:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1652198300; cv=none; d=google.com; s=arc-20160816; b=rIcgdg0wU14LqwcAx4jQqyQeEuv7PI3543v0ZYJKAhC2CvWIoDogX2XxXvWCSo6ChJ 1AxWua1BxQeM62Ay9MBLI9dxW2e79daPrlNkyLZb5kgQ5+ANFvuCI8uO6OeOqF/gN4UV 4jr0j0zhPuXM0MIRSFo5PtVPboswnVLqRhDEYwBPz0pH+RrmViYz3Cof8ZKPHTM6xoUR +oLHfEODlvZ87QbMELT4I285jWfB9oUNwVZv95RkUtO2iM6fbE4LOe8dofZjG89Kv42Q eRq1mJjSzDEV2jIjLSz5LJmpj/9GtlNM1sgKASnrTxdBU03yScu6I8/EaqgXiCe6qbH4 1RSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=AGEES+WgDgPSlNixuA78RDa+lFxyhS3Dhv+LTx+0sFM=; b=H/Z32tMocB913ZA1qMMPUH2YtRB/KRSLHjC6iRT4OQ5u8JjKbUMnCiNTB0tIRun/bP NG/dETObsMj0qqqbiZS+IuskYpVy8Mj+48kdGxI7dE8n0+GwMf3mtkw+X3UDUEmXoXqp DwmA98yiL3llayWBDhhrftO692JHNCI0KwrsypS7ae/E/dW2UW9nxMC9PkMELBKUGDaV v5eeKu8j+WT6Hk38DgZqWyssILTzNfAJCBd3IryQEUZxlPNWVCaFLiklrlc8lhnG+Bll 9g6GTocREy1Jdrq4BXmyb/+mMbM3gYXJJqp+6FAurMZiscxj4cTGKz38k604VTB2O6lE zPPQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=vRqZj3tu; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id k12-20020a170902d58c00b0015874d582f1si777346plh.326.2022.05.10.08.58.03; Tue, 10 May 2022 08:58:20 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=vRqZj3tu; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242038AbiEJNxO (ORCPT + 99 others); Tue, 10 May 2022 09:53:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55734 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243839AbiEJNgT (ORCPT ); Tue, 10 May 2022 09:36:19 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [IPv6:2604:1380:4601:e00::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6509D2D4B6A; Tue, 10 May 2022 06:25:14 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id EF11FB81DA5; Tue, 10 May 2022 13:25:11 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 64455C385A6; Tue, 10 May 2022 13:25:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1652189110; bh=m/KzUjZoMTgCKajKGydd3BJwee1yg8hQHP/84Xq6VcI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vRqZj3tuW8Xr25Awk66tKLUkMFw2E2XMGFxBHAhmyKbIwvG5GnTk3btcz8Y30oxP2 bvgKtwyBhXuJZMSmOcz/2JCfjTL9RGN+mlbYn4PTGD82CHHTicR7mvZ3VRrokbJdn5 pA8O7ZJvTRvz8ZMlG1GlPKPzC4zzSypcZxQ4U6tY= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Thomas Pfaff , Thomas Gleixner , Marc Zyngier Subject: [PATCH 5.10 16/70] genirq: Synchronize interrupt thread startup Date: Tue, 10 May 2022 15:07:35 +0200 Message-Id: <20220510130733.342313374@linuxfoundation.org> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220510130732.861729621@linuxfoundation.org> References: <20220510130732.861729621@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-7.7 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_HI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Thomas Pfaff commit 8707898e22fd665bc1d7b18b809be4b56ce25bdd upstream. A kernel hang can be observed when running setserial in a loop on a kernel with force threaded interrupts. The sequence of events is: setserial open("/dev/ttyXXX") request_irq() do_stuff() -> serial interrupt -> wake(irq_thread) desc->threads_active++; close() free_irq() kthread_stop(irq_thread) synchronize_irq() <- hangs because desc->threads_active != 0 The thread is created in request_irq() and woken up, but does not get on a CPU to reach the actual thread function, which would handle the pending wake-up. kthread_stop() sets the should stop condition which makes the thread immediately exit, which in turn leaves the stale threads_active count around. This problem was introduced with commit 519cc8652b3a, which addressed a interrupt sharing issue in the PCIe code. Before that commit free_irq() invoked synchronize_irq(), which waits for the hard interrupt handler and also for associated threads to complete. To address the PCIe issue synchronize_irq() was replaced with __synchronize_hardirq(), which only waits for the hard interrupt handler to complete, but not for threaded handlers. This was done under the assumption, that the interrupt thread already reached the thread function and waits for a wake-up, which is guaranteed to be handled before acting on the stop condition. The problematic case, that the thread would not reach the thread function, was obviously overlooked. Make sure that the interrupt thread is really started and reaches thread_fn() before returning from __setup_irq(). This utilizes the existing wait queue in the interrupt descriptor. The wait queue is unused for non-shared interrupts. For shared interrupts the usage might cause a spurious wake-up of a waiter in synchronize_irq() or the completion of a threaded handler might cause a spurious wake-up of the waiter for the ready flag. Both are harmless and have no functional impact. [ tglx: Amended changelog ] Fixes: 519cc8652b3a ("genirq: Synchronize only with single thread on free_irq()") Signed-off-by: Thomas Pfaff Signed-off-by: Thomas Gleixner Reviewed-by: Marc Zyngier Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/552fe7b4-9224-b183-bb87-a8f36d335690@pcs.com Signed-off-by: Greg Kroah-Hartman --- kernel/irq/internals.h | 2 ++ kernel/irq/irqdesc.c | 2 ++ kernel/irq/manage.c | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 10 deletions(-) --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -29,12 +29,14 @@ extern struct irqaction chained_action; * IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed * IRQTF_AFFINITY - irq thread is requested to adjust affinity * IRQTF_FORCED_THREAD - irq action is force threaded + * IRQTF_READY - signals that irq thread is ready */ enum { IRQTF_RUNTHREAD, IRQTF_WARNED, IRQTF_AFFINITY, IRQTF_FORCED_THREAD, + IRQTF_READY, }; /* --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -405,6 +405,7 @@ static struct irq_desc *alloc_desc(int i lockdep_set_class(&desc->lock, &irq_desc_lock_class); mutex_init(&desc->request_mutex); init_rcu_head(&desc->rcu); + init_waitqueue_head(&desc->wait_for_threads); desc_set_defaults(irq, desc, node, affinity, owner); irqd_set(&desc->irq_data, flags); @@ -573,6 +574,7 @@ int __init early_irq_init(void) raw_spin_lock_init(&desc[i].lock); lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); mutex_init(&desc[i].request_mutex); + init_waitqueue_head(&desc[i].wait_for_threads); desc_set_defaults(i, &desc[i], node, NULL, NULL); } return arch_early_irq_init(); --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1149,6 +1149,31 @@ static void irq_wake_secondary(struct ir } /* + * Internal function to notify that a interrupt thread is ready. + */ +static void irq_thread_set_ready(struct irq_desc *desc, + struct irqaction *action) +{ + set_bit(IRQTF_READY, &action->thread_flags); + wake_up(&desc->wait_for_threads); +} + +/* + * Internal function to wake up a interrupt thread and wait until it is + * ready. + */ +static void wake_up_and_wait_for_irq_thread_ready(struct irq_desc *desc, + struct irqaction *action) +{ + if (!action || !action->thread) + return; + + wake_up_process(action->thread); + wait_event(desc->wait_for_threads, + test_bit(IRQTF_READY, &action->thread_flags)); +} + +/* * Interrupt handler thread */ static int irq_thread(void *data) @@ -1159,6 +1184,8 @@ static int irq_thread(void *data) irqreturn_t (*handler_fn)(struct irq_desc *desc, struct irqaction *action); + irq_thread_set_ready(desc, action); + if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD, &action->thread_flags)) handler_fn = irq_forced_thread_fn; @@ -1583,8 +1610,6 @@ __setup_irq(unsigned int irq, struct irq } if (!shared) { - init_waitqueue_head(&desc->wait_for_threads); - /* Setup the type (level, edge polarity) if configured: */ if (new->flags & IRQF_TRIGGER_MASK) { ret = __irq_set_trigger(desc, @@ -1674,14 +1699,8 @@ __setup_irq(unsigned int irq, struct irq irq_setup_timings(desc, new); - /* - * Strictly no need to wake it up, but hung_task complains - * when no hard interrupt wakes the thread up. - */ - if (new->thread) - wake_up_process(new->thread); - if (new->secondary) - wake_up_process(new->secondary->thread); + wake_up_and_wait_for_irq_thread_ready(desc, new); + wake_up_and_wait_for_irq_thread_ready(desc, new->secondary); register_irq_proc(irq, desc); new->dir = NULL;