Changeset View
Standalone View
sys/netinet/tcp_subr.c
Context not available. | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/jail.h> | #include <sys/jail.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/refcount.h> | |||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#ifdef INET6 | #ifdef INET6 | ||||
#include <sys/domain.h> | #include <sys/domain.h> | ||||
Context not available. | |||||
VNET_DEFINE(int, tcp_v6mssdflt) = TCP6_MSS; | VNET_DEFINE(int, tcp_v6mssdflt) = TCP6_MSS; | ||||
#endif | #endif | ||||
struct rwlock tcp_function_lock; | |||||
static int | static int | ||||
sysctl_net_inet_tcp_mss_check(SYSCTL_HANDLER_ARGS) | sysctl_net_inet_tcp_mss_check(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
jtl: It looks like there is an extra line here. | |||||
Not Done Inline Actionsack rrs: ack | |||||
Context not available. | |||||
void *ip4hdr, const void *ip6hdr); | void *ip4hdr, const void *ip6hdr); | ||||
static void tcp_timer_discard(struct tcpcb *, uint32_t); | static void tcp_timer_discard(struct tcpcb *, uint32_t); | ||||
static struct tcp_function_block tcp_def_funcblk = { | |||||
"default", | |||||
tcp_output, | |||||
tcp_do_segment, | |||||
Not Done Inline ActionsI am curious why you chose to make the tcp_timer_{activate, active, stop} functions be pointers. Is the goal to actually have different logic there? Or, is the goal simply to allow the user to call different functions when the timer fires? I haven't done profiling to prove this, but I suspect the tcp_timer_{activate, active, stop} functions are called at something like a 1000:1 ratio when compared to the timer firing and the actual timer function being run. And, I suspect that calling the tcp_timer_{activate, active, stop} functions will be cheaper than calling tp->t_fb->tcp_timer_{activate, active, stop}. If we're going to incur the cost for an indirect function lookup, I think we should try to push it to the times when the timer fires. (BTW, I had not originally considered the timer functions for indirection. I had assumed that someone who wanted to change these would need to roto-till the whole TCP stack. But, I like this, because it provides a way to do minor surgery, if that is your only goal.) jtl: I am curious why you chose to make the tcp_timer_{activate, active, stop} functions be pointers. | |||||
Not Done Inline ActionsIn order to implement something like Googles internet draft RACK new timers will be rrs: In order to implement something like Googles internet draft RACK new timers will be
needed. | |||||
Not Done Inline ActionsCan we get the desired functionality by modifying the stock tcp_timer_{activate, active, stop} functions so they call the a user-defined function if the specified timer isn't one of the default ones? (In other words, instead of panic'ing on an unknown timer, try to call the function from the tcp function block?) I really think it would be good to push the function indirection down a level so that we can avoid calling it for every tcp_timer_{activate, active, stop} function call in the default stack. jtl: Can we get the desired functionality by modifying the stock tcp_timer_{activate, active, stop}… | |||||
Not Done Inline ActionsWow great minds do think alike.. I was just mulling over this and going to add that That still won't help us in the cancel/callback stuff.. but I will have to think of a rrs: Wow great minds do think alike.. I was just mulling over this and going to add that
very… | |||||
tcp_default_ctloutput, | |||||
NULL, | |||||
NULL, | |||||
NULL, | |||||
NULL, | |||||
NULL, | |||||
NULL, | |||||
NULL, | |||||
0, | |||||
0 | |||||
}; | |||||
struct tcp_funchead t_functions; | |||||
static struct tcp_function_block *tcp_func_set_ptr = &tcp_def_funcblk; | |||||
static struct tcp_function_block * | |||||
find_tcp_functions_locked(struct tcp_function_set *fs) | |||||
{ | |||||
struct tcp_function *f; | |||||
struct tcp_function_block *blk=NULL; | |||||
TAILQ_FOREACH(f, &t_functions, tf_next) { | |||||
if (strcmp(f->tf_fb->tfb_tcp_block_name, fs->function_set_name) == 0) { | |||||
blk = f->tf_fb; | |||||
break; | |||||
} | |||||
} | |||||
return(blk); | |||||
} | |||||
static struct tcp_function_block * | |||||
find_tcp_fb_locked(struct tcp_function_block *blk, struct tcp_function **s) | |||||
{ | |||||
struct tcp_function_block *rblk=NULL; | |||||
struct tcp_function *f; | |||||
TAILQ_FOREACH(f, &t_functions, tf_next) { | |||||
if (f->tf_fb == blk) { | |||||
rblk = blk; | |||||
if (s) { | |||||
*s = f; | |||||
} | |||||
break; | |||||
Not Done Inline ActionsBoth "find" functions take a reference, but only one has "and_ref" in the name. Shouldn't they be consistent? jtl: Both "find" functions take a reference, but only one has "and_ref" in the name. Shouldn't they… | |||||
Not Done Inline ActionsSure let me do that for the next spin :-) rrs: Sure let me do that for the next spin :-) | |||||
} | |||||
} | |||||
return (rblk); | |||||
} | |||||
struct tcp_function_block * | |||||
find_and_ref_tcp_functions(struct tcp_function_set *fs) | |||||
{ | |||||
struct tcp_function_block *blk; | |||||
Not Done Inline ActionsCan't you break at this point? jtl: Can't you break at this point? | |||||
Not Done Inline ActionsOpps yep your right there should be a break there :-) rrs: Opps yep your right there should be a break there :-) | |||||
rw_rlock(&tcp_function_lock); | |||||
blk = find_tcp_functions_locked(fs); | |||||
if (blk) | |||||
refcount_acquire(&blk->tfb_refcnt); | |||||
rw_runlock(&tcp_function_lock); | |||||
return(blk); | |||||
} | |||||
Not Done Inline ActionsHow about adding "default" somewhere in the function name to further clarify the purpose of the function? (e.g. sysctl_net_inet_tcp_function_default) jtl: How about adding "default" somewhere in the function name to further clarify the purpose of the… | |||||
Not Done Inline Actionssure I will include it in the next pass rrs: sure I will include it in the next pass | |||||
struct tcp_function_block * | |||||
find_and_ref_tcp_fb(struct tcp_function_block *blk) | |||||
{ | |||||
struct tcp_function_block *rblk; | |||||
rw_rlock(&tcp_function_lock); | |||||
rblk = find_tcp_fb_locked(blk, NULL); | |||||
if (rblk) | |||||
refcount_acquire(&rblk->tfb_refcnt); | |||||
rw_runlock(&tcp_function_lock); | |||||
Not Done Inline ActionsPerhaps, if you split up find_and_ref_tcp_fb() the way you split up find_tcp_functions() and find_tcp_functions_locked(), you could call common "find" code here. jtl: Perhaps, if you split up find_and_ref_tcp_fb() the way you split up find_tcp_functions() and… | |||||
Not Done Inline Actionssure let me look at doing that :-) rrs: sure let me look at doing that :-) | |||||
return(rblk); | |||||
} | |||||
static int | |||||
sysctl_net_inet_default_tcp_functions(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error=ENOENT; | |||||
struct tcp_function_set fs; | |||||
struct tcp_function_block *blk; | |||||
memset(&fs, 0, sizeof(fs)); | |||||
rw_rlock(&tcp_function_lock); | |||||
blk = find_tcp_fb_locked(tcp_func_set_ptr, NULL); | |||||
Not Done Inline ActionsYou really only need a read lock here, correct? jtl: You really only need a read lock here, correct? | |||||
Not Done Inline Actionsyep good catch :-) rrs: yep good catch :-) | |||||
if (blk) { | |||||
/* Found him */ | |||||
strcpy(fs.function_set_name, blk->tfb_tcp_block_name); | |||||
fs.pcbcnt = blk->tfb_refcnt; | |||||
} | |||||
rw_runlock(&tcp_function_lock); | |||||
error = sysctl_handle_string(oidp, fs.function_set_name, | |||||
sizeof(fs.function_set_name), req); | |||||
/* Check for error or no change */ | |||||
if (error != 0 || req->newptr == NULL) | |||||
return(error); | |||||
rw_wlock(&tcp_function_lock); | |||||
blk = find_tcp_functions_locked(&fs); | |||||
if ((blk == NULL) || | |||||
Not Done Inline ActionsI think we need a check for TCP_FUNC_BEING_REMOVED here. I thought it was there in a previous rev. Maybe I'm wrong. jtl: I think we need a check for TCP_FUNC_BEING_REMOVED here. I thought it was there in a previous… | |||||
Not Done Inline ActionsProbably a good idea.. rrs: Probably a good idea.. | |||||
(blk->tfb_flags & TCP_FUNC_BEING_REMOVED)) { | |||||
error = ENOENT; | |||||
Not Done Inline ActionsHow about "default_function" instead of "setfunctions"? jtl: How about "default_function" instead of "setfunctions"? | |||||
Not Done Inline Actionsok sounds good. rrs: ok sounds good. | |||||
goto done; | |||||
} | |||||
tcp_func_set_ptr = blk; | |||||
done: | |||||
rw_wunlock(&tcp_function_lock); | |||||
return (error); | |||||
} | |||||
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, functions_default, | |||||
CTLTYPE_STRING | CTLFLAG_RW, | |||||
NULL, 0, sysctl_net_inet_default_tcp_functions, "A", | |||||
"Set/get the default TCP functions"); | |||||
static int | |||||
sysctl_net_inet_list_available(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error, cnt, linesz; | |||||
struct tcp_function *f; | |||||
char *buffer, *cp; | |||||
size_t bufsz, outsz; | |||||
cnt = 0; | |||||
rw_rlock(&tcp_function_lock); | |||||
TAILQ_FOREACH(f, &t_functions, tf_next) { | |||||
cnt++; | |||||
} | |||||
rw_runlock(&tcp_function_lock); | |||||
bufsz = (cnt+2) * (TCP_FUNCTION_NAME_LEN_MAX + 12) + 1; | |||||
buffer = malloc(bufsz, M_TEMP, M_WAITOK); | |||||
error = 0; | |||||
cp = buffer; | |||||
linesz = snprintf(cp, bufsz, "\n%-32s%c %s\n", "Stack", 'D', "PCB count"); | |||||
Not Done Inline ActionsThis should be 'if (at >= n)'. Otherwise, the code will be allowed to write one record past its allocation. jtl: This should be 'if (at >= n)'. Otherwise, the code will be allowed to write one record past its… | |||||
Not Done Inline ActionsGreat catch thanks! rrs: Great catch thanks! | |||||
cp += linesz; | |||||
bufsz -= linesz; | |||||
outsz = linesz; | |||||
Not Done Inline ActionsShould this return (at * sizeof(struct tcp_function_set)) instead of sz? jtl: Should this return (at * sizeof(struct tcp_function_set)) instead of sz? | |||||
Not Done Inline ActionsYeah it should since in theory the size could shrink .. good catch. rrs: Yeah it should since in theory the size could shrink .. good catch. | |||||
rw_rlock(&tcp_function_lock); | |||||
TAILQ_FOREACH(f, &t_functions, tf_next) { | |||||
linesz = snprintf(cp, bufsz, "%-32s%c %u\n", | |||||
f->tf_fb->tfb_tcp_block_name, | |||||
(f->tf_fb == tcp_func_set_ptr) ? '*' : ' ', | |||||
f->tf_fb->tfb_refcnt); | |||||
Not Done Inline ActionsShould probably be CTLFLAG_RD. jtl: Should probably be CTLFLAG_RD. | |||||
Not Done Inline Actionsopps yeah you are right! rrs: opps yeah you are right! | |||||
Not Done Inline ActionsYou should be able to increment at by just using strlen(f->fb->tcp_block_name). Or, you can use strlcpy(), which gives you size for free. Alternatively, you could print this out line-by-line with SYSCTL_OUT(). You could simply allocate an 80-character buffer and fill it with data for each line. You could even print the stats that way. jtl: You should be able to increment at by just using strlen(f->fb->tcp_block_name). Or, you can use… | |||||
Not Done Inline ActionsThere are always lots of alternatives.. I will take your strlen(f->) suggestion :-) rrs: There are always lots of alternatives.. I will take your strlen(f->) suggestion :-) | |||||
if (linesz >= bufsz) { | |||||
error = EOVERFLOW; | |||||
break; | |||||
} | |||||
cp += linesz; | |||||
bufsz -= linesz; | |||||
outsz += linesz; | |||||
} | |||||
rw_runlock(&tcp_function_lock); | |||||
if (error == 0) | |||||
error = sysctl_handle_string(oidp, buffer, outsz + 1, req); | |||||
Not Done Inline ActionsWhen will buffer ever be NULL? jtl: When will buffer ever be NULL? | |||||
Not Done Inline Actionsyour right .. TSNH :-) rrs: your right .. TSNH :-) | |||||
free(buffer, M_TEMP); | |||||
return (error); | |||||
} | |||||
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, functions_available, | |||||
CTLTYPE_STRING|CTLFLAG_RD, | |||||
NULL, 0, sysctl_net_inet_list_available, "A", | |||||
"list available TCP Function sets"); | |||||
/* | /* | ||||
* Target size of TCP PCB hash tables. Must be a power of two. | * Target size of TCP PCB hash tables. Must be a power of two. | ||||
* | * | ||||
Not Done Inline ActionsWhy n+2? jtl: Why n+2? | |||||
Not Done Inline ActionsBecause in theory it can grow after the return, so I always give a couple of more. rrs: Because in theory it can grow after the return, so I always give a couple of more.
Its cheap… | |||||
Context not available. | |||||
#define V_tcpcb_zone VNET(tcpcb_zone) | #define V_tcpcb_zone VNET(tcpcb_zone) | ||||
MALLOC_DEFINE(M_TCPLOG, "tcplog", "TCP address and flags print buffers"); | MALLOC_DEFINE(M_TCPLOG, "tcplog", "TCP address and flags print buffers"); | ||||
MALLOC_DEFINE(M_TCPFUNCTIONS, "tcpfunc", "TCP function set memory"); | |||||
static struct mtx isn_mtx; | static struct mtx isn_mtx; | ||||
#define ISN_LOCK_INIT() mtx_init(&isn_mtx, "isn_mtx", NULL, MTX_DEF) | #define ISN_LOCK_INIT() mtx_init(&isn_mtx, "isn_mtx", NULL, MTX_DEF) | ||||
Context not available. | |||||
return (hashsize); | return (hashsize); | ||||
} | } | ||||
int | |||||
register_tcp_functions(struct tcp_function_block *blk, int wait) | |||||
{ | |||||
struct tcp_function_block *lblk; | |||||
struct tcp_function *n; | |||||
struct tcp_function_set fs; | |||||
if ((blk->tfb_tcp_output == NULL) || | |||||
(blk->tfb_tcp_do_segment == NULL) || | |||||
Not Done Inline ActionsReuse find_tcp_functions_locked()? You would just need to change find_tcp_functions_locked() to take a string instead of a tcp_function_set as an argument. jtl: Reuse find_tcp_functions_locked()? You would just need to change find_tcp_functions_locked() to… | |||||
Not Done Inline ActionsGood plan :-) rrs: Good plan :-) | |||||
(blk->tfb_tcp_ctloutput == NULL) || | |||||
(strlen(blk->tfb_tcp_block_name) == 0)) { | |||||
/* | |||||
* These functions are required and you | |||||
Not Done Inline ActionsNeed to free 'n' before returning. jtl: Need to free 'n' before returning. | |||||
Not Done Inline ActionsOpps good catch. rrs: Opps good catch. | |||||
* need a name. | |||||
*/ | |||||
return (EINVAL); | |||||
} | |||||
if (blk->tfb_tcp_timer_stop_all || | |||||
blk->tfb_tcp_timers_left || | |||||
blk->tfb_tcp_timer_activate || | |||||
blk->tfb_tcp_timer_active || | |||||
blk->tfb_tcp_timer_stop) { | |||||
/* | |||||
* If you define one timer function you | |||||
* must have them all. | |||||
*/ | |||||
if ((blk->tfb_tcp_timer_stop_all == NULL) || | |||||
(blk->tfb_tcp_timers_left == NULL) || | |||||
(blk->tfb_tcp_timer_activate == NULL) || | |||||
(blk->tfb_tcp_timer_active == NULL) || | |||||
(blk->tfb_tcp_timer_stop == NULL)) { | |||||
return (EINVAL); | |||||
} | |||||
} | |||||
n = malloc(sizeof(struct tcp_function), M_TCPFUNCTIONS, wait); | |||||
if (n == NULL) { | |||||
Not Done Inline ActionsI agree with this. But, an opportunity for future enhancement is to mark a function list as "deleted" (so that no new TCPCBs can use it), while still maintaining it in the list until its refcount drops to 0. However, I think this needs to go within the write-locked block. Otherwise, there is a potential race condition where a new TCPCB takes a reference on this block before you've removed it from the list. jtl: I agree with this. But, an opportunity for future enhancement is to mark a function list as… | |||||
Not Done Inline ActionsYeah the rw lock needs to be before the return/ebusy.. I will have to think about the dropping ref-count thing.. Though likely you will be rrs: Yeah the rw lock needs to be before the return/ebusy..
I will have to think about the dropping… | |||||
return (ENOMEM); | |||||
} | |||||
n->tf_fb = blk; | |||||
strcpy(fs.function_set_name, blk->tfb_tcp_block_name); | |||||
rw_wlock(&tcp_function_lock); | |||||
Not Done Inline ActionsAnother opportunity to use find_and_ref_tcp_fb(), if you split it up? jtl: Another opportunity to use find_and_ref_tcp_fb(), if you split it up? | |||||
Not Done Inline ActionsTrue.. I did this quickly on Saturday so I did miss some of the consolidation :-) rrs: True.. I did this quickly on Saturday so I did miss some of the consolidation :-) | |||||
lblk = find_tcp_functions_locked(&fs); | |||||
if (lblk) { | |||||
/* Duplicate name space not allowed */ | |||||
rw_wunlock(&tcp_function_lock); | |||||
free(n, M_TCPFUNCTIONS); | |||||
return (EALREADY); | |||||
} | |||||
refcount_init(&blk->tfb_refcnt, 0); | |||||
blk->tfb_flags = 0; | |||||
TAILQ_INSERT_TAIL(&t_functions, n, tf_next); | |||||
rw_wunlock(&tcp_function_lock); | |||||
return(0); | |||||
} | |||||
int | |||||
deregister_tcp_functions(struct tcp_function_block *blk) | |||||
{ | |||||
struct tcp_function_block *lblk; | |||||
struct tcp_function *f; | |||||
int error=ENOENT; | |||||
if (strcmp(blk->tfb_tcp_block_name, "default") == 0) { | |||||
/* You can't un-register the default */ | |||||
Not Done Inline Actions!= NULL? jtl: != NULL? | |||||
Not Done Inline ActionsI don't see the need to do != NULL, why? Is not if (blk->tcp_timer_stop_all) the same thing has if (blk->tcp_timer_stop_all != NULL) And I have seen both methods used throughout the kernel. rrs: I don't see the need to do != NULL, why?
Is not
if (blk->tcp_timer_stop_all)
the same thing… | |||||
Not Done Inline Actions
style(9) says to use != NULL. Someone called me on it (post-commit) recently. I suspect you'll get the same, especially as you compare with == NULL just lines later. I'm trying to save you the post-commit style comments. :-) jtl: > I don't see the need to do != NULL, why?
style(9) says to use != NULL. Someone called me on… | |||||
return (EPERM); | |||||
} | |||||
rw_wlock(&tcp_function_lock); | |||||
if (blk == tcp_func_set_ptr) { | |||||
/* You can't free the current default */ | |||||
rw_wunlock(&tcp_function_lock); | |||||
return (EBUSY); | |||||
} | |||||
if (blk->tfb_refcnt) { | |||||
/* Still tcb attached, mark it. */ | |||||
blk->tfb_flags |= TCP_FUNC_BEING_REMOVED; | |||||
rw_wunlock(&tcp_function_lock); | |||||
return (EBUSY); | |||||
} | |||||
lblk = find_tcp_fb_locked(blk, &f); | |||||
if (lblk) { | |||||
/* Found */ | |||||
TAILQ_REMOVE(&t_functions, f, tf_next); | |||||
f->tf_fb = NULL; | |||||
free(f, M_TCPFUNCTIONS); | |||||
error = 0; | |||||
} | |||||
rw_wunlock(&tcp_function_lock); | |||||
Not Done Inline ActionsCan this be done before the lock? jtl: Can this be done before the lock? | |||||
Not Done Inline Actionssure rrs: sure | |||||
return (error); | |||||
} | |||||
void | void | ||||
tcp_init(void) | tcp_init(void) | ||||
{ | { | ||||
Context not available. | |||||
if (hhook_head_register(HHOOK_TYPE_TCP, HHOOK_TCP_EST_OUT, | if (hhook_head_register(HHOOK_TYPE_TCP, HHOOK_TCP_EST_OUT, | ||||
&V_tcp_hhh[HHOOK_TCP_EST_OUT], HHOOK_NOWAIT|HHOOK_HEADISINVNET) != 0) | &V_tcp_hhh[HHOOK_TCP_EST_OUT], HHOOK_NOWAIT|HHOOK_HEADISINVNET) != 0) | ||||
printf("%s: WARNING: unable to register helper hook\n", __func__); | printf("%s: WARNING: unable to register helper hook\n", __func__); | ||||
/* Setup the tcp function block list */ | |||||
TAILQ_INIT(&t_functions); | |||||
rw_init_flags(&tcp_function_lock, "tcp_func_lock" , 0); | |||||
register_tcp_functions(&tcp_def_funcblk, M_WAITOK); | |||||
hashsize = TCBHASHSIZE; | hashsize = TCBHASHSIZE; | ||||
TUNABLE_INT_FETCH(tcbhash_tuneable, &hashsize); | TUNABLE_INT_FETCH(tcbhash_tuneable, &hashsize); | ||||
if (hashsize == 0) { | if (hashsize == 0) { | ||||
Not Done Inline ActionsGiven the accumulation of "cleanup" code on error, should we move this to a "goto error" block? jtl: Given the accumulation of "cleanup" code on error, should we move this to a "goto error" block? | |||||
Not Done Inline Actionspossibly .. I was in particular trying not to re-arrange existing code too much but rrs: possibly .. I was in particular trying not to re-arrange existing code too much but
it might be… | |||||
Not Done Inline ActionsIt looks like you acquire a read lock to read this elsewhere. This should probably be consistent. (In this case, you could just acquire the write lock a few lines earlier.) jtl: It looks like you acquire a read lock to read this elsewhere. This should probably be… | |||||
Not Done Inline ActionsI will move back the lock, since we are going to wlock anyway. rrs: I will move back the lock, since we are going to wlock anyway. | |||||
Not Done Inline ActionsTo accomodate future flags, this should probably be |=. jtl: To accomodate future flags, this should probably be |=. | |||||
Not Done Inline Actionsok rrs: ok | |||||
Context not available. | |||||
tp->ccv = &tm->ccv; | tp->ccv = &tm->ccv; | ||||
tp->ccv->type = IPPROTO_TCP; | tp->ccv->type = IPPROTO_TCP; | ||||
tp->ccv->ccvc.tcp = tp; | tp->ccv->ccvc.tcp = tp; | ||||
rw_rlock(&tcp_function_lock); | |||||
tp->t_fb = tcp_func_set_ptr; | |||||
refcount_acquire(&tp->t_fb->tfb_refcnt); | |||||
rw_runlock(&tcp_function_lock); | |||||
if (tp->t_fb->tfb_tcp_fb_init) { | |||||
(*tp->t_fb->tfb_tcp_fb_init)(tp); | |||||
} | |||||
/* | /* | ||||
* Use the current system default CC algorithm. | * Use the current system default CC algorithm. | ||||
*/ | */ | ||||
Context not available. | |||||
if (CC_ALGO(tp)->cb_init != NULL) | if (CC_ALGO(tp)->cb_init != NULL) | ||||
if (CC_ALGO(tp)->cb_init(tp->ccv) > 0) { | if (CC_ALGO(tp)->cb_init(tp->ccv) > 0) { | ||||
if (tp->t_fb->tfb_tcp_fb_fini) | |||||
(*tp->t_fb->tfb_tcp_fb_fini)(tp); | |||||
refcount_release(&tp->t_fb->tfb_refcnt); | |||||
uma_zfree(V_tcpcb_zone, tm); | uma_zfree(V_tcpcb_zone, tm); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
Context not available. | |||||
tp->osd = &tm->osd; | tp->osd = &tm->osd; | ||||
if (khelp_init_osd(HELPER_CLASS_TCP, tp->osd)) { | if (khelp_init_osd(HELPER_CLASS_TCP, tp->osd)) { | ||||
if (tp->t_fb->tfb_tcp_fb_fini) | |||||
(*tp->t_fb->tfb_tcp_fb_fini)(tp); | |||||
refcount_release(&tp->t_fb->tfb_refcnt); | |||||
uma_zfree(V_tcpcb_zone, tm); | uma_zfree(V_tcpcb_zone, tm); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
Not Done Inline ActionsIs there a way to avoid acquiring a lock here? (If so, I'm not seeing one off the top of my head.) jtl: Is there a way to avoid acquiring a lock here? (If so, I'm not seeing one off the top of my… | |||||
Not Done Inline ActionsI don't see that you can and assure that you get the rrs: I don't see that you can and assure that you get the
right pointers ref-count updated correctly! | |||||
Context not available. | |||||
if (TCPS_HAVERCVDSYN(tp->t_state)) { | if (TCPS_HAVERCVDSYN(tp->t_state)) { | ||||
tcp_state_change(tp, TCPS_CLOSED); | tcp_state_change(tp, TCPS_CLOSED); | ||||
(void) tcp_output(tp); | (void) tp->t_fb->tfb_tcp_output(tp); | ||||
TCPSTAT_INC(tcps_drops); | TCPSTAT_INC(tcps_drops); | ||||
} else | } else | ||||
TCPSTAT_INC(tcps_conndrops); | TCPSTAT_INC(tcps_conndrops); | ||||
Context not available. | |||||
tcp_timer_stop(tp, TT_KEEP); | tcp_timer_stop(tp, TT_KEEP); | ||||
tcp_timer_stop(tp, TT_2MSL); | tcp_timer_stop(tp, TT_2MSL); | ||||
tcp_timer_stop(tp, TT_DELACK); | tcp_timer_stop(tp, TT_DELACK); | ||||
if (tp->t_fb->tfb_tcp_timer_stop_all) { | |||||
/* Call the stop-all function of the methods */ | |||||
tp->t_fb->tfb_tcp_timer_stop_all(tp); | |||||
} | |||||
/* | /* | ||||
* If we got enough samples through the srtt filter, | * If we got enough samples through the srtt filter, | ||||
Context not available. | |||||
inp->inp_ppcb = NULL; | inp->inp_ppcb = NULL; | ||||
if ((tp->t_timers->tt_flags & TT_MASK) == 0) { | if ((tp->t_timers->tt_flags & TT_MASK) == 0) { | ||||
/* We own the last reference on tcpcb, let's free it. */ | /* We own the last reference on tcpcb, let's free it. */ | ||||
if ((tp->t_fb->tfb_tcp_timers_left) && | |||||
(tp->t_fb->tfb_tcp_timers_left(tp))) { | |||||
/* Some fb timers left running! */ | |||||
return; | |||||
} | |||||
if (tp->t_fb->tfb_tcp_fb_fini) | |||||
(*tp->t_fb->tfb_tcp_fb_fini)(tp); | |||||
refcount_release(&tp->t_fb->tfb_refcnt); | |||||
tp->t_inpcb = NULL; | tp->t_inpcb = NULL; | ||||
uma_zfree(V_tcpcb_zone, tp); | uma_zfree(V_tcpcb_zone, tp); | ||||
released = in_pcbrele_wlocked(inp); | released = in_pcbrele_wlocked(inp); | ||||
Context not available. | |||||
tp->t_timers->tt_flags &= ~timer_type; | tp->t_timers->tt_flags &= ~timer_type; | ||||
if ((tp->t_timers->tt_flags & TT_MASK) == 0) { | if ((tp->t_timers->tt_flags & TT_MASK) == 0) { | ||||
/* We own the last reference on this tcpcb, let's free it. */ | /* We own the last reference on this tcpcb, let's free it. */ | ||||
if ((tp->t_fb->tfb_tcp_timers_left) && | |||||
(tp->t_fb->tfb_tcp_timers_left(tp))) { | |||||
/* Some fb timers left running! */ | |||||
goto leave; | |||||
} | |||||
if (tp->t_fb->tfb_tcp_fb_fini) | |||||
(*tp->t_fb->tfb_tcp_fb_fini)(tp); | |||||
refcount_release(&tp->t_fb->tfb_refcnt); | |||||
tp->t_inpcb = NULL; | tp->t_inpcb = NULL; | ||||
uma_zfree(V_tcpcb_zone, tp); | uma_zfree(V_tcpcb_zone, tp); | ||||
if (in_pcbrele_wlocked(inp)) { | if (in_pcbrele_wlocked(inp)) { | ||||
Context not available. | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
leave: | |||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
INP_INFO_RUNLOCK(&V_tcbinfo); | INP_INFO_RUNLOCK(&V_tcbinfo); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
Context not available. | |||||
tp->snd_recover = tp->snd_max; | tp->snd_recover = tp->snd_max; | ||||
if (tp->t_flags & TF_SACK_PERMIT) | if (tp->t_flags & TF_SACK_PERMIT) | ||||
EXIT_FASTRECOVERY(tp->t_flags); | EXIT_FASTRECOVERY(tp->t_flags); | ||||
tcp_output(tp); | tp->t_fb->tfb_tcp_output(tp); | ||||
} | } | ||||
#ifdef INET | #ifdef INET | ||||
Context not available. |
It looks like there is an extra line here.