fixed bug #37614 "Errors from ipX_output are not processed". Now tcp_output(_segment) checks for the return value of ipX_output and does not try to send more on error. A netif driver can call tcp_txnow() (from tcpip_thread!) to try to send again if TX buffers are available again.

This commit is contained in:
goldsimon 2015-02-17 08:02:34 +01:00
parent 90db821036
commit 5d13b5a2fb
5 changed files with 113 additions and 42 deletions

View File

@ -186,6 +186,13 @@ HISTORY
++ Bugfixes:
2015-02-17: Simon Goldschmidt
* tcp_impl.h, tcp_out.c, tcp.c, api_msg.c: fixed bug #37614 "Errors from
ipX_output are not processed". Now tcp_output(_segment) checks for the return
value of ipX_output and does not try to send more on error. A netif driver
can call tcp_txnow() (from tcpip_thread!) to try to send again if TX buffers
are available again.
2015-02-14: patches by Freddie Chopin
* snmp*: made community writable, fixed some const pointers

View File

@ -1482,6 +1482,7 @@ err_mem:
}
if (err == ERR_OK) {
err_t out_err;
conn->write_offset += len;
if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
/* return sent length */
@ -1490,18 +1491,34 @@ err_mem:
write_finished = 1;
conn->write_offset = 0;
}
tcp_output(conn->pcb.tcp);
out_err = tcp_output(conn->pcb.tcp);
if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
/* If tcp_output fails with fatal error or no route is found,
don't try writing any more but return the error
to the application thread. */
err = out_err;
write_finished = 1;
conn->current_msg->msg.w.len = 0;
}
} else if ((err == ERR_MEM) && !dontblock) {
/* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
we do NOT return to the application thread, since ERR_MEM is
only a temporary error! */
/* tcp_write returned ERR_MEM, try tcp_output anyway */
tcp_output(conn->pcb.tcp);
err_t out_err = tcp_output(conn->pcb.tcp);
if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
/* If tcp_output fails with fatal error or no route is found,
don't try writing any more but return the error
to the application thread. */
err = out_err;
write_finished = 1;
conn->current_msg->msg.w.len = 0;
} else {
#if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif
}
} else {
/* On errors != ERR_MEM, we don't try writing any more but return
the error to the application thread. */

View File

@ -879,13 +879,17 @@ tcp_slowtmr_start:
if (pcb->persist_backoff > 0) {
/* If snd_wnd is zero, use persist timer to send 1 byte probes
* instead of using the standard retransmission mechanism. */
pcb->persist_cnt++;
if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
pcb->persist_backoff++;
u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
if (pcb->persist_cnt < backoff_cnt) {
pcb->persist_cnt++;
}
if (pcb->persist_cnt >= backoff_cnt) {
if (tcp_zero_window_probe(pcb) == ERR_OK) {
pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
pcb->persist_backoff++;
}
}
tcp_zero_window_probe(pcb);
}
} else {
/* Increase the retransmission timer if it is running */
@ -957,8 +961,10 @@ tcp_slowtmr_start:
(pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
/ TCP_SLOW_INTERVAL)
{
tcp_keepalive(pcb);
pcb->keep_cnt_sent++;
err = tcp_keepalive(pcb);
if (err == ERR_OK) {
pcb->keep_cnt_sent++;
}
}
}
@ -1130,6 +1136,19 @@ tcp_fasttmr_start:
}
}
/** Call tcp_output for all active pcbs that have TF_NAGLEMEMERR set */
void
tcp_txnow(void)
{
struct tcp_pcb *pcb;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->flags & TF_NAGLEMEMERR) {
tcp_output(pcb);
}
}
}
/** Pass pcb->refused_data to the recv callback */
err_t
tcp_process_refused_data(struct tcp_pcb *pcb)

View File

