Received: by 2002:a25:f815:0:0:0:0:0 with SMTP id u21csp3632489ybd; Tue, 25 Jun 2019 06:03:52 -0700 (PDT) X-Google-Smtp-Source: APXvYqzRfM+YgHsWWRfWWNj6GxiufcqXJ0DYtzYkUE2DO7cmvZ0VNoyJgQQvX5jSVqckD7Y945DW X-Received: by 2002:a63:6dc5:: with SMTP id i188mr25253503pgc.188.1561467832026; Tue, 25 Jun 2019 06:03:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1561467832; cv=none; d=google.com; s=arc-20160816; b=DLi3LQRpfeP3Ku1BFHuX6D0O1+H038zIbT28gG5UP7Lh75L3eQiJOBbDVJfXRqJTkN y419Kv5RKLEkGzqyELoHL8cKiVggSVQeUtYF6ryCW6qaybTn8oO5P/4yR+nH+QI9BfZD p4yZ9hcRLDYtnBXJqbIWuKmnJ3lAhbBeVFete70aZrtvmoCkLge/XMKMjNLF2pbmds1R J92gPUKwRXaBt6vXbf5JAsFCOaThgGkwyEi4YGysuwGCe3nk7PDz7WYHDt9g40mV8uBR v6xHalmMHMaepNvsei+OmtHZwRo+1xkURXCxgQ4likcpSAWznNEVa5I+DOpYpgrFj4Qt eczw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:subject:cc:to :from:date:user-agent:message-id; bh=7Rvef36B9/Hboiwi2ZpJJjdQGpoCkLbD3x+RlKuPPGQ=; b=dM9MS8BRI6TbOxKxk2ftP1vBqGenmnTtxDFzXdYI56bzqyd+knmrCGPZDqiHMs0gDZ /KBSWsu45EOaR6aK4UFEuVm9Bvz55j5HkXeg6c0BiGGJKCSc9nEPzMGodmh8Ul5o4ELr 6F6277tOoOCFc3Ni7+MbbA/iIt3q8JL+gyh3mbqsBPyY4XA8yCv/xXFjrwjqoxMkV3iN 5Yvr4rriy8j+IqTyJyaZAReFBl19G/G5Lj58A778Fc5LHluUboQmcKn8h6YPlw22GYhB 6IcA9qgEtZvVrCtM30D0PgRdvLHfp+D5wxrhIi1Xv6/yf4tiTymQE+Or27Jl0pHwZ6/t EuhA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x8si13179494pgj.322.2019.06.25.06.03.33; Tue, 25 Jun 2019 06:03:52 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730881AbfFYLYn (ORCPT + 99 others); Tue, 25 Jun 2019 07:24:43 -0400 Received: from Galois.linutronix.de ([193.142.43.55]:41852 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727138AbfFYLYj (ORCPT ); Tue, 25 Jun 2019 07:24:39 -0400 Received: from localhost ([127.0.0.1] helo=nanos.tec.linutronix.de) by Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from ) id 1hfjYw-0004rC-6K; Tue, 25 Jun 2019 13:24:34 +0200 Message-Id: <20190625112405.666964552@linutronix.de> User-Agent: quilt/0.65 Date: Tue, 25 Jun 2019 13:13:55 +0200 From: Thomas Gleixner To: LKML Cc: x86@kernel.org, Robert Hodaszi , Vadim Pasternak , Ido Schimmel , Greg Kroah-Hartman , linux-serial@vger.kernel.org, Marc Zyngier Subject: [patch 2/5] genirq: Add optional hardware synchronization for shutdown References: <20190625111353.863718167@linutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org free_irq() ensures that no hardware interrupt handler is executing on a different CPU before actually releasing resources and deactivating the interrupt completely in a domain hierarchy. But that does not catch the case where the interrupt is on flight at the hardware level but not yet serviced by the target CPU. That creates an interesing race condition: CPU 0 CPU 1 IRQ CHIP interrupt is raised sent to CPU1 Unable to handle immediately (interrupts off, deep idle delay) mask() ... free() shutdown() synchronize_irq() release_resources() do_IRQ() -> resources are not available That might be harmless and just trigger a spurious interrupt warning, but some interrupt chips might get into a wedged state. Provide infrastructure for interrupt chips to provide an optional irq_inflight() callback and use it for the synchronization in free_irq(). synchronize_[hard]irq() are not using this mechanism as it might actually deadlock unter certain conditions. Fixes: 464d12309e1b ("x86/vector: Switch IOAPIC to global reservation mode") Reported-by: Robert Hodaszi Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 2 ++ kernel/irq/manage.c | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -418,6 +418,7 @@ static inline irq_hw_number_t irqd_to_hw * required. This is used for CPU hotplug where the * target CPU is not yet set in the cpu_online_mask. * @irq_retrigger: resend an IRQ to the CPU + * @irq_inflight: chip level detection of interrupts in flight (optional) * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ * @irq_set_wake: enable/disable power-management wake-on of an IRQ * @irq_bus_lock: function to lock access to slow bus (i2c) chips @@ -462,6 +463,7 @@ struct irq_chip { int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force); int (*irq_retrigger)(struct irq_data *data); + int (*irq_inflight)(struct irq_data *data); int (*irq_set_type)(struct irq_data *data, unsigned int flow_type); int (*irq_set_wake)(struct irq_data *data, unsigned int on); --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -35,8 +35,10 @@ static int __init setup_forced_irqthread early_param("threadirqs", setup_forced_irqthreads); #endif -static void __synchronize_hardirq(struct irq_desc *desc) +static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip) { + struct irq_data *irqd = irq_desc_get_irq_data(desc); + struct irq_chip *chip = irq_data_get_irq_chip(irqd); bool inprogress; do { @@ -52,6 +54,13 @@ static void __synchronize_hardirq(struct /* Ok, that indicated we're done: double-check carefully. */ raw_spin_lock_irqsave(&desc->lock, flags); inprogress = irqd_irq_inprogress(&desc->irq_data); + + /* + * If requested and supported, check at the chip whether it + * is in flight at the hardware level: + */ + if (!inprogress && sync_chip && chip && chip->irq_inflight) + inprogress = chip->irq_inflight(irqd); raw_spin_unlock_irqrestore(&desc->lock, flags); /* Oops, that failed? */ @@ -74,13 +83,16 @@ static void __synchronize_hardirq(struct * Returns: false if a threaded handler is active. * * This function may be called - with care - from IRQ context. + * + * It does not check whether there is an interrupt on flight at the + * hardware level, but not serviced yet, as this might deadlock. */ bool synchronize_hardirq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); if (desc) { - __synchronize_hardirq(desc); + __synchronize_hardirq(desc, false); return !atomic_read(&desc->threads_active); } @@ -97,13 +109,16 @@ EXPORT_SYMBOL(synchronize_hardirq); * holding a resource the IRQ handler may need you will deadlock. * * This function may be called - with care - from IRQ context. + * + * It does not check whether there is an interrupt on flight at the + * hardware level, but not serviced yet, as this might deadlock. */ void synchronize_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); if (desc) { - __synchronize_hardirq(desc); + __synchronize_hardirq(desc, false); /* * We made sure that no hardirq handler is * running. Now verify that no threaded handlers are @@ -1729,8 +1744,12 @@ static struct irqaction *__free_irq(stru unregister_handler_proc(irq, action); - /* Make sure it's not being used on another CPU: */ - synchronize_hardirq(irq); + /* + * Make sure it's not being used on another CPU and if the chip + * supports it also make sure that there is no (not yet serviced) + * interrupt on flight at the hardware level. + */ + __synchronize_hardirq(desc, true); #ifdef CONFIG_DEBUG_SHIRQ /*