feat(lwip): Add socket UDP sync function

This commit is contained in:
Dong Heng
2018-07-26 14:28:33 +08:00
parent 8af3aa5ccf
commit cf46ba82be
7 changed files with 277 additions and 0 deletions

View File

@ -27,6 +27,23 @@ config LWIP_SOCKET_MULTITHREAD
Enable the option can enable LWIP socket multithread and all
function will be thread safe.
config ESP_UDP_SYNC_SEND
bool "LWIP socket UDP sync send"
default y
help
Enable the option can enable LWIP socket UDP sync send. CPU cost
should decrease but memory cost increase and it can make UDP
throughput increase a lot.
config ESP_UDP_SYNC_RETRY_MAX
int "LWIP socket UDP sync send retry max count"
range 1 10
default 5
depends on ESP_UDP_SYNC_SEND
help
When UDP sync send count reaches the value, then the packet should
be lost and LWIP core thread wake up the up-level send thread.
config LWIP_MAX_SOCKETS
int "Max number of open sockets"
range 1 16

View File

@ -1409,6 +1409,9 @@ lwip_netconn_do_send(void *m)
#endif
#if LWIP_UDP
case NETCONN_UDP:
#if ESP_UDP
udp_sync_regitser(msg);
#endif /* ESP_UDP */
#if LWIP_CHECKSUM_ON_COPY
if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
@ -1432,7 +1435,11 @@ lwip_netconn_do_send(void *m)
}
}
}
#if ESP_UDP
udp_sync_ack(msg);
#else
TCPIP_APIMSG_ACK(msg);
#endif /* ESP_UDP */
}
#if LWIP_TCP

View File

