On Tue, Feb 06, 2024 at 03:09:32PM +0200, Andy Shevchenko wrote: > On Tue, Feb 06, 2024 at 03:33:22PM +0800, Yicong Yang wrote: > > From: Yicong Yang <yangyicong@xxxxxxxxxxxxx> > > > > We accidently met the issue that the bash prompt is not shown after the > > previous command done and until the next input if there's only one CPU > > (In our issue other CPUs are isolated by isolcpus=). Further analysis > > shows it's because the port entering runtime suspend even if there's > > still pending chars in the buffer and the pending chars will only be > > processed in next device resuming. We are using amba-pl011 and the > > problematic flow is like below: > > > > Bash kworker > > tty_write() > > file_tty_write() > > n_tty_write() > > uart_write() > > __uart_start() > > pm_runtime_get() // wakeup waker > > queue_work() > > pm_runtime_work() > > rpm_resume() > > status = RPM_RESUMING > > serial_port_runtime_resume() > > port->ops->start_tx() > > pl011_tx_chars() > > uart_write_wakeup() > > […] > > __uart_start() > > pm_runtime_get() < 0 // because runtime status = RPM_RESUMING > > // later data are not commit to the port driver > > status = RPM_ACTIVE > > rpm_idle() -> rpm_suspend() > > > > This patch tries to fix this by checking the port busy before entering > > runtime suspending. A runtime_suspend callback is added for the port > > driver. When entering runtime suspend the callback is invoked, if there's > > still pending chars in the buffer then flush the buffer. ... > > +static int serial_port_runtime_suspend(struct device *dev) > > +{ > > + struct serial_port_device *port_dev = to_serial_base_port_device(dev); > > + struct uart_port *port; > > + unsigned long flags; > > + int ret = 0; > > + > > + port = port_dev->port; > > + > > + if (port->flags & UPF_DEAD) > > + return ret; > > + > > + uart_port_lock_irqsave(port, &flags); > > + if (__serial_port_busy(port)) { > > + port->ops->start_tx(port); > > > + pm_runtime_mark_last_busy(dev); > > Do you think we need to call this under a lock? > > > + ret = -EBUSY; > > + } > > + uart_port_unlock_irqrestore(port, flags); > > + > > + return ret; > > +} > > With the above I would rather write it as > > static int __serial_port_busy(struct uart_port *port) > { > if (uart_tx_stopped(port)) > return 0; > > if (uart_circ_chars_pending(&port->state->xmit) > return -EBUSY; > > return 0; > } > > static int serial_port_runtime_suspend(struct device *dev) > { > int ret; > ... > uart_port_lock_irqsave(port, &flags); > ret = __serial_port_busy(port); > if (ret) > port->ops->start_tx(port); > uart_port_unlock_irqrestore(port, flags); > if (ret) > pm_runtime_mark_last_busy(dev); And obvious question here: why in case of 0 we can't mark this as busy as well? I.o.w. why do we need to mark it only when error is set? > return ret; > } > > It also seems aligned with the resume implementation above. > > ... > > For the consistency's sake the resume can be refactored as > > static int serial_port_runtime_resume(struct device *dev) > { > ... > int ret; > ... > ret = __serial_port_busy(port); > if (ret) > ... > } > > but this can be done later. -- With Best Regards, Andy Shevchenko