パケット受信シリーズ-前半部実装(カーネルインタフェース)

8237 ワード

本文の主な内容:ネットワークパケット受信の上半部実現、主にカーネルインタフェース関連部分を分析する.
カーネルバージョン:2.6.37
Author:zhangskd @ csdn blog
 
上半部の実装
 
受信パケットの前半部の処理フローは次のとおりです.
el_interrupt()/NIC駆動
    |--> el_receive()/NIC駆動
                |--> netif_rx()/カーネルインタフェース
                           |--> enqueue_to_backlog()/カーネルインタフェース
 
NICドライバに関する部分を分析しましたが、カーネルインタフェースに関する部分を見てみましょう.)
 
netif_rx
 
netif_rx()はカーネルがネットワークパケットを受信するエントリである(現在、多くのNICは新しいインタフェースNAPIをサポートしており、後述する).
netif_rx()プライマリコールenqueue_to_backlog()は後続処理を行う.
/**
 * netif_rx - post buffer to the network code
 * @skb: buffer to post
 * This function receives a packet from a device and queues it for the upper (protocol)
 * levels to process. It always succeeds. The buffer may be dropped during processing
 * for congestion control or by the protocol layers.
 * return values:
 * NET_RX_SUCCESS (no congestion)
 * NET_RX_DROP (packet was dropped)
 */

int netif_rx(struct sk_buff *skb)
{
    int ret;

    /* if netpoll wants it, pretend we never saw it */
    if (netpoll_rx(skb))
        return NET_RX_DROP;

    /*        skb->tstamp */
    if (netdev_tstamp_prequeue)
       net_timestamp_check(skb);

   trace_netif_rx(skb);
#ifdef CONFIG_RPS
    /*     RPS,      */
    ...
#else
    {
        unsigned int qtail;
        ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
        put_cpu();
    }
#endif
    return ret;
}

 
softnet_data
 
各cpuにはソフトウェアが1つあります.dataインスタンス.パケットの送受信に使用されます.
/* Incoming packets are placed on per-cpu queues */

struct softnet_data {
    struct Qdisc *output_queue; /*       */
    struct Qdisc **output_queue_tailp;
 
     /*             ,              */
    struct list_head poll_list;

    struct sk_buff *completion_queue; /*            */
    
    /*     , input_pkt_queue   */
    struct sk_buff_head process_queue;

    /* stats */
    unsigned int processed; /*           */
    unsigned int time_squeeze; /* poll               */
    unsigned int cpu_collision;
    unsigned int received_rps;

#ifdef CONFIG_RPS
    /*     RPS */
    ...
#endif

    unsigned dropped; /*              */

    /*     ,         。
     *  NAPI  ,  NAPI             。
     */
    struct sk_buff_head input_pkt_queue;
    struct napi_struct backlog; /*     , NAPI     */
};

 
定義#テイギ#
/* Device drivers call our routines to queue packets here.
 * We empty the queue in the local softnet handler.
 */
DEFINE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
EXPORT_PER_CPU_SYMBOL(softnet_data);

 
初期化
/* Initialize the DEV module. At boot time this walks the device list and
 * unhooks any devices that fail to initialise (normally hardware not present)
 * and leaves us with a valid list of present and active devices.
 *
 * This is called single threaded during boot, so no need to take the rtnl semaphore.
 */

static int __init net_dev_init(void)
{
    ...
    /* Initialise the packet receive queues.
     *      cpu softnet_data  。
     */
    for_each_possible_cpu(i) {
        struct softnet_data *sd = &per_cpu(softnet_data, i);
        memset(sd, 0, sizeof(*sd));
        skb_queue_head_init(&sd->input_pkt_queue);
        skb_queue_head_init(&sd->process_queue);
        sd->completion_queue = NULL;
        INIT_LIST_HEAD(&sd->poll_list);
        sd->output_queue = NULL;
        sd->output_queue_tailp = &sd->output_queue;

#ifdef CONFIG_RPS
        ...
#endif

        sd->backlog.poll = process_backlog; /*  NAPI        */
        sd->backlog.weight = weight_p; /* 64,              */
        sd->backlog.gro_list = NULL;
        sd->backlog.gro_count = 0;
    }
    ...
    /*           */
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);
    ...
}

 
enqueue_to_backlog
 
