设为首页 收藏本站
查看: 1575|回复: 0

[经验分享] xen网络前端驱动代码分析(发送篇)

[复制链接]

尚未签到

发表于 2015-10-12 07:22:50 | 显示全部楼层 |阅读模式
  发送的核心函数为 xennet_start_xmit
  

  static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    unsigned short id;
    struct netfront_info *np = netdev_priv(dev);
    struct xen_netif_tx_request *tx;
    struct xen_netif_extra_info *extra;
    char *data = skb->data;
    RING_IDX i;
    grant_ref_t ref;
    unsigned long mfn;
    int notify;
    int frags = skb_shinfo(skb)->nr_frags;
    unsigned int offset = offset_in_page(data);
    unsigned int len = skb_headlen(skb);

      spin_lock_irq(&np->tx_lock);
      if (unlikely(!netif_carrier_ok(dev) ||
             (frags > 1 && !xennet_can_sg(dev)) ||
             netif_needs_gso(dev, skb))) {
        spin_unlock_irq(&np->tx_lock);
        goto drop;
    }

      i = np->tx.req_prod_pvt;
  
    id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
    np->tx_skbs[id].skb = skb;

  这里通过get_id_from_freelist从tx_skb_freelist中获得一个freelist id。我们知道netfront_info->tx_skbs是一个union skb_entry类型的数组,如果这个slot为空那么就存放一个index id,否则是一个skb指针。所有free slot的index id自动形成一个list,head就是netfront_info->tx_skb_freelist
  
    tx = RING_GET_REQUEST(&np->tx, i);
通过netfront_info->tx.req_prod_pvt拿到xen_netif_tx_request

    tx->id   = id;
    ref = gnttab_claim_grant_reference(&np->gref_tx_head);
    BUG_ON((signed short)ref < 0);
    mfn = virt_to_mfn(data);
    gnttab_grant_foreign_access_ref(
        ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly);
    tx->gref = np->grant_tx_ref[id] = ref;
    tx->offset = offset;
    tx->size = len;
    extra = NULL;

  生成xen_netif_tx_request结构体,tx->id为tx_skbs中的一个空闲index,tx->gref和netfront_info->grant_tx_ref[id]相同,都是virt_to_mfn(skb->data)得到的skb线性区域的page地址,tx->offset为skb->data在page中的offset,tx->size为skb线性区域的大小
      tx->flags = 0;
    if (skb->ip_summed == CHECKSUM_PARTIAL)
        /* local packet? */
        tx->flags |= NETTXF_csum_blank | NETTXF_data_validated;
    else if (skb->ip_summed == CHECKSUM_UNNECESSARY)
        /* remote but checksummed. */
        tx->flags |= NETTXF_data_validated;

    if (skb_shinfo(skb)->gso_size) {
        struct xen_netif_extra_info *gso;

  如果skb_shinfo(skb)是一个gso skb,那么需要生成相应的xen_netif_extra_info结构体
  
        gso = (struct xen_netif_extra_info *)
            RING_GET_REQUEST(&np->tx, &#43;&#43;i);

        if (extra)
            extra->flags |= XEN_NETIF_EXTRA_FLAG_MORE;
        else
            tx->flags |= NETTXF_extra_info;

  
        gso->u.gso.size = skb_shinfo(skb)->gso_size;
        gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4;
        gso->u.gso.pad = 0;
        gso->u.gso.features = 0;

        gso->type = XEN_NETIF_EXTRA_TYPE_GSO;
        gso->flags = 0;
        extra = gso;
    }

      np->tx.req_prod_pvt = i &#43; 1;

    xennet_make_frags(skb, dev, tx);
    tx->size = skb->len;

  xennet_make_frags用来把skb线性区域以及skb_shinfo(skb)->frags中的skb_frag_t指向的数据以page为单位生成xen_netif_tx_request结构体,并把这些xen_netif_tx_request放到tx IO ring中

    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify);
    if (notify)
        notify_remote_via_irq(np->netdev->irq);
通知后端有数据需要发送

    dev->stats.tx_bytes &#43;= skb->len;
    dev->stats.tx_packets&#43;&#43;;

    /* Note: It is not safe to access skb after xennet_tx_buf_gc()! */
    xennet_tx_buf_gc(dev);
用来处理tx IO ring中的rsp_prod生成的xen_netif_tx_response

    if (!netfront_tx_slot_available(np))
        netif_stop_queue(dev);

    spin_unlock_irq(&np->tx_lock);

    return NETDEV_TX_OK;

