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:
Wu Jian Gang 2016-11-25 13:44:02 +08:00
commit 7f2c6a9d80
7 changed files with 122 additions and 72 deletions

View file

@ -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

View file

@ -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)
{
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;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
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);
}
}
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;
/**
* 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)
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 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;
}
}
}
if (inactive != NULL) {
tcp_pcb_remove(&tcp_active_pcbs, inactive);
memp_free(MEMP_TCP_PCB, inactive);
}
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){
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 ++;
}
}
for (listen = tcp_listen_pcbs.listen_pcbs; listen != NULL; listen = listen->next){
tcp_pcb_num->total ++;
tcp_pcb_num->listen ++;
}
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;
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();
} 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_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 ++;
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_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 ++;
}
if (time_wait_num >= MEMP_NUM_TCP_PCB)
tcp_kill_lastack();
#endif
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);

View file

@ -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;

View file

@ -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

View file

@ -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 = { \

View file

@ -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

View file

@ -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)