一、Forward数据包的存储转发:
1、fwdPktSend线程
{
While(1)
{
recvmsg(fwdPktSockFd, &msg);
fwdPktSendPacket();
-> osMsgQSend(txMsgQ);
}
}
2、fwdPktRecv线程
{
appDemoDxChNetRxPacketCbRegister(appDemoDxChForwardPktCb);
While(1)
{
osMsgQRecv(fwdPktTaskQueueId, );
...
sendmsg(fwdPktSockFd,&msg); //netlink发送数据包
}
}
3、transmitTask线程
{
While(1)
{
osMsgQRecv(transmitTaskQueueId, );
}
}
evHandler_recv_fifo函数中,调用appDemoDxChForwardPktCb(),该函数中
osMsgQSend(fwdPktTaskQueueId, &packets);来唤醒fwdPktRecv线程的 osMsgQRecv(fwdPktTaskQueueId, );
osMsgQSend和osMsgQRecv的实现在驱动mv_kernelExt中.
osMsgQRecv->mvKernelExt_MsgQRecv->mv_do_wait_on_queue_timeout:
While(q->messages == 0)
while (tsk->waitqueue && timeout)
{
if (unlikely(signal_pending(tsk->task)))
{
tsk->tasklockflag = 0;
mv_delete_from_waitqueue(tsk);
return -1;
}
set_task_state(tsk->task, TASK_INTERRUPTIBLE);
MV_GLOBAL_UNLOCK();
timeout = schedule_timeout(timeout);
MV_GLOBAL_LOCK();
}
Schedule_timeout()已经进入INTERRUPTIBLE的睡眠状态。
osMsgQSend->mvKernelExt_MsgQSend()
While(q-Messages == q->maxmsgs)
{
mv_do_wait_on_queue_timeout();
}
...
Wake_up_process();
整个数据转发的流程比较清晰:
中断的event函数中,调用evHandler_recv_fifo->appDemoDxChForwardPktCb(),
osMsgQSend(fwdPktTaskQueueId, &packets)像消息队列发送packates后wakeup osMsgRecv(fwdPktTaskQueueId),及线程fwdPktRecv:sendmsg(fwdPktSockFd,&msg),向内核发送netlink数据包;
内核arch/arm/plat-feroceon/my_drivers_lsp/mv_prestera_net/mv_prestera_mux.c中通过netlink_kernel_create()创建内核线程netlink_recv_packet,来接收应用线程fwdPktRecv的sendmsg请求;
netlink_recv_packet的工作如下:
fwd_skb = alloc_skb(NLMSG_PAYLOAD(nlh));
memcpy(fwd_skb->data, NLMSG_DATA(nlh), len);
kfree_skb(skb);
appDemoFwdRxPkt(fwd_skb);
appDemoFwdRxPkt()函数完成net网卡的端口数据转发:
Struct net_device *dev = __dev_get_by_name(&init_net, “mux0”); //获取mux0的网卡
Fwd_skb->dev = dev;
mvPortForward(fwd_skb, dev);
-> netif_receive_skb(skb); //linux协议栈入口,数据包送上层处理
(1)、fwdPktSend线程:
recvmsg线程neilink方式阻塞获取内核mux0的出来的报文,并调用cpssFdbMacFindPort查找port号,最后通过osMsgQSend(transmitTaskQueueId, &packet, size)向port转发该报文;
(2)、transmitTask线程:
osMsgRecv(transmitTaskQueueId, &packet);获取该类型报文后,send_acket_DxCh();->dxChNetIfSdmaTxPacketSend(); DMA方式发送packet到交换机;
(3)、fwdPktRecv线程:
交换芯片中断收包后,osMsgQSend(fwdPktTaskQueueId, &packets)唤醒该线程的osMsgQRecv(fwdPktTaskQueueId, );来向内核netlink发送该二层包;内核netlink线程收到该包后,发送到内核linux协议栈,并绑定网口mux0;
(4)、lldp线程:
该线程通过
appDemoDxChNetRxPacketCbRegister(appDemoDxChNetLldpProtocolCb);
注册中断hook函数,该函数中通过osMsgQSend(lldpTaskQueueId);唤醒osMsgQRecv(lldpTaskQueueId, &packet),memcpy拷贝packet来实现对该packet的协议处理;最后通过osMsgQSend(transmitTaskQueueId)来转发出去。
可以看到,该协议的处理,至少有4次内存拷贝,收发的各2次内存拷贝:存储转发到接收缓缓冲池lldpTaskQueueId的1次内存拷贝,协议任务获取从该缓冲池获取包的一次内存拷贝;发送到transmitTaskQueueId缓冲池的1次内存拷贝,transmitTask线程从transmitTaskQueueId缓冲池转发到交换芯片端口的一次内存拷贝。
fwdPktRecv线程的示意图如下所示:
二、event处理流程:
- 创建event_handle任务:
userEventHandler.c 里面,appdemoEventRequestDrvnModeInit()函数中,for创建了10个evHndl_x任务线程,均执行appDemoEvHndlr()处理函数:
While(1)
{
Select();
cpssEventRecv();
->appDemoEnPpEvTreat();
case CPSS_PP_RX_BUFFER_QUEUE0_E:
case CPSS_PP_RX_BUFFER_QUEUE1_E:
case CPSS_PP_RX_BUFFER_QUEUE2_E:
case CPSS_PP_RX_BUFFER_QUEUE3_E:
case CPSS_PP_RX_BUFFER_QUEUE4_E:
case CPSS_PP_RX_BUFFER_QUEUE5_E:
case CPSS_PP_RX_BUFFER_QUEUE6_E:
case CPSS_PP_RX_BUFFER_QUEUE7_E:
cpssEnRxPacketGet(queue_num);
->appDemoDxChNetRxPktHandle();
->cpssDxChNetIfSdmaRxPacketGet();
->dxChNetIfRxPacketGet();
packetBuffsAddrPtr[i] = phy2virt(rxDesc>buffPointer);
这里获取到了rc_packet的地址。
-> for(ii = 0 ; ii < numRegistrations ; ii++)
{
(rxPktReceiveCbArray[ii])(devNum,queueIdx,numOfBuff,
packetBuffs,buffLenArr,&rxParams);
}
依次调用所有注册的hook函数。
Hook函数的注册通过appDemoDxChNetRxPacketCbRegister()来实现,所有的协议都需要通过该函数来注册hook():
appDemoDxChNetRxPacketCbRegister(appDemoLink);
appDemoDxChNetRxPacketCbRegister(appDemoDxChForwardPktCb);
appDemoDxChNetRxPacketCbRegister(appDemoDxChNetLldpProtocolCb);
1)、appDemoLink:
如果22端口目的mac==0x01 80 c2 00 00 86的packate,获取千兆口mac的link状态,checkPhyLinkStatus();更新mac千兆口的link状态;
2)、appDemoDxChForwardPktCb:
fwdPktRecv任务线程中,通过appDemoDxChNetRxPacketCbRegister(appDemoDxChForwardPktCb); 来注册hook;
appDemoDxChForwardPktCb()函数中,主要通过
osMemCpy()报文拷贝;
osMsgQSend(fwdPktTaskQueueId, &packets);