-/* A simple network driver using virtio.
+/* A network driver using virtio.
*
* Copyright 2007 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
*
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/virtio.h>
-#include <linux/virtio_ids.h>
#include <linux/virtio_net.h>
#include <linux/scatterlist.h>
#include <linux/if_vlan.h>
struct virtio_net_hdr hdr;
struct virtio_net_hdr_mrg_rxbuf mhdr;
};
+ unsigned int num_sg;
};
static inline struct skb_vnet_hdr *skb_vnet_hdr(struct sk_buff *skb)
bool oom = false;
sg_init_table(sg, 2+MAX_SKB_FRAGS);
- for (;;) {
+ do {
struct skb_vnet_hdr *hdr;
- skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(vi->dev, MAX_PACKET_LEN);
if (unlikely(!skb)) {
oom = true;
break;
}
- skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, MAX_PACKET_LEN);
hdr = skb_vnet_hdr(skb);
break;
}
vi->num++;
- }
+ } while (err >= num);
if (unlikely(vi->num > vi->max))
vi->max = vi->num;
vi->rvq->vq_ops->kick(vi->rvq);
if (!vi->mergeable_rx_bufs)
return try_fill_recv_maxbufs(vi, gfp);
- for (;;) {
+ do {
skb_frag_t *f;
- skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN);
if (unlikely(!skb)) {
oom = true;
break;
}
- skb_reserve(skb, NET_IP_ALIGN);
-
f = &skb_shinfo(skb)->frags[0];
f->page = get_a_page(vi, gfp);
if (!f->page) {
break;
}
vi->num++;
- }
+ } while (err > 0);
if (unlikely(vi->num > vi->max))
vi->max = vi->num;
vi->rvq->vq_ops->kick(vi->rvq);
/* Out of packets? */
if (received < budget) {
napi_complete(napi);
- if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq))
- && napi_schedule_prep(napi)) {
+ if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq)) &&
+ napi_schedule_prep(napi)) {
vi->rvq->vq_ops->disable_cb(vi->rvq);
__napi_schedule(napi);
goto again;
return received;
}
-static void free_old_xmit_skbs(struct virtnet_info *vi)
+static unsigned int free_old_xmit_skbs(struct virtnet_info *vi)
{
struct sk_buff *skb;
- unsigned int len;
+ unsigned int len, tot_sgs = 0;
while ((skb = vi->svq->vq_ops->get_buf(vi->svq, &len)) != NULL) {
pr_debug("Sent skb %p\n", skb);
__skb_unlink(skb, &vi->send);
vi->dev->stats.tx_bytes += skb->len;
vi->dev->stats.tx_packets++;
- kfree_skb(skb);
+ tot_sgs += skb_vnet_hdr(skb)->num_sg;
+ dev_kfree_skb_any(skb);
}
+ return tot_sgs;
}
static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
{
- int num;
struct scatterlist sg[2+MAX_SKB_FRAGS];
struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb);
const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
else
sg_set_buf(sg, &hdr->hdr, sizeof(hdr->hdr));
- num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
- return vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
+ hdr->num_sg = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
+ return vi->svq->vq_ops->add_buf(vi->svq, sg, hdr->num_sg, 0, skb);
}
static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct virtnet_info *vi = netdev_priv(dev);
+ int capacity;
again:
/* Free up any pending old buffers before queueing new ones. */
free_old_xmit_skbs(vi);
- /* Put new one in send queue and do transmit */
- __skb_queue_head(&vi->send, skb);
- if (likely(xmit_skb(vi, skb) >= 0)) {
- vi->svq->vq_ops->kick(vi->svq);
- /* Don't wait up for transmitted skbs to be freed. */
- skb_orphan(skb);
- nf_reset(skb);
- return NETDEV_TX_OK;
+ /* Try to transmit */
+ capacity = xmit_skb(vi, skb);
+
+ /* This can happen with OOM and indirect buffers. */
+ if (unlikely(capacity < 0)) {
+ netif_stop_queue(dev);
+ dev_warn(&dev->dev, "Unexpected full queue\n");
+ if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) {
+ vi->svq->vq_ops->disable_cb(vi->svq);
+ netif_start_queue(dev);
+ goto again;
+ }
+ return NETDEV_TX_BUSY;
}
+ vi->svq->vq_ops->kick(vi->svq);
- /* Ring too full for this packet, remove it from queue again. */
- pr_debug("%s: virtio not prepared to send\n", dev->name);
- __skb_unlink(skb, &vi->send);
- netif_stop_queue(dev);
-
- /* Activate callback for using skbs: if this returns false it
- * means some were used in the meantime. */
- if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) {
- vi->svq->vq_ops->disable_cb(vi->svq);
- netif_start_queue(dev);
- goto again;
+ /*
+ * Put new one in send queue. You'd expect we'd need this before
+ * xmit_skb calls add_buf(), since the callback can be triggered
+ * immediately after that. But since the callback just triggers
+ * another call back here, normal network xmit locking prevents the
+ * race.
+ */
+ __skb_queue_head(&vi->send, skb);
+
+ /* Don't wait up for transmitted skbs to be freed. */
+ skb_orphan(skb);
+ nf_reset(skb);
+
+ /* Apparently nice girls don't return TX_BUSY; stop the queue
+ * before it gets out of hand. Naturally, this wastes entries. */
+ if (capacity < 2+MAX_SKB_FRAGS) {
+ netif_stop_queue(dev);
+ if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) {
+ /* More just got used, free them then recheck. */
+ capacity += free_old_xmit_skbs(vi);
+ if (capacity >= 2+MAX_SKB_FRAGS) {
+ netif_start_queue(dev);
+ vi->svq->vq_ops->disable_cb(vi->svq);
+ }
+ }
}
- return NETDEV_TX_BUSY;
+
+ return NETDEV_TX_OK;
}
static int virtnet_set_mac_address(struct net_device *dev, void *p)
INIT_DELAYED_WORK(&vi->refill, refill_work);
/* If we can receive ANY GSO packets, we must allocate large ones. */
- if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4)
- || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6)
- || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) ||
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) ||
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
vi->big_packets = true;
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
return err;
}
-static void virtnet_remove(struct virtio_device *vdev)
+static void __devexit virtnet_remove(struct virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
struct sk_buff *skb;
VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN,
};
-static struct virtio_driver virtio_net = {
+static struct virtio_driver virtio_net_driver = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
static int __init init(void)
{
- return register_virtio_driver(&virtio_net);
+ return register_virtio_driver(&virtio_net_driver);
}
static void __exit fini(void)
{
- unregister_virtio_driver(&virtio_net);
+ unregister_virtio_driver(&virtio_net_driver);
}
module_init(init);
module_exit(fini);