aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/kgdb_nmi.c
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2014-05-29 04:48:45 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-29 14:28:05 -0400
commit8a0ff60f7eeab3df34d475c952b9d75799de8975 (patch)
tree7f9f3fd66a75044d5ef44924d95effc08cd2cf6a /drivers/tty/serial/kgdb_nmi.c
parent06d18289256ec58b78ae8d5b83ff70c3c34309f5 (diff)
serial: kgdb_nmi: Switch from tasklets to real timers
kgdb_nmi uses tasklets on the assumption they will not be scheduled until the next timer tick. This assumption is invalid and can lead to live lock, continually servicing the kgdb_nmi tasklet. This is fixed by using the timer API instead. Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/kgdb_nmi.c')
-rw-r--r--drivers/tty/serial/kgdb_nmi.c30
1 files changed, 9 insertions, 21 deletions
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c
index d51b2a1ba909..20d21d09ee86 100644
--- a/drivers/tty/serial/kgdb_nmi.c
+++ b/drivers/tty/serial/kgdb_nmi.c
@@ -80,24 +80,10 @@ static struct console kgdb_nmi_console = {
80 80
81struct kgdb_nmi_tty_priv { 81struct kgdb_nmi_tty_priv {
82 struct tty_port port; 82 struct tty_port port;
83 struct tasklet_struct tlet; 83 struct timer_list timer;
84 STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo; 84 STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
85}; 85};
86 86
87/*
88 * Our debugging console is polled in a tasklet, so we'll check for input
89 * every tick. In HZ-less mode, we should program the next tick. We have
90 * to use the lowlevel stuff as no locks should be grabbed.
91 */
92#ifdef CONFIG_HIGH_RES_TIMERS
93static void kgdb_tty_poke(void)
94{
95 tick_program_event(ktime_get(), 0);
96}
97#else
98static inline void kgdb_tty_poke(void) {}
99#endif
100
101static struct tty_port *kgdb_nmi_port; 87static struct tty_port *kgdb_nmi_port;
102 88
103static void kgdb_tty_recv(int ch) 89static void kgdb_tty_recv(int ch)
@@ -108,14 +94,13 @@ static void kgdb_tty_recv(int ch)
108 if (!kgdb_nmi_port || ch < 0) 94 if (!kgdb_nmi_port || ch < 0)
109 return; 95 return;
110 /* 96 /*
111 * Can't use port->tty->driver_data as tty might be not there. Tasklet 97 * Can't use port->tty->driver_data as tty might be not there. Timer
112 * will check for tty and will get the ref, but here we don't have to 98 * will check for tty and will get the ref, but here we don't have to
113 * do that, and actually, we can't: we're in NMI context, no locks are 99 * do that, and actually, we can't: we're in NMI context, no locks are
114 * possible. 100 * possible.
115 */ 101 */
116 priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port); 102 priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port);
117 kfifo_in(&priv->fifo, &c, 1); 103 kfifo_in(&priv->fifo, &c, 1);
118 kgdb_tty_poke();
119} 104}
120 105
121static int kgdb_nmi_poll_one_knock(void) 106static int kgdb_nmi_poll_one_knock(void)
@@ -199,7 +184,8 @@ static void kgdb_nmi_tty_receiver(unsigned long data)
199 struct kgdb_nmi_tty_priv *priv = (void *)data; 184 struct kgdb_nmi_tty_priv *priv = (void *)data;
200 char ch; 185 char ch;
201 186
202 tasklet_schedule(&priv->tlet); 187 priv->timer.expires = jiffies + (HZ/100);
188 add_timer(&priv->timer);
203 189
204 if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo))) 190 if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo)))
205 return; 191 return;
@@ -215,7 +201,9 @@ static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
215 container_of(port, struct kgdb_nmi_tty_priv, port); 201 container_of(port, struct kgdb_nmi_tty_priv, port);
216 202
217 kgdb_nmi_port = port; 203 kgdb_nmi_port = port;
218 tasklet_schedule(&priv->tlet); 204 priv->timer.expires = jiffies + (HZ/100);
205 add_timer(&priv->timer);
206
219 return 0; 207 return 0;
220} 208}
221 209
@@ -224,7 +212,7 @@ static void kgdb_nmi_tty_shutdown(struct tty_port *port)
224 struct kgdb_nmi_tty_priv *priv = 212 struct kgdb_nmi_tty_priv *priv =
225 container_of(port, struct kgdb_nmi_tty_priv, port); 213 container_of(port, struct kgdb_nmi_tty_priv, port);
226 214
227 tasklet_kill(&priv->tlet); 215 del_timer(&priv->timer);
228 kgdb_nmi_port = NULL; 216 kgdb_nmi_port = NULL;
229} 217}
230 218
@@ -243,7 +231,7 @@ static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
243 return -ENOMEM; 231 return -ENOMEM;
244 232
245 INIT_KFIFO(priv->fifo); 233 INIT_KFIFO(priv->fifo);
246 tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv); 234 setup_timer(&priv->timer, kgdb_nmi_tty_receiver, (unsigned long)priv);
247 tty_port_init(&priv->port); 235 tty_port_init(&priv->port);
248 priv->port.ops = &kgdb_nmi_tty_port_ops; 236 priv->port.ops = &kgdb_nmi_tty_port_ops;
249 tty->driver_data = priv; 237 tty->driver_data = priv;