Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mxser: fix timeout calculation for low rates

Paul reported, that low rates like B300 make the driver to hang in
mxser_wait_until_sent. His debugging tackled the issue down to the
info->timeout computation in mxser_set_baud. Obviously, ints are used
there and they easily overflow with these low rates: B300 makes
info->timeout to be -373.

So switch all these types to unsigned as it ought to be. And use the u64
domain to perform the computation as in the worst case, we need 35 bits
to store the computed value (before division).

And use do_div not to break 32 bit kernels.

[v2] make it actually build

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Paul <Paul@abelian.netcom.co.uk>
Tested-by: <Paul@abelian.netcom.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Jiri Slaby and committed by
Greg Kroah-Hartman
104583b5 432219fd

+11 -5
+11 -5
drivers/tty/mxser.c
··· 246 246 unsigned char err_shadow; 247 247 248 248 struct async_icount icount; /* kernel counters for 4 input interrupts */ 249 - int timeout; 249 + unsigned int timeout; 250 250 251 251 int read_status_mask; 252 252 int ignore_status_mask; 253 - int xmit_fifo_size; 253 + unsigned int xmit_fifo_size; 254 254 int xmit_head; 255 255 int xmit_tail; 256 256 int xmit_cnt; ··· 572 572 static int mxser_set_baud(struct tty_struct *tty, long newspd) 573 573 { 574 574 struct mxser_port *info = tty->driver_data; 575 - int quot = 0, baud; 575 + unsigned int quot = 0, baud; 576 576 unsigned char cval; 577 + u64 timeout; 577 578 578 579 if (!info->ioaddr) 579 580 return -1; ··· 595 594 quot = 0; 596 595 } 597 596 598 - info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); 599 - info->timeout += HZ / 50; /* Add .02 seconds of slop */ 597 + /* 598 + * worst case (128 * 1000 * 10 * 18432) needs 35 bits, so divide in the 599 + * u64 domain 600 + */ 601 + timeout = (u64)info->xmit_fifo_size * HZ * 10 * quot; 602 + do_div(timeout, info->baud_base); 603 + info->timeout = timeout + HZ / 50; /* Add .02 seconds of slop */ 600 604 601 605 if (quot) { 602 606 info->MCR |= UART_MCR_DTR;