netif_rx()呼び出しenqueue_to_backlog()で処理します.
まず、現在のcpuのソフトウェアを取得します.dataインスタンスsd、そして:
1.受信キューsd->input_pkt_Queueは空ではなく、パケットの処理にソフトブレークがあったことを示しています.
ソフト割り込みを再度トリガする必要がなく、受信キューの末尾にパケットを直接追加すればよい.
2.受信キューsd->input_pkt_Queueは空で、現在ソフトブレークがパケットを処理していないことを示しています.
仮想デバイスbacklogをsd->poll_に追加Listでポーリングを行い、最後にNET_を設定します.RX_SOFTIRQ
フラグはソフトブレークをトリガーします.
3.受信キューsd->input_pkt_Queueがいっぱいになったら、パケットを直接破棄します.
/* queue an skb to a per CPU backlog queue (may be a remote CPU queue). */

static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail)
{
    struct softnet_data *sd;
    unsigned long flags;

    sd = &per_cpu(softnet_data, cpu); /*     cpu  softnet_data   */

    local_irq_save(flags); /*        */
    rps_lock(sd);

    if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) {

        /*          ,                ,
         *            ,                 。
         */
        if (skb_queue_len(&sd->input_pkt_queue)) {
enqueue:
            __skb_queue_tail(&sd->input_pkt_queue, skb); /*           */

            input_queue_tail_incr_save(sd, qtail);
            rps_unlock(sd);

            local_irq_restore(flags); /*        */
            return NET_RX_SUCCESS;
        }

        /* Schedule NAPI for backlog device.
         *         ,               ,
         *      backlog   sd->poll_list       ,
         *     NET_RX_SOFTIRQ       。
         */
        if (! __test_and_set_bit(NAPT_STATE_SCHED, &sd->backlog.state)) {
            if (! rps_ipi_queued(sd))
                ____napi_schedule(sd, &sd->backlog);
        }
        goto enqueue;
    }

    sd->dropped++; /*               */
    rps_unlock(sd);
    local_irq_restore(flags); /*        */
    atomic_long_inc(&skb->dev->rx_dropped);
    kfree_skb(skb); /*       */
    return NET_RX_DROP;
}

int netdev_tstamp_prequeue = 1; /*        */
int netdev_max_backlog = 1000; /*           */

 
napi_structは、非NAPIの駆動に対応する仮想デバイスを表す.
/* Structure for NAPI scheduling similar to tasklet but with weighting */

struct napi_struct {
    /* The poll_list must only be managed by the entity which changes the
     * state of the NAPI_STATE_SCHED bit. This means whoever atomically
     * sets that bit can add this napi_struct to the per-cpu poll_list, and
     * whoever clears that bit can remove from the list right before clearing the bit.
     */
    struct list_head poll_list; /*                 */
    unsigned long state; /*         */
    int weight; /*          , NAPI weight_p,   64 */
    int (*poll) (struct napi_struct *, int); /*         ,   process_backlog() */

#ifdef CONFIG_NETPOLL
    ...
#endif

    unsigned int gro_count;
    struct net_device *dev;
    struct list_head dev_list;
    struct sk_buff *gro_list;
    struct sk_buff *skb;
};
static inline void ____napi_schedule(struct softnet_data *sd, struct napi_struct *napi)
{
    /*  napi_struct   softnet_data poll_list  */
    list_add_tail(&napi->poll_list, &sd->poll_list);
    __raise_softirq_irqoff(NET_RX_SOFTIRQ); /*          */
}