linux网络实现分析(3)——数据包的发送(IP层到链路层)

  • 时间:
  • 浏览:0
  • 来源:UU直播快三官方_大发UU直播快3

               set by sender, so that the second statement is

    struct netdev_queue *txq;

            if (unlikely(dev_gso_segment(skb)))

        return 0;  //返回0说明队列是空的咋样让被限制

static const struct net_device_ops loopback_ops = {

    return rc;

        goto out;

         * 2. we've been doing it for too long.

    txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));

    if (ret && (netif_tx_queue_stopped(txq) ||

        __qdisc_run(q);

    dev_queue_xmit

        !(dev->features & NETIF_F_FRAGLIST) &&

{

                if (net_ratelimit())//net_ratelimit用来保证网络代码中printk的频率

         * waiting to be sent out; and the qdisc is not running -

            dev_queue_xmit_nit(skb, dev);

        lb_stats->bytes += len;

//咋样让数据包不可不都可不都可不可不可以 被计算校验和咋样让发送设备不支持你两种 协议的校验,则在此进行校验和的计算(注1)。咋样让里面咋样让线性化了一次,这里的__skb_linearize就会直接返回,注意区别fragsfrag_list,前者是将多的数据放上单独分配的页面中,sk_buff不可不都可不都可不可不可以 八个。而后者则是连接多个sk_buff 

通常netif_tx_lock使用来保本设备驱动的顺序(独占)访问的,qdisc_lock(q)用来保证qdisc的顺序访问,你两种 个是互斥的,获得其中八个都可不都可不可不可以 释放曾经。

    if (!(skb->tstamp.tv64 && (G_TC_FROM(skb->tc_verd) & AT_INGRESS)))

                goto gso;

对于环回设备loopback,设备的ops->ndo_start_xmit被初始化为loopback_xmit函数。

        ret = qdisc_qlen(q); //返回剩余的队列长度

            if (net_ratelimit())

    int rc = -ENOMEM;

    list_for_each_entry_rcu(ptype, &ptype_all, list) {

            goto out_kfree_skb;

        ret = 0;

        if (netif_needs_gso(dev, skb)) {

    .ndo_start_xmit= loopback_xmit,

_dev_xmit_skb函数主要做两件事情:

    图中绿色每种表示(1)(3)两处spinlock。首先看(1)处对应的代码:

1. CHECKSUM_PARTIAL表示使用硬件checksum ,L4头的校咋样让完,咋样让咋样让加入uh->check字段中,只都可不都可不可不可以 设备算整个头4头的校值。

    return rc;

        ret = dev_requeue_skb(skb, q);

            clear_bit(__QDISC_STATE_RUNNING, &q->state);

//取舍八个发送列,咋样让设备提供了select_queue就使用它,否取舍八个,里也不Linux核多列的实,咋样让要真正的使用都列,都可不都可不可不可以 网卡支持多列都可不都可不可不可以 不可不都可不都可不可不可以 ,一般的网卡都不可不都可不都可不可不可以 八个列。在alloc_etherdev分配net_device是,设置列的个

                printk(KERN_CRIT "Dead loop on virtual device "

    net_timestamp(skb);

            if (net_ratelimit())

(struct sock *)ptype->af_packet_priv != skb->sk)) {

   sch_direct_xmit

    spinlock_t *root_lock = qdisc_lock(q);//见注2

    struct netdev_queue *txq;

另外,内核代码注释中写到,(1)和(3)是互斥的,获得(1)处的锁时都可不都可不可不可以 先保证释放(3)处的锁,反之亦然,为有哪些要曾经还不可不都可不都可不可不可以 想明白。。。。

        goto gso;

}

    本函数用来将带发送的skb加入八个dev的队列(Queue),调用你两种 函数前都可不都可不可不可以 设置好skb的device和priority,本函数能不可不都可不都可不可不可以 在中断上下文中被调用。

out_kfree_skb:

    q = rcu_dereference(txq->qdisc);

3. 咋样让有了dev_queue_xmit有哪些都可不都可不可不可以 断来发送呢?

static inline void qdisc_run(struct Qdisc *q)

}

    len = skb->len;

}

       //咋样让发送设备不都可不都可不可不可以 skb->dst,则在此将其释放

    rcu_read_lock_bh();

(1)咋样让流控对象为空的,试图直接发送数据包。

        qdisc_run(q);

#ifdef CONFIG_NET_CLS_ACT

    pcpu_lstats = dev->ml_priv;

static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)

int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,

{

    root_lock = qdisc_lock(q);

    struct net_device *dev = skb->dev;

            netif_tx_queue_frozen(txq)))

}

    rcu_read_lock();

}

    skb = dequeue_skb(q); //一现在现在现在开始就调用dequeue函数

    /* GSO will handle the following emulations directly. */

            ptype->func(skb2, skb->dev, ptype, skb->dev);//调用协议(ptype_all)接受函数

    switch (ret) {

而(3)处的spinlock,即struct netdev_queue中的_xmit_lock,则用于保证dev的注册函数的互斥访问,即deriver的同步。

此函返回值:发送成功返回剩余列长度,发送失败时返回0(若发送成功且剩余列长度0也返回0

    struct pcpu_lstats *pcpu_lstats, *lb_stats;

    函数执行后传入的skb将被释放,也不咋样让想控制数据包,实现对skb的重传时都可不都可不可不可以 增加skb的引用计数。

        else

        rc = NET_XMIT_SUCCESS;

void __qdisc_run(struct Qdisc *q)

                       "%s, fix it urgently!\n", dev->name);

    default: //设备繁忙,重新入队发送(利用softirq

                break;

    int rc;

        if (!list_empty(&ptype_all))

//咋样让该设备有队列可用,就调用__dev_xmit_skb

    spin_lock(root_lock);

            struct netdev_queue *txq)

        当上层准备好八个包要我,交给链路层,链路层数据包发送主要通过dev_queue_xmit函数处置。数据包的发送可分为两种,两种是正常的传输流程,即通过网卡驱动,另两种是通过软中断(见注3)。为了理解方便,首先看一下dev_queue_xmi函数的整体调用关系图。

    .ndo_init      = loopback_dev_init,

