网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx

上传人:b****7 文档编号:22427129 上传时间:2023-02-04 格式:DOCX 页数:16 大小:21.33KB
下载 相关 举报
网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx_第1页
第1页 / 共16页
网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx_第2页
第2页 / 共16页
网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx_第3页
第3页 / 共16页
网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx_第4页
第4页 / 共16页
网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx

《网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx》由会员分享,可在线阅读,更多相关《网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx(16页珍藏版)》请在冰豆网上搜索。

网卡驱动和队列层中的数据包接收Linux TCPIP协议栈笔记Word下载.docx

内核通过调用

dma_map_single(structdevice*dev,void*buffer,size_tsize,enumdma_data_directiondirection)

建立映射关系。

structdevice*dev,描述一个设备;

buffer:

把哪个地址映射给设备;

也就是某一个skb——要映射全部,当然是做一个双向链表的循环即可;

size:

缓存大小;

direction:

映射方向——谁传给谁:

一般来说,是“双向”映射,数据在设备和内存之间双向流动;

对于PCI设备而言(网卡一般是PCI的),通过另一个包裹函数pci_map_single,这样,就把buffer交给设备了!

设备可以直接从里边读/取数据。

3、这一步由硬件完成;

4、取消映射

dma_unmap_single,对PCI而言,大多调用它的包裹函数pci_unmap_single,不取消的话,缓存控制权还在设备手里,要调用它,把主动权掌握在CPU手里——因为我们已经接收到数据了,应该由CPU把数据交给上层网络栈;

当然,不取消之前,通常要读一些状态位信息,诸如此类,一般是调用

dma_sync_single_for_cpu()

让CPU在取消映射前,就可以访问DMA缓冲区中的内容。

关于DMA映射的更多内容,可以参考《Linux设备驱动程序》“内存映射和DMA”章节相关内容!

OK,有了这些知识,我们就可以来看e100的代码了,它跟上面讲的步骤基本上一样的——绕了这么多圈子,就是想绕到e100上面了,呵呵!

在e100_open函数中,调用e100_up,我们前面分析它时,略过了一个重要的东东,就是环形缓冲区的建立,这一步,是通过

e100_rx_alloc_list函数调用完成的:

static

inte100_rx_alloc_list(structnic*nic)

1.{

2.structrx*rx;

3.unsignedinti,count=nic->

params.rfds.count;

4.

5.nic->

rx_to_use=nic->

rx_to_clean=NULL;

6.nic->

ru_running=RU_UNINITIALIZED;

7.

8./*结构structrx用来描述一个缓冲区节点,这里分配了count个*/

9.if(!

(nic->

rxs=kmalloc(sizeof(structrx)*count,GFP_ATOMIC)))

10.return-ENOMEM;

11.memset(nic->

rxs,0,sizeof(structrx)*count);

12.

13./*虽然是连续分配的,不过还是遍历它,建立双向链表,然后为每一个rx的skb指针分员分配空间

14.skb用来描述内核中的一个数据包,呵呵,说到重点了*/

15.for(rx=nic->

rxs,i=0;

i<

count;

rx++,i++){

16.rx->

next=(i+1<

count)?

rx+1:

nic->

rxs;

17.rx->

prev=(i==0)?

rxs+count-1:

rx-1;

18.if(e100_rx_alloc_skb(nic,rx)){/*分配缓存*/

19.e100_rx_clean_list(nic);

20.return-ENOMEM;

21.}

22.}

23.

24.nic->

rx_to_clean=nic->

25.nic->

ru_running=RU_SUSPENDED;

26.

27.return0;

28.}

staticinte100_rx_alloc_list(structnic*nic){structrx*rx;

unsignedinti,count=nic->

nic->

/*结构structrx用来描述一个缓冲区节点,这里分配了count个*/if(!

rxs=kmalloc(sizeof(structrx)*count,GFP_ATOMIC)))return-ENOMEM;

memset(nic->

/*虽然是连续分配的,不过还是遍历它,建立双向链表,然后为每一个rx的skb指针分员分配空间skb用来描述内核中的一个数据包,呵呵,说到重点了*/for(rx=nic->

rx++,i++){rx->

rx->

if(e100_rx_alloc_skb(nic,rx)){/*分配缓存*/e100_rx_clean_list(nic);

return-ENOMEM;

}}nic->

return0;

}

#defineRFD_BUF_LEN(sizeof(structrfd)+VLAN_ETH_FRAME_LEN)

