Subject: kern/938: tty flow control is broken
To: None <gnats-admin@NetBSD.ORG>
From: Dave Sainty <Dave.Sainty@sans.vuw.ac.nz>
List: netbsd-bugs
Date: 04/03/1995 03:20:06
>Number:         938
>Category:       kern
>Synopsis:       software flow control is never deactivated
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Apr  3 03:20:03 1995
>Originator:     Dave Sainty
>Organization:
sans.vuw.ac.nz
>Release:        1.0
>Environment:
	
netbsd, sys/kern/tty.c
System: NetBSD tao.sans.vuw.ac.nz 1.0 NetBSD 1.0 (TAO) #3: Wed Jan 11 14:40:22 NZDT 1995 @tao.sans.vuw.ac.nz:/mnt/milton/sys/arch/i386/compile/TAO i386


>Description:
	

This problem showed up when auto-logging into a slip provider using a 28k8
modem on a 386sx20 with a perl script, so I believe the problem occurs where
data is incoming at a far greater speed than it is being processed. The I'm
not familiar enough with the tty code to tell for sure or even know for sure
if my fix is the 'right' one, but it certainly solves the problem, and
certainly _looks_ as though the code I've modified is wrong.

in kern/tty.c ttyblock is called by the line:
	if (ISSET(iflag, IXOFF) || ISSET(tp->t_cflag, CHWFLOW))
		ttyblock(tp);

i.e. is called whether hardware or software flow control is active (IXOFF=soft)

but in ttyblock, no check is done before using software flow control that it
is software flow control that is required, so as long as some form of flow
control is used, software flow control will be used also.

	if (total >= TTYHOG / 2 &&
	    !ISSET(tp->t_state, TS_TBLOCK) &&
	    !ISSET(tp->t_lflag, ICANON) || tp->t_canq.c_cc > 0 &&
	    tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
Here >>		if (putc(tp->t_cc[VSTOP], &tp->t_outq) == 0) {
			SET(tp->t_state, TS_TBLOCK);
			ttstart(tp);
		}
		/* try to block remote output via hardware flow control */
		if (ISSET(tp->t_cflag, CHWFLOW) && tp->t_hwiflow &&
		    (*tp->t_hwiflow)(tp, 1) != 0)
			SET(tp->t_state, TS_TBLOCK);
	}


>How-To-Repeat:
	
Very difficult to do so, but the code involved does look wrong...
>Fix:
	

In particular, I'm not sure how the initial comparison for tp->t_cc[VSTOP] !=
_POSIX_VDISABLE should be handled. It looks like it is also wrong as what the
stop character is set to should not be relevant for hardware flow control. All
the patch below does is check that IXOFF is set before using software flow
control and if not, doesn't.


*** sys/kern/tty.c	Sun Sep 18 18:36:30 1994
--- sys/kern/tty.new.c	Sat Apr  1 23:28:28 1995
***************
*** 1046,1052 ****
  	    !ISSET(tp->t_state, TS_TBLOCK) &&
  	    !ISSET(tp->t_lflag, ICANON) || tp->t_canq.c_cc > 0 &&
  	    tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
! 		if (putc(tp->t_cc[VSTOP], &tp->t_outq) == 0) {
  			SET(tp->t_state, TS_TBLOCK);
  			ttstart(tp);
  		}
--- 1046,1053 ----
  	    !ISSET(tp->t_state, TS_TBLOCK) &&
  	    !ISSET(tp->t_lflag, ICANON) || tp->t_canq.c_cc > 0 &&
  	    tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
! 		if (ISSET(tp->t_iflag, IXOFF) &&
! 				(putc(tp->t_cc[VSTOP], &tp->t_outq) == 0)) {
  			SET(tp->t_state, TS_TBLOCK);
  			ttstart(tp);
  		}
>Audit-Trail:
>Unformatted: