Changeset View
Standalone View
sys/netinet/tcp_usrreq.c
Context not available. | |||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/refcount.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
Context not available. | |||||
goto out; | goto out; | ||||
#endif | #endif | ||||
tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); | tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
out: | out: | ||||
TCPDEBUG2(PRU_CONNECT); | TCPDEBUG2(PRU_CONNECT); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
Context not available. | |||||
(error = tcp_offload_connect(so, nam)) == 0) | (error = tcp_offload_connect(so, nam)) == 0) | ||||
goto out; | goto out; | ||||
#endif | #endif | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
goto out; | goto out; | ||||
} | } | ||||
#endif | #endif | ||||
Context not available. | |||||
goto out; | goto out; | ||||
#endif | #endif | ||||
tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); | tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
out: | out: | ||||
TCPDEBUG2(PRU_CONNECT); | TCPDEBUG2(PRU_CONNECT); | ||||
Context not available. | |||||
socantsendmore(so); | socantsendmore(so); | ||||
tcp_usrclosed(tp); | tcp_usrclosed(tp); | ||||
if (!(inp->inp_flags & INP_DROPPED)) | if (!(inp->inp_flags & INP_DROPPED)) | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
out: | out: | ||||
TCPDEBUG2(PRU_SHUTDOWN); | TCPDEBUG2(PRU_SHUTDOWN); | ||||
Context not available. | |||||
tcp_offload_rcvd(tp); | tcp_offload_rcvd(tp); | ||||
else | else | ||||
#endif | #endif | ||||
tcp_output(tp); | tp->t_fb->tcp_output(tp); | ||||
out: | out: | ||||
TCPDEBUG2(PRU_RCVD); | TCPDEBUG2(PRU_RCVD); | ||||
Context not available. | |||||
!(flags & PRUS_NOTREADY)) { | !(flags & PRUS_NOTREADY)) { | ||||
if (flags & PRUS_MORETOCOME) | if (flags & PRUS_MORETOCOME) | ||||
tp->t_flags |= TF_MORETOCOME; | tp->t_flags |= TF_MORETOCOME; | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
if (flags & PRUS_MORETOCOME) | if (flags & PRUS_MORETOCOME) | ||||
tp->t_flags &= ~TF_MORETOCOME; | tp->t_flags &= ~TF_MORETOCOME; | ||||
} | } | ||||
Context not available. | |||||
tp->snd_up = tp->snd_una + sbavail(&so->so_snd); | tp->snd_up = tp->snd_una + sbavail(&so->so_snd); | ||||
if (!(flags & PRUS_NOTREADY)) { | if (!(flags & PRUS_NOTREADY)) { | ||||
tp->t_flags |= TF_FORCEDATA; | tp->t_flags |= TF_FORCEDATA; | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
tp->t_flags &= ~TF_FORCEDATA; | tp->t_flags &= ~TF_FORCEDATA; | ||||
} | } | ||||
} | } | ||||
Context not available. | |||||
error = sbready(&so->so_snd, m, count); | error = sbready(&so->so_snd, m, count); | ||||
SOCKBUF_UNLOCK(&so->so_snd); | SOCKBUF_UNLOCK(&so->so_snd); | ||||
if (error == 0) | if (error == 0) | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (error); | return (error); | ||||
Context not available. | |||||
int | int | ||||
tcp_ctloutput(struct socket *so, struct sockopt *sopt) | tcp_ctloutput(struct socket *so, struct sockopt *sopt) | ||||
{ | { | ||||
int error, opt, optval; | int error; | ||||
u_int ui; | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
struct tcp_info ti; | |||||
char buf[TCP_CA_NAME_MAX]; | |||||
struct cc_algo *algo; | |||||
error = 0; | error = 0; | ||||
inp = sotoinpcb(so); | inp = sotoinpcb(so); | ||||
Context not available. | |||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (ECONNRESET); | return (ECONNRESET); | ||||
} | } | ||||
tp = intotcpcb(inp); | |||||
if (tp->t_fb->tcp_ctloutput) { | |||||
jtl: When a function block is added, don't you check to ensure this is non-NULL? | |||||
rrsAuthorUnsubmitted Not Done Inline Actionstrue rrs: true | |||||
/* Pass in the INP locked, called must unlock it */ | |||||
return (tp->t_fb->tcp_ctloutput(so, sopt, inp, tp)); | |||||
Not Done Inline ActionsBecause tcp_ctloutput() is the function calling INP_WLOCK, would it be safer/better/more expected to have it also unlock the INP after the stack-specific function returns? (Given the amount of locking/unlocking that occurs in the _ctloutput functions, this may be pretty irrelevant. Either way, the expectation will need to be documented.) jtl: Because tcp_ctloutput() is the function calling INP_WLOCK, would it be safer/better/more… | |||||
Not Done Inline ActionsNo I don't think so. I think you *want* to be able to sub-call the rrs: No I don't think so. I think you *want* to be able to sub-call the
generic one if your new… | |||||
} else { | |||||
INP_WUNLOCK(inp); | |||||
return(ENOENT); | |||||
} | |||||
} | |||||
int | |||||
tcp_default_ctloutput(struct socket *so, struct sockopt *sopt, struct inpcb *inp, struct tcpcb *tp) | |||||
{ | |||||
int error, opt, optval; | |||||
u_int ui; | |||||
struct tcp_info ti; | |||||
struct tcp_function_set fsn; | |||||
struct tcp_function_block *blk; | |||||
char buf[TCP_CA_NAME_MAX]; | |||||
struct cc_algo *algo; | |||||
switch (sopt->sopt_dir) { | switch (sopt->sopt_dir) { | ||||
case SOPT_SET: | case SOPT_SET: | ||||
switch (sopt->sopt_name) { | switch (sopt->sopt_name) { | ||||
Context not available. | |||||
else if (tp->t_flags & TF_NOPUSH) { | else if (tp->t_flags & TF_NOPUSH) { | ||||
tp->t_flags &= ~TF_NOPUSH; | tp->t_flags &= ~TF_NOPUSH; | ||||
if (TCPS_HAVEESTABLISHED(tp->t_state)) | if (TCPS_HAVEESTABLISHED(tp->t_state)) | ||||
error = tcp_output(tp); | error = tp->t_fb->tcp_output(tp); | ||||
} | } | ||||
goto unlock_and_done; | goto unlock_and_done; | ||||
Context not available. | |||||
goto unlock_and_done; | goto unlock_and_done; | ||||
#endif | #endif | ||||
case TCP_FUNCTION_BLK: | |||||
Not Done Inline ActionsBecause this is truly global code (every stack *must* have it), should this be moved to the main tcp_ctloutput() function? jtl: Because this is truly global code (every stack *must* have it), should this be moved to the… | |||||
Not Done Inline ActionsWe need to discuss this .. I don't see that it is *not* in the generic code.. i.e. you decided You make a function that looks for FOO and then if its not rrs: We need to discuss this .. I don't see that it is *not* in the generic code.. i.e. you decided… | |||||
jtlUnsubmitted Not Done Inline ActionsI still think it might be worth putting this into the main ctloutput function, so that an alternate TCP stack can't override it, even accidentally (by using an overlapping identifier). jtl: I still think it might be worth putting this into the main ctloutput function, so that an… | |||||
rrsAuthorUnsubmitted Not Done Inline ActionsOk, it adds a *lot* of code into a tiny function so why not (even though rrs: Ok, it adds a *lot* of code into a tiny function so why not (even though
I think such a thing… | |||||
INP_WUNLOCK(inp); | |||||
error = sooptcopyin(sopt, &fsn, sizeof fsn, | |||||
sizeof fsn); | |||||
if (error) | |||||
return (error); | |||||
INP_WLOCK_RECHECK(inp); | |||||
if (tp->t_state != TCPS_CLOSED) { | |||||
/* | |||||
* The user has advanced the state | |||||
* past the initial point, we can't | |||||
* switch since we are down the road | |||||
* and a new set of functions may | |||||
* not be compatibile. | |||||
*/ | |||||
INP_WUNLOCK(inp); | |||||
return(EINVAL); | |||||
} | |||||
blk = find_and_ref_tcp_functions(&fsn); | |||||
if (blk == NULL) { | |||||
INP_WUNLOCK(inp); | |||||
return (ENOENT); | |||||
} | |||||
if (tp->t_fb != blk) { | |||||
if (blk->flags & TCP_FUNC_BEING_REMOVED) { | |||||
refcount_release(&blk->refcnt); | |||||
INP_WUNLOCK(inp); | |||||
return (ENOENT); | |||||
} | |||||
Not Done Inline ActionsThis section can probably be enclosed in "if (blk != tp->t_fb)". jtl: This section can probably be enclosed in "if (blk != tp->t_fb)". | |||||
/* | |||||
* Release the old refcnt, the | |||||
* lookup acquires a ref on the | |||||
* new one. | |||||
*/ | |||||
if (tp->t_fb->tcp_fb_fini) | |||||
(*tp->t_fb->tcp_fb_fini)(tp); | |||||
refcount_release(&tp->t_fb->refcnt); | |||||
tp->t_fb = blk; | |||||
if (tp->t_fb->tcp_fb_init) { | |||||
(*tp->t_fb->tcp_fb_init)(tp); | |||||
} | |||||
} | |||||
goto unlock_and_done; | |||||
default: | default: | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
error = ENOPROTOOPT; | error = ENOPROTOOPT; | ||||
Not Done Inline ActionsAgain, should this move into the main tcp_ctloutput() function? jtl: Again, should this move into the main tcp_ctloutput() function? | |||||
Not Done Inline ActionsAgain I think it is already :=) rrs: Again I think it is already :=) | |||||
Context not available. | |||||
error = sooptcopyout(sopt, &optval, sizeof optval); | error = sooptcopyout(sopt, &optval, sizeof optval); | ||||
break; | break; | ||||
#endif | #endif | ||||
case TCP_FUNCTION_BLK: | |||||
strcpy(fsn.function_set_name, tp->t_fb->tcp_block_name); | |||||
fsn.pcbcnt = tp->t_fb->refcnt; | |||||
INP_WUNLOCK(inp); | |||||
error = sooptcopyout(sopt, &fsn, sizeof fsn); | |||||
break; | |||||
default: | default: | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
error = ENOPROTOOPT; | error = ENOPROTOOPT; | ||||
Context not available. | |||||
sbflush(&so->so_rcv); | sbflush(&so->so_rcv); | ||||
tcp_usrclosed(tp); | tcp_usrclosed(tp); | ||||
if (!(inp->inp_flags & INP_DROPPED)) | if (!(inp->inp_flags & INP_DROPPED)) | ||||
tcp_output(tp); | tp->t_fb->tcp_output(tp); | ||||
} | } | ||||
} | } | ||||
Context not available. |
When a function block is added, don't you check to ensure this is non-NULL?