Skip to content

Commit 65a3b5a

Browse files
jukkarnashif
authored andcommitted
net: tcp2: Do not close connection if we run out of memory
Usually the out-of-memory situation will clear itself eventually, so if that happens in TCP, then keep the connection running and let the user to decide what to do next. Signed-off-by: Jukka Rissanen <[email protected]>
1 parent 69d9eb6 commit 65a3b5a

File tree

1 file changed

+87
-31
lines changed

1 file changed

+87
-31
lines changed

subsys/net/ip/tcp2.c

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,12 @@ static int tcp_endpoint_set(union tcp_endpoint *ep, struct net_pkt *pkt,
126126
case AF_INET:
127127
if (IS_ENABLED(CONFIG_NET_IPV4)) {
128128
struct net_ipv4_hdr *ip = NET_IPV4_HDR(pkt);
129-
struct tcphdr *th = th_get(pkt);
129+
struct tcphdr *th;
130+
131+
th = th_get(pkt);
132+
if (!th) {
133+
return -ENOBUFS;
134+
}
130135

131136
memset(ep, 0, sizeof(*ep));
132137

@@ -145,7 +150,12 @@ static int tcp_endpoint_set(union tcp_endpoint *ep, struct net_pkt *pkt,
145150
case AF_INET6:
146151
if (IS_ENABLED(CONFIG_NET_IPV6)) {
147152
struct net_ipv6_hdr *ip = NET_IPV6_HDR(pkt);
148-
struct tcphdr *th = th_get(pkt);
153+
struct tcphdr *th;
154+
155+
th = th_get(pkt);
156+
if (!th) {
157+
return -ENOBUFS;
158+
}
149159

150160
memset(ep, 0, sizeof(*ep));
151161

@@ -361,8 +371,12 @@ static void tcp_send_process(struct k_work *work)
361371

362372
if (conn->in_retransmission) {
363373
if (conn->send_retries > 0) {
364-
tcp_send(tcp_pkt_clone(pkt));
365-
conn->send_retries--;
374+
struct net_pkt *clone = tcp_pkt_clone(pkt);
375+
376+
if (clone) {
377+
tcp_send(clone);
378+
conn->send_retries--;
379+
}
366380
} else {
367381
tcp_conn_unref(conn);
368382
conn = NULL;
@@ -376,7 +390,6 @@ static void tcp_send_process(struct k_work *work)
376390
next) : tcp_pkt_clone(pkt);
377391
if (!pkt) {
378392
NET_ERR("net_pkt alloc failure");
379-
tcp_conn_unref(conn);
380393
return;
381394
}
382395

@@ -634,22 +647,22 @@ static int ip_header_add(struct tcp *conn, struct net_pkt *pkt)
634647
return -EINVAL;
635648
}
636649

637-
static void tcp_out_ext(struct tcp *conn, uint8_t flags, struct net_pkt *data,
638-
uint32_t seq)
650+
static int tcp_out_ext(struct tcp *conn, uint8_t flags, struct net_pkt *data,
651+
uint32_t seq)
639652
{
640653
struct net_pkt *pkt;
641-
int ret;
654+
int ret = 0;
642655

643656
pkt = tcp_pkt_alloc(conn, sizeof(struct tcphdr));
644657
if (!pkt) {
658+
ret = -ENOBUFS;
645659
goto out;
646660
}
647661

648662
if (data) {
649663
/* Append the data buffer to the pkt */
650664
net_pkt_append_buffer(pkt, data->buffer);
651665
data->buffer = NULL;
652-
tcp_pkt_unref(data);
653666
}
654667

655668
ret = ip_header_add(conn, pkt);
@@ -673,20 +686,20 @@ static void tcp_out_ext(struct tcp *conn, uint8_t flags, struct net_pkt *data,
673686
NET_DBG("%s", log_strdup(tcp_th(pkt)));
674687

675688
if (tcp_send_cb) {
676-
tcp_send_cb(pkt);
689+
ret = tcp_send_cb(pkt);
677690
goto out;
678691
}
679692

680693
sys_slist_append(&conn->send_queue, &pkt->next);
681694

682695
tcp_send_process((struct k_work *)&conn->send_timer);
683696
out:
684-
return;
697+
return ret;
685698
}
686699

687700
static void tcp_out(struct tcp *conn, uint8_t flags)
688701
{
689-
tcp_out_ext(conn, flags, NULL /* no data */, conn->seq);
702+
(void)tcp_out_ext(conn, flags, NULL /* no data */, conn->seq);
690703
}
691704

692705
static int tcp_pkt_pull(struct net_pkt *pkt, size_t len)
@@ -707,8 +720,8 @@ static int tcp_pkt_pull(struct net_pkt *pkt, size_t len)
707720
return ret;
708721
}
709722

710-
static void tcp_pkt_peek(struct net_pkt *to, struct net_pkt *from, size_t pos,
711-
size_t len)
723+
static int tcp_pkt_peek(struct net_pkt *to, struct net_pkt *from, size_t pos,
724+
size_t len)
712725
{
713726
net_pkt_cursor_init(to);
714727
net_pkt_cursor_init(from);
@@ -718,7 +731,7 @@ static void tcp_pkt_peek(struct net_pkt *to, struct net_pkt *from, size_t pos,
718731
net_pkt_skip(from, pos);
719732
}
720733

721-
net_pkt_copy(to, from, len);
734+
return net_pkt_copy(to, from, len);
722735
}
723736

724737
static bool tcp_window_full(struct tcp *conn)
@@ -766,14 +779,27 @@ static int tcp_send_data(struct tcp *conn)
766779
goto out;
767780
}
768781

769-
tcp_pkt_peek(pkt, conn->send_data, pos, len);
782+
ret = tcp_pkt_peek(pkt, conn->send_data, pos, len);
783+
if (ret < 0) {
784+
tcp_pkt_unref(pkt);
785+
ret = -ENOBUFS;
786+
goto out;
787+
}
788+
789+
ret = tcp_out_ext(conn, PSH | ACK, pkt, conn->seq + conn->unacked_len);
790+
if (ret == 0) {
791+
conn->unacked_len += len;
792+
}
770793

771-
tcp_out_ext(conn, PSH | ACK, pkt, conn->seq + conn->unacked_len);
794+
/* The data we want to send, has been moved to the send queue so we
795+
* can unref the head net_pkt. If there was an error, we need to remove
796+
* the packet anyway.
797+
*/
798+
tcp_pkt_unref(pkt);
772799

773-
conn->unacked_len += len;
774-
out:
775800
conn_send_data_dump(conn);
776801

802+
out:
777803
return ret;
778804
}
779805

@@ -809,6 +835,14 @@ static int tcp_send_queued_data(struct tcp *conn)
809835
subscribe = false;
810836
}
811837

838+
/* If we have out-of-bufs case, then do not start retransmit timer
839+
* yet. The socket layer will catch this and resend data if needed.
840+
*/
841+
if (ret == -ENOBUFS) {
842+
NET_DBG("No bufs, cancelling retransmit timer");
843+
k_delayed_work_cancel(&conn->send_data_timer);
844+
}
845+
812846
if (subscribe) {
813847
conn->send_data_retries = 0;
814848
k_delayed_work_submit(&conn->send_data_timer, K_MSEC(tcp_rto));
@@ -821,6 +855,7 @@ static void tcp_resend_data(struct k_work *work)
821855
{
822856
struct tcp *conn = CONTAINER_OF(work, struct tcp, send_data_timer);
823857
bool conn_unref = false;
858+
int ret;
824859

825860
NET_DBG("send_data_retries=%hu", conn->send_data_retries);
826861

@@ -832,10 +867,14 @@ static void tcp_resend_data(struct k_work *work)
832867

833868
conn->data_mode = TCP_DATA_MODE_RESEND;
834869
conn->unacked_len = 0;
835-
tcp_send_data(conn);
836870

837-
conn->send_data_retries++;
871+
ret = tcp_send_data(conn);
872+
if (ret == 0) {
873+
conn->send_data_retries++;
874+
}
875+
838876
k_delayed_work_submit(&conn->send_data_timer, K_MSEC(tcp_rto));
877+
839878
out:
840879
if (conn_unref) {
841880
tcp_conn_unref(conn);
@@ -1088,6 +1127,7 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
10881127
uint8_t next = 0, fl = th ? th->th_flags : 0;
10891128
size_t tcp_options_len = th ? (th->th_off - 5) * 4 : 0;
10901129
size_t len;
1130+
int ret;
10911131

10921132
k_mutex_lock(&conn->lock, K_FOREVER);
10931133

@@ -1217,10 +1257,8 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
12171257
conn_send_data_dump(conn);
12181258

12191259
if (!k_delayed_work_remaining_get(&conn->send_data_timer)) {
1220-
NET_ERR("conn: %p, Missing a subscription "
1260+
NET_DBG("conn: %p, Missing a subscription "
12211261
"of the send_data queue timer", conn);
1222-
tcp_out(conn, RST);
1223-
conn_state(conn, TCP_CLOSED);
12241262
break;
12251263
}
12261264
conn->send_data_retries = 0;
@@ -1230,7 +1268,8 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
12301268
}
12311269
conn->data_mode = TCP_DATA_MODE_SEND;
12321270

1233-
if (tcp_send_queued_data(conn) < 0) {
1271+
ret = tcp_send_queued_data(conn);
1272+
if (ret < 0 && ret != -ENOBUFS) {
12341273
tcp_out(conn, RST);
12351274
conn_state(conn, TCP_CLOSED);
12361275
break;
@@ -1324,7 +1363,6 @@ int net_tcp_put(struct net_context *context)
13241363
tcp_out_ext(conn, FIN | ACK, NULL,
13251364
conn->seq + conn->unacked_len);
13261365
conn_seq(conn, + 1);
1327-
13281366
conn_state(conn, TCP_FIN_WAIT_1);
13291367

13301368
k_mutex_unlock(&conn->lock);
@@ -1355,18 +1393,22 @@ int net_tcp_update_recv_wnd(struct net_context *context, int32_t delta)
13551393
int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt)
13561394
{
13571395
struct tcp *conn = context->tcp;
1396+
struct net_buf *orig_buf = NULL;
13581397
int ret = 0;
13591398
size_t len;
13601399

13611400
if (!conn || conn->state != TCP_ESTABLISHED) {
1362-
ret = -ENOTCONN;
1363-
goto out;
1401+
return -ENOTCONN;
13641402
}
13651403

13661404
k_mutex_lock(&conn->lock, K_FOREVER);
13671405

13681406
len = net_pkt_get_len(pkt);
13691407

1408+
if (conn->send_data->buffer) {
1409+
orig_buf = net_buf_frag_last(conn->send_data->buffer);
1410+
}
1411+
13701412
net_pkt_append_buffer(conn->send_data, pkt->buffer);
13711413
conn->send_data_total += len;
13721414
NET_DBG("conn: %p Queued %zu bytes (total %zu)", conn, len,
@@ -1375,14 +1417,28 @@ int net_tcp_queue_data(struct net_context *context, struct net_pkt *pkt)
13751417
tcp_pkt_unref(pkt);
13761418

13771419
ret = tcp_send_queued_data(conn);
1378-
if (ret < 0) {
1379-
k_mutex_unlock(&conn->lock);
1420+
if (ret < 0 && ret != -ENOBUFS) {
13801421
tcp_conn_unref(conn);
13811422
goto out;
13821423
}
13831424

1425+
if (ret == -ENOBUFS) {
1426+
/* Restore the original data so that we do not resend the pkt
1427+
* data multiple times.
1428+
*/
1429+
conn->send_data_total -= len;
1430+
1431+
if (orig_buf) {
1432+
pkt->buffer = orig_buf->frags;
1433+
orig_buf->frags = NULL;
1434+
} else {
1435+
pkt->buffer = conn->send_data->buffer;
1436+
conn->send_data->buffer = NULL;
1437+
}
1438+
}
1439+
out:
13841440
k_mutex_unlock(&conn->lock);
1385-
out:
1441+
13861442
return ret;
13871443
}
13881444

0 commit comments

Comments
 (0)