Message ID | 1594386725-10346-3-git-send-email-sumit.garg@linaro.org |
---|---|
State | New |
Headers | show |
Series | Introduce NMI aware serial drivers | expand |
On Fri, Jul 10, 2020 at 06:42:03PM +0530, Sumit Garg wrote: > 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 <sumit.garg@linaro.org> > --- > drivers/tty/serial/serial_core.c | 97 +++++++++++++++++++++++++++++++++++++++- > include/linux/serial_core.h | 43 ++++++++++++++++++ > 2 files changed, 138 insertions(+), 2 deletions(-) > > 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; If this is unlikely then why bother check it at all? > + > + 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)); This isn't error handling. This is just yelling if an error happens (which might be OK except that the upper level includes a mechanism to report overruns when we get too much data on the serial line). > +} > +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 <linux/tty.h> > #include <linux/mutex.h> > #include <linux/sysrq.h> > +#include <linux/irq_work.h> > +#include <linux/kfifo.h> > #include <uapi/linux/serial_core.h> > > #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; This structure is statically allocated... can we use containerof instead of backlinks? > +}; > + > 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; Should there be an #ifdef CONFIG_CONSOLE_POLL on the nmi_state. It's a bit odd... you've created a static inline (uart_nmi_active) to get rid of ifdef'ry in the .c file and then only provide a single implementation anyway. > }; > > 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; > +} What is the point of this helper? Daniel.
On Mon, 13 Jul 2020 at 19:48, Daniel Thompson <daniel.thompson@linaro.org> wrote: > > On Fri, Jul 10, 2020 at 06:42:03PM +0530, Sumit Garg wrote: > > 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 <sumit.garg@linaro.org> > > --- > > drivers/tty/serial/serial_core.c | 97 +++++++++++++++++++++++++++++++++++++++- > > include/linux/serial_core.h | 43 ++++++++++++++++++ > > 2 files changed, 138 insertions(+), 2 deletions(-) > > > > 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; > > If this is unlikely then why bother check it at all? > Okay, will remove this check. > > > + > > + 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)); > > This isn't error handling. This is just yelling if an error happens > (which might be OK except that the upper level includes a mechanism to > report overruns when we get too much data on the serial line). > I guess you are referring to following mechanism to report overruns: --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3350,7 +3350,8 @@ static void uart_nmi_tx_work(struct irq_work *tx_work) 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)); + if (!kfifo_in(&port->nmi_state.rx_fifo, rx_data, 1)) + ++port->icount.buf_overrun; } EXPORT_SYMBOL_GPL(uart_nmi_handle_rx_data); If yes, then I will include the above change in this patch. > > > +} > > +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 <linux/tty.h> > > #include <linux/mutex.h> > > #include <linux/sysrq.h> > > +#include <linux/irq_work.h> > > +#include <linux/kfifo.h> > > #include <uapi/linux/serial_core.h> > > > > #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; > > This structure is statically allocated... can we use containerof instead > of backlinks? > Earlier I was using container_of() only but only to make NMI APIs to look a little cleaner to avoid multiple use of container_of(), I added this backlink. But if backlinks aren't preferred for statically allocated structures then will switch back to use of container_of(). > > > +}; > > + > > 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; > > Should there be an #ifdef CONFIG_CONSOLE_POLL on the nmi_state. > Yeah we could keep it under CONFIG_CONSOLE_POLL since currently we only enable NMI state under poll_init(). But it seems like this NMI feature can be enabled independently of polling mode as well. AFAIK, CONFIG_CONSOLE_POLL is there to provide safer uart poll read/write APIs for the debugger which should be able to work irrespective of serial device interrupt being requested as an NMI or an IRQ. So that was the initial idea to not keep it under CONFIG_CONSOLE_POLL. But if you still prefer it otherwise then I can do corresponding changes. > It's a bit odd... you've created a static inline (uart_nmi_active) to > get rid of ifdef'ry in the .c file and then only provide a single > implementation anyway. > Yeah initial idea was to get rid of ifdef'ry but still I saw them being useful to provide a consistent set of wrapper to manage 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; > > +} > > What is the point of this helper? > These are just to provide a consistent set of wrappers to alter/retrieve NMI state. Also, I got this motivation for them after looking at how tty port flags were managed. -Sumit > > Daniel.
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 <linux/tty.h> #include <linux/mutex.h> #include <linux/sysrq.h> +#include <linux/irq_work.h> +#include <linux/kfifo.h> #include <uapi/linux/serial_core.h> #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 */
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 <sumit.garg@linaro.org> --- drivers/tty/serial/serial_core.c | 97 +++++++++++++++++++++++++++++++++++++++- include/linux/serial_core.h | 43 ++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) -- 2.7.4