From patchwork Fri Jul 10 13:12:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sumit Garg X-Patchwork-Id: 235232 Delivered-To: patches@linaro.org Received: by 2002:a92:d244:0:0:0:0:0 with SMTP id v4csp442661ilg; Fri, 10 Jul 2020 06:12:38 -0700 (PDT) X-Received: by 2002:a62:7e0d:: with SMTP id z13mr59245977pfc.161.1594386758184; Fri, 10 Jul 2020 06:12:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1594386758; cv=none; d=google.com; s=arc-20160816; b=kqxbWaeBbu3Y2AOCAznsZHFAenG3Eb9GqxbjvPmHCso62hNm0cYjen1Rx9K67N0ga2 boaOL5qywUESMroYe9Z+bPW/X36WyZM33WBgMPkbdvKu+O25ErsVc3eiZKJyJbQ7UeGf zLA0z+qOhnPjjjP0n4deqZf5VWza4v9AuMknn9M5SYgWBUdQdqcGjxT3RtLz8knUHf2B lZ5wMWnzrJlbWcpqBsUa5FJoCb8iqozds8la6W4Pnl6MuekY63nSTrsPDJl4GK9G+zgL X7vlN1ggxJYz3XNiQr35FnJ6SYvQpN2jr0YIa11RBQQEzDB5AqKvlPNRCjgtMVpEf41o U6lA== 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=ggNZqpqXwncDazTh/hsPA0z2SqrkNlF0s1+2splHy4I=; b=V8x0JD7DmCBXJ8LxayyL4yzEZLQ2bxKo2RZOGMKztZhtEjJfv4vK+uimciNZe0YY/A V7597tt9TPQWbd/8PTy0s/nvn3AzSa63rtlOOyA2XbJOTooMrOAA51ay4nqTZNHP67/r lEZ6+pFT83BeCA2nb1pZA5nresUfXPi/ua5ddvxGeEwEJ64xWyP8lukn8q63g+ve7fWP /ZPH+FIOCldqwVNt7uGJyjlTE61u+iV4icdGDHzxxNYaU9dlr2ezPL1mjapXeyIGi2H0 VDGFjaEA1I75DN1onnubtFGULhGFCa/k9IejnjK/b+c+2af0iAU2PtrgbHUes/hOLHy5 wjEQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=bRSwE7Xn; 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 p22sor8078927pjv.27.2020.07.10.06.12.38 for (Google Transport Security); Fri, 10 Jul 2020 06:12:38 -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=bRSwE7Xn; 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=ggNZqpqXwncDazTh/hsPA0z2SqrkNlF0s1+2splHy4I=; b=bRSwE7Xn72jNoTl+Z9mMPNbq2tgzoYdAoNJgR1xcbQ8nIBtmYH3q4GyviAjh+Al5Q1 /1eRuUXQJIxSn3c4tkpyHfg99pr7Y0J3ATswtRrWR2mWahN+YtrbHUcn6pYkt1O1ev/+ TSeT9dgaUUB3IyacpcW4Me00v6GHzZ4dxZoGx68SOOC8KKZ4EF0753w4jtLJ4db+yZGt b92yt3yHYCf1gNADCLIZdaGvnc3jtC92+8cdy6IJgePiYjMY4crIl+gKfl1HfgpF92Ds 0fqeNh36xHGGWDrJHyUc9TLLoGoM5Aam42j3bS1lAmU/eWXAmjS6TYCrFjXIXAsLoGzy Rzzw== 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=ggNZqpqXwncDazTh/hsPA0z2SqrkNlF0s1+2splHy4I=; b=PMsBq8OOyLJilzOBvd5A3pxtQts/2RlDtggPRBDBYNYhipAaqzWAQFDIRwl++MQsZ/ wHwhsccjyZLVpLql/k0cTgzAFZzY9tZtk8uUkpHqoExeNcNUIIRRxo1/H41++WQ2gaTQ y4Sdiew2jH32WVpxx81N9U1jDMUtCqYj5DCNHCa5TolEJ44ObOccr/4JWtGpVOwjccsk 0yiYE1gpYRJHQbTBmnghAnypc/vC2Zk79e96MDRejuPX+D87X43n7ku1kGQ5f/OI/cCr UehptTyOeFTMNcW0sU3DM5TF35PARFkfvCUtwgpvxbmpsxScwVzW2is/dTqcl2wasJFL Mtdg== X-Gm-Message-State: AOAM533claQyRmUPgFPP5Hp+TUwd355sBlvNz2wc/3m/EwP8tyOupr7Y 0quRNEzHIQvZNcYi3suMbfG7fLYL X-Google-Smtp-Source: ABdhPJyZM676YzN290bcALoFGjeiBPsbknFGrzkzbxF+waitHV35zKA7jnpZ5Cm8bPF/iwU2bp+0Dg== X-Received: by 2002:a17:90a:ea0f:: with SMTP id w15mr5969065pjy.138.1594386757819; Fri, 10 Jul 2020 06:12:37 -0700 (PDT) Return-Path: Received: from localhost.localdomain ([117.210.211.230]) by smtp.gmail.com with ESMTPSA id d25sm5553279pgn.2.2020.07.10.06.12.34 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 10 Jul 2020 06:12:37 -0700 (PDT) From: Sumit Garg To: daniel.thompson@linaro.org Cc: patches@linaro.org, Sumit Garg Subject: [RFC INTERNAL v2 2/4] serial: core: Add framework to allow NMI aware serial drivers Date: Fri, 10 Jul 2020 18:42:03 +0530 Message-Id: <1594386725-10346-3-git-send-email-sumit.garg@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1594386725-10346-1-git-send-email-sumit.garg@linaro.org> References: <1594386725-10346-1-git-send-email-sumit.garg@linaro.org> With the advent of pseudo NMIs on arm64 platforms, its been now possible to have NMI driven serial drivers which enables us to have magic sysrq running in NMI context that could be helpful to debug hardlockup scenarios especially via enabling kernel debugger to run in NMI context. So add corresponding NMI helper APIs that can be leveraged by serial drivers. Also, make sysrq handler APIs NMI safe. Signed-off-by: Sumit Garg --- drivers/tty/serial/serial_core.c | 97 +++++++++++++++++++++++++++++++++++++++- include/linux/serial_core.h | 43 ++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) -- 2.7.4 diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 57840cf..a59d7ff 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3181,16 +3181,28 @@ static bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) return true; } - schedule_work(&sysrq_enable_work); + if (in_nmi()) + irq_work_queue(&port->nmi_state.sysrq_toggle_work); + else + schedule_work(&sysrq_enable_work); port->sysrq = 0; return true; } + +static void uart_nmi_toggle_work(struct irq_work *work) +{ + schedule_work(&sysrq_enable_work); +} #else static inline bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) { return false; } + +static void uart_nmi_toggle_work(struct irq_work *work) +{ +} #endif int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) @@ -3273,12 +3285,93 @@ int uart_handle_break(struct uart_port *port) port->sysrq = 0; } - if (port->flags & UPF_SAK) + if (in_nmi() && (port->flags & UPF_SAK)) + irq_work_queue(&port->nmi_state.sysrq_sak_work); + else if (port->flags & UPF_SAK) do_SAK(state->port.tty); return 0; } EXPORT_SYMBOL_GPL(uart_handle_break); +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 = nmi_state->port; + + do_SAK(port->state->port.tty); +} + +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 = nmi_state->port; + 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; + } + + if (unlikely(!kfifo_len(&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.ch, 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 = nmi_state->port; + + spin_lock(&port->lock); + if (nmi_state->tx_irq_callback) + nmi_state->tx_irq_callback(port); + spin_unlock(&port->lock); +} + +void uart_nmi_handle_rx_data(struct uart_port *port, + struct uart_nmi_rx_data *rx_data) +{ + WARN_ON_ONCE(!kfifo_in(&port->nmi_state.rx_fifo, rx_data, 1)); +} +EXPORT_SYMBOL_GPL(uart_nmi_handle_rx_data); + +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; + + port->nmi_state.port = port; + init_irq_work(&port->nmi_state.tx_work, uart_nmi_tx_work); + init_irq_work(&port->nmi_state.rx_work, uart_nmi_rx_work); + init_irq_work(&port->nmi_state.sysrq_sak_work, uart_nmi_sak_work); + init_irq_work(&port->nmi_state.sysrq_toggle_work, uart_nmi_toggle_work); + + return ret; +} +EXPORT_SYMBOL_GPL(uart_nmi_state_init); + 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..51d62f4 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,27 @@ struct uart_icount { typedef unsigned int __bitwise upf_t; typedef unsigned int __bitwise upstat_t; +struct uart_nmi_rx_data { + unsigned int ch; + unsigned int overrun; + 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; + + struct uart_port *port; +}; + struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* in/out[bwl] */ @@ -255,6 +278,7 @@ struct uart_port { struct gpio_desc *rs485_term_gpio; /* enable RS485 bus termination */ struct serial_iso7816 iso7816; void *private_data; /* generic platform data pointer */ + struct uart_nmi_state nmi_state; }; static inline int serial_port_in(struct uart_port *up, int offset) @@ -475,4 +499,23 @@ 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. + */ + +int uart_nmi_state_init(struct uart_port *port); +void uart_nmi_handle_rx_data(struct uart_port *port, + struct uart_nmi_rx_data *rx_data); + +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; +} + #endif /* LINUX_SERIAL_CORE_H */