GTA01 shared UART vs. flow control (bug #788)

Mike (mwester) mwester at dls.net
Sat Feb 2 17:53:51 CET 2008


Proposed simple solutions - comments welcome, even more welcome would be an 
explicit statement of which solution will be accepted into the 2.6.24 kernel 
for the GTA01.

I propose four variants to fix this problem in a manner that will resolve 
the objections presented by the earlier patches.  Based on the excellent 
feedback on this topic last time it was discussed, I restate the problem in 
a different way:

--> A problem exists in the serial driver wherein the kernel can hang 
indefinitely if hardware flow-control is asserted on a serial port that is 
also serving as the console for the device.

By way of background, I did look at a random selection of other serial 
drivers to see how the console is handled in event of flow control.  It 
seems that in general, flow control is simply ignored by the drivers for 
console output -- resulting in console output being transmitted regardless 
of flow control in the case where the driver handles flow control.  The 
general structure is that kernel console output will busy-wait on the serial 
port until the UART accepts the next character (clearly a "chatty" console 
is a waste of CPU cyles).  So on most drivers with dumb ports, console 
output will not lock up the kernel -- it will transmit a character despite 
flow control being asserted, and presumably the device on the other end that 
asserted flow control in the first place will drop the console output and 
report a buffer overrun or some other error.  So clearly, there is no kernel 
guarantee that flow control is honored in the first place.

---> Option 1:
We avoid the problem entirely by pro-actively dropping the character to be 
transmitted if flow-control is enabled:

 s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
 {
        unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
+       unsigned int umcon = rd_regl(cons_uart, S3C2410_UMCON);
+
+       /* If auto HW flow control enabled, don't risk transmitting. */
+       if (umcon & S3C2410_UMCOM_AFC)
+               return;
+
        while (!s3c24xx_serial_console_txrdy(port, ufcon))
                barrier();
        wr_regb(cons_uart, S3C2410_UTXH, ch);
 }

Ideally we would only do this is flow-control is *asserted*, but when 
hardware flow-control is enabled, there doesn't seem to be a good way to 
determine this.  It could also be argued that we could check if the FIFO has 
space left before we just drop the character, but given that the FIFO is far 
smaller than most kernel printk() messages, that just delays the inevitable.

---> Option 2:
We attempt to duplicate the behavior of the typical "dumb" serial driver, 
and transmit anyway by temporarily disabling auto-flow-control mode:

 s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
 {
        unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
+       unsigned int umcon = rd_regl(cons_uart, S3C2410_UMCON);
+
+       /* If auto HW flow control enabled, temporarily turn it off */
+       if (umcon & S3C2410_UMCOM_AFC)
+               wr_regl(port, S3C2410_UMCON, (umcon & !S3C2410_UMCOM_AFC));
+
        while (!s3c24xx_serial_console_txrdy(port, ufcon))
                barrier();
        wr_regb(cons_uart, S3C2410_UTXH, ch);
+
+       if (umcon & S3C2410_UMCOM_AFC)
+               wr_regl(port, S3C2410_UMCON, umcon);
 }

This may be a better solution, but I'm just not sure if the extra complexity 
is worth it.  Once particular problem is that by doing this, we've not 
*really* sent the console character, we've let the first character in the 
FIFO transmit -- and there's no guarantee in the general case that the first 
character is console output.  Also, I'm concerned about timing issues here; 
there's no locking anywhere in the driver -- is there a possibility that bad 
timing could result in an IRQ being generated as the FIFO empties that would 
trigger other code in this driver to fill it up again?

---> Option 3:
 Simply disallow auto HW flow-control entirely if the port is used as a 
console.  The code is similar to the above; we simply test if the AFC is 
enabled, and if so we disable it and leave it that way.  This option 
duplicates the behavior of the patch I submitted for bug #788, but in a far 
less intrusive and far simpler manner.

---> Option 4:
  Add a timer to Option 1 or 3.  This was proposed in earlier discussions. 
If we hard-code a value for the timer, this is not a difficult thing to do. 
However, after examining what other serial drivers do with regard to flow 
control (i.e. they ignore it), I wonder if the added complexity is really 
worthwhile.  Also, if the value is to be configurable (kconfig?), the patch 
really does become intrusive once again.


I welcome comments, and hope we can agree on a solution that we can 
implement in the 2.6.24 kernel for the neo.

Regards,
Mike (mwester)






More information about the openmoko-kernel mailing list