1.static

inline

inte100_rx_alloc_skb(structnic*nic,structrx*rx)

2.{

3./*skb缓存的分配,是通过调用系统函数dev_alloc_skb来完成的,它同内核栈中通常调用alloc_skb的区别在于,

4.它是原子的,所以,通常在中断上下文中使用*/

5.if(!

(rx->

skb=dev_alloc_skb(RFD_BUF_LEN+NET_IP_ALIGN)))

6.return-ENOMEM;

8./*初始化必要的成员*/

9.rx->

skb->

dev=nic->

netdev;

10.skb_reserve(rx->

skb,NET_IP_ALIGN);

11./*这里在数据区之前,留了一块sizeof(structrfd)这么大的空间,该结构的

12.一个重要作用,用来保存一些状态信息,比如,在接收数据之前,可以先通过

13.它,来判断是否真有数据到达等,诸如此类*/

14.memcpy(rx->

data,&

blank_rfd,sizeof(structrfd));

15./*这是最关键的一步,建立DMA映射,把每一个缓冲区rx->

data都映射给了设备,缓存区节点

16.rx利用dma_addr保存了每一次映射的地址,这个地址后面会被用到*/

17.rx->

dma_addr=pci_map_single(nic->

pdev,rx->

data,

18.RFD_BUF_LEN,PCI_DMA_BIDIRECTIONAL);

19.

20.if(pci_dma_mapping_error(rx->

dma_addr)){

21.dev_kfree_skb_any(rx->

skb);

22.rx->

skb=0;

23.rx->

dma_addr=0;

24.return-ENOMEM;

25.}

27./*LinktheRFDtoendofRFAbylinkingpreviousRFDto

28.*thisone,andclearingELbitofprevious.*/

29.if(rx->

prev->

skb){

30.structrfd*prev_rfd=(structrfd*)rx->

data;

31./*put_unaligned(val,ptr);

用到把var放到ptr指针的地方,它能处理处理内存对齐的问题

32.prev_rfd是在缓冲区开始处保存的一点空间,它的link成员,也保存了映射后的地址*/

33.put_unaligned(cpu_to_le32(rx->

dma_addr),

34.(u32*)&

prev_rfd->

link);

35.wmb();

36.prev_rfd->

command&

=~cpu_to_le16(cb_el);

37.pci_dma_sync_single_for_device(nic->

dma_addr,

38.sizeof(structrfd),PCI_DMA_TODEVICE);

39.}

40.

41.return0;

42.}

#defineRFD_BUF_LEN(sizeof(structrfd)+VLAN_ETH_FRAME_LEN)staticinlineinte100_rx_alloc_skb(structnic*nic,structrx*rx){/*skb缓存的分配,是通过调用系统函数dev_alloc_skb来完成的,它同内核栈中通常调用alloc_skb的区别在于,它是原子的,所以,通常在中断上下文中使用*/if(!

skb=dev_alloc_skb(RFD_BUF_LEN+NET_IP_ALIGN)))return-ENOMEM;

/*初始化必要的成员*/rx->