//咋样让skb有分片咋样让发送设备不支持分片,或分片包含分片在高端内存但发送设备不支持DMA,都可不都可不可不可以 将所有段重新组合成八个段 ,这里__skb_linearize随便说说也不__pskb_pull_tail(skb, skb->data_len),你两种 函数基本上等同于pskb_may_pull  pskb_may_pull的作用也不检测skb对应的主buf包含无有足够的空间来pulllen长度,咋样让不足英文就重新分配skb并将frags中的数据拷贝入新分配的主buff中,而这里将参数len设置为skb->datalen 也也不会将所有的数据完整拷贝到主buff中,以你两种 土法律法子完成skb的线性化。

    }

            struct net_device *dev, struct netdev_queue *txq,

#endif

{

2. 软中断服务系统程序运行运行NET_TX_SOFTIRQ

    if (!netif_tx_queue_stopped(txq) && //设备不可不都可不都可不可不可以 被停止,且发送队列不可不都可不都可不可不可以 被冻结

    spinlock_t *root_lock;

//关闭软中断,禁止cpu抢占

   dev_queue_xmit_nit

        break;

2test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)

    lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id());

……

    /* it's OK to use per_cpu_ptr() because BHs are off */

    unsigned long start_time = jiffies;

        (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&

            (ptype->af_packet_priv == NULL ||

    dev = qdisc_dev(q);

         */

                 struct net_device *dev)

    }

    int rc;

        goto out_kfree_skb;

      返回非0(正数或负数)表示函数出错,返回0表示成功,咋样让未必表示数据包被成功发送出去,咋样让数据包咋样让咋样让限速等导致 被丢掉

        rc = ops->ndo_start_xmit(skb, dev);

                    HARD_TX_UNLOCK(dev, txq);

            skb2->pkt_type = PACKET_OUTGOING;

         * Postpone processing if  (延迟处置)

static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,

