Chinaunix首页 | 论坛 | 博客
  • 博客访问: 502740
  • 博文数量: 157
  • 博客积分: 3010
  • 博客等级: 中校
  • 技术积分: 1608
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-16 09:30
文章存档

2010年(155)

2008年(2)

我的朋友

分类:

2010-03-11 20:39:23


四:数据接收
为了了解网卡数据接收的过程。有必要先讨论DMA的具体过程。
DMA传输数据可以分为以下几个步骤:
首先:CPU向DMA送命令,如DMA方式,主存地址,传送的字数等,之后CPU执行原来的程序.
然后DMA 控制在 I/O 设备与主存间交换数据。接收数据完后, 向CPU发DMA请求,取得总线控制权,进行数据传送,修改卡上主存地址,修改字数计数器内且检查其值是否为零,不为零则继续传送,若已为零,则向 CPU发中断请求.。
也就是说,网卡收到包时,将它放入当前skb->data中。再来一个包时。DMA会修改卡上主存地址,转到skb->next,将数据放入其中。这也就是,一个skb->data存储一个数据包的原因。
好了,现在就可以来看具体的代码实现了。
当网络数据到络,网卡将其放到DMA内存,然后DMA向CPU报告中断,CPU根据中断向量,找到中断处理例程,也就是我们前面注册的e100_intr()进行处理。
static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs)
{
         struct net_device *netdev = dev_id;
         struct nic *nic = netdev_priv(netdev);
         u8 stat_ack = readb(&nic->csr->scb.stat_ack);
 
         DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack);
 
         if(stat_ack == stat_ack_not_ours ||          /* Not our interrupt */
            stat_ack == stat_ack_not_present)   /* Hardware is ejected */
                   return IRQ_NONE;
 
         /* Ack interrupt(s) */
         //发送中断ACK。Cpu向设备发送ACK。表示此中断已经处理
         writeb(stat_ack, &nic->csr->scb.stat_ack);
 
         /* We hit Receive No Resource (RNR); restart RU after cleaning */
         if(stat_ack & stat_ack_rnr)
                   nic->ru_running = 0;
         //禁用中断
         e100_disable_irq(nic);
         //CPU开始调度此设备。转而会运行netdev->poll
         netif_rx_schedule(netdev);
 
         return IRQ_HANDLED;
}
netif_rx_schedule(netdev)后,cpu开始调度此设备,轮询设备是否有数据要处理。转后调用netdev->poll函数,即:e100_poll()
static int e100_poll(struct net_device *netdev, int *budget)
{
         struct nic *nic = netdev_priv(netdev);
         unsigned int work_to_do = min(netdev->quota, *budget);
         unsigned int work_done = 0;
         int tx_cleaned;
         //开始对nic中,DMA数据的处理
         e100_rx_clean(nic, &work_done, work_to_do);
         tx_cleaned = e100_tx_clean(nic);
 
         /* If no Rx and Tx cleanup work was done, exit polling mode. */
         if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) {
                   netif_rx_complete(netdev);
                   e100_enable_irq(nic);
                   return 0;
         }
 
         *budget -= work_done;
         netdev->quota -= work_done;
 
         return 1;
}
跟踪进e100_rx_clean():
static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done,
         unsigned int work_to_do)
{
         struct rx *rx;
 
         /* Indicate newly arrived packets */
         //遍历环形DMA中的数据,调用e100_rx_indicate()进行处理
         for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {
                   if(e100_rx_indicate(nic, rx, work_done, work_to_do))
                            break; /* No more to clean */
         }
 
         /* Alloc new skbs to refill list */
         for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {
                   if(unlikely(e100_rx_alloc_skb(nic, rx)))
                            break; /* Better luck next time (see watchdog) */
         }
 
         e100_start_receiver(nic);
}
在这里,它会遍历环形DMA中的数据,即从nic->rx_to_clean开始的数据,直至数据全部处理完
进入处理函数:e100_rx_indicate()
static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,
         unsigned int *work_done, unsigned int work_to_do)
{
         struct sk_buff *skb = rx->skb;
         //从这里取得rfd.其中包括了一些接收信息,但不是链路传过来的有效数据
         struct rfd *rfd = (struct rfd *)skb->data;
         u16 rfd_status, actual_size;
 
         if(unlikely(work_done && *work_done >= work_to_do))
                   return -EAGAIN;
         //同步DMA缓存
         pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr,
                   sizeof(struct rfd), PCI_DMA_FROMDEVICE);
         //取得接收状态
         rfd_status = le16_to_cpu(rfd->status);
         DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status);
         /* If data isn't ready, nothing to indicate */
         //没有接收完全,返回
         if(unlikely(!(rfd_status & cb_complete)))
                     return -EAGAIN;
         //取得接收数据的长度
         actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF;
         if(unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd)))
                   actual_size = RFD_BUF_LEN - sizeof(struct rfd);
 
         //取消DMA缓存映射
         pci_unmap_single(nic->pdev, rx->dma_addr,
                   RFD_BUF_LEN, PCI_DMA_FROMDEVICE);
         //由于RFD不是链路传入的数据,清除
         skb_reserve(skb, sizeof(struct rfd));
         //调整skb中的tail指针,与len更新
         skb_put(skb, actual_size);
         //取得链路层协议
         skb->protocol = eth_type_trans(skb, nic->netdev);
         //接收失败
         if(unlikely(!(rfd_status & cb_ok))) {
                   /* Don't indicate if hardware indicates errors */
                   nic->net_stats.rx_dropped++;
                   dev_kfree_skb_any(skb);
         }
         //数据超长。Drop it 
else if(actual_size > nic->netdev->mtu + VLAN_ETH_HLEN) {
                   /* Don't indicate oversized frames */
                   nic->rx_over_length_errors++;
                   nic->net_stats.rx_dropped++;
                   dev_kfree_skb_any(skb);
         } else {
                   //成功的接收了,更新统计计数
                   nic->net_stats.rx_packets++;
                   nic->net_stats.rx_bytes += actual_size;
                   nic->netdev->last_rx = jiffies;
                   //送至上次协议处理
                   netif_receive_skb(skb);
                   if(work_done)
                            (*work_done)++;
         }
 
         rx->skb = NULL;
 
         return 0;
}
上面代码中要去判断接收是否完全,为什么要去判断呢?根据DMA机制,是网卡把数据放入DMA之后。DMA再向CPU发中断的嘛?呵呵。在这里进行接收完全判断是因为:
1:由其它原因造成的中断
2:在处理中断时候。数据又到达了。网卡依然会把它放至下一个skb。而在代码处理中是遍历处理的,也就是说处理下一个skb的时候,可能网卡正在传数据。
好了,运行到netif_receive_skb()之后,数据包被送到上层。关于后续的处理流程,以后会有专题讨论
 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/luoye144200720102030/archive/2009/06/05/4243376.aspx
阅读(818) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~