skb_reserve(rx->

/*这里在数据区之前,留了一块sizeof(structrfd)这么大的空间,该结构的一个重要作用,用来保存一些状态信息,比如,在接收数据之前,可以先通过它,来判断是否真有数据到达等,诸如此类*/memcpy(rx->

/*这是最关键的一步,建立DMA映射,把每一个缓冲区rx->

data都映射给了设备,缓存区节点rx利用dma_addr保存了每一次映射的地址,这个地址后面会被用到*/rx->

data,RFD_BUF_LEN,PCI_DMA_BIDIRECTIONAL);

if(pci_dma_mapping_error(rx->

dma_addr)){dev_kfree_skb_any(rx->

return-ENOMEM;

}/*LinktheRFDtoendofRFAbylinkingpreviousRFDto*thisone,andclearingELbitofprevious.*/if(rx->

skb){structrfd*prev_rfd=(structrfd*)rx->

/*put_unaligned(val,ptr);

用到把var放到ptr指针的地方,它能处理处理内存对齐的问题prev_rfd是在缓冲区开始处保存的一点空间,它的link成员,也保存了映射后的地址*/put_unaligned(cpu_to_le32(rx->

dma_addr),(u32*)&

wmb();

prev_rfd->

pci_dma_sync_single_for_device(nic->

dma_addr,sizeof(structrfd),PCI_DMA_TODEVICE);

}return0;

e100_rx_alloc_list函数在一个循环中,建立了环形缓冲区,并调用e100_rx_alloc_skb为每个缓冲区分配了空间,并做了

DMA映射。

这样,我们就可以来看接收数据的过程了。

前面我们讲过,中断函数中,调用netif_rx_schedule,表明使用轮询技术,系统会在未来某一时刻,调用设备的poll函数:

inte100_poll(structnet_device*netdev,int*budget)

2.structnic*nic=netdev_priv(netdev);

3.unsignedintwork_to_do=min(netdev->

quota,*budget);

4.unsignedintwork_done=0;

5.inttx_cleaned;

6.

7.e100_rx_clean(nic,&

work_done,work_to_do);

8.tx_cleaned=e100_tx_clean(nic);

9.

10./*IfnoRxandTxcleanupworkwasdone,exitpollingmode.*/

11.if((!

tx_cleaned&

&

(work_done==0))||!

netif_running(netdev)){

12.netif_rx_complete(netdev);

13.e100_enable_irq(nic);

14.return0;

15.}

16.

17.*budget-=work_done;

18.netdev->

quota-=work_done;

20.return1;

21.}

staticinte100_poll(structnet_device*netdev,int*budget){structnic*nic=netdev_priv(netdev);

unsignedintwork_to_do=min(netdev->

unsignedintwork_done=0;

inttx_cleaned;

e100_rx_clean(nic,&

tx_cleaned=e100_tx_clean(nic);

/*IfnoRxandTxcleanupworkwasdone,exitpollingmode.*/if((!

netif_running(netdev)){netif_rx_complete(netdev);

e100_enable_irq(nic);

return0;

}*budget-=work_done;

netdev->

return1;

目前,我们只关心rx,所以,e100_rx_clean函数就成了我们关注的对像,它用来从缓冲队列中接收全部数据(这或许是取名为clean的原因吧!

):

voide100_rx_clean(structnic*nic,unsignedint*work_done,

1.unsignedintwork_to_do)

3.structrx*rx;

4.intrestart_required=0;

5.structrx*rx_to_start=NULL;

7./*arewealreadyrnr?

thenpayattention!

!

thisensuresthat

8.*thestatemachineprogressionneverallowsastartwitha

9.*partiallycleanedlist,avoidingaracebetweenhardware

10.*andrx_to_cleanwheninNAPImode*/

11.if(RU_SUSPENDED==nic->

ru_running)

12.restart_required=1;

13.

14./*函数最重要的工作,就是遍历环形缓冲区,接收数据*/

rx_to_clean;

skb;

rx=nic->

rx_to_clean=rx->

next){

16.interr=e100_rx_indicate(nic,rx,work_done,work_to_do);

17.if(-EAGAIN==err){

18./*hitquotasohavemoreworktodo,restartonce

19.*cleanupiscomplete*/

20.restart_required=0;

21.break;

22.}else

if(-ENODATA==err)

23.break;

/*Nomoretoclean*/

24.}

25.

26./*saveourstartingpointastheplacewe'

llrestartthereceiver*/

27.if(restart_required)

28.rx_to_start=nic->

29.

30./*Allocnewskbstorefilllist*/

31.for(rx=nic->

rx_to_use;

!

rx->

rx_to_use=rx->

32.if(unlikely(e100_rx_alloc_skb(nic,rx)))

33.break;

/*Betterlucknexttime(seewatchdog)*/

34.}

35.

36.if(restart_required){

37.//ackthernr?

38.writeb(stat_ack_rnr,&

csr->

scb.stat_ack);

39.e100_start_receiver(nic,rx_to_start);

40.if(work_done)

41.(*work_done)++;

43.}

staticinlinevoide100_rx_clean(structnic*nic,unsignedint*work_done,unsignedintwork_to_do)

{

structrx*rx;

intrestart_required=0;

structrx*rx_to_start=NULL;

/*arewealreadyrnr?

thisensuresthat*thestatemachineprogressionneverallowsastartwitha*partiallycleanedlist,avoidingaracebetweenhardware*andrx_to_cleanwheninNAPImode*/if(RU_SUSPENDED==nic->

ru_running)

restart_required=1;

/*函数最重要的工作,就是遍历环形缓冲区,接收数据*/

for(rx=nic->

next)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 幼儿读物

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1