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 Enable the option can enable LWIP socket multithread and all
function will be thread safe. 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 config LWIP_MAX_SOCKETS
int "Max number of open sockets" int "Max number of open sockets"
range 1 16 range 1 16

View File

@ -1409,6 +1409,9 @@ lwip_netconn_do_send(void *m)
#endif #endif
#if LWIP_UDP #if LWIP_UDP
case NETCONN_UDP: case NETCONN_UDP:
#if ESP_UDP
udp_sync_regitser(msg);
#endif /* ESP_UDP */
#if LWIP_CHECKSUM_ON_COPY #if LWIP_CHECKSUM_ON_COPY
if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) { 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, 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); TCPIP_APIMSG_ACK(msg);
#endif /* ESP_UDP */
} }
#if LWIP_TCP #if LWIP_TCP

View File

@ -415,6 +415,10 @@ again:
extern void send_from_list(); extern void send_from_list();
send_from_list(); send_from_list();
#if ESP_UDP
udp_sync_proc();
#endif
sleeptime = sys_timeouts_sleeptime(); sleeptime = sys_timeouts_sleeptime();
if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) { if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) {
/* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred /* 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 #define ESP_LWIP 1
#ifdef CONFIG_ESP_UDP_SYNC_SEND
#define ESP_UDP 1
#endif
#ifdef CONFIG_LWIP_SOCKET_MULTITHREAD #ifdef CONFIG_LWIP_SOCKET_MULTITHREAD
#define SOCKETS_MT #define SOCKETS_MT
#endif #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__ */ #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. * header, meaning we should not pass target low-level address here.
*/ */
err = esp_aio_sendto(&aio, NULL, 0); err = esp_aio_sendto(&aio, NULL, 0);
#if ESP_UDP
udp_sync_set_ret(err);
#endif
if (err != ERR_OK) { if (err != ERR_OK) {
if (err == ERR_MEM){ if (err == ERR_MEM){
insert_to_list(aio.fd, p); insert_to_list(aio.fd, p);