xen网络前端驱动代码分析(发送篇)

发送的核心函数为 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, ++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 + 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 += skb->len;
    dev->stats.tx_packets++;

    /* 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++;
    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 += 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++);
        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++) {
        skb_frag_t *frag = skb_shinfo(skb)->frags + 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++);
        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的值,总共增加了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++) {
            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 "xennet_tx_buf_gc: warning "
                       "-- grant still in use by backend "
                       "domain.\n");
                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[i], netfront_info->grant_tx_ref[id]的资源,把id加到np->tx_skb_freelist中,同时调用dev_kfree_skb_irq(skb),该函数释放一个skb refcnt,直到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 + ((np->tx.sring->req_prod - prod) >> 1) + 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队列继续发送
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值