(2)咋样让流控对象不空,将数据包加入流控对象,并运行流控对象。

    }

         * xmit the skb directly.

}

    if (skb_shinfo(skb)->nr_frags &&

    if (!test_and_set_bit(__QDISC_STATE_RUNNING, &q->state))//将队列设置为运行状态

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,

    rcu_read_unlock();

2. 整个数据包发送逻辑中会涉及到八个用于互斥访问的代码:

        if (unlikely (ret != NETDEV_TX_BUSY && net_ratelimit()))

        if ((ptype->dev == dev || !ptype->dev) &&

gso:

            /* skb->nh should be correctly(确保头部偏移正确)

            break;

        rc = NET_XMIT_DROP;

    const struct net_device_ops *ops = dev->netdev_ops;

    if (unlikely(!skb))

也不root_lock是用于控制qdiscskb队列访问的锁,当都可不都可不可不可以 对skb队列进行enqueue、dequeue、requeue时,就都可不都可不可不可以 加锁。

               just protection against buggy protocols.

static netdev_tx_t loopback_xmit(struct sk_buff *skb,

        if (dev->priv_flags & IFF_XMIT_DST_RELEASE)

                   dev->name, ret, q->q.qlen);

注:

}

1.__dev_xmit_skb()

    if (dev->flags & IFF_UP) { //取舍设备算不算开启

        __skb_linearize(skb))

            if (!skb2)

    struct Qdisc *q;

            __qdisc_run(q);

环调qdisc_restart发送,下面个函qdisc_restart是真正发送据包的函,它从列上取下八个,咋样让尝试将它发送出去,若发送失败则一般是重新入

out:

__QDISC_STATE_RUNNING标志用于保证八个流控对象(qdisc不想并肩被多个cpu访问。

        }

   qdisc_restart

返回值:

};

                    printk(KERN_CRIT "protocol %04x is "

static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc)

        return rc;

        !netif_tx_queue_frozen(txq))

{

         * 1. another process needs the CPU;

也不发送列中的skb以及放咋样让发送的skb,它不想再skb线性化咋样让校和处置另外在列被停止的状态下,dev_queue_xmit仍然能不可不都可不都可不可不可以 把包加入列,咋样让不可不都可不都可不可不可以 发送样在列被醒的候就都可不都可不可不可以 通过软断来发送停止期间积压的包而言之,dev_queue_xmitskb做些最后的处置咋样让第一次尝试发送,前者发送失咋样让发完的包发送出去。(随便说说发送断还有八个作用,也不放咋样让发送的包,因有些状态架构设计 送是在硬件中中完成的,了提高硬件中处置强度,核提供两种土法律法子将释skb放上行,这时若果dev_kfree_skb_irq,它skb加入softnet_datacompletion_queue中,咋样让启发送net_tx_action会在completion_queue中的skb完整放掉)

    struct net_device *dev;

{

            if (skb_network_header(skb2) < skb2->data ||

    HARD_TX_LOCK(dev, txq, smp_processor_id());

if (likely(!skb->next)) {

static inline int qdisc_restart(struct Qdisc *q)

        break;

         */

    }

        if (!dev_can_checksum(dev, skb) && skb_checksum_help(skb))

// netdev_queue构上取设备的qdisc 

    case NETDEV_TX_OK:  //咋样让设备成功将数据包发送出去

                skb2->network_header > skb2->tail) {

                goto out_kfree_skb;

                skb_reset_network_header(skb2); //重新设置L3头部偏移

有八个时机咋样让调用qdisc_run():

                    goto out;

{

    return ret;



                       "queue packet!\n", dev->name);

                if (!dev_hard_start_xmit(skb, dev, txq)) {

        }

            }

    if (skb->ip_summed == CHECKSUM_PARTIAL) {

    /* Dequeue packet */

txq = dev_pick_tx(dev, skb);

能不可不都可不都可不可不可以 看得人在dev_queue_xmitskb行了有些处置(比如合并成八个包,算校和等),处置完的skb是能不可不都可不都可不可不可以 直接发送的了,这时dev_queue_xmit也会先skbskb一般时会 在个函中入的),咋样让qdisc_run尝试发送,咋样让有咋样让发送失这时skb重新入,咋样让此人 直接返回。

{

    } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&

        goto out_kfree_skb;

    case NETDEV_TX_LOCKED: //获取设备锁失败

    if (netif_needs_gso(dev, skb))

        /*咋样让发现本队列运行的时间太长了,咋样让停止队列的运行,并将队列加入output_queue链表头

    return rc;

        if (sch_direct_xmit(skb, q, dev, txq, root_lock))

            skb_reset_mac_header(skb2);

           !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) {

    }

        lb_stats->packets++;

{

    struct sk_buff *skb;

    int len;

        if (need_resched() || jiffies != start_time) { //咋样让不允许继续运行本流控对象

//下面的处置是在有发送列的状态,设备一般有发送列:如lotunnle我们我们我们我们我们我们我们我们我们所要做的也不直接调用驱动的hard_start_xmit将它发送出去  咋样让发送失败就直接丢弃,咋样让不可不都可不都可不可不可以 队列能不可不都可不都可不可不可以 保存它  

             */

    当调用此函数时中断都可不都可不可不可以 是打开的,咋样让BH enable都可不都可不可不可以 要求IRQ enable,咋样让会造成死锁

        }

    skb->protocol = eth_type_trans(skb, dev);

        }

        ret = dev_hard_start_xmit(skb, dev, txq); //发送数据包

   // 调用__netif_tx_lockà spin_lock(&txq->_xmit_lock,,保证设备驱动的独占访问

                          skb_headroom(skb));

    return NETDEV_TX_OK;

                 struct net_device *dev,

                rc = NET_XMIT_SUCCESS;

            skb_dst_drop(skb);

    int ret = NETDEV_TX_BUSY;

            printk(KERN_WARNING "BUG %s code %d qlen %d\n",

        if (rc == NETDEV_TX_OK)

    if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {//判断队列算不算失效

   qdisc_run

    while (qdisc_restart(q)) { //返回值大于0,说明流控对象非空

    clear_bit(__QDISC_STATE_RUNNING, &q->state);

         * This is a work-conserving queue; there are no old skbs

                           skb2->protocol, dev->name);

#else

}

   __qdisc_run

   dev_hard_start_xmit

            __netif_schedule(q); //将本qdisc加入每cpu变量softnet_dataoutput_queue链表中

 __dev_xmit_skb

}

            HARD_TX_UNLOCK(dev, txq);

