Received: by 2002:a05:6a10:a0d1:0:0:0:0 with SMTP id j17csp911613pxa; Wed, 12 Aug 2020 17:01:04 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwc/e3YH2kJuc6Pr7lwHgCaPTH/t7HrbpnT+D2N8jtBfa/u/Wv8ANnuDR7pSl6ir8LkLaEo X-Received: by 2002:a17:906:4aca:: with SMTP id u10mr1278079ejt.320.1597276864058; Wed, 12 Aug 2020 17:01:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1597276864; cv=none; d=google.com; s=arc-20160816; b=DFK3VB+K2WNpjsTxD3q3wa3X4/LwAFZWO4W7BNZhj/q3/h0ATijQiuSpjQJlLdfd5o AEMDBriuyc8Bw/TCuj3eNL8VX/Dfr3HPnANpWe91KNYh5GvKDlxKwhVS1vatzJ39hprf hBZgoSsTUqd71nkjoay0bQpOnP2nBaBPk6vkhHYaqcIoZY7Ltu481+DjNjbeHVpl2FZl jFpNAanGfHcjbTZgAje3w/qsUZ4eTbODPPlGskBoLpL8Oe7xWQD6YqTQ29LnScZjspBG ZJPqetUfEQ5mlHSHhl4nBdkaBqr+NaGoTS13hrnS4s9frfyLFrmoarammE8Cu7IJ25bM 5iXQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=HkhzGPTFs+cac0M1GsyIfO4qoWfrJUKbKZsDfMHWaAg=; b=ero9unASgsxP9EGCPUKiUNPXSnrWDI3Xm+TX75oMVmIJRahoAEMSkU9QDjYlEuj5CG WTg6NlaE9Va2Kgg6jqUfEWe1gM6HMGBQcJOASYUcV3q8SyHdo4tIBo96zTldZjPuGHLZ q+l9zHc6FAYECn32v/5pbaBBgmMSzI0ZmFXHI1235Hnps9S5+xDMlqEif1f/+Gbfr3fm Pvf/rMJ6Brx+F0bl8O+l5lGc1VAf9snJWrJns1MUoId1zLh0Osgf2l+eOaBttO+CUSVR BKSiD2xVgIOB4Rruf00SdWZf46n3zD4foUJCeDsLPu3pSpB1mkqiJcqwWTOGBp6jPG6F vbWg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=HFkzTwNa; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id v5si2169451ejg.368.2020.08.12.17.00.41; Wed, 12 Aug 2020 17:01:04 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=HFkzTwNa; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726591AbgHLX7v (ORCPT + 99 others); Wed, 12 Aug 2020 19:59:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50344 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726518AbgHLX7t (ORCPT ); Wed, 12 Aug 2020 19:59:49 -0400 Received: from mail-vs1-xe41.google.com (mail-vs1-xe41.google.com [IPv6:2607:f8b0:4864:20::e41]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3AEB0C061383 for ; Wed, 12 Aug 2020 16:59:49 -0700 (PDT) Received: by mail-vs1-xe41.google.com with SMTP id 1so2031896vsl.1 for ; Wed, 12 Aug 2020 16:59:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=HkhzGPTFs+cac0M1GsyIfO4qoWfrJUKbKZsDfMHWaAg=; b=HFkzTwNacjMvOvgx22Z3SG8a6pDXPQ+p9Z6vB8BMDuAuTtWaCiM8aYdJxMIuZeRmbO 45cxZTBoq3sVtBgHK8iPHQYje/WU0QiZt/AseLymk+FGgtaJFeJMA/jXvbUVcH4VnaAI 9x5KXG/OaT2CkwffRwz7Xb5RmKH+Bk1BlH9R8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=HkhzGPTFs+cac0M1GsyIfO4qoWfrJUKbKZsDfMHWaAg=; b=teI5X3HIPjNbYtym95gQ+ERejr4Wfycx+5B4bhqOooTYvGK4NABJGCapSHw/+vg7+a 1gdrO3tMGxMULguTicU9OTwae3QOCCQs64OII5dqNN4GwpE/eA3VyOZ4H2H9F43tz05Y PJyqkdvQRzT6dIfWmtluEsTzH2QeGPUdccjAWLMWwyoOBqzXgEII0TX3OBbfju5N7Jq3 n+Tdunfmrg1V9T6qjTvzbd8Sg2ya4XG8wsBkKo2iX/fJzHRgxqOjesA8emNlTtvNUE+8 JivlBuY+JdC8P6Itj7LE9wnuM2Ng1ZdHXRWlPpvA3T0EgtdG4wmiyF2SZg2oTCgHXWsg RYEA== X-Gm-Message-State: AOAM5314EE+p9uHxs/Nmf4d56O7HgtZ1eXPMVT4Stc1idXnjc+LuWcy2 jxEqCDobIgD4KzMqdetSBzRDphdM6ms= X-Received: by 2002:a67:bb06:: with SMTP id m6mr1457030vsn.54.1597276787888; Wed, 12 Aug 2020 16:59:47 -0700 (PDT) Received: from mail-vk1-f174.google.com (mail-vk1-f174.google.com. [209.85.221.174]) by smtp.gmail.com with ESMTPSA id o63sm566039vke.22.2020.08.12.16.59.46 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 12 Aug 2020 16:59:47 -0700 (PDT) Received: by mail-vk1-f174.google.com with SMTP id x187so900057vkc.1 for ; Wed, 12 Aug 2020 16:59:46 -0700 (PDT) X-Received: by 2002:a1f:ae42:: with SMTP id x63mr1608058vke.100.1597276786166; Wed, 12 Aug 2020 16:59:46 -0700 (PDT) MIME-Version: 1.0 References: <1595333413-30052-1-git-send-email-sumit.garg@linaro.org> <1595333413-30052-3-git-send-email-sumit.garg@linaro.org> In-Reply-To: <1595333413-30052-3-git-send-email-sumit.garg@linaro.org> From: Doug Anderson Date: Wed, 12 Aug 2020 16:59:34 -0700 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: [RFC 2/5] serial: core: Add framework to allow NMI aware serial drivers To: Sumit Garg Cc: Greg Kroah-Hartman , Daniel Thompson , linux-serial@vger.kernel.org, kgdb-bugreport@lists.sourceforge.net, Jiri Slaby , Russell King - ARM Linux , Jason Wessel , LKML , Linux ARM 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 Hi, On Tue, Jul 21, 2020 at 5:11 AM Sumit Garg wrote: > > Add NMI framework APIs in serial core which can be leveraged by serial > drivers to have NMI driven serial transfers. These APIs are kept under > CONFIG_CONSOLE_POLL as currently kgdb initializing uart in polling mode > is the only known user to enable NMI driven serial port. > > The general idea is to intercept RX characters in NMI context, if those > are specific to magic sysrq then allow corresponding handler to run in > NMI context. Otherwise defer all other RX and TX operations to IRQ work > queue in order to run those in normal interrupt context. > > Also, since magic sysrq entry APIs will need to be invoked from NMI > context, so make those APIs NMI safe via deferring NMI unsafe work to > IRQ work queue. > > Signed-off-by: Sumit Garg > --- > drivers/tty/serial/serial_core.c | 120 ++++++++++++++++++++++++++++++++++++++- > include/linux/serial_core.h | 67 ++++++++++++++++++++++ > 2 files changed, 185 insertions(+), 2 deletions(-) > > diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c > index 57840cf..6342e90 100644 > --- a/drivers/tty/serial/serial_core.c > +++ b/drivers/tty/serial/serial_core.c > @@ -3181,8 +3181,14 @@ static bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) > return true; > } > > +#ifdef CONFIG_CONSOLE_POLL > + if (in_nmi()) > + irq_work_queue(&port->nmi_state.sysrq_toggle_work); > + else > + schedule_work(&sysrq_enable_work); > +#else > schedule_work(&sysrq_enable_work); > - > +#endif It should be a very high bar to have #ifdefs inside functions. I don't think this meets it. Instead maybe something like this (untested and maybe slightly wrong syntax, but hopefully makes sense?): Outside the function: #ifdef CONFIG_CONSOLE_POLL #define queue_port_nmi_work(port, work_type) irq_work_queue(&port->nmi_state.work_type) #else #define queue_port_nmi_work(port, work_type) #endif ...and then: if (IS_ENABLED(CONFIG_CONSOLE_POLL) && in_nmi()) queue_port_nmi_work(port, sysrq_toggle_work); else schedule_work(&sysrq_enable_work); --- The whole double-hopping is really quite annoying. I guess schedule_work() can't be called from NMI context but can be called from IRQ context? So you need to first transition from NMI context to IRQ context and then go and schedule the work? Almost feels like we should just fix schedule_work() to do this double-hop for you if called from NMI context. Seems like you could even re-use the list pointers in the work_struct to keep the queue of people who need to be scheduled from the next irq_work? Worst case it seems like you could add a schedule_work_nmi() that would do all the hoops for you. ...but I also know very little about NMI so maybe I'm being naive. > port->sysrq = 0; > return true; > } > @@ -3273,12 +3279,122 @@ int uart_handle_break(struct uart_port *port) > port->sysrq = 0; > } > > - if (port->flags & UPF_SAK) > + if (port->flags & UPF_SAK) { > +#ifdef CONFIG_CONSOLE_POLL > + if (in_nmi()) > + irq_work_queue(&port->nmi_state.sysrq_sak_work); > + else > + do_SAK(state->port.tty); > +#else > do_SAK(state->port.tty); > +#endif > + } Similar comment as above about avoiding #ifdef in functions. NOTE: if you have something like schedule_work_nmi() I think you could just modify the do_SAK() function to call it and consider do_SAK() to be NMI safe. > return 0; > } > EXPORT_SYMBOL_GPL(uart_handle_break); > > +#ifdef CONFIG_CONSOLE_POLL > +int uart_nmi_handle_char(struct uart_port *port, unsigned int status, > + unsigned int overrun, unsigned int ch, > + unsigned int flag) > +{ > + struct uart_nmi_rx_data rx_data; > + > + if (!in_nmi()) > + return 0; > + > + rx_data.status = status; > + rx_data.overrun = overrun; > + rx_data.ch = ch; > + rx_data.flag = flag; > + > + if (!kfifo_in(&port->nmi_state.rx_fifo, &rx_data, 1)) > + ++port->icount.buf_overrun; > + > + return 1; > +} > +EXPORT_SYMBOL_GPL(uart_nmi_handle_char); > + > +static void uart_nmi_rx_work(struct irq_work *rx_work) > +{ > + struct uart_nmi_state *nmi_state = > + container_of(rx_work, struct uart_nmi_state, rx_work); > + struct uart_port *port = > + container_of(nmi_state, struct uart_port, nmi_state); > + struct uart_nmi_rx_data rx_data; > + > + /* > + * In polling mode, serial device is initialized much prior to > + * TTY port becoming active. This scenario is especially useful > + * from debugging perspective such that magic sysrq or debugger > + * entry would still be possible even when TTY port isn't > + * active (consider a boot hang case or if a user hasn't opened > + * the serial port). So we discard any other RX data apart from > + * magic sysrq commands in case TTY port isn't active. > + */ > + if (!port->state || !tty_port_active(&port->state->port)) { > + kfifo_reset(&nmi_state->rx_fifo); > + return; > + } > + > + spin_lock(&port->lock); > + while (kfifo_out(&nmi_state->rx_fifo, &rx_data, 1)) > + uart_insert_char(port, rx_data.status, rx_data.overrun, > + rx_data.ch, rx_data.flag); > + spin_unlock(&port->lock); > + > + tty_flip_buffer_push(&port->state->port); > +} > + > +static void uart_nmi_tx_work(struct irq_work *tx_work) > +{ > + struct uart_nmi_state *nmi_state = > + container_of(tx_work, struct uart_nmi_state, tx_work); > + struct uart_port *port = > + container_of(nmi_state, struct uart_port, nmi_state); > + > + spin_lock(&port->lock); > + if (nmi_state->tx_irq_callback) > + nmi_state->tx_irq_callback(port); > + spin_unlock(&port->lock); > +} > + > +static void uart_nmi_sak_work(struct irq_work *work) > +{ > + struct uart_nmi_state *nmi_state = > + container_of(work, struct uart_nmi_state, sysrq_sak_work); > + struct uart_port *port = > + container_of(nmi_state, struct uart_port, nmi_state); > + > + do_SAK(port->state->port.tty); > +} > + > +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL > +static void uart_nmi_toggle_work(struct irq_work *work) > +{ > + schedule_work(&sysrq_enable_work); > +} Nit: weird that it's called "toggle" work but just wrapps "enable" work. > +#endif > + > +int uart_nmi_state_init(struct uart_port *port) > +{ > + int ret; > + > + ret = kfifo_alloc(&port->nmi_state.rx_fifo, 256, GFP_KERNEL); > + if (ret) > + return ret; > + > + init_irq_work(&port->nmi_state.rx_work, uart_nmi_rx_work); > + init_irq_work(&port->nmi_state.tx_work, uart_nmi_tx_work); > + init_irq_work(&port->nmi_state.sysrq_sak_work, uart_nmi_sak_work); > +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL > + init_irq_work(&port->nmi_state.sysrq_toggle_work, uart_nmi_toggle_work); > +#endif > + return ret; > +} > +EXPORT_SYMBOL_GPL(uart_nmi_state_init); > +#endif > + > EXPORT_SYMBOL(uart_write_wakeup); > EXPORT_SYMBOL(uart_register_driver); > EXPORT_SYMBOL(uart_unregister_driver); > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 9fd550e..84487a9 100644 > --- a/include/linux/serial_core.h > +++ b/include/linux/serial_core.h > @@ -18,6 +18,8 @@ > #include > #include > #include > +#include > +#include > #include > > #ifdef CONFIG_SERIAL_CORE_CONSOLE > @@ -103,6 +105,28 @@ struct uart_icount { > typedef unsigned int __bitwise upf_t; > typedef unsigned int __bitwise upstat_t; > > +#ifdef CONFIG_CONSOLE_POLL > +struct uart_nmi_rx_data { > + unsigned int status; > + unsigned int overrun; > + unsigned int ch; > + unsigned int flag; > +}; > + > +struct uart_nmi_state { > + bool active; > + > + struct irq_work tx_work; > + void (*tx_irq_callback)(struct uart_port *port); > + > + struct irq_work rx_work; > + DECLARE_KFIFO_PTR(rx_fifo, struct uart_nmi_rx_data); > + > + struct irq_work sysrq_sak_work; > + struct irq_work sysrq_toggle_work; > +}; > +#endif > + > struct uart_port { > spinlock_t lock; /* port lock */ > unsigned long iobase; /* in/out[bwl] */ > @@ -255,6 +279,9 @@ struct uart_port { > struct gpio_desc *rs485_term_gpio; /* enable RS485 bus termination */ > struct serial_iso7816 iso7816; > void *private_data; /* generic platform data pointer */ > +#ifdef CONFIG_CONSOLE_POLL > + struct uart_nmi_state nmi_state; > +#endif > }; > > static inline int serial_port_in(struct uart_port *up, int offset) > @@ -475,4 +502,44 @@ extern int uart_handle_break(struct uart_port *port); > !((cflag) & CLOCAL)) > > int uart_get_rs485_mode(struct uart_port *port); > + > +/* > + * The following are helper functions for the NMI aware serial drivers. > + * Currently NMI support is only enabled under polling mode. > + */ > + > +#ifdef CONFIG_CONSOLE_POLL > +int uart_nmi_state_init(struct uart_port *port); > +int uart_nmi_handle_char(struct uart_port *port, unsigned int status, > + unsigned int overrun, unsigned int ch, > + unsigned int flag); > + > +static inline bool uart_nmi_active(struct uart_port *port) > +{ > + return port->nmi_state.active; > +} > + > +static inline void uart_set_nmi_active(struct uart_port *port, bool val) > +{ > + port->nmi_state.active = val; > +} > +#else > +static inline int uart_nmi_handle_char(struct uart_port *port, > + unsigned int status, > + unsigned int overrun, > + unsigned int ch, unsigned int flag) > +{ > + return 0; > +} > + > +static inline bool uart_nmi_active(struct uart_port *port) > +{ > + return false; > +} > + > +static inline void uart_set_nmi_active(struct uart_port *port, bool val) > +{ > +} > +#endif > + > #endif /* LINUX_SERIAL_CORE_H */ > -- > 2.7.4 >