From patchwork Fri Jul 17 06:20:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sumit Garg X-Patchwork-Id: 235672 Delivered-To: patches@linaro.org Received: by 2002:a92:d244:0:0:0:0:0 with SMTP id v4csp1428546ilg; Thu, 16 Jul 2020 23:21:29 -0700 (PDT) X-Received: by 2002:a65:6707:: with SMTP id u7mr7376299pgf.233.1594966889152; Thu, 16 Jul 2020 23:21:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1594966889; cv=none; d=google.com; s=arc-20160816; b=J5cAwY6k8MTbzFj4sZuQhCDtcdMhP6O+v3ORuYgcj6YXmf17D/vIfl4H5PfGSYsCyc KKCjBphvyy9rNO8hf5kIGnfQaONzv7cQtvSEAI7Mh8Ljn81i0sJ+TmBmSUAFDF9T3AhQ iu6n+Ltrn2MDcfQyV2jaW/PoJkezH3g9J0N16kNc3+LB9Wm/Wm/p4CJAgpW6A40+d3Ky 8nDUxSt+UKuwAHMRL7kgq9Pi98FbNxsmDKw/q+3FHnCP/T2xHkzebYRK0wN7bmfm+QIF Zd0srBmMoRFQ3MOLK5PKuGevOi+Yh2tKYl+zG/7i7ukaj+mtkz9cE7pebU9Ox4zsLpfz fk2g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=d9jgaYzqa/Xp8bfEMe71U9w/oiR/y7kdym77YvEoKHo=; b=AOdwrT3ikeVGlYUXVGO94rqKE1VHKd+AKab7N+IGZ6NS/NKMN+4dHg4+13ISLl7YRs Abj50rBZJlosDn3STaSafEwZ50LbsDi700cPwjhBZ0/oVby8VTnWYK5TdExNTFqFs/O5 Yi+pTotd5jDe4tpT1WoMxV5vWniJL3X5WVjoS1KX2JQ+7s1rvQ3FDuephDwrvDe4H3eP mS9V/paHi++4WtZHTUGzvnPF98XnqGxbNzNAx+qKoKnFy/9T8iQd6WFzbY8XS9ZG/47A flta51xQm7eJilUe5fYj5oyodwEShJO7xxuIEG8cqUrnKyhg8dM0witWLmQPQhq5lUIl hL7w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=JNO0JHXM; spf=pass (google.com: domain of sumit.garg@linaro.org designates 209.85.220.65 as permitted sender) smtp.mailfrom=sumit.garg@linaro.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from mail-sor-f65.google.com (mail-sor-f65.google.com. [209.85.220.65]) by mx.google.com with SMTPS id d8sor2951416pja.43.2020.07.16.23.21.29 for (Google Transport Security); Thu, 16 Jul 2020 23:21:29 -0700 (PDT) Received-SPF: pass (google.com: domain of sumit.garg@linaro.org designates 209.85.220.65 as permitted sender) client-ip=209.85.220.65; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=JNO0JHXM; spf=pass (google.com: domain of sumit.garg@linaro.org designates 209.85.220.65 as permitted sender) smtp.mailfrom=sumit.garg@linaro.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=d9jgaYzqa/Xp8bfEMe71U9w/oiR/y7kdym77YvEoKHo=; b=JNO0JHXMzQUF26hyna70dkSKOh5zg4GLKb1HKvb1vuUjK12XuKk8uoLOqTRGJPcz5F 0skIvwKO1aFBlfPvBl8e5Itbm/3ci6YeQFGbJ9/7Cd0iXHeICt9NuNxQeoboDWQSOmHj 6eXnUfw/03P+jNmlxS87WgSDcevzwUVoAm9jFjavFW+QrO9l5Gwdk4dd4eqr4X3H2psu uXHz66HDLr8aZ9Ig2dUx6GC4Vtq6gDJbjMf+hHviUcgEXHoNKo/tnX7j714WgoNY436k lyW/MrNiijuVow/yzwOs6NzSUI/21g2GsLiSs+FAhXC2dq9WV72HtfANI+lsfFs4fdDk V1aA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=d9jgaYzqa/Xp8bfEMe71U9w/oiR/y7kdym77YvEoKHo=; b=THPC3XMjqd6qv6wpBDb6+BK7iEFWSwaOqxxTUrHCSlc5rIJxfag4+vFQDQXbN4T44U MEEz+Z072BYhKReN0Db3tIBnsUGtvOrSYtdetJZmBSOfPDWIZuhEQr0o3I02hKqB7tUE PhAKrA7rRV2L6jFY1IRqOwu6vuVCY0PwpCBnrTMTmpHzNBxJlvHkPIWvbaj9z028Wcik GPkNB/f/M2SkE3Icv4yrbNPuLu+dvlLGyegeRK1R3HhxogBTapppujR1/tdpnXOBvzZu czYsDOcSKjo4N7RFxtW2k6j2iVFpxUC2sHpF5Ikx+Y8suLrKbbuz6xDATedx7scMHc1C PJaQ== X-Gm-Message-State: AOAM531OviaTSrS/VMYmv7t2OzhW6j7gFymQk4xMugsLzm23A4bWNObl txhV/TUTC2XiJvm3YdH5xJ8jzCCJThEIWg== X-Google-Smtp-Source: ABdhPJwgadiOK6bOJRPuD8vtA8CQ7/hWDPW3/09tvBkAWqmAUCkAn5ylwTRy3eFaCac8uLqc7DWqtQ== X-Received: by 2002:a17:90a:d314:: with SMTP id p20mr7581654pju.99.1594966888688; Thu, 16 Jul 2020 23:21:28 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([117.210.211.74]) by smtp.gmail.com with ESMTPSA id b4sm6630466pfo.137.2020.07.16.23.21.24 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 16 Jul 2020 23:21:27 -0700 (PDT) From: Sumit Garg To: daniel.thompson@linaro.org Cc: patches@linaro.org, Sumit Garg Subject: [RFC INTERNAL v3 2/4] serial: core: Add framework to allow NMI aware serial drivers Date: Fri, 17 Jul 2020 11:50:55 +0530 Message-Id: <1594966857-5215-3-git-send-email-sumit.garg@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1594966857-5215-1-git-send-email-sumit.garg@linaro.org> References: <1594966857-5215-1-git-send-email-sumit.garg@linaro.org> With the advent of pseudo NMIs on arm64 platforms, it will be possible to have NMI driven serial drivers which enables us to have magic sysrq running in NMI context. So add NMI framework APIs in serial core that can be leveraged by serial drivers operating in polling mode to have NMI driven serial transfers. 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 NMI unsafe RX and TX operations to IRQ work queue in order to run those in normal interrupt context. Also, since magic sysrq entry APIs can 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(-) -- 2.7.4 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 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 + } 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); +} +#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 */