tcp_sack.c (8291294024cbe743b2d0dd60c6307c96ca053df9) tcp_sack.c (e891d82b5674ff785a8ab1018610c598f36b7b8a)
1/*-
2 * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 156 unchanged lines hidden (view full) ---

165extern struct uma_zone *sack_hole_zone;
166
167SYSCTL_NODE(_net_inet_tcp, OID_AUTO, sack, CTLFLAG_RW, 0, "TCP SACK");
168int tcp_do_sack = 1;
169SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, enable, CTLFLAG_RW,
170 &tcp_do_sack, 0, "Enable/Disable TCP SACK support");
171TUNABLE_INT("net.inet.tcp.sack.enable", &tcp_do_sack);
172
1/*-
2 * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 156 unchanged lines hidden (view full) ---

165extern struct uma_zone *sack_hole_zone;
166
167SYSCTL_NODE(_net_inet_tcp, OID_AUTO, sack, CTLFLAG_RW, 0, "TCP SACK");
168int tcp_do_sack = 1;
169SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, enable, CTLFLAG_RW,
170 &tcp_do_sack, 0, "Enable/Disable TCP SACK support");
171TUNABLE_INT("net.inet.tcp.sack.enable", &tcp_do_sack);
172
173static int tcp_sack_maxholes = 128;
174SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, maxholes, CTLFLAG_RW,
175 &tcp_sack_maxholes, 0,
176 "Maximum number of TCP SACK holes allowed per connection");
177
178static int tcp_sack_globalmaxholes = 65536;
179SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalmaxholes, CTLFLAG_RW,
180 &tcp_sack_globalmaxholes, 0,
181 "Global maximum number of TCP SACK holes");
182
183static int tcp_sack_globalholes = 0;
184SYSCTL_INT(_net_inet_tcp_sack, OID_AUTO, globalholes, CTLFLAG_RD,
185 &tcp_sack_globalholes, 0,
186 "Global number of TCP SACK holes currently allocated");
173/*
174 * This function is called upon receipt of new valid data (while not in header
175 * prediction mode), and it updates the ordered list of sacks.
176 */
177void
178tcp_update_sack_list(tp, rcv_laststart, rcv_lastend)
179 struct tcpcb *tp;
180 tcp_seq rcv_laststart, rcv_lastend;

--- 111 unchanged lines hidden (view full) ---

