mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/tw8770_fix_socket_memory_leak' into 'master'
lwip: fix socket memory leak issue 1. Add socket memory leak debug counter 2. Fix TCP PCB leak issue Currently ESP32 support maximum 16 TCP PCBs and all TCP PCB are allocated from heap memory. In some scenario, we may have memory leak issue, for example, the application already created 16 TCP PCB, then it close 5 of them, because the TCP state machine, the LWIP core may not free all the 5 TCP PCB immediately, maybe some is in TIME_WAIT status, some is in FIN_WAIT_1 etc. Then the application try to malloc 17th TCP PCB (the application think they just create 12 because they already close 5), memp_malloc() will return true because the heap is not out of memory, but actually we got 17 TCP PCB. When the scenario repeat again and again (in our Audio application, it repeat more than 10000 times), more and more TCP PCB will be created in the system, each TCP PCB require 200B, then memory leak happen (In Audio application, I saw more than 26 TCP PCB are created, and 10*200=2K memory are leaked). See merge request !223
This commit is contained in:
commit
7f2c6a9d80
@ -18,6 +18,8 @@
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/priv/memp_priv.h"
|
||||
#include "lwip/memp.h"
|
||||
|
||||
#define DBG_LWIP_IP_SHOW(info, ip) printf("%s type=%d ip=%x\n", (info), (ip).type, (ip).u_addr.ip4.addr)
|
||||
#define DBG_LWIP_IP_PCB_SHOW(pcb) \
|
||||
@ -127,3 +129,22 @@ void dbg_lwip_udp_rxtx_show(void)
|
||||
printf("TBC\n");
|
||||
}
|
||||
|
||||
#if (ESP_CNT_DEBUG == 1)
|
||||
|
||||
uint32_t g_lwip_mem_cnt[MEMP_MAX][2];
|
||||
extern const struct memp_desc * const memp_pools[MEMP_MAX];
|
||||
|
||||
void dbg_lwip_cnt_show(void)
|
||||
{
|
||||
int i=0;
|
||||
|
||||
printf("-----lwip memory counter-----\n");
|
||||
printf("%6s %8s %8s\n", "index", "alloc", "free");
|
||||
for (i=0; i<MEMP_MAX; i++){
|
||||
printf("%6u %8u %8u\n", i, g_lwip_mem_cnt[i][0], g_lwip_mem_cnt[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1389,59 +1389,58 @@ tcp_kill_timewait(void)
|
||||
}
|
||||
|
||||
#if ESP_LWIP
|
||||
/**
|
||||
* Kills the oldest connection that is in FIN_WAIT_2 state.
|
||||
* Called from tcp_alloc() if no more connections are available.
|
||||
*/
|
||||
static void tcp_kill_finwait2(void)
|
||||
typedef struct {
|
||||
u8_t time_wait;
|
||||
u8_t closing;
|
||||
u8_t fin_wait2;
|
||||
u8_t last_ack;
|
||||
u8_t fin_wait1;
|
||||
u8_t listen;
|
||||
u8_t bound;
|
||||
u8_t total;
|
||||
}tcp_pcb_num_t;
|
||||
|
||||
void tcp_pcb_num_cal(tcp_pcb_num_t *tcp_pcb_num)
|
||||
{
|
||||
struct tcp_pcb *pcb, *inactive;
|
||||
u32_t inactivity;
|
||||
/* Go through the list of FIN_WAIT_2 pcbs and get the oldest pcb. */
|
||||
inactivity = 0;
|
||||
inactive = NULL;
|
||||
struct tcp_pcb_listen *listen;
|
||||
struct tcp_pcb *pcb;
|
||||
|
||||
if (!tcp_pcb_num){
|
||||
return;
|
||||
}
|
||||
|
||||
memset(tcp_pcb_num, 0, sizeof(*tcp_pcb_num));
|
||||
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
tcp_pcb_num->total ++;
|
||||
tcp_pcb_num->time_wait ++;
|
||||
}
|
||||
|
||||
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next){
|
||||
tcp_pcb_num->total ++;
|
||||
if (pcb->state == FIN_WAIT_2){
|
||||
if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) {
|
||||
inactivity = tcp_ticks - pcb->tmr;
|
||||
inactive = pcb;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inactive != NULL) {
|
||||
tcp_pcb_remove(&tcp_active_pcbs, inactive);
|
||||
memp_free(MEMP_TCP_PCB, inactive);
|
||||
tcp_pcb_num->fin_wait2 ++;
|
||||
} else if (pcb->state == LAST_ACK) {
|
||||
tcp_pcb_num->last_ack ++;
|
||||
} else if (pcb->state == CLOSING) {
|
||||
tcp_pcb_num->closing ++;
|
||||
} else if (pcb->state == FIN_WAIT_1){
|
||||
tcp_pcb_num->fin_wait1 ++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the oldest connection that is in LAST_ACK state.
|
||||
* Called from tcp_alloc() if no more connections are available.
|
||||
*/
|
||||
static void tcp_kill_lastack(void)
|
||||
{
|
||||
struct tcp_pcb *pcb, *inactive;
|
||||
u32_t inactivity;
|
||||
/* Go through the list of LAST_ACK pcbs and get the oldest pcb. */
|
||||
inactivity = 0;
|
||||
inactive = NULL;
|
||||
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
if (pcb->state == LAST_ACK) {
|
||||
if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) {
|
||||
inactivity = tcp_ticks - pcb->tmr;
|
||||
inactive = pcb;
|
||||
for (listen = tcp_listen_pcbs.listen_pcbs; listen != NULL; listen = listen->next){
|
||||
tcp_pcb_num->total ++;
|
||||
tcp_pcb_num->listen ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inactive != NULL) {
|
||||
tcp_pcb_remove(&tcp_active_pcbs, inactive);
|
||||
memp_free(MEMP_TCP_PCB, inactive);
|
||||
|
||||
for (pcb = tcp_bound_pcbs; pcb != NULL; pcb = pcb->next){
|
||||
tcp_pcb_num->total ++;
|
||||
tcp_pcb_num->bound ++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a new tcp_pcb structure.
|
||||
*
|
||||
@ -1455,34 +1454,34 @@ tcp_alloc(u8_t prio)
|
||||
u32_t iss;
|
||||
|
||||
#if ESP_LWIP
|
||||
/*Kills the oldest connection that is in TIME_WAIT state.*/
|
||||
u8_t time_wait_num = 0;
|
||||
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
|
||||
time_wait_num ++;
|
||||
}
|
||||
tcp_pcb_num_t tcp_pcb_num;
|
||||
|
||||
if (time_wait_num >= MEMP_NUM_TCP_PCB)
|
||||
tcp_pcb_num_cal(&tcp_pcb_num);
|
||||
|
||||
if (tcp_pcb_num.total >= MEMP_NUM_TCP_PCB){
|
||||
if (tcp_pcb_num.time_wait > 0){
|
||||
tcp_kill_timewait();
|
||||
|
||||
/*Kills the oldest connection that is in FIN_WAIT_2 state.*/
|
||||
time_wait_num = 0;
|
||||
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next){
|
||||
if (pcb->state == FIN_WAIT_2)
|
||||
time_wait_num ++;
|
||||
} else if (tcp_pcb_num.last_ack > 0){
|
||||
tcp_kill_state(LAST_ACK);
|
||||
} else if (tcp_pcb_num.closing > 0){
|
||||
tcp_kill_state(CLOSING);
|
||||
} else if (tcp_pcb_num.fin_wait2 > 0){
|
||||
tcp_kill_state(FIN_WAIT_2);
|
||||
} else if (tcp_pcb_num.fin_wait1 > 0){
|
||||
tcp_kill_state(FIN_WAIT_1);
|
||||
} else {
|
||||
tcp_kill_prio(prio);
|
||||
}
|
||||
}
|
||||
|
||||
if (time_wait_num >= MEMP_NUM_TCP_PCB)
|
||||
tcp_kill_finwait2();
|
||||
|
||||
/*Kills the oldest connection that is in LAST_ACK state.*/
|
||||
time_wait_num = 0;
|
||||
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next){
|
||||
if (pcb->state == LAST_ACK)
|
||||
time_wait_num ++;
|
||||
tcp_pcb_num_cal(&tcp_pcb_num);
|
||||
if (tcp_pcb_num.total >= MEMP_NUM_TCP_PCB){
|
||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: no available tcp pcb %d %d %d %d %d %d %d %d\n",
|
||||
tcp_pcb_num.total, tcp_pcb_num.time_wait, tcp_pcb_num.last_ack, tcp_pcb_num.closing,
|
||||
tcp_pcb_num.fin_wait2, tcp_pcb_num.fin_wait1, tcp_pcb_num.listen, tcp_pcb_num.bound));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (time_wait_num >= MEMP_NUM_TCP_PCB)
|
||||
tcp_kill_lastack();
|
||||
#endif
|
||||
|
||||
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
|
||||
|
@ -176,7 +176,8 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
|
||||
struct tcp_seg *seg;
|
||||
u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
|
||||
|
||||
if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
|
||||
seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
|
||||
if (seg == NULL) {
|
||||
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n"));
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
|
@ -20,5 +20,6 @@ void dbg_lwip_tcp_pcb_show(void);
|
||||
void dbg_lwip_udp_pcb_show(void);
|
||||
void dbg_lwip_tcp_rxtx_show(void);
|
||||
void dbg_lwip_udp_rxtx_show(void);
|
||||
void dbg_lwip_mem_cnt_show(void);
|
||||
|
||||
#endif
|
||||
|
@ -71,8 +71,25 @@ extern const struct memp_desc* const memp_pools[MEMP_MAX];
|
||||
#include "lwip/mem.h"
|
||||
|
||||
#define memp_init()
|
||||
#if ESP_CNT_DEBUG
|
||||
static inline void* memp_malloc(int type)
|
||||
{
|
||||
ESP_CNT_MEM_MALLOC_INC(type);
|
||||
return mem_malloc(memp_pools[type]->size);
|
||||
}
|
||||
|
||||
static inline void memp_free(int type, void *mem)
|
||||
{
|
||||
ESP_CNT_MEM_FREE_INC(type);
|
||||
mem_free(mem);
|
||||
}
|
||||
|
||||
//#define memp_malloc(type) mem_malloc(memp_pools[type]->size); ESP_CNT_MEM_MALLOC_INC(type)
|
||||
//#define memp_free(type, mem) mem_free(mem); ESP_CNT_MEM_FREE_INC(type)
|
||||
#else
|
||||
#define memp_malloc(type) mem_malloc(memp_pools[type]->size)
|
||||
#define memp_free(type, mem) mem_free(mem)
|
||||
#endif
|
||||
|
||||
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
|
||||
const struct memp_desc memp_ ## name = { \
|
||||
|
@ -140,6 +140,16 @@ struct memp_desc {
|
||||
#endif /* MEMP_MEM_MALLOC */
|
||||
};
|
||||
|
||||
#if (ESP_CNT_DEBUG == 1)
|
||||
extern uint32_t g_lwip_mem_cnt[MEMP_MAX][2];
|
||||
#define ESP_CNT_MEM_MALLOC_INC(type) g_lwip_mem_cnt[type][0]++
|
||||
#define ESP_CNT_MEM_FREE_INC(type) g_lwip_mem_cnt[type][1]++
|
||||
#else
|
||||
#define ESP_CNT_MEM_MALLOC_INC(type)
|
||||
#define ESP_CNT_MEM_FREE_INC(type)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
#define DECLARE_LWIP_MEMPOOL_DESC(desc) (desc),
|
||||
#else
|
||||
|
@ -571,6 +571,7 @@
|
||||
#define ESP_IP4_ATON 1
|
||||
#define ESP_LIGHT_SLEEP 1
|
||||
#define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY
|
||||
#define ESP_CNT_DEBUG 0
|
||||
|
||||
#define TCP_WND_DEFAULT (4*TCP_MSS)
|
||||
#define TCP_SND_BUF_DEFAULT (2*TCP_MSS)
|
||||
|
Loading…
x
Reference in New Issue
Block a user