Linuxがnetif_receive_skbからパケットをドロップするのはなぜですか?

Linuxがnetif_receive_skbからパケットをドロップするのはなぜですか?

私はLinuxボックスを持っていて、外部からファイルを受信(ダウンロード)するときにtcpdumpで大量(30%)のTCP再送を確認しています。 dropwatchユーティリティを使用すると、カーネル関数net_receive_skb()で多くのパケット損失が発生します。これは、NICがデータを受信したがパケットが処理されたときに一部がカーネルから破棄されたことを意味します。廃棄されたパケットが多いため、再送信が必要な理由を説明できます。

dropwatchの出力は次のとおりです。

dropwatch -l kas 
Initalizing kallsyms db
dropwatch> start
Enabling monitoring...
Kernel monitoring activated.
Issue Ctrl-C to stop monitoring
1 drops at tcp_rcv_established+906 (0xffffffff814d0a66)
6 drops at unix_dgram_connect+4ac (0xffffffff8151890c)
6 drops at unix_dgram_connect+4ac (0xffffffff8151890c)
19 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
5 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
9 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
7 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
6 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
14 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
15 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
2 drops at __netif_receive_skb+49f (0xffffffff8147b4ef)
2 drops at inet_csk_reset_xmit_timer.clone.1+265 (0xffffffff814d9cb5) ^CGot a stop message
dropwatch> exit
Shutting down ...

システムは、カーネル2.6.32(centOSパッケージ名2.6.32-696.el6.x86_64)を含むCentOS 6.2です。それで、カーネルソースコードのnetif_receive_skbバージョンを見て、パケット損失の原因を見つけました。 kfree_skb(関数の終わり)を呼び出すと、失われたパケットの痕跡が残っている場所が1つしか見つかりませんでした。コードは次のとおりです

int netif_receive_skb(struct sk_buff *skb)
{
    struct packet_type *ptype, *pt_prev;
    struct net_device *orig_dev;
    struct net_device *null_or_orig;
    int ret = NET_RX_DROP;
    __be16 type;

    if (!skb->tstamp.tv64)
        net_timestamp(skb);

    if (skb->vlan_tci && vlan_hwaccel_do_receive(skb))
        return NET_RX_SUCCESS;

    /* if we've gotten here through NAPI, check netpoll */
    if (netpoll_receive_skb(skb))
        return NET_RX_DROP;

    if (!skb->iif)
        skb->iif = skb->dev->ifindex;

    null_or_orig = NULL;
    orig_dev = skb->dev;
    if (orig_dev->master) {
        if (skb_bond_should_drop(skb))
            null_or_orig = orig_dev; /* deliver only exact match */
        else
            skb->dev = orig_dev->master;
    }

    __get_cpu_var(netdev_rx_stat).total++;

    skb_reset_network_header(skb);
    skb_reset_transport_header(skb);
    skb->mac_len = skb->network_header - skb->mac_header;

    pt_prev = NULL;

    rcu_read_lock();

#ifdef CONFIG_NET_CLS_ACT
    if (skb->tc_verd & TC_NCLS) {
        skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
        goto ncls;
    }
#endif

    list_for_each_entry_rcu(ptype, &ptype_all, list) {
        if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
            ptype->dev == orig_dev) {
            if (pt_prev)
                ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = ptype;
        }
    }

#ifdef CONFIG_NET_CLS_ACT
    skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;
ncls:
#endif

    skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;
    skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;

    type = skb->protocol;
    list_for_each_entry_rcu(ptype,
            &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
        if (ptype->type == type &&
            (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
             ptype->dev == orig_dev)) {
            if (pt_prev)
                ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = ptype;
        }
    }

    if (pt_prev) {
        ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    } else {
        kfree_skb(skb);
        /* Jamal, now you will not able to escape explaining
         * me how you were going to use this. :-)
         */
        ret = NET_RX_DROP;
    }

out:
    rcu_read_unlock();
    return ret;
}

kfree_skbへの呼び出しは、skb-> devがどのプロトコルのptypeリストにも登録されていない場合にのみ発生するため、ptypeリストの2回のループ後もpt_prevはNULLのままです。これは、システムがすべてのパッケージの小さなサブセットのみを削除するため、意味がありません。つまり、デバイスは「ほとんどの場合、プロトコルptypeリストに登録されますが、時にはそこには登録されません」という意味です。

だから質問は - 私はドロップウォッチの結果とnetif_receive_skbコードを理解しながら、何が間違いを犯したのですか?この関数で報告されたパケット損失のより合理的な説明は何ですか?

ベストアンサー1

おそらく、これは以下から抜粋したようです。https://access.redhat.com/solutions/657483なぜそこに落ちたのかを説明するのに役立ちます。

RT および RHEL7 カーネルには、エラー以外の条件に対して rx_dropped カウンタを更新するコードが含まれています。

  • ソフトネットワークバックログがいっぱいです。
  • 無効なVLANタグ
  • 不明または登録されていないプロトコルを使用してパケットを受信しました。
  • サーバーが IPv4 専用に構成されている場合、IPv6 フレーム

おすすめ記事