@ -94,7 +94,7 @@
#endif
/* Forward declarations.*/
static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
/** Allocate a pbuf and create a tcphdr at p->payload, used for output
* functions other than the default tcp_output -> tcp_output_segment
@ -895,6 +895,7 @@ tcp_build_wnd_scale_option(u32_t *opts)
err_t
tcp_send_empty_ack(struct tcp_pcb *pcb)
{
err_t err;
struct pbuf *p;
u8_t optlen = 0;
#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
@ -909,6 +910,8 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
if (p == NULL) {
/* let tcp_fasttmr retry sending this ACK */
pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
return ERR_BUF;
}
@ -917,8 +920,6 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
/* remove ACK flags from the PCB, as we send an empty ACK now */
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
/* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
#if LWIP_TCP_TIMESTAMPS
@ -934,15 +935,23 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
&pcb->local_ip, &pcb->remote_ip);
#endif
#if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
err = ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
err = ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free(p);
return ERR_OK;
if (err != ERR_OK) {
/* let tcp_fasttmr retry sending this ACK */
pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
} else {
/* remove ACK flags from the PCB, as we sent an empty ACK now */
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
return err;
}
/**
@ -957,6 +966,7 @@ tcp_output(struct tcp_pcb *pcb)
{
struct tcp_seg *seg, *useg;
u32_t wnd, snd_nxt;
err_t err;
#if TCP_CWND_DEBUG
s16_t i = 0;
#endif /* TCP_CWND_DEBUG */
@ -1041,17 +1051,23 @@ tcp_output(struct tcp_pcb *pcb)
++i;
#endif /* TCP_CWND_DEBUG */
pcb->unsent = seg->next;
if (pcb->state != SYN_SENT) {
TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
#if TCP_OVERSIZE_DBGCHECK
seg->oversize_left = 0;
#endif /* TCP_OVERSIZE_DBGCHECK */
tcp_output_segment(seg, pcb);
err = tcp_output_segment(seg, pcb);
if (err != ERR_OK) {
/* segment could not be sent, for whatever reason */
pcb->flags |= TF_NAGLEMEMERR;
return err;
}
pcb->unsent = seg->next;
if (pcb->state != SYN_SENT) {
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
pcb->snd_nxt = snd_nxt;
@ -1106,9 +1122,10 @@ tcp_output(struct tcp_pcb *pcb)
* @param seg the tcp_seg to send
* @param pcb the tcp_pcb for the TCP connection used to send the segment
*/
static void
static err_t
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
{
err_t err;
u16_t len;
u32_t *opts;
@ -1174,7 +1191,7 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
ipX_addr_t *local_ip;
ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip);
if ((netif == NULL) || (local_ip == NULL)) {
return;
return ERR_RTE;
}
ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip);
}
@ -1237,12 +1254,13 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
TCP_STATS_INC(tcp.xmit);
#if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip,
err = ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip,
pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
err = ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
pcb->tos, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/
return err;
}
/**
@ -1448,9 +1466,10 @@ tcp_rexmit_fast(struct tcp_pcb *pcb)
*
* @param pcb the tcp_pcb for which to send a keepalive packet
*/
void
err_t
tcp_keepalive(struct tcp_pcb *pcb)
{
err_t err;
struct pbuf *p;
#if CHECKSUM_GEN_TCP
struct tcp_hdr *tcphdr;
@ -1465,9 +1484,9 @@ tcp_keepalive(struct tcp_pcb *pcb)
p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
if(p == NULL) {
LWIP_DEBUGF(TCP_DEBUG,
LWIP_DEBUGF(TCP_DEBUG,
("tcp_keepalive: could not allocate memory for pbuf\n"));
return;
return ERR_MEM;
}
#if CHECKSUM_GEN_TCP
tcphdr = (struct tcp_hdr *)p->payload;
@ -1479,17 +1498,18 @@ tcp_keepalive(struct tcp_pcb *pcb)
/* Send output to IP */
#if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip,
err = ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip,
pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
err = ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
0, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free(p);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
pcb->snd_nxt - 1, pcb->rcv_nxt));
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
return err;
}
@ -1501,9 +1521,10 @@ tcp_keepalive(struct tcp_pcb *pcb)
*
* @param pcb the tcp_pcb for which to send a zero-window probe packet
*/
void
err_t
tcp_zero_window_probe(struct tcp_pcb *pcb)
{
err_t err;
struct pbuf *p;
struct tcp_hdr *tcphdr;
struct tcp_seg *seg;
@ -1525,7 +1546,8 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
seg = pcb->unsent;
}
if(seg == NULL) {
return;
/* nothing to send, zero window probe not needed */
return ERR_OK;
}
is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
@ -1535,7 +1557,7 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
if(p == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
return;
return ERR_MEM;
}
tcphdr = (struct tcp_hdr *)p->payload;
@ -1559,16 +1581,17 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
/* Send output to IP */
#if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
err = ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
0, IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
err = ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free(p);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
" ackno %"U32_F".\n",
pcb->snd_nxt - 1, pcb->rcv_nxt));
" ackno %"U32_F" err %d.\n",
pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
return err;
}
#endif /* LWIP_TCP */

View File

@ -61,6 +61,11 @@ void tcp_tmr (void); /* Must be called every
void tcp_slowtmr (void);
void tcp_fasttmr (void);
/* Call this from a netif driver (watch out for threading issues!) that has
returned a memory error on transmit and now has free buffers to send more.
This iterates all active pcbs that had an error and tries to call
tcp_output, so use this with care as it might slow down the system. */
void tcp_txnow (void);
/* Only used by IP to pass a TCP segment to TCP: */
void tcp_input (struct pbuf *p, struct netif *inp);
@ -490,8 +495,8 @@ void tcp_rst_impl(u32_t seqno, u32_t ackno,
u32_t tcp_next_iss(void);
void tcp_keepalive(struct tcp_pcb *pcb);
void tcp_zero_window_probe(struct tcp_pcb *pcb);
err_t tcp_keepalive(struct tcp_pcb *pcb);
err_t tcp_zero_window_probe(struct tcp_pcb *pcb);
#if TCP_CALCULATE_EFF_SEND_MSS
u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest