Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1694489
  • 博文数量: 511
  • 博客积分: 967
  • 博客等级: 准尉
  • 技术积分: 2560
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-06 14:19
文章分类

全部博文(511)

文章存档

2016年(11)

2015年(61)

2014年(257)

2013年(63)

2012年(119)

分类: LINUX

2014-05-17 22:22:59

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
   

前文已经学习到了sch_direct_xmit函数,下面进入下一级函数。
  1. int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
  2.             struct netdev_queue *txq)
  3. {
  4.     const struct net_device_ops *ops = dev->netdev_ops;
  5.     int rc = NETDEV_TX_OK;

  6.     if (likely(!skb->next)) {
         /* skb->next为null,即只发送一个skb */

  1.         if (!list_empty(&ptype_all))
  2.             dev_queue_xmit_nit(skb, dev); //ptype_all上的协议处理,如tcpdump

  3.         /*
  4.          * If device doesnt need skb->dst, release it right now while
  5.          * its hot in this cpu cache
  6.          */
  7.         /* device 不需要dst */
  8.         if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
  9.             skb_dst_drop(skb);

         /* 使该skb不属于任何一个socket */
  1.         skb_orphan_try(skb);

  2.         if (netif_needs_gso(dev, skb)) {
  3.             /* 需要scatter gather功能 */
             
              /*
              从注释上看,是进行perform protocol segmentation on skb。
              但是实际工作,目前我不清楚
              */
  1.             if (unlikely(dev_gso_segment(skb)))
  2.                 goto out_kfree_skb;
  3.             if (skb->next)
  4.                 goto gso;
  5.         } else {
  6.             //不需要scatter gather
            
             /*
             如需要连续内存,则将skb内存线性化,即连续内存 
             */
  1.             if (skb_needs_linearize(skb, dev) &&
  2.              __skb_linearize(skb))
  3.                 goto out_kfree_skb;

  4.             /* If packet is not checksummed and device does not
  5.              * support checksumming for this protocol, complete
  6.              * checksumming here.
  7.              */
  8.             if (skb->ip_summed == CHECKSUM_PARTIAL) {
  9.                 /* 计算checksum */
  10.                 skb_set_transport_header(skb, skb->csum_start -
  11.                      skb_headroom(skb));
  12.                 if (!dev_can_checksum(dev, skb) &&
  13.                  skb_checksum_help(skb))
  14.                     goto out_kfree_skb;
  15.             }
  16.         }

         /* 发送skb数据 */
  1.         rc = ops->ndo_start_xmit(skb, dev);
  2.         if (rc == NETDEV_TX_OK)
  3.             txq_trans_update(txq);
  4.         return rc;
  5.     }

  6. gso:
  7.     /* 循环发送数据包 */
  8.     do {
  9.         struct sk_buff *nskb = skb->next;

  10.         skb->next = nskb->next;
  11.         nskb->next = NULL;

  12.         /*
  13.          * If device doesnt need nskb->dst, release it right now while
  14.          * its hot in this cpu cache
  15.          */
  16.         if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
  17.             skb_dst_drop(nskb);

  18.         rc = ops->ndo_start_xmit(nskb, dev);
  19.         if (unlikely(rc != NETDEV_TX_OK)) {
  20.             if (rc & ~NETDEV_TX_MASK)
  21.                 goto out_kfree_gso_skb;
  22.             nskb->next = skb->next;
  23.             skb->next = nskb;
  24.             return rc;
  25.         }
  26.         txq_trans_update(txq);
  27.         if (unlikely(netif_tx_queue_stopped(txq) && skb->next))
  28.             return NETDEV_TX_BUSY;
  29.     } while (skb->next);

  30. out_kfree_gso_skb:
  31.     if (likely(skb->next == NULL))
  32.         skb->destructor = DEV_GSO_CB(skb)->destructor;
  33. out_kfree_skb:
  34.     kfree_skb(skb);
  35.     return rc;
  36. }
继续下一个函数,ops->ndo_start_xmit。又是一个回调函数,这个回调函数由驱动所决定。以e1000_main.c为例,
  1. static const struct net_device_ops e1000_netdev_ops = {
  2.     .ndo_open        = e1000_open,
  3.     .ndo_stop        = e1000_close,
  4.     .ndo_start_xmit        = e1000_xmit_frame,
  5.     .ndo_get_stats        = e1000_get_stats,
  6.     .ndo_set_rx_mode    = e1000_set_rx_mode,
  7.     .ndo_set_mac_address    = e1000_set_mac,
  8.     .ndo_tx_timeout     = e1000_tx_timeout,
  9.     .ndo_change_mtu        = e1000_change_mtu,
  10.     .ndo_do_ioctl        = e1000_ioctl,
  11.     .ndo_validate_addr    = eth_validate_addr,

  12.     .ndo_vlan_rx_register    = e1000_vlan_rx_register,
  13.     .ndo_vlan_rx_add_vid    = e1000_vlan_rx_add_vid,
  14.     .ndo_vlan_rx_kill_vid    = e1000_vlan_rx_kill_vid,
  15. #ifdef CONFIG_NET_POLL_CONTROLLER
  16.     .ndo_poll_controller    = e1000_netpoll,
  17. #endif
  18. };
从这里可以看出,对于e1000_main来说,该回调函数为e1000_xmit_frame。这个涉及到驱动层,一般都与硬件相关,在此就不继续探讨了。

那么现在,一个基本的IP包发送流程,基本上浏览完毕。小小的总结一下,当linux发送IP数据包时,数据包有可能是立刻发送出去,也有可能由下一次的软中断发送。






阅读(523) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~