--- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1447,47 +1447,29 @@ tp->t_rocol = 0; CLR(tp->t_state, TS_LOCAL); ttwakeup(tp); - if (ISSET(tp->t_state, TS_TBLOCK)) { - if (rw & FWRITE) - FLUSHQ(&tp->t_outq); - ttyunblock(tp); - - /* - * Don't let leave any state that might clobber the - * next line discipline (although we should do more - * to send the START char). Not clearing the state - * may have caused the "putc to a clist with no - * reserved cblocks" panic/kprintf. - */ - CLR(tp->t_state, TS_TBLOCK); - -#if 0 /* forget it, sleeping isn't always safe and we don't know when it is */ - if (ISSET(tp->t_iflag, IXOFF)) { - /* - * XXX wait a bit in the hope that the stop - * character (if any) will go out. Waiting - * isn't good since it allows races. This - * will be fixed when the stop character is - * put in a special queue. Don't bother with - * the checks in ttywait() since the timeout - * will save us. - */ - SET(tp->t_state, TS_SO_OCOMPLETE); - ttysleep(tp, TSA_OCOMPLETE(tp), 0, - "ttyfls", hz / 10); - /* - * Don't try sending the stop character again. - */ - CLR(tp->t_state, TS_TBLOCK); - goto again; - } -#endif - } } if (rw & FWRITE) { FLUSHQ(&tp->t_outq); ttwwakeup(tp); } + /* + * Unblock input flow control only after the final output flush so + * the START character queued by ttyunblock() is not discarded by + * the FLUSHQ above. The old ordering stranded IXOFF peers forever: + * the resume char was queued and then thrown away, while TS_TBLOCK + * was already cleared so no later path re-sent it. + */ + if ((rw & FREAD) && ISSET(tp->t_state, TS_TBLOCK)) { + ttyunblock(tp); + + /* + * Don't leave any state that might clobber the next line + * discipline (although we should do more to send the START + * char). Not clearing the state may have caused the + * "putc to a clist with no reserved cblocks" panic/kprintf. + */ + CLR(tp->t_state, TS_TBLOCK); + } lwkt_reltoken(&tp->t_token); }