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