分享

linux协议栈之链路层上的数据传输之二

 enchen008 2012-04-10

 数据接收

  解网卡数据接收过程。有必先讨论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%02Xn", 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%04Xn", 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));
     //调整skbtail指针,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(),数据包被送层。
 二数据发送进入发送函数们先看e100_up()->e100_alloc_cbs函数:static int e100_alloc_cbs(struct nic *nic)
{
     struct cb *cb;
     unsigned int i, count = nic->params.cbs.count;
     nic->cuc_cmd = cuc_start;
     nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = NULL;
     nic->cbs_avail = 0;
     //线性DMA映射,这里返回虚拟地址,供CPU使
     nic->cbs = pci_alloc_consistent(nic->pdev,
          sizeof(struct cb) * count, &nic->cbs_dma_addr);
     if(!nic->cbs)
          return -ENOMEM;
     //建立环形发送缓冲区
     for(cb = nic->cbs, i = 0; i < count; cb++, i++) {
          cb->next = (i + 1 < count) ? cb + 1 : nic->cbs;
          cb->prev = (i == 0) ? nic->cbs + count - 1 : cb - 1;
          cb->dma_addr = nic->cbs_dma_addr + i * sizeof(struct cb);
          cb->link = cpu_to_le32(nic->cbs_dma_addr +
              ((i+1) % count) * sizeof(struct cb));
          cb->skb = NULL;
     }
     //始化各指针,使其指向缓冲始位置
     nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = nic->cbs;
     nic->cbs_avail = count;
     return 0;
}
段代码里,完成发送准备工作,建立发送环形缓存。发送数剧时,只数据送入缓存即数据最终dev-> hard_start_xmit函数。e100代码里,e100_xmit_frame(). 进入里面看static int e100_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
     struct nic *nic = netdev_priv(netdev);
     int err;
     if(nic->flags & ich_10h_workaround) {
          e100_exec_cmd(nic, cuc_nop, 0);
          udelay(1);
     }
     err = e100_exec_cb(nic, skb, e100_xmit_prepare);
     switch(err) {
     case -ENOSPC:
          /* We queued the skb, but now we're out of space. */
          netif_stop_queue(netdev);
          break;
     case -ENOMEM:
          /* This is a hard error - log it. */
          DPRINTK(TX_ERR, DEBUG, "Out of Tx resources, returning skbn");
          netif_stop_queue(netdev);
          return 1;
     }
     netdev->trans_start = jiffies;
     return 0;
}
继续跟踪进 e100_exec_cb(nic, skb, e100_xmit_prepare);static inline int e100_exec_cb(struct nic *nic, struct sk_buff *skb,
     void (*cb_prepare)(struct nic *, struct cb *, struct sk_buff *))
{
     struct cb *cb;
     unsigned long flags;
     int err = 0;
     spin_lock_irqsave(&nic->cb_lock, flags);
     if(unlikely(!nic->cbs_avail)) {
          err = -ENOMEM;
          goto err_unlock;
     }
     //skb 推入环形发送缓冲
     //cb_to_use:发送缓冲当使位置
     cb = nic->cb_to_use;
     nic->cb_to_use = cb->next;
     nic->cbs_avail--;
     cb->skb = skb;
     if(unlikely(!nic->cbs_avail))
          err = -ENOSPC;
     cb_prepare(nic, cb, skb);
     /* Order is important otherwise we'll be in a race with h/w:
     * set S-bit in current first, then clear S-bit in previous. */
     cb->command |= cpu_to_le16(cb_s);
     wmb();
     cb->prev->command &= cpu_to_le16(~cb_s);
     //当发送数据不空。数剧全部发送
     while(nic->cb_to_send != nic->cb_to_use) {
          if(unlikely(e100_exec_cmd(nic, nic->cuc_cmd,
              nic->cb_to_send->dma_addr))) {
              /* Ok, here's where things get sticky. It's
              * possible that we can't schedule the command
              * because the controller is too busy, so
              * let's just queue the command and try again
              * when another command is scheduled. */
              break;
          } else {
              nic->cuc_cmd = cuc_resume;
              nic->cb_to_send = nic->cb_to_send->next;
          }
     }
err_unlock:
     spin_unlock_irqrestore(&nic->cb_lock, flags);
     return err;
}
这里们看,发送数据过程主由e100_exec_cmd完成。跟踪进去static inline int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr)
{
     unsigned long flags;
     unsigned int i;
     int err = 0;
     spin_lock_irqsave(&nic->cmd_lock, flags);
     /* Previous command is accepted when SCB clears */
     for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) {
          if(likely(!readb(&nic->csr->scb.cmd_lo)))
              break;
          cpu_relax();
          if(unlikely(i > (E100_WAIT_SCB_TIMEOUT >> 1)))
              udelay(5);
     }
     if(unlikely(i == E100_WAIT_SCB_TIMEOUT)) {
          err = -EAGAIN;
          goto err_unlock;
     }
     if(unlikely(cmd != cuc_resume))
          //数据存放地址放入应寄存器
writel(dma_addr, &nic->csr->scb.gen_ptr);
     //发送操作入控制寄存器
     writeb(cmd, &nic->csr->scb.cmd_lo);
err_unlock:
     spin_unlock_irqrestore(&nic->cmd_lock, flags);
     return err;
}
  从此以看。Intel 100M网卡发送数据处理,只需地址,命令入相应寄存器即

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多