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

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

```bash
$ 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とかいう謎の数字はエラーコードっぽいけど何のエラーなのかなーと調べてみた。

http://opengrok.dyndns.info/source/xref/linux/include/asm-generic/errno-base.h#31

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

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

http://opengrok.dyndns.info/source/xref/linux/drivers/net/virtio_net.c#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が呼ばれているのでそっちを覗きに行くと

http://opengrok.dyndns.info/source/xref/linux/drivers/virtio/virtio_ring.c#191

```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の空きが無くなったんだな、とわかる。

http://opengrok.dyndns.info/source/xref/linux/drivers/virtio/virtio_ring.c#77

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

結局何が起こったの
---------------
ネットワークとかよくわからんし、とりあえずググってみる

http://www.coverfire.com/articles/queueing-in-the-linux-network-stack/

![image](http://gyazo.dyndns.info/data/dd160e56e36eb9e1a1a03caba0942f5b.png)

さっきまで読んでいた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というコマンドで足せるらしい。

http://www.cyberciti.biz/faq/gentoo-centos-rhel-debian-fedora-increasing-txqueuelen/

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

### queueを分けて分散処理にする
今回のケースならNICを増やすか配布サーバを足して分業させれば良い

### queueへのpush回数を減らす
送信要求量を減らせばいい。今回だと

- 配布ツール内での並列実行数を減らしてみるとか
- rsyncを使ってるならbwlimitオプションで帯域制限するとか(http://www.admon.org/system-tuning/throttle-io-rate-limit-disk-io-for-rsync/)

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

参考文献
---------------
- QUEUEING IN THE LINUX NETWORK STACK
http://www.coverfire.com/articles/queueing-in-the-linux-network-stack/

- Throttle I/O Rate: Limit disk I/O for rsync
http://www.admon.org/system-tuning/throttle-io-rate-limit-disk-io-for-rsync/