From patchwork Wed Sep 12 03:32:09 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Vorontsov X-Patchwork-Id: 1459991 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 2DB3F402E1 for ; Fri, 14 Sep 2012 18:13:17 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TCaKh-0000H7-PO; Fri, 14 Sep 2012 18:09:39 +0000 Received: from mail-ie0-f177.google.com ([209.85.223.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TBdiw-0007SF-FJ for linux-arm-kernel@lists.infradead.org; Wed, 12 Sep 2012 03:34:47 +0000 Received: by ieje10 with SMTP id e10so2312788iej.36 for ; Tue, 11 Sep 2012 20:34:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=date:from:to:cc:subject:message-id:references:mime-version :content-type:content-disposition:in-reply-to:user-agent :x-gm-message-state; bh=Xg7qr2MYafnn7HlKMVOLya27qsSfhMpBGAq/MrA+WFw=; b=Lr6W/te95LvmIty7jNmoxXvayZAxHQ1Tbp/N0oJJALTSYZIXQ+8ChrLwPtDEcN4zvh e0E4ik7uBjnioklZ+2+GOY9ChGMwN2FlfjlFb8OV6rITM7Pv7Tof5V0NLbIkorXcSEay 5Z1qkcLtI81Qd+ibwshL0QUWlRUBVRCNoBXRz3iaY/vcs+Qs1FEGTSdwq61SFCFFCoha t7T4A9m4OJhkLV6VH9va1XR+V0Xijb2KqeuPuv8zf3XuyyIUHa2pf7DIHwhzEHhffJa+ Oji0Uk3pOAPmjf0R2s39IimH/zUdFO0xWYXiYwlINN+lONRgafgZyDjWfCwHhq1iS11/ Eu6g== Received: by 10.50.95.198 with SMTP id dm6mr18642093igb.60.1347420884440; Tue, 11 Sep 2012 20:34:44 -0700 (PDT) Received: from localhost (c-71-204-165-222.hsd1.ca.comcast.net. [71.204.165.222]) by mx.google.com with ESMTPS id hz10sm3803681igc.12.2012.09.11.20.34.41 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 11 Sep 2012 20:34:43 -0700 (PDT) Date: Tue, 11 Sep 2012 20:32:09 -0700 From: Anton Vorontsov To: Thomas Gleixner , Alan Cox Subject: [RFC] tty/serial/kgdboc: Add and wire up clear_irqs callback Message-ID: <20120912033209.GA32156@lizard> References: <20120911093042.GA12471@lizard> <1347356106-25368-6-git-send-email-anton.vorontsov@linaro.org> <20120911151540.362001c6@pyramind.ukuu.org.uk> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20120911151540.362001c6@pyramind.ukuu.org.uk> User-Agent: Mutt/1.5.21 (2010-09-15) X-Gm-Message-State: ALoCoQmlHtctrZcwVfXyQ1A9S+lNnldP8QO4X4biLn9ICSupETtNJ6fCoTwI9NtatuQpNVGmo8d0 X-Bad-Reply: References and In-Reply-To but no 'Re:' in Subject. X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.223.177 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-Mailman-Approved-At: Fri, 14 Sep 2012 14:09:29 -0400 Cc: Brian Swetland , linaro-kernel@lists.linaro.org, Russell King , patches@linaro.org, Greg Kroah-Hartman , Colin Cross , linux-kernel@vger.kernel.org, Arve =?utf-8?B?SGrDuG5uZXbDpWc=?= , John Stultz , linux-serial@vger.kernel.org, Jason Wessel , kgdb-bugreport@lists.sourceforge.net, Andrew Morton , kernel-team@android.com, linux-arm-kernel@lists.infradead.org, Alan Cox X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org On Tue, Sep 11, 2012 at 03:15:40PM +0100, Alan Cox wrote: > Anton Vorontsov wrote: > > This patch implements a new callback: clear_irqs. It is used for the > > This bit I still really don't like. I would like to know what the generic > IRQ folks thing about it and if Thomas Gleixner has any brilliant ideas > here. I don't think its a show stopper it would just be nice if there was > a better solution first. Yup, good idea, Cc'ing. Hello Thomas, We're dissussing a patch that adds a clear_irq callback into UART drivers. For convenience, the particular patch is inlined at the end of this email. The rationale and the background for the whole thing can be found here: http://lkml.org/lkml/2012/9/10/2 So, just for visual clearness, and for the fun of it, here is some glorious ascii art of what we have: ,---NMI-|`````| UART_IRQ---INT_controller | CPU | `---IRQ-|,,,,,| Pretty much standard scheme. That is, on the interrupt controller level we can reroute any IRQ to NMI, and back in 2008 folks at Google found that rerouting the UART IRQ to NMI brings some really cool features: we can have a very reliable and powerful debugger pretty much on every embedded machine, and without loosing the UART/console port itself. So, it works like this: - At boot time, Linux arch/ code reroutes UART IRQ to NMI, we connect the port to the KGDBoC, and so forth...; - User starts typing on the serial port; - UART raises its IRQ line; - Through the controller, one of CPUs gets an NMI; - In NMI context, CPU reads a character from UART; - Then it checks if the character resulted into the special 'enter KGDB' magic sequence: - If yes, then the CPU invites other CPUs into the KGDB, and thus kernel enters the debugger; - If the character wasn't part of the magic command (or the sequence is yet incomplete), then CPU exits NMI and continues as normal. The "problem" is in the last step. If we exit NMI without making UART know that we're done with the interrupt, we will reenter the NMI immediately, even without any new characters from the UART. The obvious solution would be to "mask/reroute NMI at INT_controller level or queue serial port's IRQ routine from somewhere, e.g. a tasklet or software raised IRQ". Unfortunately, this means that we have to keep NMI disabled for this long: 1. We exit the NMI context with NMI source disabled/rerouted; 2. CPU continues to execute the kernel; 3. Kernel receives a timer interrupt, or software-raised interrupt, or UART IRQ, which was temporary rerouted back to normal interrupts; 4. It executes normal IRQ-entry, tracing, lots of other stuff, interrupt handlers, softirq handlers, and thus we clear the UART interrupt; 5. Once the UART is cleared, we reenable NMI (in the arch-code, we can do that in our do_IRQ()); As you can see, with this solution the NMI debugger will be effectively disabled from 1. to 5., thus shall the hang happen in this sensitive code, we would no longer able to debug it. And this defeats the main purpose of the NMI debugger: we must keep NMI enabled all the time when we're not in the debugger, the NMI debugger is always available (unless the debugger crashed :-) That's why I came with the clear_irq callback in the serial drivers that we call from the NMI context, it's much simpler and keeps the debugger robust. So, personally I too can't think of any other plausible solution that would keep all the features intact. Thanks, Anton. - - - - [PATCH] tty/serial/kgdboc: Add and wire up clear_irqs callback This patch implements a new callback: clear_irqs. It is used for the cases when KDB-entry (e.g. NMI) and KDB IO (e.g. serial port) shares the same interrupt. To get the idea, let's take some real example (ARM machine): we have a serial port which interrupt is routed to an NMI, and the interrupt is used to enter KDB. Once there is some activity on the serial port, the CPU receives NMI exception, and we fall into KDB shell. So, it is our "debug console", and it is able to interrupt (and thus debug) even IRQ handlers themselves. When used that way, the interrupt never reaches serial driver's IRQ handler routine, which means that serial driver will not silence the interrupt. NMIs behaviour are quite arch-specific, and we can't assume that we can use them as ordinary IRQs, e.g. on some arches (like ARM) we can't handle data aborts, the behaviour is undefined then. So we can't just handle execution to serial driver's IRQ handler from the NMI context once we're done with KDB (plus this would defeat the debugger's purpose: we want the NMI handler be as simple as possible, so it will have less chances to hang). So, given that have to deal with it somehow, we have two options: 1. Implement something that clears the interrupt; 2. Implement a whole new concept of grabbing tty for exclusive KDB use, plus implement mask/unmask callbacks, i.e.: - Since consoles might use ttys w/o opending them, we would have to make kdb respect CON_ENABLED flag (maybe a good idea to do it anyway); - Add 'bool exclusive' argument to tty_find_polling_driver(), if set to 1, the function will refuse to return an already opened tty; and will use the flag in tty_reopen() to not allow multiple users (there are already checks for pty masters, which are "open once" ttys); - Once we got the tty exclusively, we would need to call some new uart->mask_all_but_rx_interrupts call before we want to use the port for NMI/KDB, and unmask_all_but_rx_interrupts after we're done with it. The second option is obviously more complex, needlessly so, and less generic. So I went with the first one: we just consume all the interrupts. The tty becomes silently unusable for the rest of the world when we use it with KDB; but once we reroute the serial IRQ source back from NMI to an ordinary IRQ (in KDB this can be done with 'disable_nmi' command), it will behave as normal. p.s. Since the callback is so far used only by polling users, we place it under the appropriate #ifdef. Signed-off-by: Anton Vorontsov --- drivers/tty/serial/kgdboc.c | 10 ++++++++++ drivers/tty/serial/serial_core.c | 15 +++++++++++++++ include/linux/kgdb.h | 1 + include/linux/serial_core.h | 1 + include/linux/tty_driver.h | 1 + 5 files changed, 28 insertions(+) diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 2b42a01..0aa08c8 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -227,6 +227,15 @@ static int kgdboc_get_char(void) kgdb_tty_line); } +static void kgdboc_clear_irqs(void) +{ + if (!kgdb_tty_driver) + return; + if (kgdb_tty_driver->ops->clear_irqs) + kgdb_tty_driver->ops->clear_irqs(kgdb_tty_driver, + kgdb_tty_line); +} + static void kgdboc_put_char(u8 chr) { if (!kgdb_tty_driver) @@ -298,6 +307,7 @@ static struct kgdb_io kgdboc_io_ops = { .name = "kgdboc", .read_char = kgdboc_get_char, .write_char = kgdboc_put_char, + .clear_irqs = kgdboc_clear_irqs, .pre_exception = kgdboc_pre_exp_handler, .post_exception = kgdboc_post_exp_handler, }; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index dcb2d5a..93c36cb 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2187,6 +2187,20 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) port = state->uart_port; port->ops->poll_put_char(port, ch); } + +static void uart_clear_irqs(struct tty_driver *driver, int line) +{ + struct uart_driver *drv = driver->driver_state; + struct uart_state *state = drv->state + line; + struct uart_port *port; + + if (!state || !state->uart_port) + return; + + port = state->uart_port; + if (port->ops->clear_irqs) + port->ops->clear_irqs(port); +} #endif static const struct tty_operations uart_ops = { @@ -2219,6 +2233,7 @@ static const struct tty_operations uart_ops = { .poll_init = uart_poll_init, .poll_get_char = uart_poll_get_char, .poll_put_char = uart_poll_put_char, + .clear_irqs = uart_clear_irqs, #endif }; diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index 3b111a6..1fd1cf0 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -295,6 +295,7 @@ struct kgdb_io { const char *name; int (*read_char) (void); void (*write_char) (u8); + void (*clear_irqs) (void); void (*flush) (void); int (*init) (void); void (*pre_exception) (void); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 822c887..855fb6e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -277,6 +277,7 @@ struct uart_ops { int (*poll_init)(struct uart_port *); void (*poll_put_char)(struct uart_port *, unsigned char); int (*poll_get_char)(struct uart_port *); + void (*clear_irqs)(struct uart_port *); #endif }; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index dd976cf..42f8a87 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -282,6 +282,7 @@ struct tty_operations { int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); void (*poll_put_char)(struct tty_driver *driver, int line, char ch); + void (*clear_irqs)(struct tty_driver *driver, int line); #endif const struct file_operations *proc_fops; };