Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3032530imu; Mon, 19 Nov 2018 09:37:03 -0800 (PST) X-Google-Smtp-Source: AJdET5eVohtHTk9ciPtyY8AtK1csAaOq9YYKE0qSql34dAXoDNUFeFplQZaXe+Y939r3tl/mgd45 X-Received: by 2002:a62:1a44:: with SMTP id a65mr24387944pfa.30.1542649023947; Mon, 19 Nov 2018 09:37:03 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1542649023; cv=none; d=google.com; s=arc-20160816; b=JyZa/+rBlxVGMLmeazm7tnlPdbTSOJL33fa0E3n/MAfoB22c6Kv4SzdB0IMNpi2hui n1sSMMkoBUSdULHZNG2Pydc6GidiXqnGEJs30Bgeb0Bdzb5My5zYx2i2Rv1rNbra8Kn4 5HPPmT3L8B5VDMFkln57270BbE+jCnrHVBoPtUeUxKR/i4144VNjQ7M2tCTVqDlABYAX vbOu1EhCmFROYYP85qoT7wyqjC1UPfVI+lOlCtv3NpFsO1eWtyYfK0yFymNWChY7Tff6 bJwYklr+Ok/9t3V43+N/3fGDCKje53LQw9spbvDkTNnqVn07A3vcK5huSwxmXvKLNRuy lTcg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=UZpVXszQMiY6NrF+iJOGm79CpA8yUc50lOaFHTZGJic=; b=sEdhy4efd1ZHSqRex0M2MshWNOLigiCdwdR/k/4VO/JSJrhp1kziv0HjdPqBDcRUin 39fNl0OMrahcQCIS2o8jUsEcahSly6cOGWh9gV1ZIhrPkkbgjtEbfo0GbJ6lOwqBQcra Ayhocl3O9ZPkrav371aApVxO6oAH1BCKt6Ppxjz39hzCHRIYnnSq3rGI4CuAdH2q9ySV aFMefVcZmvwWZI14thKB9Ig31rm22xCV91HwRgpLFlY4SNDpp7VoBQCBgBm7n7f31bsF ZCO/yNrB5nquf40W706LxXGXgqmgksj9b4RS6I3HDSNZEdUeRYG5acFYT7GyjUKXZ8fO 0RmA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="dXk7Iuq/"; 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 g8si32824235pfa.11.2018.11.19.09.36.49; Mon, 19 Nov 2018 09:37:03 -0800 (PST) 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; dkim=pass header.i=@kernel.org header.s=default header.b="dXk7Iuq/"; 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 S2390617AbeKTDQu (ORCPT + 99 others); Mon, 19 Nov 2018 22:16:50 -0500 Received: from mail.kernel.org ([198.145.29.99]:54892 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389075AbeKTDQu (ORCPT ); Mon, 19 Nov 2018 22:16:50 -0500 Received: from localhost (5356596B.cm-6-7b.dynamic.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 2E84221104; Mon, 19 Nov 2018 16:52:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1542646355; bh=3NNOCJegBSSVcmOGpzDTmduE9rrTOmzWFSfJ+3DKSxw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dXk7Iuq/eLKyLT/zu1O1Im+CtALk8pJ1VNx+K83P53HjgsnhoSVIckOtfmh9Tj2oi Es405wK3G3cwICYv7ALJv076eBCFBY27fqrtU+Qka4JwBG/I8nVhGSnuJ1UsDcJwyL pB8TiOT7to+aU4MNauCoPHWUiDysFhLqYhTlQ0MU= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Phil Elwell , Sasha Levin Subject: [PATCH 4.9 10/83] sc16is7xx: Fix for multi-channel stall Date: Mon, 19 Nov 2018 17:28:36 +0100 Message-Id: <20181119162613.988059711@linuxfoundation.org> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181119162612.046511542@linuxfoundation.org> References: <20181119162612.046511542@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.9-stable review patch. If anyone has any objections, please let me know. ------------------ From: Phil Elwell [ Upstream commit 8344498721059754e09d30fe255a12dab8fb03ef ] The SC16IS752 is a dual-channel device. The two channels are largely independent, but the IRQ signals are wired together as an open-drain, active low signal which will be driven low while either of the channels requires attention, which can be for significant periods of time until operations complete and the interrupt can be acknowledged. In that respect it is should be treated as a true level-sensitive IRQ. The kernel, however, needs to be able to exit interrupt context in order to use I2C or SPI to access the device registers (which may involve sleeping). Therefore the interrupt needs to be masked out or paused in some way. The usual way to manage sleeping from within an interrupt handler is to use a threaded interrupt handler - a regular interrupt routine does the minimum amount of work needed to triage the interrupt before waking the interrupt service thread. If the threaded IRQ is marked as IRQF_ONESHOT the kernel will automatically mask out the interrupt until the thread runs to completion. The sc16is7xx driver used to use a threaded IRQ, but a patch switched to using a kthread_worker in order to set realtime priorities on the handler thread and for other optimisations. The end result is non-threaded IRQ that schedules some work then returns IRQ_HANDLED, making the kernel think that all IRQ processing has completed. The work-around to prevent a constant stream of interrupts is to mark the interrupt as edge-sensitive rather than level-sensitive, but interpreting an active-low source as a falling-edge source requires care to prevent a total cessation of interrupts. Whereas an edge-triggering source will generate a new edge for every interrupt condition a level-triggering source will keep the signal at the interrupting level until it no longer requires attention; in other words, the host won't see another edge until all interrupt conditions are cleared. It is therefore vital that the interrupt handler does not exit with an outstanding interrupt condition, otherwise the kernel will not receive another interrupt unless some other operation causes the interrupt state on the device to be cleared. The existing sc16is7xx driver has a very simple interrupt "thread" (kthread_work job) that processes interrupts on each channel in turn until there are no more. If both channels are active and the first channel starts interrupting while the handler for the second channel is running then it will not be detected and an IRQ stall ensues. This could be handled easily if there was a shared IRQ status register, or a convenient way to determine if the IRQ had been deasserted for any length of time, but both appear to be lacking. Avoid this problem (or at least make it much less likely to happen) by reducing the granularity of per-channel interrupt processing to one condition per iteration, only exiting the overall loop when both channels are no longer interrupting. Signed-off-by: Phil Elwell Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -661,7 +661,7 @@ static void sc16is7xx_handle_tx(struct u uart_write_wakeup(port); } -static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) +static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) { struct uart_port *port = &s->p[portno].port; @@ -670,7 +670,7 @@ static void sc16is7xx_port_irq(struct sc iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); if (iir & SC16IS7XX_IIR_NO_INT_BIT) - break; + return false; iir &= SC16IS7XX_IIR_ID_MASK; @@ -692,16 +692,23 @@ static void sc16is7xx_port_irq(struct sc port->line, iir); break; } - } while (1); + } while (0); + return true; } static void sc16is7xx_ist(struct kthread_work *ws) { struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work); - int i; - for (i = 0; i < s->devtype->nr_uart; ++i) - sc16is7xx_port_irq(s, i); + while (1) { + bool keep_polling = false; + int i; + + for (i = 0; i < s->devtype->nr_uart; ++i) + keep_polling |= sc16is7xx_port_irq(s, i); + if (!keep_polling) + break; + } } static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)