日期:2014-05-16  浏览次数:20953 次

OpenWRT数据发送过程【Linux内核-OpenWRT】

之前一篇写的不完整,重新写一篇

OpenWRT数据发送过程 这里使用的是ath9k网卡驱动,硬件平台是TP-link TL-WR841N V7.1 路由器

1.  packet_sendmsg()
    Linux kernel发送数据的接口函数是packet_sendmsg,本质上对应了user space的sendmsg实现。上层通过调用sendmsg实现数据的发送。将待发送的数据放入kernel space中。
    在内核文件夹linux-3.3.8的子目录:/net/packet中,找到文件af_packet.c,这个文件里定义了如下一个结构:

static const struct proto_ops packet_ops = {
    …
	.sendmsg =      packet_sendmsg,
    .recvmsg =      packet_recvmsg,
	…
};
这个结构采用了C99标准的初始化方式,基本用法是“.成员=变量值”(已经预先定义了一个proto_ops结构,里面包含sendmsg函数指针)。这个结构中把上层的sendmsg函数和下层的packet_sendmsg函数对应了起来。上层通过调用sendmsg就将数据传递给了packet_sendmsg函数。下面我们就从内核态的packet_sendmsg出发来研究一下数据的发送过程。
下面来看看packet_sendmsg()的实现(位于linux-3.3.8/net/packet文件夹下的af_packet.c文件中)
static int packet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
{
    struct sock *sk = sock->sk;
    struct packet_sock *po = pkt_sk(sk);
    if (po->tx_ring.pg_vec)
        return tpacket_snd(po, msg);
    else
        return packet_snd(sock, msg, len);
}

2.  packet_snd()
调用packet_snd()(位于linux-3.3.8/net/packet文件夹下的af_packet.c文件中)。
static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
{
    ...
    // 首先把数据从user space拷贝到kernel space
    err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov, vnet_hdr_len);
    ...
    /*
     *    Now send it
     */
    // 然后用dev_queue_xmit()来发送skb.
    err = dev_queue_xmit(skb);
    if (err > 0 && (err = net_xmit_errno(err)) != 0)
        goto out_unlock;
    ...
}

3.  dev_queue_xmit()
调用dev_queue_xmit()(位于linux-3.3.8/net/core文件夹下的dev.c文件中)。
int dev_queue_xmit(struct sk_buff *skb)
{
    struct net_device *dev = skb->dev;
    struct netdev_queue *txq;
    struct Qdisc *q;
    int rc = -ENOMEM;
    skb_reset_mac_header(skb);
    /* Disable soft irqs for various locks below. Also
     * stops preemption for RCU.
     */
    rcu_read_lock_bh();
    skb_update_prio(skb);
    txq = netdev_pick_tx(dev, skb);
    q = rcu_dereference_bh(txq->qdisc);
#ifdef CONFIG_NET_CLS_ACT
    skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
    trace_net_dev_queue(skb);
    if (q->enqueue) {
        rc = __dev_xmit_skb(skb, q, dev, txq);
        goto out;
    }
    ...
}

4.  __dev_xmit_skb()
调用__dev_xmit_skb()(位于linux-3.3.8/net/core文件夹下的dev.c文件中)。
static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, struct net_device *dev, struct netdev_queue *txq)
{
    ...
    if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
        kfree_skb(skb);
        rc = NET_XMIT_DROP;
    } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
           qdisc_run_begin(q)) {
        /*
         * This is a work-conserving queue; there are no old skbs
         * waiting to be sent out; and the qdisc is not running -
         * xmit the skb directly.
         */
        if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE))
            skb_dst_force(skb);
        qdisc_bstats_update(q, skb);
        // 注意这里
        if (sch_direct_xmit(skb, q, dev, txq, root_lock)) {
            if (unlikely(