LoginSignup
2
4

More than 5 years have passed since last update.

net eth1: Unexpected TX queue failure: -28 って何だろう、という話

Last updated at Posted at 2015-01-18

概要

配布ツールを使ってサーバにソースコードを配布したら見慣れないエラーが出たのでこれって何やねんと調べた話。kernelとか良くわからん子なので間違ってたらごめんなさい。ちなみにkernelのバージョンとかも気にしてないので古いかも

$ dmesg | tail -1
[16276010.045007] net eth1: Unexpected TX queue failure: -28

調査

まずこのメッセージってkernelのstart_xmit関数内のメッセージです。xmitはtransmitという意味っぽい

linux/drivers/net/virtio_net.c
static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct virtnet_info *vi = netdev_priv(dev);
    int capacity;

    /* Free up any pending old buffers before queueing new ones. */
    free_old_xmit_skbs(vi);

    /* Try to transmit */
    capacity = xmit_skb(vi, skb);

    /* This can happen with OOM and indirect buffers. */
    if (unlikely(capacity < 0)) {
        if (likely(capacity == -ENOMEM)) {
            if (net_ratelimit())
                dev_warn(&dev->dev,
                     "TX queue failure: out of memory\n");
        } else {
            dev->stats.tx_fifo_errors++;
            if (net_ratelimit())
                dev_warn(&dev->dev,
                     "Unexpected TX queue failure: %d\n",
                     capacity);
        }
        dev->stats.tx_dropped++;
        kfree_skb(skb);
        return NETDEV_TX_OK;
    }

.......................
}

上でENOMEMのチェックをしているから28とかいう謎の数字はエラーコードっぽいけど何のエラーなのかなーと調べてみた。

linux/include/asm-generic/errno-base.h#31
#define ENOSPC      28  /* No space left on device */

もう何となく答えが見えた感じですね!でまあxmit_skbからcapacityが返ってきているのでxmit_skbを調べてみる

linux/drivers/net/virtio_net.c#xmit_skb
static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
{
......................
    return virtqueue_add_buf(vi->svq, vi->tx_sg, hdr->num_sg,
                 0, skb, GFP_ATOMIC);
}

結局virtqueue_add_bufが呼ばれているのでそっちを覗きに行くと

linux/drivers/virtio/virtio_ring.c#191
int virtqueue_add_buf(struct virtqueue *_vq,
              struct scatterlist sg[],
              unsigned int out,
              unsigned int in,
              void *data,
              gfp_t gfp)
{
    struct vring_virtqueue *vq = to_vvq(_vq);
    unsigned int i, avail, uninitialized_var(prev);
    int head;

....................
    if (vq->num_free < out + in) {
        pr_debug("Can't add buf len %i - avail = %i\n",
             out + in, vq->num_free);
        /* FIXME: for historical reasons, we force a notify here if
         * there are outgoing parts to the buffer.  Presumably the
         * host should service the ring ASAP. */
        if (out)
            vq->notify(&vq->vq);
        END_USE(vq);
        return -ENOSPC;
    }
....................
}

まあ何となくqueueの空きが無くなったんだな、とわかる。

linux/drivers/virtio/virtio_ring.c#77
/* Number of free buffers */
unsigned int num_free;

結局何が起こったの

ネットワークとかよくわからんし、とりあえずググってみる

image

さっきまで読んでいたvirtio_ringがring bufferでDriving Queueというものらしい

で、このQueueにはsocket kernel buffer(SKB)というパケットデータ相当の情報へのポインタが積まれている。

パケットの送信要求が起こるとstart_xmitが呼ばれてNICに送られるんだけどNICの処理スピードが遅いと詰まってしまうようだ

どうすればいいいの?

ここから先は実際に試した訳ではない眉唾な情報です

queueが詰まった場合の対処方法って大体決まっていて以下のような対処をするのですが

  • queueのsizeを増やす
  • queueを分けて分散処理にする
  • queueへのpush回数を減らす
  • queueのpop回数を増やす

上から今回のケースに当てはめて、対処可能なのか調べる。

queueのsizeを増やす

txqueuelenというコマンドで足せるらしい。

ifconfig ${interface} txqueuelen ${size}
ifconfig eth1 txqueuelen 10000
ifconfig eth0 txqueuelen 5000

queueを分けて分散処理にする

今回のケースならNICを増やすか配布サーバを足して分業させれば良い

queueへのpush回数を減らす

送信要求量を減らせばいい。今回だと

queueのpop回数を増やす

性能の良いNICにする。
ハードウェアは良くわからんのですが1GBaseのカードから10Gbaseのものに変えると良くなったりするのだろうか?

参考文献

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4