【转载】高性能网络I/O框架-netmap源码分析(2)

来源:互联网 发布:跳转到一个页面的js 编辑:程序博客网 时间:2024/04/28 02:38

高性能网络I/O框架-netmap源码分析(2) 

作者:gfree.wind@gmail.com
原文地址:<http://blog.chinaunix.net/uid-23629988-id-3608622.html>

前面e1000_probe的分析,按照Linux驱动框架,接下来就该e1000_open。netmap并没有对e1000_open进行任何修改,而是改动了e1000_configure,其会被e1000_open及e1000_up调用。

e1000_configure的修改

按照惯例,还是先看diff文件

@@ -393,6 +397,10 @@ static void e1000_configure(struct e1000    e1000_configure_tx(adapter);    e1000_setup_rctl(adapter);    e1000_configure_rx(adapter);+#ifdef DEV_NETMAP+   if (e1000_netmap_init_buffers(adapter))+       return;+#endif /* DEV_NETMAP */    /* call E1000_DESC_UNUSED which always leaves    * at least 1 descriptor unused to make sure    * next_to_use != next_to_clean */ 

从diff文件可以看出,netmap替代了原有的e1000申请ring buffer的代码。如果e1000_netmap_init_buffers成功返回,e1000_configure就直接退出了。

接下来进入e1000_netmap_init_buffers:

/** Make the tx and rx rings point to the netmap buffers.*/static int e1000_netmap_init_buffers(struct SOFTC_T *adapter){    struct e1000_hw *hw = &adapter->hw;    struct ifnet *ifp = adapter->netdev;    struct netmap_adapter* na = NA(ifp);    struct netmap_slot* slot;    struct e1000_tx_ring* txr = &adapter->tx_ring[0];    unsigned int i, r, si;    uint64_t paddr;    /*     还记得前面的netmap_attach吗?    所谓的attach,即申请了netmap_adapter,并将net_device->ax25_ptr保存了指针,并设置了NETMAP_SET_CAPABLE。    因此这里做一个sanity check,以免影响正常的网卡驱动    */    if (!na || !(na->ifp->if_capenable & IFCAP_NETMAP))        return 0;    /* e1000_no_rx_alloc如其名,为一个不该调用的函数,只输出一行错误日志 */    adapter->alloc_rx_buf = e1000_no_rx_alloc;    for (r = 0; r < na->num_rx_rings; r++) {        struct e1000_rx_ring *rxr;        /* 初始化对应的netmap对应的ring */        slot = netmap_reset(na, NR_RX, r, 0);        if (!slot) {            D("strange, null netmap ring %d", r);            return 0;        }        /* 得到e1000对应的ring */        rxr = &adapter->rx_ring[r];        for (i = 0; i < rxr->count; i++) {            // XXX the skb check and cleanup can go away            struct e1000_buffer *bi = &rxr->buffer_info[i];            /* 将当前的buff索引转换为netmap的buff索引 */            si = netmap_idx_n2k(&na->rx_rings[r], i);            /* 获得netmap的buff的物理地址 */            PNMB(slot + si, &paddr);            if (bi->skb)                D("rx buf %d was set", i);            bi->skb = NULL;            // netmap_load_map(...)            /* 现在网卡的这个buffer已经指向了netmap申请的buff地址了 */            E1000_RX_DESC(*rxr, i)->buffer_addr = htole64(paddr);        }        rxr->next_to_use = 0;        /*         下面这几行代码没看明白怎么回事。        有明白的同学指点一下,多谢。        */        /* preserve buffers already made available to clients */        i = rxr->count - 1 - na->rx_rings[0].nr_hwavail;        if (i < 0)        i += rxr->count;        D("i now is %d", i);        wmb(); /* Force memory writes to complete */        writel(i, hw->hw_addr + rxr->rdt);    }    /*     初始化发送ring,与接收类似.    区别在于没有考虑发送多队列。难道是因为e1000只可能是接收多队列,发送只可能是一个队列?    这个问题不影响后面的代码阅读。咱们可以暂时将其假设为e1000只有一个发送队列    */    /* now initialize the tx ring(s) */    slot = netmap_reset(na, NR_TX, 0, 0);    for (i = 0; i < na->num_tx_desc; i++) {        si = netmap_idx_n2k(&na->tx_rings[0], i);        PNMB(slot + si, &paddr);        // netmap_load_map(...)        E1000_TX_DESC(*txr, i)->buffer_addr = htole64(paddr);    }    return 1;} 

e1000cleanrx_irq的修改

@@ -3952,6 +3973,11 @@ static bool e1000_clean_rx_irq(struct e1    bool cleaned = false;    unsigned int total_rx_bytes=0, total_rx_packets=0;+#ifdef DEV_NETMAP+   ND("calling netmap_rx_irq");+   if (netmap_rx_irq(netdev, 0, work_done))+       return 1; /* seems to be ignored */+#endif /* DEV_NETMAP */    i = rx_ring->next_to_clean;    rx_desc = E1000_RX_DESC(*rx_ring, i);    buffer_info = &rx_ring->buffer_info[i]; 

进入netmap_rx_irq, int netmaprxirq(struct ifnet *ifp, int q, int *workdone) { struct netmapadapter *na; struct netmap_kring *r; NMSELINFOT *main_wq;

 if (!(ifp->if_capenable & IFCAP_NETMAP))        return 0;    na = NA(ifp);    /*     尽管函数名为rx,但实际上这个函数服务于rx和tx两种情况,用work_done做区分。    */    if (work_done) { /* RX path */        r = na->rx_rings + q;        r->nr_kflags |= NKR_PENDINTR;        main_wq = (na->num_rx_rings > 1) ? &na->rx_si : NULL;    } else { /* tx path */        r = na->tx_rings + q;        main_wq = (na->num_tx_rings > 1) ? &na->tx_si : NULL;        work_done = &q; /* dummy */    }    /*     na->separate_locks只在ixgbe和bridge中会被设置为1。    根据下面的代码,这个separate_locks表示多队列时,是每个队列使用一个锁。——这样可以提高性能    其余的代码基本相同。都是唤醒等待数据的进程。     */    if (na->separate_locks) {        mtx_lock(&r->q_lock);        selwakeuppri(&r->si, PI_NET);        mtx_unlock(&r->q_lock);        if (main_wq) {            mtx_lock(&na->core_lock);            selwakeuppri(main_wq, PI_NET);            mtx_unlock(&na->core_lock);        }    } else {        mtx_lock(&na->core_lock);        selwakeuppri(&r->si, PI_NET);        if (main_wq)            selwakeuppri(main_wq, PI_NET);        mtx_unlock(&na->core_lock);    }    *work_done = 1; /* do not fire napi again */    return 1;} 


发送部分的修改与接收类似,就不重复了。
今天的学习,到此为止 (未完待续。。。)

0 0
原创粉丝点击