原文地址:https://blog.csdn.net/qq_21748131/article/details/123038734
zynq解决使用LWIP时报错unable to alloc pbuf in recv_handler错误
错误原因
错误原因
解决办法
总结
在使用LWIP时候zynq的SDK范例运行非常正常,但是在此基础上增加别的协议栈时容易发生unable to alloc pbuf in recv_handler错误。
错误原因
1:这个错误在setup_rx_bds函数中发生,该函数功能是释放DMA的空闲bd块和传递以太网数据包。setup_rx_bds在emacps_recv_handler函数中调用。
void setup_rx_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *rxring)
{
XEmacPs_Bd *rxbd;
XStatus status;
struct pbuf *p;
u32_t freebds;
u32_t bdindex;
u32 *temp;
u32_t index;
index = get_base_index_rxpbufsstorage (xemacpsif);
freebds = XEmacPs_BdRingGetFreeCnt (rxring);
while (freebds > 0) {
freebds--;
#ifdef ZYNQMP_USE_JUMBO
p = pbuf_alloc(PBUF_RAW, MAX_FRAME_SIZE_JUMBO, PBUF_POOL);
#else
p = pbuf_alloc(PBUF_RAW, XEMACPS_MAX_FRAME_SIZE, PBUF_POOL);
#endif
if (!p) {
#if LINK_STATS
lwip_stats.link.memerr++;
lwip_stats.link.drop++;
#endif
printf("unable to alloc pbuf in recv_handler\r\n");
return;
}
status = XEmacPs_BdRingAlloc(rxring, 1, &rxbd);
if (status != XST_SUCCESS) {
LWIP_DEBUGF(NETIF_DEBUG, ("setup_rx_bds: Error allocating RxBD\r\n"));
pbuf_free(p);
return;
}
status = XEmacPs_BdRingToHw(rxring, 1, rxbd);
if (status != XST_SUCCESS) {
LWIP_DEBUGF(NETIF_DEBUG, ("Error committing RxBD to hardware: "));
if (status == XST_DMA_SG_LIST_ERROR) {
LWIP_DEBUGF(NETIF_DEBUG, ("XST_DMA_SG_LIST_ERROR: this function was called out of sequence with XEmacPs_BdRingAlloc()\r\n"));
}
else {
LWIP_DEBUGF(NETIF_DEBUG, ("set of BDs was rejected because the first BD did not have its start-of-packet bit set, or the last BD did not have its end-of-packet bit set, or any one of the BD set has 0 as length value\r\n"));
}
pbuf_free(p);
XEmacPs_BdRingUnAlloc(rxring, 1, rxbd);
return;
}
#ifdef ZYNQMP_USE_JUMBO
if (xemacpsif->emacps.Config.IsCacheCoherent == 0) {
Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)MAX_FRAME_SIZE_JUMBO);
}
#else
if (xemacpsif->emacps.Config.IsCacheCoherent == 0) {
Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)XEMACPS_MAX_FRAME_SIZE);
}
#endif
bdindex = XEMACPS_BD_TO_INDEX(rxring, rxbd);
temp = (u32 *)rxbd;
if (bdindex == (XLWIP_CONFIG_N_RX_DESC - 1)) {
*temp = 0x00000002;
} else {
*temp = 0;
}
temp++;
*temp = 0;
dsb();
XEmacPs_BdSetAddressRx(rxbd, (UINTPTR)p->payload);
rx_pbufs_storage[index + bdindex] = (UINTPTR)p;
}
}
错误发生在 printf(“unable to alloc pbuf in recv_handler\r\n”); 这个地方,是申请pbuf失败。
网上最多查到的办法是增大pbuf部分的内存,例如 MEM_SIZE, MEMP_NUM_PBUF, PBUF_POOL_SIZE.这些参数。但是使用LWIP范例时候同样的用法为什么不会报错呢?我们再分析一下进入emacps_recv_handler函数分析一下。
错误原因
2:emacps_recv_handler函数是DMA函数的回调函数,以太网DMA收到数据包时候将会调用该函数。
void emacps_recv_handler(void *arg)
{
struct pbuf *p;
XEmacPs_Bd *rxbdset, *curbdptr;
struct xemac_s *xemac;
xemacpsif_s *xemacpsif;
XEmacPs_BdRing *rxring;
volatile s32_t bd_processed;
s32_t rx_bytes, k;
u32_t bdindex;
u32_t regval;
u32_t index;
u32_t gigeversion;
xemac = (struct xemac_s *)(arg);
xemacpsif = (xemacpsif_s *)(xemac->state);
rxring = &XEmacPs_GetRxRing(&xemacpsif->emacps);
#ifdef OS_IS_FREERTOS
xInsideISR++;
#endif
gigeversion = ((Xil_In32(xemacpsif->emacps.Config.BaseAddress + 0xFC)) >> 16) & 0xFFF;
index = get_base_index_rxpbufsstorage (xemacpsif);
/*
* If Reception done interrupt is asserted, call RX call back function
* to handle the processed BDs and then raise the according flag.
*/
regval = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET);
XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET, regval);
if (gigeversion <= 2) {
resetrx_on_no_rxdata(xemacpsif);
}
//
while(1) {
bd_processed = XEmacPs_BdRingFromHwRx(rxring, XLWIP_CONFIG_N_RX_DESC, &rxbdset);
if (bd_processed <= 0) {
break;
}
for (k = 0, curbdptr=rxbdset; k < bd_processed; k++) {
bdindex = XEMACPS_BD_TO_INDEX(rxring, curbdptr);
p = (struct pbuf *)rx_pbufs_storage[index + bdindex];
/*
* Adjust the buffer size to the actual number of bytes received.
*/
#ifdef ZYNQMP_USE_JUMBO
rx_bytes = XEmacPs_GetRxFrameSize(&xemacpsif->emacps, curbdptr);
#else
rx_bytes = XEmacPs_BdGetLength(curbdptr);
#endif
pbuf_realloc(p, rx_bytes);
/* store it in the receive queue,
* where it'll be processed by a different handler
*/
if (pq_enqueue(xemacpsif->recv_q, (void*)p) < 0) {
#if LINK_STATS
lwip_stats.link.memerr++;
lwip_stats.link.drop++;
#endif
pbuf_free(p);
}
curbdptr = XEmacPs_BdRingNext( rxring, curbdptr);
}
/* free up the BD's */
XEmacPs_BdRingFree(rxring, bd_processed, rxbdset);
setup_rx_bds(xemacpsif, rxring); //函数在这里调用
#if !NO_SYS
sys_sem_signal(&xemac->sem_rx_data_available);
#endif
}
#ifdef OS_IS_FREERTOS
xInsideISR--;
#endif
return;
}
我们看到调用setup_rx_bds(xemacpsif, rxring); 的时候是在循环中使用。这样会多次调用他导致重复的释放db块,然后引起FreeCnt计数错误,发生错误后每次都增加FreeCnt的计数器。
解决办法
所以我们把setup_rx_bds(xemacpsif, rxring); 放到while循环外,避免多次调用导致可能的错误计数发生。
while(1) {
bd_processed = XEmacPs_BdRingFromHwRx(rxring, XLWIP_CONFIG_N_RX_DESC, &rxbdset);
if (bd_processed <= 0) {
break;
}
for (k = 0, curbdptr=rxbdset; k < bd_processed; k++) {
bdindex = XEMACPS_BD_TO_INDEX(rxring, curbdptr);
p = (struct pbuf *)rx_pbufs_storage[index + bdindex];
/*
* Adjust the buffer size to the actual number of bytes received.
*/
#ifdef ZYNQMP_USE_JUMBO
rx_bytes = XEmacPs_GetRxFrameSize(&xemacpsif->emacps, curbdptr);
#else
rx_bytes = XEmacPs_BdGetLength(curbdptr);
#endif
pbuf_realloc(p, rx_bytes);
/* store it in the receive queue,
* where it'll be processed by a different handler
*/
if (pq_enqueue(xemacpsif->recv_q, (void*)p) < 0) {
#if LINK_STATS
lwip_stats.link.memerr++;
lwip_stats.link.drop++;
#endif
pbuf_free(p);
}
curbdptr = XEmacPs_BdRingNext( rxring, curbdptr);
}
/* free up the BD's */
XEmacPs_BdRingFree(rxring, bd_processed, rxbdset);
// setup_rx_bds(xemacpsif, rxring); //函数在这里调用
#if !NO_SYS
sys_sem_signal(&xemac->sem_rx_data_available);
#endif
}
setup_rx_bds(xemacpsif, rxring); //放到这里
#ifdef OS_IS_FREERTOS
xInsideISR--;
#endif
return;
}
警告测试这样放置后解决该问题。
总结
所以解决他的两种方法:
1:参考Xlinx社区方法增大内存但是我试了试好像不管用 lwIP netif: ‘unable to alloc pbuf in recv_handler’ message
2:就是我们在循环外调用setup_rx_bds的方法
————————————————
版权声明:本文为CSDN博主「qq_21748131」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_21748131/article/details/123038734