net: tcp2: Pass data to application without any TCP locks

When receiving data that needs to be passed the data to application,
queue it for short time so that we do not have TCP connection lock
held. This way if the application wants to send data, there is no
possibility that the connection lock would prevent sending data.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2020-10-21 14:26:22 +03:00 committed by Maureen Helm
parent 6003c988e1
commit 735fcc63bf
2 changed files with 41 additions and 3 deletions

View file

@ -293,6 +293,7 @@ static void tcp_send_queue_flush(struct tcp *conn)
static int tcp_conn_unref(struct tcp *conn)
{
int key, ref_count = atomic_get(&conn->ref_count);
struct net_pkt *pkt;
NET_DBG("conn: %p, ref_count=%d", conn, ref_count);
@ -314,6 +315,13 @@ static int tcp_conn_unref(struct tcp *conn)
key = irq_lock();
/* If there is any pending data, pass that to application */
while ((pkt = k_fifo_get(&conn->recv_data, K_NO_WAIT)) != NULL) {
net_context_packet_received(
(struct net_conn *)conn->context->conn_handler,
pkt, NULL, NULL, conn->recv_user_data);
}
if (conn->context->conn_handler) {
net_conn_unregister(conn->context->conn_handler);
conn->context->conn_handler = NULL;
@ -597,9 +605,13 @@ static int tcp_data_get(struct tcp *conn, struct net_pkt *pkt)
net_pkt_skip(up, net_pkt_get_len(up) - len);
net_context_packet_received(
(struct net_conn *)conn->context->conn_handler,
up, NULL, NULL, conn->recv_user_data);
/* Do not pass data to application with TCP conn
* locked as there could be an issue when the app tries
* to send the data and the conn is locked. So the recv
* data is placed in fifo which is flushed in tcp_in()
* after unlocking the conn
*/
k_fifo_put(&conn->recv_data, up);
}
}
out:
@ -962,6 +974,7 @@ static struct tcp *tcp_conn_alloc(void)
memset(conn, 0, sizeof(*conn));
k_mutex_init(&conn->lock);
k_fifo_init(&conn->recv_data);
conn->state = TCP_LISTEN;
@ -1188,6 +1201,10 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
struct tcphdr *th = pkt ? th_get(pkt) : NULL;
uint8_t next = 0, fl = 0;
size_t tcp_options_len = th ? (th->th_off - 5) * 4 : 0;
struct net_conn *conn_handler = NULL;
struct net_pkt *recv_pkt;
void *recv_user_data;
struct k_fifo *recv_data_fifo;
size_t len;
int ret;
@ -1463,7 +1480,27 @@ next_state:
goto next_state;
}
/* If the conn->context is not set, then the connection was already
* closed.
*/
if (conn->context) {
conn_handler = (struct net_conn *)conn->context->conn_handler;
}
recv_user_data = conn->recv_user_data;
recv_data_fifo = &conn->recv_data;
k_mutex_unlock(&conn->lock);
/* Pass all the received data stored in recv fifo to the application.
* This is done like this so that we do not have any connection lock
* held.
*/
while (conn_handler && atomic_get(&conn->ref_count) > 0 &&
(recv_pkt = k_fifo_get(recv_data_fifo, K_NO_WAIT)) != NULL) {
net_context_packet_received(conn_handler, recv_pkt, NULL, NULL,
recv_user_data);
}
}
/* Active connection close: send FIN and go to FIN_WAIT_1 state */

View file

@ -191,6 +191,7 @@ struct tcp { /* TCP connection */
};
struct k_mutex lock;
struct k_sem connect_sem; /* semaphore for blocking connect */
struct k_fifo recv_data; /* temp queue before passing data to app */
struct tcp_options recv_options;
struct k_delayed_work send_timer;
struct k_delayed_work send_data_timer;