@ -415,6 +415,10 @@ again:
extern void send_from_list();
send_from_list();
#if ESP_UDP
udp_sync_proc();
#endif
sleeptime = sys_timeouts_sleeptime();
if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) {
/* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred

View File

@ -0,0 +1,175 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
#include <sys/errno.h>
#include "lwip/udp.h"
#include "lwip/priv/api_msg.h"
#include "lwip/priv/tcp_priv.h"
#include "esp_log.h"
#if ESP_UDP
#define UDP_SYNC_MAX MEMP_NUM_NETCONN
#define UDP_SYNC_RETRY_MAX CONFIG_ESP_UDP_SYNC_RETRY_MAX
/*
* All function has no mutex, so they must put into one task(LWIP main task).
*/
#if LWIP_TCPIP_CORE_LOCKING
#define TCPIP_APIMSG_ACK(m) NETCONN_SET_SAFE_ERR((m)->conn, (m)->err)
#else /* LWIP_TCPIP_CORE_LOCKING */
#define TCPIP_APIMSG_ACK(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
#endif /* LWIP_TCPIP_CORE_LOCKING */
typedef struct udp_sync {
struct api_msg *msg;
int ret;
int retry;
} udp_sync_t;
static const char *TAG = "udp_sync";
static size_t s_udp_sync_num;
static udp_sync_t s_udp_sync[UDP_SYNC_MAX];
static bool s_register_locked;
static struct api_msg *s_cur_msg;
/*
* @brief initialize UDP sync module
*/
void udp_sync_init(void)
{
memset(s_udp_sync, 0, sizeof(s_udp_sync));
s_register_locked = false;
s_udp_sync_num = 0;
}
/*
* @brief register a UDP API message(struct api_msg) to module
*/
void udp_sync_regitser(void *in_msg)
{
s_cur_msg = in_msg;
if (s_register_locked == true)
return ;
struct api_msg *msg = (struct api_msg *)in_msg;
int s = msg->conn->socket;
if (s < 0 || s >= UDP_SYNC_MAX) {
ESP_LOGE(TAG, "UDP sync register error, socket is %d", s);
return ;
} else if (s_udp_sync[s].msg) {
ESP_LOGE(TAG, "UDP sync register error, msg is %p", s_udp_sync[s].msg);
return ;
}
s_udp_sync_num++;
s_udp_sync[s].ret = ERR_OK;
s_udp_sync[s].retry = 0;
s_udp_sync[s].msg = msg;
}
/*
* @brief ack the message
*/
void udp_sync_ack(void *in_msg)
{
struct api_msg *msg = (struct api_msg *)in_msg;
int s = msg->conn->socket;
if (s < 0 || s >= UDP_SYNC_MAX) {
ESP_LOGE(TAG, "UDP sync ack error, socket is %d", s);
return ;
} else if (!s_udp_sync[s].msg) {
ESP_LOGE(TAG, "UDP sync ack error, msg is NULL");
return ;
}
/* Only cache when low-level has no buffer to send packet */
if (s_udp_sync[s].ret != ERR_MEM || s_udp_sync[s].retry >= UDP_SYNC_RETRY_MAX) {
s_udp_sync[s].msg = NULL;
s_udp_sync[s].retry = 0;
s_udp_sync[s].ret = ERR_OK;
s_udp_sync_num--;
/* Todo: return real result */
msg->err = ESP_OK;
TCPIP_APIMSG_ACK(msg);
} else {
s_udp_sync[s].retry++;
ESP_LOGD(TAG, "UDP sync ack error, errno %d", s_udp_sync[s].ret);
}
s_cur_msg = NULL;
}
/*
* @brief set the current message send result
*/
void udp_sync_set_ret(int ret)
{
/* Only poll and regitser can set current message */
if (!s_cur_msg) {
/* You may use it to debug */
//ESP_LOGE(TAG, "UDP sync ack error, current message is NULL");
return ;
}
struct api_msg *msg = s_cur_msg;
int s = msg->conn->socket;
if (s < 0 || s >= UDP_SYNC_MAX) {
ESP_LOGE(TAG, "UDP sync ack error, socket is %d", s);
return ;
} else if (!s_udp_sync[s].msg) {
ESP_LOGE(TAG, "UDP sync ack error, msg is NULL");
return ;
}
s_udp_sync[s].ret = ret;
}
/*
* @brief process the sync
*/
void udp_sync_proc(void)
{
if (!s_udp_sync_num)
return ;
s_register_locked = true;
for (int i = 0; i < UDP_SYNC_MAX; i++) {
if (!s_udp_sync[i].msg)
continue;
lwip_netconn_do_send(s_udp_sync[i].msg);
#if 0
//Todo: Add this later
if (s_udp_sync[i].ret != ERR_OK)
break;
#endif
}
s_register_locked = false;
}
#endif /* ESP_UDP */

View File

@ -52,6 +52,10 @@
#define ESP_LWIP 1
#ifdef CONFIG_ESP_UDP_SYNC_SEND
#define ESP_UDP 1
#endif
#ifdef CONFIG_LWIP_SOCKET_MULTITHREAD
#define SOCKETS_MT
#endif
@ -2213,4 +2217,12 @@ void *memp_malloc_ll(size_t type);
* @}
*/
#if ESP_UDP
#if !LWIP_UDP || !LWIP_SOCKET || !ESP_LWIP
#error "LWIP_UDP & LWIP_SOCKET & ESP_LWIP must be enable"
#else
#include "udp_sync.h"
#endif
#endif
#endif /* __LWIP_HDR_LWIPOPTS_H__ */

View File

@ -0,0 +1,59 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _UDP_SYNC_H
#define _UDP_SYNC_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* @brief initialize UDP sync module
*/
void udp_sync_init(void);
/*
* @brief register a UDP API message(struct api_msg) to module
*
* @param in_msg message pointer
*/
void udp_sync_regitser(void *in_msg);
/*
* @brief ack the message
*
* @param in_msg message pointer
*/
void udp_sync_ack(void *in_msg);
/*
* @brief set the current message send result
*
* @param ret current message send result
*/
void udp_sync_set_ret(int ret);
/*
* @brief process the sync
*
* @param ret current message send result
*/
void udp_sync_proc(void);
#ifdef __cplusplus
}
#endif
#endif /* _UDP_SYNC_H */

View File

@ -302,6 +302,9 @@ static int8_t low_level_output(struct netif* netif, struct pbuf* p)
* header, meaning we should not pass target low-level address here.
*/
err = esp_aio_sendto(&aio, NULL, 0);
#if ESP_UDP
udp_sync_set_ret(err);
#endif
if (err != ERR_OK) {
if (err == ERR_MEM){
insert_to_list(aio.fd, p);