1 /* 2 * Copyright (c) 2003, 2004 Jeffrey M. Hsu. All rights reserved. 3 * Copyright (c) 2003, 2004 The DragonFly Project. All rights reserved. 4 * 5 * This code is derived from software contributed to The DragonFly Project 6 * by Jeffrey M. Hsu. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of The DragonFly Project nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific, prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 /* 35 * Copyright (c) 2003, 2004 Jeffrey M. Hsu. All rights reserved. 36 * 37 * License terms: all terms for the DragonFly license above plus the following: 38 * 39 * 4. All advertising materials mentioning features or use of this software 40 * must display the following acknowledgement: 41 * 42 * This product includes software developed by Jeffrey M. Hsu 43 * for the DragonFly Project. 44 * 45 * This requirement may be waived with permission from Jeffrey Hsu. 46 * This requirement will sunset and may be removed on July 8 2005, 47 * after which the standard DragonFly license (as shown above) will 48 * apply. 49 */ 50 51 /* 52 * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 53 * The Regents of the University of California. All rights reserved. 54 * 55 * Redistribution and use in source and binary forms, with or without 56 * modification, are permitted provided that the following conditions 57 * are met: 58 * 1. Redistributions of source code must retain the above copyright 59 * notice, this list of conditions and the following disclaimer. 60 * 2. Redistributions in binary form must reproduce the above copyright 61 * notice, this list of conditions and the following disclaimer in the 62 * documentation and/or other materials provided with the distribution. 63 * 3. All advertising materials mentioning features or use of this software 64 * must display the following acknowledgement: 65 * This product includes software developed by the University of 66 * California, Berkeley and its contributors. 67 * 4. Neither the name of the University nor the names of its contributors 68 * may be used to endorse or promote products derived from this software 69 * without specific prior written permission. 70 * 71 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 72 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 73 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 74 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 75 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 76 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 77 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 78 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 79 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 80 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 81 * SUCH DAMAGE. 82 * 83 * @(#)tcp_timer.c 8.2 (Berkeley) 5/24/95 84 * $FreeBSD: src/sys/netinet/tcp_timer.c,v 1.34.2.14 2003/02/03 02:33:41 hsu Exp $ 85 * $DragonFly: src/sys/netinet/tcp_timer.c,v 1.14 2005/05/10 15:48:10 hsu Exp $ 86 */ 87 88 #include "opt_compat.h" 89 #include "opt_inet6.h" 90 #include "opt_tcpdebug.h" 91 92 #include <sys/param.h> 93 #include <sys/systm.h> 94 #include <sys/kernel.h> 95 #include <sys/mbuf.h> 96 #include <sys/sysctl.h> 97 #include <sys/socket.h> 98 #include <sys/socketvar.h> 99 #include <sys/protosw.h> 100 #include <sys/thread.h> 101 #include <sys/globaldata.h> 102 103 #include <machine/cpu.h> /* before tcp_seq.h, for tcp_random18() */ 104 105 #include <net/route.h> 106 107 #include <netinet/in.h> 108 #include <netinet/in_systm.h> 109 #include <netinet/in_pcb.h> 110 #ifdef INET6 111 #include <netinet6/in6_pcb.h> 112 #endif 113 #include <netinet/ip_var.h> 114 #include <netinet/tcp.h> 115 #include <netinet/tcp_fsm.h> 116 #include <netinet/tcp_seq.h> 117 #include <netinet/tcp_timer.h> 118 #include <netinet/tcp_var.h> 119 #include <netinet/tcpip.h> 120 #ifdef TCPDEBUG 121 #include <netinet/tcp_debug.h> 122 #endif 123 124 static int 125 sysctl_msec_to_ticks(SYSCTL_HANDLER_ARGS) 126 { 127 int error, s, tt; 128 129 tt = *(int *)oidp->oid_arg1; 130 s = (int)((int64_t)tt * 1000 / hz); 131 132 error = sysctl_handle_int(oidp, &s, 0, req); 133 if (error || !req->newptr) 134 return (error); 135 136 tt = (int)((int64_t)s * hz / 1000); 137 if (tt < 1) 138 return (EINVAL); 139 140 *(int *)oidp->oid_arg1 = tt; 141 return (0); 142 } 143 144 int tcp_keepinit; 145 SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINIT, keepinit, CTLTYPE_INT|CTLFLAG_RW, 146 &tcp_keepinit, 0, sysctl_msec_to_ticks, "I", ""); 147 148 int tcp_keepidle; 149 SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPIDLE, keepidle, CTLTYPE_INT|CTLFLAG_RW, 150 &tcp_keepidle, 0, sysctl_msec_to_ticks, "I", ""); 151 152 int tcp_keepintvl; 153 SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINTVL, keepintvl, CTLTYPE_INT|CTLFLAG_RW, 154 &tcp_keepintvl, 0, sysctl_msec_to_ticks, "I", ""); 155 156 int tcp_delacktime; 157 SYSCTL_PROC(_net_inet_tcp, TCPCTL_DELACKTIME, delacktime, 158 CTLTYPE_INT|CTLFLAG_RW, &tcp_delacktime, 0, sysctl_msec_to_ticks, "I", 159 "Time before a delayed ACK is sent"); 160 161 int tcp_msl; 162 SYSCTL_PROC(_net_inet_tcp, OID_AUTO, msl, CTLTYPE_INT|CTLFLAG_RW, 163 &tcp_msl, 0, sysctl_msec_to_ticks, "I", "Maximum segment lifetime"); 164 165 int tcp_rexmit_min; 166 SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_min, CTLTYPE_INT|CTLFLAG_RW, 167 &tcp_rexmit_min, 0, sysctl_msec_to_ticks, "I", "Minimum Retransmission Timeout"); 168 169 int tcp_rexmit_slop; 170 SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_slop, CTLTYPE_INT|CTLFLAG_RW, 171 &tcp_rexmit_slop, 0, sysctl_msec_to_ticks, "I", 172 "Retransmission Timer Slop"); 173 174 static int always_keepalive = 0; 175 SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_RW, 176 &always_keepalive , 0, "Assume SO_KEEPALIVE on all TCP connections"); 177 178 static int tcp_keepcnt = TCPTV_KEEPCNT; 179 /* max idle probes */ 180 int tcp_maxpersistidle; 181 /* max idle time in persist */ 182 int tcp_maxidle; 183 184 /* 185 * Tcp protocol timeout routine called every 500 ms. 186 * Updates timestamps used for TCP 187 * causes finite state machine actions if timers expire. 188 */ 189 void 190 tcp_slowtimo(void) 191 { 192 int s; 193 194 s = splnet(); 195 196 tcp_maxidle = tcp_keepcnt * tcp_keepintvl; 197 198 splx(s); 199 } 200 201 /* 202 * Cancel all timers for TCP tp. 203 */ 204 void 205 tcp_canceltimers(struct tcpcb *tp) 206 { 207 callout_stop(tp->tt_2msl); 208 callout_stop(tp->tt_persist); 209 callout_stop(tp->tt_keep); 210 callout_stop(tp->tt_rexmt); 211 } 212 213 int tcp_syn_backoff[TCP_MAXRXTSHIFT + 1] = 214 { 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, 64, 64 }; 215 216 int tcp_backoff[TCP_MAXRXTSHIFT + 1] = 217 { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; 218 219 static int tcp_totbackoff = 511; /* sum of tcp_backoff[] */ 220 221 /* 222 * TCP timer processing. 223 */ 224 void 225 tcp_timer_delack(void *xtp) 226 { 227 struct tcpcb *tp = xtp; 228 int s; 229 230 s = splnet(); 231 if (callout_pending(tp->tt_delack) || !callout_active(tp->tt_delack)) { 232 splx(s); 233 return; 234 } 235 callout_deactivate(tp->tt_delack); 236 237 tp->t_flags |= TF_ACKNOW; 238 tcpstat.tcps_delack++; 239 tcp_output(tp); 240 splx(s); 241 } 242 243 void 244 tcp_timer_2msl(void *xtp) 245 { 246 struct tcpcb *tp = xtp; 247 int s; 248 #ifdef TCPDEBUG 249 int ostate; 250 251 ostate = tp->t_state; 252 #endif 253 s = splnet(); 254 if (callout_pending(tp->tt_2msl) || !callout_active(tp->tt_2msl)) { 255 splx(s); 256 return; 257 } 258 callout_deactivate(tp->tt_2msl); 259 /* 260 * 2 MSL timeout in shutdown went off. If we're closed but 261 * still waiting for peer to close and connection has been idle 262 * too long, or if 2MSL time is up from TIME_WAIT, delete connection 263 * control block. Otherwise, check again in a bit. 264 */ 265 if (tp->t_state != TCPS_TIME_WAIT && 266 (ticks - tp->t_rcvtime) <= tcp_maxidle) 267 callout_reset(tp->tt_2msl, tcp_keepintvl, 268 tcp_timer_2msl, tp); 269 else 270 tp = tcp_close(tp); 271 272 #ifdef TCPDEBUG 273 if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 274 tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 275 #endif 276 splx(s); 277 } 278 279 void 280 tcp_timer_keep(void *xtp) 281 { 282 struct tcpcb *tp = xtp; 283 struct tcptemp *t_template; 284 int s; 285 #ifdef TCPDEBUG 286 int ostate; 287 288 ostate = tp->t_state; 289 #endif 290 s = splnet(); 291 if (callout_pending(tp->tt_keep) || !callout_active(tp->tt_keep)) { 292 splx(s); 293 return; 294 } 295 callout_deactivate(tp->tt_keep); 296 /* 297 * Keep-alive timer went off; send something 298 * or drop connection if idle for too long. 299 */ 300 tcpstat.tcps_keeptimeo++; 301 if (tp->t_state < TCPS_ESTABLISHED) 302 goto dropit; 303 if ((always_keepalive || 304 tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) && 305 tp->t_state <= TCPS_CLOSING) { 306 if ((ticks - tp->t_rcvtime) >= tcp_keepidle + tcp_maxidle) 307 goto dropit; 308 /* 309 * Send a packet designed to force a response 310 * if the peer is up and reachable: 311 * either an ACK if the connection is still alive, 312 * or an RST if the peer has closed the connection 313 * due to timeout or reboot. 314 * Using sequence number tp->snd_una-1 315 * causes the transmitted zero-length segment 316 * to lie outside the receive window; 317 * by the protocol spec, this requires the 318 * correspondent TCP to respond. 319 */ 320 tcpstat.tcps_keepprobe++; 321 t_template = tcp_maketemplate(tp); 322 if (t_template) { 323 tcp_respond(tp, t_template->tt_ipgen, 324 &t_template->tt_t, (struct mbuf *)NULL, 325 tp->rcv_nxt, tp->snd_una - 1, 0); 326 tcp_freetemplate(t_template); 327 } 328 callout_reset(tp->tt_keep, tcp_keepintvl, tcp_timer_keep, tp); 329 } else 330 callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); 331 332 #ifdef TCPDEBUG 333 if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) 334 tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 335 #endif 336 splx(s); 337 return; 338 339 dropit: 340 tcpstat.tcps_keepdrops++; 341 tp = tcp_drop(tp, ETIMEDOUT); 342 343 #ifdef TCPDEBUG 344 if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 345 tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 346 #endif 347 splx(s); 348 } 349 350 void 351 tcp_timer_persist(void *xtp) 352 { 353 struct tcpcb *tp = xtp; 354 int s; 355 #ifdef TCPDEBUG 356 int ostate; 357 358 ostate = tp->t_state; 359 #endif 360 s = splnet(); 361 if (callout_pending(tp->tt_persist) || !callout_active(tp->tt_persist)){ 362 splx(s); 363 return; 364 } 365 callout_deactivate(tp->tt_persist); 366 /* 367 * Persistance timer into zero window. 368 * Force a byte to be output, if possible. 369 */ 370 tcpstat.tcps_persisttimeo++; 371 /* 372 * Hack: if the peer is dead/unreachable, we do not 373 * time out if the window is closed. After a full 374 * backoff, drop the connection if the idle time 375 * (no responses to probes) reaches the maximum 376 * backoff that we would use if retransmitting. 377 */ 378 if (tp->t_rxtshift == TCP_MAXRXTSHIFT && 379 ((ticks - tp->t_rcvtime) >= tcp_maxpersistidle || 380 (ticks - tp->t_rcvtime) >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { 381 tcpstat.tcps_persistdrop++; 382 tp = tcp_drop(tp, ETIMEDOUT); 383 goto out; 384 } 385 tcp_setpersist(tp); 386 tp->t_flags |= TF_FORCE; 387 tcp_output(tp); 388 tp->t_flags &= ~TF_FORCE; 389 390 out: 391 #ifdef TCPDEBUG 392 if (tp && tp->t_inpcb->inp_socket->so_options & SO_DEBUG) 393 tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 394 #endif 395 splx(s); 396 } 397 398 void 399 tcp_save_congestion_state(struct tcpcb *tp) 400 { 401 tp->snd_cwnd_prev = tp->snd_cwnd; 402 tp->snd_wacked_prev = tp->snd_wacked; 403 tp->snd_ssthresh_prev = tp->snd_ssthresh; 404 tp->snd_recover_prev = tp->snd_recover; 405 if (IN_FASTRECOVERY(tp)) 406 tp->t_flags |= TF_WASFRECOVERY; 407 else 408 tp->t_flags &= ~TF_WASFRECOVERY; 409 if (tp->t_flags & TF_RCVD_TSTMP) { 410 tp->t_rexmtTS = ticks; 411 tp->t_flags |= TF_FIRSTACCACK; 412 } 413 #ifdef later 414 tcp_sack_save_scoreboard(&tp->scb); 415 #endif 416 } 417 418 void 419 tcp_revert_congestion_state(struct tcpcb *tp) 420 { 421 tp->snd_cwnd = tp->snd_cwnd_prev; 422 tp->snd_wacked = tp->snd_wacked_prev; 423 tp->snd_ssthresh = tp->snd_ssthresh_prev; 424 tp->snd_recover = tp->snd_recover_prev; 425 if (tp->t_flags & TF_WASFRECOVERY) 426 ENTER_FASTRECOVERY(tp); 427 if (tp->t_flags & TF_FASTREXMT) { 428 ++tcpstat.tcps_sndfastrexmitbad; 429 if (tp->t_flags & TF_EARLYREXMT) 430 ++tcpstat.tcps_sndearlyrexmitbad; 431 } else 432 ++tcpstat.tcps_sndrtobad; 433 tp->t_badrxtwin = 0; 434 tp->t_rxtshift = 0; 435 tp->snd_nxt = tp->snd_max; 436 #ifdef later 437 tcp_sack_revert_scoreboard(&tp->scb, tp->snd_una); 438 #endif 439 } 440 441 void 442 tcp_timer_rexmt(void *xtp) 443 { 444 struct tcpcb *tp = xtp; 445 int s; 446 int rexmt; 447 #ifdef TCPDEBUG 448 int ostate; 449 450 ostate = tp->t_state; 451 #endif 452 s = splnet(); 453 if (callout_pending(tp->tt_rexmt) || !callout_active(tp->tt_rexmt)) { 454 splx(s); 455 return; 456 } 457 callout_deactivate(tp->tt_rexmt); 458 /* 459 * Retransmission timer went off. Message has not 460 * been acked within retransmit interval. Back off 461 * to a longer retransmit interval and retransmit one segment. 462 */ 463 if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { 464 tp->t_rxtshift = TCP_MAXRXTSHIFT; 465 tcpstat.tcps_timeoutdrop++; 466 tp = tcp_drop(tp, tp->t_softerror ? 467 tp->t_softerror : ETIMEDOUT); 468 goto out; 469 } 470 if (tp->t_rxtshift == 1) { 471 /* 472 * first retransmit; record ssthresh and cwnd so they can 473 * be recovered if this turns out to be a "bad" retransmit. 474 * A retransmit is considered "bad" if an ACK for this 475 * segment is received within RTT/2 interval; the assumption 476 * here is that the ACK was already in flight. See 477 * "On Estimating End-to-End Network Path Properties" by 478 * Allman and Paxson for more details. 479 */ 480 tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); 481 tcp_save_congestion_state(tp); 482 tp->t_flags &= ~(TF_FASTREXMT | TF_EARLYREXMT); 483 } 484 /* Throw away SACK blocks on a RTO, as specified by RFC2018. */ 485 tcp_sack_cleanup(&tp->scb); 486 tcpstat.tcps_rexmttimeo++; 487 if (tp->t_state == TCPS_SYN_SENT) 488 rexmt = TCP_REXMTVAL(tp) * tcp_syn_backoff[tp->t_rxtshift]; 489 else 490 rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; 491 TCPT_RANGESET(tp->t_rxtcur, rexmt, 492 tp->t_rttmin, TCPTV_REXMTMAX); 493 /* 494 * Disable rfc1323 and rfc1644 if we havn't got any response to 495 * our third SYN to work-around some broken terminal servers 496 * (most of which have hopefully been retired) that have bad VJ 497 * header compression code which trashes TCP segments containing 498 * unknown-to-them TCP options. 499 */ 500 if ((tp->t_state == TCPS_SYN_SENT) && (tp->t_rxtshift == 3)) 501 tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP|TF_REQ_CC); 502 /* 503 * If losing, let the lower level know and try for 504 * a better route. Also, if we backed off this far, 505 * our srtt estimate is probably bogus. Clobber it 506 * so we'll take the next rtt measurement as our srtt; 507 * move the current srtt into rttvar to keep the current 508 * retransmit times until then. 509 */ 510 if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { 511 #ifdef INET6 512 if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) 513 in6_losing(tp->t_inpcb); 514 else 515 #endif 516 in_losing(tp->t_inpcb); 517 tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); 518 tp->t_srtt = 0; 519 } 520 tp->snd_nxt = tp->snd_una; 521 tp->rexmt_high = tp->snd_una; 522 tp->snd_recover = tp->snd_max; 523 /* 524 * Force a segment to be sent. 525 */ 526 tp->t_flags |= TF_ACKNOW; 527 /* 528 * If timing a segment in this window, stop the timer. 529 */ 530 tp->t_rtttime = 0; 531 /* 532 * Close the congestion window down to one segment 533 * (we'll open it by one segment for each ack we get). 534 * Since we probably have a window's worth of unacked 535 * data accumulated, this "slow start" keeps us from 536 * dumping all that data as back-to-back packets (which 537 * might overwhelm an intermediate gateway). 538 * 539 * There are two phases to the opening: Initially we 540 * open by one mss on each ack. This makes the window 541 * size increase exponentially with time. If the 542 * window is larger than the path can handle, this 543 * exponential growth results in dropped packet(s) 544 * almost immediately. To get more time between 545 * drops but still "push" the network to take advantage 546 * of improving conditions, we switch from exponential 547 * to linear window opening at some threshhold size. 548 * For a threshhold, we use half the current window 549 * size, truncated to a multiple of the mss. 550 * 551 * (the minimum cwnd that will give us exponential 552 * growth is 2 mss. We don't allow the threshhold 553 * to go below this.) 554 */ 555 { 556 u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; 557 558 if (win < 2) 559 win = 2; 560 tp->snd_cwnd = tp->t_maxseg; 561 tp->snd_wacked = 0; 562 tp->snd_ssthresh = win * tp->t_maxseg; 563 tp->t_dupacks = 0; 564 } 565 EXIT_FASTRECOVERY(tp); 566 tcp_output(tp); 567 568 out: 569 #ifdef TCPDEBUG 570 if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) 571 tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO); 572 #endif 573 splx(s); 574 } 575