drop:
    dev->stats.tx_dropped&#43;&#43;;
    dev_kfree_skb(skb);
    return NETDEV_TX_OK;
}

  

  下面来看两个辅助函数,xennet_make_frags,xennet_tx_buf_gc
  static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                  struct xen_netif_tx_request *tx)
{
    struct netfront_info *np = netdev_priv(dev);
    char *data = skb->data;
    unsigned long mfn;
    RING_IDX prod = np->tx.req_prod_pvt;
    int frags = skb_shinfo(skb)->nr_frags;
    unsigned int offset = offset_in_page(data);
    unsigned int len = skb_headlen(skb);
    unsigned int id;
    grant_ref_t ref;
    int i;

      /* While the header overlaps a page boundary (including being larger than a page), split it it into page-sized chunks. */
      while (len > PAGE_SIZE - offset) {
        tx->size = PAGE_SIZE - offset;
        tx->flags |= NETTXF_more_data;
        len -= tx->size;
        data &#43;= tx->size;
        offset = 0;

        id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
        np->tx_skbs[id].skb = skb_get(skb);
        tx = RING_GET_REQUEST(&np->tx, prod&#43;&#43;);
        tx->id = id;
        ref = gnttab_claim_grant_reference(&np->gref_tx_head);
        BUG_ON((signed short)ref < 0);

        mfn = virt_to_mfn(data);
        gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id,
                        mfn, GNTMAP_readonly);

        tx->gref = np->grant_tx_ref[id] = ref;
        tx->offset = offset;
        tx->size = len;
        tx->flags = 0;
    }

  上面这个循环用于处理skb线性区域。先对offset -> PAGE_SIZE的部分生成一个xen_netif_tx_request。之后只要len还多,就对0 -> PAGE_SIZE(此时offset为0)的部分一个个生成xen_netif_tx_request,所有这些xen_netif_tx_request被依次append到tx IO ring中
      /* Grant backend access to each skb fragment page. */
    for (i = 0; i < frags; i&#43;&#43;) {
        skb_frag_t *frag = skb_shinfo(skb)->frags &#43; i;

        tx->flags |= NETTXF_more_data;

        id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
        np->tx_skbs[id].skb = skb_get(skb);
        tx = RING_GET_REQUEST(&np->tx, prod&#43;&#43;);
        tx->id = id;
        ref = gnttab_claim_grant_reference(&np->gref_tx_head);
        BUG_ON((signed short)ref < 0);

        mfn = pfn_to_mfn(page_to_pfn(frag->page));
        gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id,
                        mfn, GNTMAP_readonly);

        tx->gref = np->grant_tx_ref[id] = ref;
        tx->offset = frag->page_offset;
        tx->size = frag->size;
        tx->flags = 0;
    }

  这里对skb非线性区域,skb_shinfo(skb)->frags的部分进行处理。对每一个skb_frag_t(除了最后一个),都有tx->flags |= NETTXF_more_data,之后的步骤同前面

    np->tx.req_prod_pvt = prod;

  增加对应的tx.req_prod_pvt的&#20540;,总共增加了prod个xen_netif_tx_request
}

  

  xennet_tx_buf_gc用来处理xen_netif_tx_response,
  static void xennet_tx_buf_gc(struct net_device *dev)
{
    RING_IDX cons, prod;
    unsigned short id;
    struct netfront_info *np = netdev_priv(dev);
    struct sk_buff *skb;

    BUG_ON(!netif_carrier_ok(dev));

      do {
        prod = np->tx.sring->rsp_prod;
        rmb(); /* Ensure we see responses up to 'rp'. */

        for (cons = np->tx.rsp_cons; cons != prod; cons&#43;&#43;) {
            struct xen_netif_tx_response *txrsp;

            txrsp = RING_GET_RESPONSE(&np->tx, cons);
            if (txrsp->status == NETIF_RSP_NULL)
                continue;

            id  = txrsp->id;
            skb = np->tx_skbs[id].skb;
            if (unlikely(gnttab_query_foreign_access(
                np->grant_tx_ref[id]) != 0)) {
                printk(KERN_ALERT &quot;xennet_tx_buf_gc: warning &quot;
                       &quot;-- grant still in use by backend &quot;
                       &quot;domain.\n&quot;);
                BUG();
            }
            gnttab_end_foreign_access_ref(
                np->grant_tx_ref[id], GNTMAP_readonly);
            gnttab_release_grant_reference(
                &np->gref_tx_head, np->grant_tx_ref[id]);
            np->grant_tx_ref[id] = GRANT_INVALID_REF;
            add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, id);
            dev_kfree_skb_irq(skb);

  依次从tx.rsp_cons到tx.sring->rsp_prod遍历,取出xen_netif_tx_response,如果txrsp->status不为NETIF_RSP_NULL,则基于txrsp->id释放netfront_info->tx_skbs, netfront_info->grant_tx_ref[id]的资源,把id加到np->tx_skb_freelist中,同时调用dev_kfree_skb_irq(skb),该函数释放一个skbrefcnt,直到refcnt为0后把skb加到softnet_data->completion中,表示这个skb发送成功。
  注意对于存在多个skb_frag_t的skb,每个对应的xen_netif_tx_response都会递减一个skb refcnt,最终这个skb被回收
          }

          np->tx.rsp_cons = prod;

          np->tx.sring->rsp_event =
            prod &#43; ((np->tx.sring->req_prod - prod) >> 1) &#43; 1;
        mb();       /* update shared area */
    } while ((cons == prod) && (prod != np->tx.sring->rsp_prod));

    xennet_maybe_wake_tx(dev);

  xennet_maybe_wake_tx判断net_device是否可用以及当前是否有slot来发送一个最大frags的skb,如果可以则调用netif_tx_wake_queue调度netdev_queue->qdisc队列继续发送
}

  

         版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-125574-1-1.html 上篇帖子: Xen添加hypercall获取page_fault次数 下篇帖子: CentOS6下快速配置Xen 4.1.1环境
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表