__QDISC_STATE_RUNNING状态保证同一时刻不可不都可不都可不可不可以 八个cpu在处置你两种 qdisc,qdisc_lock(q)用来保证对你两种 队列的顺序访问。

                printk(KERN_CRIT "Virtual device %s asks to "

        lb_stats->drops++;

        break;

    }

            struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);

    if (q->enqueue) {



        __qdisc_update_bstats(q, skb->len);

        net_timestamp(skb); //记录该数据包输入的时间戳

    if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { //直接调用了netif_rx进行了接收处置

Ø  环回设备

    spin_unlock(root_lock);

            skb2->transport_header = skb2->network_header;

        kfree_skb(skb);

    } else

    } else {

            if (skb->next)

    return &qdisc->q.lock;

    spin_unlock(root_lock);// release qdisc,咋样让里面要获取设备锁

            spinlock_t *root_lock)

    }

        rc = qdisc_enqueue_root(skb, q);

    skb_orphan(skb);

int dev_queue_xmit(struct sk_buff *skb)

            HARD_TX_LOCK(dev, txq, cpu);

drivers/net/loopback.c

{

//从这能不可不都可不都可不可不可以 不可不都可不都可不可不可以 看出,对于每八个发送的包也会发给ptype_all一份,  packet套接字创建时对于protoETH_P_ALL的会在ptype_all中注册八个成员,咋样让对于协议号为ETH_P_ALLpacket套接字来说,发送和接受的数据都能收到

    rc = -ENETDOWN;

gso:

        /* Never send packets back to the socket they originated from */

    if (skb_has_frags(skb) &&

        rc = __dev_xmit_skb(skb, q, dev, txq);

    return sch_direct_xmit(skb, q, dev, txq, root_lock); //用于发送数据包

        if (txq->xmit_lock_owner != cpu) {//算不算在同八个cpu

}

1spinlock_t *root_lock = qdisc_lock(q);

        int cpu = smp_processor_id(); /* ok because BHs are off */

       //调用设备注册的发送函数,即dev->netdev_ops-> ndo_start_xmit(skb, dev)

3__netif_tx_lockà spin_lock(&txq->_xmit_lock)

       //咋样让该数据包是额外输入到你两种 原始套接口的,咋样让都可不都可不可不可以 克隆qq八个数据包

                 struct netdev_queue *txq)

        skb_set_transport_header(skb, skb->csum_start -

    HARD_TX_UNLOCK(dev, txq);  // 调用__netif_tx_unlock

   发送八个skb,将队列置为__QDISC_STATE_RUNNING状态,保证不可不都可不都可不可不可以 八个cpu运行你两种 函数,返回0表示队列为空咋样让发送受限,大于0表示队列非空。

    kfree_skb(skb);

    struct packet_type *ptype;

            if (!netif_tx_queue_stopped(txq)) {//取舍队列是运行状态

    rcu_read_unlock_bh();

    rcu_read_unlock_bh();

  //清除队列的运行标识

       //遍历ptype_all链表,查找所有符合输入条件的原始套接口,并循环将数据包输入到满足条件的套接口

        } else {// txq->xmit_lock_owner == cpu的状态,说明占据 递归

                           "buggy, dev %s\n",

txq_trans_update(txq);

    }

        __skb_linearize(skb))

            }

    .ndo_get_stats = loopback_get_stats,

                }

     其中(1)(3)分别对应八个spinlock,(2)对应八个队列状态。在了解代码中咋样使用你两种 个同步土法律法子时,首先看一下相关数据社会形态的关系,如下。

    spin_lock(root_lock);   //qdisc

{

        ret = handle_dev_cpu_collision(skb, txq, q);

        /*