First of all, I'm using RL-NET V4 Legacy, MDK 5.25. And the uC I am using is the ARM7 LPC2468.
The system I'm programming uses "a lot" of sockets. It has:- Default TCPNET HTTP server up to 3 sessions;- Default TCPNET FTP server 1 session;- A custom server for custom communications - up to 30 client TCPNET socket connections (Maintained by "Custom Comm" Task);- 1 TCPNET UDP socket for logging messages through the network to a specific logging app (logs from several tasks!);- 1 Default TCPNET DNS client.
And it has 2 BSD sockets:
- 1 BSD TCP socket for SQL querying (Maintained by "Database Querying" Task);- 1 BSD TCP socket for Internet communications / RESTful (Maintained by "Replication" Task);
All the TCPNET native sockets are used within the WebMain Task, and they are maintained through events/mailboxes shared between the WebMain Task and the other Tasks.In contrast with that, each BSD socket is used within each individual task, not in the WebMain Task.
The documentation just says that TCPNET NATIVE sockets must be dealt with in the Web Main Task, since it's functions are non-reentrant. Meanwhile, it says the BSD sockets support concurrency. It also says that BSD sockets are based on the TCP native sockets.
The reason I use both BSD and TCP native sockets is because in the beginning the development used only TCP native sockets, then we realised that for other new tasks it was easier to do by using BSD sockets.
But the question is, can I use both? Or I must stick with one of the them?
Yes, you can use both. A BSD socket is just a layer that "sits" on native TCP sockets.Of course, there are limitations. For example, if the TCP source socket is assigned with a BSD layer, it must be fully controlled by the BSD. You should not control such a TCP socket directly from your code.
If you assign a native TCP socket with the get socket function, the socket is independent of the BSD layer and can be used as desired.
Franc Urbanc
The problem is that, before this, we were using the TCP native sockets and using them inside concurrent tasks instead of inside the Main TCP Task, which was really wrong! And in top of that we were also using BSD sockets in other tasks... we were having troubles with the system getting the ethernet stuck, like if the memory was fragmented but sometimes part of it was released, and we got the ethernet unstuck for a few seconds before it got stuck again... we were also having ETH MEM LOCKS sporadically.
With these problems, we've changed all the TCP native sockets to be manipulated only inside the Main TCP Task. The BSD sockets are still being used in other Tasks. It seems that the problem was attenuated!
Unfortunately, we're still having an issue!The problem is that we're still getting the error ETH_MEM_LOCK after several hours of runtime - last time it took 24 hours for this to happen.
For what we know, ETH_MEM_LOCK stands for:
/* Locked Memory management function (alloc/free) re-entered. *//* RTX multithread protection malfunctioning, not implemented *//* or interrupt disable is not functioning correctly. */
However, as I said, we're using TCP native only inside the Main TCP Task. And the BSD sockets are used outside, in other tasks.As assumed, this should work, right?
Now there's only one TCP Native socket, which its purpose is logging messages from tasks through UDP socket. The rest of the sockets are all BSD.The routines we call for the BSD sockets are:
- socket();- closesocket();- connect();- ioctlsocket();- send();- recv();- sendto();- recvfrom();- bind();- accept();- listen();
Below there is most part of the code we're using today for the TCP Native sockets.At the end I've also added the ETH interrupt.
@Edit: we are also using HTTP and FTP native servers
// File webmain.c -- Main TCP Task extern int logger_task(void); extern void logger_task_init(void); static __task void web_main_task(void) { dhcp_disable(); os_evt_clr(0xFFFF, os_tsk_self()); logger_task_init(); // initialize mailbox for logging messages while (1) { os_evt_wait_or(1, 0xFFFF); // event set on web_tick_task (100ms) and ethernet interrupt (driver) BOOL tcpNetPend = __TRUE; int loggerPend = 1; while (tcpNetPend || loggerPend) { loggerPend = logger_func(); tcpNetPend = main_TcpNet(); } // Event from http (button 'reset' clicked) if (os_evt_wait_and(WEB_FORCE_RESET, 0) == OS_R_EVT) { os_evt_clr(0xFFFF, os_tsk_self()); soft_reset(); } } } ... ... // File dbg_udp.c // dbgPrint() -- Function that is used to insert message on mailbox from other Tasks int logger_func(void) { logger_message_t *pmsg = NULL; if (os_mbx_wait(&logger_mail, (void*)&pmsg, 0) != OS_R_TMO) { if (pmsg != NULL) { dbg_udp_send(pmsg->c, pmsg->str, pmsg->len); // socket, udp_get_buf() and udp_send() _free_box(logger_box, pmsg); pmsg = NULL; } } return os_mbx_check(&logger_mail) != LOGGER_MESSAGE_MAX; } static int _started = 0; static U8 _sock = 0; static int _sendfail = 0; static U16 __dbg_udp_callback(U8 sock, U8 *remip, U16 remp, U8 *buf, U16 len) { return 0; } void dbg_udp_init(void) { if (!_started) { if (!_sock) _sock = udp_get_socket(0, UDP_OPT_SEND_CS, __dbg_udp_callback); if (_sock) _started = udp_open(_sock, 4301); } } void dbg_udp_send(int c, char *msg, uint32_t len) { dbg_udp_init(); if (_sendfail) len++; if (_started && msg != NULL && len > 0) { if (len >= DBG_BUF_MAX) len = DBG_BUF_MAX-1; U8 *buf = udp_get_buf(len | 0x8000); if (buf != NULL) { if (_sendfail) { mem_copy(buf, (void*)msg, len-1); buf[len-1] = '!'; } else { mem_copy(buf, (void*)msg, len); } __dbg_cript((char*)buf, len); // only cripts each byte of the buffer _sendfail = 0; udp_send(_sock, (U8[]){ 255, 255, 255, 255 }, 4303, buf, len); } else { _sendfail = 1; } } } ... ... // File EMAC_LAN8720.c -- Eth driver // #define DRIVER_EMAC_FILTER_EN -- Enables or disables filtering of MAC Address static void interrupt_ethernet (void) __irq { /* EMAC Ethernet Controller Interrupt function. */ OS_FRAME *frame; U32 idx,int_stat,RxLen,info; U32 *sp,*dp; #if DRIVER_EMAC_FILTER_EN extern U8 own_hw_adr[]; struct __ethernet_header *eh = NULL; int process_eth_buf; uint8_t broadcast_mac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; #endif while ((int_stat = (MAC_INTSTATUS & MAC_INTENABLE)) != 0) { MAC_INTCLEAR = int_stat; if (int_stat & INT_RX_DONE) { /* Packet received, check if packet is valid. */ idx = MAC_RXCONSUMEINDEX; while (idx != MAC_RXPRODUCEINDEX) { info = Rx_Stat[idx].Info; if (!(info & RINFO_LAST_FLAG)) { goto rel; } RxLen = (info & RINFO_SIZE) - 3; if (RxLen > ETH_MTU || (info & RINFO_ERR_MASK)) { /* Invalid frame, ignore it and free buffer. */ goto rel; } #if DRIVER_EMAC_FILTER_EN process_eth_buf = 0; eh = (struct __ethernet_header *)Rx_Desc[idx].Packet; if (mem_comp(eh->destination, own_hw_adr, ETH_ADRLEN) == 0) { process_eth_buf = 1; } else if (mem_comp(eh->destination, broadcast_mac, ETH_ADRLEN) == 0) { process_eth_buf = 1; } if (process_eth_buf == 1) { #endif /* Flag 0x80000000 to skip sys_error() call when out of memory. */ frame = alloc_mem (RxLen | 0x80000000); /* if 'alloc_mem()' has failed, ignore this packet. */ if (frame != NULL) { dp = (U32 *)&frame->data[0]; sp = (U32 *)Rx_Desc[idx].Packet; for (RxLen = (RxLen + 3) >> 2; RxLen; RxLen--) { *dp++ = *sp++; } put_in_queue (frame); isr_evt_set(1, wmain_tid); } #if DRIVER_EMAC_FILTER_EN } #endif rel: if (++idx == NUM_RX_FRAG) idx = 0; /* Release frame from EMAC buffer. */ MAC_RXCONSUMEINDEX = idx; } } if (int_stat & INT_TX_DONE) { /* Frame transmit completed. */ } } /* Acknowledge the interrupt. */ VICVectAddr = 0; } void sys_error (ERROR_CODE code) { except_params_t params = {0}; params[0] = isr_tsk_get(); switch (code) { case ERR_MEM_LOCK: /* Locked Memory management function (alloc/free) re-entered. */ /* RTX multithread protection malfunctioning, not implemented */ /* or interrupt disable is not functioning correctly. */ except_add(EXCEPT_ETH_MEM_LOCK, params); break; // others cases are implemented // they were removed to reduce the question } // wdt force reset soft_reset(); } ... ...
ERR_MEM_LOCK is an indication that the functions: alloc_mem(), resize_mem() or free_mem() are re-entered with one of these. The functions are only protected from interrupt with int_disable_eth() and int_enable_eth(). So using native sockets from various tasks is dangerous.
BSD sockets, however, are protected by mutex.
Thank you for your promptness and helping. We appreciate it very much!
Unfortunately we are not calling these functions from anywhere else in the code, we've searched and looked everywhere in the project. The only place it's being called, that we have access to, is inside the ETH interrupt handler, which is posted in my last comment.
We've deactivated the FTP and HTTP native servers to reduce the options. So now we're only using the logger socket (native socket) inside the Main TCPnet Task, and 2 BSD sockets in other 2 Tasks (SQL Task and Replication Task).
We're still getting the exception ERR_MEM_LOCK.
Do you have any suggestions?
Memory functions are called internally from the TCPnet core, usually from TCP or UDP sockets. Therefore, it is important that the native socket functions are not called from different tasks.
Are you sure int_disable_eth() and int_enable_eth() are working properly?
Do you manipulate enabling / disabling EMAC interrupt from the interrupt handler? This can be dangerous because int_disable_eth() is usually not an atomic function.
Can you try the debug version and record MEM debugging messages? Maybe you can see there an indication of what would be wrong.
Hello Franc Urbanc
Thank you once again.
Franc Urbanc said:Are you sure int_disable_eth() and int_enable_eth() are working properly? Do you manipulate enabling / disabling EMAC interrupt from the interrupt handler? This can be dangerous because int_disable_eth() is usually not an atomic function.
It seems that the code is right. The Ethernet interrupt bit is the 21, as stated by the LPC24XX User Manual. See the code below.
Also, we don't manipulate enabling or disabling the ETH interrupt from any interrupt handler or other places.
/*--------------------------- int_enable_eth --------------------------------*/ void int_enable_eth (void) { /* Ethernet Interrupt Enable function. */ VICIntEnable |= 1 << 21; } /*--------------------------- int_disable_eth -------------------------------*/ void int_disable_eth (void) { /* Ethernet Interrupt Disable function. */ VICIntEnClr |= 1 << 21; }
Franc Urbanc said:Can you try the debug version and record MEM debugging messages? Maybe you can see there an indication of what would be wrong.
We will do this! Thank you!
#Edit:
We are doing the debugging with the debug version.For now we can tell that the int_disable_eth() function is working just fine, since if we comment the line:"VICIntEnClr |= 1 << 21;" we rapidly get the error: 698.4 MEM-ERR:Alloc, Recursive call
We will continue testing with the debug version to see if we get the ERR_MEM_LOCK.