Skip to content

Commit cc81696

Browse files
fancergregkh
authored andcommitted
serial: 8250_dw: Fix common clocks usage race condition
The race condition may happen if the UART reference clock is shared with some other device (on Baikal-T1 SoC it's another DW UART port). In this case if that device changes the clock rate while serial console is using it the DW 8250 UART port might not only end up with an invalid uartclk value saved, but may also experience a distorted output data since baud-clock could have been changed. In order to fix this lets at least try to adjust the 8250 port setting like UART clock rate in case if the reference clock rate change is discovered. The driver will call the new method to update 8250 UART port clock rate settings. It's done by means of the clock event notifier registered at the port startup and unregistered in the shutdown callback method. Note 1. In order to avoid deadlocks we had to execute the UART port update method in a dedicated deferred work. This is due to (in my opinion redundant) the clock update implemented in the dw8250_set_termios() method. Note 2. Before the ref clock is manually changed by the custom set_termios() function we swap the port uartclk value with new rate adjusted to be suitable for the requested baud. It is necessary in order to effectively disable a functionality of the ref clock events handler for the current UART port, since uartclk update will be done a bit further in the generic serial8250_do_set_termios() function. Signed-off-by: Serge Semin <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0be160c commit cc81696

File tree

1 file changed

+101
-2
lines changed

1 file changed

+101
-2
lines changed

drivers/tty/serial/8250/8250_dw.c

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <linux/of_irq.h>
2020
#include <linux/of_platform.h>
2121
#include <linux/platform_device.h>
22+
#include <linux/workqueue.h>
23+
#include <linux/notifier.h>
2224
#include <linux/slab.h>
2325
#include <linux/acpi.h>
2426
#include <linux/clk.h>
@@ -43,6 +45,8 @@ struct dw8250_data {
4345
int msr_mask_off;
4446
struct clk *clk;
4547
struct clk *pclk;
48+
struct notifier_block clk_notifier;
49+
struct work_struct clk_work;
4650
struct reset_control *rst;
4751

4852
unsigned int skip_autocfg:1;
@@ -54,6 +58,16 @@ static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data)
5458
return container_of(data, struct dw8250_data, data);
5559
}
5660

61+
static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb)
62+
{
63+
return container_of(nb, struct dw8250_data, clk_notifier);
64+
}
65+
66+
static inline struct dw8250_data *work_to_dw8250_data(struct work_struct *work)
67+
{
68+
return container_of(work, struct dw8250_data, clk_work);
69+
}
70+
5771
static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
5872
{
5973
struct dw8250_data *d = to_dw8250_data(p->private_data);
@@ -260,6 +274,46 @@ static int dw8250_handle_irq(struct uart_port *p)
260274
return 0;
261275
}
262276

277+
static void dw8250_clk_work_cb(struct work_struct *work)
278+
{
279+
struct dw8250_data *d = work_to_dw8250_data(work);
280+
struct uart_8250_port *up;
281+
unsigned long rate;
282+
283+
rate = clk_get_rate(d->clk);
284+
if (rate <= 0)
285+
return;
286+
287+
up = serial8250_get_port(d->data.line);
288+
289+
serial8250_update_uartclk(&up->port, rate);
290+
}
291+
292+
static int dw8250_clk_notifier_cb(struct notifier_block *nb,
293+
unsigned long event, void *data)
294+
{
295+
struct dw8250_data *d = clk_to_dw8250_data(nb);
296+
297+
/*
298+
* We have no choice but to defer the uartclk update due to two
299+
* deadlocks. First one is caused by a recursive mutex lock which
300+
* happens when clk_set_rate() is called from dw8250_set_termios().
301+
* Second deadlock is more tricky and is caused by an inverted order of
302+
* the clk and tty-port mutexes lock. It happens if clock rate change
303+
* is requested asynchronously while set_termios() is executed between
304+
* tty-port mutex lock and clk_set_rate() function invocation and
305+
* vise-versa. Anyway if we didn't have the reference clock alteration
306+
* in the dw8250_set_termios() method we wouldn't have needed this
307+
* deferred event handling complication.
308+
*/
309+
if (event == POST_RATE_CHANGE) {
310+
queue_work(system_unbound_wq, &d->clk_work);
311+
return NOTIFY_OK;
312+
}
313+
314+
return NOTIFY_DONE;
315+
}
316+
263317
static void
264318
dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
265319
{
@@ -283,9 +337,16 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
283337
clk_disable_unprepare(d->clk);
284338
rate = clk_round_rate(d->clk, newrate);
285339
if (rate > 0) {
340+
/*
341+
* Premilinary set the uartclk to the new clock rate so the
342+
* clock update event handler caused by the clk_set_rate()
343+
* calling wouldn't actually update the UART divisor since
344+
* we about to do this anyway.
345+
*/
346+
swap(p->uartclk, rate);
286347
ret = clk_set_rate(d->clk, newrate);
287-
if (!ret)
288-
p->uartclk = rate;
348+
if (ret)
349+
swap(p->uartclk, rate);
289350
}
290351
clk_prepare_enable(d->clk);
291352

@@ -312,6 +373,39 @@ static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
312373
serial8250_do_set_ldisc(p, termios);
313374
}
314375

376+
static int dw8250_startup(struct uart_port *p)
377+
{
378+
struct dw8250_data *d = to_dw8250_data(p->private_data);
379+
int ret;
380+
381+
/*
382+
* Some platforms may provide a reference clock shared between several
383+
* devices. In this case before using the serial port first we have to
384+
* make sure that any clock state change is known to the UART port at
385+
* least post factum.
386+
*/
387+
if (d->clk) {
388+
ret = clk_notifier_register(d->clk, &d->clk_notifier);
389+
if (ret)
390+
dev_warn(p->dev, "Failed to set the clock notifier\n");
391+
}
392+
393+
return serial8250_do_startup(p);
394+
}
395+
396+
static void dw8250_shutdown(struct uart_port *p)
397+
{
398+
struct dw8250_data *d = to_dw8250_data(p->private_data);
399+
400+
serial8250_do_shutdown(p);
401+
402+
if (d->clk) {
403+
clk_notifier_unregister(d->clk, &d->clk_notifier);
404+
405+
flush_work(&d->clk_work);
406+
}
407+
}
408+
315409
/*
316410
* dw8250_fallback_dma_filter will prevent the UART from getting just any free
317411
* channel on platforms that have DMA engines, but don't have any channels
@@ -407,6 +501,8 @@ static int dw8250_probe(struct platform_device *pdev)
407501
p->serial_out = dw8250_serial_out;
408502
p->set_ldisc = dw8250_set_ldisc;
409503
p->set_termios = dw8250_set_termios;
504+
p->startup = dw8250_startup;
505+
p->shutdown = dw8250_shutdown;
410506

411507
p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
412508
if (!p->membase)
@@ -468,6 +564,9 @@ static int dw8250_probe(struct platform_device *pdev)
468564
if (IS_ERR(data->clk))
469565
return PTR_ERR(data->clk);
470566

567+
INIT_WORK(&data->clk_work, dw8250_clk_work_cb);
568+
data->clk_notifier.notifier_call = dw8250_clk_notifier_cb;
569+
471570
err = clk_prepare_enable(data->clk);
472571
if (err)
473572
dev_warn(dev, "could not enable optional baudclk: %d\n", err);

0 commit comments

Comments
 (0)