292{
293 int tmp_olen;
294 u_char *tmp_cp;
295 struct sackhole *cur, *p, *temp;
296
297 INP_LOCK_ASSERT(tp->t_inpcb);
298 if (!tp->sack_enable)
299 return (1);
187/*
188 * This function is called upon receipt of new valid data (while not in header
189 * prediction mode), and it updates the ordered list of sacks.
190 */
191void
192tcp_update_sack_list(tp, rcv_laststart, rcv_lastend)
193 struct tcpcb *tp;
194 tcp_seq rcv_laststart, rcv_lastend;

--- 111 unchanged lines hidden (view full) ---

306{
307 int tmp_olen;
308 u_char *tmp_cp;
309 struct sackhole *cur, *p, *temp;
310
311 INP_LOCK_ASSERT(tp->t_inpcb);
312 if (!tp->sack_enable)
313 return (1);
300
314 if ((th->th_flags & TH_ACK) == 0)
315 return (1);
301 /* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
302 if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
303 return (1);
316 /* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
317 if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
318 return (1);
304 /* If ack is outside window, ignore the SACK options */
319 /* If ack is outside [snd_una, snd_max], ignore the SACK options */
305 if (SEQ_LT(th->th_ack, tp->snd_una) || SEQ_GT(th->th_ack, tp->snd_max))
306 return (1);
307 tmp_cp = cp + 2;
308 tmp_olen = optlen - 2;
309 tcpstat.tcps_sack_rcv_blocks++;
320 if (SEQ_LT(th->th_ack, tp->snd_una) || SEQ_GT(th->th_ack, tp->snd_max))
321 return (1);
322 tmp_cp = cp + 2;
323 tmp_olen = optlen - 2;
324 tcpstat.tcps_sack_rcv_blocks++;
310 if (tp->snd_numholes < 0)
325 if (tp->snd_numholes < 0) /* XXX panic? */
311 tp->snd_numholes = 0;
312 if (tp->t_maxseg == 0)
313 panic("tcp_sack_option"); /* Should never happen */
314 while (tmp_olen > 0) {
315 struct sackblk sack;
316
317 bcopy(tmp_cp, (char *) &(sack.start), sizeof(tcp_seq));
318 sack.start = ntohl(sack.start);

--- 8 unchanged lines hidden (view full) ---

327 continue; /* old block */
328 if (SEQ_GT(th->th_ack, tp->snd_una)) {
329 if (SEQ_LT(sack.start, th->th_ack))
330 continue;
331 }
332 if (SEQ_GT(sack.end, tp->snd_max))
333 continue;
334 if (tp->snd_holes == NULL) { /* first hole */
326 tp->snd_numholes = 0;
327 if (tp->t_maxseg == 0)
328 panic("tcp_sack_option"); /* Should never happen */
329 while (tmp_olen > 0) {
330 struct sackblk sack;
331
332 bcopy(tmp_cp, (char *) &(sack.start), sizeof(tcp_seq));
333 sack.start = ntohl(sack.start);

--- 8 unchanged lines hidden (view full) ---

342 continue; /* old block */
343 if (SEQ_GT(th->th_ack, tp->snd_una)) {
344 if (SEQ_LT(sack.start, th->th_ack))
345 continue;
346 }
347 if (SEQ_GT(sack.end, tp->snd_max))
348 continue;
349 if (tp->snd_holes == NULL) { /* first hole */
350 if (tcp_sack_globalholes >= tcp_sack_globalmaxholes ||
351 tcp_sack_maxholes == 0) {
352 tcpstat.tcps_sack_sboverflow++;
353 continue;
354 }
335 tp->snd_holes = (struct sackhole *)
336 uma_zalloc(sack_hole_zone,M_NOWAIT);
337 if (tp->snd_holes == NULL) {
338 /* ENOBUFS, so ignore SACKed block for now*/
339 continue;
340 }
341 cur = tp->snd_holes;
342 cur->start = th->th_ack;
343 cur->end = sack.start;
344 cur->rxmit = cur->start;
345 cur->next = NULL;
346 tp->snd_numholes = 1;
355 tp->snd_holes = (struct sackhole *)
356 uma_zalloc(sack_hole_zone,M_NOWAIT);
357 if (tp->snd_holes == NULL) {
358 /* ENOBUFS, so ignore SACKed block for now*/
359 continue;
360 }
361 cur = tp->snd_holes;
362 cur->start = th->th_ack;
363 cur->end = sack.start;
364 cur->rxmit = cur->start;
365 cur->next = NULL;
366 tp->snd_numholes = 1;
367 tcp_sack_globalholes++;
347 tp->rcv_lastsack = sack.end;
348 continue; /* with next sack block */
349 }
350 /* Go thru list of holes: p = previous, cur = current */
351 p = cur = tp->snd_holes;
352 while (cur) {
353 if (SEQ_LEQ(sack.end, cur->start))
354 /* SACKs data before the current hole */

--- 14 unchanged lines hidden (view full) ---

369 cur = p->next;
370 } else {
371 cur = cur->next;
372 uma_zfree(sack_hole_zone, p);
373 p = cur;
374 tp->snd_holes = p;
375 }
376 tp->snd_numholes--;
368 tp->rcv_lastsack = sack.end;
369 continue; /* with next sack block */
370 }
371 /* Go thru list of holes: p = previous, cur = current */
372 p = cur = tp->snd_holes;
373 while (cur) {
374 if (SEQ_LEQ(sack.end, cur->start))
375 /* SACKs data before the current hole */

--- 14 unchanged lines hidden (view full) ---

390 cur = p->next;
391 } else {
392 cur = cur->next;
393 uma_zfree(sack_hole_zone, p);
394 p = cur;
395 tp->snd_holes = p;
396 }
397 tp->snd_numholes--;
398 tcp_sack_globalholes--;
377 continue;
378 }
379 /* otherwise, move start of hole forward */
380 cur->start = sack.end;
381 cur->rxmit = SEQ_MAX(cur->rxmit, cur->start);
382 p = cur;
383 cur = cur->next;
384 continue;

--- 7 unchanged lines hidden (view full) ---

392 continue;
393 }
394 if (SEQ_LT(cur->start, sack.start) &&
395 SEQ_GT(cur->end, sack.end)) {
396 /*
397 * ACKs some data in middle of a hole; need to
398 * split current hole
399 */
399 continue;
400 }
401 /* otherwise, move start of hole forward */
402 cur->start = sack.end;
403 cur->rxmit = SEQ_MAX(cur->rxmit, cur->start);
404 p = cur;
405 cur = cur->next;
406 continue;

--- 7 unchanged lines hidden (view full) ---

414 continue;
415 }
416 if (SEQ_LT(cur->start, sack.start) &&
417 SEQ_GT(cur->end, sack.end)) {
418 /*
419 * ACKs some data in middle of a hole; need to
420 * split current hole
421 */
422 if (tp->snd_numholes >= tcp_sack_maxholes ||
423 tcp_sack_globalholes >=
424 tcp_sack_globalmaxholes) {
425 tcpstat.tcps_sack_sboverflow++;
426 continue;
427 }
400 temp = (struct sackhole *)
401 uma_zalloc(sack_hole_zone,M_NOWAIT);
402 if (temp == NULL)
403 continue; /* ENOBUFS */
404 temp->next = cur->next;
405 temp->start = sack.end;
406 temp->end = cur->end;
407 temp->rxmit = SEQ_MAX(cur->rxmit, temp->start);
408 cur->end = sack.start;
409 cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
410 cur->next = temp;
411 p = temp;
412 cur = p->next;
413 tp->snd_numholes++;
428 temp = (struct sackhole *)
429 uma_zalloc(sack_hole_zone,M_NOWAIT);
430 if (temp == NULL)
431 continue; /* ENOBUFS */
432 temp->next = cur->next;
433 temp->start = sack.end;
434 temp->end = cur->end;
435 temp->rxmit = SEQ_MAX(cur->rxmit, temp->start);
436 cur->end = sack.start;
437 cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
438 cur->next = temp;
439 p = temp;
440 cur = p->next;
441 tp->snd_numholes++;
442 tcp_sack_globalholes++;
414 }
415 }
416 /* At this point, p points to the last hole on the list */
417 if (SEQ_LT(tp->rcv_lastsack, sack.start)) {
418 /*
419 * Need to append new hole at end.
420 * Last hole is p (and it's not NULL).
421 */
443 }
444 }
445 /* At this point, p points to the last hole on the list */
446 if (SEQ_LT(tp->rcv_lastsack, sack.start)) {
447 /*
448 * Need to append new hole at end.
449 * Last hole is p (and it's not NULL).
450 */
451 if (tp->snd_numholes >= tcp_sack_maxholes ||
452 tcp_sack_globalholes >= tcp_sack_globalmaxholes) {
453 tcpstat.tcps_sack_sboverflow++;
454 continue;
455 }
422 temp = (struct sackhole *)
423 uma_zalloc(sack_hole_zone,M_NOWAIT);
424 if (temp == NULL)
425 continue; /* ENOBUFS */
426 temp->start = tp->rcv_lastsack;
427 temp->end = sack.start;
428 temp->rxmit = temp->start;
429 temp->next = 0;
430 p->next = temp;
431 tp->rcv_lastsack = sack.end;
432 tp->snd_numholes++;
456 temp = (struct sackhole *)
457 uma_zalloc(sack_hole_zone,M_NOWAIT);
458 if (temp == NULL)
459 continue; /* ENOBUFS */
460 temp->start = tp->rcv_lastsack;
461 temp->end = sack.start;
462 temp->rxmit = temp->start;
463 temp->next = 0;
464 p->next = temp;
465 tp->rcv_lastsack = sack.end;
466 tp->snd_numholes++;
467 tcp_sack_globalholes++;
433 }
434 }
435 return (0);
436}
437
438/*
439 * Delete stale (i.e, cumulatively ack'd) holes. Hole is deleted only if
440 * it is completely acked; otherwise, tcp_sack_option(), called from

--- 12 unchanged lines hidden (view full) ---

453 struct sackhole *cur = tp->snd_holes;
454 struct sackhole *prev;
455 while (cur)
456 if (SEQ_LEQ(cur->end, lastack)) {
457 prev = cur;
458 cur = cur->next;
459 uma_zfree(sack_hole_zone, prev);
460 tp->snd_numholes--;
468 }
469 }
470 return (0);
471}
472
473/*
474 * Delete stale (i.e, cumulatively ack'd) holes. Hole is deleted only if
475 * it is completely acked; otherwise, tcp_sack_option(), called from

--- 12 unchanged lines hidden (view full) ---

488 struct sackhole *cur = tp->snd_holes;
489 struct sackhole *prev;
490 while (cur)
491 if (SEQ_LEQ(cur->end, lastack)) {
492 prev = cur;
493 cur = cur->next;
494 uma_zfree(sack_hole_zone, prev);
495 tp->snd_numholes--;
496 tcp_sack_globalholes--;
461 } else if (SEQ_LT(cur->start, lastack)) {
462 cur->start = lastack;
463 if (SEQ_LT(cur->rxmit, cur->start))
464 cur->rxmit = cur->start;
465 break;
466 } else
467 break;
468 tp->snd_holes = cur;

--- 6 unchanged lines hidden (view full) ---

475 struct sackhole *p, *q;
476
477 INP_LOCK_ASSERT(tp->t_inpcb);
478 q = tp->snd_holes;
479 while (q != NULL) {
480 p = q;
481 q = q->next;
482 uma_zfree(sack_hole_zone, p);
497 } else if (SEQ_LT(cur->start, lastack)) {
498 cur->start = lastack;
499 if (SEQ_LT(cur->rxmit, cur->start))
500 cur->rxmit = cur->start;
501 break;
502 } else
503 break;
504 tp->snd_holes = cur;

--- 6 unchanged lines hidden (view full) ---

511 struct sackhole *p, *q;
512
513 INP_LOCK_ASSERT(tp->t_inpcb);
514 q = tp->snd_holes;
515 while (q != NULL) {
516 p = q;
517 q = q->next;
518 uma_zfree(sack_hole_zone, p);
519 tcp_sack_globalholes--;
483 }
484 tp->snd_holes = 0;
520 }
521 tp->snd_holes = 0;
522 tp->snd_numholes = 0;
485}
486
487/*
488 * Partial ack handling within a sack recovery episode.
489 * Keeping this very simple for now. When a partial ack
490 * is received, force snd_cwnd to a value that will allow
491 * the sender to transmit no more than 2 segments.
492 * If necessary, a better scheme can be adopted at a

--- 105 unchanged lines hidden ---
523}
524
525/*
526 * Partial ack handling within a sack recovery episode.
527 * Keeping this very simple for now. When a partial ack
528 * is received, force snd_cwnd to a value that will allow
529 * the sender to transmit no more than 2 segments.
530 * If necessary, a better scheme can be adopted at a

--- 105 unchanged lines hidden ---