Index: tcp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.141
diff -u -2 -0 -r1.141 tcp_usrreq.c
--- tcp_usrreq.c	17 Sep 2006 13:39:35 -0000	1.141
+++ tcp_usrreq.c	10 Nov 2006 17:28:11 -0000
@@ -1240,188 +1240,179 @@
 		ti->tcpi_snd_wscale = tp->snd_scale;
 		ti->tcpi_rcv_wscale = tp->rcv_scale;
 	}
 	ti->tcpi_snd_ssthresh = tp->snd_ssthresh;
 	ti->tcpi_snd_cwnd = tp->snd_cwnd;
 
 	/*
 	 * FreeBSD-specific extension fields for tcp_info.
 	 */
 	ti->tcpi_rcv_space = tp->rcv_wnd;
 	ti->tcpi_snd_wnd = tp->snd_wnd;
 	ti->tcpi_snd_bwnd = tp->snd_bwnd;
 }
 
 /*
  * The new sockopt interface makes it possible for us to block in the
  * copyin/out step (if we take a page fault).  Taking a page fault at
  * splnet() is probably a Bad Thing.  (Since sockets and pcbs both now
  * use TSM, there probably isn't any need for this function to run at
  * splnet() any more.  This needs more examination.)
- *
- * XXXRW: The locking here is wrong; we may take a page fault while holding
- * the inpcb lock.
  */
 int
 tcp_ctloutput(so, sopt)
 	struct socket *so;
 	struct sockopt *sopt;
 {
 	int	error, opt, optval;
 	struct	inpcb *inp;
 	struct	tcpcb *tp;
 	struct	tcp_info ti;
 
 	error = 0;
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("tcp_ctloutput: inp == NULL"));
 	INP_LOCK(inp);
 	if (sopt->sopt_level != IPPROTO_TCP) {
 		INP_UNLOCK(inp);
 #ifdef INET6
 		if (INP_CHECK_SOCKAF(so, AF_INET6))
 			error = ip6_ctloutput(so, sopt);
 		else
 #endif /* INET6 */
 		error = ip_ctloutput(so, sopt);
 		return (error);
 	}
+	if (sopt->sopt_dir == SOPT_SET) {
+		INP_UNLOCK(inp);
+		error = sooptcopyin(sopt, &optval, sizeof optval,
+				    sizeof optval);
+		if (error)
+			return (error);
+		INP_LOCK(inp);
+	}
 	if (inp->inp_vflag & (INP_TIMEWAIT | INP_DROPPED)) {
-		error = ECONNRESET;
-		goto out;
+		INP_UNLOCK(inp);
+		return (ECONNRESET);
 	}
 	tp = intotcpcb(inp);
 
 	switch (sopt->sopt_dir) {
 	case SOPT_SET:
 		switch (sopt->sopt_name) {
 #ifdef TCP_SIGNATURE
 		case TCP_MD5SIG:
-			error = sooptcopyin(sopt, &optval, sizeof optval,
-					    sizeof optval);
-			if (error)
-				break;
-
 			if (optval > 0)
 				tp->t_flags |= TF_SIGNATURE;
 			else
 				tp->t_flags &= ~TF_SIGNATURE;
 			break;
 #endif /* TCP_SIGNATURE */
 		case TCP_NODELAY:
 		case TCP_NOOPT:
-			error = sooptcopyin(sopt, &optval, sizeof optval,
-					    sizeof optval);
-			if (error)
-				break;
-
 			switch (sopt->sopt_name) {
 			case TCP_NODELAY:
 				opt = TF_NODELAY;
 				break;
 			case TCP_NOOPT:
 				opt = TF_NOOPT;
 				break;
 			default:
 				opt = 0; /* dead code to fool gcc */
 				break;
 			}
 
 			if (optval)
 				tp->t_flags |= opt;
 			else
 				tp->t_flags &= ~opt;
 			break;
 
 		case TCP_NOPUSH:
-			error = sooptcopyin(sopt, &optval, sizeof optval,
-					    sizeof optval);
-			if (error)
-				break;
-
 			if (optval)
 				tp->t_flags |= TF_NOPUSH;
 			else {
 				tp->t_flags &= ~TF_NOPUSH;
 				error = tcp_output(tp);
 			}
 			break;
 
 		case TCP_MAXSEG:
-			error = sooptcopyin(sopt, &optval, sizeof optval,
-					    sizeof optval);
-			if (error)
-				break;
-
 			if (optval > 0 && optval <= tp->t_maxseg &&
 			    optval + 40 >= tcp_minmss)
 				tp->t_maxseg = optval;
 			else
 				error = EINVAL;
 			break;
 
 		case TCP_INFO:
 			error = EINVAL;
 			break;
 
 		default:
 			error = ENOPROTOOPT;
 			break;
 		}
+		INP_UNLOCK(inp);
 		break;
 
 	case SOPT_GET:
 		switch (sopt->sopt_name) {
 #ifdef TCP_SIGNATURE
 		case TCP_MD5SIG:
 			optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
+			INP_UNLOCK(inp);
 			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 #endif
 		case TCP_NODELAY:
 			optval = tp->t_flags & TF_NODELAY;
+			INP_UNLOCK(inp);
 			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 		case TCP_MAXSEG:
 			optval = tp->t_maxseg;
+			INP_UNLOCK(inp);
 			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 		case TCP_NOOPT:
 			optval = tp->t_flags & TF_NOOPT;
+			INP_UNLOCK(inp);
 			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 		case TCP_NOPUSH:
 			optval = tp->t_flags & TF_NOPUSH;
+			INP_UNLOCK(inp);
 			error = sooptcopyout(sopt, &optval, sizeof optval);
 			break;
 		case TCP_INFO:
 			tcp_fill_info(tp, &ti);
+			INP_UNLOCK(inp);
 			error = sooptcopyout(sopt, &ti, sizeof ti);
 			break;
 		default:
+			INP_UNLOCK(inp);
 			error = ENOPROTOOPT;
 			break;
 		}
 		break;
 	}
-out:
-	INP_UNLOCK(inp);
 	return (error);
 }
 
 /*
  * tcp_sendspace and tcp_recvspace are the default send and receive window
  * sizes, respectively.  These are obsolescent (this information should
  * be set by the route).
  */
 u_long	tcp_sendspace = 1024*32;
 SYSCTL_ULONG(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_RW,
     &tcp_sendspace , 0, "Maximum outgoing TCP datagram size");
 u_long	tcp_recvspace = 1024*64;
 SYSCTL_ULONG(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_RW,
     &tcp_recvspace , 0, "Maximum incoming TCP datagram size");
 
 /*
  * Attach TCP protocol to socket, allocating
  * internet protocol control block, tcp control block,
  * bufer space, and entering LISTEN state if to accept connections.
  */
